@siteed/audio-studio 3.0.0 → 3.0.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.
- package/CHANGELOG.md +1073 -414
- package/README.md +1 -1
- package/android/src/main/CMakeLists.txt +3 -0
- package/build/cjs/AudioAnalysis/audioFeaturesWasm.js +7 -155
- package/build/cjs/AudioAnalysis/audioFeaturesWasm.js.map +1 -1
- package/build/cjs/AudioAnalysis/audioFeaturesWasm.web.js +164 -0
- package/build/cjs/AudioAnalysis/audioFeaturesWasm.web.js.map +1 -0
- package/build/cjs/AudioAnalysis/melSpectrogramWasm.js +8 -140
- package/build/cjs/AudioAnalysis/melSpectrogramWasm.js.map +1 -1
- package/build/cjs/AudioAnalysis/melSpectrogramWasm.web.js +149 -0
- package/build/cjs/AudioAnalysis/melSpectrogramWasm.web.js.map +1 -0
- package/build/cjs/prebuilt/wasm/mel-spectrogram.js +18 -0
- package/build/esm/AudioAnalysis/audioFeaturesWasm.js +7 -122
- package/build/esm/AudioAnalysis/audioFeaturesWasm.js.map +1 -1
- package/build/esm/AudioAnalysis/audioFeaturesWasm.web.js +126 -0
- package/build/esm/AudioAnalysis/audioFeaturesWasm.web.js.map +1 -0
- package/build/esm/AudioAnalysis/melSpectrogramWasm.js +8 -107
- package/build/esm/AudioAnalysis/melSpectrogramWasm.js.map +1 -1
- package/build/esm/AudioAnalysis/melSpectrogramWasm.web.js +111 -0
- package/build/esm/AudioAnalysis/melSpectrogramWasm.web.js.map +1 -0
- package/build/esm/prebuilt/wasm/mel-spectrogram.js +18 -0
- package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts +3 -15
- package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts.map +1 -1
- package/build/types/AudioAnalysis/audioFeaturesWasm.web.d.ts +24 -0
- package/build/types/AudioAnalysis/audioFeaturesWasm.web.d.ts.map +1 -0
- package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts +3 -15
- package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts.map +1 -1
- package/build/types/AudioAnalysis/melSpectrogramWasm.web.d.ts +16 -0
- package/build/types/AudioAnalysis/melSpectrogramWasm.web.d.ts.map +1 -0
- package/package.json +3 -2
- package/src/AudioAnalysis/audioFeaturesWasm.ts +18 -179
- package/src/AudioAnalysis/audioFeaturesWasm.web.ts +200 -0
- package/src/AudioAnalysis/melSpectrogramWasm.ts +23 -169
- package/src/AudioAnalysis/melSpectrogramWasm.web.ts +179 -0
|
@@ -1,179 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
// Native stub — WASM mel spectrogram is web-only.
|
|
2
|
+
// These functions are only called in web contexts; on native, the C++ TurboModule handles mel spectrograms.
|
|
2
3
|
|
|
3
|
-
let modulePromise: Promise<MelSpectrogramWasmModule> | null = null
|
|
4
|
-
|
|
5
|
-
function getModule(): Promise<MelSpectrogramWasmModule> {
|
|
6
|
-
if (!modulePromise) {
|
|
7
|
-
modulePromise = (async () => {
|
|
8
|
-
// Dynamic import of the prebuilt SINGLE_FILE Emscripten module
|
|
9
|
-
// @ts-expect-error -- prebuilt Emscripten JS glue has no .d.ts
|
|
10
|
-
const mod = await import('../../prebuilt/wasm/mel-spectrogram.js')
|
|
11
|
-
const factory = mod.default ?? mod
|
|
12
|
-
return factory() as Promise<MelSpectrogramWasmModule>
|
|
13
|
-
})().catch((err) => {
|
|
14
|
-
modulePromise = null
|
|
15
|
-
throw err
|
|
16
|
-
})
|
|
17
|
-
}
|
|
18
|
-
return modulePromise
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// --- Streaming (per-frame) API for live mel spectrogram ---
|
|
22
|
-
|
|
23
|
-
let streamingModule: MelSpectrogramWasmModule | null = null
|
|
24
|
-
let streamingNMels = 0
|
|
25
|
-
let streamingFramePtr = 0
|
|
26
|
-
let streamingMelPtr = 0
|
|
27
|
-
let streamingFrameCapacity = 0
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Initialise the WASM streaming processor. Call once before computeMelFrame().
|
|
31
|
-
* Re-initialises only when config changes.
|
|
32
|
-
*/
|
|
33
4
|
export async function initMelStreamingWasm(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
5
|
+
_sampleRate: number,
|
|
6
|
+
_nMels?: number,
|
|
7
|
+
_fftLength?: number,
|
|
8
|
+
_windowSizeSamples?: number,
|
|
9
|
+
_hopLengthSamples?: number,
|
|
10
|
+
_fMin?: number,
|
|
11
|
+
_fMax?: number
|
|
41
12
|
): Promise<void> {
|
|
42
|
-
|
|
43
|
-
streamingModule = Module
|
|
44
|
-
const actualFMax = fMax > 0 ? fMax : sampleRate / 2
|
|
45
|
-
Module._mel_spectrogram_init(
|
|
46
|
-
sampleRate,
|
|
47
|
-
fftLength,
|
|
48
|
-
windowSizeSamples,
|
|
49
|
-
hopLengthSamples,
|
|
50
|
-
nMels,
|
|
51
|
-
fMin,
|
|
52
|
-
actualFMax,
|
|
53
|
-
0 /* hann */
|
|
54
|
-
)
|
|
55
|
-
streamingNMels = nMels
|
|
56
|
-
|
|
57
|
-
// Pre-allocate output buffer (fixed size)
|
|
58
|
-
if (streamingMelPtr) Module._free(streamingMelPtr)
|
|
59
|
-
streamingMelPtr = Module._malloc(nMels * 4)
|
|
60
|
-
|
|
61
|
-
// Frame input buffer allocated on demand in computeMelFrame
|
|
62
|
-
streamingFrameCapacity = 0
|
|
63
|
-
streamingFramePtr = 0
|
|
13
|
+
throw new Error('WASM mel spectrogram is not available on native')
|
|
64
14
|
}
|
|
65
15
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
* Returns null if not initialised or on error.
|
|
69
|
-
*/
|
|
70
|
-
export function computeMelFrameWasm(samples: Float32Array): number[] | null {
|
|
71
|
-
if (!streamingModule || !streamingMelPtr) return null
|
|
72
|
-
const Module = streamingModule
|
|
73
|
-
|
|
74
|
-
// (Re-)allocate frame input buffer if needed
|
|
75
|
-
if (samples.length > streamingFrameCapacity) {
|
|
76
|
-
if (streamingFramePtr) Module._free(streamingFramePtr)
|
|
77
|
-
streamingFramePtr = Module._malloc(samples.length * 4)
|
|
78
|
-
streamingFrameCapacity = samples.length
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Copy samples to WASM heap
|
|
82
|
-
Module.HEAPF32.set(samples, streamingFramePtr >> 2)
|
|
83
|
-
|
|
84
|
-
const ok = Module._mel_spectrogram_compute_frame(
|
|
85
|
-
streamingFramePtr,
|
|
86
|
-
samples.length,
|
|
87
|
-
streamingMelPtr
|
|
88
|
-
)
|
|
89
|
-
if (!ok) return null
|
|
90
|
-
|
|
91
|
-
// Read mel output from WASM heap
|
|
92
|
-
const offset = streamingMelPtr >> 2
|
|
93
|
-
const result = new Array(streamingNMels)
|
|
94
|
-
for (let i = 0; i < streamingNMels; i++) {
|
|
95
|
-
result[i] = Module.HEAPF32[offset + i]
|
|
96
|
-
}
|
|
97
|
-
return result
|
|
16
|
+
export function computeMelFrameWasm(_samples: Float32Array): number[] | null {
|
|
17
|
+
return null
|
|
98
18
|
}
|
|
99
19
|
|
|
100
|
-
/**
|
|
101
|
-
* Computes a mel spectrogram via the WASM-compiled C++ implementation.
|
|
102
|
-
* Lazy-loads the WASM module on first call.
|
|
103
|
-
*/
|
|
104
20
|
export async function computeMelSpectrogramWasm(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
21
|
+
_audioData: Float32Array,
|
|
22
|
+
_sampleRate: number,
|
|
23
|
+
_nMels: number,
|
|
24
|
+
_windowSizeSamples: number,
|
|
25
|
+
_hopLengthSamples: number,
|
|
26
|
+
_fMin: number,
|
|
27
|
+
_fMax: number,
|
|
28
|
+
_windowType: 'hann' | 'hamming',
|
|
29
|
+
_normalize: boolean,
|
|
30
|
+
_logScale: boolean
|
|
115
31
|
): Promise<number[][]> {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const fftLength = 2048
|
|
119
|
-
const windowTypeInt = windowType === 'hamming' ? 1 : 0
|
|
120
|
-
|
|
121
|
-
// Allocate input buffer on WASM heap
|
|
122
|
-
const numSamples = audioData.length
|
|
123
|
-
const inputPtr = Module._malloc(numSamples * 4) // 4 bytes per float
|
|
124
|
-
Module.HEAPF32.set(audioData, inputPtr >> 2)
|
|
125
|
-
|
|
126
|
-
// Call the C bridge
|
|
127
|
-
const resultPtr = Module._mel_spectrogram_compute(
|
|
128
|
-
inputPtr,
|
|
129
|
-
numSamples,
|
|
130
|
-
sampleRate,
|
|
131
|
-
fftLength,
|
|
132
|
-
windowSizeSamples,
|
|
133
|
-
hopLengthSamples,
|
|
134
|
-
nMels,
|
|
135
|
-
fMin,
|
|
136
|
-
fMax,
|
|
137
|
-
windowTypeInt,
|
|
138
|
-
logScale ? 1 : 0,
|
|
139
|
-
normalize ? 1 : 0
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
// Free input buffer
|
|
143
|
-
Module._free(inputPtr)
|
|
144
|
-
|
|
145
|
-
if (resultPtr === 0) {
|
|
146
|
-
throw new Error(
|
|
147
|
-
'mel_spectrogram_compute returned null (too few samples?)'
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Read CMelSpectrogramResult struct (wasm32 pointers are 4 bytes)
|
|
152
|
-
// struct layout: { float* data (offset 0), int timeSteps (offset 4), int nMels (offset 8) }
|
|
153
|
-
const dataPtr = Module.getValue(resultPtr, 'i32')
|
|
154
|
-
const timeSteps = Module.getValue(resultPtr + 4, 'i32')
|
|
155
|
-
const resultNMels = Module.getValue(resultPtr + 8, 'i32')
|
|
156
|
-
|
|
157
|
-
if (!dataPtr || timeSteps <= 0 || resultNMels <= 0) {
|
|
158
|
-
Module._mel_spectrogram_free(resultPtr)
|
|
159
|
-
throw new Error(
|
|
160
|
-
'mel_spectrogram_compute returned invalid result struct'
|
|
161
|
-
)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Copy spectrogram data to JS arrays
|
|
165
|
-
const spectrogram: number[][] = []
|
|
166
|
-
const heapOffset = dataPtr >> 2 // float32 offset into HEAPF32
|
|
167
|
-
for (let t = 0; t < timeSteps; t++) {
|
|
168
|
-
const row = new Array(resultNMels)
|
|
169
|
-
for (let m = 0; m < resultNMels; m++) {
|
|
170
|
-
row[m] = Module.HEAPF32[heapOffset + t * resultNMels + m]
|
|
171
|
-
}
|
|
172
|
-
spectrogram.push(row)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Free the C result
|
|
176
|
-
Module._mel_spectrogram_free(resultPtr)
|
|
177
|
-
|
|
178
|
-
return spectrogram
|
|
32
|
+
throw new Error('WASM mel spectrogram is not available on native')
|
|
179
33
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import type { MelSpectrogramWasmModule } from './mel-spectrogram-wasm'
|
|
2
|
+
|
|
3
|
+
let modulePromise: Promise<MelSpectrogramWasmModule> | null = null
|
|
4
|
+
|
|
5
|
+
function getModule(): Promise<MelSpectrogramWasmModule> {
|
|
6
|
+
if (!modulePromise) {
|
|
7
|
+
modulePromise = (async () => {
|
|
8
|
+
// Dynamic import of the prebuilt SINGLE_FILE Emscripten module
|
|
9
|
+
// @ts-expect-error -- prebuilt Emscripten JS glue has no .d.ts
|
|
10
|
+
const mod = await import('../../prebuilt/wasm/mel-spectrogram.js')
|
|
11
|
+
const factory = mod.default ?? mod
|
|
12
|
+
return factory() as Promise<MelSpectrogramWasmModule>
|
|
13
|
+
})().catch((err) => {
|
|
14
|
+
modulePromise = null
|
|
15
|
+
throw err
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
return modulePromise
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// --- Streaming (per-frame) API for live mel spectrogram ---
|
|
22
|
+
|
|
23
|
+
let streamingModule: MelSpectrogramWasmModule | null = null
|
|
24
|
+
let streamingNMels = 0
|
|
25
|
+
let streamingFramePtr = 0
|
|
26
|
+
let streamingMelPtr = 0
|
|
27
|
+
let streamingFrameCapacity = 0
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Initialise the WASM streaming processor. Call once before computeMelFrame().
|
|
31
|
+
* Re-initialises only when config changes.
|
|
32
|
+
*/
|
|
33
|
+
export async function initMelStreamingWasm(
|
|
34
|
+
sampleRate: number,
|
|
35
|
+
nMels = 128,
|
|
36
|
+
fftLength = 2048,
|
|
37
|
+
windowSizeSamples = 400,
|
|
38
|
+
hopLengthSamples = 160,
|
|
39
|
+
fMin = 0,
|
|
40
|
+
fMax = 0
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
const Module = await getModule()
|
|
43
|
+
streamingModule = Module
|
|
44
|
+
const actualFMax = fMax > 0 ? fMax : sampleRate / 2
|
|
45
|
+
Module._mel_spectrogram_init(
|
|
46
|
+
sampleRate,
|
|
47
|
+
fftLength,
|
|
48
|
+
windowSizeSamples,
|
|
49
|
+
hopLengthSamples,
|
|
50
|
+
nMels,
|
|
51
|
+
fMin,
|
|
52
|
+
actualFMax,
|
|
53
|
+
0 /* hann */
|
|
54
|
+
)
|
|
55
|
+
streamingNMels = nMels
|
|
56
|
+
|
|
57
|
+
// Pre-allocate output buffer (fixed size)
|
|
58
|
+
if (streamingMelPtr) Module._free(streamingMelPtr)
|
|
59
|
+
streamingMelPtr = Module._malloc(nMels * 4)
|
|
60
|
+
|
|
61
|
+
// Frame input buffer allocated on demand in computeMelFrame
|
|
62
|
+
streamingFrameCapacity = 0
|
|
63
|
+
streamingFramePtr = 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Compute a single mel spectrogram frame from raw PCM samples via WASM C++.
|
|
68
|
+
* Returns null if not initialised or on error.
|
|
69
|
+
*/
|
|
70
|
+
export function computeMelFrameWasm(samples: Float32Array): number[] | null {
|
|
71
|
+
if (!streamingModule || !streamingMelPtr) return null
|
|
72
|
+
const Module = streamingModule
|
|
73
|
+
|
|
74
|
+
// (Re-)allocate frame input buffer if needed
|
|
75
|
+
if (samples.length > streamingFrameCapacity) {
|
|
76
|
+
if (streamingFramePtr) Module._free(streamingFramePtr)
|
|
77
|
+
streamingFramePtr = Module._malloc(samples.length * 4)
|
|
78
|
+
streamingFrameCapacity = samples.length
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Copy samples to WASM heap
|
|
82
|
+
Module.HEAPF32.set(samples, streamingFramePtr >> 2)
|
|
83
|
+
|
|
84
|
+
const ok = Module._mel_spectrogram_compute_frame(
|
|
85
|
+
streamingFramePtr,
|
|
86
|
+
samples.length,
|
|
87
|
+
streamingMelPtr
|
|
88
|
+
)
|
|
89
|
+
if (!ok) return null
|
|
90
|
+
|
|
91
|
+
// Read mel output from WASM heap
|
|
92
|
+
const offset = streamingMelPtr >> 2
|
|
93
|
+
const result = new Array(streamingNMels)
|
|
94
|
+
for (let i = 0; i < streamingNMels; i++) {
|
|
95
|
+
result[i] = Module.HEAPF32[offset + i]
|
|
96
|
+
}
|
|
97
|
+
return result
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Computes a mel spectrogram via the WASM-compiled C++ implementation.
|
|
102
|
+
* Lazy-loads the WASM module on first call.
|
|
103
|
+
*/
|
|
104
|
+
export async function computeMelSpectrogramWasm(
|
|
105
|
+
audioData: Float32Array,
|
|
106
|
+
sampleRate: number,
|
|
107
|
+
nMels: number,
|
|
108
|
+
windowSizeSamples: number,
|
|
109
|
+
hopLengthSamples: number,
|
|
110
|
+
fMin: number,
|
|
111
|
+
fMax: number,
|
|
112
|
+
windowType: 'hann' | 'hamming',
|
|
113
|
+
normalize: boolean,
|
|
114
|
+
logScale: boolean
|
|
115
|
+
): Promise<number[][]> {
|
|
116
|
+
const Module = await getModule()
|
|
117
|
+
|
|
118
|
+
const fftLength = 2048
|
|
119
|
+
const windowTypeInt = windowType === 'hamming' ? 1 : 0
|
|
120
|
+
|
|
121
|
+
// Allocate input buffer on WASM heap
|
|
122
|
+
const numSamples = audioData.length
|
|
123
|
+
const inputPtr = Module._malloc(numSamples * 4) // 4 bytes per float
|
|
124
|
+
Module.HEAPF32.set(audioData, inputPtr >> 2)
|
|
125
|
+
|
|
126
|
+
// Call the C bridge
|
|
127
|
+
const resultPtr = Module._mel_spectrogram_compute(
|
|
128
|
+
inputPtr,
|
|
129
|
+
numSamples,
|
|
130
|
+
sampleRate,
|
|
131
|
+
fftLength,
|
|
132
|
+
windowSizeSamples,
|
|
133
|
+
hopLengthSamples,
|
|
134
|
+
nMels,
|
|
135
|
+
fMin,
|
|
136
|
+
fMax,
|
|
137
|
+
windowTypeInt,
|
|
138
|
+
logScale ? 1 : 0,
|
|
139
|
+
normalize ? 1 : 0
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
// Free input buffer
|
|
143
|
+
Module._free(inputPtr)
|
|
144
|
+
|
|
145
|
+
if (resultPtr === 0) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
'mel_spectrogram_compute returned null (too few samples?)'
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Read CMelSpectrogramResult struct (wasm32 pointers are 4 bytes)
|
|
152
|
+
// struct layout: { float* data (offset 0), int timeSteps (offset 4), int nMels (offset 8) }
|
|
153
|
+
const dataPtr = Module.getValue(resultPtr, 'i32')
|
|
154
|
+
const timeSteps = Module.getValue(resultPtr + 4, 'i32')
|
|
155
|
+
const resultNMels = Module.getValue(resultPtr + 8, 'i32')
|
|
156
|
+
|
|
157
|
+
if (!dataPtr || timeSteps <= 0 || resultNMels <= 0) {
|
|
158
|
+
Module._mel_spectrogram_free(resultPtr)
|
|
159
|
+
throw new Error(
|
|
160
|
+
'mel_spectrogram_compute returned invalid result struct'
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Copy spectrogram data to JS arrays
|
|
165
|
+
const spectrogram: number[][] = []
|
|
166
|
+
const heapOffset = dataPtr >> 2 // float32 offset into HEAPF32
|
|
167
|
+
for (let t = 0; t < timeSteps; t++) {
|
|
168
|
+
const row = new Array(resultNMels)
|
|
169
|
+
for (let m = 0; m < resultNMels; m++) {
|
|
170
|
+
row[m] = Module.HEAPF32[heapOffset + t * resultNMels + m]
|
|
171
|
+
}
|
|
172
|
+
spectrogram.push(row)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Free the C result
|
|
176
|
+
Module._mel_spectrogram_free(resultPtr)
|
|
177
|
+
|
|
178
|
+
return spectrogram
|
|
179
|
+
}
|