comparison src/app/services/feature-extraction/FeatureExtractionWorker.ts @ 226:4865567d9e43

Refactor feature extraction service to use piper streaming client/server. Change FeatureExtractionWorker accordingly and calling code.
author Lucas Thompson <dev@lucas.im>
date Fri, 21 Apr 2017 12:59:41 +0100
parents 4aa6b1266251
children efa23f33393b
comparison
equal deleted inserted replaced
225:16d19c12e42f 226:4865567d9e43
2 * Created by lucas on 01/12/2016. 2 * Created by lucas on 01/12/2016.
3 */ 3 */
4 4
5 import {PiperVampService, ListRequest, ListResponse} from 'piper'; 5 import {PiperVampService, ListRequest, ListResponse} from 'piper';
6 import { 6 import {
7 PiperSimpleClient, SimpleRequest, 7 SimpleRequest
8 SimpleResponse
9 } from 'piper/HigherLevelUtilities'; 8 } from 'piper/HigherLevelUtilities';
10 import { VampExamplePlugins } from 'piper/ext/VampExamplePluginsModule'; 9 import { VampExamplePlugins } from 'piper/ext/VampExamplePluginsModule';
11 import {AvailableLibraries} from "./feature-extraction.service"; 10 import {AvailableLibraries} from "./feature-extraction.service";
11 import {
12 DedicatedWorkerGlobalScope,
13 WebWorkerStreamingServer
14 } from "piper/servers/WebWorkerStreamingServer";
15 import {
16 PiperStreamingService,
17 StreamingResponse,
18 StreamingService
19 } from "piper/StreamingService";
20 import {Observable} from "rxjs/Observable";
12 21
13 // TODO TypeScript has a .d.ts file for webworkers, but for some reason it clashes with the typings for dom and causes compiler errors
14 interface WorkerGlobalScope {
15 onmessage: (this: this, ev: MessageEvent) => any;
16 postMessage(data: any): void;
17 importScripts(uri: string): void;
18 }
19 22
20 interface MessageEvent { 23 interface MessageEvent {
21 readonly data: any; 24 readonly data: any;
22 } 25 }
23 26
24 type LibraryUri = string; 27 type LibraryUri = string;
25 type LibraryKey = string; 28 type LibraryKey = string;
26 29
27 type RequireJs = (libs: string[], callback: (...libs: any[]) => void) => void; 30 type RequireJs = (libs: string[], callback: (...libs: any[]) => void) => void;
28 31
32 class AggregateStreamingService implements StreamingService {
33 private services: Map<LibraryKey, PiperStreamingService>;
34
35 constructor() {
36 this.services = new Map<LibraryKey, PiperStreamingService>();
37 this.services.set(
38 'vamp-example-plugins',
39 new PiperStreamingService(new PiperVampService(VampExamplePlugins()))
40 );
41 }
42
43 addService(key: LibraryKey, service: PiperStreamingService): void {
44 this.services.set(key, service);
45 }
46
47 list(request: ListRequest): Promise<ListResponse> {
48 return Promise.all(
49 [...this.services.values()].map(client => client.list({}))
50 ).then(allAvailable => ({
51 available: allAvailable.reduce(
52 (all, current) => all.concat(current.available),
53 []
54 )
55 })
56 );
57 }
58
59 process(request: SimpleRequest): Observable<StreamingResponse> {
60 return undefined;
61 }
62
63 collect(request: SimpleRequest): Observable<StreamingResponse> {
64 const key = request.key.split(':')[0];
65 return this.services.has(key) ?
66 this.services.get(key).collect(request) : Observable.throw("Invalid key");
67 }
68 }
69
29 export default class FeatureExtractionWorker { 70 export default class FeatureExtractionWorker {
30 private workerScope: WorkerGlobalScope; 71 private workerScope: DedicatedWorkerGlobalScope;
31 private clients: Map<LibraryKey, PiperSimpleClient>; 72 private services: Map<LibraryKey, PiperStreamingService>;
32 private remoteLibraries: Map<LibraryKey, LibraryUri>; 73 private remoteLibraries: Map<LibraryKey, LibraryUri>;
74 private server: WebWorkerStreamingServer;
75 private service: AggregateStreamingService;
33 76
34 constructor(workerScope: WorkerGlobalScope, private requireJs: RequireJs) { 77 constructor(workerScope: DedicatedWorkerGlobalScope,
78 private requireJs: RequireJs) {
35 this.workerScope = workerScope; 79 this.workerScope = workerScope;
36 this.clients = new Map<LibraryKey, PiperSimpleClient>(); 80 this.services = new Map<LibraryKey, PiperStreamingService>();
37 this.remoteLibraries = new Map<LibraryKey, LibraryUri>(); 81 this.remoteLibraries = new Map<LibraryKey, LibraryUri>();
38 this.clients.set( 82 this.service = new AggregateStreamingService();
39 'vamp-example-plugins', 83 this.setupImportLibraryListener();
40 new PiperSimpleClient(new PiperVampService(VampExamplePlugins())) 84 this.server = new WebWorkerStreamingServer(
85 this.workerScope,
86 this.service
41 ); 87 );
88 }
42 89
90 private setupImportLibraryListener(): void {
43 this.workerScope.onmessage = (ev: MessageEvent) => { 91 this.workerScope.onmessage = (ev: MessageEvent) => {
44 const sendResponse = (result) => { 92 const sendResponse = (result) => {
45 this.workerScope.postMessage({ 93 this.workerScope.postMessage({
46 method: ev.data.method, 94 method: ev.data.method,
47 result: result 95 result: result
48 }); 96 });
49 }; 97 };
50 switch (ev.data.method) { 98 switch (ev.data.method) {
51 case 'list':
52 this.list(ev.data.params)
53 .then(sendResponse)
54 .catch(err => console.error(err)); // TODO handle error
55 break;
56 case 'process':
57 this.process(ev.data.params)
58 .then(sendResponse)
59 .catch(err => console.error(err)); // TODO handle error
60 break;
61 case 'collect':
62 this.collect(ev.data.params)
63 .then(sendResponse)
64 .catch(err => console.error(err)); // TODO handle error
65 break;
66 case 'import': 99 case 'import':
67 // this.workerScope.importScripts(ev.data.params);
68 const key: LibraryKey = ev.data.params; 100 const key: LibraryKey = ev.data.params;
69 if (this.remoteLibraries.has(key)) { 101 if (this.remoteLibraries.has(key)) {
70 this.requireJs([this.remoteLibraries.get(key)], (plugin) => { 102 this.requireJs([this.remoteLibraries.get(key)], (plugin) => {
71 this.clients.set( 103 this.services.set(
72 key, 104 key,
73 new PiperSimpleClient(new PiperVampService(plugin.createLibrary())) 105 new PiperStreamingService(
106 new PiperVampService(plugin.createLibrary())
107 )
74 ); // TODO won't always be an emscripten module 108 ); // TODO won't always be an emscripten module
75 this.list({}).then(sendResponse); 109 this.service.addService(key, this.services.get(key));
110 this.service.list({}).then(sendResponse);
76 }); 111 });
77 } else { 112 } else {
78 console.error('Non registered library key.'); // TODO handle error 113 console.error('Non registered library key.'); // TODO handle error
79 } 114 }
80 break; 115 break;
84 this.remoteLibraries.set(key, available[key]); 119 this.remoteLibraries.set(key, available[key]);
85 }); 120 });
86 } 121 }
87 }; 122 };
88 } 123 }
89
90 private list(request: ListRequest): Promise<ListResponse> {
91 // TODO actually pay attention to ListRequest
92 return Promise.all([...this.clients.values()].map(client => client.list({})))
93 .then(allAvailable => {
94 return {
95 available: allAvailable.reduce(
96 (all, current) => all.concat(current.available),
97 []
98 )
99 };
100 });
101 }
102
103 // TODO reduce dupe
104 private process(request: SimpleRequest): Promise<SimpleResponse> {
105 const key: LibraryKey = request.key.split(':')[0];
106 const client: PiperSimpleClient = this.clients.get(key);
107 return client ? client.process(request) : Promise.reject("Invalid plugin library key.");
108 }
109
110 private collect(request: SimpleRequest): Promise<SimpleResponse> {
111 const key: LibraryKey = request.key.split(':')[0];
112 const client: PiperSimpleClient = this.clients.get(key);
113 return client ? client.collect(request) : Promise.reject("Invalid plugin library key.");
114 }
115 } 124 }