@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
|
@@ -9,6 +9,10 @@ private let recordingInterruptedEvent: String = "onRecordingInterrupted"
|
|
|
9
9
|
private let deviceChangedEvent: String = "deviceChangedEvent"
|
|
10
10
|
private let trimProgressEvent: String = "TrimProgress"
|
|
11
11
|
private let errorEvent: String = "error"
|
|
12
|
+
private let audioStreamChunkEvent: String = "AudioDataStreamChunk"
|
|
13
|
+
private let audioStreamProgressEvent: String = "AudioDataStreamProgress"
|
|
14
|
+
private let audioStreamCompleteEvent: String = "AudioDataStreamComplete"
|
|
15
|
+
private let audioStreamErrorEvent: String = "AudioDataStreamError"
|
|
12
16
|
private let DEFAULT_SEGMENT_DURATION_MS = 100
|
|
13
17
|
private let audioDeviceTypeBuiltinMic = "builtin_mic"
|
|
14
18
|
private let audioDeviceTypeBluetooth = "bluetooth"
|
|
@@ -18,13 +22,16 @@ private let audioDeviceTypeWiredHeadphones = "wired_headphones"
|
|
|
18
22
|
private let audioDeviceTypeSpeaker = "speaker"
|
|
19
23
|
private let audioDeviceTypeUnknown = "unknown"
|
|
20
24
|
|
|
21
|
-
public class AudioStudioModule: Module, AudioStreamManagerDelegate, AudioDeviceManagerDelegate {
|
|
25
|
+
public class AudioStudioModule: Module, AudioStreamManagerDelegate, AudioDeviceManagerDelegate, AudioStreamDecoderDelegate {
|
|
22
26
|
private var streamManager = AudioStreamManager()
|
|
23
27
|
private let notificationCenter = UNUserNotificationCenter.current()
|
|
24
28
|
private let notificationIdentifier = "audio_recording_notification"
|
|
25
29
|
private var deviceManager = AudioDeviceManager()
|
|
26
30
|
private var deviceChangeObserver: Any?
|
|
27
31
|
|
|
32
|
+
private let streamDecodersLock = NSLock()
|
|
33
|
+
private var streamDecoders: [String: AudioStreamDecoder] = [:]
|
|
34
|
+
|
|
28
35
|
// Serial queue for AVAudioEngine lifecycle ops (prepare/start/stop).
|
|
29
36
|
// Prevents concurrent mutation of shared engine state and keeps callers
|
|
30
37
|
// off the main thread to avoid UI freezes during heavy native init.
|
|
@@ -43,7 +50,11 @@ public class AudioStudioModule: Module, AudioStreamManagerDelegate, AudioDeviceM
|
|
|
43
50
|
recordingInterruptedEvent,
|
|
44
51
|
deviceChangedEvent,
|
|
45
52
|
trimProgressEvent,
|
|
46
|
-
errorEvent
|
|
53
|
+
errorEvent,
|
|
54
|
+
audioStreamChunkEvent,
|
|
55
|
+
audioStreamProgressEvent,
|
|
56
|
+
audioStreamCompleteEvent,
|
|
57
|
+
audioStreamErrorEvent
|
|
47
58
|
])
|
|
48
59
|
|
|
49
60
|
OnCreate {
|
|
@@ -71,6 +82,20 @@ public class AudioStudioModule: Module, AudioStreamManagerDelegate, AudioDeviceM
|
|
|
71
82
|
OnDestroy {
|
|
72
83
|
Logger.debug("AudioStudioModule", "Module destroyed, stopping device monitoring.")
|
|
73
84
|
_ = streamManager.stopRecording()
|
|
85
|
+
// Cancel any in-flight streamAudioData decoders before the module
|
|
86
|
+
// is torn down. Without this, decoder threads can outlive the
|
|
87
|
+
// module and try to emit events through a destroyed instance.
|
|
88
|
+
// Detach each decoder's delegate *before* cancelling so the
|
|
89
|
+
// terminal "cancelled" events the worker emits after observing
|
|
90
|
+
// `cancel()` are dropped instead of being forwarded.
|
|
91
|
+
self.streamDecodersLock.lock()
|
|
92
|
+
let inflight = self.streamDecoders
|
|
93
|
+
self.streamDecoders.removeAll()
|
|
94
|
+
self.streamDecodersLock.unlock()
|
|
95
|
+
for (_, decoder) in inflight {
|
|
96
|
+
decoder.delegate = nil
|
|
97
|
+
decoder.cancel()
|
|
98
|
+
}
|
|
74
99
|
// Clear device manager delegate
|
|
75
100
|
deviceManager.delegate = nil
|
|
76
101
|
}
|
|
@@ -901,8 +926,166 @@ public class AudioStudioModule: Module, AudioStreamManagerDelegate, AudioDeviceM
|
|
|
901
926
|
}
|
|
902
927
|
}
|
|
903
928
|
}
|
|
929
|
+
|
|
930
|
+
AsyncFunction("streamAudioData") { (options: [String: Any], promise: Promise) in
|
|
931
|
+
guard let requestId = options["requestId"] as? String,
|
|
932
|
+
let fileUri = options["fileUri"] as? String else {
|
|
933
|
+
promise.reject(
|
|
934
|
+
"ERR_AUDIO_STREAM_INVALID_RANGE",
|
|
935
|
+
"fileUri and requestId are required"
|
|
936
|
+
)
|
|
937
|
+
return
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
let streamFormat = options["streamFormat"] as? String ?? "float32"
|
|
941
|
+
guard streamFormat == "float32" else {
|
|
942
|
+
promise.reject(
|
|
943
|
+
"ERR_AUDIO_STREAM_UNSUPPORTED_FORMAT",
|
|
944
|
+
"Only streamFormat='float32' is supported"
|
|
945
|
+
)
|
|
946
|
+
return
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
let chunkDurationMs = options["chunkDurationMs"] as? Int ?? 1000
|
|
950
|
+
guard (10...60000).contains(chunkDurationMs) else {
|
|
951
|
+
promise.reject(
|
|
952
|
+
"ERR_AUDIO_STREAM_INVALID_RANGE",
|
|
953
|
+
"chunkDurationMs must be in [10, 60000]"
|
|
954
|
+
)
|
|
955
|
+
return
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
let opts = AudioStreamDecoder.Options(
|
|
959
|
+
requestId: requestId,
|
|
960
|
+
fileUri: fileUri,
|
|
961
|
+
startTimeMs: options["startTimeMs"] as? Double,
|
|
962
|
+
endTimeMs: options["endTimeMs"] as? Double,
|
|
963
|
+
targetSampleRate: options["targetSampleRate"] as? Double,
|
|
964
|
+
channels: options["channels"] as? Int,
|
|
965
|
+
normalizeAudio: options["normalizeAudio"] as? Bool ?? true,
|
|
966
|
+
chunkDurationMs: chunkDurationMs,
|
|
967
|
+
maxChunkBytes: options["maxChunkBytes"] as? Int,
|
|
968
|
+
maxBufferedChunks: options["maxBufferedChunks"] as? Int ?? 4,
|
|
969
|
+
backpressureTimeoutMs: options["backpressureTimeoutMs"] as? Double
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
let decoder = AudioStreamDecoder(options: opts)
|
|
973
|
+
decoder.delegate = self
|
|
974
|
+
self.streamDecodersLock.lock()
|
|
975
|
+
if self.streamDecoders[requestId] != nil {
|
|
976
|
+
self.streamDecodersLock.unlock()
|
|
977
|
+
promise.reject(
|
|
978
|
+
"ERR_AUDIO_STREAM_BUSY",
|
|
979
|
+
"requestId already in use"
|
|
980
|
+
)
|
|
981
|
+
return
|
|
982
|
+
}
|
|
983
|
+
self.streamDecoders[requestId] = decoder
|
|
984
|
+
self.streamDecodersLock.unlock()
|
|
985
|
+
decoder.start()
|
|
986
|
+
promise.resolve(["requestId": requestId])
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
AsyncFunction("cancelStreamAudioData") { (requestId: String, promise: Promise) in
|
|
990
|
+
self.streamDecodersLock.lock()
|
|
991
|
+
let decoder = self.streamDecoders[requestId]
|
|
992
|
+
self.streamDecodersLock.unlock()
|
|
993
|
+
decoder?.cancel()
|
|
994
|
+
promise.resolve(["requestId": requestId, "cancelled": decoder != nil])
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
Function("acknowledgeStreamAudioChunk") { (requestId: String, chunkIndex: Int) in
|
|
998
|
+
self.streamDecodersLock.lock()
|
|
999
|
+
let decoder = self.streamDecoders[requestId]
|
|
1000
|
+
self.streamDecodersLock.unlock()
|
|
1001
|
+
decoder?.acknowledgeChunk(chunkIndex)
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
AsyncFunction("getAudioDecodeCapabilities") { (promise: Promise) in
|
|
1005
|
+
promise.resolve([
|
|
1006
|
+
"platform": "ios",
|
|
1007
|
+
"supportedInputFormats": [
|
|
1008
|
+
"audio/wav",
|
|
1009
|
+
"audio/aac",
|
|
1010
|
+
"audio/mp4",
|
|
1011
|
+
"audio/mpeg",
|
|
1012
|
+
"audio/x-m4a",
|
|
1013
|
+
"audio/caf",
|
|
1014
|
+
"audio/aiff",
|
|
1015
|
+
],
|
|
1016
|
+
"supportedOutputFormats": ["float32"],
|
|
1017
|
+
"supportsCancellation": true,
|
|
1018
|
+
"supportsBackpressure": true,
|
|
1019
|
+
"supportsTimeRange": true,
|
|
1020
|
+
"supportsTargetSampleRate": true,
|
|
1021
|
+
"supportsChannelMixing": true,
|
|
1022
|
+
"knownLimitations": [
|
|
1023
|
+
"Opus/WebM input depends on AVFoundation codec availability for the iOS version."
|
|
1024
|
+
],
|
|
1025
|
+
])
|
|
1026
|
+
}
|
|
904
1027
|
}
|
|
905
|
-
|
|
1028
|
+
|
|
1029
|
+
private func releaseStreamDecoder(_ requestId: String) {
|
|
1030
|
+
streamDecodersLock.lock()
|
|
1031
|
+
streamDecoders.removeValue(forKey: requestId)
|
|
1032
|
+
streamDecodersLock.unlock()
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/// Returns true when `requestId` is still tracked by the module. Used as
|
|
1036
|
+
/// the lifecycle gate for delegate sends: callbacks that race with
|
|
1037
|
+
/// `OnDestroy` (which clears the map *before* cancelling) see `false`
|
|
1038
|
+
/// and skip `sendEvent`, so we never push events through a destroyed
|
|
1039
|
+
/// React context.
|
|
1040
|
+
private func isActiveStreamDecoder(_ requestId: String) -> Bool {
|
|
1041
|
+
streamDecodersLock.lock()
|
|
1042
|
+
defer { streamDecodersLock.unlock() }
|
|
1043
|
+
return streamDecoders[requestId] != nil
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// MARK: - AudioStreamDecoderDelegate
|
|
1047
|
+
|
|
1048
|
+
public func streamDecoder(
|
|
1049
|
+
_ decoder: AudioStreamDecoder,
|
|
1050
|
+
didEmitChunk payload: [String: Any]
|
|
1051
|
+
) {
|
|
1052
|
+
guard let requestId = payload["requestId"] as? String,
|
|
1053
|
+
isActiveStreamDecoder(requestId) else { return }
|
|
1054
|
+
sendEvent(audioStreamChunkEvent, payload)
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
public func streamDecoder(
|
|
1058
|
+
_ decoder: AudioStreamDecoder,
|
|
1059
|
+
didReportProgress payload: [String: Any]
|
|
1060
|
+
) {
|
|
1061
|
+
guard let requestId = payload["requestId"] as? String,
|
|
1062
|
+
isActiveStreamDecoder(requestId) else { return }
|
|
1063
|
+
sendEvent(audioStreamProgressEvent, payload)
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
public func streamDecoder(
|
|
1067
|
+
_ decoder: AudioStreamDecoder,
|
|
1068
|
+
didCompleteWith payload: [String: Any]
|
|
1069
|
+
) {
|
|
1070
|
+
guard let requestId = payload["requestId"] as? String,
|
|
1071
|
+
isActiveStreamDecoder(requestId) else { return }
|
|
1072
|
+
releaseStreamDecoder(requestId)
|
|
1073
|
+
sendEvent(audioStreamCompleteEvent, payload)
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
public func streamDecoder(
|
|
1077
|
+
_ decoder: AudioStreamDecoder,
|
|
1078
|
+
didFailWith payload: [String: Any]
|
|
1079
|
+
) {
|
|
1080
|
+
guard let requestId = payload["requestId"] as? String,
|
|
1081
|
+
isActiveStreamDecoder(requestId) else { return }
|
|
1082
|
+
if let code = payload["code"] as? String,
|
|
1083
|
+
code != "ERR_AUDIO_STREAM_CANCELLED" {
|
|
1084
|
+
releaseStreamDecoder(requestId)
|
|
1085
|
+
}
|
|
1086
|
+
sendEvent(audioStreamErrorEvent, payload)
|
|
1087
|
+
}
|
|
1088
|
+
|
|
906
1089
|
func audioStreamManager(_ manager: AudioStreamManager, didReceiveInterruption info: [String: Any]) {
|
|
907
1090
|
Logger.debug("AudioStudioModule", "Delegate: didReceiveInterruption: \(info)")
|
|
908
1091
|
// Convert iOS interruption events to match the TypeScript types
|
package/package.json
CHANGED
|
@@ -1,147 +1,164 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
2
|
+
"name": "@siteed/audio-studio",
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "Comprehensive audio processing library for React Native and Expo with recording, analysis, visualization, and streaming capabilities across iOS, Android, and web",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"main": "./build/cjs/index.js",
|
|
8
|
+
"module": "./build/esm/index.js",
|
|
9
|
+
"types": "./build/types/index.d.ts",
|
|
10
|
+
"expo": {
|
|
11
|
+
"plugin": "./app.plugin.js"
|
|
12
|
+
},
|
|
13
|
+
"author": "Arthur Breton <abreton@siteed.net> (https://github.com/deeeed)",
|
|
14
|
+
"homepage": "https://github.com/deeeed/audiolab/blob/main/packages/audio-studio/README.md",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/deeeed/audiolab.git",
|
|
18
|
+
"directory": "packages/audio-studio"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/deeeed/audiolab/issues"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"react-native",
|
|
25
|
+
"expo",
|
|
26
|
+
"audio",
|
|
27
|
+
"recording",
|
|
28
|
+
"audio-analysis",
|
|
29
|
+
"audio-processing",
|
|
30
|
+
"audio-visualization",
|
|
31
|
+
"waveform",
|
|
32
|
+
"spectrogram",
|
|
33
|
+
"mel-spectrogram",
|
|
34
|
+
"mfcc",
|
|
35
|
+
"audio-features",
|
|
36
|
+
"audio-compression",
|
|
37
|
+
"opus",
|
|
38
|
+
"aac",
|
|
39
|
+
"pcm",
|
|
40
|
+
"wav",
|
|
41
|
+
"cross-platform",
|
|
42
|
+
"background-recording",
|
|
43
|
+
"audio-trimming",
|
|
44
|
+
"dual-stream"
|
|
45
|
+
],
|
|
46
|
+
"files": [
|
|
47
|
+
"src",
|
|
48
|
+
"android",
|
|
49
|
+
"ios",
|
|
50
|
+
"cpp",
|
|
51
|
+
"plugin",
|
|
52
|
+
"app.plugin.js",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"CHANGELOG.md",
|
|
55
|
+
"generated",
|
|
56
|
+
"expo-module.config.json",
|
|
57
|
+
"README.md",
|
|
58
|
+
"package.json",
|
|
59
|
+
"*.podspec",
|
|
60
|
+
"prebuilt",
|
|
61
|
+
"build",
|
|
62
|
+
"!ios/build",
|
|
63
|
+
"!android/build",
|
|
64
|
+
"!android/gradle",
|
|
65
|
+
"!android/gradlew",
|
|
66
|
+
"!android/gradlew.bat",
|
|
67
|
+
"!android/local.properties",
|
|
68
|
+
"!ios/AudioStudioTests",
|
|
69
|
+
"!ios/AudioStudioTests/**",
|
|
70
|
+
"!ios/tests",
|
|
71
|
+
"!ios/tests/**",
|
|
72
|
+
"!android/src/androidTest",
|
|
73
|
+
"!android/src/androidTest/**",
|
|
74
|
+
"!android/src/test",
|
|
75
|
+
"!android/src/test/**",
|
|
76
|
+
"!android/src/test/resources",
|
|
77
|
+
"!android/src/test/resources/**",
|
|
78
|
+
"!**/__tests__",
|
|
79
|
+
"!**/__fixtures__",
|
|
80
|
+
"!**/__mocks__",
|
|
81
|
+
"!**/.*"
|
|
82
|
+
],
|
|
83
|
+
"scripts": {
|
|
84
|
+
"build:wasm": "bash scripts/build-wasm.sh",
|
|
85
|
+
"build": "rimraf build && yarn build:types && yarn build:cjs && yarn build:esm && yarn build:plugin && cp -r prebuilt/ build/cjs/prebuilt && cp -r prebuilt/ build/esm/prebuilt",
|
|
86
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
87
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
88
|
+
"build:types": "tsc -p tsconfig.types.json",
|
|
89
|
+
"build:plugin": "tsc --project plugin/tsconfig.json && cp plugin/build/index.js plugin/build/index.cjs",
|
|
90
|
+
"build:plugin:dev": "expo-module build plugin",
|
|
91
|
+
"build:dev": "expo-module build",
|
|
92
|
+
"clean": "expo-module clean && rimraf build plugin/build",
|
|
93
|
+
"lint": "expo-module lint",
|
|
94
|
+
"lint:fix": "expo-module lint --fix",
|
|
95
|
+
"test": "expo-module test",
|
|
96
|
+
"test:android": "yarn test:android:unit && yarn test:android:instrumented",
|
|
97
|
+
"test:android:unit": "cd ../../apps/playground/android && ./gradlew :siteed-audio-studio:test",
|
|
98
|
+
"test:android:instrumented": "cd ../../apps/playground/android && ./gradlew :siteed-audio-studio:connectedAndroidTest",
|
|
99
|
+
"test:android:unit:watch": "cd ../../apps/playground/android && ./gradlew :siteed-audio-studio:test --continuous",
|
|
100
|
+
"test:ios": "cd ../../apps/playground/ios && xcodebuild -workspace AudioDevPlayground.xcworkspace -scheme AudioDevPlayground -destination 'generic/platform=iOS Simulator' build",
|
|
101
|
+
"test:coverage": "cd ../../apps/playground/android && ./gradlew :siteed-audio-studio:jacocoTestReport",
|
|
102
|
+
"typecheck": "tsc --noEmit",
|
|
103
|
+
"docgen": "typedoc src/index.ts --plugin typedoc-plugin-markdown --readme none --out ../../documentation_site/docs/api-reference/API && node ../../scripts/escape-mdx-generics.js ../../documentation_site/docs/api-reference",
|
|
104
|
+
"prepare": "yarn build && node -e \"require('fs').renameSync('./plugin/build/index.d.ts', './plugin/build/index.d.cts')\"",
|
|
105
|
+
"prepublishOnly.disabled": "expo-module prepublishOnly",
|
|
106
|
+
"expo-module": "expo-module",
|
|
107
|
+
"open:ios": "open -a \"Xcode\" ../../apps/playground/ios",
|
|
108
|
+
"open:android": "open -a \"Android Studio\" ../../apps/playground/android",
|
|
109
|
+
"size": "bundle-size && size-limit",
|
|
110
|
+
"release": "./publish.sh",
|
|
111
|
+
"agent:test:unit": "yarn test:android:unit",
|
|
112
|
+
"agent:test:integration": "yarn test:android:instrumented",
|
|
113
|
+
"agent:compilation:check": "yarn typecheck && yarn build",
|
|
114
|
+
"android": "cd ../../apps/playground && yarn android",
|
|
115
|
+
"android:launch": "cd ../../apps/playground && yarn android:launch",
|
|
116
|
+
"validate:stream-long": "node scripts/validate-stream-long.mjs",
|
|
117
|
+
"android:device": "cd ../../apps/playground && yarn android:device",
|
|
118
|
+
"android:device:launch": "cd ../../apps/playground && yarn android:device:launch",
|
|
119
|
+
"summarize:stream-long": "node scripts/summarize-stream-long.mjs"
|
|
120
|
+
},
|
|
121
|
+
"devDependencies": {
|
|
122
|
+
"@expo/config-plugins": "~54.0.0",
|
|
123
|
+
"@expo/npm-proofread": "^1.0.1",
|
|
124
|
+
"@siteed/publisher": "^0.4.18",
|
|
125
|
+
"@size-limit/preset-big-lib": "^11.1.4",
|
|
126
|
+
"@types/jest": "^29.5.12",
|
|
127
|
+
"@types/node": "^20.12.7",
|
|
128
|
+
"@types/react": "~19.0.10",
|
|
129
|
+
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
130
|
+
"@typescript-eslint/parser": "^7.7.0",
|
|
131
|
+
"bundle-size": "^1.1.5",
|
|
132
|
+
"eslint": "^8.56.0",
|
|
133
|
+
"eslint-config-prettier": "^9.1.0",
|
|
134
|
+
"eslint-config-universe": "^12.0.0",
|
|
135
|
+
"eslint-plugin-import": "^2.29.1",
|
|
136
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
137
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
138
|
+
"eslint-plugin-react": "^7.34.1",
|
|
139
|
+
"expo": "^54.0.0",
|
|
140
|
+
"expo-module-scripts": "^4.1.7",
|
|
141
|
+
"expo-modules-core": "~3.0.0",
|
|
142
|
+
"jest": "^29.7.0",
|
|
143
|
+
"prettier": "^3.2.5",
|
|
144
|
+
"react": "19.2.0",
|
|
145
|
+
"react-native": "0.83.6",
|
|
146
|
+
"rimraf": "^6.0.1",
|
|
147
|
+
"size-limit": "^11.1.4",
|
|
148
|
+
"ts-jest": "^29.2.6",
|
|
149
|
+
"ts-node": "^10.9.2",
|
|
150
|
+
"typedoc": "^0.27.4",
|
|
151
|
+
"typedoc-plugin-markdown": "~4.4.2",
|
|
152
|
+
"typescript": "~5.8.3"
|
|
153
|
+
},
|
|
154
|
+
"peerDependencies": {
|
|
155
|
+
"@expo/config-plugins": ">=7.0.0",
|
|
156
|
+
"expo": ">=52.0.0",
|
|
157
|
+
"react": "*",
|
|
158
|
+
"react-native": "*"
|
|
159
|
+
},
|
|
160
|
+
"publishConfig": {
|
|
161
|
+
"access": "public",
|
|
162
|
+
"registry": "https://registry.npmjs.org"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Test Scripts
|
|
2
|
+
|
|
3
|
+
This directory contains unified test scripts for expo-audio-studio.
|
|
4
|
+
|
|
5
|
+
## run_tests.sh
|
|
6
|
+
|
|
7
|
+
A unified test runner that can execute tests for both Android and iOS platforms.
|
|
8
|
+
|
|
9
|
+
### Usage
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
./scripts/run_tests.sh [platform] [type]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Parameters
|
|
16
|
+
|
|
17
|
+
- **platform**: Which platform to test
|
|
18
|
+
- `all` (default) - Run tests for both platforms
|
|
19
|
+
- `android` - Run Android tests only
|
|
20
|
+
- `ios` - Run iOS tests only
|
|
21
|
+
|
|
22
|
+
- **type**: Which type of tests to run
|
|
23
|
+
- `all` (default) - Run all test types
|
|
24
|
+
- `unit` - Run unit tests (Android only)
|
|
25
|
+
- `instrumented` - Run instrumented tests (Android only)
|
|
26
|
+
- `standalone` - Run standalone Swift tests (iOS only)
|
|
27
|
+
|
|
28
|
+
### Examples
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Run all tests for both platforms
|
|
32
|
+
./scripts/run_tests.sh
|
|
33
|
+
|
|
34
|
+
# Run Android tests only
|
|
35
|
+
./scripts/run_tests.sh android
|
|
36
|
+
|
|
37
|
+
# Run Android unit tests only
|
|
38
|
+
./scripts/run_tests.sh android unit
|
|
39
|
+
|
|
40
|
+
# Run iOS tests only
|
|
41
|
+
./scripts/run_tests.sh ios
|
|
42
|
+
|
|
43
|
+
# Run all tests explicitly
|
|
44
|
+
./scripts/run_tests.sh all all
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Requirements
|
|
48
|
+
|
|
49
|
+
- **Android**: Requires Android SDK and a connected device/emulator for instrumented tests
|
|
50
|
+
- **iOS**: Requires Swift compiler (comes with Xcode)
|
|
51
|
+
|
|
52
|
+
### Output
|
|
53
|
+
|
|
54
|
+
The script provides colored output:
|
|
55
|
+
- ๐งช Test execution progress
|
|
56
|
+
- โ
Success messages in green
|
|
57
|
+
- โ Failure messages in red
|
|
58
|
+
- Summary of tests passed/failed
|