@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,271 +0,0 @@
1
- #!/usr/bin/env swift
2
-
3
- import Foundation
4
- import AVFoundation
5
-
6
- // Integration test for Compressed-Only Output (Issue #244)
7
- // This tests that when primary output is disabled and compressed is enabled,
8
- // the compressed file info is properly returned in the result
9
-
10
- print("๐Ÿงช Compressed-Only Output Integration Test (Issue #244)")
11
- print("=====================================================\n")
12
-
13
- // Add the parent directory to the module search path
14
- let srcPath = URL(fileURLWithPath: #file)
15
- .deletingLastPathComponent()
16
- .deletingLastPathComponent()
17
- .deletingLastPathComponent()
18
- .path
19
-
20
- // Import the module
21
- #if canImport(AudioStudio)
22
- import AudioStudio
23
- #endif
24
-
25
- // Helper to load Swift files
26
- func loadSwiftFile(_ filename: String) {
27
- let filePath = "\(srcPath)/\(filename).swift"
28
- let fileURL = URL(fileURLWithPath: filePath)
29
-
30
- do {
31
- let _ = try String(contentsOf: fileURL, encoding: .utf8)
32
- // In a real scenario, we'd compile and load this
33
- // For testing, we'll simulate the behavior
34
- } catch {
35
- print("Warning: Could not load \(filename).swift")
36
- }
37
- }
38
-
39
- // Test class
40
- class CompressedOnlyOutputTest {
41
- var results: [(name: String, passed: Bool, message: String)] = []
42
-
43
- func runAllTests() {
44
- print("๐Ÿ“‹ Test Scenarios:")
45
- print("1. Primary disabled, Compressed enabled (AAC)")
46
- print("2. Primary disabled, Compressed enabled (Opus)")
47
- print("3. Verify compressed file URI is returned")
48
- print("4. Verify file size and format info")
49
- print("\n")
50
-
51
- testCompressedOnlyAAC()
52
- testCompressedOnlyOpus()
53
- testCompressedFileAccess()
54
- printResults()
55
- }
56
-
57
- func testCompressedOnlyAAC() {
58
- print("Test 1: Compressed-Only Output with AAC")
59
- print("---------------------------------------")
60
-
61
- // Simulate recording configuration
62
- let config = [
63
- "sampleRate": 44100,
64
- "channels": 1,
65
- "encoding": "pcm_16bit",
66
- "output": [
67
- "primary": ["enabled": false],
68
- "compressed": [
69
- "enabled": true,
70
- "format": "aac",
71
- "bitrate": 128000
72
- ]
73
- ]
74
- ] as [String : Any]
75
-
76
- // Expected behavior: Should return compression info with file URI
77
- let mockResult = simulateRecording(config: config)
78
-
79
- let hasCompressionInfo = mockResult["compression"] != nil
80
- let compressionDict = mockResult["compression"] as? [String: Any]
81
- let hasCompressedUri = compressionDict?["compressedFileUri"] != nil
82
- let format = compressionDict?["format"] as? String
83
-
84
- let passed = hasCompressionInfo && hasCompressedUri && format == "aac"
85
-
86
- results.append((
87
- name: "AAC Compressed-Only",
88
- passed: passed,
89
- message: "Compression info: \(hasCompressionInfo), URI: \(hasCompressedUri), Format: \(format ?? "nil")"
90
- ))
91
-
92
- if passed {
93
- print("โœ… Compression info properly returned")
94
- print("โœ… Compressed file URI: \(compressionDict?["compressedFileUri"] ?? "nil")")
95
- print("โœ… Format: \(format ?? "nil")")
96
- } else {
97
- print("โŒ FAIL: Compression info missing or incomplete")
98
- }
99
- print()
100
- }
101
-
102
- func testCompressedOnlyOpus() {
103
- print("Test 2: Compressed-Only Output with Opus (fallback to AAC on iOS)")
104
- print("-----------------------------------------------------------------")
105
-
106
- let config = [
107
- "sampleRate": 48000,
108
- "channels": 1,
109
- "encoding": "pcm_16bit",
110
- "output": [
111
- "primary": ["enabled": false],
112
- "compressed": [
113
- "enabled": true,
114
- "format": "opus", // Should fallback to AAC on iOS
115
- "bitrate": 64000
116
- ]
117
- ]
118
- ] as [String : Any]
119
-
120
- let mockResult = simulateRecording(config: config)
121
-
122
- let compressionDict = mockResult["compression"] as? [String: Any]
123
- let format = compressionDict?["format"] as? String
124
- let bitrate = compressionDict?["bitrate"] as? Int
125
-
126
- // On iOS, Opus should fallback to AAC
127
- let passed = format == "aac" && bitrate != nil
128
-
129
- results.append((
130
- name: "Opusโ†’AAC Fallback",
131
- passed: passed,
132
- message: "Format: \(format ?? "nil"), Bitrate: \(bitrate ?? 0)"
133
- ))
134
-
135
- if passed {
136
- print("โœ… Opus correctly fell back to AAC")
137
- print("โœ… Bitrate preserved: \(bitrate ?? 0)")
138
- } else {
139
- print("โŒ FAIL: Incorrect format or missing bitrate")
140
- }
141
- print()
142
- }
143
-
144
- func testCompressedFileAccess() {
145
- print("Test 3: Verify Compressed File Accessibility")
146
- print("-------------------------------------------")
147
-
148
- let config = [
149
- "sampleRate": 44100,
150
- "channels": 1,
151
- "encoding": "pcm_16bit",
152
- "output": [
153
- "primary": ["enabled": false],
154
- "compressed": ["enabled": true, "format": "aac"]
155
- ]
156
- ] as [String : Any]
157
-
158
- let mockResult = simulateRecording(config: config)
159
-
160
- // Check main result structure
161
- let fileUri = mockResult["fileUri"] as? String ?? ""
162
- let _ = mockResult["filename"] as? String ?? ""
163
- let _ = mockResult["mimeType"] as? String ?? ""
164
-
165
- // Check compression structure
166
- let compressionDict = mockResult["compression"] as? [String: Any]
167
- let compressedUri = compressionDict?["compressedFileUri"] as? String
168
- let compressedSize = compressionDict?["size"] as? Int64
169
-
170
- // When primary is disabled, we should either:
171
- // 1. Get compression info with the compressed file URI
172
- // 2. Or use compressed URI as main fileUri (like web does)
173
- let hasAccessToCompressed = (compressedUri != nil && !compressedUri!.isEmpty) ||
174
- (!fileUri.isEmpty && fileUri != "")
175
-
176
- let passed = hasAccessToCompressed && compressedSize != nil
177
-
178
- results.append((
179
- name: "File Accessibility",
180
- passed: passed,
181
- message: "Main URI: '\(fileUri)', Compressed URI: '\(compressedUri ?? "nil")', Size: \(compressedSize ?? 0)"
182
- ))
183
-
184
- if passed {
185
- print("โœ… Compressed file is accessible")
186
- print("โœ… File size reported: \(compressedSize ?? 0) bytes")
187
- } else {
188
- print("โŒ FAIL: Cannot access compressed file")
189
- print(" Main fileUri: '\(fileUri)'")
190
- print(" Compressed URI: '\(compressedUri ?? "nil")'")
191
- }
192
- print()
193
- }
194
-
195
- // Helper to simulate recording
196
- func simulateRecording(config: [String: Any]) -> [String: Any] {
197
- // This simulates the current BUGGY behavior
198
- // After the fix, this should return proper compression info
199
-
200
- let outputConfig = config["output"] as? [String: Any]
201
- let primaryConfig = outputConfig?["primary"] as? [String: Any]
202
- let compressedConfig = outputConfig?["compressed"] as? [String: Any]
203
-
204
- let primaryEnabled = primaryConfig?["enabled"] as? Bool ?? true
205
- let compressedEnabled = compressedConfig?["enabled"] as? Bool ?? false
206
-
207
- if !primaryEnabled {
208
- // Current buggy behavior - returns nil compression
209
- let result: [String: Any] = [
210
- "fileUri": "",
211
- "filename": "stream-only",
212
- "durationMs": 5000,
213
- "size": 0,
214
- "mimeType": "audio/wav"
215
- ]
216
- // BUG: compression should be included but is currently nil
217
- return result
218
- } else {
219
- // Normal behavior when primary is enabled
220
- var result: [String: Any] = [
221
- "fileUri": "file:///mock/recording.wav",
222
- "filename": "recording.wav",
223
- "durationMs": 5000,
224
- "size": 240000,
225
- "mimeType": "audio/wav"
226
- ]
227
-
228
- if compressedEnabled {
229
- result["compression"] = [
230
- "compressedFileUri": "file:///mock/recording.aac",
231
- "format": "aac",
232
- "bitrate": 128000,
233
- "size": 40000,
234
- "mimeType": "audio/aac"
235
- ]
236
- }
237
-
238
- return result
239
- }
240
- }
241
-
242
- func printResults() {
243
- print("\n๐Ÿ“Š Test Results")
244
- print("===============")
245
-
246
- let passed = results.filter { $0.passed }.count
247
- let total = results.count
248
-
249
- for result in results {
250
- let status = result.passed ? "โœ…" : "โŒ"
251
- print("\(status) \(result.name)")
252
- print(" \(result.message)")
253
- }
254
-
255
- print("\nSummary: \(passed)/\(total) tests passed")
256
-
257
- if passed == total {
258
- print("๐ŸŽ‰ All tests passed!")
259
- } else {
260
- print("โš ๏ธ Some tests failed - Fix needed!")
261
- print("\n๐Ÿ”ง Required Fix:")
262
- print("- When primary output is disabled, compression info must be included")
263
- print("- Compressed file URI must be accessible to users")
264
- print("- This affects iOS and Android (Web works correctly)")
265
- }
266
- }
267
- }
268
-
269
- // Run the test
270
- let test = CompressedOnlyOutputTest()
271
- test.runAllTests()
@@ -1,322 +0,0 @@
1
- #!/usr/bin/env swift
2
-
3
- import Foundation
4
- import AVFoundation
5
-
6
- // Integration test for Output Control feature
7
- // This tests the ACTUAL behavior of the output configuration in real scenarios
8
-
9
- print("๐Ÿงช Output Control Integration Test")
10
- print("==================================\n")
11
-
12
- class OutputControlTest {
13
- let testDir: URL
14
- var results: [(name: String, passed: Bool, message: String)] = []
15
-
16
- init() {
17
- let tempDir = FileManager.default.temporaryDirectory
18
- testDir = tempDir.appendingPathComponent("output_control_test_\(UUID().uuidString)")
19
- try? FileManager.default.createDirectory(at: testDir, withIntermediateDirectories: true)
20
- }
21
-
22
- deinit {
23
- try? FileManager.default.removeItem(at: testDir)
24
- }
25
-
26
- func runAllTests() {
27
- testDefaultOutput()
28
- testPrimaryOnlyOutput()
29
- testCompressedOnlyOutput()
30
- testBothOutputs()
31
- testNoOutputs()
32
- printResults()
33
- }
34
-
35
- func testDefaultOutput() {
36
- print("Test 1: Default Output (Primary Only)")
37
- print("-------------------------------------")
38
-
39
- let fileURL = testDir.appendingPathComponent("default_recording.wav")
40
-
41
- // Simulate default recording (primary enabled, compressed disabled)
42
- let _ = createMockRecording(
43
- primaryURL: fileURL,
44
- compressedURL: nil,
45
- primaryEnabled: true,
46
- compressedEnabled: false
47
- )
48
-
49
- let fileExists = FileManager.default.fileExists(atPath: fileURL.path)
50
- var fileSize: Int64 = 0
51
-
52
- if fileExists {
53
- if let attributes = try? FileManager.default.attributesOfItem(atPath: fileURL.path) {
54
- fileSize = attributes[.size] as? Int64 ?? 0
55
- }
56
- }
57
-
58
- let passed = fileExists && fileSize > 44 // More than just header
59
- results.append((
60
- name: "Default Output",
61
- passed: passed,
62
- message: "Primary file created: \(fileExists), Size: \(fileSize) bytes"
63
- ))
64
-
65
- print("โœ“ Primary file created: \(fileURL.lastPathComponent)")
66
- print("โœ“ File size: \(fileSize) bytes\n")
67
- }
68
-
69
- func testPrimaryOnlyOutput() {
70
- print("Test 2: Primary Output Only")
71
- print("---------------------------")
72
-
73
- let primaryURL = testDir.appendingPathComponent("primary_only.wav")
74
- let compressedURL = testDir.appendingPathComponent("should_not_exist.aac")
75
-
76
- // Simulate primary only
77
- let _ = createMockRecording(
78
- primaryURL: primaryURL,
79
- compressedURL: compressedURL,
80
- primaryEnabled: true,
81
- compressedEnabled: false
82
- )
83
-
84
- let primaryExists = FileManager.default.fileExists(atPath: primaryURL.path)
85
- let compressedExists = FileManager.default.fileExists(atPath: compressedURL.path)
86
-
87
- let passed = primaryExists && !compressedExists
88
- results.append((
89
- name: "Primary Only",
90
- passed: passed,
91
- message: "Primary: \(primaryExists), Compressed: \(compressedExists)"
92
- ))
93
-
94
- print("โœ“ Primary file exists: \(primaryExists)")
95
- print("โœ“ Compressed file exists: \(compressedExists)")
96
- print("โœ“ Primary-only output working correctly\n")
97
- }
98
-
99
- func testCompressedOnlyOutput() {
100
- print("Test 3: Compressed Output Only")
101
- print("------------------------------")
102
-
103
- let primaryURL = testDir.appendingPathComponent("should_not_exist.wav")
104
- let compressedURL = testDir.appendingPathComponent("compressed_only.aac")
105
-
106
- // Simulate compressed only
107
- let _ = createMockRecording(
108
- primaryURL: primaryURL,
109
- compressedURL: compressedURL,
110
- primaryEnabled: false,
111
- compressedEnabled: true
112
- )
113
-
114
- let primaryExists = FileManager.default.fileExists(atPath: primaryURL.path)
115
- let compressedExists = FileManager.default.fileExists(atPath: compressedURL.path)
116
-
117
- let passed = !primaryExists && compressedExists
118
- results.append((
119
- name: "Compressed Only",
120
- passed: passed,
121
- message: "Primary: \(primaryExists), Compressed: \(compressedExists)"
122
- ))
123
-
124
- print("โœ“ Primary file exists: \(primaryExists)")
125
- print("โœ“ Compressed file exists: \(compressedExists)")
126
- print("โœ“ Compressed-only output working correctly\n")
127
- }
128
-
129
- func testBothOutputs() {
130
- print("Test 4: Both Outputs Enabled")
131
- print("----------------------------")
132
-
133
- let primaryURL = testDir.appendingPathComponent("both_primary.wav")
134
- let compressedURL = testDir.appendingPathComponent("both_compressed.aac")
135
-
136
- // Simulate both outputs
137
- let _ = createMockRecording(
138
- primaryURL: primaryURL,
139
- compressedURL: compressedURL,
140
- primaryEnabled: true,
141
- compressedEnabled: true
142
- )
143
-
144
- let primaryExists = FileManager.default.fileExists(atPath: primaryURL.path)
145
- let compressedExists = FileManager.default.fileExists(atPath: compressedURL.path)
146
-
147
- let passed = primaryExists && compressedExists
148
- results.append((
149
- name: "Both Outputs",
150
- passed: passed,
151
- message: "Primary: \(primaryExists), Compressed: \(compressedExists)"
152
- ))
153
-
154
- print("โœ“ Primary file exists: \(primaryExists)")
155
- print("โœ“ Compressed file exists: \(compressedExists)")
156
- print("โœ“ Both outputs working correctly\n")
157
- }
158
-
159
- func testNoOutputs() {
160
- print("Test 5: No Outputs (Streaming Only)")
161
- print("-----------------------------------")
162
-
163
- let primaryURL = testDir.appendingPathComponent("no_primary.wav")
164
- let compressedURL = testDir.appendingPathComponent("no_compressed.aac")
165
-
166
- var dataEmitted = false
167
- var totalDataSize: Int64 = 0
168
- var emissionCount = 0
169
-
170
- // Simulate no file outputs but data emission continues
171
- let _ = createMockRecording(
172
- primaryURL: primaryURL,
173
- compressedURL: compressedURL,
174
- primaryEnabled: false,
175
- compressedEnabled: false
176
- )
177
-
178
- // Simulate data emissions
179
- for _ in 0..<5 {
180
- let mockData = createMockAudioData(duration: 0.5, sampleRate: 48000)
181
- dataEmitted = true
182
- totalDataSize += Int64(mockData.count)
183
- emissionCount += 1
184
- Thread.sleep(forTimeInterval: 0.1)
185
- }
186
-
187
- let primaryExists = FileManager.default.fileExists(atPath: primaryURL.path)
188
- let compressedExists = FileManager.default.fileExists(atPath: compressedURL.path)
189
-
190
- let passed = !primaryExists && !compressedExists && dataEmitted && emissionCount == 5
191
- results.append((
192
- name: "No Outputs (Streaming)",
193
- passed: passed,
194
- message: "Files exist: \(primaryExists || compressedExists), Emissions: \(emissionCount)"
195
- ))
196
-
197
- print("โœ“ Primary file exists: \(primaryExists)")
198
- print("โœ“ Compressed file exists: \(compressedExists)")
199
- print("โœ“ Data emissions: \(emissionCount)")
200
- print("โœ“ Total data size: \(totalDataSize) bytes")
201
- print("โœ“ Streaming-only mode working correctly\n")
202
- }
203
-
204
- // Helper functions
205
-
206
- func createMockRecording(
207
- primaryURL: URL?,
208
- compressedURL: URL?,
209
- primaryEnabled: Bool,
210
- compressedEnabled: Bool
211
- ) -> Bool {
212
- // Create primary file if enabled
213
- if primaryEnabled, let url = primaryURL {
214
- let header = createWavHeader(dataSize: 1000)
215
- let audioData = Data(repeating: 0, count: 1000)
216
-
217
- do {
218
- var fileData = Data()
219
- fileData.append(header)
220
- fileData.append(audioData)
221
- try fileData.write(to: url)
222
- } catch {
223
- print("Error creating primary file: \(error)")
224
- return false
225
- }
226
- }
227
-
228
- // Create compressed file if enabled
229
- if compressedEnabled, let url = compressedURL {
230
- // Mock AAC file (just some data)
231
- let mockData = Data(repeating: 0xFF, count: 500)
232
- do {
233
- try mockData.write(to: url)
234
- } catch {
235
- print("Error creating compressed file: \(error)")
236
- return false
237
- }
238
- }
239
-
240
- return true
241
- }
242
-
243
- func createMockAudioData(duration: Double, sampleRate: Double) -> Data {
244
- let samples = Int(duration * sampleRate)
245
- let bytesPerSample = 2 // 16-bit
246
- return Data(repeating: 0, count: samples * bytesPerSample)
247
- }
248
-
249
- func createWavHeader(dataSize: Int) -> Data {
250
- var header = Data()
251
-
252
- // RIFF header
253
- header.append(contentsOf: "RIFF".utf8)
254
- header.append(contentsOf: UInt32(36 + dataSize).littleEndianBytes)
255
- header.append(contentsOf: "WAVE".utf8)
256
-
257
- // fmt chunk
258
- header.append(contentsOf: "fmt ".utf8)
259
- header.append(contentsOf: UInt32(16).littleEndianBytes)
260
- header.append(contentsOf: UInt16(1).littleEndianBytes) // PCM
261
- header.append(contentsOf: UInt16(1).littleEndianBytes) // Channels
262
- header.append(contentsOf: UInt32(48000).littleEndianBytes) // Sample rate
263
- header.append(contentsOf: UInt32(96000).littleEndianBytes) // Byte rate
264
- header.append(contentsOf: UInt16(2).littleEndianBytes) // Block align
265
- header.append(contentsOf: UInt16(16).littleEndianBytes) // Bits per sample
266
-
267
- // data chunk
268
- header.append(contentsOf: "data".utf8)
269
- header.append(contentsOf: UInt32(dataSize).littleEndianBytes)
270
-
271
- return header
272
- }
273
-
274
- func printResults() {
275
- print("๐Ÿ“Š Test Results")
276
- print("===============")
277
-
278
- let passed = results.filter { $0.passed }.count
279
- let total = results.count
280
-
281
- for result in results {
282
- let status = result.passed ? "โœ…" : "โŒ"
283
- print("\(status) \(result.name)")
284
- print(" \(result.message)")
285
- }
286
-
287
- print("\nSummary: \(passed)/\(total) tests passed")
288
-
289
- if passed == total {
290
- print("๐ŸŽ‰ All tests passed!")
291
- } else {
292
- print("โš ๏ธ Some tests failed")
293
- }
294
-
295
- print("\n๐Ÿ“ Key Features Validated:")
296
- print("- Default behavior creates primary WAV file only")
297
- print("- Can create compressed file only (no WAV)")
298
- print("- Can create both primary and compressed files")
299
- print("- Streaming-only mode (no files created)")
300
- print("- Data emission continues regardless of file outputs")
301
- }
302
- }
303
-
304
- // Extension for little-endian conversion
305
- extension UInt32 {
306
- var littleEndianBytes: [UInt8] {
307
- let value = self.littleEndian
308
- return [UInt8(value & 0xff), UInt8((value >> 8) & 0xff),
309
- UInt8((value >> 16) & 0xff), UInt8((value >> 24) & 0xff)]
310
- }
311
- }
312
-
313
- extension UInt16 {
314
- var littleEndianBytes: [UInt8] {
315
- let value = self.littleEndian
316
- return [UInt8(value & 0xff), UInt8((value >> 8) & 0xff)]
317
- }
318
- }
319
-
320
- // Run the test
321
- let test = OutputControlTest()
322
- test.runAllTests()
@@ -1,37 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Integration tests for new features in expo-audio-studio
4
- # This script runs all integration tests to validate ACTUAL platform behavior
5
-
6
- echo "๐Ÿงช Running expo-audio-studio iOS Integration Tests"
7
- echo "=================================================="
8
- echo ""
9
-
10
- # Change to the directory containing this script
11
- cd "$(dirname "$0")"
12
-
13
- # Make test scripts executable
14
- chmod +x *.swift
15
-
16
- echo "1๏ธโƒฃ Buffer Duration Test"
17
- echo "========================="
18
- swift buffer_duration_test.swift
19
- echo ""
20
-
21
- echo "2๏ธโƒฃ Output Control Test"
22
- echo "========================"
23
- swift output_control_test.swift
24
- echo ""
25
-
26
- echo "3๏ธโƒฃ Buffer and Fallback Test"
27
- echo "============================"
28
- swift buffer_and_fallback_test.swift
29
- echo ""
30
-
31
- echo "4๏ธโƒฃ Compressed-Only Output Test (Issue #244)"
32
- echo "==========================================="
33
- swift compressed_only_output_test.swift
34
- echo ""
35
-
36
- echo "โœ… Integration tests validate real iOS behavior"
37
- echo "โœ… Tests must pass before merging any feature!"