@siteed/audio-studio 3.0.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 (375) hide show
  1. package/CHANGELOG.md +535 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/android/build.gradle +143 -0
  5. package/android/src/androidTest/assets/chorus.wav +0 -0
  6. package/android/src/androidTest/assets/jfk.wav +0 -0
  7. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  8. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  9. package/android/src/androidTest/java/net/siteed/audiostudio/AudioProcessorInstrumentedTest.kt +197 -0
  10. package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderInstrumentedTest.kt +541 -0
  11. package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderPerformanceInstrumentedTest.kt +234 -0
  12. package/android/src/androidTest/java/net/siteed/audiostudio/integration/AudioFocusStrategyIntegrationTest.kt +332 -0
  13. package/android/src/androidTest/java/net/siteed/audiostudio/integration/BufferDurationIntegrationTest.kt +324 -0
  14. package/android/src/androidTest/java/net/siteed/audiostudio/integration/CompressedOnlyOutputTest.kt +253 -0
  15. package/android/src/androidTest/java/net/siteed/audiostudio/integration/DeviceDisconnectionFallbackTest.kt +218 -0
  16. package/android/src/androidTest/java/net/siteed/audiostudio/integration/EventEmissionIntervalTest.kt +120 -0
  17. package/android/src/androidTest/java/net/siteed/audiostudio/integration/M4aFormatTest.kt +345 -0
  18. package/android/src/androidTest/java/net/siteed/audiostudio/integration/OutputControlIntegrationTest.kt +340 -0
  19. package/android/src/androidTest/java/net/siteed/audiostudio/integration/PcmStreamingDurationTest.kt +252 -0
  20. package/android/src/androidTest/java/net/siteed/audiostudio/integration/README.md +95 -0
  21. package/android/src/androidTest/java/net/siteed/audiostudio/integration/run_integration_tests.sh +43 -0
  22. package/android/src/main/AndroidManifest.xml +30 -0
  23. package/android/src/main/CMakeLists.txt +29 -0
  24. package/android/src/main/java/net/siteed/audiostudio/AudioAnalysisData.kt +188 -0
  25. package/android/src/main/java/net/siteed/audiostudio/AudioDataEncoder.kt +9 -0
  26. package/android/src/main/java/net/siteed/audiostudio/AudioDeviceManager.kt +1741 -0
  27. package/android/src/main/java/net/siteed/audiostudio/AudioFeaturesNative.kt +26 -0
  28. package/android/src/main/java/net/siteed/audiostudio/AudioFileHandler.kt +136 -0
  29. package/android/src/main/java/net/siteed/audiostudio/AudioFormatUtils.kt +354 -0
  30. package/android/src/main/java/net/siteed/audiostudio/AudioNotificationsManager.kt +439 -0
  31. package/android/src/main/java/net/siteed/audiostudio/AudioProcessor.kt +2237 -0
  32. package/android/src/main/java/net/siteed/audiostudio/AudioRecorderManager.kt +2163 -0
  33. package/android/src/main/java/net/siteed/audiostudio/AudioRecordingService.kt +167 -0
  34. package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +1112 -0
  35. package/android/src/main/java/net/siteed/audiostudio/AudioTrimmer.kt +1099 -0
  36. package/android/src/main/java/net/siteed/audiostudio/Constants.kt +37 -0
  37. package/android/src/main/java/net/siteed/audiostudio/EventSender.kt +7 -0
  38. package/android/src/main/java/net/siteed/audiostudio/FFT.kt +100 -0
  39. package/android/src/main/java/net/siteed/audiostudio/Features.kt +98 -0
  40. package/android/src/main/java/net/siteed/audiostudio/LogUtils.kt +93 -0
  41. package/android/src/main/java/net/siteed/audiostudio/MelSpectrogramNative.kt +36 -0
  42. package/android/src/main/java/net/siteed/audiostudio/NotificationConfig.kt +72 -0
  43. package/android/src/main/java/net/siteed/audiostudio/PermissionUtils.kt +68 -0
  44. package/android/src/main/java/net/siteed/audiostudio/RecordingActionReceiver.kt +59 -0
  45. package/android/src/main/java/net/siteed/audiostudio/RecordingConfig.kt +259 -0
  46. package/android/src/main/java/net/siteed/audiostudio/WaveformConfig.kt +19 -0
  47. package/android/src/main/java/net/siteed/audiostudio/WaveformRenderer.kt +159 -0
  48. package/android/src/main/jni/AudioFeaturesJNI.cpp +152 -0
  49. package/android/src/main/jni/MelSpectrogramJNI.cpp +165 -0
  50. package/android/src/main/res/drawable/ic_default_action_icon.xml +16 -0
  51. package/android/src/main/res/drawable/ic_microphone.xml +13 -0
  52. package/android/src/main/res/drawable/ic_pause.xml +10 -0
  53. package/android/src/main/res/drawable/ic_play.xml +10 -0
  54. package/android/src/main/res/drawable/ic_stop.xml +10 -0
  55. package/android/src/main/res/layout/notification_recording.xml +37 -0
  56. package/android/src/test/java/net/siteed/audiostudio/AudioFileHandlerTest.kt +279 -0
  57. package/android/src/test/java/net/siteed/audiostudio/AudioFocusStrategyTest.kt +249 -0
  58. package/android/src/test/java/net/siteed/audiostudio/AudioFormatTest.kt +151 -0
  59. package/android/src/test/java/net/siteed/audiostudio/AudioFormatUtilsTest.kt +273 -0
  60. package/android/src/test/java/net/siteed/audiostudio/DeviceDisconnectionFallbackUnitTest.kt +140 -0
  61. package/android/src/test/resources/chorus.wav +0 -0
  62. package/android/src/test/resources/generate_test_audio.py +94 -0
  63. package/android/src/test/resources/jfk.wav +0 -0
  64. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  65. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  66. package/app.plugin.js +3 -0
  67. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +4 -0
  68. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  69. package/build/cjs/AudioAnalysis/audioFeaturesWasm.js +164 -0
  70. package/build/cjs/AudioAnalysis/audioFeaturesWasm.js.map +1 -0
  71. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +213 -0
  72. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  73. package/build/cjs/AudioAnalysis/extractAudioData.js +21 -0
  74. package/build/cjs/AudioAnalysis/extractAudioData.js.map +1 -0
  75. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +90 -0
  76. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  77. package/build/cjs/AudioAnalysis/extractPreview.js +28 -0
  78. package/build/cjs/AudioAnalysis/extractPreview.js.map +1 -0
  79. package/build/cjs/AudioAnalysis/extractWaveform.js +18 -0
  80. package/build/cjs/AudioAnalysis/extractWaveform.js.map +1 -0
  81. package/build/cjs/AudioAnalysis/melSpectrogramWasm.js +149 -0
  82. package/build/cjs/AudioAnalysis/melSpectrogramWasm.js.map +1 -0
  83. package/build/cjs/AudioDeviceManager.js +688 -0
  84. package/build/cjs/AudioDeviceManager.js.map +1 -0
  85. package/build/cjs/AudioRecorder.provider.js +78 -0
  86. package/build/cjs/AudioRecorder.provider.js.map +1 -0
  87. package/build/cjs/AudioStudio.native.js +8 -0
  88. package/build/cjs/AudioStudio.native.js.map +1 -0
  89. package/build/cjs/AudioStudio.types.js +11 -0
  90. package/build/cjs/AudioStudio.types.js.map +1 -0
  91. package/build/cjs/AudioStudio.web.js +708 -0
  92. package/build/cjs/AudioStudio.web.js.map +1 -0
  93. package/build/cjs/AudioStudioModule.js +718 -0
  94. package/build/cjs/AudioStudioModule.js.map +1 -0
  95. package/build/cjs/WebRecorder.web.js +865 -0
  96. package/build/cjs/WebRecorder.web.js.map +1 -0
  97. package/build/cjs/constants/platformLimitations.js +99 -0
  98. package/build/cjs/constants/platformLimitations.js.map +1 -0
  99. package/build/cjs/constants.js +20 -0
  100. package/build/cjs/constants.js.map +1 -0
  101. package/build/cjs/events.js +29 -0
  102. package/build/cjs/events.js.map +1 -0
  103. package/build/cjs/hooks/useAudioDevices.js +179 -0
  104. package/build/cjs/hooks/useAudioDevices.js.map +1 -0
  105. package/build/cjs/index.js +64 -0
  106. package/build/cjs/index.js.map +1 -0
  107. package/build/cjs/trimAudio.js +76 -0
  108. package/build/cjs/trimAudio.js.map +1 -0
  109. package/build/cjs/useAudioRecorder.js +535 -0
  110. package/build/cjs/useAudioRecorder.js.map +1 -0
  111. package/build/cjs/utils/BlobFix.js +502 -0
  112. package/build/cjs/utils/BlobFix.js.map +1 -0
  113. package/build/cjs/utils/audioProcessing.js +136 -0
  114. package/build/cjs/utils/audioProcessing.js.map +1 -0
  115. package/build/cjs/utils/cleanNativeOptions.js +22 -0
  116. package/build/cjs/utils/cleanNativeOptions.js.map +1 -0
  117. package/build/cjs/utils/concatenateBuffers.js +25 -0
  118. package/build/cjs/utils/concatenateBuffers.js.map +1 -0
  119. package/build/cjs/utils/convertPCMToFloat32.js +124 -0
  120. package/build/cjs/utils/convertPCMToFloat32.js.map +1 -0
  121. package/build/cjs/utils/crc32.js +52 -0
  122. package/build/cjs/utils/crc32.js.map +1 -0
  123. package/build/cjs/utils/encodingToBitDepth.js +17 -0
  124. package/build/cjs/utils/encodingToBitDepth.js.map +1 -0
  125. package/build/cjs/utils/getWavFileInfo.js +96 -0
  126. package/build/cjs/utils/getWavFileInfo.js.map +1 -0
  127. package/build/cjs/utils/writeWavHeader.js +88 -0
  128. package/build/cjs/utils/writeWavHeader.js.map +1 -0
  129. package/build/cjs/workers/InlineFeaturesExtractor.web.js +294 -0
  130. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +1 -0
  131. package/build/cjs/workers/inlineAudioWebWorker.web.js +190 -0
  132. package/build/cjs/workers/inlineAudioWebWorker.web.js.map +1 -0
  133. package/build/cjs/workers/wasmGlueString.web.js +27 -0
  134. package/build/cjs/workers/wasmGlueString.web.js.map +1 -0
  135. package/build/esm/AudioAnalysis/AudioAnalysis.types.js +3 -0
  136. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  137. package/build/esm/AudioAnalysis/audioFeaturesWasm.js +126 -0
  138. package/build/esm/AudioAnalysis/audioFeaturesWasm.js.map +1 -0
  139. package/build/esm/AudioAnalysis/extractAudioAnalysis.js +205 -0
  140. package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  141. package/build/esm/AudioAnalysis/extractAudioData.js +14 -0
  142. package/build/esm/AudioAnalysis/extractAudioData.js.map +1 -0
  143. package/build/esm/AudioAnalysis/extractMelSpectrogram.js +86 -0
  144. package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  145. package/build/esm/AudioAnalysis/extractPreview.js +25 -0
  146. package/build/esm/AudioAnalysis/extractPreview.js.map +1 -0
  147. package/build/esm/AudioAnalysis/extractWaveform.js +11 -0
  148. package/build/esm/AudioAnalysis/extractWaveform.js.map +1 -0
  149. package/build/esm/AudioAnalysis/melSpectrogramWasm.js +111 -0
  150. package/build/esm/AudioAnalysis/melSpectrogramWasm.js.map +1 -0
  151. package/build/esm/AudioDeviceManager.js +681 -0
  152. package/build/esm/AudioDeviceManager.js.map +1 -0
  153. package/build/esm/AudioRecorder.provider.js +40 -0
  154. package/build/esm/AudioRecorder.provider.js.map +1 -0
  155. package/build/esm/AudioStudio.native.js +6 -0
  156. package/build/esm/AudioStudio.native.js.map +1 -0
  157. package/build/esm/AudioStudio.types.js +8 -0
  158. package/build/esm/AudioStudio.types.js.map +1 -0
  159. package/build/esm/AudioStudio.web.js +704 -0
  160. package/build/esm/AudioStudio.web.js.map +1 -0
  161. package/build/esm/AudioStudioModule.js +713 -0
  162. package/build/esm/AudioStudioModule.js.map +1 -0
  163. package/build/esm/WebRecorder.web.js +861 -0
  164. package/build/esm/WebRecorder.web.js.map +1 -0
  165. package/build/esm/constants/platformLimitations.js +90 -0
  166. package/build/esm/constants/platformLimitations.js.map +1 -0
  167. package/build/esm/constants.js +17 -0
  168. package/build/esm/constants.js.map +1 -0
  169. package/build/esm/events.js +21 -0
  170. package/build/esm/events.js.map +1 -0
  171. package/build/esm/hooks/useAudioDevices.js +176 -0
  172. package/build/esm/hooks/useAudioDevices.js.map +1 -0
  173. package/build/esm/index.js +23 -0
  174. package/build/esm/index.js.map +1 -0
  175. package/build/esm/trimAudio.js +69 -0
  176. package/build/esm/trimAudio.js.map +1 -0
  177. package/build/esm/useAudioRecorder.js +529 -0
  178. package/build/esm/useAudioRecorder.js.map +1 -0
  179. package/build/esm/utils/BlobFix.js +498 -0
  180. package/build/esm/utils/BlobFix.js.map +1 -0
  181. package/build/esm/utils/audioProcessing.js +133 -0
  182. package/build/esm/utils/audioProcessing.js.map +1 -0
  183. package/build/esm/utils/cleanNativeOptions.js +19 -0
  184. package/build/esm/utils/cleanNativeOptions.js.map +1 -0
  185. package/build/esm/utils/concatenateBuffers.js +21 -0
  186. package/build/esm/utils/concatenateBuffers.js.map +1 -0
  187. package/build/esm/utils/convertPCMToFloat32.js +120 -0
  188. package/build/esm/utils/convertPCMToFloat32.js.map +1 -0
  189. package/build/esm/utils/crc32.js +50 -0
  190. package/build/esm/utils/crc32.js.map +1 -0
  191. package/build/esm/utils/encodingToBitDepth.js +13 -0
  192. package/build/esm/utils/encodingToBitDepth.js.map +1 -0
  193. package/build/esm/utils/getWavFileInfo.js +92 -0
  194. package/build/esm/utils/getWavFileInfo.js.map +1 -0
  195. package/build/esm/utils/writeWavHeader.js +84 -0
  196. package/build/esm/utils/writeWavHeader.js.map +1 -0
  197. package/build/esm/workers/InlineFeaturesExtractor.web.js +291 -0
  198. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +1 -0
  199. package/build/esm/workers/inlineAudioWebWorker.web.js +187 -0
  200. package/build/esm/workers/inlineAudioWebWorker.web.js.map +1 -0
  201. package/build/esm/workers/wasmGlueString.web.js +24 -0
  202. package/build/esm/workers/wasmGlueString.web.js.map +1 -0
  203. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +198 -0
  204. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  205. package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts +24 -0
  206. package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts.map +1 -0
  207. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts +74 -0
  208. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  209. package/build/types/AudioAnalysis/extractAudioData.d.ts +3 -0
  210. package/build/types/AudioAnalysis/extractAudioData.d.ts.map +1 -0
  211. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts +20 -0
  212. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +1 -0
  213. package/build/types/AudioAnalysis/extractPreview.d.ts +11 -0
  214. package/build/types/AudioAnalysis/extractPreview.d.ts.map +1 -0
  215. package/build/types/AudioAnalysis/extractWaveform.d.ts +8 -0
  216. package/build/types/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  217. package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts +16 -0
  218. package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts.map +1 -0
  219. package/build/types/AudioDeviceManager.d.ts +187 -0
  220. package/build/types/AudioDeviceManager.d.ts.map +1 -0
  221. package/build/types/AudioRecorder.provider.d.ts +11 -0
  222. package/build/types/AudioRecorder.provider.d.ts.map +1 -0
  223. package/build/types/AudioStudio.native.d.ts +3 -0
  224. package/build/types/AudioStudio.native.d.ts.map +1 -0
  225. package/build/types/AudioStudio.types.d.ts +760 -0
  226. package/build/types/AudioStudio.types.d.ts.map +1 -0
  227. package/build/types/AudioStudio.web.d.ts +96 -0
  228. package/build/types/AudioStudio.web.d.ts.map +1 -0
  229. package/build/types/AudioStudioModule.d.ts +3 -0
  230. package/build/types/AudioStudioModule.d.ts.map +1 -0
  231. package/build/types/WebRecorder.web.d.ts +208 -0
  232. package/build/types/WebRecorder.web.d.ts.map +1 -0
  233. package/build/types/constants/platformLimitations.d.ts +40 -0
  234. package/build/types/constants/platformLimitations.d.ts.map +1 -0
  235. package/build/types/constants.d.ts +14 -0
  236. package/build/types/constants.d.ts.map +1 -0
  237. package/build/types/events.d.ts +29 -0
  238. package/build/types/events.d.ts.map +1 -0
  239. package/build/types/hooks/useAudioDevices.d.ts +15 -0
  240. package/build/types/hooks/useAudioDevices.d.ts.map +1 -0
  241. package/build/types/index.d.ts +21 -0
  242. package/build/types/index.d.ts.map +1 -0
  243. package/build/types/trimAudio.d.ts +25 -0
  244. package/build/types/trimAudio.d.ts.map +1 -0
  245. package/build/types/useAudioRecorder.d.ts +22 -0
  246. package/build/types/useAudioRecorder.d.ts.map +1 -0
  247. package/build/types/utils/BlobFix.d.ts +9 -0
  248. package/build/types/utils/BlobFix.d.ts.map +1 -0
  249. package/build/types/utils/audioProcessing.d.ts +24 -0
  250. package/build/types/utils/audioProcessing.d.ts.map +1 -0
  251. package/build/types/utils/cleanNativeOptions.d.ts +15 -0
  252. package/build/types/utils/cleanNativeOptions.d.ts.map +1 -0
  253. package/build/types/utils/concatenateBuffers.d.ts +8 -0
  254. package/build/types/utils/concatenateBuffers.d.ts.map +1 -0
  255. package/build/types/utils/convertPCMToFloat32.d.ts +13 -0
  256. package/build/types/utils/convertPCMToFloat32.d.ts.map +1 -0
  257. package/build/types/utils/crc32.d.ts +7 -0
  258. package/build/types/utils/crc32.d.ts.map +1 -0
  259. package/build/types/utils/encodingToBitDepth.d.ts +5 -0
  260. package/build/types/utils/encodingToBitDepth.d.ts.map +1 -0
  261. package/build/types/utils/getWavFileInfo.d.ts +26 -0
  262. package/build/types/utils/getWavFileInfo.d.ts.map +1 -0
  263. package/build/types/utils/writeWavHeader.d.ts +34 -0
  264. package/build/types/utils/writeWavHeader.d.ts.map +1 -0
  265. package/build/types/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  266. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  267. package/build/types/workers/inlineAudioWebWorker.web.d.ts +2 -0
  268. package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  269. package/build/types/workers/wasmGlueString.web.d.ts +2 -0
  270. package/build/types/workers/wasmGlueString.web.d.ts.map +1 -0
  271. package/cpp/AudioFeatures.cpp +274 -0
  272. package/cpp/AudioFeatures.h +85 -0
  273. package/cpp/AudioFeaturesBridge.cpp +146 -0
  274. package/cpp/AudioFeaturesBridge.h +47 -0
  275. package/cpp/MelSpectrogram.cpp +227 -0
  276. package/cpp/MelSpectrogram.h +82 -0
  277. package/cpp/MelSpectrogramBridge.cpp +112 -0
  278. package/cpp/MelSpectrogramBridge.h +33 -0
  279. package/cpp/kiss_fft/COPYING +11 -0
  280. package/cpp/kiss_fft/_kiss_fft_guts.h +167 -0
  281. package/cpp/kiss_fft/kiss_fft.c +424 -0
  282. package/cpp/kiss_fft/kiss_fft.h +160 -0
  283. package/cpp/kiss_fft/kiss_fft_log.h +36 -0
  284. package/cpp/kiss_fft/kiss_fftr.c +155 -0
  285. package/cpp/kiss_fft/kiss_fftr.h +54 -0
  286. package/expo-module.config.json +10 -0
  287. package/ios/AudioAnalysisData.swift +74 -0
  288. package/ios/AudioDeviceManager.swift +670 -0
  289. package/ios/AudioFeaturesWrapper.h +21 -0
  290. package/ios/AudioFeaturesWrapper.mm +63 -0
  291. package/ios/AudioNotificationManager.swift +154 -0
  292. package/ios/AudioProcessingHelpers.swift +797 -0
  293. package/ios/AudioProcessor.swift +1191 -0
  294. package/ios/AudioStreamError.swift +7 -0
  295. package/ios/AudioStreamManager.swift +2369 -0
  296. package/ios/AudioStreamManagerDelegate.swift +16 -0
  297. package/ios/AudioStudio.podspec +39 -0
  298. package/ios/AudioStudioModule.swift +1111 -0
  299. package/ios/AudioStudioTests/AudioFileHandlerTests.swift +338 -0
  300. package/ios/AudioStudioTests/AudioFormatUtilsTests.swift +331 -0
  301. package/ios/AudioStudioTests/AudioTestHelpers.swift +130 -0
  302. package/ios/AudioStudioTests/CompressedOnlyOutputTests.swift +294 -0
  303. package/ios/AudioStudioTests/EventEmissionIntervalTests.swift +105 -0
  304. package/ios/AudioStudioTests/Info.plist +22 -0
  305. package/ios/AudioStudioTests/README.md +39 -0
  306. package/ios/AudioStudioTests/SimpleAudioTest.swift +98 -0
  307. package/ios/AudioStudioTests/TestAudioGenerator.swift +75 -0
  308. package/ios/DataPoint.swift +54 -0
  309. package/ios/DecodingConfig.swift +59 -0
  310. package/ios/FFT.swift +62 -0
  311. package/ios/Features.swift +95 -0
  312. package/ios/ISSUE_IOS.md +68 -0
  313. package/ios/Logger.swift +39 -0
  314. package/ios/MelSpectrogramWrapper.h +30 -0
  315. package/ios/MelSpectrogramWrapper.mm +97 -0
  316. package/ios/NotificationExtension.swift +15 -0
  317. package/ios/RecordingResult.swift +22 -0
  318. package/ios/RecordingSettings.swift +311 -0
  319. package/ios/WaveformExtractor.swift +105 -0
  320. package/ios/tests/README.md +41 -0
  321. package/ios/tests/integration/buffer_and_fallback_test.swift +178 -0
  322. package/ios/tests/integration/buffer_duration_test.swift +185 -0
  323. package/ios/tests/integration/compressed_only_output_test.swift +271 -0
  324. package/ios/tests/integration/output_control_test.swift +322 -0
  325. package/ios/tests/integration/run_integration_tests.sh +37 -0
  326. package/ios/tests/opus_support_test_macos.swift +154 -0
  327. package/ios/tests/standalone/audio_processing_test.swift +144 -0
  328. package/ios/tests/standalone/audio_recording_test.swift +277 -0
  329. package/ios/tests/standalone/audio_streaming_test.swift +249 -0
  330. package/ios/tests/standalone/standalone_test.swift +144 -0
  331. package/package.json +146 -0
  332. package/plugin/build/index.cjs +194 -0
  333. package/plugin/build/index.d.cts +22 -0
  334. package/plugin/build/index.js +194 -0
  335. package/plugin/src/index.ts +285 -0
  336. package/plugin/tsconfig.json +10 -0
  337. package/plugin/tsconfig.tsbuildinfo +1 -0
  338. package/prebuilt/wasm/mel-spectrogram.js +18 -0
  339. package/src/AudioAnalysis/AudioAnalysis.types.ts +226 -0
  340. package/src/AudioAnalysis/audio-features-wasm.d.ts +37 -0
  341. package/src/AudioAnalysis/audioFeaturesWasm.ts +200 -0
  342. package/src/AudioAnalysis/extractAudioAnalysis.ts +350 -0
  343. package/src/AudioAnalysis/extractAudioData.ts +17 -0
  344. package/src/AudioAnalysis/extractMelSpectrogram.ts +140 -0
  345. package/src/AudioAnalysis/extractPreview.ts +34 -0
  346. package/src/AudioAnalysis/extractWaveform.ts +22 -0
  347. package/src/AudioAnalysis/mel-spectrogram-wasm.d.ts +48 -0
  348. package/src/AudioAnalysis/melSpectrogramWasm.ts +179 -0
  349. package/src/AudioDeviceManager.ts +800 -0
  350. package/src/AudioRecorder.provider.tsx +57 -0
  351. package/src/AudioStudio.native.ts +6 -0
  352. package/src/AudioStudio.types.ts +899 -0
  353. package/src/AudioStudio.web.ts +911 -0
  354. package/src/AudioStudioModule.ts +984 -0
  355. package/src/WebRecorder.web.ts +1114 -0
  356. package/src/constants/platformLimitations.ts +118 -0
  357. package/src/constants.ts +21 -0
  358. package/src/events.ts +63 -0
  359. package/src/hooks/useAudioDevices.ts +213 -0
  360. package/src/index.ts +67 -0
  361. package/src/trimAudio.ts +94 -0
  362. package/src/types/crc-32.d.ts +9 -0
  363. package/src/useAudioRecorder.tsx +784 -0
  364. package/src/utils/BlobFix.ts +561 -0
  365. package/src/utils/audioProcessing.ts +205 -0
  366. package/src/utils/cleanNativeOptions.ts +18 -0
  367. package/src/utils/concatenateBuffers.ts +24 -0
  368. package/src/utils/convertPCMToFloat32.ts +170 -0
  369. package/src/utils/crc32.ts +59 -0
  370. package/src/utils/encodingToBitDepth.ts +18 -0
  371. package/src/utils/getWavFileInfo.ts +132 -0
  372. package/src/utils/writeWavHeader.ts +115 -0
  373. package/src/workers/InlineFeaturesExtractor.web.tsx +291 -0
  374. package/src/workers/inlineAudioWebWorker.web.tsx +186 -0
  375. package/src/workers/wasmGlueString.web.ts +23 -0
