changeset 306:cd117c836ca7

Merge remote-tracking branch 'lucas/dev/tracks-shape' into dev/tracks-shape
author Chris Cannam <cannam@all-day-breakfast.com>
date Fri, 12 May 2017 08:46:12 +0100
parents 1d39cc796046 (current diff) 75a234459d3b (diff)
children 930f712f687c
files
diffstat 5 files changed, 82 insertions(+), 143 deletions(-) [+]
line wrap: on
line diff
--- a/package.json	Thu May 11 11:46:07 2017 +0100
+++ b/package.json	Fri May 12 08:46:12 2017 +0100
@@ -24,7 +24,7 @@
     "@angular/router": "^4.0.0",
     "core-js": "^2.4.1",
     "hammerjs": "^2.0.8",
-    "piper": "github:cannam/piper-js#dev-tracks",
+    "piper": "github:LucasThompson/piper-js#dev-tracks",
     "requirejs": "^2.3.3",
     "rxjs": "^5.1.0",
     "waves-ui-piper": "piper-audio/waves-ui-piper",
--- a/src/app/services/feature-extraction/FeatureExtractionWorker.ts	Thu May 11 11:46:07 2017 +0100
+++ b/src/app/services/feature-extraction/FeatureExtractionWorker.ts	Fri May 12 08:46:12 2017 +0100
@@ -63,11 +63,7 @@
     return this.dispatch('process', request);
   }
 
