@libraz/libsonare 1.2.3 → 1.3.1

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.
Files changed (43) hide show
  1. package/README.md +38 -1
  2. package/dist/index.d.ts +1 -2842
  3. package/dist/index.js +3667 -1984
  4. package/dist/index.js.map +1 -1
  5. package/dist/sonare-rt-module.js +1 -1
  6. package/dist/sonare-rt.js +1 -1
  7. package/dist/sonare-rt.wasm +0 -0
  8. package/dist/sonare.js +1 -1
  9. package/dist/sonare.wasm +0 -0
  10. package/dist/worklet.d.ts +4816 -483
  11. package/dist/worklet.js +747 -440
  12. package/dist/worklet.js.map +1 -1
  13. package/package.json +2 -1
  14. package/src/analysis_helpers.ts +152 -0
  15. package/src/audio.ts +493 -0
  16. package/src/codes.ts +56 -0
  17. package/src/effects_mastering.ts +964 -0
  18. package/src/feature_core.ts +248 -0
  19. package/src/feature_music.ts +419 -0
  20. package/src/feature_pitch.ts +80 -0
  21. package/src/feature_resample.ts +21 -0
  22. package/src/feature_spectral.ts +330 -0
  23. package/src/feature_spectrogram.ts +454 -0
  24. package/src/features.ts +84 -0
  25. package/src/index.ts +341 -4963
  26. package/src/live_audio.ts +47 -0
  27. package/src/metering.ts +380 -0
  28. package/src/mixer.ts +523 -0
  29. package/src/module_state.ts +14 -0
  30. package/src/opfs_clip_pages.ts +203 -0
  31. package/src/project.ts +1614 -0
  32. package/src/public_types.ts +177 -2
  33. package/src/quick_analysis.ts +508 -0
  34. package/src/realtime_engine.ts +667 -0
  35. package/src/realtime_voice_changer.ts +275 -0
  36. package/src/scale.ts +42 -0
  37. package/src/sonare.js.d.ts +302 -4
  38. package/src/stream_analyzer.ts +275 -0
  39. package/src/stream_types.ts +26 -1
  40. package/src/streaming_mixing.ts +18 -0
  41. package/src/streaming_processors.ts +335 -0
  42. package/src/validation.ts +82 -0
  43. package/src/web_midi.ts +366 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libraz/libsonare",
3
- "version": "1.2.3",
3
+ "version": "1.3.1",
4
4
  "type": "module",
5
5
  "packageManager": "yarn@4.15.0",
6
6
  "description": "Audio analysis library for music information retrieval",
@@ -32,6 +32,7 @@
32
32
  "build": "yarn build:js && yarn build:wasm",
33
33
  "build:wasm": "mkdir -p build-wasm/em-cache && EM_CACHE=$PWD/build-wasm/em-cache emcmake cmake -S ../.. -B build-wasm -DBUILD_WASM=ON -DBUILD_MASTERING=ON -DCMAKE_BUILD_TYPE=Release && EM_CACHE=$PWD/build-wasm/em-cache cmake --build build-wasm && node scripts/write-rt-module-variant.mjs",
34
34
  "bench:wasm:isp": "mkdir -p build-wasm-bench/em-cache /tmp/libsonare-wasm-bench && EM_CACHE=$PWD/build-wasm-bench/em-cache emcmake cmake -S ../.. -B build-wasm-bench -DBUILD_WASM=ON -DBUILD_MASTERING=ON -DBUILD_BENCH=ON -DBUILD_TESTING=OFF -DBUILD_CLI=OFF -DCMAKE_BUILD_TYPE=Release && EM_CACHE=$PWD/build-wasm-bench/em-cache cmake --build build-wasm-bench --target sonare_mastering_isp_bench && cp build-wasm-bench/bin/sonare_mastering_isp_bench.js /tmp/libsonare-wasm-bench/sonare_mastering_isp_bench.cjs && cp build-wasm-bench/bin/sonare_mastering_isp_bench.wasm /tmp/libsonare-wasm-bench/sonare_mastering_isp_bench.wasm && node /tmp/libsonare-wasm-bench/sonare_mastering_isp_bench.cjs",
