@siteed/expo-audio-studio 2.18.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/README.md +13 -297
  2. package/index.d.ts +1 -0
  3. package/index.js +1 -0
  4. package/package.json +6 -135
  5. package/CHANGELOG.md +0 -493
  6. package/LICENSE +0 -21
  7. package/android/build.gradle +0 -129
  8. package/android/src/androidTest/assets/chorus.wav +0 -0
  9. package/android/src/androidTest/assets/jfk.wav +0 -0
  10. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  11. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  12. package/android/src/androidTest/java/net/siteed/audiostream/AudioProcessorInstrumentedTest.kt +0 -197
  13. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderInstrumentedTest.kt +0 -541
  14. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderPerformanceInstrumentedTest.kt +0 -234
  15. package/android/src/androidTest/java/net/siteed/audiostream/integration/AudioFocusStrategyIntegrationTest.kt +0 -332
  16. package/android/src/androidTest/java/net/siteed/audiostream/integration/BufferDurationIntegrationTest.kt +0 -324
  17. package/android/src/androidTest/java/net/siteed/audiostream/integration/CompressedOnlyOutputTest.kt +0 -253
  18. package/android/src/androidTest/java/net/siteed/audiostream/integration/DeviceDisconnectionFallbackTest.kt +0 -218
  19. package/android/src/androidTest/java/net/siteed/audiostream/integration/EventEmissionIntervalTest.kt +0 -120
  20. package/android/src/androidTest/java/net/siteed/audiostream/integration/M4aFormatTest.kt +0 -345
  21. package/android/src/androidTest/java/net/siteed/audiostream/integration/OutputControlIntegrationTest.kt +0 -340
  22. package/android/src/androidTest/java/net/siteed/audiostream/integration/PcmStreamingDurationTest.kt +0 -252
  23. package/android/src/androidTest/java/net/siteed/audiostream/integration/README.md +0 -95
  24. package/android/src/androidTest/java/net/siteed/audiostream/integration/run_integration_tests.sh +0 -43
  25. package/android/src/main/AndroidManifest.xml +0 -30
  26. package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +0 -188
  27. package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +0 -9
  28. package/android/src/main/java/net/siteed/audiostream/AudioDeviceManager.kt +0 -1741
  29. package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +0 -136
  30. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +0 -354
  31. package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +0 -439
  32. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +0 -2237
  33. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +0 -2141
  34. package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +0 -167
  35. package/android/src/main/java/net/siteed/audiostream/AudioTrimmer.kt +0 -1099
  36. package/android/src/main/java/net/siteed/audiostream/Constants.kt +0 -37
  37. package/android/src/main/java/net/siteed/audiostream/EventSender.kt +0 -7
  38. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +0 -1113
  39. package/android/src/main/java/net/siteed/audiostream/FFT.kt +0 -99
  40. package/android/src/main/java/net/siteed/audiostream/Features.kt +0 -98
  41. package/android/src/main/java/net/siteed/audiostream/LogUtils.kt +0 -93
  42. package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +0 -72
  43. package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +0 -68
  44. package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +0 -59
  45. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +0 -257
  46. package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +0 -19
  47. package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +0 -159
  48. package/android/src/main/res/drawable/ic_default_action_icon.xml +0 -16
  49. package/android/src/main/res/drawable/ic_microphone.xml +0 -13
  50. package/android/src/main/res/drawable/ic_pause.xml +0 -10
  51. package/android/src/main/res/drawable/ic_play.xml +0 -10
  52. package/android/src/main/res/drawable/ic_stop.xml +0 -10
  53. package/android/src/main/res/layout/notification_recording.xml +0 -37
  54. package/android/src/test/java/net/siteed/audiostream/AudioFileHandlerTest.kt +0 -279
  55. package/android/src/test/java/net/siteed/audiostream/AudioFocusStrategyTest.kt +0 -249
  56. package/android/src/test/java/net/siteed/audiostream/AudioFormatTest.kt +0 -151
  57. package/android/src/test/java/net/siteed/audiostream/AudioFormatUtilsTest.kt +0 -273
  58. package/android/src/test/java/net/siteed/audiostream/DeviceDisconnectionFallbackUnitTest.kt +0 -140
  59. package/android/src/test/resources/chorus.wav +0 -0
  60. package/android/src/test/resources/generate_test_audio.py +0 -94
  61. package/android/src/test/resources/jfk.wav +0 -0
  62. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  63. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  64. package/app.plugin.js +0 -3
  65. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +0 -4
  66. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  67. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +0 -210
  68. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  69. package/build/cjs/AudioAnalysis/extractAudioData.js +0 -21
  70. package/build/cjs/AudioAnalysis/extractAudioData.js.map +0 -1
  71. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +0 -92
  72. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  73. package/build/cjs/AudioAnalysis/extractPreview.js +0 -28
  74. package/build/cjs/AudioAnalysis/extractPreview.js.map +0 -1
  75. package/build/cjs/AudioAnalysis/extractWaveform.js +0 -18
  76. package/build/cjs/AudioAnalysis/extractWaveform.js.map +0 -1
  77. package/build/cjs/AudioDeviceManager.js +0 -689
  78. package/build/cjs/AudioDeviceManager.js.map +0 -1
  79. package/build/cjs/AudioRecorder.provider.js +0 -78
  80. package/build/cjs/AudioRecorder.provider.js.map +0 -1
  81. package/build/cjs/ExpoAudioStream.native.js +0 -8
  82. package/build/cjs/ExpoAudioStream.native.js.map +0 -1
  83. package/build/cjs/ExpoAudioStream.types.js +0 -11
  84. package/build/cjs/ExpoAudioStream.types.js.map +0 -1
  85. package/build/cjs/ExpoAudioStream.web.js +0 -708
  86. package/build/cjs/ExpoAudioStream.web.js.map +0 -1
  87. package/build/cjs/ExpoAudioStreamModule.js +0 -718
  88. package/build/cjs/ExpoAudioStreamModule.js.map +0 -1
  89. package/build/cjs/WebRecorder.web.js +0 -777
  90. package/build/cjs/WebRecorder.web.js.map +0 -1
  91. package/build/cjs/constants/platformLimitations.js +0 -99
  92. package/build/cjs/constants/platformLimitations.js.map +0 -1
  93. package/build/cjs/constants.js +0 -17
  94. package/build/cjs/constants.js.map +0 -1
  95. package/build/cjs/events.js +0 -29
  96. package/build/cjs/events.js.map +0 -1
  97. package/build/cjs/hooks/useAudioDevices.js +0 -179
  98. package/build/cjs/hooks/useAudioDevices.js.map +0 -1
  99. package/build/cjs/index.js +0 -58
  100. package/build/cjs/index.js.map +0 -1
  101. package/build/cjs/trimAudio.js +0 -76
  102. package/build/cjs/trimAudio.js.map +0 -1
  103. package/build/cjs/useAudioRecorder.js +0 -518
  104. package/build/cjs/useAudioRecorder.js.map +0 -1
  105. package/build/cjs/utils/BlobFix.js +0 -502
  106. package/build/cjs/utils/BlobFix.js.map +0 -1
  107. package/build/cjs/utils/audioProcessing.js +0 -136
  108. package/build/cjs/utils/audioProcessing.js.map +0 -1
  109. package/build/cjs/utils/cleanNativeOptions.js +0 -22
  110. package/build/cjs/utils/cleanNativeOptions.js.map +0 -1
  111. package/build/cjs/utils/concatenateBuffers.js +0 -25
  112. package/build/cjs/utils/concatenateBuffers.js.map +0 -1
  113. package/build/cjs/utils/convertPCMToFloat32.js +0 -124
  114. package/build/cjs/utils/convertPCMToFloat32.js.map +0 -1
  115. package/build/cjs/utils/crc32.js +0 -52
  116. package/build/cjs/utils/crc32.js.map +0 -1
  117. package/build/cjs/utils/encodingToBitDepth.js +0 -17
  118. package/build/cjs/utils/encodingToBitDepth.js.map +0 -1
  119. package/build/cjs/utils/getWavFileInfo.js +0 -96
  120. package/build/cjs/utils/getWavFileInfo.js.map +0 -1
  121. package/build/cjs/utils/writeWavHeader.js +0 -88
  122. package/build/cjs/utils/writeWavHeader.js.map +0 -1
  123. package/build/cjs/workers/InlineFeaturesExtractor.web.js +0 -859
  124. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +0 -1
  125. package/build/cjs/workers/inlineAudioWebWorker.web.js +0 -184
  126. package/build/cjs/workers/inlineAudioWebWorker.web.js.map +0 -1
  127. package/build/esm/AudioAnalysis/AudioAnalysis.types.js +0 -3
  128. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  129. package/build/esm/AudioAnalysis/extractAudioAnalysis.js +0 -202
  130. package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  131. package/build/esm/AudioAnalysis/extractAudioData.js +0 -14
  132. package/build/esm/AudioAnalysis/extractAudioData.js.map +0 -1
  133. package/build/esm/AudioAnalysis/extractMelSpectrogram.js +0 -89
  134. package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  135. package/build/esm/AudioAnalysis/extractPreview.js +0 -25
  136. package/build/esm/AudioAnalysis/extractPreview.js.map +0 -1
  137. package/build/esm/AudioAnalysis/extractWaveform.js +0 -11
  138. package/build/esm/AudioAnalysis/extractWaveform.js.map +0 -1
  139. package/build/esm/AudioDeviceManager.js +0 -682
  140. package/build/esm/AudioDeviceManager.js.map +0 -1
  141. package/build/esm/AudioRecorder.provider.js +0 -40
  142. package/build/esm/AudioRecorder.provider.js.map +0 -1
  143. package/build/esm/ExpoAudioStream.native.js +0 -6
  144. package/build/esm/ExpoAudioStream.native.js.map +0 -1
  145. package/build/esm/ExpoAudioStream.types.js +0 -8
  146. package/build/esm/ExpoAudioStream.types.js.map +0 -1
  147. package/build/esm/ExpoAudioStream.web.js +0 -704
  148. package/build/esm/ExpoAudioStream.web.js.map +0 -1
  149. package/build/esm/ExpoAudioStreamModule.js +0 -713
  150. package/build/esm/ExpoAudioStreamModule.js.map +0 -1
  151. package/build/esm/WebRecorder.web.js +0 -773
  152. package/build/esm/WebRecorder.web.js.map +0 -1
  153. package/build/esm/constants/platformLimitations.js +0 -90
  154. package/build/esm/constants/platformLimitations.js.map +0 -1
  155. package/build/esm/constants.js +0 -14
  156. package/build/esm/constants.js.map +0 -1
  157. package/build/esm/events.js +0 -21
  158. package/build/esm/events.js.map +0 -1
  159. package/build/esm/hooks/useAudioDevices.js +0 -176
  160. package/build/esm/hooks/useAudioDevices.js.map +0 -1
  161. package/build/esm/index.js +0 -20
  162. package/build/esm/index.js.map +0 -1
  163. package/build/esm/trimAudio.js +0 -69
  164. package/build/esm/trimAudio.js.map +0 -1
  165. package/build/esm/useAudioRecorder.js +0 -512
  166. package/build/esm/useAudioRecorder.js.map +0 -1
  167. package/build/esm/utils/BlobFix.js +0 -498
  168. package/build/esm/utils/BlobFix.js.map +0 -1
  169. package/build/esm/utils/audioProcessing.js +0 -133
  170. package/build/esm/utils/audioProcessing.js.map +0 -1
  171. package/build/esm/utils/cleanNativeOptions.js +0 -19
  172. package/build/esm/utils/cleanNativeOptions.js.map +0 -1
  173. package/build/esm/utils/concatenateBuffers.js +0 -21
  174. package/build/esm/utils/concatenateBuffers.js.map +0 -1
  175. package/build/esm/utils/convertPCMToFloat32.js +0 -120
  176. package/build/esm/utils/convertPCMToFloat32.js.map +0 -1
  177. package/build/esm/utils/crc32.js +0 -50
  178. package/build/esm/utils/crc32.js.map +0 -1
  179. package/build/esm/utils/encodingToBitDepth.js +0 -13
  180. package/build/esm/utils/encodingToBitDepth.js.map +0 -1
  181. package/build/esm/utils/getWavFileInfo.js +0 -92
  182. package/build/esm/utils/getWavFileInfo.js.map +0 -1
  183. package/build/esm/utils/writeWavHeader.js +0 -84
  184. package/build/esm/utils/writeWavHeader.js.map +0 -1
  185. package/build/esm/workers/InlineFeaturesExtractor.web.js +0 -856
  186. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +0 -1
  187. package/build/esm/workers/inlineAudioWebWorker.web.js +0 -181
  188. package/build/esm/workers/inlineAudioWebWorker.web.js.map +0 -1
  189. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +0 -196
  190. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  191. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts +0 -74
  192. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  193. package/build/types/AudioAnalysis/extractAudioData.d.ts +0 -3
  194. package/build/types/AudioAnalysis/extractAudioData.d.ts.map +0 -1
  195. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts +0 -14
  196. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
  197. package/build/types/AudioAnalysis/extractPreview.d.ts +0 -11
  198. package/build/types/AudioAnalysis/extractPreview.d.ts.map +0 -1
  199. package/build/types/AudioAnalysis/extractWaveform.d.ts +0 -8
  200. package/build/types/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  201. package/build/types/AudioDeviceManager.d.ts +0 -187
  202. package/build/types/AudioDeviceManager.d.ts.map +0 -1
  203. package/build/types/AudioRecorder.provider.d.ts +0 -11
  204. package/build/types/AudioRecorder.provider.d.ts.map +0 -1
  205. package/build/types/ExpoAudioStream.native.d.ts +0 -3
  206. package/build/types/ExpoAudioStream.native.d.ts.map +0 -1
  207. package/build/types/ExpoAudioStream.types.d.ts +0 -738
  208. package/build/types/ExpoAudioStream.types.d.ts.map +0 -1
  209. package/build/types/ExpoAudioStream.web.d.ts +0 -96
  210. package/build/types/ExpoAudioStream.web.d.ts.map +0 -1
  211. package/build/types/ExpoAudioStreamModule.d.ts +0 -3
  212. package/build/types/ExpoAudioStreamModule.d.ts.map +0 -1
  213. package/build/types/WebRecorder.web.d.ts +0 -198
  214. package/build/types/WebRecorder.web.d.ts.map +0 -1
  215. package/build/types/constants/platformLimitations.d.ts +0 -40
  216. package/build/types/constants/platformLimitations.d.ts.map +0 -1
  217. package/build/types/constants.d.ts +0 -11
  218. package/build/types/constants.d.ts.map +0 -1
  219. package/build/types/events.d.ts +0 -26
  220. package/build/types/events.d.ts.map +0 -1
  221. package/build/types/hooks/useAudioDevices.d.ts +0 -15
  222. package/build/types/hooks/useAudioDevices.d.ts.map +0 -1
  223. package/build/types/index.d.ts +0 -18
  224. package/build/types/index.d.ts.map +0 -1
  225. package/build/types/trimAudio.d.ts +0 -25
  226. package/build/types/trimAudio.d.ts.map +0 -1
  227. package/build/types/useAudioRecorder.d.ts +0 -22
  228. package/build/types/useAudioRecorder.d.ts.map +0 -1
  229. package/build/types/utils/BlobFix.d.ts +0 -9
  230. package/build/types/utils/BlobFix.d.ts.map +0 -1
  231. package/build/types/utils/audioProcessing.d.ts +0 -24
  232. package/build/types/utils/audioProcessing.d.ts.map +0 -1
  233. package/build/types/utils/cleanNativeOptions.d.ts +0 -15
  234. package/build/types/utils/cleanNativeOptions.d.ts.map +0 -1
  235. package/build/types/utils/concatenateBuffers.d.ts +0 -8
  236. package/build/types/utils/concatenateBuffers.d.ts.map +0 -1
  237. package/build/types/utils/convertPCMToFloat32.d.ts +0 -13
  238. package/build/types/utils/convertPCMToFloat32.d.ts.map +0 -1
  239. package/build/types/utils/crc32.d.ts +0 -7
  240. package/build/types/utils/crc32.d.ts.map +0 -1
  241. package/build/types/utils/encodingToBitDepth.d.ts +0 -5
  242. package/build/types/utils/encodingToBitDepth.d.ts.map +0 -1
  243. package/build/types/utils/getWavFileInfo.d.ts +0 -26
  244. package/build/types/utils/getWavFileInfo.d.ts.map +0 -1
  245. package/build/types/utils/writeWavHeader.d.ts +0 -34
  246. package/build/types/utils/writeWavHeader.d.ts.map +0 -1
  247. package/build/types/workers/InlineFeaturesExtractor.web.d.ts +0 -2
  248. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  249. package/build/types/workers/inlineAudioWebWorker.web.d.ts +0 -2
  250. package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  251. package/expo-module.config.json +0 -10
  252. package/ios/AudioAnalysisData.swift +0 -74
  253. package/ios/AudioDeviceManager.swift +0 -666
  254. package/ios/AudioNotificationManager.swift +0 -154
  255. package/ios/AudioProcessingHelpers.swift +0 -743
  256. package/ios/AudioProcessor.swift +0 -1151
  257. package/ios/AudioStreamError.swift +0 -7
  258. package/ios/AudioStreamManager.swift +0 -2352
  259. package/ios/AudioStreamManagerDelegate.swift +0 -16
  260. package/ios/DataPoint.swift +0 -54
  261. package/ios/DecodingConfig.swift +0 -59
  262. package/ios/ExpoAudioStream.podspec +0 -33
  263. package/ios/ExpoAudioStreamModule.swift +0 -1013
  264. package/ios/ExpoAudioStudioTests/AudioFileHandlerTests.swift +0 -338
  265. package/ios/ExpoAudioStudioTests/AudioFormatUtilsTests.swift +0 -331
  266. package/ios/ExpoAudioStudioTests/AudioTestHelpers.swift +0 -130
  267. package/ios/ExpoAudioStudioTests/CompressedOnlyOutputTests.swift +0 -294
  268. package/ios/ExpoAudioStudioTests/EventEmissionIntervalTests.swift +0 -105
  269. package/ios/ExpoAudioStudioTests/Info.plist +0 -22
  270. package/ios/ExpoAudioStudioTests/README.md +0 -39
  271. package/ios/ExpoAudioStudioTests/SimpleAudioTest.swift +0 -98
  272. package/ios/ExpoAudioStudioTests/TestAudioGenerator.swift +0 -75
  273. package/ios/FFT.swift +0 -62
  274. package/ios/Features.swift +0 -95
  275. package/ios/ISSUE_IOS.md +0 -68
  276. package/ios/Logger.swift +0 -39
  277. package/ios/NotificationExtension.swift +0 -15
  278. package/ios/RecordingResult.swift +0 -22
  279. package/ios/RecordingSettings.swift +0 -308
  280. package/ios/WaveformExtractor.swift +0 -105
  281. package/ios/tests/README.md +0 -41
  282. package/ios/tests/integration/buffer_and_fallback_test.swift +0 -178
  283. package/ios/tests/integration/buffer_duration_test.swift +0 -185
  284. package/ios/tests/integration/compressed_only_output_test.swift +0 -271
  285. package/ios/tests/integration/output_control_test.swift +0 -322
  286. package/ios/tests/integration/run_integration_tests.sh +0 -37
  287. package/ios/tests/opus_support_test_macos.swift +0 -154
  288. package/ios/tests/standalone/audio_processing_test.swift +0 -144
  289. package/ios/tests/standalone/audio_recording_test.swift +0 -277
  290. package/ios/tests/standalone/audio_streaming_test.swift +0 -249
  291. package/ios/tests/standalone/standalone_test.swift +0 -144
  292. package/plugin/build/index.cjs +0 -194
  293. package/plugin/build/index.d.cts +0 -22
  294. package/plugin/build/index.js +0 -194
  295. package/plugin/src/index.ts +0 -285
  296. package/plugin/tsconfig.json +0 -10
  297. package/plugin/tsconfig.tsbuildinfo +0 -1
  298. package/src/AudioAnalysis/AudioAnalysis.types.ts +0 -224
  299. package/src/AudioAnalysis/extractAudioAnalysis.ts +0 -344
  300. package/src/AudioAnalysis/extractAudioData.ts +0 -17
  301. package/src/AudioAnalysis/extractMelSpectrogram.ts +0 -154
  302. package/src/AudioAnalysis/extractPreview.ts +0 -34
  303. package/src/AudioAnalysis/extractWaveform.ts +0 -22
  304. package/src/AudioDeviceManager.ts +0 -803
  305. package/src/AudioRecorder.provider.tsx +0 -57
  306. package/src/ExpoAudioStream.native.ts +0 -6
  307. package/src/ExpoAudioStream.types.ts +0 -874
  308. package/src/ExpoAudioStream.web.ts +0 -905
  309. package/src/ExpoAudioStreamModule.ts +0 -990
  310. package/src/WebRecorder.web.ts +0 -1005
  311. package/src/constants/platformLimitations.ts +0 -118
  312. package/src/constants.ts +0 -18
  313. package/src/events.ts +0 -60
  314. package/src/hooks/useAudioDevices.ts +0 -213
  315. package/src/index.ts +0 -54
  316. package/src/trimAudio.ts +0 -94
  317. package/src/types/crc-32.d.ts +0 -9
  318. package/src/useAudioRecorder.tsx +0 -766
  319. package/src/utils/BlobFix.ts +0 -561
  320. package/src/utils/audioProcessing.ts +0 -205
  321. package/src/utils/cleanNativeOptions.ts +0 -18
  322. package/src/utils/concatenateBuffers.ts +0 -24
  323. package/src/utils/convertPCMToFloat32.ts +0 -170
  324. package/src/utils/crc32.ts +0 -59
  325. package/src/utils/encodingToBitDepth.ts +0 -18
  326. package/src/utils/getWavFileInfo.ts +0 -132
  327. package/src/utils/writeWavHeader.ts +0 -115
  328. package/src/workers/InlineFeaturesExtractor.web.tsx +0 -855
  329. package/src/workers/inlineAudioWebWorker.web.tsx +0 -180
