@siteed/expo-audio-studio 2.8.6 → 2.10.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 (70) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/android/build.gradle +9 -0
  3. package/android/src/androidTest/assets/chorus.wav +0 -0
  4. package/android/src/androidTest/assets/jfk.wav +0 -0
  5. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  6. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  7. package/android/src/androidTest/java/net/siteed/audiostream/AudioProcessorInstrumentedTest.kt +197 -0
  8. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderInstrumentedTest.kt +541 -0
  9. package/android/src/androidTest/java/net/siteed/audiostream/integration/BufferDurationIntegrationTest.kt +324 -0
  10. package/android/src/androidTest/java/net/siteed/audiostream/integration/OutputControlIntegrationTest.kt +340 -0
  11. package/android/src/androidTest/java/net/siteed/audiostream/integration/README.md +95 -0
  12. package/android/src/androidTest/java/net/siteed/audiostream/integration/run_integration_tests.sh +28 -0
  13. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +264 -13
  14. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +3 -15
  15. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +118 -55
  16. package/android/src/main/java/net/siteed/audiostream/LogUtils.kt +32 -4
  17. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +50 -15
  18. package/android/src/test/java/net/siteed/audiostream/AudioFileHandlerTest.kt +279 -0
  19. package/android/src/test/java/net/siteed/audiostream/AudioFormatUtilsTest.kt +273 -0
  20. package/android/src/test/resources/chorus.wav +0 -0
  21. package/android/src/test/resources/generate_test_audio.py +94 -0
  22. package/android/src/test/resources/jfk.wav +0 -0
  23. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  24. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  25. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
  26. package/build/cjs/ExpoAudioStream.types.js.map +1 -1
  27. package/build/cjs/ExpoAudioStream.web.js +38 -35
  28. package/build/cjs/ExpoAudioStream.web.js.map +1 -1
  29. package/build/cjs/WebRecorder.web.js +122 -102
  30. package/build/cjs/WebRecorder.web.js.map +1 -1
  31. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
  32. package/build/esm/ExpoAudioStream.types.js.map +1 -1
  33. package/build/esm/ExpoAudioStream.web.js +38 -35
  34. package/build/esm/ExpoAudioStream.web.js.map +1 -1
  35. package/build/esm/WebRecorder.web.js +122 -102
  36. package/build/esm/WebRecorder.web.js.map +1 -1
  37. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +3 -1
  38. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -1
  39. package/build/types/ExpoAudioStream.types.d.ts +54 -22
  40. package/build/types/ExpoAudioStream.types.d.ts.map +1 -1
  41. package/build/types/ExpoAudioStream.web.d.ts.map +1 -1
  42. package/build/types/WebRecorder.web.d.ts +19 -3
  43. package/build/types/WebRecorder.web.d.ts.map +1 -1
  44. package/ios/AudioNotificationManager.swift +2 -6
  45. package/ios/AudioStreamManager.swift +116 -50
  46. package/ios/ExpoAudioStream.podspec +6 -0
  47. package/ios/ExpoAudioStreamModule.swift +11 -8
  48. package/ios/ExpoAudioStudioTests/AudioFileHandlerTests.swift +338 -0
  49. package/ios/ExpoAudioStudioTests/AudioFormatUtilsTests.swift +331 -0
  50. package/ios/ExpoAudioStudioTests/AudioTestHelpers.swift +130 -0
  51. package/ios/ExpoAudioStudioTests/Info.plist +22 -0
  52. package/ios/ExpoAudioStudioTests/SimpleAudioTest.swift +98 -0
  53. package/ios/ExpoAudioStudioTests/TestAudioGenerator.swift +75 -0
  54. package/ios/RecordingSettings.swift +53 -22
  55. package/ios/tests/integration/buffer_duration_test.swift +185 -0
  56. package/ios/tests/integration/output_control_test.swift +322 -0
  57. package/ios/tests/integration/run_integration_tests.sh +27 -0
  58. package/ios/tests/standalone/audio_processing_test.swift +144 -0
  59. package/ios/tests/standalone/audio_recording_test.swift +277 -0
  60. package/ios/tests/standalone/audio_streaming_test.swift +249 -0
  61. package/ios/tests/standalone/standalone_test.swift +144 -0
  62. package/package.json +140 -133
  63. package/src/AudioAnalysis/AudioAnalysis.types.ts +8 -1
  64. package/src/ExpoAudioStream.types.ts +66 -22
  65. package/src/ExpoAudioStream.web.ts +45 -39
  66. package/src/WebRecorder.web.ts +164 -130
  67. package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +0 -56
  68. package/ios/siteedexpoaudiostudio.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  69. package/ios/siteedexpoaudiostudio.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  70. /package/plugin/build/{index.d.ts → index.d.cts} +0 -0
