@siteed/expo-audio-studio 2.18.5 → 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 (329) hide show
  1. package/README.md +13 -297
  2. package/index.d.ts +1 -0
  3. package/index.js +1 -0
  4. package/package.json +6 -135
  5. package/CHANGELOG.md +0 -493
  6. package/LICENSE +0 -21
  7. package/android/build.gradle +0 -129
  8. package/android/src/androidTest/assets/chorus.wav +0 -0
  9. package/android/src/androidTest/assets/jfk.wav +0 -0
  10. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  11. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  12. package/android/src/androidTest/java/net/siteed/audiostream/AudioProcessorInstrumentedTest.kt +0 -197
  13. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderInstrumentedTest.kt +0 -541
  14. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderPerformanceInstrumentedTest.kt +0 -234
  15. package/android/src/androidTest/java/net/siteed/audiostream/integration/AudioFocusStrategyIntegrationTest.kt +0 -332
  16. package/android/src/androidTest/java/net/siteed/audiostream/integration/BufferDurationIntegrationTest.kt +0 -324
  17. package/android/src/androidTest/java/net/siteed/audiostream/integration/CompressedOnlyOutputTest.kt +0 -253
  18. package/android/src/androidTest/java/net/siteed/audiostream/integration/DeviceDisconnectionFallbackTest.kt +0 -218
  19. package/android/src/androidTest/java/net/siteed/audiostream/integration/EventEmissionIntervalTest.kt +0 -120
  20. package/android/src/androidTest/java/net/siteed/audiostream/integration/M4aFormatTest.kt +0 -345
  21. package/android/src/androidTest/java/net/siteed/audiostream/integration/OutputControlIntegrationTest.kt +0 -340
  22. package/android/src/androidTest/java/net/siteed/audiostream/integration/PcmStreamingDurationTest.kt +0 -252
  23. package/android/src/androidTest/java/net/siteed/audiostream/integration/README.md +0 -95
  24. package/android/src/androidTest/java/net/siteed/audiostream/integration/run_integration_tests.sh +0 -43
  25. package/android/src/main/AndroidManifest.xml +0 -30
  26. package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +0 -188
  27. package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +0 -9
  28. package/android/src/main/java/net/siteed/audiostream/AudioDeviceManager.kt +0 -1741
  29. package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +0 -136
  30. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +0 -354
  31. package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +0 -439
  32. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +0 -2237
  33. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +0 -2141
  34. package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +0 -167
  35. package/android/src/main/java/net/siteed/audiostream/AudioTrimmer.kt +0 -1099
  36. package/android/src/main/java/net/siteed/audiostream/Constants.kt +0 -37
  37. package/android/src/main/java/net/siteed/audiostream/EventSender.kt +0 -7
  38. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +0 -1113
  39. package/android/src/main/java/net/siteed/audiostream/FFT.kt +0 -99
  40. package/android/src/main/java/net/siteed/audiostream/Features.kt +0 -98
  41. package/android/src/main/java/net/siteed/audiostream/LogUtils.kt +0 -93
  42. package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +0 -72
  43. package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +0 -68
  44. package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +0 -59
  45. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +0 -257
  46. package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +0 -19
  47. package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +0 -159
  48. package/android/src/main/res/drawable/ic_default_action_icon.xml +0 -16
  49. package/android/src/main/res/drawable/ic_microphone.xml +0 -13
  50. package/android/src/main/res/drawable/ic_pause.xml +0 -10
  51. package/android/src/main/res/drawable/ic_play.xml +0 -10
  52. package/android/src/main/res/drawable/ic_stop.xml +0 -10
  53. package/android/src/main/res/layout/notification_recording.xml +0 -37
  54. package/android/src/test/java/net/siteed/audiostream/AudioFileHandlerTest.kt +0 -279
  55. package/android/src/test/java/net/siteed/audiostream/AudioFocusStrategyTest.kt +0 -249
  56. package/android/src/test/java/net/siteed/audiostream/AudioFormatTest.kt +0 -151
  57. package/android/src/test/java/net/siteed/audiostream/AudioFormatUtilsTest.kt +0 -273
  58. package/android/src/test/java/net/siteed/audiostream/DeviceDisconnectionFallbackUnitTest.kt +0 -140
  59. package/android/src/test/resources/chorus.wav +0 -0
  60. package/android/src/test/resources/generate_test_audio.py +0 -94
  61. package/android/src/test/resources/jfk.wav +0 -0
  62. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  63. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  64. package/app.plugin.js +0 -3
  65. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +0 -4
  66. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  67. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +0 -210
  68. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  69. package/build/cjs/AudioAnalysis/extractAudioData.js +0 -21
  70. package/build/cjs/AudioAnalysis/extractAudioData.js.map +0 -1
  71. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +0 -92
  72. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  73. package/build/cjs/AudioAnalysis/extractPreview.js +0 -28
  74. package/build/cjs/AudioAnalysis/extractPreview.js.map +0 -1
  75. package/build/cjs/AudioAnalysis/extractWaveform.js +0 -18
  76. package/build/cjs/AudioAnalysis/extractWaveform.js.map +0 -1
  77. package/build/cjs/AudioDeviceManager.js +0 -689
  78. package/build/cjs/AudioDeviceManager.js.map +0 -1
  79. package/build/cjs/AudioRecorder.provider.js +0 -78
  80. package/build/cjs/AudioRecorder.provider.js.map +0 -1
  81. package/build/cjs/ExpoAudioStream.native.js +0 -8
  82. package/build/cjs/ExpoAudioStream.native.js.map +0 -1
  83. package/build/cjs/ExpoAudioStream.types.js +0 -11
  84. package/build/cjs/ExpoAudioStream.types.js.map +0 -1
  85. package/build/cjs/ExpoAudioStream.web.js +0 -708
  86. package/build/cjs/ExpoAudioStream.web.js.map +0 -1
  87. package/build/cjs/ExpoAudioStreamModule.js +0 -718
  88. package/build/cjs/ExpoAudioStreamModule.js.map +0 -1
  89. package/build/cjs/WebRecorder.web.js +0 -777
  90. package/build/cjs/WebRecorder.web.js.map +0 -1
  91. package/build/cjs/constants/platformLimitations.js +0 -99
  92. package/build/cjs/constants/platformLimitations.js.map +0 -1
  93. package/build/cjs/constants.js +0 -17
  94. package/build/cjs/constants.js.map +0 -1
  95. package/build/cjs/events.js +0 -29
  96. package/build/cjs/events.js.map +0 -1
  97. package/build/cjs/hooks/useAudioDevices.js +0 -179
  98. package/build/cjs/hooks/useAudioDevices.js.map +0 -1
  99. package/build/cjs/index.js +0 -58
  100. package/build/cjs/index.js.map +0 -1
  101. package/build/cjs/trimAudio.js +0 -76
  102. package/build/cjs/trimAudio.js.map +0 -1
  103. package/build/cjs/useAudioRecorder.js +0 -518
  104. package/build/cjs/useAudioRecorder.js.map +0 -1
  105. package/build/cjs/utils/BlobFix.js +0 -502
  106. package/build/cjs/utils/BlobFix.js.map +0 -1
  107. package/build/cjs/utils/audioProcessing.js +0 -136
  108. package/build/cjs/utils/audioProcessing.js.map +0 -1
  109. package/build/cjs/utils/cleanNativeOptions.js +0 -22
  110. package/build/cjs/utils/cleanNativeOptions.js.map +0 -1
  111. package/build/cjs/utils/concatenateBuffers.js +0 -25
  112. package/build/cjs/utils/concatenateBuffers.js.map +0 -1
  113. package/build/cjs/utils/convertPCMToFloat32.js +0 -124
  114. package/build/cjs/utils/convertPCMToFloat32.js.map +0 -1
  115. package/build/cjs/utils/crc32.js +0 -52
  116. package/build/cjs/utils/crc32.js.map +0 -1
  117. package/build/cjs/utils/encodingToBitDepth.js +0 -17
  118. package/build/cjs/utils/encodingToBitDepth.js.map +0 -1
  119. package/build/cjs/utils/getWavFileInfo.js +0 -96
  120. package/build/cjs/utils/getWavFileInfo.js.map +0 -1
  121. package/build/cjs/utils/writeWavHeader.js +0 -88
  122. package/build/cjs/utils/writeWavHeader.js.map +0 -1
  123. package/build/cjs/workers/InlineFeaturesExtractor.web.js +0 -859
  124. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +0 -1
  125. package/build/cjs/workers/inlineAudioWebWorker.web.js +0 -184
  126. package/build/cjs/workers/inlineAudioWebWorker.web.js.map +0 -1
  127. package/build/esm/AudioAnalysis/AudioAnalysis.types.js +0 -3
  128. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  129. package/build/esm/AudioAnalysis/extractAudioAnalysis.js +0 -202
  130. package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  131. package/build/esm/AudioAnalysis/extractAudioData.js +0 -14
  132. package/build/esm/AudioAnalysis/extractAudioData.js.map +0 -1
  133. package/build/esm/AudioAnalysis/extractMelSpectrogram.js +0 -89
  134. package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  135. package/build/esm/AudioAnalysis/extractPreview.js +0 -25
  136. package/build/esm/AudioAnalysis/extractPreview.js.map +0 -1
  137. package/build/esm/AudioAnalysis/extractWaveform.js +0 -11
  138. package/build/esm/AudioAnalysis/extractWaveform.js.map +0 -1
  139. package/build/esm/AudioDeviceManager.js +0 -682
  140. package/build/esm/AudioDeviceManager.js.map +0 -1
  141. package/build/esm/AudioRecorder.provider.js +0 -40
  142. package/build/esm/AudioRecorder.provider.js.map +0 -1
  143. package/build/esm/ExpoAudioStream.native.js +0 -6
  144. package/build/esm/ExpoAudioStream.native.js.map +0 -1
  145. package/build/esm/ExpoAudioStream.types.js +0 -8
  146. package/build/esm/ExpoAudioStream.types.js.map +0 -1
  147. package/build/esm/ExpoAudioStream.web.js +0 -704
  148. package/build/esm/ExpoAudioStream.web.js.map +0 -1
  149. package/build/esm/ExpoAudioStreamModule.js +0 -713
  150. package/build/esm/ExpoAudioStreamModule.js.map +0 -1
  151. package/build/esm/WebRecorder.web.js +0 -773
  152. package/build/esm/WebRecorder.web.js.map +0 -1
  153. package/build/esm/constants/platformLimitations.js +0 -90
  154. package/build/esm/constants/platformLimitations.js.map +0 -1
  155. package/build/esm/constants.js +0 -14
  156. package/build/esm/constants.js.map +0 -1
  157. package/build/esm/events.js +0 -21
  158. package/build/esm/events.js.map +0 -1
  159. package/build/esm/hooks/useAudioDevices.js +0 -176
  160. package/build/esm/hooks/useAudioDevices.js.map +0 -1
  161. package/build/esm/index.js +0 -20
  162. package/build/esm/index.js.map +0 -1
  163. package/build/esm/trimAudio.js +0 -69
  164. package/build/esm/trimAudio.js.map +0 -1
  165. package/build/esm/useAudioRecorder.js +0 -512
  166. package/build/esm/useAudioRecorder.js.map +0 -1
  167. package/build/esm/utils/BlobFix.js +0 -498
  168. package/build/esm/utils/BlobFix.js.map +0 -1
  169. package/build/esm/utils/audioProcessing.js +0 -133
  170. package/build/esm/utils/audioProcessing.js.map +0 -1
  171. package/build/esm/utils/cleanNativeOptions.js +0 -19
  172. package/build/esm/utils/cleanNativeOptions.js.map +0 -1
  173. package/build/esm/utils/concatenateBuffers.js +0 -21
  174. package/build/esm/utils/concatenateBuffers.js.map +0 -1
  175. package/build/esm/utils/convertPCMToFloat32.js +0 -120
  176. package/build/esm/utils/convertPCMToFloat32.js.map +0 -1
  177. package/build/esm/utils/crc32.js +0 -50
  178. package/build/esm/utils/crc32.js.map +0 -1
  179. package/build/esm/utils/encodingToBitDepth.js +0 -13
  180. package/build/esm/utils/encodingToBitDepth.js.map +0 -1
  181. package/build/esm/utils/getWavFileInfo.js +0 -92
  182. package/build/esm/utils/getWavFileInfo.js.map +0 -1
  183. package/build/esm/utils/writeWavHeader.js +0 -84
  184. package/build/esm/utils/writeWavHeader.js.map +0 -1
  185. package/build/esm/workers/InlineFeaturesExtractor.web.js +0 -856
  186. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +0 -1
  187. package/build/esm/workers/inlineAudioWebWorker.web.js +0 -181
  188. package/build/esm/workers/inlineAudioWebWorker.web.js.map +0 -1
  189. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +0 -196
  190. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  191. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts +0 -74
  192. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  193. package/build/types/AudioAnalysis/extractAudioData.d.ts +0 -3
  194. package/build/types/AudioAnalysis/extractAudioData.d.ts.map +0 -1
  195. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts +0 -14
  196. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
  197. package/build/types/AudioAnalysis/extractPreview.d.ts +0 -11
  198. package/build/types/AudioAnalysis/extractPreview.d.ts.map +0 -1
  199. package/build/types/AudioAnalysis/extractWaveform.d.ts +0 -8
  200. package/build/types/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  201. package/build/types/AudioDeviceManager.d.ts +0 -187
  202. package/build/types/AudioDeviceManager.d.ts.map +0 -1
  203. package/build/types/AudioRecorder.provider.d.ts +0 -11
  204. package/build/types/AudioRecorder.provider.d.ts.map +0 -1
  205. package/build/types/ExpoAudioStream.native.d.ts +0 -3
  206. package/build/types/ExpoAudioStream.native.d.ts.map +0 -1
  207. package/build/types/ExpoAudioStream.types.d.ts +0 -738
  208. package/build/types/ExpoAudioStream.types.d.ts.map +0 -1
  209. package/build/types/ExpoAudioStream.web.d.ts +0 -96
  210. package/build/types/ExpoAudioStream.web.d.ts.map +0 -1
  211. package/build/types/ExpoAudioStreamModule.d.ts +0 -3
  212. package/build/types/ExpoAudioStreamModule.d.ts.map +0 -1
  213. package/build/types/WebRecorder.web.d.ts +0 -198
  214. package/build/types/WebRecorder.web.d.ts.map +0 -1
  215. package/build/types/constants/platformLimitations.d.ts +0 -40
  216. package/build/types/constants/platformLimitations.d.ts.map +0 -1
  217. package/build/types/constants.d.ts +0 -11
  218. package/build/types/constants.d.ts.map +0 -1
  219. package/build/types/events.d.ts +0 -26
  220. package/build/types/events.d.ts.map +0 -1
  221. package/build/types/hooks/useAudioDevices.d.ts +0 -15
  222. package/build/types/hooks/useAudioDevices.d.ts.map +0 -1
  223. package/build/types/index.d.ts +0 -18
  224. package/build/types/index.d.ts.map +0 -1
  225. package/build/types/trimAudio.d.ts +0 -25
  226. package/build/types/trimAudio.d.ts.map +0 -1
  227. package/build/types/useAudioRecorder.d.ts +0 -22
  228. package/build/types/useAudioRecorder.d.ts.map +0 -1
  229. package/build/types/utils/BlobFix.d.ts +0 -9
  230. package/build/types/utils/BlobFix.d.ts.map +0 -1
  231. package/build/types/utils/audioProcessing.d.ts +0 -24
  232. package/build/types/utils/audioProcessing.d.ts.map +0 -1
  233. package/build/types/utils/cleanNativeOptions.d.ts +0 -15
  234. package/build/types/utils/cleanNativeOptions.d.ts.map +0 -1
  235. package/build/types/utils/concatenateBuffers.d.ts +0 -8
  236. package/build/types/utils/concatenateBuffers.d.ts.map +0 -1
  237. package/build/types/utils/convertPCMToFloat32.d.ts +0 -13
  238. package/build/types/utils/convertPCMToFloat32.d.ts.map +0 -1
  239. package/build/types/utils/crc32.d.ts +0 -7
  240. package/build/types/utils/crc32.d.ts.map +0 -1
  241. package/build/types/utils/encodingToBitDepth.d.ts +0 -5
  242. package/build/types/utils/encodingToBitDepth.d.ts.map +0 -1
  243. package/build/types/utils/getWavFileInfo.d.ts +0 -26
  244. package/build/types/utils/getWavFileInfo.d.ts.map +0 -1
  245. package/build/types/utils/writeWavHeader.d.ts +0 -34
  246. package/build/types/utils/writeWavHeader.d.ts.map +0 -1
  247. package/build/types/workers/InlineFeaturesExtractor.web.d.ts +0 -2
  248. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  249. package/build/types/workers/inlineAudioWebWorker.web.d.ts +0 -2
  250. package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  251. package/expo-module.config.json +0 -10
  252. package/ios/AudioAnalysisData.swift +0 -74
  253. package/ios/AudioDeviceManager.swift +0 -666
  254. package/ios/AudioNotificationManager.swift +0 -154
  255. package/ios/AudioProcessingHelpers.swift +0 -743
  256. package/ios/AudioProcessor.swift +0 -1151
  257. package/ios/AudioStreamError.swift +0 -7
  258. package/ios/AudioStreamManager.swift +0 -2352
  259. package/ios/AudioStreamManagerDelegate.swift +0 -16
  260. package/ios/DataPoint.swift +0 -54
  261. package/ios/DecodingConfig.swift +0 -59
  262. package/ios/ExpoAudioStream.podspec +0 -33
  263. package/ios/ExpoAudioStreamModule.swift +0 -1013
  264. package/ios/ExpoAudioStudioTests/AudioFileHandlerTests.swift +0 -338
  265. package/ios/ExpoAudioStudioTests/AudioFormatUtilsTests.swift +0 -331
  266. package/ios/ExpoAudioStudioTests/AudioTestHelpers.swift +0 -130
  267. package/ios/ExpoAudioStudioTests/CompressedOnlyOutputTests.swift +0 -294
  268. package/ios/ExpoAudioStudioTests/EventEmissionIntervalTests.swift +0 -105
  269. package/ios/ExpoAudioStudioTests/Info.plist +0 -22
  270. package/ios/ExpoAudioStudioTests/README.md +0 -39
  271. package/ios/ExpoAudioStudioTests/SimpleAudioTest.swift +0 -98
  272. package/ios/ExpoAudioStudioTests/TestAudioGenerator.swift +0 -75
  273. package/ios/FFT.swift +0 -62
  274. package/ios/Features.swift +0 -95
  275. package/ios/ISSUE_IOS.md +0 -68
  276. package/ios/Logger.swift +0 -39
  277. package/ios/NotificationExtension.swift +0 -15
  278. package/ios/RecordingResult.swift +0 -22
  279. package/ios/RecordingSettings.swift +0 -308
  280. package/ios/WaveformExtractor.swift +0 -105
  281. package/ios/tests/README.md +0 -41
  282. package/ios/tests/integration/buffer_and_fallback_test.swift +0 -178
  283. package/ios/tests/integration/buffer_duration_test.swift +0 -185
  284. package/ios/tests/integration/compressed_only_output_test.swift +0 -271
  285. package/ios/tests/integration/output_control_test.swift +0 -322
  286. package/ios/tests/integration/run_integration_tests.sh +0 -37
  287. package/ios/tests/opus_support_test_macos.swift +0 -154
  288. package/ios/tests/standalone/audio_processing_test.swift +0 -144
  289. package/ios/tests/standalone/audio_recording_test.swift +0 -277
  290. package/ios/tests/standalone/audio_streaming_test.swift +0 -249
  291. package/ios/tests/standalone/standalone_test.swift +0 -144
  292. package/plugin/build/index.cjs +0 -194
  293. package/plugin/build/index.d.cts +0 -22
  294. package/plugin/build/index.js +0 -194
  295. package/plugin/src/index.ts +0 -285
  296. package/plugin/tsconfig.json +0 -10
  297. package/plugin/tsconfig.tsbuildinfo +0 -1
  298. package/src/AudioAnalysis/AudioAnalysis.types.ts +0 -224
  299. package/src/AudioAnalysis/extractAudioAnalysis.ts +0 -344
  300. package/src/AudioAnalysis/extractAudioData.ts +0 -17
  301. package/src/AudioAnalysis/extractMelSpectrogram.ts +0 -154
  302. package/src/AudioAnalysis/extractPreview.ts +0 -34
  303. package/src/AudioAnalysis/extractWaveform.ts +0 -22
  304. package/src/AudioDeviceManager.ts +0 -803
  305. package/src/AudioRecorder.provider.tsx +0 -57
  306. package/src/ExpoAudioStream.native.ts +0 -6
  307. package/src/ExpoAudioStream.types.ts +0 -874
  308. package/src/ExpoAudioStream.web.ts +0 -905
  309. package/src/ExpoAudioStreamModule.ts +0 -990
  310. package/src/WebRecorder.web.ts +0 -1005
  311. package/src/constants/platformLimitations.ts +0 -118
  312. package/src/constants.ts +0 -18
  313. package/src/events.ts +0 -60
  314. package/src/hooks/useAudioDevices.ts +0 -213
  315. package/src/index.ts +0 -54
  316. package/src/trimAudio.ts +0 -94
  317. package/src/types/crc-32.d.ts +0 -9
  318. package/src/useAudioRecorder.tsx +0 -766
  319. package/src/utils/BlobFix.ts +0 -561
  320. package/src/utils/audioProcessing.ts +0 -205
  321. package/src/utils/cleanNativeOptions.ts +0 -18
  322. package/src/utils/concatenateBuffers.ts +0 -24
  323. package/src/utils/convertPCMToFloat32.ts +0 -170
  324. package/src/utils/crc32.ts +0 -59
  325. package/src/utils/encodingToBitDepth.ts +0 -18
  326. package/src/utils/getWavFileInfo.ts +0 -132
  327. package/src/utils/writeWavHeader.ts +0 -115
  328. package/src/workers/InlineFeaturesExtractor.web.tsx +0 -855
  329. package/src/workers/inlineAudioWebWorker.web.tsx +0 -180