-  collect(request: SimpleRequest): Observable<StreamingResponse> {
-    return this.dispatch('collect', request);
-  }
-
-  protected dispatch(method: 'process' | 'collect',
+  protected dispatch(method: 'process',
                      request: SimpleRequest): Observable<StreamingResponse> {
     const key = request.key.split(':')[0];
     return this.services.has(key) ?
@@ -80,19 +76,27 @@
     super();
   }
 
-  protected dispatch(method: 'process' | 'collect',
+  protected dispatch(method: 'process',
                      request: SimpleRequest): Observable<StreamingResponse> {
     let lastPercentagePoint = 0;
+    let shouldClear = false;
     return super.dispatch(method, request)
-      .scan(streamingResponseReducer)
+      .scan((acc, value) => {
+        if (shouldClear) {
+          acc.features = [];
+        }
+        return streamingResponseReducer(acc, value);
+      })
       .filter(val => {
+        const progress = val.progress;
         const percentage =
-          100 * (val.processedBlockCount / val.totalBlockCount) | 0;
+          100 * (progress.processedBlockCount / progress.totalBlockCount) | 0;
         const pointDifference = (percentage - lastPercentagePoint);
         const shouldEmit = pointDifference === 1 || percentage === 100;
         if (shouldEmit) {
           lastPercentagePoint = percentage;
         }
+        shouldClear = shouldEmit;
         return shouldEmit;
       });
   }
--- a/src/app/services/feature-extraction/FeatureReducers.ts	Thu May 11 11:46:07 2017 +0100
+++ b/src/app/services/feature-extraction/FeatureReducers.ts	Fri May 12 08:46:12 2017 +0100
@@ -1,15 +1,12 @@
 /**
  * Created by lucast on 26/04/2017.
  */
-import {StreamingResponse} from 'piper/StreamingService';
-import {Feature, FeatureList} from 'piper/Feature';
-import {SampleType} from 'piper';
-import {
-  VectorFeature, MatrixFeature, TracksFeature
-} from "piper/HigherLevelUtilities";
+import {StreamingResponse} from "piper/StreamingService";
 
 export const arrayReducer = <T>(acc: T[], val: T[]): T[] => {
-  acc.push.apply(acc, val);
+  for (let i = 0, len = val.length; i < len; ++i) {
+    acc.push(val[i]);
+  }
   return acc;
 };
 
@@ -26,80 +23,12 @@
 };
 
 export const streamingResponseReducer = (acc: StreamingResponse,
-                                         val: StreamingResponse,
-                                         i: number): StreamingResponse =>
-  {
-    if (acc.features.shape !== val.features.shape) {
-      throw new Error(`Unexpected feature shape ${val.features.shape} (expected ${acc.features.shape})`);
-    }
-
-    acc.processedBlockCount = val.processedBlockCount;
-
-    const isOneSamplePerStep = acc.outputDescriptor.configured.sampleType ===
-      SampleType.OneSamplePerStep;
-
-    let incoming, collected;
-
-    console.log("i = " + i + ", shape = " + acc.features.shape + ", count = " + acc.processedBlockCount);
-    
-    switch (acc.features.shape) {
-
-    case "vector":
-      incoming = val.features.collected as VectorFeature;
-      collected = acc.features.collected as VectorFeature;
-      if (isOneSamplePerStep) {
-	// for one sample per step vectors we know there will be
-	// totalBlockCount number of samples - so pre-allocate the
-	// Float32Array when we know the totalBlockCount (after
-	// receiving the first feature)
-	if (i === 1) {
-          const newBlock = new Float32Array(acc.totalBlockCount);
-          newBlock[0] = collected.data[0];
-          collected.data = newBlock;
-	}
-	collected.data = inPlaceTypedArrayReducer(
-	  collected.data,
-	  incoming.data,
-          i
-	);
-      } else {
-	// if not OneSamplePerStep we have to make a new array each time
-	collected.data = typedArrayReducer(
-          collected.data,
-	  incoming.data
-	);
-      }
-      break;
-
-    case "matrix":
-      incoming = val.features.collected as MatrixFeature;
-      collected = acc.features.collected as MatrixFeature;
-      collected.data = arrayReducer<Float32Array>(
-	collected.data,
-	incoming.data
-      );
-      break;
-
-    case "list":
-      incoming = val.features.collected as FeatureList;
-      collected = acc.features.collected as FeatureList;
-      acc.features.collected = arrayReducer<Feature>(
-	collected,
-	incoming
-      );
-      break;
-      
-    case "tracks":
-      incoming = val.features.collected as TracksFeature;
-      collected = acc.features.collected as TracksFeature;
-      acc.features.collected = arrayReducer<VectorFeature>(
-	collected, incoming
-      );
-      break;
-
-    default:
-      throw new Error('Invalid feature output. Aborting');
-    }
-    
-    return acc;
-  };
+                                         val: StreamingResponse):
+  StreamingResponse => {
+  acc.progress = val.progress;
+  if (val.configuration) {
+    acc.configuration = val.configuration;
+  }
+  arrayReducer(acc.features, val.features);
+  return acc;
+};
--- a/src/app/services/feature-extraction/feature-extraction.service.ts	Thu May 11 11:46:07 2017 +0100
+++ b/src/app/services/feature-extraction/feature-extraction.service.ts	Fri May 12 08:46:12 2017 +0100
@@ -14,6 +14,7 @@
   WebWorkerStreamingClient
 } from 'piper/client-stubs/WebWorkerStreamingClient';
 import {RequestId} from 'piper/protocols/WebWorkerProtocol';
