@octoseq/mir 0.1.0-main.4ecb074 → 0.1.0-main.5fdb072
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-HF3QHCRK.js +3234 -0
- package/dist/chunk-HF3QHCRK.js.map +1 -0
- package/dist/index.d.ts +2122 -47
- package/dist/index.js +3524 -39
- package/dist/index.js.map +1 -1
- package/dist/runMir-CnJQbBr8.d.ts +187 -0
- package/dist/runner/runMir.d.ts +2 -2
- package/dist/runner/runMir.js +1 -1
- package/dist/runner/workerProtocol.d.ts +8 -1
- package/dist/runner/workerProtocol.js.map +1 -1
- package/dist/types-DqH4umN8.d.ts +761 -0
- package/package.json +2 -2
- package/src/dsp/activity.ts +544 -0
- package/src/dsp/bandCqt.ts +662 -0
- package/src/dsp/bandEvents.ts +351 -0
- package/src/dsp/bandMask.ts +225 -0
- package/src/dsp/bandMir.ts +524 -0
- package/src/dsp/bandProposal.ts +552 -0
- package/src/dsp/beatCandidates.ts +299 -0
- package/src/dsp/cqt.ts +386 -0
- package/src/dsp/cqtSignals.ts +462 -0
- package/src/dsp/customSignalReduction.ts +841 -0
- package/src/dsp/eventToSignal.ts +531 -0
- package/src/dsp/frequencyBand.ts +956 -0
- package/src/dsp/mel.ts +56 -3
- package/src/dsp/musicalTime.ts +240 -0
- package/src/dsp/onset.ts +296 -30
- package/src/dsp/peakPicking.ts +519 -0
- package/src/dsp/phaseAlignment.ts +153 -0
- package/src/dsp/pitch.ts +289 -0
- package/src/dsp/resample.ts +44 -0
- package/src/dsp/signalTransforms.ts +660 -0
- package/src/dsp/silenceGating.ts +511 -0
- package/src/dsp/spectral.ts +124 -4
- package/src/dsp/tempoHypotheses.ts +395 -0
- package/src/gpu/bufferPool.ts +266 -0
- package/src/gpu/context.ts +30 -6
- package/src/gpu/helpers.ts +85 -0
- package/src/gpu/melProject.ts +83 -0
- package/src/index.ts +366 -5
- package/src/runner/runMir.ts +234 -2
- package/src/runner/workerProtocol.ts +9 -1
- package/src/types.ts +768 -3
- package/dist/chunk-DUWYCAVG.js +0 -1525
- package/dist/chunk-DUWYCAVG.js.map +0 -1
- package/dist/runMir-CSIBwNZ3.d.ts +0 -84
- package/dist/types-BE3py4fZ.d.ts +0 -83
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CQT-Derived 1D Signals for F5.
|
|
3
|
+
*
|
|
4
|
+
* These signals extract musically meaningful 1D features from the CQT representation:
|
|
5
|
+
* - harmonicEnergy: Measures tonal presence vs noise
|
|
6
|
+
* - bassPitchMotion: Measures bassline activity and low-end groove
|
|
7
|
+
* - tonalStability: Measures harmonic stability vs modulation
|
|
8
|
+
*
|
|
9
|
+
* All signals are:
|
|
10
|
+
* - 1D (one value per frame)
|
|
11
|
+
* - Time-aligned with the CQT frames
|
|
12
|
+
* - Deterministic
|
|
13
|
+
* - Normalized to [0, 1] range
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { CqtSignalId, CqtSignalResult, CqtSpectrogram, MirRunMeta } from "../types";
|
|
17
|
+
import { cqtBinToHz, hzToCqtBin } from "./cqt";
|
|
18
|
+
|
|
19
|
+
// ----------------------------
|
|
20
|
+
// Configuration Constants
|
|
21
|
+
// ----------------------------
|
|
22
|
+
|
|
23
|
+
/** Bass frequency range for bassPitchMotion */
|
|
24
|
+
const BASS_MIN_HZ = 20;
|
|
25
|
+
const BASS_MAX_HZ = 300;
|
|
26
|
+
|
|
27
|
+
/** Window size for tonal stability analysis (in frames) */
|
|
28
|
+
const TONAL_STABILITY_WINDOW_FRAMES = 20; // ~500ms at typical hop sizes
|
|
29
|
+
|
|
30
|
+
/** Number of chroma bins (one per semitone) */
|
|
31
|
+
const CHROMA_BINS = 12;
|
|
32
|
+
|
|
33
|
+
// ----------------------------
|
|
34
|
+
// Utility Functions
|
|
35
|
+
// ----------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Normalize an array to [0, 1] range using min-max scaling.
|
|
39
|
+
*/
|
|
40
|
+
function normalizeMinMax(values: Float32Array): Float32Array {
|
|
41
|
+
let min = Infinity;
|
|
42
|
+
let max = -Infinity;
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < values.length; i++) {
|
|
45
|
+
const v = values[i] ?? 0;
|
|
46
|
+
if (v < min) min = v;
|
|
47
|
+
if (v > max) max = v;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const range = max - min;
|
|
51
|
+
const result = new Float32Array(values.length);
|
|
52
|
+
|
|
53
|
+
if (range > 0) {
|
|
54
|
+
for (let i = 0; i < values.length; i++) {
|
|
55
|
+
result[i] = ((values[i] ?? 0) - min) / range;
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
// All values are the same - return 0.5
|
|
59
|
+
result.fill(0.5);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Compute the weighted centroid of an array.
|
|
67
|
+
*/
|
|
68
|
+
function weightedCentroid(values: Float32Array, startIndex: number = 0): number {
|
|
69
|
+
let sumWeighted = 0;
|
|
70
|
+
let sumWeights = 0;
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < values.length; i++) {
|
|
73
|
+
const weight = values[i] ?? 0;
|
|
74
|
+
sumWeighted += (startIndex + i) * weight;
|
|
75
|
+
sumWeights += weight;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return sumWeights > 0 ? sumWeighted / sumWeights : startIndex + values.length / 2;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Compute variance of an array.
|
|
83
|
+
*/
|
|
84
|
+
function variance(values: Float32Array): number {
|
|
85
|
+
if (values.length === 0) return 0;
|
|
86
|
+
|
|
87
|
+
let sum = 0;
|
|
88
|
+
for (let i = 0; i < values.length; i++) {
|
|
89
|
+
sum += values[i] ?? 0;
|
|
90
|
+
}
|
|
91
|
+
const mean = sum / values.length;
|
|
92
|
+
|
|
93
|
+
let sumSquaredDiff = 0;
|
|
94
|
+
for (let i = 0; i < values.length; i++) {
|
|
95
|
+
const diff = (values[i] ?? 0) - mean;
|
|
96
|
+
sumSquaredDiff += diff * diff;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return sumSquaredDiff / values.length;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ----------------------------
|
|
103
|
+
// Harmonic Energy
|
|
104
|
+
// ----------------------------
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Compute harmonic energy for a single CQT frame.
|
|
108
|
+
*
|
|
109
|
+
* Harmonic energy measures the ratio of energy concentrated at harmonic
|
|
110
|
+
* intervals (integer multiples of a fundamental) vs total energy.
|
|
111
|
+
*
|
|
112
|
+
* Algorithm:
|
|
113
|
+
* 1. Find the strongest bin as a candidate fundamental
|
|
114
|
+
* 2. Sum energy at harmonic positions (2x, 3x, 4x, ... of fundamental)
|
|
115
|
+
* 3. Divide by total energy
|
|
116
|
+
*
|
|
117
|
+
* High values indicate tonal/harmonic content; low values indicate noise.
|
|
118
|
+
*/
|
|
119
|
+
function computeHarmonicEnergyFrame(
|
|
120
|
+
frame: Float32Array,
|
|
121
|
+
cqt: CqtSpectrogram
|
|
122
|
+
): number {
|
|
123
|
+
if (frame.length === 0) return 0;
|
|
124
|
+
|
|
125
|
+
// Find total energy
|
|
126
|
+
let totalEnergy = 0;
|
|
127
|
+
for (let i = 0; i < frame.length; i++) {
|
|
128
|
+
const mag = frame[i] ?? 0;
|
|
129
|
+
totalEnergy += mag * mag;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (totalEnergy === 0) return 0;
|
|
133
|
+
|
|
134
|
+
// Find the strongest bin as fundamental candidate
|
|
135
|
+
let maxMag = 0;
|
|
136
|
+
let fundamentalBin = 0;
|
|
137
|
+
for (let i = 0; i < frame.length; i++) {
|
|
138
|
+
const mag = frame[i] ?? 0;
|
|
139
|
+
if (mag > maxMag) {
|
|
140
|
+
maxMag = mag;
|
|
141
|
+
fundamentalBin = i;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const fundamentalFreq = cqtBinToHz(fundamentalBin, cqt.config);
|
|
146
|
+
|
|
147
|
+
// Sum energy at harmonic positions
|
|
148
|
+
let harmonicEnergy = 0;
|
|
149
|
+
const numHarmonics = 6; // Check first 6 harmonics
|
|
150
|
+
|
|
151
|
+
for (let h = 1; h <= numHarmonics; h++) {
|
|
152
|
+
const harmonicFreq = fundamentalFreq * h;
|
|
153
|
+
const harmonicBin = Math.round(hzToCqtBin(harmonicFreq, cqt.config));
|
|
154
|
+
|
|
155
|
+
if (harmonicBin >= 0 && harmonicBin < frame.length) {
|
|
156
|
+
const mag = frame[harmonicBin] ?? 0;
|
|
157
|
+
// Weight lower harmonics more heavily
|
|
158
|
+
const weight = 1 / h;
|
|
159
|
+
harmonicEnergy += mag * mag * weight;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Normalize by expected harmonic weight sum
|
|
164
|
+
let weightSum = 0;
|
|
165
|
+
for (let h = 1; h <= numHarmonics; h++) {
|
|
166
|
+
weightSum += 1 / h;
|
|
167
|
+
}
|
|
168
|
+
harmonicEnergy /= weightSum;
|
|
169
|
+
|
|
170
|
+
// Return ratio of harmonic to total energy
|
|
171
|
+
return Math.min(1, harmonicEnergy / totalEnergy);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Compute harmonic energy signal from CQT.
|
|
176
|
+
*
|
|
177
|
+
* Measures sustained, pitch-aligned energy across harmonic bins.
|
|
178
|
+
* Intended to capture "tonal presence" vs noise.
|
|
179
|
+
*/
|
|
180
|
+
export function harmonicEnergy(cqt: CqtSpectrogram): CqtSignalResult {
|
|
181
|
+
const startTime = performance.now();
|
|
182
|
+
const nFrames = cqt.magnitudes.length;
|
|
183
|
+
const values = new Float32Array(nFrames);
|
|
184
|
+
|
|
185
|
+
for (let frame = 0; frame < nFrames; frame++) {
|
|
186
|
+
const cqtFrame = cqt.magnitudes[frame];
|
|
187
|
+
if (cqtFrame) {
|
|
188
|
+
values[frame] = computeHarmonicEnergyFrame(cqtFrame, cqt);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Normalize to [0, 1]
|
|
193
|
+
const normalized = normalizeMinMax(values);
|
|
194
|
+
|
|
195
|
+
const endTime = performance.now();
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
kind: "cqt1d",
|
|
199
|
+
signalId: "harmonicEnergy",
|
|
200
|
+
times: cqt.times,
|
|
201
|
+
values: normalized,
|
|
202
|
+
meta: {
|
|
203
|
+
backend: "cpu",
|
|
204
|
+
usedGpu: false,
|
|
205
|
+
timings: {
|
|
206
|
+
totalMs: endTime - startTime,
|
|
207
|
+
cpuMs: endTime - startTime,
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ----------------------------
|
|
214
|
+
// Bass Pitch Motion
|
|
215
|
+
// ----------------------------
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Compute bass pitch motion signal from CQT.
|
|
219
|
+
*
|
|
220
|
+
* Measures rate and magnitude of pitch movement in low-frequency CQT bins.
|
|
221
|
+
* Intended to capture bassline motion, groove, and low-end activity.
|
|
222
|
+
*
|
|
223
|
+
* Algorithm:
|
|
224
|
+
* 1. Extract bass-range bins from each frame
|
|
225
|
+
* 2. Compute pitch centroid in bass range
|
|
226
|
+
* 3. Compute absolute difference between consecutive frames
|
|
227
|
+
*/
|
|
228
|
+
export function bassPitchMotion(cqt: CqtSpectrogram): CqtSignalResult {
|
|
229
|
+
const startTime = performance.now();
|
|
230
|
+
const nFrames = cqt.magnitudes.length;
|
|
231
|
+
|
|
232
|
+
// Find bass bin range
|
|
233
|
+
const bassStartBin = Math.max(0, Math.floor(hzToCqtBin(BASS_MIN_HZ, cqt.config)));
|
|
234
|
+
const bassEndBin = Math.min(
|
|
235
|
+
cqt.magnitudes[0]?.length ?? 0,
|
|
236
|
+
Math.ceil(hzToCqtBin(BASS_MAX_HZ, cqt.config))
|
|
237
|
+
);
|
|
238
|
+
const bassNumBins = bassEndBin - bassStartBin;
|
|
239
|
+
|
|
240
|
+
if (bassNumBins <= 0) {
|
|
241
|
+
// No bass bins available
|
|
242
|
+
return {
|
|
243
|
+
kind: "cqt1d",
|
|
244
|
+
signalId: "bassPitchMotion",
|
|
245
|
+
times: cqt.times,
|
|
246
|
+
values: new Float32Array(nFrames),
|
|
247
|
+
meta: {
|
|
248
|
+
backend: "cpu",
|
|
249
|
+
usedGpu: false,
|
|
250
|
+
timings: { totalMs: 0, cpuMs: 0 },
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Compute bass centroid for each frame
|
|
256
|
+
const centroids = new Float32Array(nFrames);
|
|
257
|
+
|
|
258
|
+
for (let frame = 0; frame < nFrames; frame++) {
|
|
259
|
+
const cqtFrame = cqt.magnitudes[frame];
|
|
260
|
+
if (!cqtFrame) continue;
|
|
261
|
+
|
|
262
|
+
// Extract bass bins
|
|
263
|
+
const bassBins = new Float32Array(bassNumBins);
|
|
264
|
+
for (let i = 0; i < bassNumBins; i++) {
|
|
265
|
+
bassBins[i] = cqtFrame[bassStartBin + i] ?? 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Compute weighted centroid
|
|
269
|
+
centroids[frame] = weightedCentroid(bassBins, bassStartBin);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Compute motion as absolute difference between consecutive frames
|
|
273
|
+
const motion = new Float32Array(nFrames);
|
|
274
|
+
for (let frame = 1; frame < nFrames; frame++) {
|
|
275
|
+
motion[frame] = Math.abs((centroids[frame] ?? 0) - (centroids[frame - 1] ?? 0));
|
|
276
|
+
}
|
|
277
|
+
motion[0] = motion[1] ?? 0; // First frame has no previous
|
|
278
|
+
|
|
279
|
+
// Normalize to [0, 1]
|
|
280
|
+
const normalized = normalizeMinMax(motion);
|
|
281
|
+
|
|
282
|
+
const endTime = performance.now();
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
kind: "cqt1d",
|
|
286
|
+
signalId: "bassPitchMotion",
|
|
287
|
+
times: cqt.times,
|
|
288
|
+
values: normalized,
|
|
289
|
+
meta: {
|
|
290
|
+
backend: "cpu",
|
|
291
|
+
usedGpu: false,
|
|
292
|
+
timings: {
|
|
293
|
+
totalMs: endTime - startTime,
|
|
294
|
+
cpuMs: endTime - startTime,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ----------------------------
|
|
301
|
+
// Tonal Stability
|
|
302
|
+
// ----------------------------
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Fold CQT bins into chroma (12 semitones).
|
|
306
|
+
*/
|
|
307
|
+
function computeChroma(frame: Float32Array, binsPerOctave: number): Float32Array {
|
|
308
|
+
const chroma = new Float32Array(CHROMA_BINS);
|
|
309
|
+
const binsPerSemitone = binsPerOctave / CHROMA_BINS;
|
|
310
|
+
|
|
311
|
+
for (let i = 0; i < frame.length; i++) {
|
|
312
|
+
// Map CQT bin to chroma bin
|
|
313
|
+
const chromaBin = Math.floor((i % binsPerOctave) / binsPerSemitone) % CHROMA_BINS;
|
|
314
|
+
const mag = frame[i] ?? 0;
|
|
315
|
+
chroma[chromaBin] = (chroma[chromaBin] ?? 0) + mag * mag; // Energy
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Normalize
|
|
319
|
+
let sum = 0;
|
|
320
|
+
for (let i = 0; i < CHROMA_BINS; i++) {
|
|
321
|
+
sum += chroma[i] ?? 0;
|
|
322
|
+
}
|
|
323
|
+
if (sum > 0) {
|
|
324
|
+
for (let i = 0; i < CHROMA_BINS; i++) {
|
|
325
|
+
chroma[i] = (chroma[i] ?? 0) / sum;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return chroma;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Compute tonal stability signal from CQT.
|
|
334
|
+
*
|
|
335
|
+
* Measures consistency of dominant pitch structure over time.
|
|
336
|
+
* High values imply harmonic stability; low values imply modulation or noise.
|
|
337
|
+
*
|
|
338
|
+
* Algorithm:
|
|
339
|
+
* 1. Compute chroma histogram for each frame
|
|
340
|
+
* 2. Over a sliding window, compute variance of chroma distribution
|
|
341
|
+
* 3. Low variance = stable tonality; high variance = unstable
|
|
342
|
+
* 4. Invert so that high values = stable
|
|
343
|
+
*/
|
|
344
|
+
export function tonalStability(cqt: CqtSpectrogram): CqtSignalResult {
|
|
345
|
+
const startTime = performance.now();
|
|
346
|
+
const nFrames = cqt.magnitudes.length;
|
|
347
|
+
|
|
348
|
+
// Compute chroma for each frame
|
|
349
|
+
const chromas: Float32Array[] = new Array(nFrames);
|
|
350
|
+
for (let frame = 0; frame < nFrames; frame++) {
|
|
351
|
+
const cqtFrame = cqt.magnitudes[frame];
|
|
352
|
+
if (cqtFrame) {
|
|
353
|
+
chromas[frame] = computeChroma(cqtFrame, cqt.binsPerOctave);
|
|
354
|
+
} else {
|
|
355
|
+
chromas[frame] = new Float32Array(CHROMA_BINS);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Compute stability over sliding window
|
|
360
|
+
const halfWindow = Math.floor(TONAL_STABILITY_WINDOW_FRAMES / 2);
|
|
361
|
+
const instability = new Float32Array(nFrames);
|
|
362
|
+
|
|
363
|
+
for (let frame = 0; frame < nFrames; frame++) {
|
|
364
|
+
// Define window bounds
|
|
365
|
+
const windowStart = Math.max(0, frame - halfWindow);
|
|
366
|
+
const windowEnd = Math.min(nFrames, frame + halfWindow + 1);
|
|
367
|
+
const windowSize = windowEnd - windowStart;
|
|
368
|
+
|
|
369
|
+
// Compute average chroma over window
|
|
370
|
+
const avgChroma = new Float32Array(CHROMA_BINS);
|
|
371
|
+
for (let w = windowStart; w < windowEnd; w++) {
|
|
372
|
+
const chroma = chromas[w];
|
|
373
|
+
if (chroma) {
|
|
374
|
+
for (let c = 0; c < CHROMA_BINS; c++) {
|
|
375
|
+
avgChroma[c] = (avgChroma[c] ?? 0) + (chroma[c] ?? 0);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
for (let c = 0; c < CHROMA_BINS; c++) {
|
|
380
|
+
avgChroma[c] = (avgChroma[c] ?? 0) / windowSize;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Compute variance of chroma values within window
|
|
384
|
+
let totalVariance = 0;
|
|
385
|
+
for (let w = windowStart; w < windowEnd; w++) {
|
|
386
|
+
const chroma = chromas[w];
|
|
387
|
+
if (chroma) {
|
|
388
|
+
for (let c = 0; c < CHROMA_BINS; c++) {
|
|
389
|
+
const diff = (chroma[c] ?? 0) - (avgChroma[c] ?? 0);
|
|
390
|
+
totalVariance += diff * diff;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
totalVariance /= windowSize * CHROMA_BINS;
|
|
395
|
+
|
|
396
|
+
instability[frame] = totalVariance;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Normalize instability to [0, 1]
|
|
400
|
+
const normalizedInstability = normalizeMinMax(instability);
|
|
401
|
+
|
|
402
|
+
// Invert to get stability (high stability = low variance)
|
|
403
|
+
const stability = new Float32Array(nFrames);
|
|
404
|
+
for (let frame = 0; frame < nFrames; frame++) {
|
|
405
|
+
stability[frame] = 1 - (normalizedInstability[frame] ?? 0);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const endTime = performance.now();
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
kind: "cqt1d",
|
|
412
|
+
signalId: "tonalStability",
|
|
413
|
+
times: cqt.times,
|
|
414
|
+
values: stability,
|
|
415
|
+
meta: {
|
|
416
|
+
backend: "cpu",
|
|
417
|
+
usedGpu: false,
|
|
418
|
+
timings: {
|
|
419
|
+
totalMs: endTime - startTime,
|
|
420
|
+
cpuMs: endTime - startTime,
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ----------------------------
|
|
427
|
+
// Unified Signal Computation
|
|
428
|
+
// ----------------------------
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Compute a CQT-derived signal by ID.
|
|
432
|
+
*/
|
|
433
|
+
export function computeCqtSignal(
|
|
434
|
+
cqt: CqtSpectrogram,
|
|
435
|
+
signalId: CqtSignalId
|
|
436
|
+
): CqtSignalResult {
|
|
437
|
+
switch (signalId) {
|
|
438
|
+
case "harmonicEnergy":
|
|
439
|
+
return harmonicEnergy(cqt);
|
|
440
|
+
case "bassPitchMotion":
|
|
441
|
+
return bassPitchMotion(cqt);
|
|
442
|
+
case "tonalStability":
|
|
443
|
+
return tonalStability(cqt);
|
|
444
|
+
default:
|
|
445
|
+
throw new Error(`@octoseq/mir: unknown CQT signal ID: ${signalId}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Compute all CQT-derived signals.
|
|
451
|
+
*/
|
|
452
|
+
export function computeAllCqtSignals(
|
|
453
|
+
cqt: CqtSpectrogram
|
|
454
|
+
): Map<CqtSignalId, CqtSignalResult> {
|
|
455
|
+
const results = new Map<CqtSignalId, CqtSignalResult>();
|
|
456
|
+
|
|
457
|
+
results.set("harmonicEnergy", harmonicEnergy(cqt));
|
|
458
|
+
results.set("bassPitchMotion", bassPitchMotion(cqt));
|
|
459
|
+
results.set("tonalStability", tonalStability(cqt));
|
|
460
|
+
|
|
461
|
+
return results;
|
|
462
|
+
}
|