package/CHANGELOG.md CHANGED
@@ -8,6 +8,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [2.10.0] - 2025-05-26
12
+ ### Changed
13
+ - chore(expo-audio-studio): update @siteed/design-system to version 0.51.0 and refactor recording configuration (#245) ([6486c66](https://github.com/deeeed/expo-audio-stream/commit/6486c66d687093b5257fddcce575d932b6a6443b))
14
+ - feat(expo-audio-studio): add buffer duration control and skip file writing options ([bfdbcb8](https://github.com/deeeed/expo-audio-stream/commit/bfdbcb8bac7c0641d6bacfa9b6fc4e64c2621baa))
15
+ - docs: enhance contribution guidelines with Test-Driven Development practices ([2c04eff](https://github.com/deeeed/expo-audio-stream/commit/2c04eff1f6d6d3c567aad8f7d7174b7f1ad533aa))
16
+ - feat(expo-audio-studio): enhance testing framework and add instrumented tests (#242) ([6e823ec](https://github.com/deeeed/expo-audio-stream/commit/6e823ec79c77ff34441b5acf757fdbac0a974e46))
17
+ ## [2.9.0] - 2025-05-15
18
+ ### Changed
19
+ - refactor(WebRecorder): remove unused compression logic and clean up blob creation ([91f6bba](https://github.com/deeeed/expo-audio-stream/commit/91f6bba6a3afa9fe71811c2c67a5703b8751830c))
20
+ - feat: Add Web Audio Test Page and Enhance web Audio Chunk Handling (#240) ([0a3cce0](https://github.com/deeeed/expo-audio-stream/commit/0a3cce0362ecaae70566805625e5a2cbdc289291))
21
+ - docs: update README and documentation to highlight experimental advanced audio feature extraction capabilities and performance considerations ([57fc4de](https://github.com/deeeed/expo-audio-stream/commit/57fc4de3967292b20b2b62190b7fba566f352ffd))
22
+ - feat: clean up redundant code and improve stability (#238) ([1b01256](https://github.com/deeeed/expo-audio-stream/commit/1b01256c2b89a1089e1df21f3faf2908816ca54b))
23
+ - chore(expo-audio-studio): release @siteed/expo-audio-studio@2.8.6 ([50a0fce](https://github.com/deeeed/expo-audio-stream/commit/50a0fce86524b9364329e111701bd056638d2849))
11
24
  ## [2.8.6] - 2025-05-11
12
25
  ### Changed
13
26
  - chore(expo-audio-studio): update Android module configuration and prevent plugin conflicts ([7f696fb](https://github.com/deeeed/expo-audio-stream/commit/7f696fb4277f1747d1ba753397d2d6a97ea19abc))
@@ -245,7 +258,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
245
258
  - Feature: Audio features extraction during recording.
246
259
  - Feature: Consistent WAV PCM recording format across all platforms.
247
260
 
248
- [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.8.6...HEAD
261
+ [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.10.0...HEAD
262
+ [2.10.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.9.0...@siteed/expo-audio-studio@2.10.0
263
+ [2.10.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.10.0...@siteed/expo-audio-studio@2.10.1
264
+ [2.9.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.8.6...@siteed/expo-audio-studio@2.9.0
249
265
  [2.8.6]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.8.5...@siteed/expo-audio-studio@2.8.6
250
266
  [2.8.5]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.8.4...@siteed/expo-audio-studio@2.8.5
251
267
  [2.8.4]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.8.3...@siteed/expo-audio-studio@2.8.4
@@ -77,6 +77,7 @@ android {
77
77
  targetSdkVersion safeExtGet("targetSdkVersion", 34)
78
78
  versionCode 1
79
79
  versionName "0.1.0"
80
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
80
81
  }
81
82
  lintOptions {
82
83
  abortOnError false
@@ -100,6 +101,14 @@ dependencies {
100
101
  testImplementation 'junit:junit:4.13.2'
101
102
  testImplementation 'org.jetbrains.kotlin:kotlin-test:1.8.10'
102
103
  testImplementation 'org.jetbrains.kotlin:kotlin-test-junit:1.8.10'
104
+
105
+ // Add Android instrumented test dependencies
106
+ androidTestImplementation 'androidx.test:runner:1.5.2'
107
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
108
+ androidTestImplementation 'androidx.test:core:1.5.0'
109
+ androidTestImplementation 'junit:junit:4.13.2'
110
+ androidTestImplementation 'androidx.test:rules:1.5.0'
111
+ androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
103
112
  }
104
113
 
105
114
  kotlin {
@@ -0,0 +1,197 @@
1
+ package net.siteed.audiostream
2
+
3
+ import androidx.test.ext.junit.runners.AndroidJUnit4
4
+ import androidx.test.platform.app.InstrumentationRegistry
5
+ import org.junit.Test
6
+ import org.junit.Assert.*
7
+ import org.junit.Before
8
+ import org.junit.After
9
+ import org.junit.runner.RunWith
10
+ import java.io.File
11
+ import kotlin.math.abs
12
+
13
+ /**
14
+ * Instrumented tests for AudioProcessor that require Android framework components.
15
+ * These tests run on an Android device/emulator and have access to MediaExtractor/MediaCodec.
16
+ */
17
+ @RunWith(AndroidJUnit4::class)
18
+ class AudioProcessorInstrumentedTest {
19
+ private lateinit var context: android.content.Context
20
+ private lateinit var audioProcessor: AudioProcessor
21
+ private lateinit var testFilesDir: File
22
+
23
+ @Before
24
+ fun setUp() {
25
+ context = InstrumentationRegistry.getInstrumentation().targetContext
26
+ testFilesDir = context.filesDir
27
+ audioProcessor = AudioProcessor(testFilesDir)
28
+
29
+ // Copy test assets to files directory
30
+ copyTestAssets()
31
+ }
32
+
33
+ @After
34
+ fun tearDown() {
35
+ // Clean up test files
36
+ testFilesDir.listFiles()?.forEach { file ->
37
+ if (file.name.endsWith(".wav")) {
38
+ file.delete()
39
+ }
40
+ }
41
+ }
42
+
43
+ private fun copyTestAssets() {
44
+ val assetManager = context.assets
45
+ val testFiles = listOf("jfk.wav", "chorus.wav", "recorder_hello_world.wav")
46
+
47
+ testFiles.forEach { fileName ->
48
+ try {
49
+ assetManager.open(fileName).use { input ->
50
+ File(testFilesDir, fileName).outputStream().use { output ->
51
+ input.copyTo(output)
52
+ }
53
+ }
54
+ } catch (e: Exception) {
55
+ // If asset doesn't exist, skip it
56
+ println("Warning: Test asset $fileName not found")
57
+ }
58
+ }
59
+ }
60
+
61
+ // ========== Audio Loading Tests ==========
62
+
63
+ @Test
64
+ fun testLoadAudioFromAnyFormat_loadsWavFile() {
65
+ // Given
66
+ val wavFile = File(testFilesDir, "jfk.wav")
67
+ assertTrue("Test file should exist", wavFile.exists())
68
+
69
+ // When
70
+ val audioData = audioProcessor.loadAudioFromAnyFormat(wavFile.absolutePath, null)
71
+
72
+ // Then
73
+ assertNotNull("Audio data should not be null", audioData)
74
+ assertEquals("Sample rate should be 16000", 16000, audioData!!.sampleRate)
75
+ assertEquals("Should be mono", 1, audioData.channels)
76
+ assertEquals("Should be 16-bit", 16, audioData.bitDepth)
77
+ assertTrue("Should have audio data", audioData.data.isNotEmpty())
78
+ assertTrue("Duration should be positive", audioData.durationMs > 0)
79
+ }
80
+
81
+ @Test
82
+ fun testLoadAudioFromAnyFormat_withDecodingConfig() {
83
+ // Given
84
+ val wavFile = File(testFilesDir, "jfk.wav")
85
+ val config = DecodingConfig(
86
+ targetSampleRate = 44100,
87
+ targetChannels = 2, // Test channel conversion - bug fixed
88
+ targetBitDepth = 16,
89
+ normalizeAudio = false
90
+ )
91
+
92
+ // When
93
+ val audioData = audioProcessor.loadAudioFromAnyFormat(wavFile.absolutePath, config)
94
+
95
+ // Then
96
+ assertNotNull("Audio data should not be null", audioData)
97
+ assertEquals("Sample rate should be converted to 44100", 44100, audioData!!.sampleRate)
98
+ assertEquals("Should be converted to stereo", 2, audioData.channels)
99
+ }
100
+
101
+ // ========== Audio Trimming Tests ==========
102
+
103
+ @Test
104
+ fun testTrimAudio_basicTrimming() {
105
+ // Given
106
+ val wavFile = File(testFilesDir, "jfk.wav")
107
+ val startTimeMs = 1000L
108
+ val endTimeMs = 3000L
109
+
110
+ // When
111
+ val trimmedAudio = audioProcessor.trimAudio(
112
+ fileUri = wavFile.absolutePath,
113
+ startTimeMs = startTimeMs,
114
+ endTimeMs = endTimeMs,
115
+ config = null,
116
+ outputFileName = "trimmed_jfk.wav"
117
+ )
118
+
119
+ // Then
120
+ assertNotNull("Trimmed audio should not be null", trimmedAudio)
121
+
122
+ // Verify the trimmed file was created
123
+ val trimmedFile = File(testFilesDir, "trimmed_jfk.wav")
124
+ assertTrue("Trimmed file should exist", trimmedFile.exists())
125
+ }
126
+
127
+ // ========== Mel Spectrogram Tests ==========
128
+
129
+ @Test
130
+ fun testExtractMelSpectrogram_basicGeneration() {
131
+ // Given
132
+ val wavFile = File(testFilesDir, "jfk.wav")
133
+ val audioData = audioProcessor.loadAudioFromAnyFormat(wavFile.absolutePath, null)
134
+ assertNotNull("Audio data should load", audioData)
135
+
136
+ // When
137
+ val melSpectrogram = audioProcessor.extractMelSpectrogram(
138
+ audioData = audioData!!,
139
+ windowSizeMs = 25f,
140
+ hopLengthMs = 10f,
141
+ nMels = 40,
142
+ fftLength = 512
143
+ )
144
+
145
+ // Then
146
+ assertNotNull("Mel spectrogram should not be null", melSpectrogram)
147
+ assertTrue("Should have time frames", melSpectrogram.spectrogram.isNotEmpty())
148
+ assertEquals("Should have 40 mel bins", 40, melSpectrogram.spectrogram[0].size)
149
+
150
+ // Verify timestamps
151
+ assertTrue("Should have timestamps", melSpectrogram.timeStamps.isNotEmpty())
152
+ assertEquals("Timestamps should match frames",
153
+ melSpectrogram.spectrogram.size, melSpectrogram.timeStamps.size)
154
+
155
+ // Verify frequencies
156
+ assertEquals("Should have 40 frequency bins", 40, melSpectrogram.frequencies.size)
157
+ assertTrue("Frequencies should be ascending",
158
+ melSpectrogram.frequencies.zip(melSpectrogram.frequencies.drop(1))
159
+ .all { (a, b) -> a < b })
160
+ }
161
+
162
+ // ========== Preview Generation Tests ==========
163
+
164
+ @Test
165
+ fun testGeneratePreview_basicPreview() {
166
+ // Given
167
+ val wavFile = File(testFilesDir, "chorus.wav")
168
+ val audioData = audioProcessor.loadAudioFromAnyFormat(wavFile.absolutePath, null)
169
+ assertNotNull("Audio data should load", audioData)
170
+
171
+ val config = RecordingConfig(
172
+ sampleRate = audioData!!.sampleRate,
173
+ channels = audioData.channels,
174
+ encoding = "pcm_16bit",
175
+ segmentDurationMs = 20 // 50 points per second
176
+ )
177
+
178
+ // When
179
+ val preview = audioProcessor.generatePreview(
180
+ audioData = audioData,
181
+ numberOfPoints = 100,
182
+ startTimeMs = null,
183
+ endTimeMs = null,
184
+ config = config
185
+ )
186
+
187
+ // Then
188
+ assertNotNull("Preview should not be null", preview)
189
+ assertEquals("Should have 100 data points", 100, preview.dataPoints.size)
190
+
191
+ // Verify amplitude range
192
+ assertTrue("Min amplitude should be reasonable", preview.amplitudeRange.min >= 0)
193
+ assertTrue("Max amplitude should be reasonable", preview.amplitudeRange.max <= 1)
194
+ assertTrue("Max should be greater than min",
195
+ preview.amplitudeRange.max > preview.amplitudeRange.min)
196
+ }
197
+ }