35
+ "bench:wasm:phase-vocoder": "mkdir -p build-wasm-bench/em-cache /tmp/libsonare-wasm-bench && EM_CACHE=$PWD/build-wasm-bench/em-cache emcmake cmake -S ../.. -B build-wasm-bench -DBUILD_WASM=ON -DBUILD_BENCH=ON -DBUILD_TESTING=OFF -DBUILD_CLI=OFF -DCMAKE_BUILD_TYPE=Release && EM_CACHE=$PWD/build-wasm-bench/em-cache cmake --build build-wasm-bench --target sonare_phase_vocoder_streaming_bench && cp build-wasm-bench/bin/sonare_phase_vocoder_streaming_bench.js /tmp/libsonare-wasm-bench/sonare_phase_vocoder_streaming_bench.cjs && cp build-wasm-bench/bin/sonare_phase_vocoder_streaming_bench.wasm /tmp/libsonare-wasm-bench/sonare_phase_vocoder_streaming_bench.wasm && node /tmp/libsonare-wasm-bench/sonare_phase_vocoder_streaming_bench.cjs",
35
36
  "build:js": "find dist -maxdepth 1 -type f \\( -name 'chunk-*.js' -o -name 'chunk-*.js.map' -o -name '*-*.d.ts' \\) -delete 2>/dev/null; tsup",
36
37
  "clean": "rm -rf dist build-wasm LICENSE",
37
38
  "lint": "biome check src/ tests/",
