annotate src/app/spectrogram/Spectrogram.ts @ 172:b15236b03a28

Add new components to bootstrapping.
author Lucas Thompson <dev@lucas.im>
date Tue, 21 Mar 2017 17:11:23 +0000
parents 4eb3cc32f6c0
children fab88270bccc
rev   line source
dev@129 1 /**
dev@129 2 * Created by lucast on 16/03/2017.
dev@129 3 */
dev@129 4 import {RealFft, KissRealFft} from "piper/fft/RealFft";
dev@129 5 import {hann} from "piper/FftUtilities";
dev@129 6 import {Framing} from "piper";
dev@129 7 import Waves from 'waves-ui';
dev@129 8
dev@129 9 class SpectrogramEntity extends Waves.utils.MatrixEntity {
dev@129 10
dev@129 11 private samples: Float32Array;
dev@129 12 private framing: Framing;
dev@129 13 private fft: RealFft;
dev@129 14 private real: Float32Array;
dev@129 15 private nCols: number;
cannam@130 16 private columnHeight: number;
dev@129 17 private window: Float32Array;
dev@129 18
dev@129 19 constructor(samples: Float32Array, options: Framing & Object) {
dev@129 20 super();
dev@129 21 this.samples = samples;
dev@129 22 this.framing = options;
dev@129 23 this.real = new Float32Array(this.framing.blockSize);
dev@129 24 this.nCols = Math.floor(this.samples.length / this.framing.stepSize); //!!! not correct
cannam@130 25 this.columnHeight = Math.round(this.framing.blockSize / 2) + 1;
dev@129 26 this.fft = new KissRealFft(this.framing.blockSize);
dev@129 27 this.window = hann(this.framing.blockSize);
dev@129 28 }
dev@129 29
cannam@131 30 dispose(): void {
cannam@131 31 this.fft.dispose();
cannam@131 32 }
cannam@131 33
dev@129 34 getColumnCount(): number {
dev@129 35 return this.nCols;
dev@129 36 }
dev@129 37
dev@129 38 getColumnHeight(): number {
cannam@130 39 return this.columnHeight;
dev@129 40 }
dev@129 41
dev@129 42 getColumn(n: number): Float32Array {
dev@129 43
dev@129 44 const startSample = n * this.framing.stepSize;
dev@129 45 const sz = this.framing.blockSize;
dev@129 46
dev@129 47 this.real.fill(0);
dev@129 48
dev@129 49 let available = sz;
dev@129 50 if (startSample + sz >= this.samples.length) {
dev@129 51 available = this.samples.length - startSample;
dev@129 52 }
dev@129 53
dev@129 54 for (let i = 0; i < available; ++i) {
dev@129 55 this.real[i] = this.samples[startSample + i] * this.window[i];
dev@129 56 }
dev@129 57
dev@129 58 const complex = this.fft.forward(this.real);
dev@129 59
dev@129 60 const h = this.getColumnHeight();
dev@129 61 const col = new Float32Array(h);
dev@129 62
cannam@130 63 const scale = 1.0 / Math.sqrt(sz);
dev@129 64 for (let i = 0; i < h; ++i) {
cannam@130 65 const re : number = complex[i*2] * scale;
cannam@130 66 const im : number = complex[i*2+1] * scale;
cannam@130 67 const mag = Math.sqrt(re * re + im * im);
cannam@130 68 col[i] = mag;
dev@129 69 }
dev@129 70
dev@129 71 return col;
dev@129 72 }
dev@129 73 }
dev@129 74
dev@129 75 export class WavesSpectrogramLayer extends Waves.core.Layer {
dev@129 76 constructor(buffer: AudioBuffer,
dev@129 77 options: Framing & Object) {
dev@129 78
dev@129 79 const defaults = {
dev@129 80 normalise: 'hybrid',
dev@129 81 gain: 40.0,
dev@129 82 channel: 0,
dev@129 83 stepSize: 512,
dev@129 84 blockSize: 1024
dev@129 85 };
dev@129 86
dev@129 87 const mergedOptions: Framing & Object & {channel: number} =
dev@129 88 Object.assign({}, defaults, options);
dev@129 89
dev@129 90 super('entity',
dev@129 91 new SpectrogramEntity(
dev@129 92 buffer.getChannelData(mergedOptions.channel),
dev@129 93 mergedOptions
dev@129 94 ),
dev@129 95 mergedOptions
dev@129 96 );
dev@129 97
dev@129 98 this.configureShape(Waves.shapes.Matrix, {}, mergedOptions);
dev@129 99 }
dev@129 100 }