+import {collect, StreamingConfiguration} from "piper/StreamingService";
 
 type RepoUri = string;
 export interface AvailableLibraries {
@@ -66,19 +67,24 @@
   }
 
   extract(analysisItemId: string, request: SimpleRequest): Promise<void> {
-    return this.client.collect(request)
-      .do(val => {
-        if (val.totalBlockCount > 0) {
-          this.progressUpdated.next({
-            id: analysisItemId,
-            value: (val.processedBlockCount / val.totalBlockCount) * 100
-          });
-        }
-      })
-      .toPromise()
-      .then((response) => {
-        this.featuresExtracted.next(response);
+    let config: StreamingConfiguration;
+    return collect(this.client.process(request), val => {
+      if (val.configuration) {
+        config = val.configuration;
+      }
+      const progress = val.progress;
+      if (progress.totalBlockCount > 0) {
+        this.progressUpdated.next({
+          id: analysisItemId,
+          value: (progress.processedBlockCount / progress.totalBlockCount) * 100
+        });
+      }
+    }).then(features => {
+      this.featuresExtracted.next({
+        features: features,
+        outputDescriptor: config.outputDescriptor
       });
+    });
   }
 
   updateAvailableLibraries(): Observable<AvailableLibraries> {
--- a/src/app/waveform/waveform.component.ts	Thu May 11 11:46:07 2017 +0100
+++ b/src/app/waveform/waveform.component.ts	Fri May 12 08:46:12 2017 +0100
@@ -389,7 +389,7 @@
           isZooming = false;
           zoomGestureJustEnded = true;
         }
-       });
+      });
       element.addEventListener('touchmove', zoom);
     }
     // this.timeline.createTrack(track, height/2, `wave-${this.trackIdPrefix}`);
@@ -425,17 +425,17 @@
     }
     if (sample.length === 0) {
       console.log('WARNING: No samples gathered, even though we hoped for ' +
-                  (m_per * w) + ' of them');
+        (m_per * w) + ' of them');
       return 0.0;
     }
     sample.sort((a, b) => { return a - b; });
     const ix = Math.floor((sample.length * percentile) / 100);
     console.log('Estimating ' + percentile + '-%ile of ' +
-                n + '-sample dataset (' + w + ' x ' + h + ') as value ' + ix +
-                ' of sorted ' + sample.length + '-sample subset');
+      n + '-sample dataset (' + w + ' x ' + h + ') as value ' + ix +
+      ' of sorted ' + sample.length + '-sample subset');
     const estimate = sample[ix];
     console.log('Estimate is: ' + estimate + ' (where min sampled value = ' +
-                sample[0] + ' and max = ' + sample[sample.length - 1] + ')');
+      sample[0] + ' and max = ' + sample[sample.length - 1] + ')');
     return estimate;
   }
 
@@ -443,8 +443,8 @@
     const colours = hexColours.map(n => {
       const i = parseInt(n, 16);
       return [ ((i >> 16) & 255) / 255.0,
-               ((i >> 8) & 255) / 255.0,
-               ((i) & 255) / 255.0 ];
+        ((i >> 8) & 255) / 255.0,
+        ((i) & 255) / 255.0 ];
     });
     const last = colours.length - 1;
     return (value => {
@@ -461,8 +461,8 @@
       const c0 = colours[base];
       const c1 = colours[base + 1];
       return [ c0[0] * prop0 + c1[0] * prop1,
-               c0[1] * prop0 + c1[1] * prop1,
-               c0[2] * prop0 + c1[2] * prop1 ];
+        c0[1] * prop0 + c1[1] * prop1,
+        c0[2] * prop0 + c1[2] * prop1 ];
     });
   }
 
@@ -484,12 +484,12 @@
     const t = v * (1 - (1 - f) * s);
     let r = 0, g = 0, b = 0;
     switch (i % 6) {
-        case 0: r = v; g = t; b = p; break;
-        case 1: r = q; g = v; b = p; break;
-        case 2: r = p; g = v; b = t; break;
-        case 3: r = p; g = q; b = v; break;
-        case 4: r = t; g = p; b = v; break;
-        case 5: r = v; g = p; b = q; break;
+      case 0: r = v; g = t; b = p; break;
+      case 1: r = q; g = v; b = p; break;
+      case 2: r = p; g = v; b = t; break;
+      case 3: r = p; g = q; b = v; break;
+      case 4: r = t; g = p; b = v; break;
+      case 5: r = v; g = p; b = q; break;
     }
     return [ r, g, b ];
   }
@@ -610,8 +610,8 @@
   }
 
   private addLineLayer(stepDuration: number,
-		       featureData: Float32Array,
-		       colour: Colour) {
+                       featureData: Float32Array,
+                       colour: Colour) {
 
     if (featureData.length === 0) {
       return;
@@ -671,7 +671,7 @@
       this.timeline.timeContext
     );
   }
