Mercurial > hg > ugly-duckling
view src/app/spectrogram/Spectrogram.ts @ 509:041468f553e1 tip master
Merge pull request #57 from LucasThompson/fix/session-stack-max-call-stack
Fix accidental recursion in PersistentStack
author | Lucas Thompson <LucasThompson@users.noreply.github.com> |
---|---|
date | Mon, 27 Nov 2017 11:04:30 +0000 |
parents | c39df81c4dae |
children |
line wrap: on
line source
/** * Created by lucast on 16/03/2017. */ import {RealFft, KissRealFft, hann} from 'piper-js/fft'; import {KissFft} from 'piper-js/fft/KissFftModule'; import {Framing} from 'piper-js/core'; import Waves from 'waves-ui-piper'; class SpectrogramEntity extends Waves.utils.MatrixEntity { private samples: Float32Array; private sampleRate: number; private framing: Framing; private fft: RealFft; private real: Float32Array; private nCols: number; private columnHeight: number; private window: Float32Array; constructor(samples: Float32Array, options: Framing & Object, sampleRate: number) { super(); this.samples = samples; this.sampleRate = sampleRate; this.framing = options; this.real = new Float32Array(this.framing.blockSize); this.nCols = Math.floor(this.samples.length / this.framing.stepSize); // !!! not correct this.columnHeight = Math.round(this.framing.blockSize / 2) + 1; this.fft = new KissRealFft(this.framing.blockSize, KissFft); this.window = hann(this.framing.blockSize); } dispose(): void { this.fft.dispose(); } getColumnCount(): number { return this.nCols; } getColumnHeight(): number { return this.columnHeight; } getStepDuration(): number { return this.framing.stepSize / this.sampleRate; } 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); const scale = 1.0 / Math.sqrt(sz); for (let i = 0; i < h; ++i) { const re: number = complex[i * 2] * scale; const im: number = complex[i * 2 + 1] * scale; col[i] = Math.sqrt(re * re + im * im); } return col; } } export class WavesSpectrogramLayer extends Waves.core.Layer { constructor(bufferIn: AudioBuffer, options: Framing & Object) { const defaults = { normalise: 'hybrid', gain: 40.0, channel: -1, stepSize: 512, blockSize: 1024 }; const mergedOptions: Framing & Object & {channel: number} = Object.assign({}, defaults, options); const getSamples = ((buffer, channel) => { const nch = buffer.numberOfChannels; if (channel >= 0 || nch === 1) { if (channel < 0) { channel = 0; } return buffer.getChannelData(channel); } else { const before = performance.now(); console.log('mixing down ' + nch + ' channels for spectrogram...'); const mixed = Float32Array.from(buffer.getChannelData(0)); const n = mixed.length; for (let ch = 1; ch < nch; ++ch) { const buf = buffer.getChannelData(ch); for (let i = 0; i < n; ++i) { mixed[i] += buf[i]; } } const scale = 1.0 / nch; for (let i = 0; i < n; ++i) { mixed[i] *= scale; } console.log('done in ' + (performance.now() - before) + 'ms'); return mixed; } }); super( 'entity', new SpectrogramEntity(getSamples(bufferIn, mergedOptions.channel), mergedOptions, bufferIn.sampleRate), mergedOptions ); this.configureShape(Waves.shapes.Matrix, {}, mergedOptions); } }