diff src/app/app.component.ts @ 460:ccce2c09502e

Manually cherry-pick various refactoring efforts from feature/basic-session-loading
author Lucas Thompson <dev@lucas.im>
date Fri, 30 Jun 2017 10:41:30 +0100
parents 8d561b6df2fa
children 50f61d1945db
line wrap: on
line diff
--- a/src/app/app.component.ts	Fri Jun 30 00:59:38 2017 +0100
+++ b/src/app/app.component.ts	Fri Jun 30 10:41:30 2017 +0100
@@ -4,94 +4,27 @@
   AudioResourceError,
   AudioResource
 } from './services/audio-player/audio-player.service';
-import {FeatureExtractionService} from './services/feature-extraction/feature-extraction.service';
+import {
+  ExtractionResult,
+  FeatureExtractionService
+} from './services/feature-extraction/feature-extraction.service';
 import {ExtractorOutputInfo} from './feature-extraction-menu/feature-extraction-menu.component';
 import {DomSanitizer} from '@angular/platform-browser';
 import {MdIconRegistry} from '@angular/material';
 import {Subscription} from 'rxjs/Subscription';
 import {
   AnalysisItem,
-  isPendingAnalysisItem, isPendingRootAudioItem,
-  isRootAudioItem,
+  isPendingAnalysisItem,
+  isPendingRootAudioItem,
+  isLoadedRootAudioItem,
   Item,
-  PendingAnalysisItem,
-  PendingRootAudioItem,
-  RootAudioItem
-} from './analysis-item/analysis-item.component';
+  RootAudioItem,
+  LoadedRootAudioItem
+} from './analysis-item/AnalysisItem';
 import {OnSeekHandler} from './playhead/PlayHeadHelpers';
 import {UrlResourceLifetimeManager} from './app.module';
