@siteed/audio-studio 3.1.1 → 3.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioStreamDecoder.kt +640 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +134 -3
- package/android/src/main/java/net/siteed/audiostudio/Constants.kt +4 -0
- package/build/cjs/errors/AudioStreamError.js +152 -0
- package/build/cjs/errors/AudioStreamError.js.map +1 -0
- package/build/cjs/errors/AudioStreamError.test.js +61 -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 +467 -0
- package/build/cjs/streamAudioData.js.map +1 -0
- package/build/esm/errors/AudioStreamError.js +147 -0
- package/build/esm/errors/AudioStreamError.js.map +1 -0
- package/build/esm/errors/AudioStreamError.test.js +59 -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 +460 -0
- package/build/esm/streamAudioData.js.map +1 -0
- 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 +114 -0
- package/build/types/streamAudioData.d.ts.map +1 -0
- package/ios/AudioProcessingHelpers.swift +10 -5
- package/ios/AudioStreamDecoder.swift +523 -0
- package/ios/AudioStudioModule.swift +147 -3
- package/ios/AudioStudioTests/AudioStreamDecoderTests.swift +128 -0
- package/package.json +1 -1
- package/src/errors/AudioStreamError.test.ts +65 -0
- package/src/errors/AudioStreamError.ts +185 -0
- package/src/index.ts +24 -0
- package/src/streamAudioData.ts +654 -0
|
@@ -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
|
|
@@ -994,6 +1001,130 @@ class AudioStudioModule : Module(), EventSender {
|
|
|
994
1001
|
}
|
|
995
1002
|
}
|
|
996
1003
|
}
|
|
1004
|
+
|
|
1005
|
+
AsyncFunction("streamAudioData") { options: Map<String, Any?>, promise: Promise ->
|
|
1006
|
+
try {
|
|
1007
|
+
val requestId = options["requestId"] as? String
|
|
1008
|
+
?: throw IllegalArgumentException("requestId is required")
|
|
1009
|
+
val fileUri = options["fileUri"] as? String
|
|
1010
|
+
?: throw IllegalArgumentException("fileUri is required")
|
|
1011
|
+
val streamFormat = (options["streamFormat"] as? String) ?: "float32"
|
|
1012
|
+
if (streamFormat != "float32") {
|
|
1013
|
+
promise.reject(
|
|
1014
|
+
"ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT",
|
|
1015
|
+
"Only streamFormat='float32' is supported",
|
|
1016
|
+
null
|
|
1017
|
+
)
|
|
1018
|
+
return@AsyncFunction
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
val context = appContext.reactContext
|
|
1022
|
+
?: throw IllegalStateException("React context not available")
|
|
1023
|
+
|
|
1024
|
+
val decoderOptions = AudioStreamDecoder.Options(
|
|
1025
|
+
requestId = requestId,
|
|
1026
|
+
fileUri = fileUri,
|
|
1027
|
+
startTimeMs = (options["startTimeMs"] as? Number)?.toLong(),
|
|
1028
|
+
endTimeMs = (options["endTimeMs"] as? Number)?.toLong(),
|
|
1029
|
+
targetSampleRate = (options["targetSampleRate"] as? Number)?.toInt()
|
|
1030
|
+
?: (options["sampleRate"] as? Number)?.toInt(),
|
|
1031
|
+
channels = (options["channels"] as? Number)?.toInt(),
|
|
1032
|
+
normalizeAudio = (options["normalizeAudio"] as? Boolean) ?: true,
|
|
1033
|
+
chunkDurationMs = ((options["chunkDurationMs"] as? Number)?.toInt() ?: 1000)
|
|
1034
|
+
.coerceIn(10, 60000),
|
|
1035
|
+
maxChunkBytes = (options["maxChunkBytes"] as? Number)?.toInt(),
|
|
1036
|
+
maxBufferedChunks = ((options["maxBufferedChunks"] as? Number)?.toInt() ?: 4)
|
|
1037
|
+
.coerceAtLeast(1),
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
val decoder = AudioStreamDecoder(context, decoderOptions, this@AudioStudioModule)
|
|
1041
|
+
synchronized(streamDecodersLock) {
|
|
1042
|
+
if (streamDecoders.containsKey(requestId)) {
|
|
1043
|
+
promise.reject(
|
|
1044
|
+
"ERR_AUDIO_STREAM_BUSY",
|
|
1045
|
+
"requestId already in use",
|
|
1046
|
+
null
|
|
1047
|
+
)
|
|
1048
|
+
return@AsyncFunction
|
|
1049
|
+
}
|
|
1050
|
+
streamDecoders[requestId] = decoder
|
|
1051
|
+
}
|
|
1052
|
+
decoder.start()
|
|
1053
|
+
promise.resolve(bundleOf("requestId" to requestId))
|
|
1054
|
+
} catch (e: Exception) {
|
|
1055
|
+
LogUtils.e(CLASS_NAME, "streamAudioData failed: ${e.message}", e)
|
|
1056
|
+
promise.reject(
|
|
1057
|
+
"ERR_AUDIO_STREAM_DECODE_FAILED",
|
|
1058
|
+
e.message ?: "streamAudioData failed",
|
|
1059
|
+
e
|
|
1060
|
+
)
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
AsyncFunction("cancelStreamAudioData") { requestId: String, promise: Promise ->
|
|
1065
|
+
val decoder = synchronized(streamDecodersLock) {
|
|
1066
|
+
streamDecoders[requestId]
|
|
1067
|
+
}
|
|
1068
|
+
decoder?.cancel()
|
|
1069
|
+
promise.resolve(bundleOf(
|
|
1070
|
+
"requestId" to requestId,
|
|
1071
|
+
"cancelled" to (decoder != null)
|
|
1072
|
+
))
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
Function("acknowledgeStreamAudioChunk") { requestId: String, chunkIndex: Int ->
|
|
1076
|
+
val decoder = synchronized(streamDecodersLock) {
|
|
1077
|
+
streamDecoders[requestId]
|
|
1078
|
+
}
|
|
1079
|
+
decoder?.acknowledgeChunk(chunkIndex)
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
AsyncFunction("getAudioDecodeCapabilities") { promise: Promise ->
|
|
1083
|
+
promise.resolve(bundleOf(
|
|
1084
|
+
"platform" to "android",
|
|
1085
|
+
"supportedInputFormats" to listOf(
|
|
1086
|
+
"audio/wav",
|
|
1087
|
+
"audio/mpeg",
|
|
1088
|
+
"audio/mp4",
|
|
1089
|
+
"audio/aac",
|
|
1090
|
+
"audio/ogg",
|
|
1091
|
+
"audio/opus",
|
|
1092
|
+
"audio/webm",
|
|
1093
|
+
"audio/flac",
|
|
1094
|
+
"audio/amr-wb",
|
|
1095
|
+
),
|
|
1096
|
+
"supportedOutputFormats" to listOf("float32"),
|
|
1097
|
+
"supportsCancellation" to true,
|
|
1098
|
+
"supportsBackpressure" to true,
|
|
1099
|
+
"supportsTimeRange" to true,
|
|
1100
|
+
"supportsTargetSampleRate" to true,
|
|
1101
|
+
"supportsChannelMixing" to true,
|
|
1102
|
+
"knownLimitations" to listOf(
|
|
1103
|
+
"MediaCodec output rate may differ from extractor metadata for some encoders; output is resampled via linear interpolation."
|
|
1104
|
+
)
|
|
1105
|
+
))
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
private fun releaseStreamDecoder(requestId: String) {
|
|
1110
|
+
synchronized(streamDecodersLock) {
|
|
1111
|
+
streamDecoders.remove(requestId)
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
override fun streamDecoderEmit(eventName: String, payload: Bundle) {
|
|
1116
|
+
when (eventName) {
|
|
1117
|
+
Constants.AUDIO_STREAM_COMPLETE_EVENT -> {
|
|
1118
|
+
(payload.getString("requestId"))?.let { releaseStreamDecoder(it) }
|
|
1119
|
+
}
|
|
1120
|
+
Constants.AUDIO_STREAM_ERROR_EVENT -> {
|
|
1121
|
+
val code = payload.getString("code") ?: ""
|
|
1122
|
+
if (code != "ERR_AUDIO_STREAM_CANCELLED") {
|
|
1123
|
+
payload.getString("requestId")?.let { releaseStreamDecoder(it) }
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
safeSendEvent(eventName, payload)
|
|
997
1128
|
}
|
|
998
1129
|
|
|
999
1130
|
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,152 @@
|
|
|
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 normalizeCode(raw) {
|
|
62
|
+
if (!raw)
|
|
63
|
+
return null;
|
|
64
|
+
const upper = raw.toUpperCase();
|
|
65
|
+
if (upper.startsWith('ERR_AUDIO_STREAM_')) {
|
|
66
|
+
const known = [
|
|
67
|
+
'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT',
|
|
68
|
+
'ERR_AUDIO_STREAM_INVALID_RANGE',
|
|
69
|
+
'ERR_AUDIO_STREAM_DECODE_FAILED',
|
|
70
|
+
'ERR_AUDIO_STREAM_CANCELLED',
|
|
71
|
+
'ERR_AUDIO_STREAM_PERMISSION_DENIED',
|
|
72
|
+
'ERR_AUDIO_STREAM_FILE_NOT_FOUND',
|
|
73
|
+
'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT',
|
|
74
|
+
'ERR_AUDIO_STREAM_NATIVE_UNAVAILABLE',
|
|
75
|
+
'ERR_AUDIO_STREAM_BUSY',
|
|
76
|
+
'ERR_AUDIO_STREAM_UNKNOWN',
|
|
77
|
+
];
|
|
78
|
+
if (known.includes(upper)) {
|
|
79
|
+
return upper;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (upper.includes('FILE_NOT_FOUND') || upper === 'ENOENT') {
|
|
83
|
+
return 'ERR_AUDIO_STREAM_FILE_NOT_FOUND';
|
|
84
|
+
}
|
|
85
|
+
if (upper.includes('PERMISSION') || upper === 'EACCES') {
|
|
86
|
+
return 'ERR_AUDIO_STREAM_PERMISSION_DENIED';
|
|
87
|
+
}
|
|
88
|
+
if (upper.includes('UNSUPPORTED') ||
|
|
89
|
+
upper.includes('NO_SUITABLE_CODEC') ||
|
|
90
|
+
upper.includes('NO SUITABLE CODEC') ||
|
|
91
|
+
upper.includes('NOT SUPPORTED')) {
|
|
92
|
+
return 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT';
|
|
93
|
+
}
|
|
94
|
+
if (upper.includes('INVALID_RANGE') ||
|
|
95
|
+
upper.includes('OUT_OF_RANGE') ||
|
|
96
|
+
upper.includes('INVALID_TIME')) {
|
|
97
|
+
return 'ERR_AUDIO_STREAM_INVALID_RANGE';
|
|
98
|
+
}
|
|
99
|
+
if (upper.includes('CANCELLED') || upper.includes('CANCELED')) {
|
|
100
|
+
return 'ERR_AUDIO_STREAM_CANCELLED';
|
|
101
|
+
}
|
|
102
|
+
if (upper.includes('BUSY')) {
|
|
103
|
+
return 'ERR_AUDIO_STREAM_BUSY';
|
|
104
|
+
}
|
|
105
|
+
if (upper.includes('BACKPRESSURE')) {
|
|
106
|
+
return 'ERR_AUDIO_STREAM_BACKPRESSURE_TIMEOUT';
|
|
107
|
+
}
|
|
108
|
+
if (upper.includes('DECODE') ||
|
|
109
|
+
upper.includes('CODEC') ||
|
|
110
|
+
upper.includes('MALFORMED')) {
|
|
111
|
+
return 'ERR_AUDIO_STREAM_DECODE_FAILED';
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
function mapStreamError(err, fileUri, platform) {
|
|
116
|
+
if (err instanceof AudioStreamError)
|
|
117
|
+
return err;
|
|
118
|
+
const nativeMessage = getNativeMessage(err);
|
|
119
|
+
const nativeCode = getNativeCode(err);
|
|
120
|
+
const lower = nativeMessage.toLowerCase();
|
|
121
|
+
let code = normalizeCode(nativeCode) ??
|
|
122
|
+
normalizeCode(nativeMessage) ??
|
|
123
|
+
'ERR_AUDIO_STREAM_UNKNOWN';
|
|
124
|
+
if (code === 'ERR_AUDIO_STREAM_UNKNOWN') {
|
|
125
|
+
if (lower.includes('not found') || lower.includes('does not exist')) {
|
|
126
|
+
code = 'ERR_AUDIO_STREAM_FILE_NOT_FOUND';
|
|
127
|
+
}
|
|
128
|
+
else if (lower.includes('unsupported') ||
|
|
129
|
+
lower.includes('no suitable codec')) {
|
|
130
|
+
code = 'ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT';
|
|
131
|
+
}
|
|
132
|
+
else if (lower.includes('permission') || lower.includes('denied')) {
|
|
133
|
+
code = 'ERR_AUDIO_STREAM_PERMISSION_DENIED';
|
|
134
|
+
}
|
|
135
|
+
else if (lower.includes('decode') || lower.includes('codec')) {
|
|
136
|
+
code = 'ERR_AUDIO_STREAM_DECODE_FAILED';
|
|
137
|
+
}
|
|
138
|
+
else if (lower.includes('cancel')) {
|
|
139
|
+
code = 'ERR_AUDIO_STREAM_CANCELLED';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return new AudioStreamError({
|
|
143
|
+
code,
|
|
144
|
+
message: `Audio stream failed (${code}): ${nativeMessage}`,
|
|
145
|
+
recoverable: RECOVERABLE.includes(code),
|
|
146
|
+
fileUri,
|
|
147
|
+
platform,
|
|
148
|
+
nativeCode,
|
|
149
|
+
nativeMessage,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
//# 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 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 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,61 @@
|
|
|
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)({ code: 'FILE_NOT_FOUND', message: 'gone' });
|
|
15
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_FILE_NOT_FOUND');
|
|
16
|
+
expect(mapped.recoverable).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('maps unsupported codec text', () => {
|
|
19
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)(new Error('No suitable codec for audio/opus'));
|
|
20
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT');
|
|
21
|
+
});
|
|
22
|
+
it('marks cancellation as recoverable', () => {
|
|
23
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({
|
|
24
|
+
code: 'ERR_AUDIO_STREAM_CANCELLED',
|
|
25
|
+
message: 'user cancelled',
|
|
26
|
+
});
|
|
27
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_CANCELLED');
|
|
28
|
+
expect(mapped.recoverable).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('falls back to UNKNOWN', () => {
|
|
31
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({});
|
|
32
|
+
expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNKNOWN');
|
|
33
|
+
});
|
|
34
|
+
it('preserves nativeCode and nativeMessage', () => {
|
|
35
|
+
const mapped = (0, AudioStreamError_1.mapStreamError)({
|
|
36
|
+
code: 'WEIRD_NATIVE_CODE',
|
|
37
|
+
message: 'something went wrong on the bridge',
|
|
38
|
+
});
|
|
39
|
+
expect(mapped.nativeCode).toBe('WEIRD_NATIVE_CODE');
|
|
40
|
+
expect(mapped.nativeMessage).toContain('bridge');
|
|
41
|
+
});
|
|
42
|
+
it('serialises to a stable JSON payload', () => {
|
|
43
|
+
const err = new AudioStreamError_1.AudioStreamError({
|
|
44
|
+
code: 'ERR_AUDIO_STREAM_DECODE_FAILED',
|
|
45
|
+
message: 'decoder bust',
|
|
46
|
+
recoverable: false,
|
|
47
|
+
fileUri: 'file:///a.m4a',
|
|
48
|
+
platform: 'ios',
|
|
49
|
+
});
|
|
50
|
+
expect(err.toJSON()).toEqual({
|
|
51
|
+
code: 'ERR_AUDIO_STREAM_DECODE_FAILED',
|
|
52
|
+
message: 'decoder bust',
|
|
53
|
+
recoverable: false,
|
|
54
|
+
fileUri: 'file:///a.m4a',
|
|
55
|
+
platform: 'ios',
|
|
56
|
+
nativeCode: undefined,
|
|
57
|
+
nativeMessage: undefined,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//# 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({ code: 'FILE_NOT_FOUND', message: 'gone' })\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(new Error('No suitable codec for audio/opus'))\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('falls back to UNKNOWN', () => {\n const mapped = mapStreamError({})\n expect(mapped.code).toBe('ERR_AUDIO_STREAM_UNKNOWN')\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"]}
|