@siteed/expo-audio-stream 1.16.0 → 2.0.0
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 +28 -1
- package/README.md +1 -1
- package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +68 -22
- package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +24 -0
- package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +836 -386
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +134 -23
- package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +35 -29
- package/android/src/main/java/net/siteed/audiostream/Constants.kt +1 -0
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +236 -96
- package/android/src/main/java/net/siteed/audiostream/FFT.kt +55 -0
- package/android/src/main/java/net/siteed/audiostream/Features.kt +49 -7
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +4 -4
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts +55 -47
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -1
- package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts +60 -13
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -1
- package/build/AudioAnalysis/extractAudioAnalysis.js +147 -162
- package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -1
- package/build/ExpoAudioStream.types.d.ts +49 -3
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +2 -0
- package/build/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/ExpoAudioStream.web.js +8 -1
- package/build/ExpoAudioStream.web.js.map +1 -1
- package/build/ExpoAudioStreamModule.d.ts.map +1 -1
- package/build/ExpoAudioStreamModule.js +216 -12
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/WebRecorder.web.d.ts +67 -13
- package/build/WebRecorder.web.d.ts.map +1 -1
- package/build/WebRecorder.web.js +178 -173
- package/build/WebRecorder.web.js.map +1 -1
- package/build/index.d.ts +3 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -2
- package/build/index.js.map +1 -1
- package/build/useAudioRecorder.d.ts.map +1 -1
- package/build/useAudioRecorder.js +12 -8
- package/build/useAudioRecorder.js.map +1 -1
- package/build/utils/audioProcessing.d.ts +24 -0
- package/build/utils/audioProcessing.d.ts.map +1 -0
- package/build/utils/audioProcessing.js +133 -0
- package/build/utils/audioProcessing.js.map +1 -0
- package/build/workers/InlineFeaturesExtractor.web.d.ts +1 -1
- package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -1
- package/build/workers/InlineFeaturesExtractor.web.js +692 -175
- package/build/workers/InlineFeaturesExtractor.web.js.map +1 -1
- package/build/workers/inlineAudioWebWorker.web.d.ts +1 -1
- package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -1
- package/build/workers/inlineAudioWebWorker.web.js +3 -2
- package/build/workers/inlineAudioWebWorker.web.js.map +1 -1
- package/ios/AudioAnalysisData.swift +51 -16
- package/ios/AudioProcessingHelpers.swift +710 -26
- package/ios/AudioProcessor.swift +334 -185
- package/ios/AudioStreamManager.swift +66 -22
- package/ios/DataPoint.swift +25 -12
- package/ios/DecodingConfig.swift +47 -0
- package/ios/ExpoAudioStreamModule.swift +189 -104
- package/ios/FFT.swift +62 -0
- package/ios/Features.swift +24 -3
- package/ios/RecordingSettings.swift +9 -7
- package/package.json +2 -1
- package/plugin/build/index.d.ts +2 -0
- package/plugin/build/index.js +10 -3
- package/plugin/src/index.ts +10 -1
- package/src/AudioAnalysis/AudioAnalysis.types.ts +68 -52
- package/src/AudioAnalysis/extractAudioAnalysis.ts +223 -219
- package/src/ExpoAudioStream.types.ts +57 -7
- package/src/ExpoAudioStream.web.ts +8 -1
- package/src/ExpoAudioStreamModule.ts +255 -10
- package/src/WebRecorder.web.ts +231 -243
- package/src/index.ts +5 -3
- package/src/useAudioRecorder.tsx +14 -10
- package/src/utils/audioProcessing.ts +205 -0
- package/src/workers/InlineFeaturesExtractor.web.tsx +692 -175
- package/src/workers/inlineAudioWebWorker.web.tsx +3 -2
|
@@ -1,131 +1,130 @@
|
|
|
1
|
+
// packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts
|
|
2
|
+
/**
|
|
3
|
+
* This module provides functions for extracting and analyzing audio data.
|
|
4
|
+
* - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.
|
|
5
|
+
* - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.
|
|
6
|
+
* - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.
|
|
7
|
+
*/
|
|
8
|
+
import crc32 from 'crc-32';
|
|
1
9
|
import ExpoAudioStreamModule from '../ExpoAudioStreamModule';
|
|
2
10
|
import { isWeb } from '../constants';
|
|
11
|
+
import { processAudioBuffer } from '../utils/audioProcessing';
|
|
3
12
|
import { convertPCMToFloat32 } from '../utils/convertPCMToFloat32';
|
|
4
13
|
import { getWavFileInfo } from '../utils/getWavFileInfo';
|
|
5
14
|
import { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web';
|
|
6
|
-
|
|
15
|
+
function calculateCRC32ForDataPoint(data) {
|
|
16
|
+
// Convert float array to byte array for CRC32
|
|
17
|
+
const byteArray = new Uint8Array(data.length * 4);
|
|
18
|
+
const dataView = new DataView(byteArray.buffer);
|
|
19
|
+
for (let i = 0; i < data.length; i++) {
|
|
20
|
+
dataView.setFloat32(i * 4, data[i], true);
|
|
21
|
+
}
|
|
22
|
+
return crc32.buf(byteArray);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extracts detailed audio analysis from the specified audio file or buffer.
|
|
26
|
+
* Supports either time-based or byte-based ranges for flexibility in analysis.
|
|
27
|
+
*
|
|
28
|
+
* @param props - The options for extraction, including file URI, ranges, and decoding settings.
|
|
29
|
+
* @returns A promise that resolves to the audio analysis data.
|
|
30
|
+
* @throws {Error} If both time and byte ranges are provided or if required parameters are missing.
|
|
31
|
+
*/
|
|
32
|
+
export async function extractAudioAnalysis(props) {
|
|
33
|
+
const { fileUri, arrayBuffer, decodingOptions, logger, segmentDurationMs = 100, features, } = props;
|
|
7
34
|
if (isWeb) {
|
|
8
35
|
try {
|
|
9
|
-
//
|
|
10
|
-
let audioBuffer;
|
|
11
|
-
if (arrayBuffer) {
|
|
12
|
-
audioBuffer = arrayBuffer;
|
|
13
|
-
}
|
|
14
|
-
else if (fileUri) {
|
|
15
|
-
const response = await fetch(fileUri);
|
|
16
|
-
if (!response.ok) {
|
|
17
|
-
throw new Error(`Failed to fetch fileUri: ${response.statusText}`);
|
|
18
|
-
}
|
|
19
|
-
audioBuffer = await response.arrayBuffer();
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
throw new Error('Either arrayBuffer or fileUri must be provided');
|
|
23
|
-
}
|
|
24
|
-
// Create audio context with target sample rate if specified
|
|
36
|
+
// Create AudioContext here
|
|
25
37
|
const audioContext = new (window.AudioContext ||
|
|
26
38
|
window.webkitAudioContext)({
|
|
27
|
-
sampleRate: decodingOptions?.targetSampleRate,
|
|
28
|
-
});
|
|
29
|
-
// Decode the audio data
|
|
30
|
-
const decodedAudioBuffer = await audioContext.decodeAudioData(audioBuffer);
|
|
31
|
-
// Calculate the actual duration in milliseconds
|
|
32
|
-
const fullDurationMs = decodedAudioBuffer.duration * 1000;
|
|
33
|
-
const effectiveDurationMs = endTime
|
|
34
|
-
? endTime - (startTime || 0)
|
|
35
|
-
: fullDurationMs - (startTime || 0);
|
|
36
|
-
// Create a new buffer for the selected range
|
|
37
|
-
const rangeLength = decodedAudioBuffer.length;
|
|
38
|
-
const rangeBuffer = new AudioBuffer({
|
|
39
|
-
length: rangeLength,
|
|
40
|
-
numberOfChannels: decodedAudioBuffer.numberOfChannels,
|
|
41
|
-
sampleRate: decodedAudioBuffer.sampleRate,
|
|
39
|
+
sampleRate: decodingOptions?.targetSampleRate ?? 16000,
|
|
42
40
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
41
|
+
try {
|
|
42
|
+
const processedBuffer = await processAudioBuffer({
|
|
43
|
+
arrayBuffer,
|
|
44
|
+
fileUri,
|
|
45
|
+
targetSampleRate: decodingOptions?.targetSampleRate ?? 16000,
|
|
46
|
+
targetChannels: decodingOptions?.targetChannels ?? 1,
|
|
47
|
+
normalizeAudio: decodingOptions?.normalizeAudio ?? false,
|
|
48
|
+
startTimeMs: 'startTimeMs' in props ? props.startTimeMs : undefined,
|
|
49
|
+
endTimeMs: 'endTimeMs' in props ? props.endTimeMs : undefined,
|
|
50
|
+
position: 'position' in props ? props.position : undefined,
|
|
51
|
+
length: 'length' in props ? props.length : undefined,
|
|
52
|
+
audioContext, // Pass the context we created
|
|
53
|
+
logger,
|
|
54
|
+
});
|
|
55
|
+
const channelData = processedBuffer.buffer.getChannelData(0);
|
|
56
|
+
// Create and initialize the worker
|
|
57
|
+
const blob = new Blob([InlineFeaturesExtractor], {
|
|
58
|
+
type: 'application/javascript',
|
|
59
|
+
});
|
|
60
|
+
const workerUrl = URL.createObjectURL(blob);
|
|
61
|
+
const worker = new Worker(workerUrl);
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
worker.onmessage = (event) => {
|
|
64
|
+
if (event.data.error) {
|
|
65
|
+
reject(new Error(event.data.error));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const result = event.data.result;
|
|
69
|
+
// Calculate CRC32 after worker completes if requested
|
|
70
|
+
if (features?.crc32) {
|
|
71
|
+
const samplesPerSegment = Math.floor((processedBuffer.sampleRate *
|
|
72
|
+
segmentDurationMs) /
|
|
73
|
+
1000);
|
|
74
|
+
result.dataPoints = result.dataPoints.map((point, index) => {
|
|
75
|
+
const startSample = index * samplesPerSegment;
|
|
76
|
+
const segmentData = channelData.slice(startSample, startSample + samplesPerSegment);
|
|
77
|
+
return {
|
|
78
|
+
...point,
|
|
79
|
+
features: {
|
|
80
|
+
...point.features,
|
|
81
|
+
crc32: calculateCRC32ForDataPoint(segmentData),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
URL.revokeObjectURL(workerUrl);
|
|
87
|
+
worker.terminate();
|
|
88
|
+
resolve(result);
|
|
89
|
+
};
|
|
90
|
+
worker.onerror = (error) => {
|
|
91
|
+
URL.revokeObjectURL(workerUrl);
|
|
92
|
+
worker.terminate();
|
|
93
|
+
reject(error);
|
|
94
|
+
};
|
|
95
|
+
worker.postMessage({
|
|
96
|
+
channelData,
|
|
97
|
+
sampleRate: processedBuffer.sampleRate,
|
|
98
|
+
segmentDurationMs,
|
|
99
|
+
bitDepth: decodingOptions?.targetBitDepth ?? 32,
|
|
100
|
+
numberOfChannels: processedBuffer.channels,
|
|
101
|
+
// enableLogging: !!logger,
|
|
102
|
+
features,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
82
105
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
for (let channel = 0; channel < numChannels; channel++) {
|
|
86
|
-
const channelData = processedBuffer.getChannelData(channel);
|
|
87
|
-
for (let i = 0; i < newLength; i++) {
|
|
88
|
-
let sample = channelData[i];
|
|
89
|
-
// Normalize if requested
|
|
90
|
-
if (decodingOptions?.normalizeAudio) {
|
|
91
|
-
sample = Math.max(-1, Math.min(1, sample));
|
|
92
|
-
}
|
|
93
|
-
// Convert sample based on target bit depth
|
|
94
|
-
if (decodingOptions?.targetBitDepth === 16) {
|
|
95
|
-
sample = sample * 32767; // Convert to 16-bit range
|
|
96
|
-
}
|
|
97
|
-
else if (decodingOptions?.targetBitDepth === 8) {
|
|
98
|
-
sample = sample * 127; // Convert to 8-bit range
|
|
99
|
-
}
|
|
100
|
-
wavBuffer[i * targetChannels + channel] = sample;
|
|
101
|
-
}
|
|
106
|
+
finally {
|
|
107
|
+
await audioContext.close();
|
|
102
108
|
}
|
|
103
|
-
// Pass the duration to extractAudioAnalysis
|
|
104
|
-
return await extractAudioAnalysis({
|
|
105
|
-
arrayBuffer: wavBuffer.buffer,
|
|
106
|
-
bitDepth: decodingOptions?.targetBitDepth ?? 32,
|
|
107
|
-
skipWavHeader: true,
|
|
108
|
-
sampleRate: targetSampleRate,
|
|
109
|
-
numberOfChannels: targetChannels,
|
|
110
|
-
durationMs: effectiveDurationMs,
|
|
111
|
-
...restProps,
|
|
112
|
-
});
|
|
113
109
|
}
|
|
114
110
|
catch (error) {
|
|
115
|
-
|
|
111
|
+
logger?.error('Failed to process audio:', error);
|
|
116
112
|
throw error;
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
115
|
else {
|
|
120
|
-
|
|
121
|
-
return await extractAudioAnalysis({
|
|
122
|
-
fileUri,
|
|
123
|
-
decodingOptions,
|
|
124
|
-
...restProps,
|
|
125
|
-
});
|
|
116
|
+
return await ExpoAudioStreamModule.extractAudioAnalysis(props);
|
|
126
117
|
}
|
|
127
118
|
}
|
|
128
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Analyzes WAV files without decoding, preserving original PCM values.
|
|
121
|
+
* Use this function when you need to ensure the analysis matches other software by avoiding any transformations.
|
|
122
|
+
*
|
|
123
|
+
* @param props - The options for WAV analysis, including file URI and range.
|
|
124
|
+
* @returns A promise that resolves to the audio analysis data.
|
|
125
|
+
*/
|
|
126
|
+
export const extractRawWavAnalysis = async ({ fileUri, segmentDurationMs = 100, // Default to 100ms
|
|
127
|
+
arrayBuffer, bitDepth, durationMs, sampleRate, numberOfChannels, features, logger, position = 0, length, }) => {
|
|
129
128
|
if (isWeb) {
|
|
130
129
|
if (!arrayBuffer && !fileUri) {
|
|
131
130
|
throw new Error('Either arrayBuffer or fileUri must be provided');
|
|
@@ -141,7 +140,7 @@ export const extractAudioAnalysis = async ({ fileUri, pointsPerSecond = 20, arra
|
|
|
141
140
|
}
|
|
142
141
|
// Create a new copy of the ArrayBuffer to avoid detachment issues
|
|
143
142
|
const bufferCopy = arrayBuffer.slice(0);
|
|
144
|
-
logger?.log(`extractAudioAnalysis
|
|
143
|
+
logger?.log(`extractAudioAnalysis bitDepth=${bitDepth} len=${bufferCopy.byteLength}`, bufferCopy.slice(0, 100));
|
|
145
144
|
let actualBitDepth = bitDepth;
|
|
146
145
|
if (!actualBitDepth) {
|
|
147
146
|
logger?.log(`extractAudioAnalysis bitDepth not provided -- getting wav file info`);
|
|
@@ -152,21 +151,18 @@ export const extractAudioAnalysis = async ({ fileUri, pointsPerSecond = 20, arra
|
|
|
152
151
|
const { pcmValues: channelData, min, max, } = await convertPCMToFloat32({
|
|
153
152
|
buffer: arrayBuffer,
|
|
154
153
|
bitDepth: actualBitDepth,
|
|
155
|
-
skipWavHeader,
|
|
156
154
|
});
|
|
157
|
-
logger?.log(`extractAudioAnalysis
|
|
155
|
+
logger?.log(`extractAudioAnalysis convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`);
|
|
156
|
+
// Apply position and length constraints to channelData if specified
|
|
157
|
+
const startIndex = position;
|
|
158
|
+
const endIndex = length ? startIndex + length : channelData.length;
|
|
159
|
+
const constrainedChannelData = channelData.slice(startIndex, endIndex);
|
|
158
160
|
return new Promise((resolve, reject) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const blob = new Blob([InlineFeaturesExtractor], {
|
|
165
|
-
type: 'application/javascript',
|
|
166
|
-
});
|
|
167
|
-
const url = URL.createObjectURL(blob);
|
|
168
|
-
worker = new Worker(url);
|
|
169
|
-
}
|
|
161
|
+
const blob = new Blob([InlineFeaturesExtractor], {
|
|
162
|
+
type: 'application/javascript',
|
|
163
|
+
});
|
|
164
|
+
const url = URL.createObjectURL(blob);
|
|
165
|
+
const worker = new Worker(url);
|
|
170
166
|
worker.onmessage = (event) => {
|
|
171
167
|
resolve(event.data.result);
|
|
172
168
|
};
|
|
@@ -175,10 +171,10 @@ export const extractAudioAnalysis = async ({ fileUri, pointsPerSecond = 20, arra
|
|
|
175
171
|
};
|
|
176
172
|
worker.postMessage({
|
|
177
173
|
command: 'process',
|
|
178
|
-
channelData,
|
|
174
|
+
channelData: constrainedChannelData,
|
|
179
175
|
sampleRate,
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
segmentDurationMs,
|
|
177
|
+
logger,
|
|
182
178
|
bitDepth,
|
|
183
179
|
fullAudioDurationMs: durationMs,
|
|
184
180
|
numberOfChannels,
|
|
@@ -191,54 +187,43 @@ export const extractAudioAnalysis = async ({ fileUri, pointsPerSecond = 20, arra
|
|
|
191
187
|
}
|
|
192
188
|
logger?.log(`extractAudioAnalysis`, {
|
|
193
189
|
fileUri,
|
|
194
|
-
|
|
195
|
-
algorithm,
|
|
190
|
+
segmentDurationMs,
|
|
196
191
|
});
|
|
197
192
|
const res = await ExpoAudioStreamModule.extractAudioAnalysis({
|
|
198
193
|
fileUri,
|
|
199
|
-
|
|
200
|
-
skipWavHeader,
|
|
201
|
-
algorithm,
|
|
194
|
+
segmentDurationMs,
|
|
202
195
|
features,
|
|
196
|
+
position,
|
|
197
|
+
length,
|
|
203
198
|
});
|
|
204
199
|
logger?.log(`extractAudioAnalysis`, res);
|
|
205
200
|
return res;
|
|
206
201
|
}
|
|
207
202
|
};
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
pointsPerSecond: analysis.pointsPerSecond,
|
|
223
|
-
durationMs: endTime
|
|
224
|
-
? endTime - (startTime || 0)
|
|
225
|
-
: analysis.durationMs, // Use range duration if specified
|
|
226
|
-
amplitudeRange: analysis.amplitudeRange,
|
|
227
|
-
dataPoints: analysis.dataPoints.map((point) => ({
|
|
228
|
-
id: point.id,
|
|
229
|
-
amplitude: point.amplitude,
|
|
230
|
-
startTime: point.startTime,
|
|
231
|
-
endTime: point.endTime,
|
|
232
|
-
})),
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
return await ExpoAudioStreamModule.extractPreview({
|
|
203
|
+
/**
|
|
204
|
+
* Generates a simplified preview of the audio waveform for quick visualization.
|
|
205
|
+
* Ideal for UI rendering with a specified number of points.
|
|
206
|
+
*
|
|
207
|
+
* @param options - The options for the preview, including file URI and time range.
|
|
208
|
+
* @returns A promise that resolves to the audio preview data.
|
|
209
|
+
*/
|
|
210
|
+
export async function extractPreview({ fileUri, numberOfPoints = 100, startTimeMs = 0, endTimeMs = 30000, // First 30 seconds
|
|
211
|
+
decodingOptions, logger, }) {
|
|
212
|
+
const durationMs = endTimeMs - startTimeMs;
|
|
213
|
+
const segmentDurationMs = Math.floor(durationMs / numberOfPoints);
|
|
214
|
+
// Call extractAudioAnalysis with calculated parameters
|
|
215
|
+
const analysis = await extractAudioAnalysis({
|
|
236
216
|
fileUri,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
217
|
+
startTimeMs,
|
|
218
|
+
endTimeMs,
|
|
219
|
+
logger,
|
|
220
|
+
segmentDurationMs,
|
|
241
221
|
decodingOptions,
|
|
242
222
|
});
|
|
223
|
+
// Transform the result into AudioPreview format
|
|
224
|
+
return analysis;
|
|
243
225
|
}
|
|
226
|
+
export const extractAudioData = async (props) => {
|
|
227
|
+
return await ExpoAudioStreamModule.extractAudioData(props);
|
|
228
|
+
};
|
|
244
229
|
//# sourceMappingURL=extractAudioAnalysis.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractAudioAnalysis.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts\nimport { ConsoleLike } from '../ExpoAudioStream.types'\nimport ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { isWeb } from '../constants'\nimport {\n AmplitudeAlgorithm,\n AudioAnalysis,\n AudioFeaturesOptions,\n AudioPreview,\n DecodingConfig,\n PreviewOptions,\n} from './AudioAnalysis.types'\nimport { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'\nimport { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'\nimport { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'\n\nexport interface ExtractAudioAnalysisProps {\n fileUri?: string // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo\n arrayBuffer?: ArrayBuffer\n bitDepth?: number\n skipWavHeader?: boolean\n durationMs?: number\n sampleRate?: number\n numberOfChannels?: number\n algorithm?: AmplitudeAlgorithm\n position?: number // Optional number of bytes to skip. Default is 0\n length?: number // Optional number of bytes to read.\n pointsPerSecond?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions\n featuresExtratorUrl?: string\n logger?: ConsoleLike\n decodingOptions?: DecodingConfig\n}\n\nexport interface ExtractAudioFromAnyFormatProps\n extends ExtractAudioAnalysisProps {\n mimeType?: string\n decodingOptions?: DecodingConfig\n startTime?: number // Add start time in milliseconds\n endTime?: number // Add end time in milliseconds\n}\n\nexport async function extractAudioFromAnyFormat({\n fileUri,\n arrayBuffer,\n mimeType,\n decodingOptions,\n startTime,\n endTime,\n ...restProps\n}: ExtractAudioFromAnyFormatProps): Promise<AudioAnalysis> {\n if (isWeb) {\n try {\n // Get the audio data\n let audioBuffer: ArrayBuffer\n if (arrayBuffer) {\n audioBuffer = arrayBuffer\n } else if (fileUri) {\n const response = await fetch(fileUri)\n if (!response.ok) {\n throw new Error(\n `Failed to fetch fileUri: ${response.statusText}`\n )\n }\n audioBuffer = await response.arrayBuffer()\n } else {\n throw new Error(\n 'Either arrayBuffer or fileUri must be provided'\n )\n }\n\n // Create audio context with target sample rate if specified\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)({\n sampleRate: decodingOptions?.targetSampleRate,\n })\n\n // Decode the audio data\n const decodedAudioBuffer =\n await audioContext.decodeAudioData(audioBuffer)\n\n // Calculate the actual duration in milliseconds\n const fullDurationMs = decodedAudioBuffer.duration * 1000\n const effectiveDurationMs = endTime\n ? endTime - (startTime || 0)\n : fullDurationMs - (startTime || 0)\n\n // Create a new buffer for the selected range\n const rangeLength = decodedAudioBuffer.length\n const rangeBuffer = new AudioBuffer({\n length: rangeLength,\n numberOfChannels: decodedAudioBuffer.numberOfChannels,\n sampleRate: decodedAudioBuffer.sampleRate,\n })\n\n // Copy the selected range\n for (\n let channel = 0;\n channel < decodedAudioBuffer.numberOfChannels;\n channel++\n ) {\n const channelData = decodedAudioBuffer.getChannelData(channel)\n const rangeData = channelData.slice(0, rangeLength)\n rangeBuffer.copyToChannel(rangeData, channel)\n }\n\n // Use the range buffer instead of the full buffer\n let processedBuffer = rangeBuffer\n\n // Get original properties\n const originalSampleRate = processedBuffer.sampleRate\n const length = processedBuffer.length\n\n // Determine target format\n const targetChannels =\n decodingOptions?.targetChannels ??\n processedBuffer.numberOfChannels\n const targetSampleRate =\n decodingOptions?.targetSampleRate ?? processedBuffer.sampleRate\n\n // Create offline context for resampling if needed\n if (targetSampleRate !== originalSampleRate) {\n const offlineCtx = new OfflineAudioContext(\n targetChannels,\n (length * targetSampleRate) / originalSampleRate,\n targetSampleRate\n )\n const source = offlineCtx.createBufferSource()\n source.buffer = processedBuffer\n source.connect(offlineCtx.destination)\n source.start()\n processedBuffer = await offlineCtx.startRendering()\n }\n\n // Convert to the desired format\n const newLength = processedBuffer.length\n let wavBuffer: Float32Array | Int16Array | Int8Array\n\n // Create appropriate buffer based on target bit depth\n switch (decodingOptions?.targetBitDepth) {\n case 16:\n wavBuffer = new Int16Array(newLength * targetChannels)\n break\n case 8:\n wavBuffer = new Int8Array(newLength * targetChannels)\n break\n case 32:\n default:\n wavBuffer = new Float32Array(newLength * targetChannels)\n break\n }\n\n // Interleave channels and handle bit depth conversion\n const numChannels = Math.min(\n processedBuffer.numberOfChannels,\n targetChannels\n )\n for (let channel = 0; channel < numChannels; channel++) {\n const channelData = processedBuffer.getChannelData(channel)\n for (let i = 0; i < newLength; i++) {\n let sample = channelData[i]\n\n // Normalize if requested\n if (decodingOptions?.normalizeAudio) {\n sample = Math.max(-1, Math.min(1, sample))\n }\n\n // Convert sample based on target bit depth\n if (decodingOptions?.targetBitDepth === 16) {\n sample = sample * 32767 // Convert to 16-bit range\n } else if (decodingOptions?.targetBitDepth === 8) {\n sample = sample * 127 // Convert to 8-bit range\n }\n\n wavBuffer[i * targetChannels + channel] = sample\n }\n }\n\n // Pass the duration to extractAudioAnalysis\n return await extractAudioAnalysis({\n arrayBuffer: wavBuffer.buffer as ArrayBuffer,\n bitDepth: decodingOptions?.targetBitDepth ?? 32,\n skipWavHeader: true,\n sampleRate: targetSampleRate,\n numberOfChannels: targetChannels,\n durationMs: effectiveDurationMs,\n ...restProps,\n })\n } catch (error) {\n console.error('Failed to process audio:', error)\n throw error\n }\n } else {\n // For native platforms, pass through all options\n return await extractAudioAnalysis({\n fileUri,\n decodingOptions,\n ...restProps,\n })\n }\n}\n\nexport const extractAudioAnalysis = async ({\n fileUri,\n pointsPerSecond = 20,\n arrayBuffer,\n bitDepth,\n skipWavHeader = true,\n durationMs,\n sampleRate,\n numberOfChannels,\n algorithm = 'rms',\n features,\n featuresExtratorUrl,\n logger,\n}: ExtractAudioAnalysisProps): Promise<AudioAnalysis> => {\n if (isWeb) {\n if (!arrayBuffer && !fileUri) {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n if (!arrayBuffer) {\n logger?.log(`fetching fileUri`, fileUri)\n const response = await fetch(fileUri!)\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch fileUri: ${response.statusText}`\n )\n }\n\n arrayBuffer = await response.arrayBuffer()\n logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)\n }\n\n // Create a new copy of the ArrayBuffer to avoid detachment issues\n const bufferCopy = arrayBuffer.slice(0)\n logger?.log(\n `extractAudioAnalysis skipWavHeader=${skipWavHeader} bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,\n bufferCopy.slice(0, 100)\n )\n\n let actualBitDepth = bitDepth\n if (!actualBitDepth) {\n logger?.log(\n `extractAudioAnalysis bitDepth not provided -- getting wav file info`\n )\n const fileInfo = await getWavFileInfo(bufferCopy)\n actualBitDepth = fileInfo.bitDepth\n }\n logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)\n\n const {\n pcmValues: channelData,\n min,\n max,\n } = await convertPCMToFloat32({\n buffer: arrayBuffer,\n bitDepth: actualBitDepth,\n skipWavHeader,\n })\n logger?.log(\n `extractAudioAnalysis skipWaveHeader=${skipWavHeader} convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`\n )\n\n return new Promise((resolve, reject) => {\n let worker: Worker\n if (featuresExtratorUrl) {\n worker = new Worker(\n new URL(featuresExtratorUrl, window.location.href)\n )\n } else {\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const url = URL.createObjectURL(blob)\n worker = new Worker(url)\n }\n\n worker.onmessage = (event) => {\n resolve(event.data.result)\n }\n\n worker.onerror = (error) => {\n reject(error)\n }\n\n worker.postMessage({\n command: 'process',\n channelData,\n sampleRate,\n pointsPerSecond,\n algorithm,\n bitDepth,\n fullAudioDurationMs: durationMs,\n numberOfChannels,\n })\n })\n } else {\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n logger?.log(`extractAudioAnalysis`, {\n fileUri,\n pointsPerSecond,\n algorithm,\n })\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n pointsPerSecond,\n skipWavHeader,\n algorithm,\n features,\n })\n logger?.log(`extractAudioAnalysis`, res)\n return res\n }\n}\n\nexport async function extractPreview({\n fileUri,\n numberOfPoints,\n algorithm = 'rms',\n startTime,\n endTime,\n decodingOptions,\n}: PreviewOptions): Promise<AudioPreview> {\n if (isWeb) {\n // For web, we can reuse the existing extractAudioFromAnyFormat with modified parameters\n const analysis = await extractAudioFromAnyFormat({\n fileUri,\n algorithm,\n decodingOptions,\n startTime, // Pass startTime\n endTime, // Pass endTime\n pointsPerSecond:\n (numberOfPoints ?? 100) /\n ((endTime ? endTime - (startTime || 0) : 1000) / 1000), // Adjust points per second calculation\n })\n\n // Convert AudioAnalysis to AudioPreview format and adjust duration\n return {\n pointsPerSecond: analysis.pointsPerSecond,\n durationMs: endTime\n ? endTime - (startTime || 0)\n : analysis.durationMs, // Use range duration if specified\n amplitudeRange: analysis.amplitudeRange,\n dataPoints: analysis.dataPoints.map((point) => ({\n id: point.id,\n amplitude: point.amplitude,\n startTime: point.startTime,\n endTime: point.endTime,\n })),\n }\n }\n\n return await ExpoAudioStreamModule.extractPreview({\n fileUri,\n numberOfPoints,\n algorithm,\n startTime,\n endTime,\n decodingOptions,\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"extractAudioAnalysis.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts\n/**\n * This module provides functions for extracting and analyzing audio data.\n * - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.\n * - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.\n * - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.\n */\nimport crc32 from 'crc-32'\n\nimport { ConsoleLike, ExtractAudioDataOptions } from '../ExpoAudioStream.types'\nimport ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { isWeb } from '../constants'\nimport {\n AudioAnalysis,\n AudioFeaturesOptions,\n DataPoint,\n DecodingConfig,\n PreviewOptions,\n} from './AudioAnalysis.types'\nimport { processAudioBuffer } from '../utils/audioProcessing'\nimport { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'\nimport { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'\nimport { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'\n\nfunction calculateCRC32ForDataPoint(data: Float32Array): number {\n // Convert float array to byte array for CRC32\n const byteArray = new Uint8Array(data.length * 4)\n const dataView = new DataView(byteArray.buffer)\n\n for (let i = 0; i < data.length; i++) {\n dataView.setFloat32(i * 4, data[i], true)\n }\n\n return crc32.buf(byteArray)\n}\n\nexport interface ExtractWavAudioAnalysisProps {\n fileUri?: string // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo\n arrayBuffer?: ArrayBuffer\n bitDepth?: number\n durationMs?: number\n sampleRate?: number\n numberOfChannels?: number\n position?: number // Optional number of bytes to skip. Default is 0\n length?: number // Optional number of bytes to read.\n segmentDurationMs?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions\n featuresExtratorUrl?: string\n logger?: ConsoleLike\n decodingOptions?: DecodingConfig\n}\n\n// Define base options interface with common properties\ninterface BaseExtractOptions {\n fileUri?: string\n arrayBuffer?: ArrayBuffer\n /**\n * Duration of each analysis segment in milliseconds. Defaults to 100ms if not specified.\n */\n segmentDurationMs?: number\n features?: AudioFeaturesOptions\n decodingOptions?: DecodingConfig\n logger?: ConsoleLike\n}\n\n// Time-based range options\ninterface TimeRangeOptions extends BaseExtractOptions {\n startTimeMs?: number\n endTimeMs?: number\n position?: never\n length?: never\n}\n\n// Byte-based range options\ninterface ByteRangeOptions extends BaseExtractOptions {\n position?: number\n length?: number\n startTimeMs?: never\n endTimeMs?: never\n}\n\n/**\n * Options for extracting audio analysis.\n * - For time-based analysis, provide `startTimeMs` and `endTimeMs`.\n * - For byte-based analysis, provide `position` and `length`.\n * - Do not mix time and byte ranges.\n */\nexport type ExtractAudioAnalysisProps = TimeRangeOptions | ByteRangeOptions\n\n/**\n * Extracts detailed audio analysis from the specified audio file or buffer.\n * Supports either time-based or byte-based ranges for flexibility in analysis.\n *\n * @param props - The options for extraction, including file URI, ranges, and decoding settings.\n * @returns A promise that resolves to the audio analysis data.\n * @throws {Error} If both time and byte ranges are provided or if required parameters are missing.\n */\nexport async function extractAudioAnalysis(\n props: ExtractAudioAnalysisProps\n): Promise<AudioAnalysis> {\n const {\n fileUri,\n arrayBuffer,\n decodingOptions,\n logger,\n segmentDurationMs = 100,\n features,\n } = props\n\n if (isWeb) {\n try {\n // Create AudioContext here\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)({\n sampleRate: decodingOptions?.targetSampleRate ?? 16000,\n })\n\n try {\n const processedBuffer = await processAudioBuffer({\n arrayBuffer,\n fileUri,\n targetSampleRate:\n decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n startTimeMs:\n 'startTimeMs' in props ? props.startTimeMs : undefined,\n endTimeMs:\n 'endTimeMs' in props ? props.endTimeMs : undefined,\n position: 'position' in props ? props.position : undefined,\n length: 'length' in props ? props.length : undefined,\n audioContext, // Pass the context we created\n logger,\n })\n\n const channelData = processedBuffer.buffer.getChannelData(0)\n\n // Create and initialize the worker\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const workerUrl = URL.createObjectURL(blob)\n const worker = new Worker(workerUrl)\n\n return new Promise((resolve, reject) => {\n worker.onmessage = (event) => {\n if (event.data.error) {\n reject(new Error(event.data.error))\n return\n }\n\n const result: AudioAnalysis = event.data.result\n // Calculate CRC32 after worker completes if requested\n if (features?.crc32) {\n const samplesPerSegment = Math.floor(\n (processedBuffer.sampleRate *\n segmentDurationMs) /\n 1000\n )\n\n result.dataPoints = result.dataPoints.map(\n (point: DataPoint, index: number) => {\n const startSample =\n index * samplesPerSegment\n const segmentData = channelData.slice(\n startSample,\n startSample + samplesPerSegment\n )\n\n return {\n ...point,\n features: {\n ...point.features,\n crc32: calculateCRC32ForDataPoint(\n segmentData\n ),\n },\n }\n }\n )\n }\n\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n resolve(result)\n }\n\n worker.onerror = (error) => {\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n reject(error)\n }\n\n worker.postMessage({\n channelData,\n sampleRate: processedBuffer.sampleRate,\n segmentDurationMs,\n bitDepth: decodingOptions?.targetBitDepth ?? 32,\n numberOfChannels: processedBuffer.channels,\n // enableLogging: !!logger,\n features,\n })\n })\n } finally {\n await audioContext.close()\n }\n } catch (error) {\n logger?.error('Failed to process audio:', error)\n throw error\n }\n } else {\n return await ExpoAudioStreamModule.extractAudioAnalysis(props)\n }\n}\n\n/**\n * Analyzes WAV files without decoding, preserving original PCM values.\n * Use this function when you need to ensure the analysis matches other software by avoiding any transformations.\n *\n * @param props - The options for WAV analysis, including file URI and range.\n * @returns A promise that resolves to the audio analysis data.\n */\nexport const extractRawWavAnalysis = async ({\n fileUri,\n segmentDurationMs = 100, // Default to 100ms\n arrayBuffer,\n bitDepth,\n durationMs,\n sampleRate,\n numberOfChannels,\n features,\n logger,\n position = 0,\n length,\n}: ExtractWavAudioAnalysisProps): Promise<AudioAnalysis> => {\n if (isWeb) {\n if (!arrayBuffer && !fileUri) {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n if (!arrayBuffer) {\n logger?.log(`fetching fileUri`, fileUri)\n const response = await fetch(fileUri!)\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch fileUri: ${response.statusText}`\n )\n }\n\n arrayBuffer = await response.arrayBuffer()\n logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)\n }\n\n // Create a new copy of the ArrayBuffer to avoid detachment issues\n const bufferCopy = arrayBuffer.slice(0)\n logger?.log(\n `extractAudioAnalysis bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,\n bufferCopy.slice(0, 100)\n )\n\n let actualBitDepth = bitDepth\n if (!actualBitDepth) {\n logger?.log(\n `extractAudioAnalysis bitDepth not provided -- getting wav file info`\n )\n const fileInfo = await getWavFileInfo(bufferCopy)\n actualBitDepth = fileInfo.bitDepth\n }\n logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)\n\n const {\n pcmValues: channelData,\n min,\n max,\n } = await convertPCMToFloat32({\n buffer: arrayBuffer,\n bitDepth: actualBitDepth,\n })\n logger?.log(\n `extractAudioAnalysis convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`\n )\n\n // Apply position and length constraints to channelData if specified\n const startIndex = position\n const endIndex = length ? startIndex + length : channelData.length\n const constrainedChannelData = channelData.slice(startIndex, endIndex)\n\n return new Promise((resolve, reject) => {\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const url = URL.createObjectURL(blob)\n const worker = new Worker(url)\n\n worker.onmessage = (event) => {\n resolve(event.data.result)\n }\n\n worker.onerror = (error) => {\n reject(error)\n }\n\n worker.postMessage({\n command: 'process',\n channelData: constrainedChannelData,\n sampleRate,\n segmentDurationMs,\n logger,\n bitDepth,\n fullAudioDurationMs: durationMs,\n numberOfChannels,\n })\n })\n } else {\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n logger?.log(`extractAudioAnalysis`, {\n fileUri,\n segmentDurationMs,\n })\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n segmentDurationMs,\n features,\n position,\n length,\n })\n logger?.log(`extractAudioAnalysis`, res)\n return res\n }\n}\n\n/**\n * Generates a simplified preview of the audio waveform for quick visualization.\n * Ideal for UI rendering with a specified number of points.\n *\n * @param options - The options for the preview, including file URI and time range.\n * @returns A promise that resolves to the audio preview data.\n */\nexport async function extractPreview({\n fileUri,\n numberOfPoints = 100,\n startTimeMs = 0,\n endTimeMs = 30000, // First 30 seconds\n decodingOptions,\n logger,\n}: PreviewOptions): Promise<AudioAnalysis> {\n const durationMs = endTimeMs - startTimeMs\n const segmentDurationMs = Math.floor(durationMs / numberOfPoints)\n\n // Call extractAudioAnalysis with calculated parameters\n const analysis = await extractAudioAnalysis({\n fileUri,\n startTimeMs,\n endTimeMs,\n logger,\n segmentDurationMs,\n decodingOptions,\n })\n\n // Transform the result into AudioPreview format\n return analysis\n}\n\nexport const extractAudioData = async (props: ExtractAudioDataOptions) => {\n return await ExpoAudioStreamModule.extractAudioData(props)\n}"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AudioAnalysis, AudioFeaturesOptions, DecodingConfig } from './AudioAnalysis/AudioAnalysis.types';
|
|
2
2
|
import { AudioAnalysisEvent } from './events';
|
|
3
3
|
export interface CompressionInfo {
|
|
4
4
|
size: number;
|
|
@@ -13,6 +13,7 @@ export interface AudioStreamStatus {
|
|
|
13
13
|
durationMs: number;
|
|
14
14
|
size: number;
|
|
15
15
|
interval: number;
|
|
16
|
+
intervalAnalysis: number;
|
|
16
17
|
mimeType: string;
|
|
17
18
|
compression?: CompressionInfo;
|
|
18
19
|
}
|
|
@@ -29,9 +30,11 @@ export interface AudioDataEvent {
|
|
|
29
30
|
export type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit';
|
|
30
31
|
export type SampleRate = 16000 | 44100 | 48000;
|
|
31
32
|
export type BitDepth = 8 | 16 | 32;
|
|
33
|
+
export type PCMFormat = `pcm_${BitDepth}bit`;
|
|
32
34
|
export type ConsoleLike = {
|
|
33
35
|
log: (message: string, ...args: unknown[]) => void;
|
|
34
36
|
debug: (message: string, ...args: unknown[]) => void;
|
|
37
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
35
38
|
warn: (message: string, ...args: unknown[]) => void;
|
|
36
39
|
error: (message: string, ...args: unknown[]) => void;
|
|
37
40
|
};
|
|
@@ -91,14 +94,14 @@ export interface RecordingConfig {
|
|
|
91
94
|
channels?: 1 | 2;
|
|
92
95
|
encoding?: EncodingType;
|
|
93
96
|
interval?: number;
|
|
97
|
+
intervalAnalysis?: number;
|
|
94
98
|
keepAwake?: boolean;
|
|
95
99
|
showNotification?: boolean;
|
|
96
100
|
showWaveformInNotification?: boolean;
|
|
97
101
|
notification?: NotificationConfig;
|
|
98
102
|
enableProcessing?: boolean;
|
|
99
103
|
ios?: IOSConfig;
|
|
100
|
-
|
|
101
|
-
algorithm?: AmplitudeAlgorithm;
|
|
104
|
+
segmentDurationMs?: number;
|
|
102
105
|
features?: AudioFeaturesOptions;
|
|
103
106
|
onAudioStream?: (_: AudioDataEvent) => Promise<void>;
|
|
104
107
|
onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void>;
|
|
@@ -144,6 +147,49 @@ export interface WaveformConfig {
|
|
|
144
147
|
mirror?: boolean;
|
|
145
148
|
height?: number;
|
|
146
149
|
}
|
|
150
|
+
export interface ExtractAudioDataOptions {
|
|
151
|
+
fileUri: string;
|
|
152
|
+
startTimeMs?: number;
|
|
153
|
+
endTimeMs?: number;
|
|
154
|
+
position?: number;
|
|
155
|
+
length?: number;
|
|
156
|
+
/** Include normalized audio data in [-1, 1] range */
|
|
157
|
+
includeNormalizedData?: boolean;
|
|
158
|
+
/** Include base64 encoded string representation of the audio data */
|
|
159
|
+
includeBase64Data?: boolean;
|
|
160
|
+
/** Include WAV header in the PCM data (makes it a valid WAV file) */
|
|
161
|
+
includeWavHeader?: boolean;
|
|
162
|
+
/** Logger for debugging - can pass console directly. */
|
|
163
|
+
logger?: ConsoleLike;
|
|
164
|
+
/** Compute the checksum of the pcm data */
|
|
165
|
+
computeChecksum?: boolean;
|
|
166
|
+
/** Target config for the normalized audio (Android and Web) */
|
|
167
|
+
decodingOptions?: DecodingConfig;
|
|
168
|
+
}
|
|
169
|
+
export interface ExtractedAudioData {
|
|
170
|
+
/** Raw PCM audio data */
|
|
171
|
+
pcmData: Uint8Array;
|
|
172
|
+
/** Normalized audio data in [-1, 1] range (when includeNormalizedData is true) */
|
|
173
|
+
normalizedData?: Float32Array;
|
|
174
|
+
/** Base64 encoded string representation of the audio data (when includeBase64Data is true) */
|
|
175
|
+
base64Data?: string;
|
|
176
|
+
/** Sample rate in Hz (e.g., 44100, 48000) */
|
|
177
|
+
sampleRate: number;
|
|
178
|
+
/** Number of audio channels (1 for mono, 2 for stereo) */
|
|
179
|
+
channels: number;
|
|
180
|
+
/** Bits per sample (8, 16, or 32) */
|
|
181
|
+
bitDepth: BitDepth;
|
|
182
|
+
/** Duration of the audio in milliseconds */
|
|
183
|
+
durationMs: number;
|
|
184
|
+
/** PCM format identifier (e.g., "pcm_16bit") */
|
|
185
|
+
format: PCMFormat;
|
|
186
|
+
/** Total number of audio samples per channel */
|
|
187
|
+
samples: number;
|
|
188
|
+
/** Whether the pcmData includes a WAV header */
|
|
189
|
+
hasWavHeader?: boolean;
|
|
190
|
+
/** CRC32 Checksum of pcm data */
|
|
191
|
+
checksum?: number;
|
|
192
|
+
}
|
|
147
193
|
export interface UseAudioRecorderState {
|
|
148
194
|
startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>;
|
|
149
195
|
stopRecording: () => Promise<AudioRecording | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.types.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"AACA,OAAO,EACH,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.types.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"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"}
|