@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
|
@@ -21,14 +21,17 @@ import kotlinx.coroutines.cancelChildren
|
|
|
21
21
|
import kotlinx.coroutines.launch
|
|
22
22
|
import kotlinx.coroutines.withContext
|
|
23
23
|
|
|
24
|
-
class AudioStudioModule : Module(), EventSender {
|
|
24
|
+
class AudioStudioModule : Module(), EventSender, AudioStreamDecoderDelegate {
|
|
25
25
|
companion object {
|
|
26
26
|
private const val CLASS_NAME = "AudioStudioModule"
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
private lateinit var audioRecorderManager: AudioRecorderManager
|
|
30
30
|
private lateinit var audioProcessor: AudioProcessor
|
|
31
31
|
private lateinit var audioDeviceManager: AudioDeviceManager
|
|
32
|
+
|
|
33
|
+
private val streamDecoders = mutableMapOf<String, AudioStreamDecoder>()
|
|
34
|
+
private val streamDecodersLock = Object()
|
|
32
35
|
private var enablePhoneStateHandling: Boolean = false // Default to false until we check manifest
|
|
33
36
|
private var enableNotificationHandling: Boolean = false // Default to false until we check manifest
|
|
34
37
|
private var enableBackgroundAudio: Boolean = false // Default to false until we check manifest
|
|
@@ -88,7 +91,11 @@ class AudioStudioModule : Module(), EventSender {
|
|
|
88
91
|
Constants.AUDIO_ANALYSIS_EVENT_NAME,
|
|
89
92
|
Constants.RECORDING_INTERRUPTED_EVENT_NAME,
|
|
90
93
|
Constants.TRIM_PROGRESS_EVENT,
|
|
91
|
-
Constants.DEVICE_CHANGED_EVENT // Add device changed event name
|
|
94
|
+
Constants.DEVICE_CHANGED_EVENT, // Add device changed event name
|
|
95
|
+
Constants.AUDIO_STREAM_CHUNK_EVENT,
|
|
96
|
+
Constants.AUDIO_STREAM_PROGRESS_EVENT,
|
|
97
|
+
Constants.AUDIO_STREAM_COMPLETE_EVENT,
|
|
98
|
+
Constants.AUDIO_STREAM_ERROR_EVENT
|
|
92
99
|
)
|
|
93
100
|
|
|
94
101
|
// Initialize Managers
|
|
@@ -657,6 +664,21 @@ class AudioStudioModule : Module(), EventSender {
|
|
|
657
664
|
// while keeping the same module instance, and a fully cancelled
|
|
658
665
|
// scope would silently no-op every subsequent launch.
|
|
659
666
|
coroutineScope.coroutineContext.cancelChildren()
|
|
667
|
+
// Cancel any in-flight streamAudioData decoders before teardown.
|
|
668
|
+
// Their worker threads are not children of `coroutineScope` and
|
|
669
|
+
// would otherwise keep running, emitting events through a
|
|
670
|
+
// destroyed module. We clear the map *before* cancelling so that
|
|
671
|
+
// any terminal events the worker emits after observing
|
|
672
|
+
// `cancel()` are dropped by `streamDecoderEmit` (which gates on
|
|
673
|
+
// map membership). The map itself stays usable so Expo can
|
|
674
|
+
// recreate decoders on a dev-client reload without resetting any
|
|
675
|
+
// global flag.
|
|
676
|
+
val inflight = synchronized(streamDecodersLock) {
|
|
677
|
+
val snapshot = streamDecoders.values.toList()
|
|
678
|
+
streamDecoders.clear()
|
|
679
|
+
snapshot
|
|
680
|
+
}
|
|
681
|
+
inflight.forEach { it.cancel() }
|
|
660
682
|
AudioRecorderManager.destroy()
|
|
661
683
|
}
|
|
662
684
|
|
|
@@ -994,6 +1016,148 @@ class AudioStudioModule : Module(), EventSender {
|
|
|
994
1016
|
}
|
|
995
1017
|
}
|
|
996
1018
|
}
|
|
1019
|
+
|
|
1020
|
+
AsyncFunction("streamAudioData") { options: Map<String, Any?>, promise: Promise ->
|
|
1021
|
+
try {
|
|
1022
|
+
val requestId = options["requestId"] as? String
|
|
1023
|
+
?: throw IllegalArgumentException("requestId is required")
|
|
1024
|
+
val fileUri = options["fileUri"] as? String
|
|
1025
|
+
?: throw IllegalArgumentException("fileUri is required")
|
|
1026
|
+
val streamFormat = (options["streamFormat"] as? String) ?: "float32"
|
|
1027
|
+
if (streamFormat != "float32") {
|
|
1028
|
+
promise.reject(
|
|
1029
|
+
"ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT",
|
|
1030
|
+
"Only streamFormat='float32' is supported",
|
|
1031
|
+
null
|
|
1032
|
+
)
|
|
1033
|
+
return@AsyncFunction
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
val context = appContext.reactContext
|
|
1037
|
+
?: throw IllegalStateException("React context not available")
|
|
1038
|
+
|
|
1039
|
+
val chunkDurationMs = (options["chunkDurationMs"] as? Number)?.toInt() ?: 1000
|
|
1040
|
+
if (chunkDurationMs !in 10..60000) {
|
|
1041
|
+
promise.reject(
|
|
1042
|
+
"ERR_AUDIO_STREAM_INVALID_RANGE",
|
|
1043
|
+
"chunkDurationMs must be in [10, 60000]",
|
|
1044
|
+
null
|
|
1045
|
+
)
|
|
1046
|
+
return@AsyncFunction
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
val decoderOptions = AudioStreamDecoder.Options(
|
|
1050
|
+
requestId = requestId,
|
|
1051
|
+
fileUri = fileUri,
|
|
1052
|
+
startTimeMs = (options["startTimeMs"] as? Number)?.toLong(),
|
|
1053
|
+
endTimeMs = (options["endTimeMs"] as? Number)?.toLong(),
|
|
1054
|
+
targetSampleRate = (options["targetSampleRate"] as? Number)?.toInt(),
|
|
1055
|
+
channels = (options["channels"] as? Number)?.toInt(),
|
|
1056
|
+
normalizeAudio = (options["normalizeAudio"] as? Boolean) ?: true,
|
|
1057
|
+
chunkDurationMs = chunkDurationMs,
|
|
1058
|
+
maxChunkBytes = (options["maxChunkBytes"] as? Number)?.toInt(),
|
|
1059
|
+
maxBufferedChunks = ((options["maxBufferedChunks"] as? Number)?.toInt() ?: 4)
|
|
1060
|
+
.coerceAtLeast(1),
|
|
1061
|
+
backpressureTimeoutMs = (options["backpressureTimeoutMs"] as? Number)?.toLong(),
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
val decoder = AudioStreamDecoder(context, decoderOptions, this@AudioStudioModule)
|
|
1065
|
+
synchronized(streamDecodersLock) {
|
|
1066
|
+
if (streamDecoders.containsKey(requestId)) {
|
|
1067
|
+
promise.reject(
|
|
1068
|
+
"ERR_AUDIO_STREAM_BUSY",
|
|
1069
|
+
"requestId already in use",
|
|
1070
|
+
null
|
|
1071
|
+
)
|
|
1072
|
+
return@AsyncFunction
|
|
1073
|
+
}
|
|
1074
|
+
streamDecoders[requestId] = decoder
|
|
1075
|
+
}
|
|
1076
|
+
decoder.start()
|
|
1077
|
+
promise.resolve(bundleOf("requestId" to requestId))
|
|
1078
|
+
} catch (e: Exception) {
|
|
1079
|
+
LogUtils.e(CLASS_NAME, "streamAudioData failed: ${e.message}", e)
|
|
1080
|
+
promise.reject(
|
|
1081
|
+
"ERR_AUDIO_STREAM_DECODE_FAILED",
|
|
1082
|
+
e.message ?: "streamAudioData failed",
|
|
1083
|
+
e
|
|
1084
|
+
)
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
AsyncFunction("cancelStreamAudioData") { requestId: String, promise: Promise ->
|
|
1089
|
+
val decoder = synchronized(streamDecodersLock) {
|
|
1090
|
+
streamDecoders[requestId]
|
|
1091
|
+
}
|
|
1092
|
+
decoder?.cancel()
|
|
1093
|
+
promise.resolve(bundleOf(
|
|
1094
|
+
"requestId" to requestId,
|
|
1095
|
+
"cancelled" to (decoder != null)
|
|
1096
|
+
))
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
Function("acknowledgeStreamAudioChunk") { requestId: String, chunkIndex: Int ->
|
|
1100
|
+
val decoder = synchronized(streamDecodersLock) {
|
|
1101
|
+
streamDecoders[requestId]
|
|
1102
|
+
}
|
|
1103
|
+
decoder?.acknowledgeChunk(chunkIndex)
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
AsyncFunction("getAudioDecodeCapabilities") { promise: Promise ->
|
|
1107
|
+
promise.resolve(bundleOf(
|
|
1108
|
+
"platform" to "android",
|
|
1109
|
+
"supportedInputFormats" to listOf(
|
|
1110
|
+
"audio/wav",
|
|
1111
|
+
"audio/mpeg",
|
|
1112
|
+
"audio/mp4",
|
|
1113
|
+
"audio/aac",
|
|
1114
|
+
"audio/ogg",
|
|
1115
|
+
"audio/opus",
|
|
1116
|
+
"audio/webm",
|
|
1117
|
+
"audio/flac",
|
|
1118
|
+
"audio/amr-wb",
|
|
1119
|
+
),
|
|
1120
|
+
"supportedOutputFormats" to listOf("float32"),
|
|
1121
|
+
"supportsCancellation" to true,
|
|
1122
|
+
"supportsBackpressure" to true,
|
|
1123
|
+
"supportsTimeRange" to true,
|
|
1124
|
+
"supportsTargetSampleRate" to true,
|
|
1125
|
+
"supportsChannelMixing" to true,
|
|
1126
|
+
"knownLimitations" to listOf(
|
|
1127
|
+
"MediaCodec output rate may differ from extractor metadata for some encoders; output is resampled via linear interpolation."
|
|
1128
|
+
)
|
|
1129
|
+
))
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
private fun releaseStreamDecoder(requestId: String) {
|
|
1134
|
+
synchronized(streamDecodersLock) {
|
|
1135
|
+
streamDecoders.remove(requestId)
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
override fun streamDecoderEmit(eventName: String, payload: Bundle) {
|
|
1140
|
+
// Drop events from decoders that are no longer tracked. This catches
|
|
1141
|
+
// both the OnDestroy teardown path (map cleared before `cancel()`) and
|
|
1142
|
+
// post-completion stragglers, without depending on a one-way shutdown
|
|
1143
|
+
// flag that would survive Expo's `definition()` re-runs.
|
|
1144
|
+
val requestId = payload.getString("requestId") ?: return
|
|
1145
|
+
val isActive = synchronized(streamDecodersLock) {
|
|
1146
|
+
streamDecoders.containsKey(requestId)
|
|
1147
|
+
}
|
|
1148
|
+
if (!isActive) return
|
|
1149
|
+
when (eventName) {
|
|
1150
|
+
Constants.AUDIO_STREAM_COMPLETE_EVENT -> {
|
|
1151
|
+
releaseStreamDecoder(requestId)
|
|
1152
|
+
}
|
|
1153
|
+
Constants.AUDIO_STREAM_ERROR_EVENT -> {
|
|
1154
|
+
val code = payload.getString("code") ?: ""
|
|
1155
|
+
if (code != "ERR_AUDIO_STREAM_CANCELLED") {
|
|
1156
|
+
releaseStreamDecoder(requestId)
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
safeSendEvent(eventName, payload)
|
|
997
1161
|
}
|
|
998
1162
|
|
|
999
1163
|
private fun initializeManager() {
|
|
@@ -11,6 +11,10 @@ object Constants {
|
|
|
11
11
|
const val RECORDING_INTERRUPTED_EVENT_NAME = "onRecordingInterrupted"
|
|
12
12
|
const val TRIM_PROGRESS_EVENT = "TrimProgress"
|
|
13
13
|
const val DEVICE_CHANGED_EVENT = "deviceChangedEvent"
|
|
14
|
+
const val AUDIO_STREAM_CHUNK_EVENT = "AudioDataStreamChunk"
|
|
15
|
+
const val AUDIO_STREAM_PROGRESS_EVENT = "AudioDataStreamProgress"
|
|
16
|
+
const val AUDIO_STREAM_COMPLETE_EVENT = "AudioDataStreamComplete"
|
|
17
|
+
const val AUDIO_STREAM_ERROR_EVENT = "AudioDataStreamError"
|
|
14
18
|
|
|
15
19
|
// Audio constants
|
|
16
20
|
const val DEFAULT_SAMPLE_RATE = 16000 // Default sample rate for audio recording
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AudioStreamError = void 0;
|
|
4
|
+
exports.mapStreamError = mapStreamError;
|
|
5
|
+
const RECOVERABLE = [
|
|
6
|
+
'ERR_AUDIO_STREAM_CANCELLED',
|
|
7
|
+
'ERR_AUDIO_STREAM_BUSY',
|
|
8
|
+
'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT',
|
|
9
|
+
'ERR_AUDIO_STREAM_PERMISSION_DENIED',
|
|
10
|
+
];
|
|
11
|
+
class AudioStreamError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
recoverable;
|
|
14
|
+
fileUri;
|
|
15
|
+
platform;
|
|
16
|
+
nativeCode;
|
|
17
|
+
nativeMessage;
|
|
18
|
+
constructor(payload) {
|
|
19
|
+
super(payload.message);
|
|
20
|
+
this.name = 'AudioStreamError';
|
|
21
|
+
this.code = payload.code;
|
|
22
|
+
this.recoverable = payload.recoverable;
|
|
23
|
+
this.fileUri = payload.fileUri;
|
|
24
|
+
this.platform = payload.platform;
|
|
25
|
+
this.nativeCode = payload.nativeCode;
|
|
26
|
+
this.nativeMessage = payload.nativeMessage;
|
|
27
|
+
}
|
|
28
|
+
toJSON() {
|
|
29
|
+
return {
|
|
30
|
+
code: this.code,
|
|
31
|
+
message: this.message,
|
|
32
|
+
recoverable: this.recoverable,
|
|
33
|
+
fileUri: this.fileUri,
|
|
34
|
+
platform: this.platform,
|
|
35
|
+
nativeCode: this.nativeCode,
|
|
36
|
+
nativeMessage: this.nativeMessage,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.AudioStreamError = AudioStreamError;
|
|
41
|
+
function getNativeMessage(err) {
|
|
42
|
+
if (err instanceof Error)
|
|
43
|
+
return err.message;
|
|
44
|
+
if (typeof err === 'string')
|
|
45
|
+
return err;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.stringify(err) ?? String(err);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return String(err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function getNativeCode(err) {
|
|
54
|
+
if (err && typeof err === 'object' && 'code' in err) {
|
|
55
|
+
const code = err.code;
|
|
56
|
+
if (typeof code === 'string')
|
|
57
|
+
return code;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
function isUnknownAudioStreamCode(raw) {
|
|
62
|
+
if (!raw)
|
|
63
|
+
return false;
|
|
64
|
+
return (raw.toUpperCase().startsWith('ERR_AUDIO_STREAM_') &&
|
|
65
|
+
normalizeCode(raw) === null);
|
|
66
|
+
}
|
|
67
|
+
function normalizeCode(raw) {
|
|
68
|
+
if (!raw)
|
|
69
|
+
return null;
|
|
70
|
+
const upper = raw.toUpperCase();
|
|
71
|
+
if (upper.startsWith('ERR_AUDIO_STREAM_')) {
|
|
72
|
+
const known = [
|
|
73
|
+
'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT',
|
|
74
|
+
'ERR_AUDIO_STREAM_INVALID_RANGE',
|
|
75
|
+
'ERR_AUDIO_STREAM_DECODE_FAILED',
|
|
76
|
+
'ERR_AUDIO_STREAM_CANCELLED',
|
|
77
|
+
'ERR_AUDIO_STREAM_PERMISSION_DENIED',
|
|
78
|
+
'ERR_AUDIO_STREAM_FILE_NOT_FOUND',
|
|
79
|
+
'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT',
|
|
80
|
+
'ERR_AUDIO_STREAM_NATIVE_UNAVAILABLE',
|
|
81
|
+
'ERR_AUDIO_STREAM_BUSY',
|
|
82
|
+
'ERR_AUDIO_STREAM_UNKNOWN',
|
|
83
|
+
];
|
|
84
|
+
if (known.includes(upper)) {
|
|
85
|
+
return upper;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (upper.includes('FILE_NOT_FOUND') || upper === 'ENOENT') {
|
|
89
|
+
return 'ERR_AUDIO_STREAM_FILE_NOT_FOUND';
|
|
90
|
+
}
|
|
91
|
+
if (upper.includes('PERMISSION') || upper === 'EACCES') {
|
|
92
|
+
return 'ERR_AUDIO_STREAM_PERMISSION_DENIED';
|
|
93
|
+
}
|
|
94
|
+
if (upper.includes('UNSUPPORTED') ||
|
|
95
|
+
upper.includes('NO_SUITABLE_CODEC') ||
|
|
96
|
+
upper.includes('NO SUITABLE CODEC') ||
|
|
97
|
+
upper.includes('NOT SUPPORTED')) {
|
|
98
|
+
return 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT';
|
|
99
|
+
}
|
|
100
|
+
if (upper.includes('INVALID_RANGE') ||
|
|
101
|
+
upper.includes('OUT_OF_RANGE') ||
|
|
102
|
+
upper.includes('INVALID_TIME')) {
|
|
103
|
+
return 'ERR_AUDIO_STREAM_INVALID_RANGE';
|
|
104
|
+
}
|
|
105
|
+
if (upper.includes('CANCELLED') || upper.includes('CANCELED')) {
|
|
106
|
+
return 'ERR_AUDIO_STREAM_CANCELLED';
|
|
107
|
+
}
|
|
108
|
+
if (upper.includes('BUSY')) {
|
|
109
|
+
return 'ERR_AUDIO_STREAM_BUSY';
|
|
110
|
+
}
|
|
111
|
+
if (upper.includes('BACKPRESSURE')) {
|
|
112
|
+
return 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT';
|
|
113
|
+
}
|
|
114
|
+
if (upper.includes('DECODE') ||
|
|
115
|
+
upper.includes('CODEC') ||
|
|
116
|
+
upper.includes('MALFORMED')) {
|
|
117
|
+
return 'ERR_AUDIO_STREAM_DECODE_FAILED';
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function mapStreamError(err, fileUri, platform) {
|
|
122
|
+
if (err instanceof AudioStreamError)
|
|
123
|
+
return err;
|
|
124
|
+
const nativeMessage = getNativeMessage(err);
|
|
125
|
+
const nativeCode = getNativeCode(err);
|
|
126
|
+
const lower = nativeMessage.toLowerCase();
|
|
127
|
+
if (isUnknownAudioStreamCode(nativeCode)) {
|
|
128
|
+
console.warn(`[AudioStreamError] Unknown native audio stream error code: ${nativeCode}`);
|
|
129
|
+
}
|
|
130
|
+
let code = normalizeCode(nativeCode) ??
|
|
131
|
+
normalizeCode(nativeMessage) ??
|
|
132
|
+
'ERR_AUDIO_STREAM_UNKNOWN';
|
|
133
|
+
if (code === 'ERR_AUDIO_STREAM_UNKNOWN') {
|
|
134
|
+
if (lower.includes('not found') || lower.includes('does not exist')) {
|
|
135
|
+
code = 'ERR_AUDIO_STREAM_FILE_NOT_FOUND';
|
|
136
|
+
}
|
|
137
|
+
else if (lower.includes('unsupported') ||
|
|
138
|
+
lower.includes('no suitable codec')) {
|
|
139
|
+
code = 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT';
|
|
140
|
+
}
|
|
141
|
+
else if (lower.includes('permission') || lower.includes('denied')) {
|
|
142
|
+
code = 'ERR_AUDIO_STREAM_PERMISSION_DENIED';
|
|
143
|
+
}
|
|
144
|
+
else if (lower.includes('decode') || lower.includes('codec')) {
|
|
145
|
+
code = 'ERR_AUDIO_STREAM_DECODE_FAILED';
|
|
146
|
+
}
|
|
147
|
+
else if (lower.includes('cancel')) {
|
|
148
|
+
code = 'ERR_AUDIO_STREAM_CANCELLED';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return new AudioStreamError({
|
|
152
|
+
code,
|
|
153
|
+
message: `Audio stream failed (${code}): ${nativeMessage}`,
|
|
154
|
+
recoverable: RECOVERABLE.includes(code),
|
|
155
|
+
fileUri,
|
|
156
|
+
platform,
|
|
157
|
+
nativeCode,
|
|
158
|
+
nativeMessage,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=AudioStreamError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioStreamError.js","sourceRoot":"","sources":["../../../src/errors/AudioStreamError.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Stable typed errors for `streamAudioData`. Callers can switch on `code`.\n */\nexport type AudioStreamErrorCode =\n | 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT'\n | 'ERR_AUDIO_STREAM_INVALID_RANGE'\n | 'ERR_AUDIO_STREAM_DECODE_FAILED'\n | 'ERR_AUDIO_STREAM_CANCELLED'\n | 'ERR_AUDIO_STREAM_PERMISSION_DENIED'\n | 'ERR_AUDIO_STREAM_FILE_NOT_FOUND'\n | 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT'\n | 'ERR_AUDIO_STREAM_NATIVE_UNAVAILABLE'\n | 'ERR_AUDIO_STREAM_BUSY'\n | 'ERR_AUDIO_STREAM_UNKNOWN'\n\nexport interface AudioStreamErrorPayload {\n code: AudioStreamErrorCode\n message: string\n recoverable: boolean\n fileUri?: string\n platform?: string\n nativeCode?: string\n nativeMessage?: string\n}\n\nconst RECOVERABLE: AudioStreamErrorCode[] = [\n 'ERR_AUDIO_STREAM_CANCELLED',\n 'ERR_AUDIO_STREAM_BUSY',\n 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT',\n 'ERR_AUDIO_STREAM_PERMISSION_DENIED',\n]\n\nexport class AudioStreamError extends Error {\n readonly code: AudioStreamErrorCode\n readonly recoverable: boolean\n readonly fileUri?: string\n readonly platform?: string\n readonly nativeCode?: string\n readonly nativeMessage?: string\n\n constructor(payload: AudioStreamErrorPayload) {\n super(payload.message)\n this.name = 'AudioStreamError'\n this.code = payload.code\n this.recoverable = payload.recoverable\n this.fileUri = payload.fileUri\n this.platform = payload.platform\n this.nativeCode = payload.nativeCode\n this.nativeMessage = payload.nativeMessage\n }\n\n toJSON(): AudioStreamErrorPayload {\n return {\n code: this.code,\n message: this.message,\n recoverable: this.recoverable,\n fileUri: this.fileUri,\n platform: this.platform,\n nativeCode: this.nativeCode,\n nativeMessage: this.nativeMessage,\n }\n }\n}\n\nfunction getNativeMessage(err: unknown): string {\n if (err instanceof Error) return err.message\n if (typeof err === 'string') return err\n try {\n return JSON.stringify(err) ?? String(err)\n } catch {\n return String(err)\n }\n}\n\nfunction getNativeCode(err: unknown): string | undefined {\n if (err && typeof err === 'object' && 'code' in err) {\n const code = (err as { code?: unknown }).code\n if (typeof code === 'string') return code\n }\n return undefined\n}\n\nfunction isUnknownAudioStreamCode(raw: string | undefined): boolean {\n if (!raw) return false\n return (\n raw.toUpperCase().startsWith('ERR_AUDIO_STREAM_') &&\n normalizeCode(raw) === null\n )\n}\n\nfunction normalizeCode(raw: string | undefined): AudioStreamErrorCode | null {\n if (!raw) return null\n const upper = raw.toUpperCase()\n if (upper.startsWith('ERR_AUDIO_STREAM_')) {\n const known: AudioStreamErrorCode[] = [\n 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT',\n 'ERR_AUDIO_STREAM_INVALID_RANGE',\n 'ERR_AUDIO_STREAM_DECODE_FAILED',\n 'ERR_AUDIO_STREAM_CANCELLED',\n 'ERR_AUDIO_STREAM_PERMISSION_DENIED',\n 'ERR_AUDIO_STREAM_FILE_NOT_FOUND',\n 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT',\n 'ERR_AUDIO_STREAM_NATIVE_UNAVAILABLE',\n 'ERR_AUDIO_STREAM_BUSY',\n 'ERR_AUDIO_STREAM_UNKNOWN',\n ]\n if ((known as string[]).includes(upper)) {\n return upper as AudioStreamErrorCode\n }\n }\n if (upper.includes('FILE_NOT_FOUND') || upper === 'ENOENT') {\n return 'ERR_AUDIO_STREAM_FILE_NOT_FOUND'\n }\n if (upper.includes('PERMISSION') || upper === 'EACCES') {\n return 'ERR_AUDIO_STREAM_PERMISSION_DENIED'\n }\n if (\n upper.includes('UNSUPPORTED') ||\n upper.includes('NO_SUITABLE_CODEC') ||\n upper.includes('NO SUITABLE CODEC') ||\n upper.includes('NOT SUPPORTED')\n ) {\n return 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT'\n }\n if (\n upper.includes('INVALID_RANGE') ||\n upper.includes('OUT_OF_RANGE') ||\n upper.includes('INVALID_TIME')\n ) {\n return 'ERR_AUDIO_STREAM_INVALID_RANGE'\n }\n if (upper.includes('CANCELLED') || upper.includes('CANCELED')) {\n return 'ERR_AUDIO_STREAM_CANCELLED'\n }\n if (upper.includes('BUSY')) {\n return 'ERR_AUDIO_STREAM_BUSY'\n }\n if (upper.includes('BACKPRESSURE')) {\n return 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT'\n }\n if (\n upper.includes('DECODE') ||\n upper.includes('CODEC') ||\n upper.includes('MALFORMED')\n ) {\n return 'ERR_AUDIO_STREAM_DECODE_FAILED'\n }\n return null\n}\n\nexport function mapStreamError(\n err: unknown,\n fileUri?: string,\n platform?: string\n): AudioStreamError {\n if (err instanceof AudioStreamError) return err\n\n const nativeMessage = getNativeMessage(err)\n const nativeCode = getNativeCode(err)\n const lower = nativeMessage.toLowerCase()\n\n if (isUnknownAudioStreamCode(nativeCode)) {\n console.warn(\n `[AudioStreamError] Unknown native audio stream error code: ${nativeCode}`\n )\n }\n\n let code =\n normalizeCode(nativeCode) ??\n normalizeCode(nativeMessage) ??\n 'ERR_AUDIO_STREAM_UNKNOWN'\n\n if (code === 'ERR_AUDIO_STREAM_UNKNOWN') {\n if (lower.includes('not found') || lower.includes('does not exist')) {\n code = 'ERR_AUDIO_STREAM_FILE_NOT_FOUND'\n } else if (\n lower.includes('unsupported') ||\n lower.includes('no suitable codec')\n ) {\n code = 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT'\n } else if (lower.includes('permission') || lower.includes('denied')) {\n code = 'ERR_AUDIO_STREAM_PERMISSION_DENIED'\n } else if (lower.includes('decode') || lower.includes('codec')) {\n code = 'ERR_AUDIO_STREAM_DECODE_FAILED'\n } else if (lower.includes('cancel')) {\n code = 'ERR_AUDIO_STREAM_CANCELLED'\n }\n }\n\n return new AudioStreamError({\n code,\n message: `Audio stream failed (${code}): ${nativeMessage}`,\n recoverable: RECOVERABLE.includes(code),\n fileUri,\n platform,\n nativeCode,\n nativeMessage,\n })\n}\n"]}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const AudioStreamError_1 = require("./AudioStreamError");
|
|
4
|
+
describe('AudioStreamError', () => {
|
|
5
|
+
it('passes through an existing AudioStreamError unchanged', () => {
|
|
6
|
+
const original = new AudioStreamError_1.AudioStreamError({
|
|
7
|
+
code: 'ERR_AUDIO_STREAM_CANCELLED',
|
|
8
|
+
message: 'aborted',
|
|
9
|
+
recoverable: true,
|
|
10
|
+
});
|
|
11
|
+
expect((0, AudioStreamError_1.mapStreamError)(original)).toBe(original);
|
|
12
|
+
});
|
|
13
|
+
it('maps native FILE_NOT_FOUND code', () => {
|
|
14
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({
|
|
15
|
+
code: 'FILE_NOT_FOUND',
|
|
16
|
+
message: 'gone',
|
|
17
|
+
});
|
|
18
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_FILE_NOT_FOUND');
|
|
19
|
+
expect(mapped.recoverable).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
it('maps unsupported codec text', () => {
|
|
22
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)(new Error('No suitable codec for audio/opus'));
|
|
23
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT');
|
|
24
|
+
});
|
|
25
|
+
it('marks cancellation as recoverable', () => {
|
|
26
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({
|
|
27
|
+
code: 'ERR_AUDIO_STREAM_CANCELLED',
|
|
28
|
+
message: 'user cancelled',
|
|
29
|
+
});
|
|
30
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_CANCELLED');
|
|
31
|
+
expect(mapped.recoverable).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('maps backpressure timeout as recoverable', () => {
|
|
34
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({
|
|
35
|
+
code: 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT',
|
|
36
|
+
message: 'ack timed out',
|
|
37
|
+
});
|
|
38
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT');
|
|
39
|
+
expect(mapped.recoverable).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('falls back to UNKNOWN', () => {
|
|
42
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({});
|
|
43
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNKNOWN');
|
|
44
|
+
});
|
|
45
|
+
it('warns when native returns an unknown audio stream code', () => {
|
|
46
|
+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
47
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({
|
|
48
|
+
code: 'ERR_AUDIO_STREAM_FOOBAR',
|
|
49
|
+
message: 'new native code',
|
|
50
|
+
});
|
|
51
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNKNOWN');
|
|
52
|
+
expect(warn).toHaveBeenCalledWith('[AudioStreamError] Unknown native audio stream error code: ERR_AUDIO_STREAM_FOOBAR');
|
|
53
|
+
warn.mockRestore();
|
|
54
|
+
});
|
|
55
|
+
it('preserves nativeCode and nativeMessage', () => {
|
|
56
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({
|
|
57
|
+
code: 'WEIRD_NATIVE_CODE',
|
|
58
|
+
message: 'something went wrong on the bridge',
|
|
59
|
+
});
|
|
60
|
+
expect(mapped.nativeCode).toBe('WEIRD_NATIVE_CODE');
|
|
61
|
+
expect(mapped.nativeMessage).toContain('bridge');
|
|
62
|
+
});
|
|
63
|
+
it('serialises to a stable JSON payload', () => {
|
|
64
|
+
const err = new AudioStreamError_1.AudioStreamError({
|
|
65
|
+
code: 'ERR_AUDIO_STREAM_DECODE_FAILED',
|
|
66
|
+
message: 'decoder bust',
|
|
67
|
+
recoverable: false,
|
|
68
|
+
fileUri: 'file:///a.m4a',
|
|
69
|
+
platform: 'ios',
|
|
70
|
+
});
|
|
71
|
+
expect(err.toJSON()).toEqual({
|
|
72
|
+
code: 'ERR_AUDIO_STREAM_DECODE_FAILED',
|
|
73
|
+
message: 'decoder bust',
|
|
74
|
+
recoverable: false,
|
|
75
|
+
fileUri: 'file:///a.m4a',
|
|
76
|
+
platform: 'ios',
|
|
77
|
+
nativeCode: undefined,
|
|
78
|
+
nativeMessage: undefined,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=AudioStreamError.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioStreamError.test.js","sourceRoot":"","sources":["../../../src/errors/AudioStreamError.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { AudioStreamError, mapStreamError } from './AudioStreamError'\n\ndescribe('AudioStreamError', () => {\n it('passes through an existing AudioStreamError unchanged', () => {\n const original = new AudioStreamError({\n code: 'ERR_AUDIO_STREAM_CANCELLED',\n message: 'aborted',\n recoverable: true,\n })\n expect(mapStreamError(original)).toBe(original)\n })\n\n it('maps native FILE_NOT_FOUND code', () => {\n const mapped = mapStreamError({\n code: 'FILE_NOT_FOUND',\n message: 'gone',\n })\n expect(mapped.code).toBe('ERR_AUDIO_STREAM_FILE_NOT_FOUND')\n expect(mapped.recoverable).toBe(false)\n })\n\n it('maps unsupported codec text', () => {\n const mapped = mapStreamError(\n new Error('No suitable codec for audio/opus')\n )\n expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT')\n })\n\n it('marks cancellation as recoverable', () => {\n const mapped = mapStreamError({\n code: 'ERR_AUDIO_STREAM_CANCELLED',\n message: 'user cancelled',\n })\n expect(mapped.code).toBe('ERR_AUDIO_STREAM_CANCELLED')\n expect(mapped.recoverable).toBe(true)\n })\n\n it('maps backpressure timeout as recoverable', () => {\n const mapped = mapStreamError({\n code: 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT',\n message: 'ack timed out',\n })\n expect(mapped.code).toBe('ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT')\n expect(mapped.recoverable).toBe(true)\n })\n\n it('falls back to UNKNOWN', () => {\n const mapped = mapStreamError({})\n expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNKNOWN')\n })\n\n it('warns when native returns an unknown audio stream code', () => {\n const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})\n const mapped = mapStreamError({\n code: 'ERR_AUDIO_STREAM_FOOBAR',\n message: 'new native code',\n })\n expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNKNOWN')\n expect(warn).toHaveBeenCalledWith(\n '[AudioStreamError] Unknown native audio stream error code: ERR_AUDIO_STREAM_FOOBAR'\n )\n warn.mockRestore()\n })\n\n it('preserves nativeCode and nativeMessage', () => {\n const mapped = mapStreamError({\n code: 'WEIRD_NATIVE_CODE',\n message: 'something went wrong on the bridge',\n })\n expect(mapped.nativeCode).toBe('WEIRD_NATIVE_CODE')\n expect(mapped.nativeMessage).toContain('bridge')\n })\n\n it('serialises to a stable JSON payload', () => {\n const err = new AudioStreamError({\n code: 'ERR_AUDIO_STREAM_DECODE_FAILED',\n message: 'decoder bust',\n recoverable: false,\n fileUri: 'file:///a.m4a',\n platform: 'ios',\n })\n expect(err.toJSON()).toEqual({\n code: 'ERR_AUDIO_STREAM_DECODE_FAILED',\n message: 'decoder bust',\n recoverable: false,\n fileUri: 'file:///a.m4a',\n platform: 'ios',\n nativeCode: undefined,\n nativeMessage: undefined,\n })\n })\n})\n"]}
|
package/build/cjs/index.js
CHANGED
|
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.ExpoAudioStreamModule = exports.mapExtractionError = exports.AudioExtractionError = exports.useSharedAudioRecorder = exports.useAudioRecorder = exports.MAX_DURATION_MS = exports.computeMelFrameWasm = exports.initMelStreamingWasm = exports.extractMelSpectrogram = exports.extractAudioData = exports.trimAudio = exports.extractPreview = exports.extractAudioAnalysis = exports.extractRawWavAnalysis = exports.AudioStudioModule = exports.AudioRecorderProvider = exports.extractPreviewBars = exports.setMelSpectrogramWasmUrl = exports.useAudioDevices = exports.audioDeviceManager = exports.AudioDeviceManager = exports.validateRecordingConfig = exports.getFallbackBitDepth = exports.getFallbackEncoding = exports.isBitDepthSupported = exports.isEncodingSupported = exports.getPlatformCapabilities = void 0;
|
|
21
|
+
exports.ExpoAudioStreamModule = exports.mapStreamError = exports.AudioStreamError = exports.mapExtractionError = exports.AudioExtractionError = exports.useSharedAudioRecorder = exports.useAudioRecorder = exports.MAX_DURATION_MS = exports.computeMelFrameWasm = exports.initMelStreamingWasm = exports.extractMelSpectrogram = exports.getAudioDecodeCapabilities = exports.streamAudioData = exports.extractAudioData = exports.trimAudio = exports.extractPreview = exports.extractAudioAnalysis = exports.extractRawWavAnalysis = exports.AudioStudioModule = exports.AudioRecorderProvider = exports.extractPreviewBars = exports.setMelSpectrogramWasmUrl = exports.useAudioDevices = exports.audioDeviceManager = exports.AudioDeviceManager = exports.validateRecordingConfig = exports.getFallbackBitDepth = exports.getFallbackEncoding = exports.isBitDepthSupported = exports.isEncodingSupported = exports.getPlatformCapabilities = void 0;
|
|
22
22
|
const extractAudioAnalysis_1 = require("./AudioAnalysis/extractAudioAnalysis");
|
|
23
23
|
Object.defineProperty(exports, "extractRawWavAnalysis", { enumerable: true, get: function () { return extractAudioAnalysis_1.extractRawWavAnalysis; } });
|
|
24
24
|
Object.defineProperty(exports, "extractAudioAnalysis", { enumerable: true, get: function () { return extractAudioAnalysis_1.extractAudioAnalysis; } });
|
|
@@ -37,6 +37,9 @@ Object.defineProperty(exports, "AudioRecorderProvider", { enumerable: true, get:
|
|
|
37
37
|
Object.defineProperty(exports, "useSharedAudioRecorder", { enumerable: true, get: function () { return AudioRecorder_provider_1.useSharedAudioRecorder; } });
|
|
38
38
|
const AudioStudioModule_1 = __importDefault(require("./AudioStudioModule"));
|
|
39
39
|
exports.AudioStudioModule = AudioStudioModule_1.default;
|
|
40
|
+
const streamAudioData_1 = require("./streamAudioData");
|
|
41
|
+
Object.defineProperty(exports, "getAudioDecodeCapabilities", { enumerable: true, get: function () { return streamAudioData_1.getAudioDecodeCapabilities; } });
|
|
42
|
+
Object.defineProperty(exports, "streamAudioData", { enumerable: true, get: function () { return streamAudioData_1.streamAudioData; } });
|
|
40
43
|
const trimAudio_1 = require("./trimAudio");
|
|
41
44
|
Object.defineProperty(exports, "trimAudio", { enumerable: true, get: function () { return trimAudio_1.trimAudio; } });
|
|
42
45
|
const useAudioRecorder_1 = require("./useAudioRecorder");
|
|
@@ -66,6 +69,9 @@ Object.defineProperty(exports, "extractPreviewBars", { enumerable: true, get: fu
|
|
|
66
69
|
var AudioExtractionError_1 = require("./errors/AudioExtractionError");
|
|
67
70
|
Object.defineProperty(exports, "AudioExtractionError", { enumerable: true, get: function () { return AudioExtractionError_1.AudioExtractionError; } });
|
|
68
71
|
Object.defineProperty(exports, "mapExtractionError", { enumerable: true, get: function () { return AudioExtractionError_1.mapExtractionError; } });
|
|
72
|
+
var AudioStreamError_1 = require("./errors/AudioStreamError");
|
|
73
|
+
Object.defineProperty(exports, "AudioStreamError", { enumerable: true, get: function () { return AudioStreamError_1.AudioStreamError; } });
|
|
74
|
+
Object.defineProperty(exports, "mapStreamError", { enumerable: true, get: function () { return AudioStreamError_1.mapStreamError; } });
|
|
69
75
|
/** @deprecated Use AudioStudioModule instead */
|
|
70
76
|
exports.ExpoAudioStreamModule = AudioStudioModule_1.default;
|
|
71
77
|
//# sourceMappingURL=index.js.map
|
package/build/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,eAAe;;;;;;;;;;;;;;;;;;;;AAEf,+EAG6C;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,eAAe;;;;;;;;;;;;;;;;;;;;AAEf,+EAG6C;AAkDzC,sGApDA,4CAAqB,OAoDA;AACrB,qGApDA,2CAAoB,OAoDA;AAlDxB,uEAAmE;AAqD/D,iGArDK,mCAAgB,OAqDL;AApDpB,iFAG8C;AAoD1C,sGAtDA,6CAAqB,OAsDA;AAGrB,gGAxDA,uCAAe,OAwDA;AAtDnB,mEAA+D;AA8C3D,+FA9CK,+BAAc,OA8CL;AA7ClB,2EAG2C;AAgDvC,qGAlDA,yCAAoB,OAkDA;AACpB,oGAlDA,wCAAmB,OAkDA;AAhDvB,qEAGiC;AAkC7B,sGApCA,8CAAqB,OAoCA;AAcrB,uGAjDA,+CAAsB,OAiDA;AA/C1B,4EAAmD;AAkC/C,4BAlCG,2BAAiB,CAkCH;AAjCrB,uDAG0B;AAqCtB,2GAvCA,4CAA0B,OAuCA;AAD1B,gGArCA,iCAAe,OAqCA;AAnCnB,2CAAuC;AAiCnC,0FAjCK,qBAAS,OAiCL;AAhCb,yDAAqD;AAwCjD,iGAxCK,mCAAgB,OAwCL;AAtCpB,8DAA2C;AAC3C,yDAAsC;AACtC,yDAAsC;AAEtC,+BAA+B;AAC/B,uEAQwC;AAPpC,8HAAA,uBAAuB,OAAA;AACvB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,8HAAA,uBAAuB,OAAA;AAI3B,4BAA4B;AAC5B,2DAA6E;AAApE,wHAAA,kBAAkB,OAAA;AAAE,wHAAA,kBAAkB,OAAA;AAE/C,8BAA8B;AAC9B,2DAAyD;AAAhD,kHAAA,eAAe,OAAA;AAExB,yDAAqE;AAA5D,sHAAA,wBAAwB,OAAA;AACjC,yEAAuE;AAA9D,wHAAA,kBAAkB,OAAA;AAoB3B,sEAGsC;AAFlC,4HAAA,oBAAoB,OAAA;AACpB,0HAAA,kBAAkB,OAAA;AAOtB,8DAGkC;AAF9B,oHAAA,gBAAgB,OAAA;AAChB,kHAAA,cAAc,OAAA;AAoBlB,gDAAgD;AACnC,QAAA,qBAAqB,GAAG,2BAAiB,CAAA","sourcesContent":["// src/index.ts\n\nimport {\n extractRawWavAnalysis,\n extractAudioAnalysis,\n} from './AudioAnalysis/extractAudioAnalysis'\nimport { extractAudioData } from './AudioAnalysis/extractAudioData'\nimport {\n extractMelSpectrogram,\n MAX_DURATION_MS,\n} from './AudioAnalysis/extractMelSpectrogram'\nimport { extractPreview } from './AudioAnalysis/extractPreview'\nimport {\n initMelStreamingWasm,\n computeMelFrameWasm,\n} from './AudioAnalysis/melSpectrogramWasm'\nimport {\n AudioRecorderProvider,\n useSharedAudioRecorder,\n} from './AudioRecorder.provider'\nimport AudioStudioModule from './AudioStudioModule'\nimport {\n getAudioDecodeCapabilities,\n streamAudioData,\n} from './streamAudioData'\nimport { trimAudio } from './trimAudio'\nimport { useAudioRecorder } from './useAudioRecorder'\n\nexport * from './utils/convertPCMToFloat32'\nexport * from './utils/getWavFileInfo'\nexport * from './utils/writeWavHeader'\n\n// Export platform capabilities\nexport {\n getPlatformCapabilities,\n isEncodingSupported,\n isBitDepthSupported,\n getFallbackEncoding,\n getFallbackBitDepth,\n validateRecordingConfig,\n type PlatformCapabilities,\n} from './constants/platformLimitations'\n\n// Export AudioDeviceManager\nexport { AudioDeviceManager, audioDeviceManager } from './AudioDeviceManager'\n\n// Export useAudioDevices hook\nexport { useAudioDevices } from './hooks/useAudioDevices'\n\nexport { setMelSpectrogramWasmUrl } from './AudioAnalysis/wasmConfig'\nexport { extractPreviewBars } from './AudioAnalysis/extractPreviewBars'\n\nexport {\n AudioRecorderProvider,\n AudioStudioModule,\n extractRawWavAnalysis,\n extractAudioAnalysis,\n extractPreview,\n trimAudio,\n extractAudioData,\n streamAudioData,\n getAudioDecodeCapabilities,\n extractMelSpectrogram,\n initMelStreamingWasm,\n computeMelFrameWasm,\n MAX_DURATION_MS,\n useAudioRecorder,\n useSharedAudioRecorder,\n}\n\nexport {\n AudioExtractionError,\n mapExtractionError,\n} from './errors/AudioExtractionError'\nexport type {\n AudioExtractionErrorCode,\n AudioExtractionErrorPayload,\n} from './errors/AudioExtractionError'\n\nexport {\n AudioStreamError,\n mapStreamError,\n} from './errors/AudioStreamError'\nexport type {\n AudioStreamErrorCode,\n AudioStreamErrorPayload,\n} from './errors/AudioStreamError'\n\nexport type {\n StreamAudioDataOptions,\n StreamAudioDataChunk,\n StreamAudioDataProgress,\n StreamAudioDataResult,\n StreamAudioDataCallbacks,\n AudioDecodeCapabilities,\n} from './streamAudioData'\n\n// Export all types\nexport type * from './AudioAnalysis/AudioAnalysis.types'\nexport type * from './AudioStudio.types'\n\n/** @deprecated Use AudioStudioModule instead */\nexport const ExpoAudioStreamModule = AudioStudioModule\n"]}
|