@siteed/expo-audio-studio 2.18.6 → 3.0.1

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 +7 -136
  5. package/CHANGELOG.md +0 -501
  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 -670
  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 -2369
  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 -1019
  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,773 +0,0 @@
1
- // packages/expo-audio-stream/src/WebRecorder.web.ts
2
- import { encodingToBitDepth } from './utils/encodingToBitDepth';
3
- import { writeWavHeader } from './utils/writeWavHeader';
4
- import { InlineFeaturesExtractor } from './workers/InlineFeaturesExtractor.web';
5
- import { InlineAudioWebWorker } from './workers/inlineAudioWebWorker.web';
6
- const DEFAULT_WEB_BITDEPTH = 32;
7
- const DEFAULT_SEGMENT_DURATION_MS = 100;
8
- const DEFAULT_WEB_INTERVAL = 500;
9
- const DEFAULT_WEB_NUMBER_OF_CHANNELS = 1;
10
- const TAG = 'WebRecorder';
11
- export class WebRecorder {
12
- audioContext;
13
- audioWorkletNode;
14
- featureExtractorWorker;
15
- source;
16
- emitAudioEventCallback;
17
- emitAudioAnalysisCallback;
18
- config;
19
- position = 0;
20
- numberOfChannels; // Number of audio channels
21
- bitDepth; // Bit depth of the audio
22
- exportBitDepth; // Bit depth of the audio
23
- audioAnalysisData; // Keep updating the full audio analysis data with latest events
24
- logger;
25
- compressedMediaRecorder = null;
26
- compressedChunks = [];
27
- compressedSize = 0;
28
- pendingCompressedChunk = null;
29
- dataPointIdCounter = 0; // Add this property to track the counter
30
- deviceDisconnectionHandler = null;
31
- mediaStream = null;
32
- onInterruptionCallback;
33
- _isDeviceDisconnected = false;
34
- pcmData = null; // Store original PCM data
35
- totalSampleCount = 0;
36
- /**
37
- * Flag to indicate whether this is the first audio chunk after a device switch
38
- * Used to maintain proper duration counting
39
- */
40
- isFirstChunkAfterSwitch = false;
41
- /**
42
- * Gets whether the recording device has been disconnected
43
- */
44
- get isDeviceDisconnected() {
45
- return this._isDeviceDisconnected;
46
- }
47
- /**
48
- * Initializes a new WebRecorder instance for audio recording and processing
49
- * @param audioContext - The AudioContext to use for recording
50
- * @param source - The MediaStreamAudioSourceNode providing the audio input
51
- * @param recordingConfig - Configuration options for the recording
52
- * @param emitAudioEventCallback - Callback function for audio data events
53
- * @param emitAudioAnalysisCallback - Callback function for audio analysis events
54
- * @param onInterruption - Callback for recording interruptions
55
- * @param logger - Optional logger for debugging information
56
- */
57
- constructor({ audioContext, source, recordingConfig, emitAudioEventCallback, emitAudioAnalysisCallback, onInterruption, logger, }) {
58
- this.audioContext = audioContext;
59
- this.source = source;
60
- this.emitAudioEventCallback = emitAudioEventCallback;
61
- this.emitAudioAnalysisCallback = emitAudioAnalysisCallback;
62
- this.config = recordingConfig;
63
- this.logger = logger;
64
- const audioContextFormat = this.checkAudioContextFormat({
65
- sampleRate: this.audioContext.sampleRate,
66
- });
67
- this.logger?.debug('Initialized WebRecorder with config:', {
68
- sampleRate: audioContextFormat.sampleRate,
69
- bitDepth: audioContextFormat.bitDepth,
70
- numberOfChannels: audioContextFormat.numberOfChannels,
71
- });
72
- this.bitDepth = audioContextFormat.bitDepth;
73
- this.numberOfChannels =
74
- audioContextFormat.numberOfChannels ||
75
- DEFAULT_WEB_NUMBER_OF_CHANNELS; // Default to 1 if not available
76
- this.exportBitDepth =
77
- encodingToBitDepth({
78
- encoding: recordingConfig.encoding ?? 'pcm_32bit',
79
- }) ||
80
- audioContextFormat.bitDepth ||
81
- DEFAULT_WEB_BITDEPTH;
82
- this.audioAnalysisData = {
83
- amplitudeRange: { min: 0, max: 0 },
84
- rmsRange: { min: 0, max: 0 },
85
- dataPoints: [],
86
- durationMs: 0,
87
- samples: 0,
88
- bitDepth: this.bitDepth,
89
- numberOfChannels: this.numberOfChannels,
90
- sampleRate: this.config.sampleRate || this.audioContext.sampleRate,
91
- segmentDurationMs: this.config.segmentDurationMs ?? DEFAULT_SEGMENT_DURATION_MS, // Default to 100ms segments
92
- extractionTimeMs: 0,
93
- };
94
- if (recordingConfig.enableProcessing) {
95
- this.initFeatureExtractorWorker();
96
- }
97
- // Initialize compressed recording if enabled
98
- if (recordingConfig.output?.compressed?.enabled) {
99
- this.initializeCompressedRecorder();
100
- }
101
- this.mediaStream = source.mediaStream;
102
- this.onInterruptionCallback = onInterruption;
103
- // Setup device disconnection detection
104
- this.setupDeviceDisconnectionDetection();
105
- }
106
- /**
107
- * Initializes the audio worklet using an inline script
108
- * Creates and connects the audio processing pipeline
109
- */
110
- async init() {
111
- try {
112
- // Create and use inline audio worklet
113
- const blob = new Blob([InlineAudioWebWorker], {
114
- type: 'application/javascript',
115
- });
116
- const url = URL.createObjectURL(blob);
117
- await this.audioContext.audioWorklet.addModule(url);
118
- this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'recorder-processor');
119
- this.audioWorkletNode.port.onmessage = async (event) => {
120
- const command = event.data.command;
121
- if (command === 'debug') {
122
- this.logger?.debug(`[AudioWorklet] ${event.data.message}`);
123
- return;
124
- }
125
- if (command !== 'newData')
126
- return;
127
- const pcmBufferFloat = event.data.recordedData;
128
- if (!pcmBufferFloat) {
129
- this.logger?.warn('Received empty audio buffer', event);
130
- return;
131
- }
132
- // Process data in smaller chunks and emit immediately
133
- const sampleRate = event.data.sampleRate ?? this.audioContext.sampleRate;
134
- // Use chunk size from config interval or default to 2 seconds
135
- const intervalMs = this.config.interval ?? DEFAULT_WEB_INTERVAL;
136
- const chunkSize = Math.floor(sampleRate * (intervalMs / 1000));
137
- const duration = pcmBufferFloat.length / sampleRate;
138
- // Use incoming position if provided by worklet, otherwise use our tracked position
139
- const incomingPosition = typeof event.data.position === 'number'
140
- ? event.data.position
141
- : this.position;
142
- // Simple position tracking for logging (no duplicate filtering)
143
- this.logger?.debug(`Audio chunk: position=${incomingPosition.toFixed(3)}s, size=${pcmBufferFloat.length}`);
144
- // Calculate bytes per sample based on bit depth
145
- const bytesPerSample = this.bitDepth / 8;
146
- // Emit chunks without storing them
147
- for (let i = 0; i < pcmBufferFloat.length; i += chunkSize) {
148
- const chunk = pcmBufferFloat.slice(i, i + chunkSize);
149
- const chunkPosition = incomingPosition + i / sampleRate;
150
- // Calculate byte positions and samples
151
- const startPosition = Math.floor(i * bytesPerSample);
152
- const endPosition = Math.floor((i + chunk.length) * bytesPerSample);
153
- const samples = chunk.length; // Number of samples in this chunk
154
- // Only store PCM data if primary output is enabled
155
- const shouldStoreUncompressed = this.config.output?.primary?.enabled ?? true;
156
- // Store PCM chunks when needed - this is for the final WAV file
157
- if (shouldStoreUncompressed) {
158
- // Store the original Float32Array data for later WAV creation
159
- this.appendPcmData(chunk);
160
- this.totalSampleCount += chunk.length;
161
- }
162
- // Process features if enabled
163
- if (this.config.enableProcessing &&
164
- this.featureExtractorWorker) {
165
- this.featureExtractorWorker.postMessage({
166
- command: 'process',
167
- channelData: chunk,
168
- sampleRate,
169
- segmentDurationMs: this.config.segmentDurationMs ??
170
- DEFAULT_SEGMENT_DURATION_MS, // Default to 100ms
171
- bitDepth: this.bitDepth,
172
- fullAudioDurationMs: chunkPosition * 1000,
173
- numberOfChannels: this.numberOfChannels,
174
- features: this.config.features,
175
- intervalAnalysis: this.config.intervalAnalysis,
176
- startPosition,
177
- endPosition,
178
- samples,
179
- });
180
- }
181
- // Prepare compression data if available
182
- const compression = this.pendingCompressedChunk
183
- ? {
184
- data: this.pendingCompressedChunk,
185
- size: this.pendingCompressedChunk.size,
186
- totalSize: this.compressedSize,
187
- mimeType: 'audio/webm',
188
- format: this.config.output?.compressed?.format ??
189
- 'opus',
190
- bitrate: this.config.output?.compressed?.bitrate ??
191
- 128000,
192
- }
193
- : undefined;
194
- // Emit chunk immediately - whether compressed or not
195
- this.emitAudioEventCallback({
196
- data: chunk,
197
- position: chunkPosition,
198
- compression,
199
- });
200
- // Reset pending compressed chunk after we've used it
201
- this.pendingCompressedChunk = null;
202
- }
203
- // Update our position based on the worklet's position if provided
204
- this.position = incomingPosition + duration;
205
- };
206
- // Ensure we use all relevant settings from config
207
- const recordSampleRate = this.audioContext.sampleRate;
208
- const exportSampleRate = this.config.sampleRate ?? this.audioContext.sampleRate;
209
- const channels = this.config.channels ?? this.numberOfChannels;
210
- const interval = this.config.interval ?? DEFAULT_WEB_INTERVAL;
211
- this.logger?.debug(`WebRecorder initialized with config:`, {
212
- recordSampleRate,
213
- exportSampleRate,
214
- bitDepth: this.bitDepth,
215
- exportBitDepth: this.exportBitDepth,
216
- channels,
217
- interval,
218
- position: this.position,
219
- deviceId: this.config.deviceId ?? 'default',
220
- compression: this.config.output?.compressed
221
- ? {
222
- enabled: this.config.output.compressed.enabled,
223
- format: this.config.output.compressed.format,
224
- bitrate: this.config.output.compressed.bitrate,
225
- }
226
- : 'disabled',
227
- });
228
- // Initialize the worklet with all settings from config
229
- this.audioWorkletNode.port.postMessage({
230
- command: 'init',
231
- recordSampleRate,
232
- exportSampleRate,
233
- bitDepth: this.bitDepth,
234
- exportBitDepth: this.exportBitDepth,
235
- channels,
236
- interval,
237
- position: this.position, // Pass the current position to the processor
238
- enableLogging: true,
239
- });
240
- // Connect the source to the AudioWorkletNode and start recording
241
- this.source.connect(this.audioWorkletNode);
242
- this.audioWorkletNode.connect(this.audioContext.destination);
243
- }
244
- catch (error) {
245
- console.error(`[${TAG}] Failed to initialize WebRecorder`, error);
246
- }
247
- }
248
- /**
249
- * Append new PCM data to the existing buffer
250
- * @param newData New Float32Array data to append
251
- */
252
- appendPcmData(newData) {
253
- // Clone the incoming data to ensure it's not modified
254
- const dataToAdd = new Float32Array(newData);
255
- if (!this.pcmData) {
256
- // First chunk - create a copy to avoid references to original data
257
- this.pcmData = new Float32Array(dataToAdd);
258
- return;
259
- }
260
- // Create a new buffer with increased size
261
- const newBuffer = new Float32Array(this.pcmData.length + dataToAdd.length);
262
- // Copy existing data
263
- newBuffer.set(this.pcmData);
264
- // Append new data
265
- newBuffer.set(dataToAdd, this.pcmData.length);
266
- // Replace existing buffer
267
- this.pcmData = newBuffer;
268
- }
269
- /**
270
- * Initializes the feature extractor worker for audio analysis
271
- * Creates an inline worker from a blob for audio feature extraction
272
- */
273
- initFeatureExtractorWorker() {
274
- try {
275
- const blob = new Blob([InlineFeaturesExtractor], {
276
- type: 'application/javascript',
277
- });
278
- const url = URL.createObjectURL(blob);
279
- this.featureExtractorWorker = new Worker(url);
280
- this.featureExtractorWorker.onmessage =
281
- this.handleFeatureExtractorMessage.bind(this);
282
- this.featureExtractorWorker.onerror = (error) => {
283
- console.error(`[${TAG}] Feature extractor worker error:`, error);
284
- };
285
- // Initialize worker with counter if needed
286
- if (this.dataPointIdCounter > 0) {
287
- this.featureExtractorWorker.postMessage({
288
- command: 'resetCounter',
289
- value: this.dataPointIdCounter,
290
- });
291
- this.logger?.debug(`Initialized worker with counter value ${this.dataPointIdCounter}`);
292
- }
293
- this.logger?.log('Feature extractor worker initialized successfully');
294
- }
295
- catch (error) {
296
- console.error(`[${TAG}] Failed to initialize feature extractor worker`, error);
297
- }
298
- }
299
- /**
300
- * Processes audio analysis results from the feature extractor worker
301
- * Updates the audio analysis data and emits events
302
- * @param event - The event containing audio analysis results
303
- */
304
- handleFeatureExtractorMessage(event) {
305
- if (event.data.command !== 'features')
306
- return;
307
- const segmentResult = event.data.result;
308
- const uniqueNewDataPoints = this.filterUniqueDataPoints(segmentResult.dataPoints);
309
- // Update counter based on the highest ID seen
310
- this.updateDataPointCounter(uniqueNewDataPoints);
311
- // Update analysis data with the new results
312
- this.updateAudioAnalysisData(segmentResult, uniqueNewDataPoints);
313
- // Send filtered result to avoid duplicate IDs
314
- const filteredSegmentResult = {
315
- ...segmentResult,
316
- dataPoints: uniqueNewDataPoints,
317
- };
318
- this.emitAudioAnalysisCallback(filteredSegmentResult);
319
- }
320
- /**
321
- * Filters out data points with duplicate IDs
322
- */
323
- filterUniqueDataPoints(dataPoints) {
324
- // Track existing IDs to prevent duplicates
325
- const existingIds = new Set(this.audioAnalysisData.dataPoints.map((dp) => dp.id));
326
- // Filter out datapoints with duplicate IDs
327
- const uniquePoints = dataPoints.filter((dp) => !existingIds.has(dp.id));
328
- // Log filtered duplicates if any
329
- if (uniquePoints.length < dataPoints.length && this.logger?.warn) {
330
- this.logger.warn(`Filtered ${dataPoints.length - uniquePoints.length} duplicate datapoints`);
331
- }
332
- return uniquePoints;
333
- }
334
- /**
335
- * Updates the counter based on the highest ID in datapoints
336
- */
337
- updateDataPointCounter(dataPoints) {
338
- if (dataPoints.length === 0)
339
- return;
340
- const lastDataPoint = dataPoints[dataPoints.length - 1];
341
- if (lastDataPoint && typeof lastDataPoint.id === 'number') {
342
- const nextIdValue = lastDataPoint.id + 1;
343
- if (nextIdValue > this.dataPointIdCounter) {
344
- this.dataPointIdCounter = nextIdValue;
345
- this.logger?.debug(`Counter updated to ${this.dataPointIdCounter}`);
346
- }
347
- }
348
- }
349
- /**
350
- * Updates audio analysis data with segment results
351
- */
352
- updateAudioAnalysisData(segmentResult, uniqueDataPoints) {
353
- // Add unique data points to our analysis data
354
- this.audioAnalysisData.dataPoints.push(...uniqueDataPoints);
355
- this.audioAnalysisData.durationMs += segmentResult.durationMs;
356
- this.audioAnalysisData.sampleRate = segmentResult.sampleRate;
357
- // Update amplitude range if present
358
- if (segmentResult.amplitudeRange) {
359
- this.audioAnalysisData.amplitudeRange = this.mergeRange(this.audioAnalysisData.amplitudeRange, segmentResult.amplitudeRange);
360
- }
361
- // Update RMS range if present
362
- if (segmentResult.rmsRange) {
363
- this.audioAnalysisData.rmsRange = this.mergeRange(this.audioAnalysisData.rmsRange, segmentResult.rmsRange);
364
- }
365
- }
366
- /**
367
- * Merges value ranges
368
- */
369
- mergeRange(existing, newRange) {
370
- if (!existing)
371
- return { ...newRange };
372
- return {
373
- min: Math.min(existing.min, newRange.min),
374
- max: Math.max(existing.max, newRange.max),
375
- };
376
- }
377
- /**
378
- * Reset the data point counter to a specific value or zero
379
- * @param startCounterFrom Optional value to start the counter from (for continuing from previous recordings)
380
- */
381
- resetDataPointCounter(startCounterFrom) {
382
- // Set the counter with the passed value or 0
383
- this.dataPointIdCounter = startCounterFrom ?? 0;
384
- this.logger?.debug(`Reset data point counter to ${this.dataPointIdCounter}`);
385
- // Update worker counter if available
386
- if (this.featureExtractorWorker) {
387
- this.featureExtractorWorker.postMessage({
388
- command: 'resetCounter',
389
- value: this.dataPointIdCounter,
390
- });
391
- }
392
- else {
393
- this.logger?.warn('No feature extractor worker available to update counter');
394
- }
395
- }
396
- /**
397
- * Get the current data point counter value
398
- * @returns The current value of the data point counter
399
- */
400
- getDataPointCounter() {
401
- return this.dataPointIdCounter;
402
- }
403
- /**
404
- * Prepares the recorder for continuity after device switch
405
- * Sets up all necessary state to maintain proper recording continuity
406
- */
407
- prepareForDeviceSwitch() {
408
- this.isFirstChunkAfterSwitch = true;
409
- this.logger?.debug(`Prepared for device switch at position ${this.position}s`);
410
- }
411
- /**
412
- * Starts the audio recording process
413
- * Connects the audio nodes and begins capturing audio data
414
- * @param preserveCounters If true, do not reset the counter (used for device switching)
415
- */
416
- start(preserveCounters = false) {
417
- this.source.connect(this.audioWorkletNode);
418
- this.audioWorkletNode.connect(this.audioContext.destination);
419
- // Only reset the counter when not preserving state (e.g., for a fresh recording)
420
- if (!preserveCounters) {
421
- this.logger?.debug('Starting fresh recording, resetting counter to 0');
422
- this.resetDataPointCounter(0); // Explicitly reset to 0 for new recordings
423
- this.isFirstChunkAfterSwitch = false;
424
- // Clear PCM data for new recording
425
- this.pcmData = null;
426
- this.totalSampleCount = 0;
427
- }
428
- else {
429
- this.logger?.debug(`Preserving counter at ${this.dataPointIdCounter} during device switch`);
430
- }
431
- if (this.compressedMediaRecorder) {
432
- this.compressedMediaRecorder.start(this.config.interval ?? 1000);
433
- }
434
- }
435
- /**
436
- * Creates a WAV file from the stored PCM data
437
- */
438
- createWavFromPcmData() {
439
- try {
440
- // Check if we have PCM data
441
- if (!this.pcmData || this.pcmData.length === 0) {
442
- this.logger?.warn('No PCM data available to create WAV file');
443
- return null;
444
- }
445
- const sampleRate = this.config.sampleRate ?? this.audioContext.sampleRate;
446
- const channels = this.numberOfChannels || 1;
447
- // Convert float32 PCM data to 16-bit PCM for WAV
448
- const bytesPerSample = 2; // 16-bit = 2 bytes
449
- const dataLength = this.pcmData.length * bytesPerSample;
450
- const buffer = new ArrayBuffer(dataLength);
451
- const view = new DataView(buffer);
452
- // Convert Float32Array (-1 to 1) to Int16Array (-32768 to 32767)
453
- for (let i = 0; i < this.pcmData.length; i++) {
454
- const sample = Math.max(-1, Math.min(1, this.pcmData[i]));
455
- const int16Value = Math.round(sample * 32767);
456
- view.setInt16(i * 2, int16Value, true);
457
- }
458
- // Use the existing writeWavHeader utility to add a WAV header
459
- const wavBuffer = writeWavHeader({
460
- buffer,
461
- sampleRate,
462
- numChannels: channels,
463
- bitDepth: 16,
464
- isFloat: false,
465
- });
466
- return new Blob([wavBuffer], { type: 'audio/wav' });
467
- }
468
- catch (error) {
469
- this.logger?.error('Error creating WAV file from PCM data:', error);
470
- return null;
471
- }
472
- }
473
- /**
474
- * Stops the audio recording process and returns the recorded data
475
- * @returns Promise resolving to an object containing compressed and/or uncompressed blobs
476
- */
477
- async stop() {
478
- try {
479
- // Stop any compressed recording first
480
- if (this.compressedMediaRecorder &&
481
- this.compressedMediaRecorder.state !== 'inactive') {
482
- this.compressedMediaRecorder.stop();
483
- }
484
- // Wait for any pending compressed chunks to be processed
485
- if (this.compressedMediaRecorder) {
486
- // Small delay to ensure all data is processed
487
- await new Promise((resolve) => setTimeout(resolve, 100));
488
- }
489
- // Create uncompressed WAV file from the PCM data
490
- let uncompressedBlob;
491
- // Only create WAV if we have PCM data
492
- if (this.pcmData && this.pcmData.length > 0) {
493
- uncompressedBlob = this.createWavFromPcmData() || undefined;
494
- }
495
- // Return the compressed and/or uncompressed blobs if available
496
- return {
497
- compressedBlob: this.compressedChunks.length > 0
498
- ? new Blob(this.compressedChunks, {
499
- type: 'audio/webm;codecs=opus',
500
- })
501
- : undefined,
502
- uncompressedBlob,
503
- };
504
- }
505
- finally {
506
- this.cleanup();
507
- // Reset the chunks array
508
- this.compressedChunks = [];
509
- this.compressedSize = 0;
510
- this.pendingCompressedChunk = null;
511
- this.pcmData = null;
512
- this.totalSampleCount = 0;
513
- this.dataPointIdCounter = 0; // Reset counter
514
- }
515
- }
516
- /**
517
- * Cleans up resources when recording is stopped
518
- * Closes audio context and disconnects nodes
519
- */
520
- cleanup() {
521
- // Remove device disconnection handler
522
- if (this.deviceDisconnectionHandler) {
523
- this.deviceDisconnectionHandler();
524
- this.deviceDisconnectionHandler = null;
525
- }
526
- // Check if AudioContext is already closed before attempting to close it
527
- if (this.audioContext && this.audioContext.state !== 'closed') {
528
- this.audioContext.close().catch((e) => {
529
- // Log closure errors but continue cleanup
530
- this.logger?.warn('Error closing AudioContext:', e);
531
- });
532
- }
533
- // Safely disconnect audioWorkletNode if it exists
534
- if (this.audioWorkletNode) {
535
- try {
536
- this.audioWorkletNode.disconnect();
537
- }
538
- catch (e) {
539
- // Log disconnection errors but continue cleanup
540
- this.logger?.warn('Error disconnecting audioWorkletNode:', e);
541
- }
542
- }
543
- // Safely disconnect source if it exists
544
- if (this.source) {
545
- try {
546
- this.source.disconnect();
547
- }
548
- catch (e) {
549
- // Log disconnection errors but continue cleanup
550
- this.logger?.warn('Error disconnecting source:', e);
551
- }
552
- }
553
- // Always stop media stream tracks to release hardware resources
554
- this.stopMediaStreamTracks();
555
- // Mark as disconnected to prevent future errors
556
- this._isDeviceDisconnected = true;
557
- }
558
- /**
559
- * Pauses the audio recording process
560
- * Disconnects audio nodes and pauses the media recorder
561
- */
562
- pause() {
563
- try {
564
- // Note: We're just pausing, not disconnecting the device
565
- // Simply disconnect nodes temporarily without marking device as disconnected
566
- this.source.disconnect(this.audioWorkletNode);
567
- this.audioWorkletNode.disconnect(this.audioContext.destination);
568
- this.audioWorkletNode.port.postMessage({ command: 'pause' });
569
- if (this.compressedMediaRecorder?.state === 'recording') {
570
- this.compressedMediaRecorder.pause();
571
- }
572
- this.logger?.debug('Recording paused successfully');
573
- }
574
- catch (error) {
575
- this.logger?.error('Error in pause(): ', error);
576
- // Already disconnected, just ignore and continue
577
- }
578
- }
579
- /**
580
- * Stops all media stream tracks to release hardware resources
581
- * Ensures recording indicators (like microphone icon) are turned off
582
- */
583
- stopMediaStreamTracks() {
584
- // Stop all audio tracks to stop the recording icon
585
- if (this.mediaStream) {
586
- const tracks = this.mediaStream.getTracks();
587
- tracks.forEach((track) => track.stop());
588
- }
589
- else if (this.source?.mediaStream) {
590
- const tracks = this.source.mediaStream.getTracks();
591
- tracks.forEach((track) => track.stop());
592
- }
593
- }
594
- /**
595
- * Determines the audio format capabilities of the current audio context
596
- * @param sampleRate - The sample rate to check
597
- * @returns Object containing format information (sample rate, bit depth, channels)
598
- */
599
- checkAudioContextFormat({ sampleRate }) {
600
- // Create a silent AudioBuffer
601
- const frameCount = sampleRate * 1.0; // 1 second buffer
602
- const audioBuffer = this.audioContext.createBuffer(1, frameCount, sampleRate);
603
- // Check the format
604
- const channelData = audioBuffer.getChannelData(0);
605
- const bitDepth = channelData.BYTES_PER_ELEMENT * 8; // 4 bytes per element means 32-bit
606
- return {
607
- sampleRate: audioBuffer.sampleRate,
608
- bitDepth,
609
- numberOfChannels: audioBuffer.numberOfChannels,
610
- };
611
- }
612
- /**
613
- * Resumes a paused recording
614
- * Reconnects audio nodes and resumes the media recorder
615
- */
616
- resume() {
617
- // If device was disconnected, we can't resume
618
- if (this._isDeviceDisconnected) {
619
- this.logger?.warn('Cannot resume recording: device disconnected');
620
- return;
621
- }
622
- try {
623
- this.source.connect(this.audioWorkletNode);
624
- this.audioWorkletNode.connect(this.audioContext.destination);
625
- this.audioWorkletNode.port.postMessage({ command: 'resume' });
626
- this.compressedMediaRecorder?.resume();
627
- }
628
- catch (error) {
629
- this.logger?.error('Error in resume(): ', error);
630
- // Rethrow the error to inform callers
631
- throw new Error(`Failed to resume recording: ${error instanceof Error ? error.message : 'unknown error'}`);
632
- }
633
- }
634
- /**
635
- * Initializes the compressed media recorder if compression is enabled
636
- * Sets up event handlers for compressed audio data
637
- */
638
- initializeCompressedRecorder() {
639
- try {
640
- const mimeType = 'audio/webm;codecs=opus';
641
- if (!MediaRecorder.isTypeSupported(mimeType)) {
642
- this.logger?.warn('Opus compression not supported in this browser');
643
- return;
644
- }
645
- this.compressedMediaRecorder = new MediaRecorder(this.source.mediaStream, {
646
- mimeType,
647
- audioBitsPerSecond: this.config.output?.compressed?.bitrate ?? 128000,
648
- });
649
- this.compressedMediaRecorder.ondataavailable = (event) => {
650
- if (event.data.size > 0) {
651
- // Store the compressed chunk for final blob creation
652
- this.compressedChunks.push(event.data);
653
- this.compressedSize += event.data.size;
654
- // Store the pending compressed chunk for the next PCM chunk to use
655
- this.pendingCompressedChunk = event.data;
656
- }
657
- };
658
- }
659
- catch (error) {
660
- this.logger?.error('Failed to initialize compressed recorder:', error);
661
- // Setting to null to indicate initialization failed
662
- this.compressedMediaRecorder = null;
663
- }
664
- }
665
- /**
666
- * Processes features if enabled
667
- */
668
- processFeatures(chunk, sampleRate, chunkPosition, startPosition, endPosition, samples) {
669
- if (this.config.enableProcessing && this.featureExtractorWorker) {
670
- this.featureExtractorWorker.postMessage({
671
- command: 'process',
672
- channelData: chunk,
673
- sampleRate,
674
- segmentDurationMs: this.config.segmentDurationMs ??
675
- DEFAULT_SEGMENT_DURATION_MS, // Default to 100ms
676
- bitDepth: this.bitDepth,
677
- fullAudioDurationMs: chunkPosition * 1000,
678
- numberOfChannels: this.numberOfChannels,
679
- features: this.config.features,
680
- intervalAnalysis: this.config.intervalAnalysis,
681
- startPosition,
682
- endPosition,
683
- samples,
684
- });
685
- }
686
- }
687
- /**
688
- * Sets up detection for device disconnection events
689
- */
690
- setupDeviceDisconnectionDetection() {
691
- if (!this.mediaStream)
692
- return;
693
- // Function to handle track ending (which happens on device disconnection)
694
- const handleTrackEnded = () => {
695
- this.logger?.warn('Audio track ended - device disconnected');
696
- this._isDeviceDisconnected = true;
697
- // Use the callback to notify parent component about device disconnection
698
- if (this.onInterruptionCallback) {
699
- this.onInterruptionCallback({
700
- reason: 'deviceDisconnected',
701
- isPaused: true,
702
- timestamp: Date.now(),
703
- });
704
- this.logger?.debug('Notified about device disconnection');
705
- }
706
- // Ensure we disconnect nodes to prevent zombie recordings
707
- if (this.audioWorkletNode) {
708
- this.audioWorkletNode.port.postMessage({
709
- command: 'deviceDisconnected',
710
- });
711
- try {
712
- this.source.disconnect(this.audioWorkletNode);
713
- this.audioWorkletNode.disconnect();
714
- }
715
- catch (e) {
716
- // Ignore disconnection errors as the track might already be gone
717
- this.logger?.warn('Error disconnecting audioWorkletNode:', e);
718
- }
719
- }
720
- };
721
- // Add listeners to all audio tracks
722
- const tracks = this.mediaStream.getAudioTracks();
723
- tracks.forEach((track) => {
724
- track.addEventListener('ended', handleTrackEnded);
725
- });
726
- // Store the handler for cleanup
727
- this.deviceDisconnectionHandler = () => {
728
- tracks.forEach((track) => {
729
- track.removeEventListener('ended', handleTrackEnded);
730
- });
731
- };
732
- }
733
- /**
734
- * Explicitly set the position for continuous recording across device switches
735
- * @param position The position in seconds to continue from
736
- */
737
- setPosition(position) {
738
- if (position >= 0) {
739
- this.position = position;
740
- this.logger?.debug(`Position explicitly set to ${position} seconds`);
741
- }
742
- else {
743
- this.logger?.warn(`Invalid position value: ${position}, ignoring`);
744
- }
745
- }
746
- /**
747
- * Get the current position in seconds
748
- * @returns The current position
749
- */
750
- getPosition() {
751
- return this.position;
752
- }
753
- /**
754
- * Gets the current compressed chunks
755
- * @returns Array of current compressed audio chunks
756
- */
757
- getCompressedChunks() {
758
- return [...this.compressedChunks];
759
- }
760
- /**
761
- * Sets the compressed chunks from a previous recorder
762
- * @param chunks Array of compressed chunks from a previous recorder
763
- */
764
- setCompressedChunks(chunks) {
765
- if (chunks && chunks.length > 0) {
766
- this.logger?.debug(`Adding ${chunks.length} compressed chunks from previous device`);
767
- this.compressedChunks = [...chunks, ...this.compressedChunks];
768
- // Update size
769
- this.compressedSize = this.compressedChunks.reduce((size, chunk) => size + chunk.size, 0);
770
- }
771
- }
772
- }
773
- //# sourceMappingURL=WebRecorder.web.js.map