@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
package/android/src/androidTest/java/net/siteed/audiostudio/integration/CompressedOnlyOutputTest.kt
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
package net.siteed.audiostudio.integration
|
|
2
|
-
|
|
3
|
-
import android.os.Bundle
|
|
4
|
-
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
5
|
-
import androidx.test.platform.app.InstrumentationRegistry
|
|
6
|
-
import org.junit.Test
|
|
7
|
-
import org.junit.runner.RunWith
|
|
8
|
-
import org.junit.Assert.*
|
|
9
|
-
import java.io.File
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Integration test for Compressed-Only Output (Issue #244)
|
|
13
|
-
*
|
|
14
|
-
* This test validates that when primary output is disabled and compressed output
|
|
15
|
-
* is enabled, the compressed file information is properly returned in the result.
|
|
16
|
-
*/
|
|
17
|
-
@RunWith(AndroidJUnit4::class)
|
|
18
|
-
class CompressedOnlyOutputTest {
|
|
19
|
-
|
|
20
|
-
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
21
|
-
|
|
22
|
-
@Test
|
|
23
|
-
fun testCompressedOnlyOutput_AAC() {
|
|
24
|
-
println("๐งช Test: Compressed-Only Output with AAC")
|
|
25
|
-
println("---------------------------------------")
|
|
26
|
-
|
|
27
|
-
// Configuration with primary disabled, compressed enabled
|
|
28
|
-
val config = Bundle().apply {
|
|
29
|
-
putInt("sampleRate", 44100)
|
|
30
|
-
putInt("channels", 1)
|
|
31
|
-
putString("encoding", "pcm_16bit")
|
|
32
|
-
|
|
33
|
-
val outputBundle = Bundle().apply {
|
|
34
|
-
val primaryBundle = Bundle().apply {
|
|
35
|
-
putBoolean("enabled", false)
|
|
36
|
-
}
|
|
37
|
-
val compressedBundle = Bundle().apply {
|
|
38
|
-
putBoolean("enabled", true)
|
|
39
|
-
putString("format", "aac")
|
|
40
|
-
putInt("bitrate", 128000)
|
|
41
|
-
}
|
|
42
|
-
putBundle("primary", primaryBundle)
|
|
43
|
-
putBundle("compressed", compressedBundle)
|
|
44
|
-
}
|
|
45
|
-
putBundle("output", outputBundle)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Simulate recording result
|
|
49
|
-
val result = simulateRecording(config)
|
|
50
|
-
|
|
51
|
-
// Verify compression info is present
|
|
52
|
-
val compressionBundle = result.getBundle("compression")
|
|
53
|
-
assertNotNull("Compression info should not be null", compressionBundle)
|
|
54
|
-
|
|
55
|
-
// Verify compressed file URI is provided
|
|
56
|
-
val compressedFileUri = compressionBundle?.getString("compressedFileUri")
|
|
57
|
-
assertNotNull("Compressed file URI should not be null", compressedFileUri)
|
|
58
|
-
assertNotEquals("Compressed file URI should not be empty", "", compressedFileUri)
|
|
59
|
-
|
|
60
|
-
// Verify format and bitrate
|
|
61
|
-
assertEquals("aac", compressionBundle?.getString("format"))
|
|
62
|
-
assertEquals(128000, compressionBundle?.getInt("bitrate"))
|
|
63
|
-
|
|
64
|
-
println("โ
Compression info properly returned")
|
|
65
|
-
println("โ
Compressed file URI: $compressedFileUri")
|
|
66
|
-
println("โ
Format: ${compressionBundle?.getString("format")}")
|
|
67
|
-
println()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
@Test
|
|
71
|
-
fun testCompressedOnlyOutput_Opus() {
|
|
72
|
-
println("๐งช Test: Compressed-Only Output with Opus")
|
|
73
|
-
println("----------------------------------------")
|
|
74
|
-
|
|
75
|
-
val config = Bundle().apply {
|
|
76
|
-
putInt("sampleRate", 48000)
|
|
77
|
-
putInt("channels", 1)
|
|
78
|
-
putString("encoding", "pcm_16bit")
|
|
79
|
-
|
|
80
|
-
val outputBundle = Bundle().apply {
|
|
81
|
-
val primaryBundle = Bundle().apply {
|
|
82
|
-
putBoolean("enabled", false)
|
|
83
|
-
}
|
|
84
|
-
val compressedBundle = Bundle().apply {
|
|
85
|
-
putBoolean("enabled", true)
|
|
86
|
-
putString("format", "opus")
|
|
87
|
-
putInt("bitrate", 64000)
|
|
88
|
-
}
|
|
89
|
-
putBundle("primary", primaryBundle)
|
|
90
|
-
putBundle("compressed", compressedBundle)
|
|
91
|
-
}
|
|
92
|
-
putBundle("output", outputBundle)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
val result = simulateRecording(config)
|
|
96
|
-
val compressionBundle = result.getBundle("compression")
|
|
97
|
-
|
|
98
|
-
assertNotNull("Compression info should not be null", compressionBundle)
|
|
99
|
-
assertEquals("opus", compressionBundle?.getString("format"))
|
|
100
|
-
assertEquals(64000, compressionBundle?.getInt("bitrate"))
|
|
101
|
-
|
|
102
|
-
println("โ
Opus compression properly configured")
|
|
103
|
-
println()
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@Test
|
|
107
|
-
fun testFileAccessibility() {
|
|
108
|
-
println("๐งช Test: Verify Compressed File Accessibility")
|
|
109
|
-
println("--------------------------------------------")
|
|
110
|
-
|
|
111
|
-
val config = Bundle().apply {
|
|
112
|
-
putInt("sampleRate", 44100)
|
|
113
|
-
putInt("channels", 1)
|
|
114
|
-
putString("encoding", "pcm_16bit")
|
|
115
|
-
|
|
116
|
-
val outputBundle = Bundle().apply {
|
|
117
|
-
val primaryBundle = Bundle().apply {
|
|
118
|
-
putBoolean("enabled", false)
|
|
119
|
-
}
|
|
120
|
-
val compressedBundle = Bundle().apply {
|
|
121
|
-
putBoolean("enabled", true)
|
|
122
|
-
putString("format", "aac")
|
|
123
|
-
}
|
|
124
|
-
putBundle("primary", primaryBundle)
|
|
125
|
-
putBundle("compressed", compressedBundle)
|
|
126
|
-
}
|
|
127
|
-
putBundle("output", outputBundle)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
val result = simulateRecording(config)
|
|
131
|
-
|
|
132
|
-
// Check main result structure
|
|
133
|
-
val fileUri = result.getString("fileUri", "")
|
|
134
|
-
val filename = result.getString("filename", "")
|
|
135
|
-
|
|
136
|
-
// Check compression structure
|
|
137
|
-
val compressionBundle = result.getBundle("compression")
|
|
138
|
-
val compressedUri = compressionBundle?.getString("compressedFileUri")
|
|
139
|
-
val compressedSize = compressionBundle?.getLong("size", 0L) ?: 0L
|
|
140
|
-
|
|
141
|
-
// When primary is disabled, we should have access to compressed file
|
|
142
|
-
val hasAccessToCompressed = !compressedUri.isNullOrEmpty() || fileUri.isNotEmpty()
|
|
143
|
-
|
|
144
|
-
assertTrue("Should have access to compressed file", hasAccessToCompressed)
|
|
145
|
-
assertTrue("Compressed file should have size > 0", compressedSize > 0)
|
|
146
|
-
|
|
147
|
-
println("โ
Compressed file is accessible")
|
|
148
|
-
println("โ
File size reported: $compressedSize bytes")
|
|
149
|
-
println()
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
@Test
|
|
153
|
-
fun testBugScenario() {
|
|
154
|
-
println("๐งช Test: Current Bug Scenario")
|
|
155
|
-
println("-----------------------------")
|
|
156
|
-
|
|
157
|
-
// This test demonstrates the current bug
|
|
158
|
-
val config = Bundle().apply {
|
|
159
|
-
putInt("sampleRate", 44100)
|
|
160
|
-
putInt("channels", 1)
|
|
161
|
-
putString("encoding", "pcm_16bit")
|
|
162
|
-
|
|
163
|
-
val outputBundle = Bundle().apply {
|
|
164
|
-
val primaryBundle = Bundle().apply {
|
|
165
|
-
putBoolean("enabled", false)
|
|
166
|
-
}
|
|
167
|
-
val compressedBundle = Bundle().apply {
|
|
168
|
-
putBoolean("enabled", true)
|
|
169
|
-
putString("format", "aac")
|
|
170
|
-
putInt("bitrate", 128000)
|
|
171
|
-
}
|
|
172
|
-
putBundle("primary", primaryBundle)
|
|
173
|
-
putBundle("compressed", compressedBundle)
|
|
174
|
-
}
|
|
175
|
-
putBundle("output", outputBundle)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Current buggy behavior
|
|
179
|
-
val buggyResult = simulateBuggyRecording(config)
|
|
180
|
-
|
|
181
|
-
// This is what currently happens - compression is null
|
|
182
|
-
val compressionBundle = buggyResult.getBundle("compression")
|
|
183
|
-
|
|
184
|
-
if (compressionBundle == null) {
|
|
185
|
-
println("โ BUG CONFIRMED: Compression info is null when primary is disabled")
|
|
186
|
-
println(" This prevents users from accessing the compressed file")
|
|
187
|
-
} else {
|
|
188
|
-
println("โ
Bug appears to be fixed - compression info is present")
|
|
189
|
-
}
|
|
190
|
-
println()
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Simulates the expected correct behavior after fix
|
|
195
|
-
*/
|
|
196
|
-
private fun simulateRecording(config: Bundle): Bundle {
|
|
197
|
-
val outputConfig = config.getBundle("output")
|
|
198
|
-
val primaryEnabled = outputConfig?.getBundle("primary")?.getBoolean("enabled", true) ?: true
|
|
199
|
-
val compressedConfig = outputConfig?.getBundle("compressed")
|
|
200
|
-
val compressedEnabled = compressedConfig?.getBoolean("enabled", false) ?: false
|
|
201
|
-
|
|
202
|
-
return Bundle().apply {
|
|
203
|
-
if (!primaryEnabled) {
|
|
204
|
-
// Expected behavior after fix
|
|
205
|
-
putString("fileUri", "")
|
|
206
|
-
putString("filename", "stream-only")
|
|
207
|
-
putLong("durationMs", 5000)
|
|
208
|
-
putLong("size", 0)
|
|
209
|
-
putString("mimeType", "audio/wav")
|
|
210
|
-
|
|
211
|
-
// FIXED: Include compression info when compressed is enabled
|
|
212
|
-
if (compressedEnabled) {
|
|
213
|
-
val compressionBundle = Bundle().apply {
|
|
214
|
-
putString("compressedFileUri", "file:///storage/emulated/0/Android/data/test/files/recording.aac")
|
|
215
|
-
putString("format", compressedConfig?.getString("format") ?: "aac")
|
|
216
|
-
putInt("bitrate", compressedConfig?.getInt("bitrate") ?: 128000)
|
|
217
|
-
putLong("size", 40000)
|
|
218
|
-
putString("mimeType", "audio/aac")
|
|
219
|
-
}
|
|
220
|
-
putBundle("compression", compressionBundle)
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
// Normal behavior
|
|
224
|
-
putString("fileUri", "file:///storage/emulated/0/Android/data/test/files/recording.wav")
|
|
225
|
-
putString("filename", "recording.wav")
|
|
226
|
-
putLong("durationMs", 5000)
|
|
227
|
-
putLong("size", 240000)
|
|
228
|
-
putString("mimeType", "audio/wav")
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Simulates the current buggy behavior
|
|
235
|
-
*/
|
|
236
|
-
private fun simulateBuggyRecording(config: Bundle): Bundle {
|
|
237
|
-
val outputConfig = config.getBundle("output")
|
|
238
|
-
val primaryEnabled = outputConfig?.getBundle("primary")?.getBoolean("enabled", true) ?: true
|
|
239
|
-
|
|
240
|
-
return Bundle().apply {
|
|
241
|
-
if (!primaryEnabled) {
|
|
242
|
-
// Current buggy behavior
|
|
243
|
-
putString("fileUri", "")
|
|
244
|
-
putString("filename", "stream-only")
|
|
245
|
-
putLong("durationMs", 5000)
|
|
246
|
-
putLong("size", 0)
|
|
247
|
-
putString("mimeType", "audio/wav")
|
|
248
|
-
// BUG: compression is null even when compressed output is enabled
|
|
249
|
-
putBundle("compression", null)
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
package net.siteed.audiostudio.integration
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
5
|
-
import androidx.test.platform.app.InstrumentationRegistry
|
|
6
|
-
import net.siteed.audiostudio.OutputConfig
|
|
7
|
-
import net.siteed.audiostudio.RecordingConfig
|
|
8
|
-
import org.junit.After
|
|
9
|
-
import org.junit.Assert.*
|
|
10
|
-
import org.junit.Before
|
|
11
|
-
import org.junit.Test
|
|
12
|
-
import org.junit.runner.RunWith
|
|
13
|
-
import java.io.File
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Integration test for device disconnection fallback behavior.
|
|
17
|
-
* Tests various scenarios when audio devices are disconnected during recording.
|
|
18
|
-
*/
|
|
19
|
-
@RunWith(AndroidJUnit4::class)
|
|
20
|
-
class DeviceDisconnectionFallbackTest {
|
|
21
|
-
|
|
22
|
-
private lateinit var context: Context
|
|
23
|
-
private lateinit var testDir: File
|
|
24
|
-
|
|
25
|
-
@Before
|
|
26
|
-
fun setup() {
|
|
27
|
-
context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
28
|
-
|
|
29
|
-
// Create test directory
|
|
30
|
-
testDir = File(context.getExternalFilesDir(null), "fallback_test")
|
|
31
|
-
testDir.mkdirs()
|
|
32
|
-
|
|
33
|
-
// Clear any existing test files
|
|
34
|
-
testDir.listFiles()?.forEach { it.delete() }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@After
|
|
38
|
-
fun tearDown() {
|
|
39
|
-
// Clean up test files
|
|
40
|
-
testDir.listFiles()?.forEach { it.delete() }
|
|
41
|
-
testDir.delete()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@Test
|
|
45
|
-
fun testDeviceDisconnectionBehavior_Fallback() {
|
|
46
|
-
println("๐งช Test: Device Disconnection with Fallback Behavior")
|
|
47
|
-
println("--------------------------------------------------")
|
|
48
|
-
|
|
49
|
-
// Create recording config with fallback behavior
|
|
50
|
-
val config = RecordingConfig(
|
|
51
|
-
sampleRate = 44100,
|
|
52
|
-
channels = 1,
|
|
53
|
-
encoding = "pcm_16bit",
|
|
54
|
-
deviceDisconnectionBehavior = "fallback",
|
|
55
|
-
output = OutputConfig(
|
|
56
|
-
primary = OutputConfig.PrimaryOutput(
|
|
57
|
-
enabled = true,
|
|
58
|
-
format = "wav"
|
|
59
|
-
)
|
|
60
|
-
),
|
|
61
|
-
outputDirectory = testDir.absolutePath,
|
|
62
|
-
filename = "fallback_test.wav"
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
// Verify device disconnection behavior is set
|
|
66
|
-
assertEquals("Device disconnection behavior should be fallback",
|
|
67
|
-
"fallback", config.deviceDisconnectionBehavior)
|
|
68
|
-
|
|
69
|
-
println("โ
RecordingConfig correctly configured with fallback behavior")
|
|
70
|
-
|
|
71
|
-
// Note: Actual device disconnection simulation would require running the full
|
|
72
|
-
// AudioStudioModule with device connection/disconnection events
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@Test
|
|
76
|
-
fun testDeviceDisconnectionBehavior_Pause() {
|
|
77
|
-
println("๐งช Test: Device Disconnection with Pause Behavior")
|
|
78
|
-
println("-----------------------------------------------")
|
|
79
|
-
|
|
80
|
-
val config = RecordingConfig(
|
|
81
|
-
sampleRate = 44100,
|
|
82
|
-
channels = 1,
|
|
83
|
-
encoding = "pcm_16bit",
|
|
84
|
-
deviceDisconnectionBehavior = "pause",
|
|
85
|
-
output = OutputConfig(
|
|
86
|
-
primary = OutputConfig.PrimaryOutput(
|
|
87
|
-
enabled = true,
|
|
88
|
-
format = "wav"
|
|
89
|
-
)
|
|
90
|
-
),
|
|
91
|
-
outputDirectory = testDir.absolutePath,
|
|
92
|
-
filename = "pause_test.wav"
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
// Verify device disconnection behavior is set to pause
|
|
96
|
-
assertEquals("Device disconnection behavior should be pause",
|
|
97
|
-
"pause", config.deviceDisconnectionBehavior)
|
|
98
|
-
|
|
99
|
-
println("โ
RecordingConfig correctly configured with pause behavior")
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
@Test
|
|
103
|
-
fun testDefaultDeviceDisconnectionBehavior() {
|
|
104
|
-
println("๐งช Test: Default Device Disconnection Behavior")
|
|
105
|
-
println("--------------------------------------------")
|
|
106
|
-
|
|
107
|
-
// Create config without specifying deviceDisconnectionBehavior
|
|
108
|
-
val config = RecordingConfig(
|
|
109
|
-
sampleRate = 44100,
|
|
110
|
-
channels = 1,
|
|
111
|
-
encoding = "pcm_16bit",
|
|
112
|
-
output = OutputConfig(
|
|
113
|
-
primary = OutputConfig.PrimaryOutput(
|
|
114
|
-
enabled = true,
|
|
115
|
-
format = "wav"
|
|
116
|
-
)
|
|
117
|
-
),
|
|
118
|
-
outputDirectory = testDir.absolutePath,
|
|
119
|
-
filename = "default_test.wav"
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
// Default behavior should be null (handled as pause in implementation)
|
|
123
|
-
assertNull("Default device disconnection behavior should be null",
|
|
124
|
-
config.deviceDisconnectionBehavior)
|
|
125
|
-
|
|
126
|
-
println("โ
Default device disconnection behavior is null (treated as 'pause')")
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
@Test
|
|
130
|
-
fun testInterruptionEventReasons() {
|
|
131
|
-
println("๐งช Test: Interruption Event Reasons")
|
|
132
|
-
println("----------------------------------")
|
|
133
|
-
|
|
134
|
-
// Test that the expected event reasons are valid
|
|
135
|
-
val fallbackReasons = listOf("deviceFallback", "deviceSwitchFailed")
|
|
136
|
-
val pauseReasons = listOf("deviceDisconnected")
|
|
137
|
-
|
|
138
|
-
println("๐ Expected reasons for fallback behavior:")
|
|
139
|
-
fallbackReasons.forEach { reason ->
|
|
140
|
-
println(" - $reason")
|
|
141
|
-
assertNotNull("Reason should not be null", reason)
|
|
142
|
-
assertTrue("Reason should not be empty", reason.isNotEmpty())
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
println("๐ Expected reasons for pause behavior:")
|
|
146
|
-
pauseReasons.forEach { reason ->
|
|
147
|
-
println(" - $reason")
|
|
148
|
-
assertNotNull("Reason should not be null", reason)
|
|
149
|
-
assertTrue("Reason should not be empty", reason.isNotEmpty())
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
println("โ
All interruption event reasons are valid")
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
@Test
|
|
156
|
-
fun testAllDeviceDisconnectionBehaviors() {
|
|
157
|
-
println("๐งช Test: All Device Disconnection Behaviors")
|
|
158
|
-
println("-----------------------------------------")
|
|
159
|
-
|
|
160
|
-
val behaviors = listOf("fallback", "pause")
|
|
161
|
-
|
|
162
|
-
behaviors.forEach { behavior ->
|
|
163
|
-
val config = RecordingConfig(
|
|
164
|
-
sampleRate = 44100,
|
|
165
|
-
channels = 1,
|
|
166
|
-
encoding = "pcm_16bit",
|
|
167
|
-
deviceDisconnectionBehavior = behavior,
|
|
168
|
-
output = OutputConfig(
|
|
169
|
-
primary = OutputConfig.PrimaryOutput(
|
|
170
|
-
enabled = true,
|
|
171
|
-
format = "wav"
|
|
172
|
-
)
|
|
173
|
-
),
|
|
174
|
-
outputDirectory = testDir.absolutePath,
|
|
175
|
-
filename = "${behavior}_test.wav"
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
// Verify configuration
|
|
179
|
-
assertEquals("Device disconnection behavior should be $behavior",
|
|
180
|
-
behavior, config.deviceDisconnectionBehavior)
|
|
181
|
-
|
|
182
|
-
println("โ
Behavior '$behavior' correctly configured")
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
@Test
|
|
187
|
-
fun testDeviceDisconnectionWithCompressedOutput() {
|
|
188
|
-
println("๐งช Test: Device Disconnection with Compressed Output")
|
|
189
|
-
println("--------------------------------------------------")
|
|
190
|
-
|
|
191
|
-
// Test fallback behavior with compressed output enabled
|
|
192
|
-
val config = RecordingConfig(
|
|
193
|
-
sampleRate = 44100,
|
|
194
|
-
channels = 1,
|
|
195
|
-
encoding = "pcm_16bit",
|
|
196
|
-
deviceDisconnectionBehavior = "fallback",
|
|
197
|
-
output = OutputConfig(
|
|
198
|
-
primary = OutputConfig.PrimaryOutput(
|
|
199
|
-
enabled = false
|
|
200
|
-
),
|
|
201
|
-
compressed = OutputConfig.CompressedOutput(
|
|
202
|
-
enabled = true,
|
|
203
|
-
format = "aac",
|
|
204
|
-
bitrate = 128000
|
|
205
|
-
)
|
|
206
|
-
),
|
|
207
|
-
outputDirectory = testDir.absolutePath,
|
|
208
|
-
filename = "compressed_fallback_test"
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
assertEquals("Device disconnection behavior should be fallback",
|
|
212
|
-
"fallback", config.deviceDisconnectionBehavior)
|
|
213
|
-
assertTrue("Compressed output should be enabled", config.output.compressed.enabled)
|
|
214
|
-
assertFalse("Primary output should be disabled", config.output.primary.enabled)
|
|
215
|
-
|
|
216
|
-
println("โ
Fallback behavior configured with compressed-only output")
|
|
217
|
-
}
|
|
218
|
-
}
|
package/android/src/androidTest/java/net/siteed/audiostudio/integration/EventEmissionIntervalTest.kt
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
package net.siteed.audiostudio.integration
|
|
2
|
-
|
|
3
|
-
import android.os.Bundle
|
|
4
|
-
import android.util.Log
|
|
5
|
-
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
6
|
-
import androidx.test.platform.app.InstrumentationRegistry
|
|
7
|
-
import org.junit.Test
|
|
8
|
-
import org.junit.runner.RunWith
|
|
9
|
-
import org.junit.Assert.*
|
|
10
|
-
import java.io.File
|
|
11
|
-
import kotlin.math.abs
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Integration test to validate event emission interval enforcement.
|
|
15
|
-
*
|
|
16
|
-
* This test verifies that the configured intervals respect platform
|
|
17
|
-
* minimums to prevent excessive CPU usage.
|
|
18
|
-
*/
|
|
19
|
-
@RunWith(AndroidJUnit4::class)
|
|
20
|
-
class EventEmissionIntervalTest {
|
|
21
|
-
|
|
22
|
-
private val TAG = "EventEmissionIntervalTest"
|
|
23
|
-
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
24
|
-
|
|
25
|
-
@Test
|
|
26
|
-
fun testMinimumIntervalEnforcement() {
|
|
27
|
-
println("๐งช Test: Minimum Interval Enforcement")
|
|
28
|
-
println("------------------------------------")
|
|
29
|
-
|
|
30
|
-
// Test cases for different requested intervals
|
|
31
|
-
val testCases = listOf(
|
|
32
|
-
5 to 10, // 5ms should be clamped to MIN_INTERVAL (10ms)
|
|
33
|
-
10 to 10, // 10ms should remain 10ms
|
|
34
|
-
50 to 50, // 50ms should remain 50ms
|
|
35
|
-
100 to 100 // 100ms should remain 100ms
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
for ((requested, expected) in testCases) {
|
|
39
|
-
val config = Bundle().apply {
|
|
40
|
-
putInt("sampleRate", 48000)
|
|
41
|
-
putInt("channels", 1)
|
|
42
|
-
putLong("interval", requested.toLong())
|
|
43
|
-
putLong("intervalAnalysis", requested.toLong())
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Parse the config to validate interval enforcement
|
|
47
|
-
val configMap = bundleToMap(config)
|
|
48
|
-
val result = net.siteed.audiostudio.RecordingConfig.fromMap(configMap)
|
|
49
|
-
|
|
50
|
-
assertTrue("Config parsing should succeed", result.isSuccess)
|
|
51
|
-
val (recordingConfig, _) = result.getOrNull()!!
|
|
52
|
-
|
|
53
|
-
println("Requested interval: ${requested}ms")
|
|
54
|
-
println("Actual interval: ${recordingConfig.interval}ms")
|
|
55
|
-
println("Expected interval: ${expected}ms")
|
|
56
|
-
|
|
57
|
-
assertEquals(
|
|
58
|
-
"Interval should be clamped to minimum if below MIN_INTERVAL",
|
|
59
|
-
expected.toLong(),
|
|
60
|
-
recordingConfig.interval
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
assertEquals(
|
|
64
|
-
"Analysis interval should be clamped to minimum if below MIN_INTERVAL",
|
|
65
|
-
expected.toLong(),
|
|
66
|
-
recordingConfig.intervalAnalysis
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
println("โ Passed\n")
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@Test
|
|
74
|
-
fun testIntervalConsistencyAcrossPlatforms() {
|
|
75
|
-
println("๐งช Test: Platform Consistency Check")
|
|
76
|
-
println("----------------------------------")
|
|
77
|
-
|
|
78
|
-
// Document the expected behavior across platforms
|
|
79
|
-
println("Expected behavior after fix:")
|
|
80
|
-
println("- iOS: Minimum interval = 10ms")
|
|
81
|
-
println("- Android: Minimum interval = 10ms (enforced)")
|
|
82
|
-
println("")
|
|
83
|
-
|
|
84
|
-
// Verify Android enforces the minimum
|
|
85
|
-
val intervals = listOf(1, 5, 10, 50, 100)
|
|
86
|
-
for (interval in intervals) {
|
|
87
|
-
val config = Bundle().apply {
|
|
88
|
-
putLong("interval", interval.toLong())
|
|
89
|
-
putLong("intervalAnalysis", interval.toLong())
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
val configMap = bundleToMap(config)
|
|
93
|
-
val result = net.siteed.audiostudio.RecordingConfig.fromMap(configMap)
|
|
94
|
-
val (recordingConfig, _) = result.getOrNull()!!
|
|
95
|
-
|
|
96
|
-
val expectedInterval = maxOf(10, interval).toLong()
|
|
97
|
-
assertEquals(
|
|
98
|
-
"Android should enforce MIN_INTERVAL of 10ms",
|
|
99
|
-
expectedInterval,
|
|
100
|
-
recordingConfig.interval
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
println("โ Interval ${interval}ms -> ${recordingConfig.interval}ms")
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Helper to convert Bundle to Map for RecordingConfig
|
|
109
|
-
*/
|
|
110
|
-
private fun bundleToMap(bundle: Bundle): Map<String, Any?> {
|
|
111
|
-
val map = mutableMapOf<String, Any?>()
|
|
112
|
-
for (key in bundle.keySet()) {
|
|
113
|
-
when (val value = bundle.get(key)) {
|
|
114
|
-
is Bundle -> map[key] = bundleToMap(value)
|
|
115
|
-
else -> map[key] = value
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return map
|
|
119
|
-
}
|
|
120
|
-
}
|