@@ -1,666 +0,0 @@
1
- //
2
- // AudioDeviceManager.swift
3
- // Pods
4
- //
5
- // Created by Arthur on 4/29/25.
6
- //
7
-
8
- import Foundation
9
- import AVFoundation
10
- import ExpoModulesCore
11
-
12
- // MARK: - Delegate Protocol
13
- protocol AudioDeviceManagerDelegate: AnyObject {
14
- func audioDeviceManager(_ manager: AudioDeviceManager, didDetectDisconnectionOfDevice deviceId: String)
15
- // Future delegate methods can be added here
16
- }
17
-
18
- /// Manages audio device detection, selection and capabilities
19
- class AudioDeviceManager {
20
- // MARK: - Properties
21
- weak var delegate: AudioDeviceManagerDelegate? // Add delegate property
22
-
23
- // MARK: - Device Type Constants
24
-
25
- // Constants for device types - standardized across platforms
26
- private let deviceTypeBuiltinMic = "builtin_mic"
27
- private let deviceTypeBluetooth = "bluetooth"
28
- private let deviceTypeUSB = "usb"
29
- private let deviceTypeWiredHeadset = "wired_headset"
30
- private let deviceTypeWiredHeadphones = "wired_headphones"
31
- private let deviceTypeSpeaker = "speaker"
32
- private let deviceTypeUnknown = "unknown"
33
-
34
- // Flag to prevent infinite loops
35
- private static var isAudioSessionPrepared = false
36
- private static var lastPreparationTime: TimeInterval = 0
37
-
38
- // Observer handle
39
- private var routeChangeObserver: Any?
40
-
41
- // MARK: - Initialization and Deinitialization
42
- init() {
43
- // Start monitoring route changes on initialization
44
- startMonitoringDeviceChanges()
45
- }
46
-
47
- deinit {
48
- // Stop monitoring when the instance is deallocated
49
- stopMonitoringDeviceChanges()
50
- }
51
-
52
- // MARK: - Public Methods
53
-
54
- /// Maps AVAudioSession port types to standardized device types
55
- func mapDeviceType(_ portType: AVAudioSession.Port) -> String {
56
- Logger.debug("AudioDeviceManager", "Mapping device type for port: \(portType.rawValue)")
57
- switch portType {
58
- case .builtInMic:
59
- return deviceTypeBuiltinMic
60
- case .bluetoothHFP, .bluetoothA2DP, .bluetoothLE:
61
- return deviceTypeBluetooth
62
- case .headphones:
63
- return deviceTypeWiredHeadphones
64
- case .headsetMic:
65
- return deviceTypeWiredHeadset
66
- case .usbAudio:
67
- return deviceTypeUSB
68
- case .builtInSpeaker:
69
- return deviceTypeSpeaker
70
- default:
71
- return deviceTypeUnknown
72
- }
73
- }
74
-
75
- /// Prepares the audio session to detect all available devices, including Bluetooth
76
- private func prepareAudioSession(force: Bool = false) -> Bool {
77
- // Skip preparation if already prepared and not forcing
78
- let now = Date().timeIntervalSince1970
79
- let timeSinceLastPreparation = now - AudioDeviceManager.lastPreparationTime
80
-
81
- if AudioDeviceManager.isAudioSessionPrepared && !force && timeSinceLastPreparation < 5.0 {
82
- Logger.debug("AudioDeviceManager", "Audio session already prepared, skipping")
83
- return true
84
- }
85
-
86
- Logger.debug("AudioDeviceManager", "Preparing audio session for device detection")
87
- do {
88
- let session = AVAudioSession.sharedInstance()
89
-
90
- // Configure with options needed for Bluetooth detection
91
- try session.setCategory(.playAndRecord, mode: .default, options: [.allowBluetooth, .allowBluetoothA2DP, .mixWithOthers])
92
-
93
- // Activate the session
94
- try session.setActive(true, options: .notifyOthersOnDeactivation)
95
-
96
- // Give the system a moment to detect Bluetooth devices if needed
97
- // Minimal delay that still allows devices to be detected
98
- Thread.sleep(forTimeInterval: 0.1)
99
-
100
- // Mark as prepared
101
- AudioDeviceManager.isAudioSessionPrepared = true
102
- AudioDeviceManager.lastPreparationTime = now
103
-
104
- Logger.debug("AudioDeviceManager", "Audio session prepared for device detection")
105
- return true
106
- } catch {
107
- Logger.debug("AudioDeviceManager", "Failed to prepare audio session: \(error.localizedDescription)")
108
- return false
109
- }
110
- }
111
-
112
- /// Force a refresh of the audio session preparation
113
- public func forceRefreshAudioSession() -> Bool {
114
- // Only allow force refresh once every second to prevent excessive refreshes
115
- let now = Date().timeIntervalSince1970
116
- let timeSinceLastPreparation = now - AudioDeviceManager.lastPreparationTime
117
-
118
- if timeSinceLastPreparation < 1.0 {
119
- Logger.debug("AudioDeviceManager", "Skipping force refresh - too soon since last preparation (\(timeSinceLastPreparation) seconds)")
120
- return false
121
- }
122
-
123
- return prepareAudioSession(force: true)
124
- }
125
-
126
- /// Gets capabilities for an audio input device
127
- func getDeviceCapabilities(_ port: AVAudioSessionPortDescription) -> [String: Any] {
128
- Logger.debug("AudioDeviceManager", "Getting capabilities for device: \(port.portName) (ID: \(port.uid))")
129
- let session = AVAudioSession.sharedInstance()
130
-
131
- // Test standard sample rates for support
132
- let sampleRates = [8000, 16000, 22050, 44100, 48000, 96000].filter { rate in
133
- let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: Double(rate), channels: 1, interleaved: false)
134
- return session.isInputAvailable && format != nil
135
- }
136
-
137
- return [
138
- "sampleRates": sampleRates,
139
- "channelCounts": [1, 2], // Most iOS devices support mono and stereo
140
- "bitDepths": [16, 24], // Common bit depths on iOS
141
- "hasEchoCancellation": true, // iOS doesn't expose this per-device, set to true as it's generally available
142
- "hasNoiseSuppression": true, // iOS doesn't expose this per-device, set to true as it's generally available
143
- "hasAutomaticGainControl": true // iOS doesn't expose this per-device, set to true as it's generally available
144
- ]
145
- }
146
-
147
- /// Gets a list of available audio input devices
148
- func getAvailableInputDevices(promise: Promise) {
149
- Logger.debug("AudioDeviceManager", "Getting available input devices")
150
-
151
- // Prepare audio session if needed
152
- let prepared = prepareAudioSession()
153
- if !prepared {
154
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device list may be incomplete")
155
- }
156
-
157
- do {
158
- let session = AVAudioSession.sharedInstance()
159
-
160
- // We should have already activated the session in prepareAudioSession
161
- // But ensure it's active just in case
162
- try session.setActive(true)
163
-
164
- let currentPreferredInput = session.preferredInput
165
-
166
- var devices = [[String: Any]]()
167
-
168
- // First add current route devices as they're definitely available
169
- for input in session.currentRoute.inputs {
170
- let deviceType = mapDeviceType(input.portType)
171
- let isDefault = currentPreferredInput == nil ?
172
- (input.portType == .builtInMic) : // Default is usually built-in mic
173
- (input.uid == currentPreferredInput?.uid)
174
-
175
- let deviceId = normalizeBluetoothDeviceId(input.uid)
176
-
177
- Logger.debug("AudioDeviceManager", "Current route device: \(input.portName) (type: \(deviceType), ID: \(deviceId))")
178
-
179
- devices.append([
180
- "id": deviceId,
181
- "name": input.portName,
182
- "type": deviceType,
183
- "isDefault": isDefault,
184
- "capabilities": getDeviceCapabilities(input),
185
- "isAvailable": true,
186
- "source": "currentRoute"
187
- ])
188
- }
189
-
190
- // Then add from availableInputs
191
- if let availableInputs = session.availableInputs {
192
- for port in availableInputs {
193
- let deviceType = mapDeviceType(port.portType)
194
- let isDefault = currentPreferredInput == nil ?
195
- (port.portType == .builtInMic) : // Default is usually built-in mic
196
- (port.uid == currentPreferredInput?.uid)
197
-
198
- let deviceId = normalizeBluetoothDeviceId(port.uid)
199
-
200
- // Skip if already in our list
201
- if !devices.contains(where: { ($0["id"] as? String) == deviceId }) {
202
- Logger.debug("AudioDeviceManager", "Available input: \(port.portName) (type: \(deviceType), ID: \(deviceId))")
203
-
204
- devices.append([
205
- "id": deviceId,
206
- "name": port.portName,
207
- "type": deviceType,
208
- "isDefault": isDefault,
209
- "capabilities": getDeviceCapabilities(port),
210
- "isAvailable": true,
211
- "source": "availableInputs"
212
- ])
213
- }
214
- }
215
- }
216
-
217
- Logger.debug("AudioDeviceManager", "Found \(devices.count) available input devices")
218
- promise.resolve(devices)
219
- } catch {
220
- Logger.debug("AudioDeviceManager", "Error getting available input devices: \(error.localizedDescription)")
221
- promise.reject("DEVICE_DETECTION_ERROR", "Failed to get available audio devices: \(error.localizedDescription)")
222
- }
223
- }
224
-
225
- /// Gets the currently selected audio input device
226
- func getCurrentInputDevice(promise: Promise) {
227
- Logger.debug("AudioDeviceManager", "Getting current input device")
228
-
229
- // Prepare audio session if needed
230
- let prepared = prepareAudioSession()
231
- if !prepared {
232
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, current device may not be correctly detected")
233
- }
234
-
235
- do {
236
- let session = AVAudioSession.sharedInstance()
237
-
238
- // We should have already activated the session in prepareAudioSession
239
- // But ensure it's active just in case
240
- try session.setActive(true)
241
-
242
- // Check current route first
243
- if let currentPort = session.currentRoute.inputs.first {
244
- let deviceType = mapDeviceType(currentPort.portType)
245
- let isDefault = session.preferredInput == nil || session.preferredInput?.portType == currentPort.portType
246
- let deviceId = normalizeBluetoothDeviceId(currentPort.uid)
247
-
248
- Logger.debug("AudioDeviceManager", "Current input device: \(currentPort.portName) (ID: \(deviceId), type: \(deviceType))")
249
-
250
- let device: [String: Any] = [
251
- "id": deviceId,
252
- "name": currentPort.portName,
253
- "type": deviceType,
254
- "isDefault": isDefault,
255
- "capabilities": getDeviceCapabilities(currentPort),
256
- "isAvailable": true,
257
- "source": "currentRoute"
258
- ]
259
-
260
- promise.resolve(device)
261
- return
262
- }
263
-
264
- // Fallback to preferred input
265
- if let preferredInput = session.preferredInput {
266
- let deviceType = mapDeviceType(preferredInput.portType)
267
- let deviceId = normalizeBluetoothDeviceId(preferredInput.uid)
268
-
269
- Logger.debug("AudioDeviceManager", "Current input from preferred: \(preferredInput.portName) (ID: \(deviceId), type: \(deviceType))")
270
-
271
- let device: [String: Any] = [
272
- "id": deviceId,
273
- "name": preferredInput.portName,
274
- "type": deviceType,
275
- "isDefault": true,
276
- "capabilities": getDeviceCapabilities(preferredInput),
277
- "isAvailable": true,
278
- "source": "preferredInput"
279
- ]
280
-
281
- promise.resolve(device)
282
- return
283
- }
284
-
285
- // No input device is currently selected
286
- Logger.debug("AudioDeviceManager", "No current input device found")
287
- promise.resolve(nil)
288
- } catch {
289
- Logger.debug("AudioDeviceManager", "Error getting current input device: \(error.localizedDescription)")
290
- promise.reject("DEVICE_DETECTION_ERROR", "Failed to get current audio device: \(error.localizedDescription)")
291
- }
292
- }
293
-
294
- /// Gets the default audio input device (usually built-in mic)
295
- /// This is an async version useful for fallback logic.
296
- func getDefaultInputDevice() async -> AudioDevice? {
297
- Logger.debug("AudioDeviceManager", "Getting default input device")
298
-
299
- let prepared = prepareAudioSession()
300
- if !prepared {
301
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, default device detection may be inaccurate")
302
- }
303
-
304
- let session = AVAudioSession.sharedInstance()
305
- do {
306
- try session.setActive(true) // Ensure session is active
307
-
308
- // Find the built-in microphone port, which is typically the default fallback
309
- if let defaultPort = session.availableInputs?.first(where: { $0.portType == .builtInMic }) {
310
- let deviceType = mapDeviceType(defaultPort.portType)
311
- let deviceId = normalizeBluetoothDeviceId(defaultPort.uid)
312
- let capabilities = getDeviceCapabilities(defaultPort)
313
-
314
- Logger.debug("AudioDeviceManager", "Found default device: \(defaultPort.portName) (ID: \(deviceId), Type: \(deviceType))")
315
-
316
- // Convert capabilities dictionary to Capabilities struct/object if needed
317
- let audioCapabilities = AudioDeviceCapabilities(
318
- sampleRates: capabilities["sampleRates"] as? [Int] ?? [],
319
- channelCounts: capabilities["channelCounts"] as? [Int] ?? [],
320
- bitDepths: capabilities["bitDepths"] as? [Int] ?? []
321
- // Add boolean flags if available in your dictionary
322
- )
323
-
324
- return AudioDevice(
325
- id: deviceId,
326
- name: defaultPort.portName,
327
- type: deviceType,
328
- isDefault: true, // Assume it's the default we're looking for
329
- capabilities: audioCapabilities,
330
- isAvailable: true // It's available if found here
331
- )
332
- } else {
333
- Logger.debug("AudioDeviceManager", "Could not find built-in mic as default device.")
334
- return nil
335
- }
336
- } catch {
337
- Logger.debug("AudioDeviceManager", "Error getting default input device: \(error)")
338
- return nil
339
- }
340
- }
341
-
342
- /// Selects a specific audio input device for recording
343
- func selectInputDevice(_ deviceId: String, promise: Promise) {
344
- Logger.debug("AudioDeviceManager", "Attempting to select input device with ID: \(deviceId)")
345
-
346
- // Prepare audio session - use force: true for device selection to ensure we get the latest devices
347
- let prepared = prepareAudioSession(force: true)
348
- if !prepared {
349
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device selection may not work correctly")
350
- }
351
-
352
- do {
353
- let session = AVAudioSession.sharedInstance()
354
-
355
- // Ensure the session is active
356
- try session.setActive(true)
357
-
358
- // For Bluetooth devices, normalize and match by prefix
359
- let normalizedRequestedId = normalizeBluetoothDeviceId(deviceId)
360
- let isBluetoothDevice = deviceId.contains(":")
361
-
362
- Logger.debug("AudioDeviceManager", "Selecting \(isBluetoothDevice ? "Bluetooth" : "non-Bluetooth") device with normalized ID: \(normalizedRequestedId)")
363
-
364
- // Find the device with the specified ID
365
- let selectedPort: AVAudioSessionPortDescription?
366
-
367
- if isBluetoothDevice {
368
- // For Bluetooth devices, match by normalized ID
369
- selectedPort = session.availableInputs?.first { port in
370
- let portNormalizedId = normalizeBluetoothDeviceId(port.uid)
371
- let matches = portNormalizedId == normalizedRequestedId
372
- Logger.debug("AudioDeviceManager", "Checking device \(port.portName) (ID: \(port.uid), Normalized: \(portNormalizedId)) - Matches: \(matches)")
373
- return matches
374
- }
375
- } else {
376
- // For non-Bluetooth devices, direct match
377
- selectedPort = session.availableInputs?.first { port in
378
- let matches = port.uid == deviceId
379
- Logger.debug("AudioDeviceManager", "Checking device \(port.portName) (ID: \(port.uid)) - Matches: \(matches)")
380
- return matches
381
- }
382
- }
383
-
384
- guard let selectedPort = selectedPort else {
385
- Logger.debug("AudioDeviceManager", "Device not found with ID \(deviceId)")
386
-
387
- // Log all available devices to help debugging
388
- if let availableInputs = session.availableInputs {
389
- Logger.debug("AudioDeviceManager", "Available devices:")
390
- for (index, device) in availableInputs.enumerated() {
391
- Logger.debug("AudioDeviceManager", "\(index+1). \(device.portName) (ID: \(device.uid), Normalized: \(normalizeBluetoothDeviceId(device.uid)))")
392
- }
393
- } else {
394
- Logger.debug("AudioDeviceManager", "No available devices found")
395
- }
396
-
397
- promise.reject("DEVICE_NOT_FOUND", "The selected audio device is not available")
398
- return
399
- }
400
-
401
- // Set the preferred input device
402
- Logger.debug("AudioDeviceManager", "Setting preferred input to: \(selectedPort.portName) (ID: \(selectedPort.uid))")
403
- try session.setPreferredInput(selectedPort)
404
-
405
- // Verify selection
406
- if let currentInput = session.currentRoute.inputs.first {
407
- let succeeded = (currentInput.uid == selectedPort.uid ||
408
- normalizeBluetoothDeviceId(currentInput.uid) == normalizeBluetoothDeviceId(selectedPort.uid))
409
- Logger.debug("AudioDeviceManager", "Device selection \(succeeded ? "succeeded" : "failed") - Current device: \(currentInput.portName) (ID: \(currentInput.uid))")
410
- }
411
-
412
- Logger.debug("AudioDeviceManager", "Device selected successfully")
413
- promise.resolve(true)
414
- } catch {
415
- Logger.debug("AudioDeviceManager", "Failed to select device: \(error.localizedDescription)")
416
- promise.reject("DEVICE_SELECTION_FAILED", "Failed to select audio device: \(error.localizedDescription)")
417
- }
418
- }
419
-
420
- /// Selects a specific audio input device asynchronously (useful for internal calls)
421
- func selectDevice(_ deviceId: String) async -> Bool {
422
- Logger.debug("AudioDeviceManager", "Attempting to select input device with ID: \(deviceId) (async)")
423
-
424
- let prepared = prepareAudioSession(force: true)
425
- if !prepared {
426
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device selection may not work correctly")
427
- return false
428
- }
429
-
430
- do {
431
- let session = AVAudioSession.sharedInstance()
432
- try session.setActive(true)
433
-
434
- let normalizedRequestedId = normalizeBluetoothDeviceId(deviceId)
435
- let isBluetoothDevice = deviceId.contains(":")
436
-
437
- Logger.debug("AudioDeviceManager", "Selecting \(isBluetoothDevice ? "Bluetooth" : "non-Bluetooth") device with normalized ID: \(normalizedRequestedId)")
438
-
439
- let selectedPort: AVAudioSessionPortDescription?
440
- if isBluetoothDevice {
441
- selectedPort = session.availableInputs?.first { port in
442
- normalizeBluetoothDeviceId(port.uid) == normalizedRequestedId
443
- }
444
- } else {
445
- selectedPort = session.availableInputs?.first { $0.uid == deviceId }
446
- }
447
-
448
- guard let portToSet = selectedPort else {
449
- Logger.debug("AudioDeviceManager", "Device not found with ID \(deviceId) for async selection")
450
- return false
451
- }
452
-
453
- Logger.debug("AudioDeviceManager", "Setting preferred input to: \(portToSet.portName) (ID: \(portToSet.uid)) (async)")
454
- try session.setPreferredInput(portToSet)
455
- // Add a small delay hoping the system applies the change before potential next operations
456
- try await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
457
-
458
- // Optional: Verify selection succeeded (might be less reliable immediately after setting)
459
- if let currentInput = session.currentRoute.inputs.first {
460
- let succeeded = (currentInput.uid == portToSet.uid || normalizeBluetoothDeviceId(currentInput.uid) == normalizedRequestedId)
461
- Logger.debug("AudioDeviceManager", "Async selection verification: \(succeeded ? "succeeded" : "failed")")
462
- return succeeded
463
- } else {
464
- // If no current input after setting, assume failure
465
- return false
466
- }
467
-
468
- } catch {
469
- Logger.debug("AudioDeviceManager", "Failed to select device asynchronously: \(error.localizedDescription)")
470
- return false
471
- }
472
- }
473
-
474
- /// Determines if a device is still available
475
- func isDeviceAvailable(_ deviceId: String) -> Bool {
476
- Logger.debug("AudioDeviceManager", "Checking availability for device ID: \(deviceId)")
477
-
478
- // Prepare audio session if needed
479
- let prepared = prepareAudioSession()
480
- if !prepared {
481
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device availability check may not be accurate")
482
- }
483
-
484
- let session = AVAudioSession.sharedInstance()
485
-
486
- // Handle Bluetooth devices with multiple profiles (SCO, A2DP, etc.)
487
- let isBluetoothDevice = deviceId.contains(":") // Most Bluetooth devices have MAC addresses with colons
488
-
489
- if isBluetoothDevice {
490
- // For Bluetooth devices, check if any device with the same MAC address prefix is available
491
- let baseDeviceId = deviceId.split(separator: "-").first ?? Substring(deviceId)
492
-
493
- // Log all available inputs for debugging
494
- Logger.debug("AudioDeviceManager", "Available devices to check against:")
495
- if let availableInputs = session.availableInputs {
496
- for (index, device) in availableInputs.enumerated() {
497
- let normalizedId = normalizeBluetoothDeviceId(device.uid)
498
- let matches = device.uid.starts(with: String(baseDeviceId))
499
- Logger.debug("AudioDeviceManager", "\(index+1). \(device.portName) (ID: \(device.uid), Normalized: \(normalizedId)) - Matches: \(matches)")
500
- }
501
- } else {
502
- Logger.debug("AudioDeviceManager", "No available devices found")
503
- }
504
-
505
- // Also check current route
506
- for (index, input) in session.currentRoute.inputs.enumerated() {
507
- let normalizedId = normalizeBluetoothDeviceId(input.uid)
508
- let matches = input.uid.starts(with: String(baseDeviceId))
509
- Logger.debug("AudioDeviceManager", "Current route input \(index+1): \(input.portName) (ID: \(input.uid), Normalized: \(normalizedId)) - Matches: \(matches)")
510
- }
511
-
512
- let result = session.availableInputs?.contains { $0.uid.starts(with: String(baseDeviceId)) } ?? false
513
- Logger.debug("AudioDeviceManager", "Bluetooth device \(deviceId) with base ID \(baseDeviceId) available: \(result)")
514
- return result
515
- } else {
516
- // Standard device ID check for non-Bluetooth devices
517
- return session.availableInputs?.contains { $0.uid == deviceId } ?? false
518
- }
519
- }
520
-
521
- /// Resets the selected device to system default (usually built-in mic)
522
- /// - Parameter completion: Callback with success (Bool) and optional error
523
- func resetToDefaultDevice(completion: @escaping (Bool, Error?) -> Void) {
524
- Logger.debug("AudioDeviceManager", "Attempting to reset to default input device")
525
-
526
- // Prepare audio session if needed
527
- let prepared = prepareAudioSession()
528
- if !prepared {
529
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device reset may not work correctly")
530
- }
531
-
532
- do {
533
- let session = AVAudioSession.sharedInstance()
534
-
535
- // Log current device before reset
536
- if let currentDevice = session.currentRoute.inputs.first {
537
- Logger.debug("AudioDeviceManager", "Current device before reset: \(currentDevice.portName) (ID: \(currentDevice.uid))")
538
- } else {
539
- Logger.debug("AudioDeviceManager", "No current device before reset")
540
- }
541
-
542
- // Setting preferred input to nil lets the system choose the default
543
- try session.setPreferredInput(nil)
544
-
545
- // Log the device after reset
546
- if let newDevice = session.currentRoute.inputs.first {
547
- Logger.debug("AudioDeviceManager", "Reset to default device: \(newDevice.portName) (ID: \(newDevice.uid))")
548
-
549
- // Check if it's actually the built-in mic (which is the typical default)
550
- let isBuiltIn = newDevice.portType == .builtInMic
551
- Logger.debug("AudioDeviceManager", "Reset device is built-in mic: \(isBuiltIn)")
552
- } else {
553
- Logger.debug("AudioDeviceManager", "No device found after reset")
554
- }
555
-
556
- completion(true, nil)
557
- } catch {
558
- Logger.debug("AudioDeviceManager", "Failed to reset to default device: \(error.localizedDescription)")
559
- completion(false, error)
560
- }
561
- }
562
-
563
- /// Starts monitoring device connection/disconnection events
564
- private func startMonitoringDeviceChanges() {
565
- // Ensure we don't add multiple observers
566
- stopMonitoringDeviceChanges()
567
-
568
- Logger.debug("AudioDeviceManager", "Starting device change monitoring")
569
- routeChangeObserver = NotificationCenter.default.addObserver(
570
- forName: AVAudioSession.routeChangeNotification,
571
- object: nil,
572
- queue: .main // Process on main queue to avoid threading issues with delegate calls
573
- ) { [weak self] notification in
574
- self?.handleRouteChange(notification)
575
- }
576
- }
577
-
578
- /// Stops monitoring device changes
579
- private func stopMonitoringDeviceChanges() {
580
- if let observer = routeChangeObserver {
581
- Logger.debug("AudioDeviceManager", "Stopping device change monitoring")
582
- NotificationCenter.default.removeObserver(observer)
583
- routeChangeObserver = nil
584
- }
585
- }
586
-
587
- /// Handles route change notifications to detect device connections and disconnections
588
- @objc private func handleRouteChange(_ notification: Notification) {
589
- guard let userInfo = notification.userInfo,
590
- let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
591
- let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
592
- return
593
- }
594
-
595
- Logger.debug("AudioDeviceManager", "Route change detected, reason: \(reason.rawValue)")
596
-
597
- // Only proceed if a device was potentially removed or added or the route changed significantly
598
- guard reason == .oldDeviceUnavailable || reason == .newDeviceAvailable || reason == .override || reason == .routeConfigurationChange else {
599
- Logger.debug("AudioDeviceManager", "Ignoring route change reason: \(reason.rawValue)")
600
- return
601
- }
602
-
603
- // Get the *previous* route description
604
- guard let previousRoute = userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription else {
605
- Logger.debug("AudioDeviceManager", "No previous route info found for device change check.")
606
- return
607
- }
608
-
609
- // Get the *current* available input devices
610
- let currentInputs = AVAudioSession.sharedInstance().availableInputs ?? []
611
- let currentInputIds = Set(currentInputs.map { normalizeBluetoothDeviceId($0.uid) })
612
- let previousInputIds = Set(previousRoute.inputs.map { normalizeBluetoothDeviceId($0.uid) })
613
-
614
- // Check for DISCONNECTED devices (were in previous route but not in current available)
615
- for previousInputPort in previousRoute.inputs {
616
- let normalizedPreviousId = normalizeBluetoothDeviceId(previousInputPort.uid)
617
- if !currentInputIds.contains(normalizedPreviousId) {
618
- Logger.debug("AudioDeviceManager", "Detected disconnection of device: \(previousInputPort.portName) (Normalized ID: \(normalizedPreviousId))")
619
- // Keep existing disconnection delegate method unchanged
620
- delegate?.audioDeviceManager(self, didDetectDisconnectionOfDevice: normalizedPreviousId)
621
- }
622
- }
623
-
624
- // Check for CONNECTED devices (are in current available but were not in previous route)
625
- for currentInput in currentInputs {
626
- let normalizedCurrentId = normalizeBluetoothDeviceId(currentInput.uid)
627
- if !previousInputIds.contains(normalizedCurrentId) {
628
- Logger.debug("AudioDeviceManager", "Detected connection of device: \(currentInput.portName) (Normalized ID: \(normalizedCurrentId))")
629
- // Emit connection event via notification
630
- NotificationCenter.default.post(
631
- name: NSNotification.Name("DeviceConnected"),
632
- object: nil,
633
- userInfo: ["deviceId": normalizedCurrentId]
634
- )
635
- }
636
- }
637
- }
638
-
639
- /// Normalizes Bluetooth device IDs by removing profile suffixes
640
- public func normalizeBluetoothDeviceId(_ deviceId: String) -> String {
641
- // For Bluetooth devices with MAC addresses and profile suffixes (like -tsco)
642
- if deviceId.contains(":") && deviceId.contains("-") {
643
- // Split by the hyphen and take the first part (the MAC address)
644
- return deviceId.split(separator: "-").first.map(String.init) ?? deviceId
645
- }
646
- return deviceId
647
- }
648
- }
649
-
650
- // Add structure for AudioDeviceCapabilities if not defined elsewhere
651
- struct AudioDeviceCapabilities {
652
- let sampleRates: [Int]
653
- let channelCounts: [Int]
654
- let bitDepths: [Int]
655
- // Add boolean flags if needed
656
- }
657
-
658
- // Add structure for AudioDevice if not defined elsewhere
659
- struct AudioDevice {
660
- let id: String
661
- let name: String
662
- let type: String
663
- let isDefault: Bool
664
- let capabilities: AudioDeviceCapabilities
665
- let isAvailable: Bool
666
- }