@siteed/audio-studio 3.2.0-beta.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +356 -5
  2. package/android/src/main/java/net/siteed/audiostudio/AudioStreamDecoder.kt +306 -94
  3. package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +39 -6
  4. package/build/cjs/errors/AudioStreamError.js +9 -0
  5. package/build/cjs/errors/AudioStreamError.js.map +1 -1
  6. package/build/cjs/errors/AudioStreamError.test.js +22 -1
  7. package/build/cjs/errors/AudioStreamError.test.js.map +1 -1
  8. package/build/cjs/streamAudioData.js +99 -32
  9. package/build/cjs/streamAudioData.js.map +1 -1
  10. package/build/cjs/utils/audioProcessing.js +14 -10
  11. package/build/cjs/utils/audioProcessing.js.map +1 -1
  12. package/build/esm/errors/AudioStreamError.js +9 -0
  13. package/build/esm/errors/AudioStreamError.js.map +1 -1
  14. package/build/esm/errors/AudioStreamError.test.js +22 -1
  15. package/build/esm/errors/AudioStreamError.test.js.map +1 -1
  16. package/build/esm/streamAudioData.js +99 -32
  17. package/build/esm/streamAudioData.js.map +1 -1
  18. package/build/esm/utils/audioProcessing.js +14 -10
  19. package/build/esm/utils/audioProcessing.js.map +1 -1
  20. package/build/types/errors/AudioStreamError.d.ts.map +1 -1
  21. package/build/types/streamAudioData.d.ts +5 -0
  22. package/build/types/streamAudioData.d.ts.map +1 -1
  23. package/build/types/utils/audioProcessing.d.ts +2 -2
  24. package/build/types/utils/audioProcessing.d.ts.map +1 -1
  25. package/ios/AudioStreamDecoder.swift +191 -100
  26. package/ios/AudioStudioModule.swift +48 -9
  27. package/package.json +163 -146
  28. package/scripts/README.md +58 -0
  29. package/src/errors/AudioStreamError.test.ts +29 -2
  30. package/src/errors/AudioStreamError.ts +14 -0
  31. package/src/streamAudioData.ts +146 -42
  32. package/src/utils/audioProcessing.ts +25 -14
  33. package/android/src/androidTest/assets/chorus.wav +0 -0
  34. package/android/src/androidTest/assets/jfk.wav +0 -0
  35. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  36. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  37. package/android/src/androidTest/java/net/siteed/audiostudio/AudioFinalMetadataContractInstrumentedTest.kt +0 -190
  38. package/android/src/androidTest/java/net/siteed/audiostudio/AudioProcessorInstrumentedTest.kt +0 -197
  39. package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderInstrumentedTest.kt +0 -487
  40. package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderPerformanceInstrumentedTest.kt +0 -250
  41. package/android/src/androidTest/java/net/siteed/audiostudio/OpusRangeDecodeRegressionInstrumentedTest.kt +0 -186
  42. package/android/src/androidTest/java/net/siteed/audiostudio/integration/AudioFocusStrategyIntegrationTest.kt +0 -332
  43. package/android/src/androidTest/java/net/siteed/audiostudio/integration/BufferDurationIntegrationTest.kt +0 -324
  44. package/android/src/androidTest/java/net/siteed/audiostudio/integration/CompressedOnlyOutputTest.kt +0 -253
  45. package/android/src/androidTest/java/net/siteed/audiostudio/integration/DeviceDisconnectionFallbackTest.kt +0 -218
  46. package/android/src/androidTest/java/net/siteed/audiostudio/integration/EventEmissionIntervalTest.kt +0 -120
  47. package/android/src/androidTest/java/net/siteed/audiostudio/integration/M4aFormatTest.kt +0 -345
  48. package/android/src/androidTest/java/net/siteed/audiostudio/integration/OutputControlIntegrationTest.kt +0 -340
  49. package/android/src/androidTest/java/net/siteed/audiostudio/integration/PcmStreamingDurationTest.kt +0 -252
  50. package/android/src/androidTest/java/net/siteed/audiostudio/integration/README.md +0 -95
  51. package/android/src/androidTest/java/net/siteed/audiostudio/integration/run_integration_tests.sh +0 -43
  52. package/android/src/test/java/net/siteed/audiostudio/AndroidCallStateTest.kt +0 -37
  53. package/android/src/test/java/net/siteed/audiostudio/AndroidEventEmitterTest.kt +0 -28
  54. package/android/src/test/java/net/siteed/audiostudio/AudioFileHandlerTest.kt +0 -279
  55. package/android/src/test/java/net/siteed/audiostudio/AudioFocusStrategyTest.kt +0 -249
  56. package/android/src/test/java/net/siteed/audiostudio/AudioFormatTest.kt +0 -151
  57. package/android/src/test/java/net/siteed/audiostudio/AudioFormatUtilsTest.kt +0 -273
  58. package/android/src/test/java/net/siteed/audiostudio/DeviceDisconnectionFallbackUnitTest.kt +0 -140
  59. package/android/src/test/java/net/siteed/audiostudio/InterruptionAutoResumePolicyTest.kt +0 -49
  60. package/android/src/test/resources/chorus.wav +0 -0
  61. package/android/src/test/resources/generate_test_audio.py +0 -94
  62. package/android/src/test/resources/jfk.wav +0 -0
  63. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  64. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  65. package/ios/AudioStudioTests/AudioFileHandlerTests.swift +0 -338
  66. package/ios/AudioStudioTests/AudioFormatUtilsTests.swift +0 -331
  67. package/ios/AudioStudioTests/AudioStreamDecoderTests.swift +0 -128
  68. package/ios/AudioStudioTests/AudioTestHelpers.swift +0 -130
  69. package/ios/AudioStudioTests/CompressedOnlyOutputTests.swift +0 -334
  70. package/ios/AudioStudioTests/EventEmissionIntervalTests.swift +0 -105
  71. package/ios/AudioStudioTests/Info.plist +0 -22
  72. package/ios/AudioStudioTests/README.md +0 -39
  73. package/ios/AudioStudioTests/SimpleAudioTest.swift +0 -98
  74. package/ios/AudioStudioTests/TestAudioGenerator.swift +0 -75
  75. package/ios/tests/README.md +0 -41
  76. package/ios/tests/integration/buffer_and_fallback_test.swift +0 -178
  77. package/ios/tests/integration/buffer_duration_test.swift +0 -185
  78. package/ios/tests/integration/compressed_only_output_test.swift +0 -271
  79. package/ios/tests/integration/output_control_test.swift +0 -322
  80. package/ios/tests/integration/run_integration_tests.sh +0 -37
  81. package/ios/tests/opus_support_test_macos.swift +0 -154
  82. package/ios/tests/standalone/audio_processing_test.swift +0 -144
  83. package/ios/tests/standalone/audio_recording_test.swift +0 -277
  84. package/ios/tests/standalone/audio_streaming_test.swift +0 -249
  85. package/ios/tests/standalone/standalone_test.swift +0 -144