@@ -0,0 +1,234 @@
1
+ package net.siteed.audiostudio
2
+
3
+ import android.Manifest
4
+ import android.content.Context
5
+ import android.util.Log
6
+ import androidx.test.ext.junit.runners.AndroidJUnit4
7
+ import androidx.test.platform.app.InstrumentationRegistry
8
+ import androidx.test.rule.GrantPermissionRule
9
+ import expo.modules.kotlin.Promise
10
+ import org.junit.After
11
+ import org.junit.Assert.*
12
+ import org.junit.Before
13
+ import org.junit.Rule
14
+ import org.junit.Test
15
+ import org.junit.runner.RunWith
16
+ import java.io.File
17
+ import java.util.concurrent.CountDownLatch
18
+ import java.util.concurrent.TimeUnit
19
+ import kotlin.system.measureTimeMillis
20
+
21
+ /**
22
+ * Performance tests for measuring stop recording times.
23
+ */
24
+ @RunWith(AndroidJUnit4::class)
25
+ class AudioRecorderPerformanceInstrumentedTest {
26
+
27
+ @get:Rule
28
+ val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
29
+ Manifest.permission.RECORD_AUDIO
30
+ )
31
+
32
+ private lateinit var context: Context
33
+ private lateinit var filesDir: File
34
+ private lateinit var audioRecorderManager: AudioRecorderManager
35
+ private lateinit var testEventSender: TestEventSender
36
+ private lateinit var permissionUtils: PermissionUtils
37
+ private lateinit var audioDataEncoder: AudioDataEncoder
38
+
39
+ companion object {
40
+ private const val TAG = "PerformanceTest"
41
+ }
42
+
43
+ // Test event sender to capture events
44
+ private class TestEventSender : EventSender {
45
+ override fun sendExpoEvent(eventName: String, params: android.os.Bundle) {
46
+ // No-op for performance tests
47
+ }
48
+ }
49
+
50
+ @Before
51
+ fun setUp() {
52
+ context = InstrumentationRegistry.getInstrumentation().targetContext
53
+ filesDir = context.filesDir
54
+ testEventSender = TestEventSender()
55
+ permissionUtils = PermissionUtils(context)
56
+ audioDataEncoder = AudioDataEncoder()
57
+
58
+ // Initialize AudioRecorderManager
59
+ audioRecorderManager = AudioRecorderManager.initialize(
60
+ context = context,
61
+ filesDir = filesDir,
62
+ permissionUtils = permissionUtils,
63
+ audioDataEncoder = audioDataEncoder,
64
+ eventSender = testEventSender,
65
+ enablePhoneStateHandling = false,
66
+ enableBackgroundAudio = false
67
+ )
68
+
69
+ // Clean up any existing audio files
70
+ cleanupAudioFiles()
71
+ }
72
+
73
+ @After
74
+ fun tearDown() {
75
+ // Stop any ongoing recording
76
+ if (audioRecorderManager.isRecording) {
77
+ val promise = object : Promise {
78
+ override fun resolve(value: Any?) {}
79
+ override fun reject(code: String, message: String?, cause: Throwable?) {}
80
+ }
81
+ audioRecorderManager.stopRecording(promise)
82
+ }
83
+
84
+ // Clean up
85
+ AudioRecorderManager.destroy()
86
+ cleanupAudioFiles()
87
+ }
88
+
89
+ private fun cleanupAudioFiles() {
90
+ filesDir.listFiles()?.forEach { file ->
91
+ if (file.name.endsWith(".wav") || file.name.endsWith(".aac") || file.name.endsWith(".opus")) {
92
+ file.delete()
93
+ }
94
+ }
95
+ }
96
+
97
+ @Test
98
+ fun measureStopTime_5seconds() {
99
+ runPerformanceTest(5_000L, "5 second recording")
100
+ }
101
+
102
+ @Test
103
+ fun measureStopTime_30seconds() {
104
+ runPerformanceTest(30_000L, "30 second recording")
105
+ }
106
+
107
+ @Test
108
+ fun measureStopTime_1minute() {
109
+ runPerformanceTest(60_000L, "1 minute recording")
110
+ }
111
+
112
+ @Test
113
+ fun measureStopTime_2minutes() {
114
+ runPerformanceTest(120_000L, "2 minute recording")
115
+ }
116
+
117
+ @Test
118
+ fun measureStopTime_5minutes() {
119
+ runPerformanceTest(300_000L, "5 minute recording")
120
+ }
121
+
122
+ @Test
123
+ fun measureStopTime_10minutes() {
124
+ runPerformanceTest(600_000L, "10 minute recording")
125
+ }
126
+
127
+ @Test
128
+ fun measureStopTime_15minutes() {
129
+ runPerformanceTest(900_000L, "15 minute recording")
130
+ }
131
+
132
+ private fun runPerformanceTest(recordingDurationMs: Long, testName: String) {
133
+ val recordingOptions = mapOf(
134
+ "sampleRate" to 44100,
135
+ "channels" to 1,
136
+ "encoding" to "pcm_16bit",
137
+ "interval" to 1000,
138
+ "enableProcessing" to false,
139
+ "showNotification" to false,
140
+ "output" to mapOf(
141
+ "primary" to mapOf("enabled" to true),
142
+ "compressed" to mapOf("enabled" to false)
143
+ )
144
+ )
145
+
146
+ // Start recording
147
+ val startLatch = CountDownLatch(1)
148
+ audioRecorderManager.startRecording(recordingOptions, object : Promise {
149
+ override fun resolve(value: Any?) {
150
+ startLatch.countDown()
151
+ }
152
+ override fun reject(code: String, message: String?, cause: Throwable?) {
153
+ fail("Start recording failed: $message")
154
+ }
155
+ })
156
+
157
+ assertTrue("Recording should start", startLatch.await(5, TimeUnit.SECONDS))
158
+ assertTrue("Recording should be active", audioRecorderManager.isRecording)
159
+
160
+ // Record for specified duration
161
+ Thread.sleep(recordingDurationMs)
162
+
163
+ // Measure stop time
164
+ val stopLatch = CountDownLatch(1)
165
+ var fileSize = 0L
166
+ var stopResult: Map<String, Any>? = null
167
+
168
+ val stopDuration = measureTimeMillis {
169
+ audioRecorderManager.stopRecording(object : Promise {
170
+ override fun resolve(value: Any?) {
171
+ when (value) {
172
+ is android.os.Bundle -> {
173
+ fileSize = value.getLong("size", 0)
174
+ stopResult = bundleToMap(value)
175
+ }
176
+ is Map<*, *> -> {
177
+ @Suppress("UNCHECKED_CAST")
178
+ stopResult = value as? Map<String, Any>
179
+ fileSize = (stopResult?.get("size") as? Long) ?: 0
180
+ }
181
+ }
182
+ stopLatch.countDown()
183
+ }
184
+ override fun reject(code: String, message: String?, cause: Throwable?) {
185
+ fail("Stop recording failed: $message")
186
+ }
187
+ })
188
+
189
+ assertTrue("Stop should complete", stopLatch.await(10, TimeUnit.SECONDS))
190
+ }
191
+
192
+ // Log results
193
+ val fileSizeMB = fileSize / (1024.0 * 1024.0)
194
+ Log.i(TAG, """
195
+ Performance Test: $testName
196
+ - Recording Duration: ${recordingDurationMs}ms
197
+ - Stop Duration: ${stopDuration}ms
198
+ - File Size: ${"%.2f".format(fileSizeMB)}MB
199
+ - Performance: ${if (stopDuration < getTargetTime(recordingDurationMs)) "PASS" else "FAIL"}
200
+ """.trimIndent())
201
+
202
+ println("""
203
+ Performance Test: $testName
204
+ - Recording Duration: ${recordingDurationMs}ms
205
+ - Stop Duration: ${stopDuration}ms
206
+ - File Size: ${"%.2f".format(fileSizeMB)}MB
207
+ - Performance: ${if (stopDuration < getTargetTime(recordingDurationMs)) "PASS" else "FAIL"}
208
+ """.trimIndent())
209
+
210
+ assertFalse("Recording should not be active", audioRecorderManager.isRecording)
211
+ }
212
+
213
+ private fun getTargetTime(recordingDurationMs: Long): Long {
214
+ return when {
215
+ recordingDurationMs <= 5_000 -> 100
216
+ recordingDurationMs <= 30_000 -> 150
217
+ recordingDurationMs <= 60_000 -> 200
218
+ recordingDurationMs <= 300_000 -> 500
219
+ else -> 750
220
+ }
221
+ }
222
+
223
+ private fun bundleToMap(bundle: android.os.Bundle): Map<String, Any> {
224
+ val map = mutableMapOf<String, Any>()
225
+ for (key in bundle.keySet()) {
226
+ val value = bundle.get(key)
227
+ when (value) {
228
+ is android.os.Bundle -> map[key] = bundleToMap(value)
229
+ else -> value?.let { map[key] = it }
230
+ }
231
+ }
232
+ return map
233
+ }
234
+ }
@@ -0,0 +1,332 @@
1
+ package net.siteed.audiostudio.integration
2
+
3
+ import android.media.AudioManager
4
+ import android.content.Context
5
+ import androidx.test.ext.junit.runners.AndroidJUnit4
6
+ import androidx.test.platform.app.InstrumentationRegistry
7
+ import org.junit.Test
8
+ import org.junit.Assert.*
9
+ import org.junit.Before
10
+ import org.junit.After
11
+ import org.junit.runner.RunWith
12
+ import net.siteed.audiostudio.RecordingConfig
13
+ import java.io.File
14
+
15
+ /**
16
+ * Integration tests for audio focus strategy functionality.
17
+ * These tests run on actual Android devices/emulators to validate that
18
+ * audio focus strategies work correctly in real scenarios.
19
+ */
20
+ @RunWith(AndroidJUnit4::class)
21
+ class AudioFocusStrategyIntegrationTest {
22
+
23
+ private lateinit var context: Context
24
+ private lateinit var audioManager: AudioManager
25
+ private lateinit var filesDir: File
26
+
27
+ @Before
28
+ fun setUp() {
29
+ context = InstrumentationRegistry.getInstrumentation().targetContext
30
+ audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
31
+ filesDir = context.filesDir
32
+ }
33
+
34
+ @After
35
+ fun tearDown() {
36
+ // Clean up any test files
37
+ val testFiles = filesDir.listFiles { _, name ->
38
+ name.startsWith("test_audio_focus_")
39
+ }
40
+ testFiles?.forEach { it.delete() }
41
+ }
42
+
43
+ @Test
44
+ fun testRecordingConfigWithBackgroundStrategy() {
45
+ val options = mapOf(
46
+ "sampleRate" to 44100,
47
+ "channels" to 1,
48
+ "encoding" to "pcm_16bit",
49
+ "keepAwake" to true,
50
+ "autoResumeAfterInterruption" to true,
51
+ "android" to mapOf(
52
+ "audioFocusStrategy" to "background"
53
+ )
54
+ )
55
+
56
+ val result = RecordingConfig.fromMap(options)
57
+ assertTrue("Config creation should succeed", result.isSuccess)
58
+
59
+ val (config, audioFormat) = result.getOrThrow()
60
+
61
+ // Verify audio focus strategy configuration
62
+ assertEquals("Audio focus strategy should be background", "background", config.audioFocusStrategy)
63
+ assertTrue("keepAwake should be true for background recording", config.keepAwake)
64
+ assertTrue("autoResumeAfterInterruption should be true", config.autoResumeAfterInterruption)
65
+
66
+ // Verify audio format is properly configured
67
+ assertNotNull("Audio format should be created", audioFormat)
68
+ assertEquals("MIME type should be audio/wav", "audio/wav", audioFormat.mimeType)
69
+ }
70
+
71
+ @Test
72
+ fun testRecordingConfigWithInteractiveStrategy() {
73
+ val options = mapOf(
74
+ "sampleRate" to 44100,
75
+ "channels" to 1,
76
+ "encoding" to "pcm_16bit",
77
+ "keepAwake" to false,
78
+ "autoResumeAfterInterruption" to true,
79
+ "android" to mapOf(
80
+ "audioFocusStrategy" to "interactive"
81
+ )
82
+ )
83
+
84
+ val result = RecordingConfig.fromMap(options)
85
+ assertTrue("Config creation should succeed", result.isSuccess)
86
+
87
+ val (config, audioFormat) = result.getOrThrow()
88
+
89
+ // Verify audio focus strategy configuration
90
+ assertEquals("Audio focus strategy should be interactive", "interactive", config.audioFocusStrategy)
91
+ assertFalse("keepAwake should be false for interactive recording", config.keepAwake)
92
+ assertTrue("autoResumeAfterInterruption should be true", config.autoResumeAfterInterruption)
93
+
94
+ // Verify audio format is properly configured
95
+ assertNotNull("Audio format should be created", audioFormat)
96
+ assertEquals("MIME type should be audio/wav", "audio/wav", audioFormat.mimeType)
97
+ }
98
+
99
+ @Test
100
+ fun testRecordingConfigWithCommunicationStrategy() {
101
+ val options = mapOf(
102
+ "sampleRate" to 16000, // Common speech sample rate
103
+ "channels" to 1,
104
+ "encoding" to "pcm_16bit",
105
+ "keepAwake" to false,
106
+ "autoResumeAfterInterruption" to true,
107
+ "android" to mapOf(
108
+ "audioFocusStrategy" to "communication"
109
+ )
110
+ )
111
+
112
+ val result = RecordingConfig.fromMap(options)
113
+ assertTrue("Config creation should succeed", result.isSuccess)
114
+
115
+ val (config, audioFormat) = result.getOrThrow()
116
+
117
+ // Verify audio focus strategy configuration
118
+ assertEquals("Audio focus strategy should be communication", "communication", config.audioFocusStrategy)
119
+ assertEquals("Sample rate should be 16000 for speech", 16000, config.sampleRate)
120
+ assertFalse("keepAwake should be false", config.keepAwake)
121
+ assertTrue("autoResumeAfterInterruption should be true", config.autoResumeAfterInterruption)
122
+
123
+ // Verify audio format is properly configured
124
+ assertNotNull("Audio format should be created", audioFormat)
125
+ assertEquals("MIME type should be audio/wav", "audio/wav", audioFormat.mimeType)
126
+ }
127
+
128
+ @Test
129
+ fun testRecordingConfigWithNoneStrategy() {
130
+ val options = mapOf(
131
+ "sampleRate" to 44100,
132
+ "channels" to 1,
133
+ "encoding" to "pcm_16bit",
134
+ "keepAwake" to false,
135
+ "android" to mapOf(
136
+ "audioFocusStrategy" to "none"
137
+ )
138
+ )
139
+
140
+ val result = RecordingConfig.fromMap(options)
141
+ assertTrue("Config creation should succeed", result.isSuccess)
142
+
143
+ val (config, audioFormat) = result.getOrThrow()
144
+
145
+ // Verify audio focus strategy configuration
146
+ assertEquals("Audio focus strategy should be none", "none", config.audioFocusStrategy)
147
+
148
+ // Verify audio format is properly configured
149
+ assertNotNull("Audio format should be created", audioFormat)
150
+ assertEquals("MIME type should be audio/wav", "audio/wav", audioFormat.mimeType)
151
+ }
152
+
153
+ @Test
154
+ fun testStrategyOverrideBehavior() {
155
+ val options = mapOf(
156
+ "sampleRate" to 44100,
157
+ "channels" to 1,
158
+ "encoding" to "pcm_16bit",
159
+ "keepAwake" to true, // This would normally default to background
160
+ "android" to mapOf(
161
+ "audioFocusStrategy" to "communication" // But we override to communication
162
+ )
163
+ )
164
+
165
+ val result = RecordingConfig.fromMap(options)
166
+ assertTrue("Config creation should succeed", result.isSuccess)
167
+
168
+ val (config, audioFormat) = result.getOrThrow()
169
+
170
+ // Verify that explicit strategy overrides keepAwake defaults
171
+ assertEquals("Audio focus strategy should be communication (overriding keepAwake default)", "communication", config.audioFocusStrategy)
172
+ assertTrue("keepAwake should still be true", config.keepAwake)
173
+
174
+ // Verify audio format is properly configured
175
+ assertNotNull("Audio format should be created", audioFormat)
176
+ }
177
+
178
+ @Test
179
+ fun testAudioFocusStrategyWithCompression() {
180
+ val options = mapOf(
181
+ "sampleRate" to 44100,
182
+ "channels" to 1,
183
+ "encoding" to "pcm_16bit",
184
+ "android" to mapOf(
185
+ "audioFocusStrategy" to "background"
186
+ ),
187
+ "output" to mapOf(
188
+ "compressed" to mapOf(
189
+ "enabled" to true,
190
+ "format" to "aac",
191
+ "bitrate" to 128000
192
+ )
193
+ )
194
+ )
195
+
196
+ val result = RecordingConfig.fromMap(options)
197
+ assertTrue("Config creation should succeed", result.isSuccess)
198
+
199
+ val (config, audioFormat) = result.getOrThrow()
200
+
201
+ // Verify audio focus strategy works with compression
202
+ assertEquals("Audio focus strategy should be background", "background", config.audioFocusStrategy)
203
+ assertTrue("Compressed output should be enabled", config.output.compressed.enabled)
204
+ assertEquals("Compression format should be aac", "aac", config.output.compressed.format)
205
+ assertEquals("Bitrate should be 128000", 128000, config.output.compressed.bitrate)
206
+
207
+ // Verify audio format is properly configured
208
+ assertNotNull("Audio format should be created", audioFormat)
209
+ assertEquals("MIME type should be audio/wav", "audio/wav", audioFormat.mimeType)
210
+ }
211
+
212
+ @Test
213
+ fun testAudioFocusStrategyWithNotifications() {
214
+ val options = mapOf(
215
+ "sampleRate" to 44100,
216
+ "channels" to 1,
217
+ "encoding" to "pcm_16bit",
218
+ "showNotification" to true,
219
+ "showWaveformInNotification" to true,
220
+ "android" to mapOf(
221
+ "audioFocusStrategy" to "background"
222
+ ),
223
+ "notification" to mapOf(
224
+ "title" to "Recording Audio",
225
+ "text" to "Background recording in progress",
226
+ "icon" to "ic_mic"
227
+ )
228
+ )
229
+
230
+ val result = RecordingConfig.fromMap(options)
231
+ assertTrue("Config creation should succeed", result.isSuccess)
232
+
233
+ val (config, audioFormat) = result.getOrThrow()
234
+
235
+ // Verify audio focus strategy works with notifications
236
+ assertEquals("Audio focus strategy should be background", "background", config.audioFocusStrategy)
237
+ assertTrue("Notifications should be enabled", config.showNotification)
238
+ assertTrue("Waveform in notification should be enabled", config.showWaveformInNotification)
239
+ assertEquals("Notification title should match", "Recording Audio", config.notification.title)
240
+ assertEquals("Notification text should match", "Background recording in progress", config.notification.text)
241
+ assertEquals("Notification icon should match", "ic_mic", config.notification.icon)
242
+
243
+ // Verify audio format is properly configured
244
+ assertNotNull("Audio format should be created", audioFormat)
245
+ assertEquals("MIME type should be audio/wav", "audio/wav", audioFormat.mimeType)
246
+ }
247
+
248
+ @Test
249
+ fun testAudioFocusStrategyValidation() {
250
+ // Test all valid strategies
251
+ val strategies = listOf("background", "interactive", "communication", "none")
252
+
253
+ for (strategy in strategies) {
254
+ val options = mapOf(
255
+ "sampleRate" to 44100,
256
+ "channels" to 1,
257
+ "encoding" to "pcm_16bit",
258
+ "android" to mapOf(
259
+ "audioFocusStrategy" to strategy
260
+ )
261
+ )
262
+
263
+ val result = RecordingConfig.fromMap(options)
264
+ assertTrue("Config creation should succeed for strategy: $strategy", result.isSuccess)
265
+
266
+ val (config, _) = result.getOrThrow()
267
+ assertEquals("Audio focus strategy should be $strategy", strategy, config.audioFocusStrategy)
268
+ }
269
+ }
270
+
271
+ @Test
272
+ fun testInvalidAudioFocusStrategyHandling() {
273
+ val options = mapOf(
274
+ "sampleRate" to 44100,
275
+ "channels" to 1,
276
+ "encoding" to "pcm_16bit",
277
+ "android" to mapOf(
278
+ "audioFocusStrategy" to "invalid_strategy"
279
+ )
280
+ )
281
+
282
+ val result = RecordingConfig.fromMap(options)
283
+ assertTrue("Config creation should succeed even with invalid strategy", result.isSuccess)
284
+
285
+ val (config, _) = result.getOrThrow()
286
+ assertEquals("Invalid strategy should be preserved", "invalid_strategy", config.audioFocusStrategy)
287
+ }
288
+
289
+ @Test
290
+ fun testCompleteAudioFocusConfiguration() {
291
+ val options = mapOf(
292
+ "sampleRate" to 44100,
293
+ "channels" to 1,
294
+ "encoding" to "pcm_16bit",
295
+ "keepAwake" to true,
296
+ "autoResumeAfterInterruption" to true,
297
+ "showNotification" to true,
298
+ "showWaveformInNotification" to false,
299
+ "enableProcessing" to false,
300
+ "android" to mapOf(
301
+ "audioFocusStrategy" to "communication"
302
+ ),
303
+ "notification" to mapOf(
304
+ "title" to "Voice Call Recording",
305
+ "text" to "Call in progress",
306
+ "icon" to "ic_call"
307
+ )
308
+ )
309
+
310
+ val result = RecordingConfig.fromMap(options)
311
+ assertTrue("Config creation should succeed", result.isSuccess)
312
+
313
+ val (config, audioFormat) = result.getOrThrow()
314
+
315
+ // Verify complete configuration
316
+ assertEquals("Audio focus strategy should be communication", "communication", config.audioFocusStrategy)
317
+ assertEquals("Sample rate should be 44100", 44100, config.sampleRate)
318
+ assertEquals("Channels should be 1", 1, config.channels)
319
+ assertEquals("Encoding should be pcm_16bit", "pcm_16bit", config.encoding)
320
+ assertTrue("keepAwake should be true", config.keepAwake)
321
+ assertFalse("showWaveformInNotification should be false", config.showWaveformInNotification)
322
+ assertTrue("showNotification should be true", config.showNotification)
323
+ assertTrue("autoResumeAfterInterruption should be true", config.autoResumeAfterInterruption)
324
+ assertFalse("enableProcessing should be false", config.enableProcessing)
325
+ assertEquals("Notification title should match", "Voice Call Recording", config.notification.title)
326
+ assertEquals("Notification text should match", "Call in progress", config.notification.text)
327
+
328
+ // Verify audio format is properly configured
329
+ assertNotNull("Audio format should be created", audioFormat)
330
+ assertEquals("MIME type should be audio/wav", "audio/wav", audioFormat.mimeType)
331
+ }
332
+ }