dev@74
|
1 import {Injectable, Inject} from '@angular/core';
|
dev@47
|
2 import {
|
dev@226
|
3 ListResponse
|
dev@497
|
4 } from 'piper-js/core';
|
dev@64
|
5 import {
|
dev@497
|
6 OneShotExtractionRequest as SimpleRequest,
|
dev@497
|
7 } from 'piper-js/one-shot';
|
dev@236
|
8 import {Subject} from 'rxjs/Subject';
|
dev@236
|
9 import {Observable} from 'rxjs/Observable';
|
dev@324
|
10 import {Http} from '@angular/http';
|
dev@226
|
11 import {
|
dev@226
|
12 countingIdProvider,
|
dev@497
|
13 WebWorkerStreamingClient,
|
dev@497
|
14 RequestId
|
dev@497
|
15 } from 'piper-js/web-worker';
|
dev@497
|
16 import {collect, StreamingConfiguration} from 'piper-js/streaming';
|
dev@349
|
17 import {
|
dev@349
|
18 KnownShapedFeature,
|
dev@349
|
19 toKnownShape
|
dev@349
|
20 } from '../../visualisations/FeatureUtilities';
|
dev@494
|
21 import {NotificationService} from '../notifications/notifications.service';
|
dev@40
|
22
|
dev@74
|
23 type RepoUri = string;
|
dev@74
|
24 export interface AvailableLibraries {
|
dev@74
|
25 [libraryKey: string]: RepoUri;
|
dev@74
|
26 }
|
dev@74
|
27
|
dev@226
|
28 export interface Progress {
|
dev@226
|
29 id: RequestId;
|
dev@226
|
30 value: number; // between 0 and 100, for material-ui
|
dev@226
|
31 }
|
dev@226
|
32
|
dev@350
|
33 export interface ExtractionResult {
|
dev@350
|
34 id: RequestId;
|
dev@350
|
35 result: KnownShapedFeature;
|
dev@396
|
36 unit?: string;
|
dev@350
|
37 }
|
dev@350
|
38
|
dev@40
|
39 @Injectable()
|
dev@40
|
40 export class FeatureExtractionService {
|
dev@40
|
41
|
dev@40
|
42 private worker: Worker;
|
dev@350
|
43 private featuresExtracted: Subject<ExtractionResult>;
|
dev@350
|
44 featuresExtracted$: Observable<ExtractionResult>;
|
dev@74
|
45 private librariesUpdated: Subject<ListResponse>;
|
dev@74
|
46 librariesUpdated$: Observable<ListResponse>;
|
dev@226
|
47 private progressUpdated: Subject<Progress>;
|
dev@226
|
48 progressUpdated$: Observable<Progress>;
|
dev@226
|
49 private client: WebWorkerStreamingClient;
|
dev@44
|
50
|
dev@236
|
51 constructor(private http: Http,
|
dev@494
|
52 @Inject('PiperRepoUri') private repositoryUri: RepoUri,
|
dev@494
|
53 private notifier: NotificationService) {
|
dev@40
|
54 this.worker = new Worker('bootstrap-feature-extraction-worker.js');
|
dev@350
|
55 this.featuresExtracted = new Subject<ExtractionResult>();
|
dev@51
|
56 this.featuresExtracted$ = this.featuresExtracted.asObservable();
|
dev@74
|
57 this.librariesUpdated = new Subject<ListResponse>();
|
dev@74
|
58 this.librariesUpdated$ = this.librariesUpdated.asObservable();
|
dev@226
|
59 this.progressUpdated = new Subject<Progress>();
|
dev@226
|
60 this.progressUpdated$ = this.progressUpdated.asObservable();
|
dev@74
|
61 this.worker.addEventListener('message', (ev: MessageEvent) => {
|
dev@74
|
62 const isValidResponse = ev.data.method === 'import'
|
dev@226
|
63 && ev.data.result && ev.data.result.available ;
|
dev@74
|
64 if (isValidResponse) {
|
dev@226
|
65 (ev as Event).stopImmediatePropagation();
|
dev@74
|
66 this.librariesUpdated.next(ev.data.result);
|
dev@74
|
67 }
|
dev@226
|
68 }, true);
|
dev@226
|
69
|
dev@226
|
70 this.client = new WebWorkerStreamingClient(
|
dev@226
|
71 this.worker,
|
dev@226
|
72 countingIdProvider(0)
|
dev@236
|
73 );
|
dev@40
|
74 }
|
dev@40
|
75
|
dev@47
|
76 list(): Promise<ListResponse> {
|
dev@326
|
77 return this.client.list({});
|
dev@40
|
78 }
|
dev@40
|
79
|
dev@350
|
80 extract(analysisItemId: string,
|
dev@350
|
81 request: SimpleRequest): Promise<ExtractionResult> {
|
dev@305
|
82 let config: StreamingConfiguration;
|
dev@305
|
83 return collect(this.client.process(request), val => {
|
dev@305
|
84 if (val.configuration) {
|
dev@305
|
85 config = val.configuration;
|
dev@305
|
86 }
|
dev@305
|
87 const progress = val.progress;
|
dev@305
|
88 if (progress.totalBlockCount > 0) {
|
dev@305
|
89 this.progressUpdated.next({
|
dev@305
|
90 id: analysisItemId,
|
dev@305
|
91 value: (progress.processedBlockCount / progress.totalBlockCount) * 100
|
dev@305
|
92 });
|
dev@305
|
93 }
|
dev@305
|
94 }).then(features => {
|
dev@349
|
95 const shaped = toKnownShape({
|
dev@305
|
96 features: features,
|
dev@305
|
97 outputDescriptor: config.outputDescriptor
|
dev@226
|
98 });
|
dev@396
|
99 const result = config.outputDescriptor.configured.unit ? {
|
dev@396
|
100 id: analysisItemId,
|
dev@396
|
101 result: shaped,
|
dev@396
|
102 unit: shaped.shape === 'notes' ?
|
dev@396
|
103 'MIDI note' : config.outputDescriptor.configured.unit
|
dev@396
|
104 } : {
|
dev@350
|
105 id: analysisItemId,
|
dev@350
|
106 result: shaped
|
dev@350
|
107 };
|
dev@350
|
108 this.featuresExtracted.next(result);
|
dev@350
|
109 return result;
|
dev@305
|
110 });
|
dev@62
|
111 }
|
dev@62
|
112
|
dev@324
|
113 updateAvailableLibraries(): void {
|
dev@324
|
114 this.http.get(this.repositoryUri)
|
dev@324
|
115 .toPromise() // just turn into a promise for now to subscribe / execute
|
dev@324
|
116 .then(res => {
|
dev@74
|
117 this.worker.postMessage({
|
dev@74
|
118 method: 'addRemoteLibraries',
|
dev@324
|
119 params: res.json()
|
dev@349
|
120 });
|
dev@74
|
121 })
|
dev@494
|
122 .catch(err => this.notifier.displayError(err));
|
dev@74
|
123 }
|
dev@74
|
124
|
dev@74
|
125 load(libraryKey: string): void {
|
dev@74
|
126 this.worker.postMessage({method: 'import', params: libraryKey});
|
dev@74
|
127 }
|
dev@40
|
128 }
|