diff options
Diffstat (limited to 'tools/mq_editor/mq_extract.js')
| -rw-r--r-- | tools/mq_editor/mq_extract.js | 60 |
1 files changed, 13 insertions, 47 deletions
diff --git a/tools/mq_editor/mq_extract.js b/tools/mq_editor/mq_extract.js index c7ead4d..237f1ab 100644 --- a/tools/mq_editor/mq_extract.js +++ b/tools/mq_editor/mq_extract.js @@ -2,21 +2,17 @@ // McAulay-Quatieri sinusoidal analysis // Extract partials from audio buffer -function extractPartials(audioBuffer, params) { - const {fftSize, hopSize, threshold, sampleRate} = params; +function extractPartials(params, stftCache) { + const {fftSize, threshold, sampleRate} = params; - // Get mono channel (mix to mono if stereo) - const signal = getMono(audioBuffer); - const numFrames = Math.floor((signal.length - fftSize) / hopSize); - - // Analyze frames + // Analyze frames from cache const frames = []; + const numFrames = stftCache.getNumFrames(); for (let i = 0; i < numFrames; ++i) { - const offset = i * hopSize; - const frame = signal.slice(offset, offset + fftSize); - const peaks = detectPeaks(frame, fftSize, sampleRate, threshold); - const time = offset / sampleRate; - frames.push({time, peaks}); + const cachedFrame = stftCache.getFrameAtIndex(i); + const squaredAmp = stftCache.getSquaredAmplitude(cachedFrame.time); + const peaks = detectPeaks(squaredAmp, fftSize, sampleRate, threshold); + frames.push({time: cachedFrame.time, peaks}); } // Track trajectories @@ -28,45 +24,15 @@ function extractPartials(audioBuffer, params) { partial.ampCurve = fitBezier(partial.times, partial.amps); } - return partials; -} - -// Get mono signal -function getMono(audioBuffer) { - const data = audioBuffer.getChannelData(0); - if (audioBuffer.numberOfChannels === 1) { - return data; - } - - // Mix to mono - const left = audioBuffer.getChannelData(0); - const right = audioBuffer.getChannelData(1); - const mono = new Float32Array(left.length); - for (let i = 0; i < left.length; ++i) { - mono[i] = (left[i] + right[i]) * 0.5; - } - return mono; + return {partials, frames}; } -// Detect peaks in FFT frame -function detectPeaks(frame, fftSize, sampleRate, thresholdDB) { - // Apply Hann window - const windowed = new Float32Array(fftSize); - for (let i = 0; i < fftSize; ++i) { - const w = 0.5 - 0.5 * Math.cos(2 * Math.PI * i / fftSize); - windowed[i] = frame[i] * w; - } - - // FFT (using built-in) - const spectrum = realFFT(windowed); - - // Convert to magnitude dB +// Detect peaks in FFT frame (squaredAmp is pre-computed cached re*re+im*im) +function detectPeaks(squaredAmp, fftSize, sampleRate, thresholdDB) { + // Convert squared amplitude to dB (10*log10 == 20*log10 of magnitude) const mag = new Float32Array(fftSize / 2); for (let i = 0; i < fftSize / 2; ++i) { - const re = spectrum[i * 2]; - const im = spectrum[i * 2 + 1]; - const magLin = Math.sqrt(re * re + im * im); - mag[i] = 20 * Math.log10(Math.max(magLin, 1e-10)); + mag[i] = 10 * Math.log10(Math.max(squaredAmp[i], 1e-20)); } // Find local maxima above threshold |
