@siteed/audio-studio 3.1.1 → 3.2.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 +375 -4
- package/android/src/main/java/net/siteed/audiostudio/AudioStreamDecoder.kt +852 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +167 -3
- package/android/src/main/java/net/siteed/audiostudio/Constants.kt +4 -0
- package/build/cjs/errors/AudioStreamError.js +161 -0
- package/build/cjs/errors/AudioStreamError.js.map +1 -0
- package/build/cjs/errors/AudioStreamError.test.js +82 -0
- package/build/cjs/errors/AudioStreamError.test.js.map +1 -0
- package/build/cjs/index.js +7 -1
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/streamAudioData.js +534 -0
- package/build/cjs/streamAudioData.js.map +1 -0
- package/build/cjs/utils/audioProcessing.js +14 -10
- package/build/cjs/utils/audioProcessing.js.map +1 -1
- package/build/esm/errors/AudioStreamError.js +156 -0
- package/build/esm/errors/AudioStreamError.js.map +1 -0
- package/build/esm/errors/AudioStreamError.test.js +80 -0
- package/build/esm/errors/AudioStreamError.test.js.map +1 -0
- package/build/esm/index.js +3 -1
- package/build/esm/index.js.map +1 -1
- package/build/esm/streamAudioData.js +527 -0
- package/build/esm/streamAudioData.js.map +1 -0
- package/build/esm/utils/audioProcessing.js +14 -10
- package/build/esm/utils/audioProcessing.js.map +1 -1
- package/build/types/errors/AudioStreamError.d.ts +25 -0
- package/build/types/errors/AudioStreamError.d.ts.map +1 -0
- package/build/types/errors/AudioStreamError.test.d.ts +2 -0
- package/build/types/errors/AudioStreamError.test.d.ts.map +1 -0
- package/build/types/index.d.ts +5 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/streamAudioData.d.ts +119 -0
- package/build/types/streamAudioData.d.ts.map +1 -0
- package/build/types/utils/audioProcessing.d.ts +2 -2
- package/build/types/utils/audioProcessing.d.ts.map +1 -1
- package/ios/AudioProcessingHelpers.swift +10 -5
- package/ios/AudioStreamDecoder.swift +614 -0
- package/ios/AudioStudioModule.swift +186 -3
- package/package.json +163 -146
- package/scripts/README.md +58 -0
- package/src/errors/AudioStreamError.test.ts +92 -0
- package/src/errors/AudioStreamError.ts +199 -0
- package/src/index.ts +24 -0
- package/src/streamAudioData.ts +758 -0
- package/src/utils/audioProcessing.ts +25 -14
- package/android/src/androidTest/assets/chorus.wav +0 -0
- package/android/src/androidTest/assets/jfk.wav +0 -0
- package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
- package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/AudioFinalMetadataContractInstrumentedTest.kt +0 -190
- package/android/src/androidTest/java/net/siteed/audiostudio/AudioProcessorInstrumentedTest.kt +0 -197
- package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderInstrumentedTest.kt +0 -487
- package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderPerformanceInstrumentedTest.kt +0 -250
- package/android/src/androidTest/java/net/siteed/audiostudio/OpusRangeDecodeRegressionInstrumentedTest.kt +0 -186
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/AudioFocusStrategyIntegrationTest.kt +0 -332
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/BufferDurationIntegrationTest.kt +0 -324
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/CompressedOnlyOutputTest.kt +0 -253
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/DeviceDisconnectionFallbackTest.kt +0 -218
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/EventEmissionIntervalTest.kt +0 -120
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/M4aFormatTest.kt +0 -345
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/OutputControlIntegrationTest.kt +0 -340
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/PcmStreamingDurationTest.kt +0 -252
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/README.md +0 -95
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/run_integration_tests.sh +0 -43
- package/android/src/test/java/net/siteed/audiostudio/AndroidCallStateTest.kt +0 -37
- package/android/src/test/java/net/siteed/audiostudio/AndroidEventEmitterTest.kt +0 -28
- package/android/src/test/java/net/siteed/audiostudio/AudioFileHandlerTest.kt +0 -279
- package/android/src/test/java/net/siteed/audiostudio/AudioFocusStrategyTest.kt +0 -249
- package/android/src/test/java/net/siteed/audiostudio/AudioFormatTest.kt +0 -151
- package/android/src/test/java/net/siteed/audiostudio/AudioFormatUtilsTest.kt +0 -273
- package/android/src/test/java/net/siteed/audiostudio/DeviceDisconnectionFallbackUnitTest.kt +0 -140
- package/android/src/test/java/net/siteed/audiostudio/InterruptionAutoResumePolicyTest.kt +0 -49
- package/android/src/test/resources/chorus.wav +0 -0
- package/android/src/test/resources/generate_test_audio.py +0 -94
- package/android/src/test/resources/jfk.wav +0 -0
- package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
- package/android/src/test/resources/recorder_hello_world.wav +0 -0
- package/ios/AudioStudioTests/AudioFileHandlerTests.swift +0 -338
- package/ios/AudioStudioTests/AudioFormatUtilsTests.swift +0 -331
- package/ios/AudioStudioTests/AudioTestHelpers.swift +0 -130
- package/ios/AudioStudioTests/CompressedOnlyOutputTests.swift +0 -334
- package/ios/AudioStudioTests/EventEmissionIntervalTests.swift +0 -105
- package/ios/AudioStudioTests/Info.plist +0 -22
- package/ios/AudioStudioTests/README.md +0 -39
- package/ios/AudioStudioTests/SimpleAudioTest.swift +0 -98
- package/ios/AudioStudioTests/TestAudioGenerator.swift +0 -75
- package/ios/tests/README.md +0 -41
- package/ios/tests/integration/buffer_and_fallback_test.swift +0 -178
- package/ios/tests/integration/buffer_duration_test.swift +0 -185
- package/ios/tests/integration/compressed_only_output_test.swift +0 -271
- package/ios/tests/integration/output_control_test.swift +0 -322
- package/ios/tests/integration/run_integration_tests.sh +0 -37
- package/ios/tests/opus_support_test_macos.swift +0 -154
- package/ios/tests/standalone/audio_processing_test.swift +0 -144
- package/ios/tests/standalone/audio_recording_test.swift +0 -277
- package/ios/tests/standalone/audio_streaming_test.swift +0 -249
- package/ios/tests/standalone/standalone_test.swift +0 -144
|
@@ -41,8 +41,11 @@ export async function processAudioBuffer({ arrayBuffer, fileUri, targetSampleRat
|
|
|
41
41
|
// Create context at original sample rate first
|
|
42
42
|
ctx =
|
|
43
43
|
audioContext ||
|
|
44
|
-
new (window.AudioContext ||
|
|
44
|
+
new (window.AudioContext ||
|
|
45
|
+
window.webkitAudioContext)();
|
|
45
46
|
buffer = await ctx.decodeAudioData(audioData);
|
|
47
|
+
const effectiveTargetSampleRate = targetSampleRate ?? buffer.sampleRate;
|
|
48
|
+
const effectiveTargetChannels = targetChannels ?? buffer.numberOfChannels;
|
|
46
49
|
logger?.debug('Decoded audio buffer:', {
|
|
47
50
|
originalChannels: buffer.numberOfChannels,
|
|
48
51
|
originalSampleRate: buffer.sampleRate,
|
|
@@ -60,21 +63,22 @@ export async function processAudioBuffer({ arrayBuffer, fileUri, targetSampleRat
|
|
|
60
63
|
const bytesPerSample = 2; // 16-bit audio = 2 bytes per sample
|
|
61
64
|
const adjustedStartSample = position !== undefined
|
|
62
65
|
? Math.floor((position / bytesPerSample) *
|
|
63
|
-
(buffer.sampleRate /
|
|
66
|
+
(buffer.sampleRate / effectiveTargetSampleRate))
|
|
64
67
|
: startSample;
|
|
65
68
|
const samplesNeeded = length !== undefined
|
|
66
69
|
? Math.floor((length / bytesPerSample) *
|
|
67
|
-
(buffer.sampleRate /
|
|
68
|
-
: endTimeMs !== undefined
|
|
69
|
-
? Math.floor(((endTimeMs - startTimeMs) / 1000) *
|
|
70
|
+
(buffer.sampleRate / effectiveTargetSampleRate))
|
|
71
|
+
: endTimeMs !== undefined
|
|
72
|
+
? Math.floor(((endTimeMs - (startTimeMs ?? 0)) / 1000) *
|
|
73
|
+
buffer.sampleRate)
|
|
70
74
|
: buffer.length - adjustedStartSample;
|
|
71
75
|
logger?.debug('Sample calculations (adjusted):', {
|
|
72
76
|
originalStartSample: startSample,
|
|
73
77
|
adjustedStartSample,
|
|
74
78
|
samplesNeeded,
|
|
75
79
|
originalSampleRate: buffer.sampleRate,
|
|
76
|
-
targetSampleRate,
|
|
77
|
-
conversionRatio: buffer.sampleRate /
|
|
80
|
+
targetSampleRate: effectiveTargetSampleRate,
|
|
81
|
+
conversionRatio: buffer.sampleRate / effectiveTargetSampleRate,
|
|
78
82
|
expectedDurationMs: (samplesNeeded / buffer.sampleRate) * 1000,
|
|
79
83
|
});
|
|
80
84
|
// Create temporary buffer for the segment
|
|
@@ -88,7 +92,7 @@ export async function processAudioBuffer({ arrayBuffer, fileUri, targetSampleRat
|
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
// Create offline context for resampling
|
|
91
|
-
const offlineCtx = new OfflineAudioContext(
|
|
95
|
+
const offlineCtx = new OfflineAudioContext(effectiveTargetChannels, Math.ceil((samplesNeeded * effectiveTargetSampleRate) / buffer.sampleRate), effectiveTargetSampleRate);
|
|
92
96
|
// Create source and connect
|
|
93
97
|
const source = offlineCtx.createBufferSource();
|
|
94
98
|
source.buffer = segmentBuffer;
|
|
@@ -101,7 +105,7 @@ export async function processAudioBuffer({ arrayBuffer, fileUri, targetSampleRat
|
|
|
101
105
|
const durationMs = Math.round((samplesNeeded / buffer.sampleRate) * 1000);
|
|
102
106
|
logger?.debug('Final processed audio:', {
|
|
103
107
|
outputSamples: channelData.length,
|
|
104
|
-
outputSampleRate:
|
|
108
|
+
outputSampleRate: effectiveTargetSampleRate,
|
|
105
109
|
durationMs,
|
|
106
110
|
});
|
|
107
111
|
return {
|
|
@@ -109,7 +113,7 @@ export async function processAudioBuffer({ arrayBuffer, fileUri, targetSampleRat
|
|
|
109
113
|
channelData,
|
|
110
114
|
samples: channelData.length,
|
|
111
115
|
durationMs,
|
|
112
|
-
sampleRate:
|
|
116
|
+
sampleRate: effectiveTargetSampleRate,
|
|
113
117
|
channels: processedBuffer.numberOfChannels,
|
|
114
118
|
};
|
|
115
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audioProcessing.js","sourceRoot":"","sources":["../../../src/utils/audioProcessing.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/audio-studio/src/utils/audioProcessing.ts\nimport { Platform } from 'react-native'\n\nimport { ConsoleLike } from '../AudioStudio.types'\n\nexport interface ProcessAudioBufferOptions {\n arrayBuffer?: ArrayBuffer\n fileUri?: string\n targetSampleRate: number\n targetChannels: number\n normalizeAudio: boolean\n startTimeMs?: number\n endTimeMs?: number\n position?: number\n length?: number\n audioContext?: AudioContext\n logger?: ConsoleLike\n}\n\nexport interface ProcessedAudioData {\n channelData: Float32Array\n samples: number\n durationMs: number\n sampleRate: number\n channels: number\n buffer: AudioBuffer\n}\n\nexport async function processAudioBuffer({\n arrayBuffer,\n fileUri,\n targetSampleRate,\n targetChannels,\n normalizeAudio,\n startTimeMs,\n endTimeMs,\n position,\n length,\n audioContext,\n logger,\n}: ProcessAudioBufferOptions): Promise<ProcessedAudioData> {\n if (Platform.OS !== 'web') {\n throw new Error('processAudioBuffer is only supported on web')\n }\n\n let ctx: AudioContext | undefined\n let buffer: AudioBuffer | undefined\n\n try {\n // Log initial parameters\n logger?.debug('Process audio buffer - Initial params:', {\n hasArrayBuffer: !!arrayBuffer,\n fileUri,\n targetSampleRate,\n targetChannels,\n normalizeAudio,\n startTimeMs,\n endTimeMs,\n position,\n length,\n })\n\n // Get the audio data\n let audioData: ArrayBuffer\n if (arrayBuffer) {\n audioData = 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 audioData = await response.arrayBuffer()\n } else {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n logger?.debug('Audio data loaded:', {\n byteLength: audioData.byteLength,\n firstBytes: Array.from(new Uint8Array(audioData.slice(0, 16))),\n })\n\n // Create context at original sample rate first\n ctx =\n audioContext ||\n new (window.AudioContext || (window as any).webkitAudioContext)()\n buffer = await ctx.decodeAudioData(audioData)\n\n logger?.debug('Decoded audio buffer:', {\n originalChannels: buffer.numberOfChannels,\n originalSampleRate: buffer.sampleRate,\n originalDuration: buffer.duration,\n originalLength: buffer.length,\n })\n\n // Calculate time range\n const startSample =\n startTimeMs !== undefined\n ? Math.floor((startTimeMs / 1000) * buffer.sampleRate)\n : position !== undefined\n ? Math.floor(position / 2)\n : 0\n\n // Fix: Adjust position calculation based on original sample rate\n // When position is provided in bytes, we need to account for the original sample rate\n const bytesPerSample = 2 // 16-bit audio = 2 bytes per sample\n const adjustedStartSample =\n position !== undefined\n ? Math.floor(\n (position / bytesPerSample) *\n (buffer.sampleRate / targetSampleRate)\n )\n : startSample\n\n const samplesNeeded =\n length !== undefined\n ? Math.floor(\n (length / bytesPerSample) *\n (buffer.sampleRate / targetSampleRate)\n )\n : endTimeMs !== undefined && startTimeMs !== undefined\n ? Math.floor(\n ((endTimeMs - startTimeMs) / 1000) * buffer.sampleRate\n )\n : buffer.length - adjustedStartSample\n\n logger?.debug('Sample calculations (adjusted):', {\n originalStartSample: startSample,\n adjustedStartSample,\n samplesNeeded,\n originalSampleRate: buffer.sampleRate,\n targetSampleRate,\n conversionRatio: buffer.sampleRate / targetSampleRate,\n expectedDurationMs: (samplesNeeded / buffer.sampleRate) * 1000,\n })\n\n // Create temporary buffer for the segment\n const segmentBuffer = ctx.createBuffer(\n buffer.numberOfChannels,\n samplesNeeded,\n buffer.sampleRate\n )\n\n // Copy the segment\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const channelData = buffer.getChannelData(channel)\n const segmentData = segmentBuffer.getChannelData(channel)\n for (let i = 0; i < samplesNeeded; i++) {\n segmentData[i] = channelData[adjustedStartSample + i]\n }\n }\n\n // Create offline context for resampling\n const offlineCtx = new OfflineAudioContext(\n targetChannels,\n Math.ceil((samplesNeeded * targetSampleRate) / buffer.sampleRate),\n targetSampleRate\n )\n\n // Create source and connect\n const source = offlineCtx.createBufferSource()\n source.buffer = segmentBuffer\n source.connect(offlineCtx.destination)\n\n // Render at new sample rate\n source.start()\n const processedBuffer = await offlineCtx.startRendering()\n\n // Get the final audio data\n const channelData = processedBuffer.getChannelData(0)\n const durationMs = Math.round(\n (samplesNeeded / buffer.sampleRate) * 1000\n )\n\n logger?.debug('Final processed audio:', {\n outputSamples: channelData.length,\n outputSampleRate: targetSampleRate,\n durationMs,\n })\n\n return {\n buffer: processedBuffer,\n channelData,\n samples: channelData.length,\n durationMs,\n sampleRate: targetSampleRate,\n channels: processedBuffer.numberOfChannels,\n }\n } catch (error) {\n logger?.error('Failed to process audio buffer:', {\n error,\n position,\n length,\n startTimeMs,\n endTimeMs,\n bufferLength: buffer?.length,\n })\n throw error\n } finally {\n if (!audioContext && ctx) {\n await ctx.close()\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"audioProcessing.js","sourceRoot":"","sources":["../../../src/utils/audioProcessing.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/audio-studio/src/utils/audioProcessing.ts\nimport { Platform } from 'react-native'\n\nimport { ConsoleLike } from '../AudioStudio.types'\n\nexport interface ProcessAudioBufferOptions {\n arrayBuffer?: ArrayBuffer\n fileUri?: string\n targetSampleRate?: number\n targetChannels?: number\n normalizeAudio: boolean\n startTimeMs?: number\n endTimeMs?: number\n position?: number\n length?: number\n audioContext?: AudioContext\n logger?: ConsoleLike\n}\n\nexport interface ProcessedAudioData {\n channelData: Float32Array\n samples: number\n durationMs: number\n sampleRate: number\n channels: number\n buffer: AudioBuffer\n}\n\nexport async function processAudioBuffer({\n arrayBuffer,\n fileUri,\n targetSampleRate,\n targetChannels,\n normalizeAudio,\n startTimeMs,\n endTimeMs,\n position,\n length,\n audioContext,\n logger,\n}: ProcessAudioBufferOptions): Promise<ProcessedAudioData> {\n if (Platform.OS !== 'web') {\n throw new Error('processAudioBuffer is only supported on web')\n }\n\n let ctx: AudioContext | undefined\n let buffer: AudioBuffer | undefined\n\n try {\n // Log initial parameters\n logger?.debug('Process audio buffer - Initial params:', {\n hasArrayBuffer: !!arrayBuffer,\n fileUri,\n targetSampleRate,\n targetChannels,\n normalizeAudio,\n startTimeMs,\n endTimeMs,\n position,\n length,\n })\n\n // Get the audio data\n let audioData: ArrayBuffer\n if (arrayBuffer) {\n audioData = 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 audioData = await response.arrayBuffer()\n } else {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n logger?.debug('Audio data loaded:', {\n byteLength: audioData.byteLength,\n firstBytes: Array.from(new Uint8Array(audioData.slice(0, 16))),\n })\n\n // Create context at original sample rate first\n ctx =\n audioContext ||\n new (window.AudioContext ||\n (\n window as unknown as {\n webkitAudioContext?: typeof AudioContext\n }\n ).webkitAudioContext)()\n buffer = await ctx.decodeAudioData(audioData)\n\n const effectiveTargetSampleRate = targetSampleRate ?? buffer.sampleRate\n const effectiveTargetChannels = targetChannels ?? buffer.numberOfChannels\n\n logger?.debug('Decoded audio buffer:', {\n originalChannels: buffer.numberOfChannels,\n originalSampleRate: buffer.sampleRate,\n originalDuration: buffer.duration,\n originalLength: buffer.length,\n })\n\n // Calculate time range\n const startSample =\n startTimeMs !== undefined\n ? Math.floor((startTimeMs / 1000) * buffer.sampleRate)\n : position !== undefined\n ? Math.floor(position / 2)\n : 0\n\n // Fix: Adjust position calculation based on original sample rate\n // When position is provided in bytes, we need to account for the original sample rate\n const bytesPerSample = 2 // 16-bit audio = 2 bytes per sample\n const adjustedStartSample =\n position !== undefined\n ? Math.floor(\n (position / bytesPerSample) *\n (buffer.sampleRate / effectiveTargetSampleRate)\n )\n : startSample\n\n const samplesNeeded =\n length !== undefined\n ? Math.floor(\n (length / bytesPerSample) *\n (buffer.sampleRate / effectiveTargetSampleRate)\n )\n : endTimeMs !== undefined\n ? Math.floor(\n ((endTimeMs - (startTimeMs ?? 0)) / 1000) *\n buffer.sampleRate\n )\n : buffer.length - adjustedStartSample\n\n logger?.debug('Sample calculations (adjusted):', {\n originalStartSample: startSample,\n adjustedStartSample,\n samplesNeeded,\n originalSampleRate: buffer.sampleRate,\n targetSampleRate: effectiveTargetSampleRate,\n conversionRatio: buffer.sampleRate / effectiveTargetSampleRate,\n expectedDurationMs: (samplesNeeded / buffer.sampleRate) * 1000,\n })\n\n // Create temporary buffer for the segment\n const segmentBuffer = ctx.createBuffer(\n buffer.numberOfChannels,\n samplesNeeded,\n buffer.sampleRate\n )\n\n // Copy the segment\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const channelData = buffer.getChannelData(channel)\n const segmentData = segmentBuffer.getChannelData(channel)\n for (let i = 0; i < samplesNeeded; i++) {\n segmentData[i] = channelData[adjustedStartSample + i]\n }\n }\n\n // Create offline context for resampling\n const offlineCtx = new OfflineAudioContext(\n effectiveTargetChannels,\n Math.ceil(\n (samplesNeeded * effectiveTargetSampleRate) / buffer.sampleRate\n ),\n effectiveTargetSampleRate\n )\n\n // Create source and connect\n const source = offlineCtx.createBufferSource()\n source.buffer = segmentBuffer\n source.connect(offlineCtx.destination)\n\n // Render at new sample rate\n source.start()\n const processedBuffer = await offlineCtx.startRendering()\n\n // Get the final audio data\n const channelData = processedBuffer.getChannelData(0)\n const durationMs = Math.round(\n (samplesNeeded / buffer.sampleRate) * 1000\n )\n\n logger?.debug('Final processed audio:', {\n outputSamples: channelData.length,\n outputSampleRate: effectiveTargetSampleRate,\n durationMs,\n })\n\n return {\n buffer: processedBuffer,\n channelData,\n samples: channelData.length,\n durationMs,\n sampleRate: effectiveTargetSampleRate,\n channels: processedBuffer.numberOfChannels,\n }\n } catch (error) {\n logger?.error('Failed to process audio buffer:', {\n error,\n position,\n length,\n startTimeMs,\n endTimeMs,\n bufferLength: buffer?.length,\n })\n throw error\n } finally {\n if (!audioContext && ctx) {\n await ctx.close()\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable typed errors for `streamAudioData`. Callers can switch on `code`.
|
|
3
|
+
*/
|
|
4
|
+
export type AudioStreamErrorCode = 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT' | 'ERR_AUDIO_STREAM_INVALID_RANGE' | 'ERR_AUDIO_STREAM_DECODE_FAILED' | 'ERR_AUDIO_STREAM_CANCELLED' | 'ERR_AUDIO_STREAM_PERMISSION_DENIED' | 'ERR_AUDIO_STREAM_FILE_NOT_FOUND' | 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT' | 'ERR_AUDIO_STREAM_NATIVE_UNAVAILABLE' | 'ERR_AUDIO_STREAM_BUSY' | 'ERR_AUDIO_STREAM_UNKNOWN';
|
|
5
|
+
export interface AudioStreamErrorPayload {
|
|
6
|
+
code: AudioStreamErrorCode;
|
|
7
|
+
message: string;
|
|
8
|
+
recoverable: boolean;
|
|
9
|
+
fileUri?: string;
|
|
10
|
+
platform?: string;
|
|
11
|
+
nativeCode?: string;
|
|
12
|
+
nativeMessage?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class AudioStreamError extends Error {
|
|
15
|
+
readonly code: AudioStreamErrorCode;
|
|
16
|
+
readonly recoverable: boolean;
|
|
17
|
+
readonly fileUri?: string;
|
|
18
|
+
readonly platform?: string;
|
|
19
|
+
readonly nativeCode?: string;
|
|
20
|
+
readonly nativeMessage?: string;
|
|
21
|
+
constructor(payload: AudioStreamErrorPayload);
|
|
22
|
+
toJSON(): AudioStreamErrorPayload;
|
|
23
|
+
}
|
|
24
|
+
export declare function mapStreamError(err: unknown, fileUri?: string, platform?: string): AudioStreamError;
|
|
25
|
+
//# sourceMappingURL=AudioStreamError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioStreamError.d.ts","sourceRoot":"","sources":["../../../src/errors/AudioStreamError.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC1B,qCAAqC,GACrC,gCAAgC,GAChC,gCAAgC,GAChC,4BAA4B,GAC5B,oCAAoC,GACpC,iCAAiC,GACjC,uCAAuC,GACvC,qCAAqC,GACrC,uBAAuB,GACvB,0BAA0B,CAAA;AAEhC,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;CACzB;AASD,qBAAa,gBAAiB,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;gBAEnB,OAAO,EAAE,uBAAuB;IAW5C,MAAM,IAAI,uBAAuB;CAWpC;AAwFD,wBAAgB,cAAc,CAC1B,GAAG,EAAE,OAAO,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAClB,gBAAgB,CA4ClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioStreamError.test.d.ts","sourceRoot":"","sources":["../../../src/errors/AudioStreamError.test.ts"],"names":[],"mappings":""}
|
package/build/types/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { extractPreview } from './AudioAnalysis/extractPreview';
|
|
|
5
5
|
import { initMelStreamingWasm, computeMelFrameWasm } from './AudioAnalysis/melSpectrogramWasm';
|
|
6
6
|
import { AudioRecorderProvider, useSharedAudioRecorder } from './AudioRecorder.provider';
|
|
7
7
|
import AudioStudioModule from './AudioStudioModule';
|
|
8
|
+
import { getAudioDecodeCapabilities, streamAudioData } from './streamAudioData';
|
|
8
9
|
import { trimAudio } from './trimAudio';
|
|
9
10
|
import { useAudioRecorder } from './useAudioRecorder';
|
|
10
11
|
export * from './utils/convertPCMToFloat32';
|
|
@@ -15,9 +16,12 @@ export { AudioDeviceManager, audioDeviceManager } from './AudioDeviceManager';
|
|
|
15
16
|
export { useAudioDevices } from './hooks/useAudioDevices';
|
|
16
17
|
export { setMelSpectrogramWasmUrl } from './AudioAnalysis/wasmConfig';
|
|
17
18
|
export { extractPreviewBars } from './AudioAnalysis/extractPreviewBars';
|
|
18
|
-
export { AudioRecorderProvider, AudioStudioModule, extractRawWavAnalysis, extractAudioAnalysis, extractPreview, trimAudio, extractAudioData, extractMelSpectrogram, initMelStreamingWasm, computeMelFrameWasm, MAX_DURATION_MS, useAudioRecorder, useSharedAudioRecorder, };
|
|
19
|
+
export { AudioRecorderProvider, AudioStudioModule, extractRawWavAnalysis, extractAudioAnalysis, extractPreview, trimAudio, extractAudioData, streamAudioData, getAudioDecodeCapabilities, extractMelSpectrogram, initMelStreamingWasm, computeMelFrameWasm, MAX_DURATION_MS, useAudioRecorder, useSharedAudioRecorder, };
|
|
19
20
|
export { AudioExtractionError, mapExtractionError, } from './errors/AudioExtractionError';
|
|
20
21
|
export type { AudioExtractionErrorCode, AudioExtractionErrorPayload, } from './errors/AudioExtractionError';
|
|
22
|
+
export { AudioStreamError, mapStreamError, } from './errors/AudioStreamError';
|
|
23
|
+
export type { AudioStreamErrorCode, AudioStreamErrorPayload, } from './errors/AudioStreamError';
|
|
24
|
+
export type { StreamAudioDataOptions, StreamAudioDataChunk, StreamAudioDataProgress, StreamAudioDataResult, StreamAudioDataCallbacks, AudioDecodeCapabilities, } from './streamAudioData';
|
|
21
25
|
export type * from './AudioAnalysis/AudioAnalysis.types';
|
|
22
26
|
export type * from './AudioStudio.types';
|
|
23
27
|
/** @deprecated Use AudioStudioModule instead */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,qBAAqB,EACrB,oBAAoB,EACvB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,EACH,qBAAqB,EACrB,eAAe,EAClB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EACH,oBAAoB,EACpB,mBAAmB,EACtB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EACH,qBAAqB,EACrB,sBAAsB,EACzB,MAAM,0BAA0B,CAAA;AACjC,OAAO,iBAAiB,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAGtC,OAAO,EACH,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,oBAAoB,GAC5B,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAG7E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAEvE,OAAO,EACH,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,GACzB,CAAA;AAED,OAAO,EACH,oBAAoB,EACpB,kBAAkB,GACrB,MAAM,+BAA+B,CAAA;AACtC,YAAY,EACR,wBAAwB,EACxB,2BAA2B,GAC9B,MAAM,+BAA+B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,qBAAqB,EACrB,oBAAoB,EACvB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,EACH,qBAAqB,EACrB,eAAe,EAClB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EACH,oBAAoB,EACpB,mBAAmB,EACtB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EACH,qBAAqB,EACrB,sBAAsB,EACzB,MAAM,0BAA0B,CAAA;AACjC,OAAO,iBAAiB,MAAM,qBAAqB,CAAA;AACnD,OAAO,EACH,0BAA0B,EAC1B,eAAe,EAClB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAGtC,OAAO,EACH,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,oBAAoB,GAC5B,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAG7E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAEvE,OAAO,EACH,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,eAAe,EACf,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,GACzB,CAAA;AAED,OAAO,EACH,oBAAoB,EACpB,kBAAkB,GACrB,MAAM,+BAA+B,CAAA;AACtC,YAAY,EACR,wBAAwB,EACxB,2BAA2B,GAC9B,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACH,gBAAgB,EAChB,cAAc,GACjB,MAAM,2BAA2B,CAAA;AAClC,YAAY,EACR,oBAAoB,EACpB,uBAAuB,GAC1B,MAAM,2BAA2B,CAAA;AAElC,YAAY,EACR,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,GAC1B,MAAM,mBAAmB,CAAA;AAG1B,mBAAmB,qCAAqC,CAAA;AACxD,mBAAmB,qBAAqB,CAAA;AAExC,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,KAAoB,CAAA"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level API: stream decoded audio from a stored file as bounded Float32
|
|
3
|
+
* chunks without materializing the full PCM range in memory.
|
|
4
|
+
*
|
|
5
|
+
* See `docs/STREAM_AUDIO_DATA.md` for the full contract and rollout notes.
|
|
6
|
+
*/
|
|
7
|
+
export interface StreamAudioDataOptions {
|
|
8
|
+
/** URI of the audio file to decode. */
|
|
9
|
+
fileUri: string;
|
|
10
|
+
/** Start time in milliseconds (default: 0). */
|
|
11
|
+
startTimeMs?: number;
|
|
12
|
+
/** End time in milliseconds (default: end-of-file). */
|
|
13
|
+
endTimeMs?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Source sample rate hint. Ignored if `targetSampleRate` is set; native
|
|
16
|
+
* decoders read the actual rate from the file.
|
|
17
|
+
*/
|
|
18
|
+
sampleRate?: number;
|
|
19
|
+
/** Output sample rate. Native resamples when this differs from the file. */
|
|
20
|
+
targetSampleRate?: number;
|
|
21
|
+
/** Output channel count (1 = mono downmix, 2 = stereo passthrough). */
|
|
22
|
+
channels?: number;
|
|
23
|
+
/** Clamp samples to [-1, 1] and replace non-finite values with 0. */
|
|
24
|
+
normalizeAudio?: boolean;
|
|
25
|
+
/** Target chunk duration in ms (default: 1000, min: 10, max: 60000). */
|
|
26
|
+
chunkDurationMs?: number;
|
|
27
|
+
/** Soft cap on chunk size in bytes (Float32 = 4 bytes/sample). */
|
|
28
|
+
maxChunkBytes?: number;
|
|
29
|
+
/** Max chunks queued in native before JS ack pauses decode (default: 4). */
|
|
30
|
+
maxBufferedChunks?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Optional timeout for a chunk acknowledgement while backpressure is active.
|
|
33
|
+
* Undefined/0 disables timeout so long transcription callbacks can run.
|
|
34
|
+
*/
|
|
35
|
+
backpressureTimeoutMs?: number;
|
|
36
|
+
/** Output PCM format; only `'float32'` supported today. */
|
|
37
|
+
streamFormat?: 'float32';
|
|
38
|
+
/** Abort the in-flight request. Resolves promise with `cancelled: true`. */
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
}
|
|
41
|
+
export interface StreamAudioDataChunk {
|
|
42
|
+
/** Native request id; constant across all chunks of one call. */
|
|
43
|
+
requestId: string;
|
|
44
|
+
/** Zero-based monotonic chunk index. */
|
|
45
|
+
chunkIndex: number;
|
|
46
|
+
/** Start time in output-rate ms (rounded to nearest sample). */
|
|
47
|
+
startTimeMs: number;
|
|
48
|
+
/** End time in output-rate ms. */
|
|
49
|
+
endTimeMs: number;
|
|
50
|
+
/** Duration in ms (`endTimeMs - startTimeMs`). */
|
|
51
|
+
durationMs: number;
|
|
52
|
+
/** First sample index in the output timeline. */
|
|
53
|
+
startSample: number;
|
|
54
|
+
/** Sample count in `samples` (interleaved if channels > 1). */
|
|
55
|
+
sampleCount: number;
|
|
56
|
+
/** Output sample rate. */
|
|
57
|
+
sampleRate: number;
|
|
58
|
+
/** Output channel count. */
|
|
59
|
+
channels: number;
|
|
60
|
+
/** Interleaved Float32 samples in [-1, 1]. */
|
|
61
|
+
samples: Float32Array;
|
|
62
|
+
/** True for the last chunk of a non-cancelled run. */
|
|
63
|
+
isFinal: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface StreamAudioDataProgress {
|
|
66
|
+
requestId: string;
|
|
67
|
+
processedMs: number;
|
|
68
|
+
durationMs: number;
|
|
69
|
+
progress: number;
|
|
70
|
+
emittedChunks: number;
|
|
71
|
+
bufferedChunks?: number;
|
|
72
|
+
}
|
|
73
|
+
export interface StreamAudioDataResult {
|
|
74
|
+
requestId: string;
|
|
75
|
+
durationMs: number;
|
|
76
|
+
sampleRate: number;
|
|
77
|
+
channels: number;
|
|
78
|
+
chunks: number;
|
|
79
|
+
samples: number;
|
|
80
|
+
cancelled: boolean;
|
|
81
|
+
}
|
|
82
|
+
export interface StreamAudioDataCallbacks {
|
|
83
|
+
/**
|
|
84
|
+
* Called with each decoded chunk. If this returns a Promise, native decode
|
|
85
|
+
* pauses until it resolves (backpressure). Throwing aborts the stream with
|
|
86
|
+
* `ERR_AUDIO_STREAM_DECODE_FAILED`.
|
|
87
|
+
*/
|
|
88
|
+
onChunk: (chunk: StreamAudioDataChunk) => void | Promise<void>;
|
|
89
|
+
/** Called whenever native reports progress. */
|
|
90
|
+
onProgress?: (progress: StreamAudioDataProgress) => void;
|
|
91
|
+
}
|
|
92
|
+
export interface AudioDecodeCapabilities {
|
|
93
|
+
platform: 'ios' | 'android' | 'web';
|
|
94
|
+
supportedInputFormats: string[];
|
|
95
|
+
supportedOutputFormats: Array<'float32'>;
|
|
96
|
+
supportsCancellation: boolean;
|
|
97
|
+
supportsBackpressure: boolean;
|
|
98
|
+
supportsTimeRange: boolean;
|
|
99
|
+
supportsTargetSampleRate: boolean;
|
|
100
|
+
supportsChannelMixing: boolean;
|
|
101
|
+
knownLimitations?: string[];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Stream decoded audio from a stored file as bounded Float32 PCM chunks.
|
|
105
|
+
*
|
|
106
|
+
* Memory bound:
|
|
107
|
+
* `chunkDurationMs * sampleRate * channels * 4 * maxBufferedChunks` +
|
|
108
|
+
* native decoder buffers.
|
|
109
|
+
*
|
|
110
|
+
* Cancellation: pass `options.signal` and call `abort()`. The returned promise
|
|
111
|
+
* resolves with `cancelled: true` (it does not reject) when cancellation wins.
|
|
112
|
+
*
|
|
113
|
+
* Backpressure: if `onChunk` returns a Promise, native decode is paused until
|
|
114
|
+
* it resolves; if it throws, the stream is aborted with a `decode_failed` error.
|
|
115
|
+
*/
|
|
116
|
+
export declare function streamAudioData(options: StreamAudioDataOptions, callbacks: StreamAudioDataCallbacks): Promise<StreamAudioDataResult>;
|
|
117
|
+
/** Discover what the running platform supports. */
|
|
118
|
+
export declare function getAudioDecodeCapabilities(): Promise<AudioDecodeCapabilities>;
|
|
119
|
+
//# sourceMappingURL=streamAudioData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamAudioData.d.ts","sourceRoot":"","sources":["../../src/streamAudioData.ts"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACnC,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAA;IACf,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2DAA2D;IAC3D,YAAY,CAAC,EAAE,SAAS,CAAA;IACxB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,WAAW,CAAA;CACvB;AAED,MAAM,WAAW,oBAAoB;IACjC,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAA;IACjB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAA;IACnB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAA;IACnB,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,8CAA8C;IAC9C,OAAO,EAAE,YAAY,CAAA;IACrB,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,uBAAuB;IACpC,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,wBAAwB;IACrC;;;;OAIG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,+CAA+C;IAC/C,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAA;CAC3D;AAED,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,CAAA;IACnC,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,sBAAsB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;IACxC,oBAAoB,EAAE,OAAO,CAAA;IAC7B,oBAAoB,EAAE,OAAO,CAAA;IAC7B,iBAAiB,EAAE,OAAO,CAAA;IAC1B,wBAAwB,EAAE,OAAO,CAAA;IACjC,qBAAqB,EAAE,OAAO,CAAA;IAC9B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC9B;AAgKD;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACjC,OAAO,EAAE,sBAAsB,EAC/B,SAAS,EAAE,wBAAwB,GACpC,OAAO,CAAC,qBAAqB,CAAC,CAQhC;AAED,mDAAmD;AACnD,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAoCnF"}
|
|
@@ -2,8 +2,8 @@ import { ConsoleLike } from '../AudioStudio.types';
|
|
|
2
2
|
export interface ProcessAudioBufferOptions {
|
|
3
3
|
arrayBuffer?: ArrayBuffer;
|
|
4
4
|
fileUri?: string;
|
|
5
|
-
targetSampleRate
|
|
6
|
-
targetChannels
|
|
5
|
+
targetSampleRate?: number;
|
|
6
|
+
targetChannels?: number;
|
|
7
7
|
normalizeAudio: boolean;
|
|
8
8
|
startTimeMs?: number;
|
|
9
9
|
endTimeMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audioProcessing.d.ts","sourceRoot":"","sources":["../../../src/utils/audioProcessing.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,MAAM,WAAW,yBAAyB;IACtC,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"audioProcessing.d.ts","sourceRoot":"","sources":["../../../src/utils/audioProcessing.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,MAAM,WAAW,yBAAyB;IACtC,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,EAAE,OAAO,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAA;CACvB;AAED,MAAM,WAAW,kBAAkB;IAC/B,WAAW,EAAE,YAAY,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,WAAW,CAAA;CACtB;AAED,wBAAsB,kBAAkB,CAAC,EACrC,WAAW,EACX,OAAO,EACP,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,WAAW,EACX,SAAS,EACT,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,MAAM,GACT,EAAE,yBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA+KzD"}
|
|
@@ -681,15 +681,20 @@ func extractRawAudioData(
|
|
|
681
681
|
for channel in 0..<channels {
|
|
682
682
|
let sample = floatData[channel][frame]
|
|
683
683
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
684
|
+
// Sanitize: replace NaN/Inf with 0 then clamp. Skip clamp when
|
|
685
|
+
// normalizeAudio=false so callers see the raw decoded magnitude,
|
|
686
|
+
// but always finite-check to avoid Swift's `Int16(_:)` /
|
|
687
|
+
// `Int32(_:)` trap on non-finite values.
|
|
688
|
+
let safeSample: Float = sample.isFinite ? sample : 0
|
|
689
|
+
let normalizedSample = decodingConfig.normalizeAudio ?
|
|
690
|
+
max(-1.0, min(1.0, safeSample)) : safeSample
|
|
691
|
+
|
|
687
692
|
switch targetBitDepth {
|
|
688
693
|
case 16:
|
|
689
|
-
let intValue =
|
|
694
|
+
let intValue = safeFloatToInt16(normalizedSample)
|
|
690
695
|
pcmData.append(contentsOf: withUnsafeBytes(of: intValue) { Array($0) })
|
|
691
696
|
case 32:
|
|
692
|
-
let intValue =
|
|
697
|
+
let intValue = safeFloatToInt32(normalizedSample)
|
|
693
698
|
pcmData.append(contentsOf: withUnsafeBytes(of: intValue) { Array($0) })
|
|
694
699
|
default:
|
|
695
700
|
throw NSError(domain: "AudioProcessing", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unsupported bit depth \(targetBitDepth)"])
|