@@ -1,249 +0,0 @@
1
- #!/usr/bin/env swift
2
-
3
- import Foundation
4
- import AVFoundation
5
-
6
- // Simple test framework
7
- struct TestResult {
8
- let name: String
9
- let passed: Bool
10
- let message: String
11
- }
12
-
13
- class AudioStreamingTest {
14
- var results: [TestResult] = []
15
- let testDir: URL
16
- var audioEngine: AVAudioEngine?
17
- var inputNode: AVAudioInputNode?
18
-
19
- init() {
20
- // Create a temporary directory for test files
21
- let tempDir = FileManager.default.temporaryDirectory
22
- testDir = tempDir.appendingPathComponent("audio_streaming_test_\(UUID().uuidString)")
23
- try? FileManager.default.createDirectory(at: testDir, withIntermediateDirectories: true)
24
- }
25
-
26
- deinit {
27
- // Clean up
28
- audioEngine?.stop()
29
- try? FileManager.default.removeItem(at: testDir)
30
- }
31
-
32
- func assert(_ condition: Bool, _ message: String, file: String = #file, line: Int = #line) {
33
- let testName = "\(file.split(separator: "/").last ?? ""):\(line)"
34
- results.append(TestResult(name: testName, passed: condition, message: message))
35
- if !condition {
36
- print("โŒ FAILED: \(message) at \(testName)")
37
- }
38
- }
39
-
40
- func assertEqual<T: Equatable>(_ a: T, _ b: T, _ message: String = "", file: String = #file, line: Int = #line) {
41
- let passed = a == b
42
- let msg = message.isEmpty ? "\(a) should equal \(b)" : message
43
- assert(passed, msg, file: file, line: line)
44
- }
45
-
46
- func run() {
47
- print("๐Ÿงช Running iOS Audio Streaming Tests...\n")
48
-
49
- testAudioEngineSetup()
50
- testRealtimeStreaming()
51
- testBufferProcessing()
52
- testMultipleFormats()
53
-
54
- // Print summary
55
- let passed = results.filter { $0.passed }.count
56
- let total = results.count
57
-
58
- print("\n๐Ÿ“Š Test Summary:")
59
- print(" Total: \(total)")
60
- print(" Passed: \(passed)")
61
- print(" Failed: \(total - passed)")
62
-
63
- if passed == total {
64
- print("\nโœ… All tests passed!")
65
- } else {
66
- print("\nโŒ Some tests failed!")
67
- exit(1)
68
- }
69
- }
70
-
71
- func testAudioEngineSetup() {
72
- print("Testing AVAudioEngine setup...")
73
-
74
- audioEngine = AVAudioEngine()
75
- inputNode = audioEngine!.inputNode
76
-
77
- assert(audioEngine != nil, "Audio engine should be created")
78
- assert(inputNode != nil, "Input node should be available")
79
-
80
- // Check input format
81
- let inputFormat = inputNode!.inputFormat(forBus: 0)
82
- assert(inputFormat.sampleRate > 0, "Input format should have valid sample rate")
83
- assert(inputFormat.channelCount > 0, "Input format should have channels")
84
-
85
- print(" Sample rate: \(inputFormat.sampleRate) Hz")
86
- print(" Channels: \(inputFormat.channelCount)")
87
- print(" Format: \(inputFormat.commonFormat.rawValue)")
88
-
89
- print("โœ“ Audio engine setup test completed")
90
- }
91
-
92
- func testRealtimeStreaming() {
93
- print("\nTesting real-time audio streaming...")
94
-
95
- guard let engine = audioEngine, let input = inputNode else {
96
- assert(false, "Audio engine not initialized")
97
- return
98
- }
99
-
100
- var bufferCount = 0
101
- var totalFrames: AVAudioFrameCount = 0
102
- let expectation = DispatchSemaphore(value: 0)
103
-
104
- // Install tap on input
105
- let format = input.outputFormat(forBus: 0)
106
- input.installTap(onBus: 0, bufferSize: 1024, format: format) { buffer, time in
107
- bufferCount += 1
108
- totalFrames += buffer.frameLength
109
-
110
- // Stop after collecting some buffers
111
- if bufferCount >= 10 {
112
- expectation.signal()
113
- }
114
- }
115
-
116
- do {
117
- try engine.start()
118
- assert(engine.isRunning, "Engine should be running")
119
-
120
- // Wait for buffers
121
- let timeout = expectation.wait(timeout: .now() + 2.0)
122
- assert(timeout == .success, "Should receive audio buffers within timeout")
123
-
124
- engine.stop()
125
- input.removeTap(onBus: 0)
126
-
127
- assert(bufferCount >= 10, "Should have received at least 10 buffers")
128
- assert(totalFrames > 0, "Should have received audio frames")
129
-
130
- print(" Received \(bufferCount) buffers")
131
- print(" Total frames: \(totalFrames)")
132
-
133
- print("โœ“ Real-time streaming test completed")
134
-
135
- } catch {
136
- assert(false, "Failed to start audio engine: \(error)")
137
- }
138
- }
139
-
140
- func testBufferProcessing() {
141
- print("\nTesting buffer processing...")
142
-
143
- guard let engine = audioEngine, let input = inputNode else {
144
- assert(false, "Audio engine not initialized")
145
- return
146
- }
147
-
148
- var processedBuffers = 0
149
- var maxAmplitude: Float = 0
150
- var totalRMS: Float = 0
151
- let expectation = DispatchSemaphore(value: 0)
152
-
153
- let format = input.outputFormat(forBus: 0)
154
- input.installTap(onBus: 0, bufferSize: 2048, format: format) { buffer, time in
155
- // Process buffer
156
- if let channelData = buffer.floatChannelData {
157
- let frameLength = Int(buffer.frameLength)
158
- let channelCount = Int(buffer.format.channelCount)
159
-
160
- for channel in 0..<channelCount {
161
- let samples = channelData[channel]
162
-
163
- // Find max amplitude
164
- for i in 0..<frameLength {
165
- let amplitude = abs(samples[i])
166
- if amplitude > maxAmplitude {
167
- maxAmplitude = amplitude
168
- }
169
- }
170
-
171
- // Calculate RMS
172
- var sum: Float = 0
173
- for i in 0..<frameLength {
174
- sum += samples[i] * samples[i]
175
- }
176
- let rms = sqrt(sum / Float(frameLength))
177
- totalRMS += rms
178
- }
179
- }
180
-
181
- processedBuffers += 1
182
- if processedBuffers >= 5 {
183
- expectation.signal()
184
- }
185
- }
186
-
187
- do {
188
- try engine.start()
189
-
190
- let timeout = expectation.wait(timeout: .now() + 2.0)
191
- assert(timeout == .success, "Should process buffers within timeout")
192
-
193
- engine.stop()
194
- input.removeTap(onBus: 0)
195
-
196
- assert(processedBuffers >= 5, "Should have processed at least 5 buffers")
197
- // Note: maxAmplitude might be 0 if there's silence
198
- print(" Processed buffers: \(processedBuffers)")
199
- print(" Max amplitude: \(maxAmplitude)")
200
- print(" Average RMS: \(totalRMS / Float(processedBuffers))")
201
-
202
- print("โœ“ Buffer processing test completed")
203
-
204
- } catch {
205
- assert(false, "Failed to process buffers: \(error)")
206
- }
207
- }
208
-
209
- func testMultipleFormats() {
210
- print("\nTesting multiple audio formats...")
211
-
212
- // Test creating different format converters
213
- let sampleRates = [8000.0, 16000.0, 44100.0, 48000.0]
214
-
215
- for sampleRate in sampleRates {
216
- // Create a format
217
- guard let format = AVAudioFormat(
218
- commonFormat: .pcmFormatFloat32,
219
- sampleRate: sampleRate,
220
- channels: 1,
221
- interleaved: false
222
- ) else {
223
- assert(false, "Failed to create format at \(sampleRate)Hz")
224
- continue
225
- }
226
-
227
- assert(format.sampleRate == sampleRate, "Format should have correct sample rate")
228
- assert(format.channelCount == 1, "Format should be mono")
229
- assert(format.commonFormat == .pcmFormatFloat32, "Format should be float32")
230
-
231
- // Test creating a buffer with this format
232
- guard let buffer = AVAudioPCMBuffer(
233
- pcmFormat: format,
234
- frameCapacity: AVAudioFrameCount(sampleRate * 0.1) // 100ms
235
- ) else {
236
- assert(false, "Failed to create buffer at \(sampleRate)Hz")
237
- continue
238
- }
239
-
240
- assert(buffer.format.sampleRate == sampleRate, "Buffer format should match")
241
- }
242
-
243
- print("โœ“ Multiple formats test completed")
244
- }
245
- }
246
-
247
- // Run the tests
248
- let test = AudioStreamingTest()
249
- test.run()
@@ -1,144 +0,0 @@
1
- #!/usr/bin/env swift
2
-
3
- import Foundation
4
- import AVFoundation
5
-
6
- // Simple test framework
7
- struct TestResult {
8
- let name: String
9
- let passed: Bool
10
- let message: String
11
- }
12
-
13
- class SimpleTest {
14
- var results: [TestResult] = []
15
-
16
- func assert(_ condition: Bool, _ message: String, file: String = #file, line: Int = #line) {
17
- let testName = "\(file.split(separator: "/").last ?? ""):\(line)"
18
- results.append(TestResult(name: testName, passed: condition, message: message))
19
- if !condition {
20
- print("โŒ FAILED: \(message) at \(testName)")
21
- }
22
- }
23
-
24
- func assertEqual<T: Equatable>(_ a: T, _ b: T, _ message: String = "", file: String = #file, line: Int = #line) {
25
- let passed = a == b
26
- let msg = message.isEmpty ? "\(a) should equal \(b)" : message
27
- assert(passed, msg, file: file, line: line)
28
- }
29
-
30
- func run() {
31
- print("๐Ÿงช Running iOS Audio Tests...\n")
32
-
33
- testWAVHeader()
34
- testAudioBuffer()
35
-
36
- // Print summary
37
- let passed = results.filter { $0.passed }.count
38
- let total = results.count
39
-
40
- print("\n๐Ÿ“Š Test Summary:")
41
- print(" Total: \(total)")
42
- print(" Passed: \(passed)")
43
- print(" Failed: \(total - passed)")
44
-
45
- if passed == total {
46
- print("\nโœ… All tests passed!")
47
- } else {
48
- print("\nโŒ Some tests failed!")
49
- exit(1)
50
- }
51
- }
52
-
53
- func testWAVHeader() {
54
- print("Testing WAV header creation...")
55
-
56
- let sampleRate = 44100
57
- let channels = 2
58
- let bitsPerSample = 16
59
- let dataSize = 1024
60
-
61
- // Create header
62
- var header = Data()
63
-
64
- // RIFF chunk
65
- header.append("RIFF".data(using: .ascii)!)
66
- var fileSize = UInt32(dataSize + 36).littleEndian
67
- header.append(Data(bytes: &fileSize, count: 4))
68
- header.append("WAVE".data(using: .ascii)!)
69
-
70
- // fmt chunk
71
- header.append("fmt ".data(using: .ascii)!)
72
- var fmtSize = UInt32(16).littleEndian
73
- header.append(Data(bytes: &fmtSize, count: 4))
74
- var audioFormat = UInt16(1).littleEndian
75
- header.append(Data(bytes: &audioFormat, count: 2))
76
- var numChannels = UInt16(channels).littleEndian
77
- header.append(Data(bytes: &numChannels, count: 2))
78
- var sampleRateValue = UInt32(sampleRate).littleEndian
79
- header.append(Data(bytes: &sampleRateValue, count: 4))
80
- let byteRate = sampleRate * channels * (bitsPerSample / 8)
81
- var byteRateValue = UInt32(byteRate).littleEndian
82
- header.append(Data(bytes: &byteRateValue, count: 4))
83
- let blockAlign = channels * (bitsPerSample / 8)
84
- var blockAlignValue = UInt16(blockAlign).littleEndian
85
- header.append(Data(bytes: &blockAlignValue, count: 2))
86
- var bitsPerSampleValue = UInt16(bitsPerSample).littleEndian
87
- header.append(Data(bytes: &bitsPerSampleValue, count: 2))
88
-
89
- // data chunk
90
- header.append("data".data(using: .ascii)!)
91
- var dataSizeValue = UInt32(dataSize).littleEndian
92
- header.append(Data(bytes: &dataSizeValue, count: 4))
93
-
94
- // Tests
95
- assertEqual(header.count, 44, "WAV header should be 44 bytes")
96
-
97
- let riffHeader = String(data: header[0..<4], encoding: .ascii)
98
- assertEqual(riffHeader, "RIFF", "Should have RIFF header")
99
-
100
- let waveFormat = String(data: header[8..<12], encoding: .ascii)
101
- assertEqual(waveFormat, "WAVE", "Should have WAVE format")
102
-
103
- print("โœ“ WAV header test completed")
104
- }
105
-
106
- func testAudioBuffer() {
107
- print("\nTesting audio buffer creation...")
108
-
109
- let sampleRate = 44100.0
110
- let duration = 0.1
111
- let frequency = 440.0
112
-
113
- let frameCount = Int(sampleRate * duration)
114
- let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: 1)!
115
-
116
- guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(frameCount)) else {
117
- assert(false, "Failed to create audio buffer")
118
- return
119
- }
120
-
121
- buffer.frameLength = AVAudioFrameCount(frameCount)
122
-
123
- // Generate sine wave
124
- let channelData = buffer.floatChannelData![0]
125
- for frame in 0..<frameCount {
126
- let phase = 2.0 * Double.pi * frequency * Double(frame) / sampleRate
127
- channelData[frame] = Float(sin(phase) * 0.5)
128
- }
129
-
130
- // Tests
131
- assertEqual(Int(buffer.frameLength), frameCount, "Frame length should match")
132
- assertEqual(buffer.format.sampleRate, sampleRate, "Sample rate should match")
133
- assertEqual(Int(buffer.format.channelCount), 1, "Should have 1 channel")
134
-
135
- let middleSample = channelData[frameCount / 4]
136
- assert(abs(middleSample) > 0.001, "Middle sample should not be zero")
137
-
138
- print("โœ“ Audio buffer test completed")
139
- }
140
- }
141
-
142
- // Run the tests
143
- let test = SimpleTest()
144
- test.run()