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