summaryrefslogtreecommitdiff
path: root/tools/mq_editor/mq_extract.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mq_editor/mq_extract.js')
-rw-r--r--tools/mq_editor/mq_extract.js60
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