-  
+
   // TODO refactor - this doesn't belong here
   private renderFeatures(extracted: SimpleResponse, colour: Colour): void {
     if (this.isOneShotExtractor && !this.hasShot) {
@@ -680,11 +680,11 @@
     }
 
     if (!extracted.hasOwnProperty('features')
-        || !extracted.hasOwnProperty('outputDescriptor')) {
+      || !extracted.hasOwnProperty('outputDescriptor')) {
       return;
     }
     if (!extracted.features.hasOwnProperty('shape')
-        || !extracted.features.hasOwnProperty('collected')) {
+      || !extracted.features.hasOwnProperty('collected')) {
       return;
     }
     const features: FeatureCollection = (extracted.features as FeatureCollection);
@@ -697,15 +697,15 @@
 
       case 'vector': {
         const collected = features.collected as VectorFeature;
-	const startTime = collected.startTime; //!!! + make use of
-	const stepDuration = collected.stepDuration;
-	const featureData = collected.data;
-	this.addLineLayer(stepDuration, featureData, colour);
-	break;
+        const startTime = collected.startTime; //!!! + make use of
+        const stepDuration = collected.stepDuration;
+        const featureData = collected.data;
+        this.addLineLayer(stepDuration, featureData, colour);
+        break;
       }
-      
+
       case 'list': {
-	const featureData = features.collected as FeatureList;
+        const featureData = features.collected as FeatureList;
         if (featureData.length === 0) {
           return;
         }
@@ -717,8 +717,8 @@
         const isRegion = hasDuration
           && featureData[0].timestamp != null;
         console.log('Have list features: length ' + featureData.length +
-                    ', isMarker ' + isMarker + ', isRegion ' + isRegion +
-                    ', hasDuration ' + hasDuration);
+          ', isMarker ' + isMarker + ', isRegion ' + isRegion +
+          ', hasDuration ' + hasDuration);
         // TODO refactor, this is incomprehensible
         if (isMarker) {
           const plotData = featureData.map(feature => ({
@@ -807,7 +807,7 @@
         break;
       }
       case 'matrix': {
-	const collected = features.collected as MatrixFeature;
+        const collected = features.collected as MatrixFeature;
         const startTime = collected.startTime; //!!! + make use of
         const stepDuration = collected.stepDuration;
         const matrixData = collected.data;
@@ -823,8 +823,8 @@
         console.log('setting gain to ' + gain);
         const matrixEntity =
           new wavesUI.utils.PrefilledMatrixEntity(matrixData,
-                                                  0, // startTime
-                                                  stepDuration);
+            0, // startTime
+            stepDuration);
         const matrixLayer = new wavesUI.helpers.MatrixLayer(matrixEntity, {
           gain,
           top: 0,
@@ -880,17 +880,17 @@
         if (mustPageForward) {
           const hasSkippedMultiplePages = offsetTimestamp - visibleDuration > visibleDuration;
 
-            this.timeline.timeContext.offset = hasSkippedMultiplePages ?
-                -currentTime +  0.5 * visibleDuration :
-                currentOffset - visibleDuration;
+          this.timeline.timeContext.offset = hasSkippedMultiplePages ?
+            -currentTime +  0.5 * visibleDuration :
+            currentOffset - visibleDuration;
           this.timeline.tracks.update();
         }
 
         if (mustPageBackward) {
           const hasSkippedMultiplePages = currentTime + visibleDuration < -currentOffset;
-            this.timeline.timeContext.offset = hasSkippedMultiplePages ?
-                -currentTime + 0.5 * visibleDuration :
-                currentOffset + visibleDuration;
+          this.timeline.timeContext.offset = hasSkippedMultiplePages ?
+            -currentTime + 0.5 * visibleDuration :
+            currentOffset + visibleDuration;
           this.timeline.tracks.update();
         }