changeset 129:161af71c80d4

Module for spectrogram stuff. Consume the WavesSpectrogramLayer in Waveform... of course.
author Lucas Thompson <dev@lucas.im>
date Thu, 16 Mar 2017 10:35:53 +0000
parents 576e7e0246cb
children 8aa1ff061503 e50248f9cda3
files src/app/spectrogram/Spectrogram.ts src/app/waveform/waveform.component.ts
diffstat 2 files changed, 95 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/spectrogram/Spectrogram.ts	Thu Mar 16 10:35:53 2017 +0000
@@ -0,0 +1,92 @@
+/**
+ * Created by lucast on 16/03/2017.
+ */
+import {RealFft, KissRealFft} from "piper/fft/RealFft";
+import {hann} from "piper/FftUtilities";
+import {Framing} from "piper";
+import Waves from 'waves-ui';
+
+class SpectrogramEntity extends Waves.utils.MatrixEntity {
+
+  private samples: Float32Array;
+  private framing: Framing;
+  private fft: RealFft;
+  private real: Float32Array;
+  private nCols: number;
+  private window: Float32Array;
+
+  constructor(samples: Float32Array, options: Framing & Object) {
+    super();
+    this.samples = samples;
+    this.framing = options;
+    this.real = new Float32Array(this.framing.blockSize);
+    this.nCols = Math.floor(this.samples.length / this.framing.stepSize); //!!! not correct
+    this.fft = new KissRealFft(this.framing.blockSize);
+    this.window = hann(this.framing.blockSize);
+  }
+
+  getColumnCount(): number {
+    return this.nCols;
+  }
+
+  getColumnHeight(): number {
+    return Math.floor(this.framing.blockSize * 0.5) + 1;
+  }
+
+  getColumn(n: number): Float32Array {
+
+    const startSample = n * this.framing.stepSize;
+    const sz = this.framing.blockSize;
+
+    this.real.fill(0);
+
+    let available = sz;
+    if (startSample + sz >= this.samples.length) {
+      available = this.samples.length - startSample;
+    }
+
+    for (let i = 0; i < available; ++i) {
+      this.real[i] = this.samples[startSample + i] * this.window[i];
+    }
+
+    const complex = this.fft.forward(this.real);
+
+    const h = this.getColumnHeight();
+    const col = new Float32Array(h);
+
+    for (let i = 0; i < h; ++i) {
+      const real: number = complex[i * 2];
+      const imaginary: number = complex[i * 2 + 1];
+      col[i] = real * real + imaginary * imaginary;
+    }
+
+    return col;
+  }
+}
+
+export class WavesSpectrogramLayer extends Waves.core.Layer {
+  constructor(buffer: AudioBuffer,
+              options: Framing & Object) {
+
+    const defaults = {
+      normalise: 'hybrid',
+      gain: 40.0,
+      channel: 0,
+      stepSize: 512,
+      blockSize: 1024
+    };
+
+    const mergedOptions: Framing & Object & {channel: number} =
+      Object.assign({}, defaults, options);
+
+    super('entity',
+      new SpectrogramEntity(
+        buffer.getChannelData(mergedOptions.channel),
+        mergedOptions
+      ),
+      mergedOptions
+    );
+
+    this.configureShape(Waves.shapes.Matrix, {}, mergedOptions);
+  }
+}
--- a/src/app/waveform/waveform.component.ts	Thu Mar 16 10:34:38 2017 +0000
+++ b/src/app/waveform/waveform.component.ts	Thu Mar 16 10:35:53 2017 +0000
@@ -15,6 +15,7 @@
 import {toSeconds} from "piper";
 import {FeatureList, Feature} from "piper/Feature";
 import * as Hammer from 'hammerjs';
+import {WavesSpectrogramLayer} from "../spectrogram/Spectrogram";
 
 type Timeline = any; // TODO what type actually is it.. start a .d.ts for waves-ui?
 type Layer = any;
@@ -379,11 +380,11 @@
     const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height / 2;
     const gridTrack = this.timeline.getTrackById('grid');
 
-    const spectrogramLayer = new wavesUI.helpers.SpectrogramLayer(buffer, {
+    const spectrogramLayer = new WavesSpectrogramLayer(buffer, {
       top: height * 0.05,
       height: height * 0.9,
       stepSize: 512,
-      fftSize: 1024,
+      blockSize: 1024,
       normalise: 'none',
       mapper: this.sunsetMapper()
     });