@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.
- package/CHANGELOG.md +356 -5
- package/android/src/main/java/net/siteed/audiostudio/AudioStreamDecoder.kt +306 -94
- package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +39 -6
- package/build/cjs/errors/AudioStreamError.js +9 -0
- package/build/cjs/errors/AudioStreamError.js.map +1 -1
- package/build/cjs/errors/AudioStreamError.test.js +22 -1
- package/build/cjs/errors/AudioStreamError.test.js.map +1 -1
- package/build/cjs/streamAudioData.js +99 -32
- package/build/cjs/streamAudioData.js.map +1 -1
- package/build/cjs/utils/audioProcessing.js +14 -10
- package/build/cjs/utils/audioProcessing.js.map +1 -1
- package/build/esm/errors/AudioStreamError.js +9 -0
- package/build/esm/errors/AudioStreamError.js.map +1 -1
- package/build/esm/errors/AudioStreamError.test.js +22 -1
- package/build/esm/errors/AudioStreamError.test.js.map +1 -1
- package/build/esm/streamAudioData.js +99 -32
- package/build/esm/streamAudioData.js.map +1 -1
- package/build/esm/utils/audioProcessing.js +14 -10
- package/build/esm/utils/audioProcessing.js.map +1 -1
- package/build/types/errors/AudioStreamError.d.ts.map +1 -1
- package/build/types/streamAudioData.d.ts +5 -0
- package/build/types/streamAudioData.d.ts.map +1 -1
- package/build/types/utils/audioProcessing.d.ts +2 -2
- package/build/types/utils/audioProcessing.d.ts.map +1 -1
- package/ios/AudioStreamDecoder.swift +191 -100
- package/ios/AudioStudioModule.swift +48 -9
- package/package.json +163 -146
- package/scripts/README.md +58 -0
- package/src/errors/AudioStreamError.test.ts +29 -2
- package/src/errors/AudioStreamError.ts +14 -0
- package/src/streamAudioData.ts +146 -42
- 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/AudioStreamDecoderTests.swift +0 -128
- 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
|
@@ -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()
|