@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,165 @@
1
+ #include <jni.h>
2
+ #include <android/log.h>
3
+ #include "MelSpectrogram.h"
4
+ #include <memory>
5
+ #include <cmath>
6
+ #include <mutex>
7
+
8
+ #define LOG_TAG "MelSpectrogramJNI"
9
+ #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
10
+ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
11
+
12
+ // Cache processor across JNI calls — avoids rebuilding FFT plan,
13
+ // window, and filterbank when config is unchanged.
14
+ static std::unique_ptr<MelSpectrogramProcessor> cachedProcessor;
15
+ static std::mutex cachedMutex;
16
+
17
+ extern "C" JNIEXPORT jobjectArray JNICALL
18
+ Java_net_siteed_audiostudio_MelSpectrogramNative_compute(
19
+ JNIEnv* env, jobject /* thiz */,
20
+ jfloatArray jSamples, jint sampleRate, jint fftLength,
21
+ jint windowSizeSamples, jint hopLengthSamples,
22
+ jint nMels, jfloat fMin, jfloat fMax,
23
+ jint windowType, jboolean logScale, jboolean normalize)
24
+ {
25
+ jfloat* samples = env->GetFloatArrayElements(jSamples, nullptr);
26
+ if (!samples) {
27
+ LOGE("Failed to get float array elements");
28
+ return nullptr;
29
+ }
30
+ jint numSamples = env->GetArrayLength(jSamples);
31
+
32
+ LOGI("compute: numSamples=%d, sampleRate=%d, fftLength=%d, windowSize=%d, hop=%d, nMels=%d",
33
+ numSamples, sampleRate, fftLength, windowSizeSamples, hopLengthSamples, nMels);
34
+
35
+ MelSpectrogramConfig config;
36
+ config.sampleRate = sampleRate;
37
+ config.fftLength = fftLength;
38
+ config.windowSizeSamples = windowSizeSamples;
39
+ config.hopLengthSamples = hopLengthSamples;
40
+ config.nMels = nMels;
41
+ config.fMin = fMin;
42
+ config.fMax = fMax;
43
+ config.windowType = windowType;
44
+ config.logScale = logScale;
45
+ config.normalize = normalize;
46
+
47
+ // Reuse processor if config matches
48
+ std::lock_guard<std::mutex> lock(cachedMutex);
49
+ if (!cachedProcessor || !(cachedProcessor->config() == config)) {
50
+ cachedProcessor = std::make_unique<MelSpectrogramProcessor>(config);
51
+ }
52
+
53
+ MelSpectrogramResult result = cachedProcessor->compute(samples, numSamples);
54
+
55
+ env->ReleaseFloatArrayElements(jSamples, samples, JNI_ABORT);
56
+
57
+ LOGI("compute done: timeSteps=%d, nMels=%d", result.timeSteps, result.nMels);
58
+
59
+ if (result.timeSteps <= 0) {
60
+ return nullptr;
61
+ }
62
+
63
+ // Create float[][] result
64
+ jclass floatArrayClass = env->FindClass("[F");
65
+ if (!floatArrayClass) {
66
+ LOGE("Failed to find float[] class");
67
+ return nullptr;
68
+ }
69
+
70
+ jobjectArray jResult = env->NewObjectArray(result.timeSteps, floatArrayClass, nullptr);
71
+ if (!jResult) {
72
+ LOGE("Failed to allocate result array");
73
+ return nullptr;
74
+ }
75
+
76
+ for (int i = 0; i < result.timeSteps; ++i) {
77
+ jfloatArray row = env->NewFloatArray(result.nMels);
78
+ if (!row) {
79
+ LOGE("Failed to allocate row %d", i);
80
+ return jResult; // Return partial result rather than leak
81
+ }
82
+ env->SetFloatArrayRegion(row, 0, result.nMels,
83
+ result.data.data() + i * result.nMels);
84
+ env->SetObjectArrayElement(jResult, i, row);
85
+ env->DeleteLocalRef(row);
86
+ }
87
+
88
+ return jResult;
89
+ }
90
+
91
+ extern "C" JNIEXPORT void JNICALL
92
+ Java_net_siteed_audiostudio_MelSpectrogramNative_init(
93
+ JNIEnv* env, jobject /* thiz */,
94
+ jint sampleRate, jint fftLength, jint windowSizeSamples,
95
+ jint hopLengthSamples, jint nMels, jfloat fMin, jfloat fMax,
96
+ jint windowType)
97
+ {
98
+ MelSpectrogramConfig config;
99
+ config.sampleRate = sampleRate;
100
+ config.fftLength = fftLength;
101
+ config.windowSizeSamples = windowSizeSamples;
102
+ config.hopLengthSamples = hopLengthSamples;
103
+ config.nMels = nMels;
104
+ config.fMin = fMin;
105
+ config.fMax = fMax;
106
+ config.windowType = windowType;
107
+ config.logScale = false;
108
+ config.normalize = false;
109
+
110
+ std::lock_guard<std::mutex> lock(cachedMutex);
111
+ if (!cachedProcessor || !(cachedProcessor->config() == config)) {
112
+ cachedProcessor = std::make_unique<MelSpectrogramProcessor>(config);
113
+ }
114
+ LOGI("init: sampleRate=%d, fftLength=%d, nMels=%d", sampleRate, fftLength, nMels);
115
+ }
116
+
117
+ extern "C" JNIEXPORT jboolean JNICALL
118
+ Java_net_siteed_audiostudio_MelSpectrogramNative_computeFrame(
119
+ JNIEnv* env, jobject /* thiz */,
120
+ jfloatArray jFrame, jfloatArray jMelOutput)
121
+ {
122
+ std::lock_guard<std::mutex> lock(cachedMutex);
123
+ if (!cachedProcessor) {
124
+ LOGE("computeFrame: processor not initialized, call init() first");
125
+ return JNI_FALSE;
126
+ }
127
+
128
+ jfloat* frame = env->GetFloatArrayElements(jFrame, nullptr);
129
+ if (!frame) {
130
+ LOGE("computeFrame: failed to get frame array");
131
+ return JNI_FALSE;
132
+ }
133
+ jint frameSize = env->GetArrayLength(jFrame);
134
+
135
+ jfloat* melOutput = env->GetFloatArrayElements(jMelOutput, nullptr);
136
+ if (!melOutput) {
137
+ env->ReleaseFloatArrayElements(jFrame, frame, JNI_ABORT);
138
+ LOGE("computeFrame: failed to get melOutput array");
139
+ return JNI_FALSE;
140
+ }
141
+
142
+ cachedProcessor->computeFrame(frame, frameSize, melOutput);
143
+
144
+ // Always apply log scaling (log(max(1e-10, val))) regardless of config.logScale
145
+ const int nMels = cachedProcessor->config().nMels;
146
+ for (int i = 0; i < nMels; ++i) {
147
+ melOutput[i] = std::log(std::max(1e-10f, melOutput[i]));
148
+ }
149
+
150
+ env->ReleaseFloatArrayElements(jFrame, frame, JNI_ABORT);
151
+ env->ReleaseFloatArrayElements(jMelOutput, melOutput, 0); // 0 = copy back
152
+
153
+ return JNI_TRUE;
154
+ }
155
+
156
+ extern "C" JNIEXPORT jint JNICALL
157
+ Java_net_siteed_audiostudio_MelSpectrogramNative_getNMels(
158
+ JNIEnv* env, jobject /* thiz */)
159
+ {
160
+ std::lock_guard<std::mutex> lock(cachedMutex);
161
+ if (!cachedProcessor) {
162
+ return 0;
163
+ }
164
+ return cachedProcessor->config().nMels;
165
+ }
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+ <path
8
+ android:fillColor="#FFFFFF"
9
+ android:pathData="M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM12,20C7.58,20 4,16.42 4,12C4,7.58 7.58,4 12,4C16.42,4 20,7.58 20,12C20,16.42 16.42,20 12,20Z"/>
10
+ <path
11
+ android:fillColor="#FFFFFF"
12
+ android:pathData="M12,17L12,7"/>
13
+ <path
14
+ android:fillColor="#FFFFFF"
15
+ android:pathData="M7,12L17,12"/>
16
+ </vector>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+ <path
8
+ android:fillColor="#FFFFFF"
9
+ android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14z"/>
10
+ <path
11
+ android:fillColor="#FFFFFF"
12
+ android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/>
13
+ </vector>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+ <path
8
+ android:fillColor="#FFFFFF"
9
+ android:pathData="M6,4h4v16H6zM14,4h4v16h-4z"/>
10
+ </vector>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+ <path
8
+ android:fillColor="#FFFFFF"
9
+ android:pathData="M8,5v14l11,-7z"/>
10
+ </vector>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+ <path
8
+ android:fillColor="#FFFFFF"
9
+ android:pathData="M6,6h12v12H6z"/>
10
+ </vector>
@@ -0,0 +1,37 @@
1
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:id="@+id/notification_layout"
3
+ android:layout_width="match_parent"
4
+ android:layout_height="wrap_content"
5
+ android:gravity="center"
6
+ android:orientation="vertical"
7
+ android:padding="8dp">
8
+
9
+ <TextView
10
+ android:id="@+id/notification_title"
11
+ android:layout_width="wrap_content"
12
+ android:layout_height="wrap_content"
13
+ android:layout_marginBottom="4dp"
14
+ android:textSize="16sp"
15
+ android:textStyle="bold" />
16
+
17
+ <TextView
18
+ android:id="@+id/notification_text"
19
+ android:layout_width="wrap_content"
20
+ android:layout_height="wrap_content"
21
+ android:layout_marginBottom="4dp"
22
+ android:textSize="14sp" />
23
+
24
+ <ImageView
25
+ android:id="@+id/notification_waveform"
26
+ android:layout_width="match_parent"
27
+ android:layout_height="64dp"
28
+ android:scaleType="fitCenter" />
29
+
30
+ <TextView
31
+ android:id="@+id/notification_duration"
32
+ android:layout_width="wrap_content"
33
+ android:layout_height="wrap_content"
34
+ android:layout_marginTop="4dp"
35
+ android:textSize="14sp" />
36
+
37
+ </LinearLayout>
@@ -0,0 +1,279 @@
1
+ package net.siteed.audiostudio
2
+
3
+ import org.junit.Test
4
+ import org.junit.Assert.*
5
+ import org.junit.Before
6
+ import org.junit.After
7
+ import java.io.ByteArrayOutputStream
8
+ import java.io.File
9
+ import java.nio.ByteBuffer
10
+ import java.nio.ByteOrder
11
+ import kotlin.test.assertNotNull
12
+
13
+ class AudioFileHandlerTest {
14
+ private lateinit var tempDir: File
15
+ private lateinit var audioFileHandler: AudioFileHandler
16
+
17
+ @Before
18
+ fun setUp() {
19
+ // Create a temporary directory for testing
20
+ tempDir = File(System.getProperty("java.io.tmpdir"), "audio_test_${System.currentTimeMillis()}")
21
+ tempDir.mkdirs()
22
+ audioFileHandler = AudioFileHandler(tempDir)
23
+ }
24
+
25
+ @After
26
+ fun tearDown() {
27
+ // Clean up temporary directory
28
+ tempDir.deleteRecursively()
29
+ }
30
+
31
+ @Test
32
+ fun testWriteWavHeader_writesCorrectHeaderFormat() {
33
+ // Given
34
+ val outputStream = ByteArrayOutputStream()
35
+ val sampleRate = 44100
36
+ val channels = 2
37
+ val bitDepth = 16
38
+
39
+ // When
40
+ audioFileHandler.writeWavHeader(outputStream, sampleRate, channels, bitDepth)
41
+ val header = outputStream.toByteArray()
42
+
43
+ // Then
44
+ assertEquals("WAV header should be 44 bytes", 44, header.size)
45
+
46
+ // Check RIFF header
47
+ assertEquals("Should start with RIFF", "RIFF", String(header.sliceArray(0..3)))
48
+ assertEquals("Should contain WAVE identifier", "WAVE", String(header.sliceArray(8..11)))
49
+ assertEquals("Should contain fmt chunk", "fmt ", String(header.sliceArray(12..15)))
50
+ assertEquals("Should contain data chunk", "data", String(header.sliceArray(36..39)))
51
+
52
+ // Check audio format (PCM = 1)
53
+ val audioFormat = ByteBuffer.wrap(header.sliceArray(20..21)).order(ByteOrder.LITTLE_ENDIAN).short
54
+ assertEquals("Audio format should be 1 (PCM)", 1, audioFormat.toInt())
55
+
56
+ // Check channels
57
+ val headerChannels = ByteBuffer.wrap(header.sliceArray(22..23)).order(ByteOrder.LITTLE_ENDIAN).short
58
+ assertEquals("Channels should match", channels, headerChannels.toInt())
59
+
60
+ // Check sample rate
61
+ val headerSampleRate = ByteBuffer.wrap(header.sliceArray(24..27)).order(ByteOrder.LITTLE_ENDIAN).int
62
+ assertEquals("Sample rate should match", sampleRate, headerSampleRate)
63
+
64
+ // Check bit depth
65
+ val headerBitDepth = ByteBuffer.wrap(header.sliceArray(34..35)).order(ByteOrder.LITTLE_ENDIAN).short
66
+ assertEquals("Bit depth should match", bitDepth, headerBitDepth.toInt())
67
+ }
68
+
69
+ @Test
70
+ fun testWriteWavHeader_calculatesCorrectByteRate() {
71
+ // Given
72
+ val outputStream = ByteArrayOutputStream()
73
+ val sampleRate = 48000
74
+ val channels = 1
75
+ val bitDepth = 24
76
+ val expectedByteRate = sampleRate * channels * bitDepth / 8
77
+
78
+ // When
79
+ audioFileHandler.writeWavHeader(outputStream, sampleRate, channels, bitDepth)
80
+ val header = outputStream.toByteArray()
81
+
82
+ // Then
83
+ val byteRate = ByteBuffer.wrap(header.sliceArray(28..31)).order(ByteOrder.LITTLE_ENDIAN).int
84
+ assertEquals("Byte rate should be correctly calculated", expectedByteRate, byteRate)
85
+ }
86
+
87
+ @Test
88
+ fun testCreateAudioFile_createsFileWithCorrectExtension() {
89
+ // Given
90
+ val extension = "wav"
91
+
92
+ // When
93
+ val file = audioFileHandler.createAudioFile(extension)
94
+
95
+ // Then
96
+ assertNotNull("File should not be null", file)
97
+ assertTrue("File should exist", file.exists())
98
+ assertTrue("File should have correct extension", file.name.endsWith(".$extension"))
99
+ assertTrue("File should have correct prefix", file.name.startsWith("recording_"))
100
+
101
+ // Clean up
102
+ file.delete()
103
+ }
104
+
105
+ @Test
106
+ fun testCreateAudioFile_createsUniqueFiles() {
107
+ // Given
108
+ val extension = "wav"
109
+
110
+ // When
111
+ val file1 = audioFileHandler.createAudioFile(extension)
112
+ Thread.sleep(10) // Small delay to ensure different timestamps
113
+ val file2 = audioFileHandler.createAudioFile(extension)
114
+
115
+ // Then
116
+ assertNotEquals("Files should have unique names", file1.name, file2.name)
117
+ assertTrue("First file should exist", file1.exists())
118
+ assertTrue("Second file should exist", file2.exists())
119
+
120
+ // Clean up
121
+ file1.delete()
122
+ file2.delete()
123
+ }
124
+
125
+ @Test
126
+ fun testDeleteFile_deletesExistingFile() {
127
+ // Given
128
+ val file = audioFileHandler.createAudioFile("wav")
129
+ assertTrue("File should exist before deletion", file.exists())
130
+
131
+ // When
132
+ val result = audioFileHandler.deleteFile(file)
133
+
134
+ // Then
135
+ assertTrue("Delete should return true", result)
136
+ assertFalse("File should not exist after deletion", file.exists())
137
+ }
138
+
139
+ @Test
140
+ fun testDeleteFile_handlesNullFile() {
141
+ // When
142
+ val result = audioFileHandler.deleteFile(null)
143
+
144
+ // Then
145
+ assertFalse("Delete should return false for null file", result)
146
+ }
147
+
148
+ @Test
149
+ fun testDeleteFile_handlesNonExistentFile() {
150
+ // Given
151
+ val nonExistentFile = File(tempDir, "non_existent.wav")
152
+ assertFalse("File should not exist", nonExistentFile.exists())
153
+
154
+ // When
155
+ val result = audioFileHandler.deleteFile(nonExistentFile)
156
+
157
+ // Then
158
+ assertFalse("Delete should return false for non-existent file", result)
159
+ }
160
+
161
+ @Test
162
+ fun testClearAudioStorage_deletesAllFiles() {
163
+ // Given
164
+ val file1 = audioFileHandler.createAudioFile("wav")
165
+ val file2 = audioFileHandler.createAudioFile("mp3")
166
+ val file3 = audioFileHandler.createAudioFile("aac")
167
+
168
+ assertTrue("File 1 should exist", file1.exists())
169
+ assertTrue("File 2 should exist", file2.exists())
170
+ assertTrue("File 3 should exist", file3.exists())
171
+
172
+ // When
173
+ audioFileHandler.clearAudioStorage()
174
+
175
+ // Then
176
+ assertFalse("File 1 should be deleted", file1.exists())
177
+ assertFalse("File 2 should be deleted", file2.exists())
178
+ assertFalse("File 3 should be deleted", file3.exists())
179
+ assertEquals("Directory should be empty", 0, tempDir.listFiles()?.size ?: 0)
180
+ }
181
+
182
+ @Test
183
+ fun testUpdateWavHeader_updatesFileSizeCorrectly() {
184
+ // Given
185
+ val file = audioFileHandler.createAudioFile("wav")
186
+ val outputStream = file.outputStream()
187
+
188
+ // Write header
189
+ audioFileHandler.writeWavHeader(outputStream, 44100, 2, 16)
190
+
191
+ // Write some audio data (1 second of silence)
192
+ val audioDataSize = 44100 * 2 * 2 // sampleRate * channels * bytesPerSample
193
+ val audioData = ByteArray(audioDataSize)
194
+ outputStream.write(audioData)
195
+ outputStream.close()
196
+
197
+ // When
198
+ audioFileHandler.updateWavHeader(file)
199
+
200
+ // Then
201
+ val updatedHeader = file.inputStream().use { it.readNBytes(44) }
202
+
203
+ // Check file size field (bytes 4-7)
204
+ val fileSize = ByteBuffer.wrap(updatedHeader.sliceArray(4..7)).order(ByteOrder.LITTLE_ENDIAN).int
205
+ assertEquals("File size should be data size + 36", audioDataSize + 36, fileSize)
206
+
207
+ // Check data size field (bytes 40-43)
208
+ val dataSize = ByteBuffer.wrap(updatedHeader.sliceArray(40..43)).order(ByteOrder.LITTLE_ENDIAN).int
209
+ assertEquals("Data size should match audio data size", audioDataSize, dataSize)
210
+
211
+ // Clean up
212
+ file.delete()
213
+ }
214
+
215
+ @Test
216
+ fun testLoadRealWavFile_readsHeaderCorrectly() {
217
+ // Given - Load a real WAV file from test resources
218
+ val resourceStream = javaClass.classLoader?.getResourceAsStream("jfk.wav")
219
+ assertNotNull("Test resource jfk.wav should exist", resourceStream)
220
+
221
+ val testFile = File(tempDir, "test_jfk.wav")
222
+ resourceStream?.use { input ->
223
+ testFile.outputStream().use { output ->
224
+ input.copyTo(output)
225
+ }
226
+ }
227
+
228
+ // When - Read the WAV header
229
+ val header = testFile.inputStream().use { it.readNBytes(44) }
230
+
231
+ // Then - Validate header structure
232
+ assertEquals("Should start with RIFF", "RIFF", String(header.sliceArray(0..3)))
233
+ assertEquals("Should contain WAVE identifier", "WAVE", String(header.sliceArray(8..11)))
234
+ assertEquals("Should contain fmt chunk", "fmt ", String(header.sliceArray(12..15)))
235
+
236
+ // Extract audio properties
237
+ val channels = ByteBuffer.wrap(header.sliceArray(22..23)).order(ByteOrder.LITTLE_ENDIAN).short
238
+ val sampleRate = ByteBuffer.wrap(header.sliceArray(24..27)).order(ByteOrder.LITTLE_ENDIAN).int
239
+ val bitDepth = ByteBuffer.wrap(header.sliceArray(34..35)).order(ByteOrder.LITTLE_ENDIAN).short
240
+
241
+ // JFK.wav is known to be mono, 16kHz, 16-bit
242
+ assertEquals("JFK audio should be mono", 1, channels.toInt())
243
+ assertEquals("JFK audio should be 16kHz", 16000, sampleRate)
244
+ assertEquals("JFK audio should be 16-bit", 16, bitDepth.toInt())
245
+
246
+ // Clean up
247
+ testFile.delete()
248
+ }
249
+
250
+ @Test
251
+ fun testProcessMultipleRealWavFiles() {
252
+ // Test with different real WAV files to ensure compatibility
253
+ val testFiles = listOf("jfk.wav", "recorder_hello_world.wav", "osr_us_000_0010_8k.wav")
254
+
255
+ for (fileName in testFiles) {
256
+ val resourceStream = javaClass.classLoader?.getResourceAsStream(fileName)
257
+ if (resourceStream != null) {
258
+ val testFile = File(tempDir, "test_$fileName")
259
+ resourceStream.use { input ->
260
+ testFile.outputStream().use { output ->
261
+ input.copyTo(output)
262
+ }
263
+ }
264
+
265
+ // Verify file was created and has content
266
+ assertTrue("$fileName should exist", testFile.exists())
267
+ assertTrue("$fileName should have more than just header", testFile.length() > 44)
268
+
269
+ // Read and validate header
270
+ val header = testFile.inputStream().use { it.readNBytes(44) }
271
+ assertEquals("$fileName should have RIFF header", "RIFF", String(header.sliceArray(0..3)))
272
+ assertEquals("$fileName should have WAVE format", "WAVE", String(header.sliceArray(8..11)))
273
+
274
+ // Clean up
275
+ testFile.delete()
276
+ }
277
+ }
278
+ }
279
+ }