-
-class PersistentStack<T> {
-  private stack: T[];
-  private history: T[][];
-
-  constructor() {
-    this.stack = [];
-    this.history = [];
-  }
-
-  shift(): T {
-    this.history.push([...this.stack]);
-    const item = this.stack[0];
-    this.stack = this.stack.slice(1);
-    return item;
-  }
-
-  unshift(item: T): number {
-    this.history.push([...this.stack]);
-    this.stack = [item, ...this.stack];
-    return this.stack.length;
-  }
-
-  findIndex(predicate: (value: T,
-                        index: number,
-                        array: T[]) => boolean): number {
-    return this.stack.findIndex(predicate);
-  }
-
-  filter(predicate: (value: T, index: number, array: T[]) => boolean): T[] {
-    return this.stack.filter(predicate);
-  }
-
-  get(index: number): T {
-    return this.stack[index];
-  }
-
-  set(index: number, value: T) {
-    this.history.push([...this.stack]);
-    this.stack = [
-      ...this.stack.slice(0, index),
-      value,
-      ...this.stack.slice(index + 1)
-    ];
-  }
-
-  map<U>(transform: (value: T, index: number, array: T[]) => U): U[] {
-    return this.stack.map(transform);
-  }
-
-  reduce<U>(reducer: (previousValue: U,
-                      currentValue: T,
-                      currentIndex: number,
-                      array: T[]) => U,
-            initialValue: U): U {
-    return this.stack.reduce(reducer, initialValue);
-  }
-
-  remove(...indices: number[]) {
-    this.history.push([...this.stack]);
-    this.stack = this.stack.reduce((acc, item, i) => {
-      if (!indices.includes(i)) {
-        acc.push(item);
-      }
-      return acc;
-    }, [] as T[]);
-  }
-
-  toIterable(): Iterable<T> {
-    return this.stack;
-  }
-}
+import {createExtractionRequest} from './analysis-item/AnalysisItem';
+import {PersistentStack} from './Session';
 
 @Component({
   selector: 'ugly-root',
@@ -105,7 +38,7 @@
   private analyses: PersistentStack<Item>; // TODO some immutable state container describing entire session
   private nRecordings: number; // TODO user control for naming a recording
   private countingId: number; // TODO improve uniquely identifying items
-  private rootAudioItem: RootAudioItem;
+  private rootAudioItem: LoadedRootAudioItem;
   private onSeek: OnSeekHandler;
 
   constructor(private audioService: AudioPlayerService,
@@ -138,7 +71,7 @@
           if (this.rootAudioItem.audioData) {
             this.canExtract = true;
             const currentRootIndex = this.analyses.findIndex(val => {
-              return isRootAudioItem(val) && val.uri === this.rootAudioItem.uri;
+              return isLoadedRootAudioItem(val) && val.uri === this.rootAudioItem.uri;
             });
             if (currentRootIndex !== -1) {
               this.analyses.set(
@@ -196,63 +129,34 @@
       id: `${++this.countingId}`,
       mimeType: file.type,
       isExportable: createExportableItem
-    } as PendingRootAudioItem;
-    this.rootAudioItem = pending as RootAudioItem; // TODO this is silly
+    } as RootAudioItem;
+    this.rootAudioItem = pending as LoadedRootAudioItem; // TODO this is silly
 
     // TODO re-ordering of items for display
     // , one alternative is a Angular Pipe / Filter for use in the Template
     this.analyses.unshift(pending);
   }
 
-  extractFeatures(outputInfo: ExtractorOutputInfo): void {
+  extractFeatures(outputInfo: ExtractorOutputInfo): string {
     if (!this.canExtract || !outputInfo) {
       return;
     }
 
     this.canExtract = false;
 
-    const placeholderCard: PendingAnalysisItem = {
+    const placeholderCard: AnalysisItem = {
       parent: this.rootAudioItem,
       hasSharedTimeline: true,
-      extractorKey: outputInfo.combinedKey,
+      extractorKey: outputInfo.extractorKey,
+      outputId: outputInfo.outputId,
       title: outputInfo.name,
       description: outputInfo.outputId,
       id: `${++this.countingId}`,
       progress: 0
     };
     this.analyses.unshift(placeholderCard);
-
-    const audioBuffer = this.rootAudioItem.audioData;
-
-    this.featureService.extract(`${this.countingId}`, {
-      audioData: [...Array(audioBuffer.numberOfChannels).keys()]
-        .map(i => audioBuffer.getChannelData(i)),
-      audioFormat: {
-        sampleRate: audioBuffer.sampleRate,
-        channelCount: audioBuffer.numberOfChannels,
-        length: audioBuffer.length
-      },
-      key: outputInfo.extractorKey,
-      outputId: outputInfo.outputId
-    }).then(result => { // TODO subscribe to the extraction service instead
-      const i = this.analyses.findIndex(val => val.id === result.id);
-      this.canExtract = true;
-      if (i !== -1) {
-        this.analyses.set(
-          i,
-          Object.assign(
-            {},
-            this.analyses.get(i),
-            result.result,
-            result.unit ? {unit: result.unit} : {}
-          )
-        );
-      }  // TODO else remove the item?
-    }).catch(err => {
-      this.canExtract = true;
-      this.analyses.shift();
-      console.error(`Error whilst extracting: ${err}`);
-    });
+    this.sendExtractionRequest(placeholderCard);
+    return placeholderCard.id;
   }
 
   removeItem(item: Item): void {
@@ -270,13 +174,13 @@
       if (this.rootAudioItem.uri === item.uri) {
         this.audioService.unload();
         const topItem = this.analyses.get(0);
-        const nullRootAudio: RootAudioItem = {uri: ''} as any; // TODO eugh
+        const nullRootAudio: LoadedRootAudioItem = {uri: ''} as any; // TODO eugh
 
         if (topItem) {
           if (isPendingAnalysisItem(topItem)) {
-            this.rootAudioItem = topItem.parent as RootAudioItem;
-          } else if(isPendingRootAudioItem(topItem)) {
-            this.rootAudioItem = topItem as RootAudioItem
+            this.rootAudioItem = topItem.parent as LoadedRootAudioItem;
+          } else if (isPendingRootAudioItem(topItem)) {
+            this.rootAudioItem = topItem as LoadedRootAudioItem;
           } else {
            this.rootAudioItem = nullRootAudio;
           }
@@ -296,4 +200,32 @@
     this.onAudioDataSubscription.unsubscribe();
     this.onProgressUpdated.unsubscribe();
   }
+
+  private sendExtractionRequest(analysis: AnalysisItem): Promise<void> {
+    const findAndUpdateItem = (result: ExtractionResult): void => {
+      // TODO subscribe to the extraction service instead
+      const i = this.analyses.findIndex(val => val.id === result.id);
+      this.canExtract = true;
+      if (i !== -1) {
+        this.analyses.set(
+          i,
+          Object.assign(
+            {},
+            this.analyses.get(i),
+            result.result,
+            result.unit ? {unit: result.unit} : {}
+          )
+        );
+      }  // TODO else remove the item?
+    };
+    return this.featureService.extract(
+      analysis.id,
+      createExtractionRequest(analysis))
+      .then(findAndUpdateItem)
+      .catch(err => {
+        this.canExtract = true;
+        this.analyses.shift();
+        console.error(`Error whilst extracting: ${err}`);
+      });
+  }
 }