@@ -0,0 +1,152 @@
1
+ import type {
2
+ AnalysisResult,
3
+ ChordAnalysisResult,
4
+ ChordDetectionOptions,
5
+ ChordQuality,
6
+ KeyCandidate,
7
+ KeyDetectionOptions,
8
+ KeyProfileName,
9
+ PitchClass,
10
+ SectionType,
11
+ } from './public_types';
12
+ import { KeyProfile as KeyProfileValues, Mode } from './public_types';
13
+ import type {
14
+ WasmAnalysisResult,
15
+ WasmChordAnalysisResult,
16
+ WasmKeyCandidateResult,
17
+ } from './sonare.js';
18
+
19
+ export function convertKeyCandidate(wasm: WasmKeyCandidateResult): KeyCandidate {
20
+ return {
21
+ key: {
22
+ root: wasm.key.root as PitchClass,
23
+ mode: wasm.key.mode as Mode,
24
+ confidence: wasm.key.confidence,
25
+ name: wasm.key.name,
26
+ shortName: wasm.key.shortName,
27
+ },
28
+ correlation: wasm.correlation,
29
+ };
30
+ }
31
+
32
+ export function keyModeValues(modes: KeyDetectionOptions['modes'] | undefined): number[] {
33
+ if (!modes) {
34
+ return [];
35
+ }
36
+ if (modes === 'major-minor') {
37
+ return [Mode.Major, Mode.Minor];
38
+ }
39
+ if (modes === 'all' || modes === 'modal') {
40
+ return [
41
+ Mode.Major,
42
+ Mode.Minor,
43
+ Mode.Dorian,
44
+ Mode.Phrygian,
45
+ Mode.Lydian,
46
+ Mode.Mixolydian,
47
+ Mode.Locrian,
48
+ ];
49
+ }
50
+ const names = {
51
+ major: Mode.Major,
52
+ minor: Mode.Minor,
53
+ dorian: Mode.Dorian,
54
+ phrygian: Mode.Phrygian,
55
+ lydian: Mode.Lydian,
56
+ mixolydian: Mode.Mixolydian,
57
+ locrian: Mode.Locrian,
58
+ } as const;
59
+ return modes.map((mode) => (typeof mode === 'number' ? mode : names[mode]));
60
+ }
61
+
62
+ export function keyProfileValue(profile: KeyDetectionOptions['profile'] | undefined): number {
63
+ if (profile === undefined) {
64
+ return -1;
65
+ }
66
+ if (typeof profile === 'number') {
67
+ return profile;
68
+ }
69
+ const names: Record<KeyProfileName, number> = {
70
+ ks: KeyProfileValues.KrumhanslSchmuckler,
71
+ krumhansl: KeyProfileValues.KrumhanslSchmuckler,
72
+ temperley: KeyProfileValues.Temperley,
73
+ shaath: KeyProfileValues.Shaath,
74
+ keyfinder: KeyProfileValues.Shaath,
75
+ 'faraldo-edmt': KeyProfileValues.FaraldoEDMT,
76
+ edmt: KeyProfileValues.FaraldoEDMT,
77
+ 'faraldo-edma': KeyProfileValues.FaraldoEDMA,
78
+ edma: KeyProfileValues.FaraldoEDMA,
79
+ 'faraldo-edmm': KeyProfileValues.FaraldoEDMM,
80
+ edmm: KeyProfileValues.FaraldoEDMM,
81
+ 'bellman-budge': KeyProfileValues.BellmanBudge,
82
+ bellman: KeyProfileValues.BellmanBudge,
83
+ };
84
+ return names[profile];
85
+ }
86
+
87
+ export function convertChordAnalysisResult(wasm: WasmChordAnalysisResult): ChordAnalysisResult {
88
+ return {
89
+ chords: wasm.chords.map((c) => ({
90
+ root: c.root as PitchClass,
91
+ bass: c.bass as PitchClass,
92
+ quality: c.quality as ChordQuality,
93
+ start: c.start,
94
+ end: c.end,
95
+ confidence: c.confidence,
96
+ name: c.name,
97
+ })),
98
+ };
99
+ }
100
+
101
+ export function chordChromaMethodValue(method: ChordDetectionOptions['chromaMethod']): number {
102
+ if (method === 'stft') {
103
+ return 0;
104
+ }
105
+ if (method === 'nnls') {
106
+ return 1;
107
+ }
108
+ throw new Error(`Invalid chord chroma method: ${method}`);
109
+ }
110
+
111
+ export function convertAnalysisResult(wasm: WasmAnalysisResult): AnalysisResult {
112
+ const beatTimes = new Float32Array(wasm.beats.length);
113
+ for (let i = 0; i < wasm.beats.length; i++) {
114
+ beatTimes[i] = wasm.beats[i].time;
115
+ }
116
+ return {
117
+ bpm: wasm.bpm,
118
+ bpmConfidence: wasm.bpmConfidence,
119
+ key: {
120
+ root: wasm.key.root as PitchClass,
121
+ mode: wasm.key.mode as Mode,
122
+ confidence: wasm.key.confidence,
123
+ name: wasm.key.name,
124
+ shortName: wasm.key.shortName,
125
+ },
126
+ timeSignature: wasm.timeSignature,
127
+ beatTimes,
128
+ beats: wasm.beats,
129
+ chords: wasm.chords.map((c) => ({
130
+ root: c.root as PitchClass,
131
+ bass: c.bass as PitchClass,
132
+ quality: c.quality as ChordQuality,
133
+ start: c.start,
134
+ end: c.end,
135
+ confidence: c.confidence,
136
+ name: c.name,
137
+ })),
138
+ sections: wasm.sections.map((s) => ({
139
+ type: s.type as SectionType,
140
+ start: s.start,
141
+ end: s.end,
142
+ energyLevel: s.energyLevel,
143
+ confidence: s.confidence,
144
+ name: s.name,
145
+ })),
146
+ timbre: wasm.timbre,
147
+ dynamics: wasm.dynamics,
148
+ rhythm: wasm.rhythm,
149
+ melody: wasm.melody,
150
+ form: wasm.form,
151
+ };
152
+ }
package/src/audio.ts ADDED
@@ -0,0 +1,493 @@
1
+ import type { VoiceChangeOptions } from './effects_mastering';
2
+ import {
3
+ harmonic,
4
+ hpss,
5
+ masterAudio,
6
+ mastering,
7
+ masteringChain,
8
+ masteringProcess,
9
+ normalize,
10
+ noteStretch,
11
+ percussive,
12
+ pitchCorrectToMidi,
13
+ pitchShift,
14
+ timeStretch,
15
+ voiceChange,
16
+ } from './effects_mastering';
17
+ import {
18
+ chroma,
19
+ lufs,
20
+ melSpectrogram,
21
+ mfcc,
22
+ momentaryLufs,
23
+ nnlsChroma,
24
+ onsetEnvelope,
25
+ pitchPyin,
26
+ pitchYin,
27
+ resample,
28
+ rmsEnergy,
29
+ shortTermLufs,
30
+ spectralBandwidth,
31
+ spectralCentroid,
32
+ spectralFlatness,
33
+ spectralRolloff,
34
+ stft,
35
+ stftDb,
36
+ trim,
37
+ zeroCrossingRate,
38
+ } from './features';
39
+ import { getSonareModule } from './module_state';
40
+ import type {
41
+ AnalysisResult,
42
+ ChordAnalysisResult,
43
+ ChordDetectionOptions,
44
+ ChromaResult,
45
+ HpssResult,
46
+ Key,
47
+ KeyCandidate,
48
+ KeyDetectionOptions,
49
+ LufsResult,
50
+ MasteringChainConfig,
51
+ MasteringChainResult,
52
+ MasteringOptions,
53
+ MasteringPreset,
54
+ MasteringProcessorParams,
55
+ MasteringResult,
56
+ MelSpectrogramResult,
57
+ MfccResult,
58
+ Mode,
59
+ NoteStretchOptions,
60
+ PitchClass,
61
+ PitchResult,
62
+ SoloProcessor,
63
+ StftResult,
64
+ } from './public_types';
65
+ import {
66
+ analyze,
67
+ analyzeWithProgress,
68
+ chordFunctionalAnalysis,
69
+ detectBeats,
70
+ detectBpm,
71
+ detectChords,
72
+ detectDownbeats,
73
+ detectKey,
74
+ detectKeyCandidates,
75
+ detectOnsets,
76
+ } from './quick_analysis';
77
+ import type { ProgressCallback, WasmNnlsChromaResult } from './sonare.js';
78
+
79
+ // ============================================================================
80
+ // Audio Class
81
+ // ============================================================================
82
+
83
+ type BrowserDecodeContext = Pick<BaseAudioContext, 'decodeAudioData' | 'sampleRate'>;
84
+
85
+ export interface BrowserAudioDecodeOptions {
86
+ /**
87
+ * AudioContext/OfflineAudioContext used for browser codec fallback. Its
88
+ * `sampleRate` becomes the returned Audio sample rate.
89
+ */
90
+ audioContext?: BrowserDecodeContext;
91
+ /**
92
+ * Factory used when `audioContext` is omitted. `targetSampleRate` is passed
93
+ * through so browsers that honor AudioContextOptions decode directly at that
94
+ * rate.
95
+ */
96
+ createAudioContext?: (options?: AudioContextOptions) => BrowserDecodeContext;
97
+ /**
98
+ * Requested fallback decode rate when this helper creates the context. If the
99
+ * browser ignores it or a context is supplied, no extra resampling is applied.
100
+ */
101
+ targetSampleRate?: number;
102
+ }
103
+
104
+ function encodedBytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {
105
+ const copy = new Uint8Array(bytes.byteLength);
106
+ copy.set(bytes);
107
+ return copy.buffer;
108
+ }
109
+
110
+ function getBrowserAudioContextFactory():
111
+ | ((options?: AudioContextOptions) => BrowserDecodeContext)
112
+ | undefined {
113
+ const root = globalThis as typeof globalThis & {
114
+ AudioContext?: new (options?: AudioContextOptions) => BaseAudioContext;
115
+ webkitAudioContext?: new (options?: AudioContextOptions) => BaseAudioContext;
116
+ };
117
+ const Ctor = root.AudioContext ?? root.webkitAudioContext;
118
+ return Ctor ? (options?: AudioContextOptions) => new Ctor(options) : undefined;
119
+ }
120
+
121
+ function audioBufferToMono(buffer: AudioBuffer): Float32Array {
122
+ const samples = new Float32Array(buffer.length);
123
+ if (buffer.numberOfChannels <= 0) {
124
+ return samples;
125
+ }
126
+ if (buffer.numberOfChannels === 1) {
127
+ samples.set(buffer.getChannelData(0));
128
+ return samples;
129
+ }
130
+ for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
131
+ const data = buffer.getChannelData(channel);
132
+ for (let i = 0; i < buffer.length; i++) {
133
+ samples[i] += data[i] / buffer.numberOfChannels;
134
+ }
135
+ }
136
+ return samples;
137
+ }
138
+
139
+ async function closeCreatedContext(context: BrowserDecodeContext): Promise<void> {
140
+ const maybeClosable = context as BrowserDecodeContext & { close?: () => Promise<void> };
141
+ if (maybeClosable.close) {
142
+ await maybeClosable.close();
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Wrapper around audio data that exposes all analysis and feature functions as instance methods.
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * import { init, Audio } from '@libraz/libsonare';
152
+ *
153
+ * await init();
154
+ *
155
+ * const audio = Audio.fromBuffer(samples, 44100);
156
+ * console.log('BPM:', audio.detectBpm());
157
+ * console.log('Key:', audio.detectKey().name);
158
+ *
159
+ * const mel = audio.melSpectrogram();
160
+ * ```
161
+ */
162
+ export class Audio {
163
+ private _samples: Float32Array;
164
+ private _sampleRate: number;
165
+
166
+ private constructor(samples: Float32Array, sampleRate: number) {
167
+ this._samples = samples;
168
+ this._sampleRate = sampleRate;
169
+ }
170
+
171
+ /**
172
+ * Create an Audio instance from raw sample data.
173
+ *
174
+ * @param samples - Mono float samples.
175
+ * @param sampleRate - Sample rate in Hz (default `48000`, matching the
176
+ * Node/Python surfaces).
177
+ */
178
+ static fromBuffer(samples: Float32Array, sampleRate = 48000): Audio {
179
+ return new Audio(samples, sampleRate);
180
+ }
181
+
182
+ /**
183
+ * Create an Audio instance by decoding audio bytes in memory.
184
+ *
185
+ * @param bytes - Encoded audio bytes such as WAV or MP3.
186
+ */
187
+ static fromMemory(bytes: Uint8Array): Audio {
188
+ const decoded = getSonareModule().audioFromMemory(bytes);
189
+ return new Audio(decoded.samples, decoded.sampleRate);
190
+ }
191
+
192
+ /**
193
+ * Decode audio bytes with the native WASM decoder first, then fall back to the
194
+ * browser codec stack (`AudioContext.decodeAudioData`) for formats such as
195
+ * AAC, OGG, and FLAC when available. Browser-decoded multi-channel audio is
196
+ * mixed down to mono to match the `Audio` wrapper contract.
197
+ */
198
+ static async fromMemoryWithBrowserFallback(
199
+ bytes: Uint8Array,
200
+ options: BrowserAudioDecodeOptions = {},
201
+ ): Promise<Audio> {
202
+ try {
203
+ return Audio.fromMemory(bytes);
204
+ } catch (nativeError) {
205
+ let createdContext = false;
206
+ const contextFactory = options.createAudioContext ?? getBrowserAudioContextFactory();
207
+ const context =
208
+ options.audioContext ??
209
+ contextFactory?.(
210
+ options.targetSampleRate ? { sampleRate: options.targetSampleRate } : undefined,
211
+ );
212
+
213
+ if (!context) {
214
+ throw new Error(
215
+ `Audio.fromMemory failed and browser decodeAudioData is unavailable: ${
216
+ nativeError instanceof Error ? nativeError.message : String(nativeError)
217
+ }`,
218
+ );
219
+ }
220
+
221
+ createdContext = !options.audioContext;
222
+ try {
223
+ const decoded = await context.decodeAudioData(encodedBytesToArrayBuffer(bytes));
224
+ return new Audio(audioBufferToMono(decoded), decoded.sampleRate || context.sampleRate);
225
+ } catch (fallbackError) {
226
+ throw new Error(
227
+ `Audio.fromMemory failed and browser decodeAudioData fallback failed: ${
228
+ fallbackError instanceof Error ? fallbackError.message : String(fallbackError)
229
+ }`,
230
+ );
231
+ } finally {
232
+ if (createdContext) {
233
+ await closeCreatedContext(context);
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ /** The raw audio samples. */
240
+ get data(): Float32Array {
241
+ return this._samples;
242
+ }
243
+
244
+ /** Number of samples. */
245
+ get length(): number {
246
+ return this._samples.length;
247
+ }
248
+
249
+ /** Sample rate in Hz. */
250
+ get sampleRate(): number {
251
+ return this._sampleRate;
252
+ }
253
+
254
+ /** Duration in seconds. */
255
+ get duration(): number {
256
+ return this._samples.length / this._sampleRate;
257
+ }
258
+
259
+ // -- Analysis --
260
+
261
+ detectBpm(): number {
262
+ return detectBpm(this._samples, this._sampleRate);
263
+ }
264
+
265
+ detectKey(options: KeyDetectionOptions = {}): Key {
266
+ return detectKey(this._samples, this._sampleRate, options);
267
+ }
268
+
269
+ detectKeyCandidates(options: KeyDetectionOptions = {}): KeyCandidate[] {
270
+ return detectKeyCandidates(this._samples, this._sampleRate, options);
271
+ }
272
+
273
+ detectOnsets(): Float32Array {
274
+ return detectOnsets(this._samples, this._sampleRate);
275
+ }
276
+
277
+ detectBeats(): Float32Array {
278
+ return detectBeats(this._samples, this._sampleRate);
279
+ }
280
+
281
+ detectDownbeats(): Float32Array {
282
+ return detectDownbeats(this._samples, this._sampleRate);
283
+ }
284
+
285
+ detectChords(options: ChordDetectionOptions = {}): ChordAnalysisResult {
286
+ return detectChords(this._samples, this._sampleRate, options);
287
+ }
288
+
289
+ chordFunctionalAnalysis(
290
+ keyRoot: PitchClass,
291
+ keyMode: Mode,
292
+ options: ChordDetectionOptions = {},
293
+ ): string[] {
294
+ return chordFunctionalAnalysis(this._samples, keyRoot, keyMode, this._sampleRate, options);
295
+ }
296
+
297
+ analyze(): AnalysisResult {
298
+ return analyze(this._samples, this._sampleRate);
299
+ }
300
+
301
+ analyzeWithProgress(onProgress: ProgressCallback): AnalysisResult {
302
+ return analyzeWithProgress(this._samples, this._sampleRate, onProgress);
303
+ }
304
+
305
+ // -- Effects --
306
+
307
+ hpss(kernelHarmonic = 31, kernelPercussive = 31): HpssResult {
308
+ return hpss(this._samples, this._sampleRate, kernelHarmonic, kernelPercussive);
309
+ }
310
+
311
+ harmonic(): Float32Array {
312
+ return harmonic(this._samples, this._sampleRate);
313
+ }
314
+
315
+ percussive(): Float32Array {
316
+ return percussive(this._samples, this._sampleRate);
317
+ }
318
+
319
+ timeStretch(rate: number): Float32Array {
320
+ return timeStretch(this._samples, this._sampleRate, rate);
321
+ }
322
+
323
+ pitchShift(semitones: number): Float32Array {
324
+ return pitchShift(this._samples, this._sampleRate, semitones);
325
+ }
326
+
327
+ pitchCorrectToMidi(currentMidi = 69.0, targetMidi = 69.0): Float32Array {
328
+ return pitchCorrectToMidi(this._samples, this._sampleRate, currentMidi, targetMidi);
329
+ }
330
+
331
+ noteStretch(options: NoteStretchOptions = {}): Float32Array {
332
+ return noteStretch(this._samples, this._sampleRate, options);
333
+ }
334
+
335
+ voiceChange(options: VoiceChangeOptions = {}): Float32Array {
336
+ return voiceChange(this._samples, this._sampleRate, options);
337
+ }
338
+
339
+ normalize(targetDb = 0.0): Float32Array {
340
+ return normalize(this._samples, this._sampleRate, targetDb);
341
+ }
342
+
343
+ mastering(options: MasteringOptions = {}): MasteringResult {
344
+ return mastering(this._samples, this._sampleRate, options);
345
+ }
346
+
347
+ masteringChain(config: MasteringChainConfig): MasteringChainResult {
348
+ return masteringChain(this._samples, this._sampleRate, config);
349
+ }
350
+
351
+ masterAudio(
352
+ presetName: MasteringPreset = 'pop',
353
+ overrides: Record<string, number | boolean> | null = null,
354
+ ): MasteringChainResult {
355
+ return masterAudio(this._samples, this._sampleRate, presetName, overrides ?? {});
356
+ }
357
+
358
+ masteringProcess(
359
+ processorName: SoloProcessor,
360
+ params: MasteringProcessorParams = {},
361
+ ): MasteringResult {
362
+ return masteringProcess(processorName, this._samples, this._sampleRate, params);
363
+ }
364
+
365
+ trim(thresholdDb = -60.0): Float32Array {
366
+ return trim(this._samples, this._sampleRate, thresholdDb);
367
+ }
368
+
369
+ // -- Features --
370
+
371
+ stft(nFft = 2048, hopLength = 512): StftResult {
372
+ return stft(this._samples, this._sampleRate, nFft, hopLength);
373
+ }
374
+
375
+ stftDb(nFft = 2048, hopLength = 512): { nBins: number; nFrames: number; db: Float32Array } {
376
+ return stftDb(this._samples, this._sampleRate, nFft, hopLength);
377
+ }
378
+
379
+ melSpectrogram(
380
+ nFft = 2048,
381
+ hopLength = 512,
382
+ nMels = 128,
383
+ fmin = 0,
384
+ fmax = 0,
385
+ htk = false,
386
+ ): MelSpectrogramResult {
387
+ return melSpectrogram(this._samples, this._sampleRate, nFft, hopLength, nMels, fmin, fmax, htk);
388
+ }
389
+
390
+ mfcc(
391
+ nFft = 2048,
392
+ hopLength = 512,
393
+ nMels = 128,
394
+ nMfcc = 20,
395
+ fmin = 0,
396
+ fmax = 0,
397
+ htk = false,
398
+ ): MfccResult {
399
+ return mfcc(this._samples, this._sampleRate, nFft, hopLength, nMels, nMfcc, fmin, fmax, htk);
400
+ }
401
+
402
+ chroma(nFft = 2048, hopLength = 512): ChromaResult {
403
+ return chroma(this._samples, this._sampleRate, nFft, hopLength);
404
+ }
405
+
406
+ nnlsChroma(): WasmNnlsChromaResult {
407
+ return nnlsChroma(this._samples, this._sampleRate);
408
+ }
409
+
410
+ onsetEnvelope(nFft = 2048, hopLength = 512, nMels = 128): Float32Array {
411
+ return onsetEnvelope(this._samples, this._sampleRate, nFft, hopLength, nMels);
412
+ }
413
+
414
+ lufs(): LufsResult {
415
+ return lufs(this._samples, this._sampleRate);
416
+ }
417
+
418
+ momentaryLufs(): Float32Array {
419
+ return momentaryLufs(this._samples, this._sampleRate);
420
+ }
421
+
422
+ shortTermLufs(): Float32Array {
423
+ return shortTermLufs(this._samples, this._sampleRate);
424
+ }
425
+
426
+ spectralCentroid(nFft = 2048, hopLength = 512): Float32Array {
427
+ return spectralCentroid(this._samples, this._sampleRate, nFft, hopLength);
428
+ }
429
+
430
+ spectralBandwidth(nFft = 2048, hopLength = 512): Float32Array {
431
+ return spectralBandwidth(this._samples, this._sampleRate, nFft, hopLength);
432
+ }
433
+
434
+ spectralRolloff(nFft = 2048, hopLength = 512, rollPercent = 0.85): Float32Array {
435
+ return spectralRolloff(this._samples, this._sampleRate, nFft, hopLength, rollPercent);
436
+ }
437
+
438
+ spectralFlatness(nFft = 2048, hopLength = 512): Float32Array {
439
+ return spectralFlatness(this._samples, this._sampleRate, nFft, hopLength);
440
+ }
441
+
442
+ zeroCrossingRate(frameLength = 2048, hopLength = 512): Float32Array {
443
+ return zeroCrossingRate(this._samples, this._sampleRate, frameLength, hopLength);
444
+ }
445
+
446
+ rmsEnergy(frameLength = 2048, hopLength = 512): Float32Array {
447
+ return rmsEnergy(this._samples, this._sampleRate, frameLength, hopLength);
448
+ }
449
+
450
+ pitchYin(
451
+ frameLength = 2048,
452
+ hopLength = 512,
453
+ fmin = 65.0,
454
+ fmax = 2093.0,
455
+ threshold = 0.3,
456
+ fillNa = false,
457
+ ): PitchResult {
458
+ return pitchYin(
459
+ this._samples,
460
+ this._sampleRate,
461
+ frameLength,
462
+ hopLength,
463
+ fmin,
464
+ fmax,
465
+ threshold,
466
+ fillNa,
467
+ );
468
+ }
469
+
470
+ pitchPyin(
471
+ frameLength = 2048,
472
+ hopLength = 512,
473
+ fmin = 65.0,
474
+ fmax = 2093.0,
475
+ threshold = 0.3,
476
+ fillNa = false,
477
+ ): PitchResult {
478
+ return pitchPyin(
479
+ this._samples,
480
+ this._sampleRate,
481
+ frameLength,
482
+ hopLength,
483
+ fmin,
484
+ fmax,
485
+ threshold,
486
+ fillNa,
487
+ );
488
+ }
489
+
490
+ resample(targetSr: number): Float32Array {
491
+ return resample(this._samples, this._sampleRate, targetSr);
492
+ }
493
+ }
package/src/codes.ts ADDED
@@ -0,0 +1,56 @@
1
+ import type { AutomationCurve, MeterTap, PanLaw, PanMode, SendTiming } from './public_types';
2
+
3
+ export function automationCurveCode(curve: AutomationCurve): number {
4
+ switch (curve) {
5
+ case 'linear':
6
+ return 0;
7
+ case 'exponential':
8
+ return 1;
9
+ case 'hold':
10
+ return 2;
11
+ case 's-curve':
12
+ return 3;
13
+ default:
14
+ throw new Error(`Invalid automation curve: ${curve}`);
15
+ }
16
+ }
17
+
18
+ export function panLawCode(panLaw: PanLaw | number): number {
19
+ if (typeof panLaw === 'number') {
20
+ return panLaw;
21
+ }
22
+ switch (panLaw) {
23
+ case 'const4.5dB':
24
+ return 1;
25
+ case 'const6dB':
26
+ return 2;
27
+ case 'linear0dB':
28
+ return 3;
29
+ default:
30
+ return 0;
31
+ }
32
+ }
33
+
34
+ export function panModeCode(panMode: PanMode | number): number {
35
+ if (typeof panMode === 'number') {
36
+ return panMode;
37
+ }
38
+ switch (panMode) {
39
+ case 'stereoPan':
40
+ case 'stereo-pan':
41
+ return 1;
42
+ case 'dualPan':
43
+ case 'dual-pan':
44
+ return 2;
45
+ default:
46
+ return 0;
47
+ }
48
+ }
49
+
50
+ export function meterTapCode(tap: MeterTap | number): number {
51
+ return tap === 'preFader' || tap === 0 ? 0 : 1;
52
+ }
53
+
54
+ export function sendTimingCode(timing: SendTiming | number): number {
55
+ return timing === 'preFader' || timing === 0 ? 0 : 1;
56
+ }