@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,26 @@
1
+ package net.siteed.audiostudio
2
+
3
+ object AudioFeaturesNative {
4
+ init {
5
+ System.loadLibrary("audio-studio-cpp")
6
+ }
7
+
8
+ external fun computeFrame(
9
+ samples: FloatArray,
10
+ sampleRate: Int,
11
+ fftLength: Int,
12
+ nMfcc: Int,
13
+ nMelFilters: Int,
14
+ computeMfcc: Boolean,
15
+ computeChroma: Boolean
16
+ ): HashMap<String, Any>
17
+
18
+ external fun init(
19
+ sampleRate: Int,
20
+ fftLength: Int,
21
+ nMfcc: Int,
22
+ nMelFilters: Int,
23
+ computeMfcc: Boolean,
24
+ computeChroma: Boolean
25
+ )
26
+ }
@@ -0,0 +1,136 @@
1
+ package net.siteed.audiostudio
2
+
3
+ import android.util.Log
4
+ import java.io.File
5
+ import java.io.IOException
6
+ import java.io.OutputStream
7
+ import java.io.RandomAccessFile
8
+ import java.util.UUID
9
+ import net.siteed.audiostudio.LogUtils
10
+
11
+ class AudioFileHandler(private val filesDir: File) {
12
+ companion object {
13
+ private const val CLASS_NAME = "AudioFileHandler"
14
+ }
15
+
16
+ // Method to write WAV file header
17
+ fun writeWavHeader(out: OutputStream, sampleRateInHz: Int, channels: Int, bitDepth: Int) {
18
+ val header = ByteArray(44)
19
+ val byteRate = sampleRateInHz * channels * bitDepth / 8
20
+ val blockAlign = channels * bitDepth / 8
21
+
22
+ // RIFF/WAVE header
23
+ "RIFF".toByteArray().copyInto(header, 0)
24
+ // (file size - 8) to be updated later
25
+ header[4] = 0 // Placeholder
26
+ header[5] = 0 // Placeholder
27
+ header[6] = 0 // Placeholder
28
+ header[7] = 0 // Placeholder
29
+ "WAVE".toByteArray().copyInto(header, 8)
30
+ "fmt ".toByteArray().copyInto(header, 12)
31
+
32
+ // 16 for PCM
33
+ header[16] = 16
34
+ header[17] = 0
35
+ header[18] = 0
36
+ header[19] = 0
37
+
38
+ // PCM format ID
39
+ header[20] = 1 // Audio format 1 for PCM (not compressed)
40
+ header[21] = 0
41
+
42
+ // Number of channels
43
+ header[22] = (channels and 0xff).toByte()
44
+ header[23] = (channels shr 8 and 0xff).toByte()
45
+
46
+ // Sample rate
47
+ header[24] = (sampleRateInHz and 0xff).toByte()
48
+ header[25] = (sampleRateInHz shr 8 and 0xff).toByte()
49
+ header[26] = (sampleRateInHz shr 16 and 0xff).toByte()
50
+ header[27] = (sampleRateInHz shr 24 and 0xff).toByte()
51
+
52
+ // Byte rate
53
+ header[28] = (byteRate and 0xff).toByte()
54
+ header[29] = (byteRate shr 8 and 0xff).toByte()
55
+ header[30] = (byteRate shr 16 and 0xff).toByte()
56
+ header[31] = (byteRate shr 24 and 0xff).toByte()
57
+
58
+ // Block align
59
+ header[32] = (blockAlign and 0xff).toByte()
60
+ header[33] = (blockAlign shr 8 and 0xff).toByte()
61
+
62
+ // Bits per sample
63
+ header[34] = (bitDepth and 0xff).toByte()
64
+ header[35] = (bitDepth shr 8 and 0xff).toByte()
65
+
66
+ // Data chunk
67
+ "data".toByteArray().copyInto(header, 36)
68
+ // Data size to be updated later
69
+ header[40] = 0 // Placeholder
70
+ header[41] = 0 // Placeholder
71
+ header[42] = 0 // Placeholder
72
+ header[43] = 0 // Placeholder
73
+
74
+ out.write(header, 0, 44)
75
+ }
76
+
77
+ fun updateWavHeader(file: File) {
78
+ try {
79
+ RandomAccessFile(file, "rw").use { raf ->
80
+ val fileSize = raf.length()
81
+ val dataSize = fileSize - 44 // Subtract the header size
82
+
83
+ raf.seek(4) // Write correct file size, excluding the first 8 bytes of the RIFF header
84
+ raf.writeInt(Integer.reverseBytes((dataSize + 36).toInt()))
85
+
86
+ raf.seek(40) // Go to the data size position
87
+ raf.writeInt(Integer.reverseBytes(dataSize.toInt())) // Write the size of the data segment
88
+ }
89
+ } catch (e: IOException) {
90
+ println("Could not update WAV header: ${e.message}")
91
+ }
92
+ }
93
+
94
+ fun clearAudioStorage() {
95
+ filesDir.listFiles()?.forEach {
96
+ it.delete()
97
+ }
98
+ }
99
+
100
+ fun createAudioFile(extension: String): File {
101
+ val timestamp = System.currentTimeMillis()
102
+ val uuid = UUID.randomUUID().toString()
103
+ val filename = "recording_${timestamp}_${uuid}.${extension}"
104
+
105
+ return try {
106
+ File(filesDir, filename).apply {
107
+ parentFile?.mkdirs() // Create directories if they don't exist
108
+ createNewFile() // Create the file
109
+ }
110
+ } catch (e: Exception) {
111
+ LogUtils.e(CLASS_NAME, "Failed to create audio file", e)
112
+ throw e
113
+ }
114
+ }
115
+
116
+ fun deleteFile(file: File?): Boolean {
117
+ return try {
118
+ if (file == null) {
119
+ LogUtils.w(CLASS_NAME, "Attempted to delete null file")
120
+ false
121
+ } else if (!file.exists()) {
122
+ LogUtils.w(CLASS_NAME, "File does not exist: ${file.absolutePath}")
123
+ false
124
+ } else {
125
+ val wasDeleted = file.delete()
126
+ if (!wasDeleted) {
127
+ LogUtils.w(CLASS_NAME, "Failed to delete file: ${file.absolutePath}")
128
+ }
129
+ wasDeleted
130
+ }
131
+ } catch (e: Exception) {
132
+ LogUtils.e(CLASS_NAME, "Error deleting file: ${file?.absolutePath}", e)
133
+ false
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,354 @@
1
+ package net.siteed.audiostudio
2
+
3
+ import android.media.AudioFormat
4
+ import java.nio.ByteBuffer
5
+ import java.nio.ByteOrder
6
+ import kotlin.math.*
7
+
8
+ object AudioFormatUtils {
9
+ /**
10
+ * Converts a byte array of audio data to a float array based on the given encoding.
11
+ * @param audioData The raw audio data in bytes.
12
+ * @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
13
+ * @return A float array with normalized audio samples in the range [-1.0, 1.0].
14
+ */
15
+ fun convertByteArrayToFloatArray(audioData: ByteArray, encoding: String): FloatArray {
16
+ return when (encoding) {
17
+ "pcm_8bit" -> {
18
+ val floatArray = FloatArray(audioData.size)
19
+ for (i in audioData.indices) {
20
+ // Convert unsigned 8-bit to float in range [-1.0, 1.0]
21
+ floatArray[i] = ((audioData[i].toInt() and 0xFF) - 128) / 128.0f
22
+ }
23
+ floatArray
24
+ }
25
+ "pcm_16bit" -> {
26
+ val floatArray = FloatArray(audioData.size / 2)
27
+ val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
28
+ for (i in floatArray.indices) {
29
+ floatArray[i] = buffer.short / 32768.0f // Normalize to [-1.0, 1.0]
30
+ }
31
+ floatArray
32
+ }
33
+ "pcm_32bit" -> {
34
+ val floatArray = FloatArray(audioData.size / 4)
35
+ val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
36
+ for (i in floatArray.indices) {
37
+ floatArray[i] = buffer.int / 2_147_483_648.0f // Normalize to [-1.0, 1.0]
38
+ }
39
+ floatArray
40
+ }
41
+ else -> {
42
+ // Default to 16-bit PCM if encoding is not recognized
43
+ val floatArray = FloatArray(audioData.size / 2)
44
+ val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
45
+ for (i in floatArray.indices) {
46
+ floatArray[i] = buffer.short / 32768.0f
47
+ }
48
+ floatArray
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Calculates the bit depth (number of bits per sample) based on the encoding string.
55
+ * @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
56
+ * @return The bit depth as an integer.
57
+ */
58
+ fun getBitDepth(encoding: String): Int {
59
+ return when (encoding) {
60
+ "pcm_8bit" -> 8
61
+ "pcm_16bit" -> 16
62
+ "pcm_32bit" -> 32
63
+ else -> 16 // Default to 16-bit if not recognized
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Determines the AudioFormat encoding constant based on the encoding string.
69
+ * @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
70
+ * @return The corresponding AudioFormat constant.
71
+ */
72
+ fun getAudioFormat(encoding: String): Int {
73
+ return when (encoding) {
74
+ "pcm_8bit" -> AudioFormat.ENCODING_PCM_8BIT
75
+ "pcm_16bit" -> AudioFormat.ENCODING_PCM_16BIT
76
+ "pcm_32bit" -> AudioFormat.ENCODING_PCM_FLOAT
77
+ else -> AudioFormat.ENCODING_PCM_16BIT // Default to 16-bit PCM
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Converts audio data between different bit depths
83
+ * @param audioData The raw audio data
84
+ * @param sourceBitDepth The original bit depth
85
+ * @param targetBitDepth The desired bit depth
86
+ * @return The converted audio data
87
+ */
88
+ fun convertBitDepth(audioData: ByteArray, sourceBitDepth: Int, targetBitDepth: Int): ByteArray {
89
+ if (sourceBitDepth == targetBitDepth || audioData.isEmpty()) {
90
+ return audioData
91
+ }
92
+
93
+ return when {
94
+ sourceBitDepth == 8 && targetBitDepth == 16 -> convert8to16(audioData)
95
+ sourceBitDepth == 16 && targetBitDepth == 8 -> convert16to8(audioData)
96
+ sourceBitDepth == 16 && targetBitDepth == 32 -> convert16to32(audioData)
97
+ sourceBitDepth == 32 && targetBitDepth == 16 -> convert32to16(audioData)
98
+ sourceBitDepth == 8 && targetBitDepth == 32 -> {
99
+ // Convert 8 -> 16 -> 32
100
+ val temp16 = convert8to16(audioData)
101
+ convert16to32(temp16)
102
+ }
103
+ sourceBitDepth == 32 && targetBitDepth == 8 -> {
104
+ // Convert 32 -> 16 -> 8
105
+ val temp16 = convert32to16(audioData)
106
+ convert16to8(temp16)
107
+ }
108
+ else -> throw IllegalArgumentException("Unsupported bit depth conversion: $sourceBitDepth to $targetBitDepth")
109
+ }
110
+ }
111
+
112
+ private fun convert8to16(data: ByteArray): ByteArray {
113
+ val output = ByteBuffer.allocate(data.size * 2).order(ByteOrder.LITTLE_ENDIAN)
114
+ for (sample in data) {
115
+ // Convert unsigned 8-bit (0-255) to signed 16-bit (-32768 to 32767)
116
+ val unsigned = sample.toInt() and 0xFF
117
+ // Map [0, 255] to [-32768, 32767]
118
+ // Special case for 0 to map to -32768
119
+ val signed16 = when (unsigned) {
120
+ 0 -> -32768
121
+ 255 -> 32767
122
+ else -> {
123
+ val normalized = (unsigned - 128) / 128.0f
124
+ (normalized * 32768).toInt().coerceIn(-32768, 32767)
125
+ }
126
+ }.toShort()
127
+ output.putShort(signed16)
128
+ }
129
+ return output.array()
130
+ }
131
+
132
+ private fun convert16to8(data: ByteArray): ByteArray {
133
+ val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
134
+ val output = ByteArray(data.size / 2)
135
+
136
+ for (i in output.indices) {
137
+ // Convert signed 16-bit to unsigned 8-bit
138
+ val sample16 = input.getShort()
139
+ // Map [-32768, 32767] to [0, 255]
140
+ val normalized = sample16 / 32768.0f
141
+ val sample8 = ((normalized * 128) + 128).toInt().coerceIn(0, 255).toByte()
142
+ output[i] = sample8
143
+ }
144
+ return output
145
+ }
146
+
147
+ private fun convert16to32(data: ByteArray): ByteArray {
148
+ val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
149
+ val output = ByteBuffer.allocate(data.size * 2).order(ByteOrder.LITTLE_ENDIAN)
150
+
151
+ while (input.hasRemaining()) {
152
+ val sample16 = input.getShort()
153
+ // Scale 16-bit to 32-bit range
154
+ val sample32 = (sample16.toInt() shl 16)
155
+ output.putInt(sample32)
156
+ }
157
+ return output.array()
158
+ }
159
+
160
+ private fun convert32to16(data: ByteArray): ByteArray {
161
+ val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
162
+ val output = ByteBuffer.allocate(data.size / 2).order(ByteOrder.LITTLE_ENDIAN)
163
+
164
+ while (input.hasRemaining()) {
165
+ val sample32 = input.getInt()
166
+ // Scale 32-bit to 16-bit range
167
+ val sample16 = (sample32 shr 16).toShort()
168
+ output.putShort(sample16)
169
+ }
170
+ return output.array()
171
+ }
172
+
173
+ /**
174
+ * Convert between different channel configurations
175
+ */
176
+ fun convertChannels(data: ByteArray, fromChannels: Int, toChannels: Int, bitDepth: Int): ByteArray {
177
+ if (fromChannels == toChannels || data.isEmpty()) {
178
+ return data
179
+ }
180
+
181
+ val bytesPerSample = bitDepth / 8
182
+ val samplesPerFrame = fromChannels
183
+ val totalFrames = data.size / (bytesPerSample * samplesPerFrame)
184
+
185
+ return when {
186
+ fromChannels == 1 && toChannels == 2 -> monoToStereo(data, bitDepth, totalFrames)
187
+ fromChannels == 2 && toChannels == 1 -> stereoToMono(data, bitDepth, totalFrames)
188
+ else -> throw IllegalArgumentException("Unsupported channel conversion: $fromChannels to $toChannels")
189
+ }
190
+ }
191
+
192
+ private fun monoToStereo(data: ByteArray, bitDepth: Int, totalFrames: Int): ByteArray {
193
+ val bytesPerSample = bitDepth / 8
194
+ val output = ByteBuffer.allocate(data.size * 2).order(ByteOrder.LITTLE_ENDIAN)
195
+ val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
196
+
197
+ for (i in 0 until totalFrames) {
198
+ when (bitDepth) {
199
+ 16 -> {
200
+ val sample = input.getShort()
201
+ output.putShort(sample) // Left
202
+ output.putShort(sample) // Right
203
+ }
204
+ 32 -> {
205
+ val sample = input.getInt()
206
+ output.putInt(sample) // Left
207
+ output.putInt(sample) // Right
208
+ }
209
+ 8 -> {
210
+ val sample = input.get()
211
+ output.put(sample) // Left
212
+ output.put(sample) // Right
213
+ }
214
+ }
215
+ }
216
+ return output.array()
217
+ }
218
+
219
+ private fun stereoToMono(data: ByteArray, bitDepth: Int, totalFrames: Int): ByteArray {
220
+ val bytesPerSample = bitDepth / 8
221
+ val output = ByteBuffer.allocate(data.size / 2).order(ByteOrder.LITTLE_ENDIAN)
222
+ val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
223
+
224
+ for (i in 0 until totalFrames) {
225
+ when (bitDepth) {
226
+ 16 -> {
227
+ val left = input.getShort()
228
+ val right = input.getShort()
229
+ val mono = ((left + right) / 2).toShort()
230
+ output.putShort(mono)
231
+ }
232
+ 32 -> {
233
+ val left = input.getInt()
234
+ val right = input.getInt()
235
+ val mono = ((left.toLong() + right.toLong()) / 2).toInt()
236
+ output.putInt(mono)
237
+ }
238
+ 8 -> {
239
+ val left = input.get().toInt() and 0xFF
240
+ val right = input.get().toInt() and 0xFF
241
+ val mono = ((left + right) / 2).toByte()
242
+ output.put(mono)
243
+ }
244
+ }
245
+ }
246
+ return output.array()
247
+ }
248
+
249
+ /**
250
+ * Normalize audio to maximum amplitude
251
+ */
252
+ fun normalizeAudio(data: ByteArray, bitDepth: Int): ByteArray {
253
+ if (data.isEmpty()) return data
254
+
255
+ val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
256
+ var maxAmplitude = 0
257
+
258
+ // Find maximum amplitude
259
+ input.rewind()
260
+ when (bitDepth) {
261
+ 16 -> {
262
+ while (input.hasRemaining()) {
263
+ val sample = abs(input.getShort().toInt())
264
+ maxAmplitude = maxOf(maxAmplitude, sample)
265
+ }
266
+ }
267
+ 32 -> {
268
+ while (input.hasRemaining()) {
269
+ val sample = abs(input.getInt())
270
+ maxAmplitude = maxOf(maxAmplitude, sample)
271
+ }
272
+ }
273
+ 8 -> {
274
+ while (input.hasRemaining()) {
275
+ val sample = abs((input.get().toInt() and 0xFF) - 128)
276
+ maxAmplitude = maxOf(maxAmplitude, sample)
277
+ }
278
+ }
279
+ }
280
+
281
+ // If already at max or silent, return as is
282
+ if (maxAmplitude == 0) return data
283
+
284
+ val maxValue = when (bitDepth) {
285
+ 16 -> Short.MAX_VALUE.toInt()
286
+ 32 -> Int.MAX_VALUE
287
+ 8 -> 127
288
+ else -> throw IllegalArgumentException("Unsupported bit depth: $bitDepth")
289
+ }
290
+
291
+ if (maxAmplitude >= maxValue) return data
292
+
293
+ // Normalize
294
+ val scaleFactor = maxValue.toFloat() / maxAmplitude
295
+ val output = ByteBuffer.allocate(data.size).order(ByteOrder.LITTLE_ENDIAN)
296
+ input.rewind()
297
+
298
+ when (bitDepth) {
299
+ 16 -> {
300
+ while (input.hasRemaining()) {
301
+ val sample = input.getShort()
302
+ val normalized = (sample * scaleFactor).toInt().coerceIn(-32768, 32767).toShort()
303
+ output.putShort(normalized)
304
+ }
305
+ }
306
+ 32 -> {
307
+ while (input.hasRemaining()) {
308
+ val sample = input.getInt()
309
+ val normalized = (sample * scaleFactor).toLong().coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()
310
+ output.putInt(normalized)
311
+ }
312
+ }
313
+ 8 -> {
314
+ while (input.hasRemaining()) {
315
+ val sample = (input.get().toInt() and 0xFF) - 128
316
+ val normalized = ((sample * scaleFactor).toInt() + 128).coerceIn(0, 255).toByte()
317
+ output.put(normalized)
318
+ }
319
+ }
320
+ }
321
+
322
+ return output.array()
323
+ }
324
+
325
+ /**
326
+ * Resample audio data to a different sample rate
327
+ */
328
+ fun resampleAudio(samples: FloatArray, fromSampleRate: Int, toSampleRate: Int): FloatArray {
329
+ if (fromSampleRate == toSampleRate || samples.isEmpty()) {
330
+ return samples
331
+ }
332
+
333
+ val resampleRatio = toSampleRate.toDouble() / fromSampleRate
334
+ val newLength = (samples.size * resampleRatio).toInt()
335
+ val resampled = FloatArray(newLength)
336
+
337
+ for (i in resampled.indices) {
338
+ val sourceIndex = i / resampleRatio
339
+ val sourceIndexInt = sourceIndex.toInt()
340
+ val fraction = sourceIndex - sourceIndexInt
341
+
342
+ if (sourceIndexInt >= samples.size - 1) {
343
+ resampled[i] = samples.last()
344
+ } else {
345
+ // Linear interpolation
346
+ val sample1 = samples[sourceIndexInt]
347
+ val sample2 = samples[sourceIndexInt + 1]
348
+ resampled[i] = (sample1 * (1 - fraction) + sample2 * fraction).toFloat()
349
+ }
350
+ }
351
+
352
+ return resampled
353
+ }
354
+ }