@@ -1,990 +0,0 @@
1
- import { requireNativeModule } from 'expo-modules-core'
2
- import { Platform } from 'react-native'
3
-
4
- import {
5
- ExtractAudioDataOptions,
6
- ExtractedAudioData,
7
- BitDepth,
8
- TrimAudioOptions,
9
- TrimAudioResult,
10
- } from './ExpoAudioStream.types'
11
- import {
12
- ExpoAudioStreamWeb,
13
- ExpoAudioStreamWebProps,
14
- } from './ExpoAudioStream.web'
15
- import { processAudioBuffer } from './utils/audioProcessing'
16
- import crc32 from './utils/crc32'
17
- import { writeWavHeader } from './utils/writeWavHeader'
18
-
19
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
- let ExpoAudioStreamModule: any
21
-
22
- if (Platform.OS === 'web') {
23
- let instance: ExpoAudioStreamWeb | null = null
24
-
25
- ExpoAudioStreamModule = (webProps: ExpoAudioStreamWebProps) => {
26
- if (!instance) {
27
- instance = new ExpoAudioStreamWeb(webProps)
28
- }
29
- return instance
30
- }
31
- ExpoAudioStreamModule.requestPermissionsAsync = async () => {
32
- try {
33
- const stream = await navigator.mediaDevices.getUserMedia({
34
- audio: true,
35
- })
36
- stream.getTracks().forEach((track) => track.stop())
37
- return {
38
- status: 'granted',
39
- expires: 'never',
40
- canAskAgain: true,
41
- granted: true,
42
- }
43
- } catch {
44
- return {
45
- status: 'denied',
46
- expires: 'never',
47
- canAskAgain: true,
48
- granted: false,
49
- }
50
- }
51
- }
52
- ExpoAudioStreamModule.getPermissionsAsync = async () => {
53
- let maybeStatus: string | null = null
54
-
55
- if (navigator?.permissions?.query) {
56
- try {
57
- const { state } = await navigator.permissions.query({
58
- name: 'microphone' as PermissionName,
59
- })
60
- maybeStatus = state
61
- } catch {
62
- maybeStatus = null
63
- }
64
- }
65
-
66
- switch (maybeStatus) {
67
- case 'granted':
68
- return {
69
- status: 'granted',
70
- expires: 'never',
71
- canAskAgain: true,
72
- granted: true,
73
- }
74
- case 'denied':
75
- return {
76
- status: 'denied',
77
- expires: 'never',
78
- canAskAgain: true,
79
- granted: false,
80
- }
81
- default:
82
- return await ExpoAudioStreamModule.requestPermissionsAsync()
83
- }
84
- }
85
- ExpoAudioStreamModule.extractAudioData = async (
86
- options: ExtractAudioDataOptions
87
- ): Promise<ExtractedAudioData> => {
88
- try {
89
- const {
90
- fileUri,
91
- position,
92
- length,
93
- startTimeMs,
94
- endTimeMs,
95
- decodingOptions,
96
- includeNormalizedData,
97
- includeBase64Data,
98
- includeWavHeader = false,
99
- logger,
100
- } = options
101
-
102
- logger?.debug('EXTRACT AUDIO - Step 1: Initial request', {
103
- fileUri,
104
- extractionParams: {
105
- position,
106
- length,
107
- startTimeMs,
108
- endTimeMs,
109
- },
110
- decodingOptions: {
111
- targetSampleRate:
112
- decodingOptions?.targetSampleRate ?? 16000,
113
- targetChannels: decodingOptions?.targetChannels ?? 1,
114
- targetBitDepth: decodingOptions?.targetBitDepth ?? 16,
115
- normalizeAudio: decodingOptions?.normalizeAudio ?? false,
116
- },
117
- outputOptions: {
118
- includeNormalizedData,
119
- includeBase64Data,
120
- includeWavHeader,
121
- },
122
- })
123
-
124
- // Process the audio using shared helper function
125
- const processedBuffer = await processAudioBuffer({
126
- fileUri,
127
- targetSampleRate: decodingOptions?.targetSampleRate ?? 16000,
128
- targetChannels: decodingOptions?.targetChannels ?? 1,
129
- normalizeAudio: decodingOptions?.normalizeAudio ?? false,
130
- position,
131
- length,
132
- startTimeMs,
133
- endTimeMs,
134
- logger,
135
- })
136
-
137
- logger?.debug('EXTRACT AUDIO - Step 2: Audio processing complete', {
138
- processedData: {
139
- samples: processedBuffer.samples,
140
- sampleRate: processedBuffer.sampleRate,
141
- channels: processedBuffer.channels,
142
- durationMs: processedBuffer.durationMs,
143
- },
144
- })
145
-
146
- const channelData = processedBuffer.channelData
147
- const bitDepth = (decodingOptions?.targetBitDepth ?? 16) as BitDepth
148
- const bytesPerSample = bitDepth / 8
149
- const numSamples = processedBuffer.samples
150
-
151
- logger?.debug('EXTRACT AUDIO - Step 3: PCM conversion setup', {
152
- channelData: {
153
- length: channelData.length,
154
- first: channelData[0],
155
- last: channelData[channelData.length - 1],
156
- },
157
- calculation: {
158
- bitDepth,
159
- bytesPerSample,
160
- numSamples,
161
- expectedBytes: numSamples * bytesPerSample,
162
- },
163
- })
164
-
165
- // Create PCM data with correct length based on original byte length
166
- const pcmData = new Uint8Array(numSamples * bytesPerSample)
167
- let offset = 0
168
-
169
- // Convert Float32 samples to PCM format
170
- for (let i = 0; i < numSamples; i++) {
171
- const sample = channelData[i]
172
- const value = Math.max(-1, Math.min(1, sample))
173
- // Convert to 16-bit signed integer
174
- let intValue = Math.round(value * 32767)
175
-
176
- // Handle negative values correctly
177
- if (intValue < 0) {
178
- intValue = 65536 + intValue
179
- }
180
-
181
- // Write as little-endian
182
- pcmData[offset++] = intValue & 255 // Low byte
183
- pcmData[offset++] = (intValue >> 8) & 255 // High byte
184
- }
185
-
186
- const durationMs = Math.round(
187
- (numSamples / processedBuffer.sampleRate) * 1000
188
- )
189
-
190
- logger?.debug('EXTRACT AUDIO - Step 4: Final output', {
191
- pcmData: {
192
- length: pcmData.length,
193
- first: pcmData[0],
194
- last: pcmData[pcmData.length - 1],
195
- },
196
- timing: {
197
- numSamples,
198
- sampleRate: processedBuffer.sampleRate,
199
- durationMs,
200
- shouldBe3000ms: endTimeMs
201
- ? endTimeMs - (startTimeMs ?? 0) === 3000
202
- : undefined,
203
- },
204
- })
205
-
206
- const result: ExtractedAudioData = {
207
- pcmData: new Uint8Array(pcmData.buffer),
208
- sampleRate: processedBuffer.sampleRate,
209
- channels: processedBuffer.channels,
210
- bitDepth,
211
- durationMs,
212
- format: `pcm_${bitDepth}bit` as const,
213
- samples: numSamples,
214
- }
215
-
216
- // Add WAV header if requested
217
- if (includeWavHeader) {
218
- logger?.debug('EXTRACT AUDIO - Step 4: Adding WAV header', {
219
- originalLength: pcmData.length,
220
- newLength: result.pcmData.length,
221
- firstBytes: Array.from(result.pcmData.slice(0, 44)), // WAV header is 44 bytes
222
- })
223
- const wavBuffer = writeWavHeader({
224
- buffer: pcmData.buffer.slice(0, pcmData.length),
225
- sampleRate: processedBuffer.sampleRate,
226
- numChannels: processedBuffer.channels,
227
- bitDepth,
228
- })
229
- result.pcmData = new Uint8Array(wavBuffer)
230
- result.hasWavHeader = true
231
- }
232
-
233
- if (includeNormalizedData) {
234
- // // Simple approach: Create normalized data directly from the PCM data
235
- // // Just convert to -1 to 1 range without any amplification
236
- // const normalizedData = new Float32Array(numSamples)
237
-
238
- // // Convert the PCM data to float values
239
- // for (let i = 0; i < numSamples; i++) {
240
- // // Get the 16-bit PCM value (little endian)
241
- // const lowByte = pcmData[i * 2]
242
- // const highByte = pcmData[i * 2 + 1]
243
- // const pcmValue = (highByte << 8) | lowByte
244
-
245
- // // Convert to signed 16-bit value
246
- // const signedValue =
247
- // pcmValue > 32767 ? pcmValue - 65536 : pcmValue
248
-
249
- // // Normalize to float between -1 and 1
250
- // normalizedData[i] = signedValue / 32768.0
251
- // }
252
- // Store the normalized data in the result
253
- result.normalizedData = channelData
254
- }
255
-
256
- if (includeBase64Data) {
257
- // Convert the PCM data to a base64 string
258
- const binary = Array.from(new Uint8Array(pcmData.buffer))
259
- .map((b) => String.fromCharCode(b))
260
- .join('')
261
- result.base64Data = btoa(binary)
262
- }
263
-
264
- if (options.computeChecksum) {
265
- result.checksum = crc32.buf(pcmData)
266
- }
267
-
268
- logger?.debug('EXTRACT AUDIO - Step 3: PCM conversion complete', {
269
- pcmStats: {
270
- length: pcmData.length,
271
- bytesPerSample,
272
- totalSamples: numSamples,
273
- firstBytes: Array.from(pcmData.slice(0, 16)),
274
- lastBytes: Array.from(pcmData.slice(-16)),
275
- },
276
- })
277
-
278
- return result
279
- } catch (error) {
280
- options.logger?.error('EXTRACT AUDIO - Error:', error)
281
- throw error
282
- }
283
- }
284
-
285
- ExpoAudioStreamModule.trimAudio = async (
286
- options: TrimAudioOptions
287
- ): Promise<TrimAudioResult> => {
288
- try {
289
- const startTime = performance.now()
290
- const {
291
- fileUri,
292
- mode = 'single',
293
- startTimeMs,
294
- endTimeMs,
295
- ranges,
296
- outputFileName,
297
- outputFormat,
298
- } = options
299
-
300
- // Validate inputs
301
- if (!fileUri) {
302
- throw new Error('fileUri is required')
303
- }
304
-
305
- if (
306
- mode === 'single' &&
307
- startTimeMs === undefined &&
308
- endTimeMs === undefined
309
- ) {
310
- throw new Error(
311
- 'At least one of startTimeMs or endTimeMs must be provided in single mode'
312
- )
313
- }
314
-
315
- if (
316
- (mode === 'keep' || mode === 'remove') &&
317
- (!ranges || ranges.length === 0)
318
- ) {
319
- throw new Error(
320
- 'ranges must be provided and non-empty for keep or remove modes'
321
- )
322
- }
323
-
324
- // Create AudioContext
325
- const audioContext = new (window.AudioContext ||
326
- (window as any).webkitAudioContext)()
327
-
328
- // First, load the entire audio file to get its properties
329
- const response = await fetch(fileUri)
330
- const arrayBuffer = await response.arrayBuffer()
331
- const originalAudioBuffer =
332
- await audioContext.decodeAudioData(arrayBuffer)
333
-
334
- // Get original audio properties
335
- const originalSampleRate = originalAudioBuffer.sampleRate
336
- const originalChannels = originalAudioBuffer.numberOfChannels
337
-
338
- // Add more detailed logging
339
- console.log(`Original audio details:`, {
340
- sampleRate: originalSampleRate,
341
- channels: originalChannels,
342
- duration: originalAudioBuffer.duration,
343
- length: originalAudioBuffer.length,
344
- // Log a few samples to verify content
345
- firstSamples: Array.from(
346
- originalAudioBuffer.getChannelData(0).slice(0, 5)
347
- ),
348
- })
349
-
350
- // Determine output format - use original values as defaults if not specified
351
- let format = outputFormat?.format || 'wav'
352
- const targetSampleRate =
353
- outputFormat?.sampleRate || originalSampleRate
354
- const targetChannels = outputFormat?.channels || originalChannels
355
- const targetBitDepth = outputFormat?.bitDepth || 16
356
-
357
- // Get file info from the URL
358
- const filename =
359
- outputFileName ||
360
- fileUri.split('/').pop() ||
361
- 'trimmed-audio.wav'
362
-
363
- // Process based on mode
364
- let resultBuffer: AudioBuffer
365
-
366
- // Report initial progress
367
- ExpoAudioStreamModule.sendEvent('TrimProgress', {
368
- progress: 10,
369
- })
370
-
371
- if (mode === 'single') {
372
- // Single mode: extract a single range
373
- // Use original sample rate and channels for extraction to preserve quality
374
- const { buffer } = await processAudioBuffer({
375
- fileUri,
376
- targetSampleRate, // Use the requested sample rate
377
- targetChannels,
378
- normalizeAudio: false,
379
- startTimeMs,
380
- endTimeMs,
381
- audioContext,
382
- })
383
-
384
- console.log(`Processed buffer details:`, {
385
- sampleRate: buffer.sampleRate,
386
- channels: buffer.numberOfChannels,
387
- duration: buffer.duration,
388
- length: buffer.length,
389
- // Log a few samples to verify content
390
- firstSamples: Array.from(
391
- buffer.getChannelData(0).slice(0, 5)
392
- ),
393
- })
394
-
395
- resultBuffer = buffer
396
-
397
- // If we need to change sample rate or channels, do it after extraction
398
- if (
399
- targetSampleRate !== originalSampleRate ||
400
- targetChannels !== originalChannels
401
- ) {
402
- console.log(
403
- `Resampling from ${originalSampleRate}Hz to ${targetSampleRate}Hz`
404
- )
405
- resultBuffer = await resampleAudioBuffer(
406
- audioContext,
407
- buffer,
408
- targetSampleRate,
409
- targetChannels
410
- )
411
- }
412
- } else {
413
- // For keep or remove modes
414
- const fullDuration = originalAudioBuffer.duration * 1000 // in ms
415
-
416
- type ProcessSegment = {
417
- startTimeMs: number
418
- endTimeMs: number
419
- }
420
-
421
- let segmentsToProcess: ProcessSegment[] = []
422
-
423
- if (mode === 'keep') {
424
- // For keep mode, use the ranges directly
425
- segmentsToProcess = ranges!
426
- } else {
427
- // mode === 'remove'
428
- // For remove mode, invert the ranges
429
- const sortedRanges = [...ranges!].sort(
430
- (a, b) => a.startTimeMs - b.startTimeMs
431
- )
432
-
433
- // Add segment from start to first range if needed
434
- if (
435
- sortedRanges.length > 0 &&
436
- sortedRanges[0].startTimeMs > 0
437
- ) {
438
- segmentsToProcess.push({
439
- startTimeMs: 0,
440
- endTimeMs: sortedRanges[0].startTimeMs,
441
- })
442
- }
443
-
444
- // Add segments between ranges
445
- for (let i = 0; i < sortedRanges.length - 1; i++) {
446
- segmentsToProcess.push({
447
- startTimeMs: sortedRanges[i].endTimeMs,
448
- endTimeMs: sortedRanges[i + 1].startTimeMs,
449
- })
450
- }
451
-
452
- // Add segment from last range to end if needed
453
- if (
454
- sortedRanges.length > 0 &&
455
- sortedRanges[sortedRanges.length - 1].endTimeMs <
456
- fullDuration
457
- ) {
458
- segmentsToProcess.push({
459
- startTimeMs:
460
- sortedRanges[sortedRanges.length - 1].endTimeMs,
461
- endTimeMs: fullDuration,
462
- })
463
- }
464
- }
465
-
466
- // Filter out empty or invalid segments
467
- segmentsToProcess = segmentsToProcess.filter(
468
- (segment) =>
469
- segment.startTimeMs < segment.endTimeMs &&
470
- segment.endTimeMs - segment.startTimeMs > 1
471
- ) // 1ms minimum
472
-
473
- if (segmentsToProcess.length === 0) {
474
- throw new Error(
475
- 'No valid segments to process after filtering ranges'
476
- )
477
- }
478
-
479
- // Process each segment using original sample rate and channels
480
- const segmentBuffers: AudioBuffer[] = []
481
-
482
- for (let i = 0; i < segmentsToProcess.length; i++) {
483
- const segment = segmentsToProcess[i]
484
-
485
- // Report progress for each segment
486
- ExpoAudioStreamModule.sendEvent('TrimProgress', {
487
- progress:
488
- 10 +
489
- Math.round((i / segmentsToProcess.length) * 40),
490
- })
491
-
492
- // Use processAudioBuffer to extract this segment
493
- const { buffer: segmentBuffer } = await processAudioBuffer({
494
- fileUri,
495
- targetSampleRate: originalSampleRate, // Use original sample rate
496
- targetChannels: originalChannels, // Use original channels
497
- normalizeAudio: false,
498
- startTimeMs: segment.startTimeMs,
499
- endTimeMs: segment.endTimeMs,
500
- audioContext,
501
- })
502
-
503
- segmentBuffers.push(segmentBuffer)
504
- }
505
-
506
- // Concatenate all segments
507
- const totalSamples = segmentBuffers.reduce(
508
- (sum, buffer) => sum + buffer.length,
509
- 0
510
- )
511
-
512
- // Create buffer with original properties first
513
- const concatenatedBuffer = audioContext.createBuffer(
514
- originalChannels,
515
- totalSamples,
516
- originalSampleRate
517
- )
518
-
519
- let offset = 0
520
- for (const segmentBuffer of segmentBuffers) {
521
- for (
522
- let channel = 0;
523
- channel < originalChannels;
524
- channel++
525
- ) {
526
- const outputData =
527
- concatenatedBuffer.getChannelData(channel)
528
- const segmentData =
529
- segmentBuffer.getChannelData(channel)
530
-
531
- for (let i = 0; i < segmentBuffer.length; i++) {
532
- outputData[offset + i] = segmentData[i]
533
- }
534
- }
535
- offset += segmentBuffer.length
536
- }
537
-
538
- resultBuffer = concatenatedBuffer
539
-
540
- // If we need to change sample rate or channels, do it after concatenation
541
- if (
542
- targetSampleRate !== originalSampleRate ||
543
- targetChannels !== originalChannels
544
- ) {
545
- console.log(
546
- `Resampling concatenated buffer from ${originalSampleRate}Hz to ${targetSampleRate}Hz`
547
- )
548
- resultBuffer = await resampleAudioBuffer(
549
- audioContext,
550
- concatenatedBuffer,
551
- targetSampleRate,
552
- targetChannels
553
- )
554
- }
555
- }
556
-
557
- // Report progress (50% - processing complete)
558
- ExpoAudioStreamModule.sendEvent('TrimProgress', {
559
- progress: 50,
560
- })
561
-
562
- // Encode the result based on the requested format
563
- let outputData: ArrayBuffer
564
- let outputMimeType: string
565
- let compressionInfo: any = null
566
-
567
- // Check if AAC was requested on web and show a warning
568
- if (format === 'aac' && Platform.OS === 'web') {
569
- console.warn(
570
- 'AAC format is not supported on web platforms. Falling back to OPUS format.'
571
- )
572
- format = 'opus'
573
- }
574
-
575
- if (format === 'wav') {
576
- // Create a properly interleaved buffer for WAV format
577
- // For WAV, we need to convert Float32Array to Int16Array (for 16-bit audio)
578
- const numSamples =
579
- resultBuffer.length * resultBuffer.numberOfChannels
580
- const interleavedData = new Int16Array(numSamples)
581
-
582
- // Log detailed information about the buffer before encoding
583
- console.log(`Creating WAV file:`, {
584
- bufferSampleRate: resultBuffer.sampleRate,
585
- bufferChannels: resultBuffer.numberOfChannels,
586
- bufferLength: resultBuffer.length,
587
- targetSampleRate,
588
- targetChannels,
589
- targetBitDepth,
590
- // Log a few samples to verify content
591
- firstSamples: Array.from(
592
- resultBuffer.getChannelData(0).slice(0, 5)
593
- ),
594
- })
595
-
596
- // Interleave channels properly
597
- for (let i = 0; i < resultBuffer.length; i++) {
598
- for (
599
- let channel = 0;
600
- channel < resultBuffer.numberOfChannels;
601
- channel++
602
- ) {
603
- // Convert float (-1.0 to 1.0) to int16 (-32768 to 32767)
604
- const floatSample =
605
- resultBuffer.getChannelData(channel)[i]
606
- // Clamp the value to -1.0 to 1.0
607
- const clampedSample = Math.max(
608
- -1.0,
609
- Math.min(1.0, floatSample)
610
- )
611
- // Convert to int16
612
- const intSample = Math.round(clampedSample * 32767)
613
- // Store in interleaved buffer
614
- interleavedData[
615
- i * resultBuffer.numberOfChannels + channel
616
- ] = intSample
617
- }
618
- }
619
-
620
- // Convert Int16Array to ArrayBuffer for WAV header
621
- const rawBuffer = interleavedData.buffer
622
-
623
- // IMPORTANT: Make sure we're using the ACTUAL sample rate of the buffer
624
- // not just what was requested in the options
625
- console.log(
626
- `Creating WAV with ${resultBuffer.numberOfChannels} channels at ${resultBuffer.sampleRate}Hz`
627
- )
628
-
629
- outputData = writeWavHeader({
630
- buffer: rawBuffer as ArrayBuffer,
631
- sampleRate: resultBuffer.sampleRate, // Use the actual buffer's sample rate
632
- numChannels: resultBuffer.numberOfChannels,
633
- bitDepth: targetBitDepth as BitDepth,
634
- })
635
- outputMimeType = 'audio/wav'
636
- } else if (format === 'opus' || format === 'aac') {
637
- try {
638
- // Try to use MediaRecorder for compressed formats
639
- const { data, bitrate } = await encodeCompressedAudio(
640
- resultBuffer,
641
- format,
642
- outputFormat?.bitrate
643
- )
644
-
645
- outputData = data
646
- outputMimeType =
647
- format === 'opus' ? 'audio/webm' : 'audio/aac'
648
- compressionInfo = {
649
- format,
650
- bitrate,
651
- size: data.byteLength,
652
- }
653
- } catch (error) {
654
- console.warn(
655
- `Failed to encode to ${format}, falling back to WAV: ${error}`
656
- )
657
-
658
- // Same WAV encoding as above
659
- const wavData = new Float32Array(
660
- resultBuffer.length * resultBuffer.numberOfChannels
661
- )
662
-
663
- for (let i = 0; i < resultBuffer.length; i++) {
664
- for (
665
- let channel = 0;
666
- channel < resultBuffer.numberOfChannels;
667
- channel++
668
- ) {
669
- wavData[
670
- i * resultBuffer.numberOfChannels + channel
671
- ] = resultBuffer.getChannelData(channel)[i]
672
- }
673
- }
674
-
675
- outputData = writeWavHeader({
676
- buffer: wavData.buffer as ArrayBuffer,
677
- sampleRate: resultBuffer.sampleRate,
678
- numChannels: resultBuffer.numberOfChannels,
679
- bitDepth: targetBitDepth as BitDepth,
680
- })
681
- outputMimeType = 'audio/wav'
682
- }
683
- } else {
684
- // Default to WAV for unsupported formats
685
- console.warn(
686
- `Format ${format} not supported on web, using WAV instead`
687
- )
688
-
689
- // Same WAV encoding as above
690
- const wavData = new Float32Array(
691
- resultBuffer.length * resultBuffer.numberOfChannels
692
- )
693
-
694
- for (let i = 0; i < resultBuffer.length; i++) {
695
- for (
696
- let channel = 0;
697
- channel < resultBuffer.numberOfChannels;
698
- channel++
699
- ) {
700
- wavData[i * resultBuffer.numberOfChannels + channel] =
701
- resultBuffer.getChannelData(channel)[i]
702
- }
703
- }
704
-
705
- outputData = writeWavHeader({
706
- buffer: wavData.buffer as ArrayBuffer,
707
- sampleRate: resultBuffer.sampleRate,
708
- numChannels: resultBuffer.numberOfChannels,
709
- bitDepth: targetBitDepth as BitDepth,
710
- })
711
- outputMimeType = 'audio/wav'
712
- }
713
-
714
- // Report progress (90% - encoding complete)
715
- ExpoAudioStreamModule.sendEvent('TrimProgress', {
716
- progress: 90,
717
- })
718
-
719
- // Create a blob and URL for the result
720
- const blob = new Blob([outputData], { type: outputMimeType })
721
- const outputUri = URL.createObjectURL(blob)
722
-
723
- // Calculate processing time
724
- const processingTimeMs = performance.now() - startTime
725
-
726
- // Report progress (100% - complete)
727
- ExpoAudioStreamModule.sendEvent('TrimProgress', {
728
- progress: 100,
729
- })
730
-
731
- // Create result object
732
- const result: TrimAudioResult = {
733
- uri: outputUri,
734
- filename,
735
- durationMs: Math.round(resultBuffer.duration * 1000),
736
- size: outputData.byteLength,
737
- sampleRate: resultBuffer.sampleRate,
738
- channels: resultBuffer.numberOfChannels,
739
- bitDepth: targetBitDepth,
740
- mimeType: outputMimeType,
741
- processingInfo: {
742
- durationMs: processingTimeMs,
743
- },
744
- }
745
-
746
- // Add compression info if available
747
- if (compressionInfo) {
748
- result.compression = compressionInfo
749
- }
750
-
751
- return result
752
- } catch (error) {
753
- console.error('Error in trimAudio:', error)
754
- throw error
755
- }
756
- }
757
-
758
- // Add a sendEvent method for web
759
- ExpoAudioStreamModule.sendEvent = (eventName: string, params: any) => {
760
- // This will be picked up by the LegacyEventEmitter in trimAudio.ts
761
- if (
762
- ExpoAudioStreamModule.listeners &&
763
- ExpoAudioStreamModule.listeners[eventName]
764
- ) {
765
- ExpoAudioStreamModule.listeners[eventName].forEach(
766
- (listener: Function) => {
767
- listener(params)
768
- }
769
- )
770
- }
771
- }
772
-
773
- // Initialize listeners object
774
- ExpoAudioStreamModule.listeners = {}
775
-
776
- // Add methods for event listeners that LegacyEventEmitter will use
777
- ExpoAudioStreamModule.addListener = (
778
- eventName: string,
779
- listener: Function
780
- ) => {
781
- if (!ExpoAudioStreamModule.listeners[eventName]) {
782
- ExpoAudioStreamModule.listeners[eventName] = []
783
- }
784
- ExpoAudioStreamModule.listeners[eventName].push(listener)
785
-
786
- // Return an object with a remove method
787
- return {
788
- remove: () => {
789
- const index =
790
- ExpoAudioStreamModule.listeners[eventName].indexOf(listener)
791
- if (index !== -1) {
792
- ExpoAudioStreamModule.listeners[eventName].splice(index, 1)
793
- }
794
- },
795
- }
796
- }
797
-
798
- ExpoAudioStreamModule.removeAllListeners = (eventName: string) => {
799
- if (ExpoAudioStreamModule.listeners[eventName]) {
800
- delete ExpoAudioStreamModule.listeners[eventName]
801
- }
802
- }
803
-
804
- ExpoAudioStreamModule.prepareRecording = async (options: any) => {
805
- // For web platform, we'll implement a simplified version that just checks permissions
806
- // and does minimal setup. The actual recording setup will still happen in startRecording.
807
- try {
808
- // Check for microphone permissions
809
- const permissionsResult =
810
- await ExpoAudioStreamModule.getPermissionsAsync()
811
- if (!permissionsResult.granted) {
812
- throw new Error('Microphone permission not granted')
813
- }
814
-
815
- // If using a web instance, call its prepareRecording method
816
- if (instance) {
817
- return await instance.prepareRecording(options)
818
- }
819
-
820
- return true
821
- } catch (error) {
822
- console.error('Error preparing recording:', error)
823
- throw error
824
- }
825
- }
826
- }
827
-
828
- // Move the encodeCompressedAudio function outside the if block to fix the ESLint error
829
- async function encodeCompressedAudio(
830
- buffer: AudioBuffer,
831
- format: 'opus' | 'aac',
832
- bitrate?: number
833
- ): Promise<{ data: ArrayBuffer; bitrate: number }> {
834
- return new Promise((resolve, reject) => {
835
- try {
836
- // On web, always use opus if aac is requested
837
- const actualFormat =
838
- Platform.OS === 'web' && format === 'aac' ? 'opus' : format
839
-
840
- // Check if MediaRecorder supports the requested format
841
- const mimeType =
842
- actualFormat === 'opus' ? 'audio/webm;codecs=opus' : 'audio/aac'
843
- if (!MediaRecorder.isTypeSupported(mimeType)) {
844
- throw new Error(`MediaRecorder does not support ${mimeType}`)
845
- }
846
-
847
- // Create a new AudioContext and source
848
- const ctx = new (window.AudioContext ||
849
- (window as any).webkitAudioContext)()
850
- const source = ctx.createBufferSource()
851
- source.buffer = buffer
852
-
853
- // Create a MediaStreamDestination to capture the audio
854
- const destination = ctx.createMediaStreamDestination()
855
- source.connect(destination)
856
-
857
- // Create a MediaRecorder with the requested format
858
- const recorder = new MediaRecorder(destination.stream, {
859
- mimeType,
860
- audioBitsPerSecond:
861
- bitrate || (actualFormat === 'opus' ? 32000 : 64000),
862
- })
863
-
864
- const chunks: Blob[] = []
865
-
866
- recorder.ondataavailable = (e) => {
867
- if (e.data.size > 0) {
868
- chunks.push(e.data)
869
- }
870
- }
871
-
872
- recorder.onstop = async () => {
873
- try {
874
- const blob = new Blob(chunks, { type: mimeType })
875
- const arrayBuffer = await blob.arrayBuffer()
876
-
877
- // Get the actual bitrate used
878
- const actualBitrate = Math.round(
879
- (arrayBuffer.byteLength * 8) / buffer.duration
880
- )
881
-
882
- resolve({
883
- data: arrayBuffer,
884
- bitrate: actualBitrate / 1000, // Convert to kbps
885
- })
886
-
887
- // Clean up
888
- ctx.close()
889
- } catch (error) {
890
- reject(error)
891
- }
892
- }
893
-
894
- // Start recording and playback
895
- recorder.start()
896
- source.start(0)
897
-
898
- // Stop recording when the buffer finishes playing
899
- setTimeout(() => {
900
- recorder.stop()
901
- source.stop()
902
- }, buffer.duration * 1000)
903
- } catch (error) {
904
- reject(error)
905
- }
906
- })
907
- }
908
-
909
- // Improved resampleAudioBuffer function
910
- async function resampleAudioBuffer(
911
- context: AudioContext,
912
- buffer: AudioBuffer,
913
- targetSampleRate: number,
914
- targetChannels: number
915
- ): Promise<AudioBuffer> {
916
- // If no change needed, return the original buffer
917
- if (
918
- buffer.sampleRate === targetSampleRate &&
919
- buffer.numberOfChannels === targetChannels
920
- ) {
921
- return buffer
922
- }
923
-
924
- console.log(
925
- `Resampling: ${buffer.sampleRate}Hz → ${targetSampleRate}Hz, ${buffer.numberOfChannels} → ${targetChannels} channels`
926
- )
927
-
928
- // Calculate the new length based on the sample rate change
929
- const newLength = Math.round(
930
- (buffer.length * targetSampleRate) / buffer.sampleRate
931
- )
932
-
933
- // Create an offline context for resampling
934
- const offlineContext = new OfflineAudioContext(
935
- targetChannels,
936
- newLength,
937
- targetSampleRate
938
- )
939
-
940
- // Create a source node
941
- const source = offlineContext.createBufferSource()
942
- source.buffer = buffer
943
-
944
- // If we need to change channel count
945
- if (buffer.numberOfChannels !== targetChannels) {
946
- if (targetChannels === 1 && buffer.numberOfChannels > 1) {
947
- // Downmix to mono
948
- const merger = offlineContext.createChannelMerger(1)
949
-
950
- // Create a gain node to reduce volume when downmixing to prevent clipping
951
- const gainNode = offlineContext.createGain()
952
- gainNode.gain.value = 1.0 / buffer.numberOfChannels
953
-
954
- source.connect(gainNode)
955
- gainNode.connect(merger)
956
- merger.connect(offlineContext.destination)
957
- } else if (targetChannels === 2 && buffer.numberOfChannels === 1) {
958
- // Upmix mono to stereo (duplicate the channel)
959
- const splitter = offlineContext.createChannelSplitter(1)
960
- const merger = offlineContext.createChannelMerger(2)
961
-
962
- source.connect(splitter)
963
- splitter.connect(merger, 0, 0)
964
- splitter.connect(merger, 0, 1)
965
- merger.connect(offlineContext.destination)
966
- } else {
967
- // For other cases, just connect and let the system handle it
968
- source.connect(offlineContext.destination)
969
- }
970
- } else {
971
- // No channel conversion needed
972
- source.connect(offlineContext.destination)
973
- }
974
-
975
- // Start rendering
976
- source.start(0)
977
- const resampledBuffer = await offlineContext.startRendering()
978
-
979
- console.log(
980
- `Resampling complete: ${resampledBuffer.length} samples at ${resampledBuffer.sampleRate}Hz`
981
- )
982
-
983
- return resampledBuffer
984
- }
985
-
986
- if (Platform.OS !== 'web') {
987
- ExpoAudioStreamModule = requireNativeModule('ExpoAudioStream')
988
- }
989
-
990
- export default ExpoAudioStreamModule