@siteed/expo-audio-studio 2.18.6 → 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 -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,670 +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
- // Preserve existing session configuration if already set up for recording
91
- if session.category == .playAndRecord {
92
- Logger.debug("AudioDeviceManager", "Session already .playAndRecord, preserving categoryOptions")
93
- try session.setActive(true, options: .notifyOthersOnDeactivation)
94
- } else {
95
- // Configure with options needed for Bluetooth detection
96
- try session.setCategory(.playAndRecord, mode: .default, options: [.allowBluetooth, .allowBluetoothA2DP, .mixWithOthers])
97
- try session.setActive(true, options: .notifyOthersOnDeactivation)
98
- }
99
-
100
- // Give the system a moment to detect Bluetooth devices if needed
101
- // Minimal delay that still allows devices to be detected
102
- Thread.sleep(forTimeInterval: 0.1)
103
-
104
- // Mark as prepared
105
- AudioDeviceManager.isAudioSessionPrepared = true
106
- AudioDeviceManager.lastPreparationTime = now
107
-
108
- Logger.debug("AudioDeviceManager", "Audio session prepared for device detection")
109
- return true
110
- } catch {
111
- Logger.debug("AudioDeviceManager", "Failed to prepare audio session: \(error.localizedDescription)")
112
- return false
113
- }
114
- }
115
-
116
- /// Force a refresh of the audio session preparation
117
- public func forceRefreshAudioSession() -> Bool {
118
- // Only allow force refresh once every second to prevent excessive refreshes
119
- let now = Date().timeIntervalSince1970
120
- let timeSinceLastPreparation = now - AudioDeviceManager.lastPreparationTime
121
-
122
- if timeSinceLastPreparation < 1.0 {
123
- Logger.debug("AudioDeviceManager", "Skipping force refresh - too soon since last preparation (\(timeSinceLastPreparation) seconds)")
124
- return false
125
- }
126
-
127
- return prepareAudioSession(force: true)
128
- }
129
-
130
- /// Gets capabilities for an audio input device
131
- func getDeviceCapabilities(_ port: AVAudioSessionPortDescription) -> [String: Any] {
132
- Logger.debug("AudioDeviceManager", "Getting capabilities for device: \(port.portName) (ID: \(port.uid))")
133
- let session = AVAudioSession.sharedInstance()
134
-
135
- // Test standard sample rates for support
136
- let sampleRates = [8000, 16000, 22050, 44100, 48000, 96000].filter { rate in
137
- let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: Double(rate), channels: 1, interleaved: false)
138
- return session.isInputAvailable && format != nil
139
- }
140
-
141
- return [
142
- "sampleRates": sampleRates,
143
- "channelCounts": [1, 2], // Most iOS devices support mono and stereo
144
- "bitDepths": [16, 24], // Common bit depths on iOS
145
- "hasEchoCancellation": true, // iOS doesn't expose this per-device, set to true as it's generally available
146
- "hasNoiseSuppression": true, // iOS doesn't expose this per-device, set to true as it's generally available
147
- "hasAutomaticGainControl": true // iOS doesn't expose this per-device, set to true as it's generally available
148
- ]
149
- }
150
-
151
- /// Gets a list of available audio input devices
152
- func getAvailableInputDevices(promise: Promise) {
153
- Logger.debug("AudioDeviceManager", "Getting available input devices")
154
-
155
- // Prepare audio session if needed
156
- let prepared = prepareAudioSession()
157
- if !prepared {
158
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device list may be incomplete")
159
- }
160
-
161
- do {
162
- let session = AVAudioSession.sharedInstance()
163
-
164
- // We should have already activated the session in prepareAudioSession
165
- // But ensure it's active just in case
166
- try session.setActive(true)
167
-
168
- let currentPreferredInput = session.preferredInput
169
-
170
- var devices = [[String: Any]]()
171
-
172
- // First add current route devices as they're definitely available
173
- for input in session.currentRoute.inputs {
174
- let deviceType = mapDeviceType(input.portType)
175
- let isDefault = currentPreferredInput == nil ?
176
- (input.portType == .builtInMic) : // Default is usually built-in mic
177
- (input.uid == currentPreferredInput?.uid)
178
-
179
- let deviceId = normalizeBluetoothDeviceId(input.uid)
180
-
181
- Logger.debug("AudioDeviceManager", "Current route device: \(input.portName) (type: \(deviceType), ID: \(deviceId))")
182
-
183
- devices.append([
184
- "id": deviceId,
185
- "name": input.portName,
186
- "type": deviceType,
187
- "isDefault": isDefault,
188
- "capabilities": getDeviceCapabilities(input),
189
- "isAvailable": true,
190
- "source": "currentRoute"
191
- ])
192
- }
193
-
194
- // Then add from availableInputs
195
- if let availableInputs = session.availableInputs {
196
- for port in availableInputs {
197
- let deviceType = mapDeviceType(port.portType)
198
- let isDefault = currentPreferredInput == nil ?
199
- (port.portType == .builtInMic) : // Default is usually built-in mic
200
- (port.uid == currentPreferredInput?.uid)
201
-
202
- let deviceId = normalizeBluetoothDeviceId(port.uid)
203
-
204
- // Skip if already in our list
205
- if !devices.contains(where: { ($0["id"] as? String) == deviceId }) {
206
- Logger.debug("AudioDeviceManager", "Available input: \(port.portName) (type: \(deviceType), ID: \(deviceId))")
207
-
208
- devices.append([
209
- "id": deviceId,
210
- "name": port.portName,
211
- "type": deviceType,
212
- "isDefault": isDefault,
213
- "capabilities": getDeviceCapabilities(port),
214
- "isAvailable": true,
215
- "source": "availableInputs"
216
- ])
217
- }
218
- }
219
- }
220
-
221
- Logger.debug("AudioDeviceManager", "Found \(devices.count) available input devices")
222
- promise.resolve(devices)
223
- } catch {
224
- Logger.debug("AudioDeviceManager", "Error getting available input devices: \(error.localizedDescription)")
225
- promise.reject("DEVICE_DETECTION_ERROR", "Failed to get available audio devices: \(error.localizedDescription)")
226
- }
227
- }
228
-
229
- /// Gets the currently selected audio input device
230
- func getCurrentInputDevice(promise: Promise) {
231
- Logger.debug("AudioDeviceManager", "Getting current input device")
232
-
233
- // Prepare audio session if needed
234
- let prepared = prepareAudioSession()
235
- if !prepared {
236
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, current device may not be correctly detected")
237
- }
238
-
239
- do {
240
- let session = AVAudioSession.sharedInstance()
241
-
242
- // We should have already activated the session in prepareAudioSession
243
- // But ensure it's active just in case
244
- try session.setActive(true)
245
-
246
- // Check current route first
247
- if let currentPort = session.currentRoute.inputs.first {
248
- let deviceType = mapDeviceType(currentPort.portType)
249
- let isDefault = session.preferredInput == nil || session.preferredInput?.portType == currentPort.portType
250
- let deviceId = normalizeBluetoothDeviceId(currentPort.uid)
251
-
252
- Logger.debug("AudioDeviceManager", "Current input device: \(currentPort.portName) (ID: \(deviceId), type: \(deviceType))")
253
-
254
- let device: [String: Any] = [
255
- "id": deviceId,
256
- "name": currentPort.portName,
257
- "type": deviceType,
258
- "isDefault": isDefault,
259
- "capabilities": getDeviceCapabilities(currentPort),
260
- "isAvailable": true,
261
- "source": "currentRoute"
262
- ]
263
-
264
- promise.resolve(device)
265
- return
266
- }
267
-
268
- // Fallback to preferred input
269
- if let preferredInput = session.preferredInput {
270
- let deviceType = mapDeviceType(preferredInput.portType)
271
- let deviceId = normalizeBluetoothDeviceId(preferredInput.uid)
272
-
273
- Logger.debug("AudioDeviceManager", "Current input from preferred: \(preferredInput.portName) (ID: \(deviceId), type: \(deviceType))")
274
-
275
- let device: [String: Any] = [
276
- "id": deviceId,
277
- "name": preferredInput.portName,
278
- "type": deviceType,
279
- "isDefault": true,
280
- "capabilities": getDeviceCapabilities(preferredInput),
281
- "isAvailable": true,
282
- "source": "preferredInput"
283
- ]
284
-
285
- promise.resolve(device)
286
- return
287
- }
288
-
289
- // No input device is currently selected
290
- Logger.debug("AudioDeviceManager", "No current input device found")
291
- promise.resolve(nil)
292
- } catch {
293
- Logger.debug("AudioDeviceManager", "Error getting current input device: \(error.localizedDescription)")
294
- promise.reject("DEVICE_DETECTION_ERROR", "Failed to get current audio device: \(error.localizedDescription)")
295
- }
296
- }
297
-
298
- /// Gets the default audio input device (usually built-in mic)
299
- /// This is an async version useful for fallback logic.
300
- func getDefaultInputDevice() async -> AudioDevice? {
301
- Logger.debug("AudioDeviceManager", "Getting default input device")
302
-
303
- let prepared = prepareAudioSession()
304
- if !prepared {
305
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, default device detection may be inaccurate")
306
- }
307
-
308
- let session = AVAudioSession.sharedInstance()
309
- do {
310
- try session.setActive(true) // Ensure session is active
311
-
312
- // Find the built-in microphone port, which is typically the default fallback
313
- if let defaultPort = session.availableInputs?.first(where: { $0.portType == .builtInMic }) {
314
- let deviceType = mapDeviceType(defaultPort.portType)
315
- let deviceId = normalizeBluetoothDeviceId(defaultPort.uid)
316
- let capabilities = getDeviceCapabilities(defaultPort)
317
-
318
- Logger.debug("AudioDeviceManager", "Found default device: \(defaultPort.portName) (ID: \(deviceId), Type: \(deviceType))")
319
-
320
- // Convert capabilities dictionary to Capabilities struct/object if needed
321
- let audioCapabilities = AudioDeviceCapabilities(
322
- sampleRates: capabilities["sampleRates"] as? [Int] ?? [],
323
- channelCounts: capabilities["channelCounts"] as? [Int] ?? [],
324
- bitDepths: capabilities["bitDepths"] as? [Int] ?? []
325
- // Add boolean flags if available in your dictionary
326
- )
327
-
328
- return AudioDevice(
329
- id: deviceId,
330
- name: defaultPort.portName,
331
- type: deviceType,
332
- isDefault: true, // Assume it's the default we're looking for
333
- capabilities: audioCapabilities,
334
- isAvailable: true // It's available if found here
335
- )
336
- } else {
337
- Logger.debug("AudioDeviceManager", "Could not find built-in mic as default device.")
338
- return nil
339
- }
340
- } catch {
341
- Logger.debug("AudioDeviceManager", "Error getting default input device: \(error)")
342
- return nil
343
- }
344
- }
345
-
346
- /// Selects a specific audio input device for recording
347
- func selectInputDevice(_ deviceId: String, promise: Promise) {
348
- Logger.debug("AudioDeviceManager", "Attempting to select input device with ID: \(deviceId)")
349
-
350
- // Prepare audio session - use force: true for device selection to ensure we get the latest devices
351
- let prepared = prepareAudioSession(force: true)
352
- if !prepared {
353
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device selection may not work correctly")
354
- }
355
-
356
- do {
357
- let session = AVAudioSession.sharedInstance()
358
-
359
- // Ensure the session is active
360
- try session.setActive(true)
361
-
362
- // For Bluetooth devices, normalize and match by prefix
363
- let normalizedRequestedId = normalizeBluetoothDeviceId(deviceId)
364
- let isBluetoothDevice = deviceId.contains(":")
365
-
366
- Logger.debug("AudioDeviceManager", "Selecting \(isBluetoothDevice ? "Bluetooth" : "non-Bluetooth") device with normalized ID: \(normalizedRequestedId)")
367
-
368
- // Find the device with the specified ID
369
- let selectedPort: AVAudioSessionPortDescription?
370
-
371
- if isBluetoothDevice {
372
- // For Bluetooth devices, match by normalized ID
373
- selectedPort = session.availableInputs?.first { port in
374
- let portNormalizedId = normalizeBluetoothDeviceId(port.uid)
375
- let matches = portNormalizedId == normalizedRequestedId
376
- Logger.debug("AudioDeviceManager", "Checking device \(port.portName) (ID: \(port.uid), Normalized: \(portNormalizedId)) - Matches: \(matches)")
377
- return matches
378
- }
379
- } else {
380
- // For non-Bluetooth devices, direct match
381
- selectedPort = session.availableInputs?.first { port in
382
- let matches = port.uid == deviceId
383
- Logger.debug("AudioDeviceManager", "Checking device \(port.portName) (ID: \(port.uid)) - Matches: \(matches)")
384
- return matches
385
- }
386
- }
387
-
388
- guard let selectedPort = selectedPort else {
389
- Logger.debug("AudioDeviceManager", "Device not found with ID \(deviceId)")
390
-
391
- // Log all available devices to help debugging
392
- if let availableInputs = session.availableInputs {
393
- Logger.debug("AudioDeviceManager", "Available devices:")
394
- for (index, device) in availableInputs.enumerated() {
395
- Logger.debug("AudioDeviceManager", "\(index+1). \(device.portName) (ID: \(device.uid), Normalized: \(normalizeBluetoothDeviceId(device.uid)))")
396
- }
397
- } else {
398
- Logger.debug("AudioDeviceManager", "No available devices found")
399
- }
400
-
401
- promise.reject("DEVICE_NOT_FOUND", "The selected audio device is not available")
402
- return
403
- }
404
-
405
- // Set the preferred input device
406
- Logger.debug("AudioDeviceManager", "Setting preferred input to: \(selectedPort.portName) (ID: \(selectedPort.uid))")
407
- try session.setPreferredInput(selectedPort)
408
-
409
- // Verify selection
410
- if let currentInput = session.currentRoute.inputs.first {
411
- let succeeded = (currentInput.uid == selectedPort.uid ||
412
- normalizeBluetoothDeviceId(currentInput.uid) == normalizeBluetoothDeviceId(selectedPort.uid))
413
- Logger.debug("AudioDeviceManager", "Device selection \(succeeded ? "succeeded" : "failed") - Current device: \(currentInput.portName) (ID: \(currentInput.uid))")
414
- }
415
-
416
- Logger.debug("AudioDeviceManager", "Device selected successfully")
417
- promise.resolve(true)
418
- } catch {
419
- Logger.debug("AudioDeviceManager", "Failed to select device: \(error.localizedDescription)")
420
- promise.reject("DEVICE_SELECTION_FAILED", "Failed to select audio device: \(error.localizedDescription)")
421
- }
422
- }
423
-
424
- /// Selects a specific audio input device asynchronously (useful for internal calls)
425
- func selectDevice(_ deviceId: String) async -> Bool {
426
- Logger.debug("AudioDeviceManager", "Attempting to select input device with ID: \(deviceId) (async)")
427
-
428
- let prepared = prepareAudioSession(force: true)
429
- if !prepared {
430
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device selection may not work correctly")
431
- return false
432
- }
433
-
434
- do {
435
- let session = AVAudioSession.sharedInstance()
436
- try session.setActive(true)
437
-
438
- let normalizedRequestedId = normalizeBluetoothDeviceId(deviceId)
439
- let isBluetoothDevice = deviceId.contains(":")
440
-
441
- Logger.debug("AudioDeviceManager", "Selecting \(isBluetoothDevice ? "Bluetooth" : "non-Bluetooth") device with normalized ID: \(normalizedRequestedId)")
442
-
443
- let selectedPort: AVAudioSessionPortDescription?
444
- if isBluetoothDevice {
445
- selectedPort = session.availableInputs?.first { port in
446
- normalizeBluetoothDeviceId(port.uid) == normalizedRequestedId
447
- }
448
- } else {
449
- selectedPort = session.availableInputs?.first { $0.uid == deviceId }
450
- }
451
-
452
- guard let portToSet = selectedPort else {
453
- Logger.debug("AudioDeviceManager", "Device not found with ID \(deviceId) for async selection")
454
- return false
455
- }
456
-
457
- Logger.debug("AudioDeviceManager", "Setting preferred input to: \(portToSet.portName) (ID: \(portToSet.uid)) (async)")
458
- try session.setPreferredInput(portToSet)
459
- // Add a small delay hoping the system applies the change before potential next operations
460
- try await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
461
-
462
- // Optional: Verify selection succeeded (might be less reliable immediately after setting)
463
- if let currentInput = session.currentRoute.inputs.first {
464
- let succeeded = (currentInput.uid == portToSet.uid || normalizeBluetoothDeviceId(currentInput.uid) == normalizedRequestedId)
465
- Logger.debug("AudioDeviceManager", "Async selection verification: \(succeeded ? "succeeded" : "failed")")
466
- return succeeded
467
- } else {
468
- // If no current input after setting, assume failure
469
- return false
470
- }
471
-
472
- } catch {
473
- Logger.debug("AudioDeviceManager", "Failed to select device asynchronously: \(error.localizedDescription)")
474
- return false
475
- }
476
- }
477
-
478
- /// Determines if a device is still available
479
- func isDeviceAvailable(_ deviceId: String) -> Bool {
480
- Logger.debug("AudioDeviceManager", "Checking availability for device ID: \(deviceId)")
481
-
482
- // Prepare audio session if needed
483
- let prepared = prepareAudioSession()
484
- if !prepared {
485
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device availability check may not be accurate")
486
- }
487
-
488
- let session = AVAudioSession.sharedInstance()
489
-
490
- // Handle Bluetooth devices with multiple profiles (SCO, A2DP, etc.)
491
- let isBluetoothDevice = deviceId.contains(":") // Most Bluetooth devices have MAC addresses with colons
492
-
493
- if isBluetoothDevice {
494
- // For Bluetooth devices, check if any device with the same MAC address prefix is available
495
- let baseDeviceId = deviceId.split(separator: "-").first ?? Substring(deviceId)
496
-
497
- // Log all available inputs for debugging
498
- Logger.debug("AudioDeviceManager", "Available devices to check against:")
499
- if let availableInputs = session.availableInputs {
500
- for (index, device) in availableInputs.enumerated() {
501
- let normalizedId = normalizeBluetoothDeviceId(device.uid)
502
- let matches = device.uid.starts(with: String(baseDeviceId))
503
- Logger.debug("AudioDeviceManager", "\(index+1). \(device.portName) (ID: \(device.uid), Normalized: \(normalizedId)) - Matches: \(matches)")
504
- }
505
- } else {
506
- Logger.debug("AudioDeviceManager", "No available devices found")
507
- }
508
-
509
- // Also check current route
510
- for (index, input) in session.currentRoute.inputs.enumerated() {
511
- let normalizedId = normalizeBluetoothDeviceId(input.uid)
512
- let matches = input.uid.starts(with: String(baseDeviceId))
513
- Logger.debug("AudioDeviceManager", "Current route input \(index+1): \(input.portName) (ID: \(input.uid), Normalized: \(normalizedId)) - Matches: \(matches)")
514
- }
515
-
516
- let result = session.availableInputs?.contains { $0.uid.starts(with: String(baseDeviceId)) } ?? false
517
- Logger.debug("AudioDeviceManager", "Bluetooth device \(deviceId) with base ID \(baseDeviceId) available: \(result)")
518
- return result
519
- } else {
520
- // Standard device ID check for non-Bluetooth devices
521
- return session.availableInputs?.contains { $0.uid == deviceId } ?? false
522
- }
523
- }
524
-
525
- /// Resets the selected device to system default (usually built-in mic)
526
- /// - Parameter completion: Callback with success (Bool) and optional error
527
- func resetToDefaultDevice(completion: @escaping (Bool, Error?) -> Void) {
528
- Logger.debug("AudioDeviceManager", "Attempting to reset to default input device")
529
-
530
- // Prepare audio session if needed
531
- let prepared = prepareAudioSession()
532
- if !prepared {
533
- Logger.debug("AudioDeviceManager", "Warning: Audio session preparation failed, device reset may not work correctly")
534
- }
535
-
536
- do {
537
- let session = AVAudioSession.sharedInstance()
538
-
539
- // Log current device before reset
540
- if let currentDevice = session.currentRoute.inputs.first {
541
- Logger.debug("AudioDeviceManager", "Current device before reset: \(currentDevice.portName) (ID: \(currentDevice.uid))")
542
- } else {
543
- Logger.debug("AudioDeviceManager", "No current device before reset")
544
- }
545
-
546
- // Setting preferred input to nil lets the system choose the default
547
- try session.setPreferredInput(nil)
548
-
549
- // Log the device after reset
550
- if let newDevice = session.currentRoute.inputs.first {
551
- Logger.debug("AudioDeviceManager", "Reset to default device: \(newDevice.portName) (ID: \(newDevice.uid))")
552
-
553
- // Check if it's actually the built-in mic (which is the typical default)
554
- let isBuiltIn = newDevice.portType == .builtInMic
555
- Logger.debug("AudioDeviceManager", "Reset device is built-in mic: \(isBuiltIn)")
556
- } else {
557
- Logger.debug("AudioDeviceManager", "No device found after reset")
558
- }
559
-
560
- completion(true, nil)
561
- } catch {
562
- Logger.debug("AudioDeviceManager", "Failed to reset to default device: \(error.localizedDescription)")
563
- completion(false, error)
564
- }
565
- }
566
-
567
- /// Starts monitoring device connection/disconnection events
568
- private func startMonitoringDeviceChanges() {
569
- // Ensure we don't add multiple observers
570
- stopMonitoringDeviceChanges()
571
-
572
- Logger.debug("AudioDeviceManager", "Starting device change monitoring")
573
- routeChangeObserver = NotificationCenter.default.addObserver(
574
- forName: AVAudioSession.routeChangeNotification,
575
- object: nil,
576
- queue: .main // Process on main queue to avoid threading issues with delegate calls
577
- ) { [weak self] notification in
578
- self?.handleRouteChange(notification)
579
- }
580
- }
581
-
582
- /// Stops monitoring device changes
583
- private func stopMonitoringDeviceChanges() {
584
- if let observer = routeChangeObserver {
585
- Logger.debug("AudioDeviceManager", "Stopping device change monitoring")
586
- NotificationCenter.default.removeObserver(observer)
587
- routeChangeObserver = nil
588
- }
589
- }
590
-
591
- /// Handles route change notifications to detect device connections and disconnections
592
- @objc private func handleRouteChange(_ notification: Notification) {
593
- guard let userInfo = notification.userInfo,
594
- let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
595
- let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
596
- return
597
- }
598
-
599
- Logger.debug("AudioDeviceManager", "Route change detected, reason: \(reason.rawValue)")
600
-
601
- // Only proceed if a device was potentially removed or added or the route changed significantly
602
- guard reason == .oldDeviceUnavailable || reason == .newDeviceAvailable || reason == .override || reason == .routeConfigurationChange else {
603
- Logger.debug("AudioDeviceManager", "Ignoring route change reason: \(reason.rawValue)")
604
- return
605
- }
606
-
607
- // Get the *previous* route description
608
- guard let previousRoute = userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription else {
609
- Logger.debug("AudioDeviceManager", "No previous route info found for device change check.")
610
- return
611
- }
612
-
613
- // Get the *current* available input devices
614
- let currentInputs = AVAudioSession.sharedInstance().availableInputs ?? []
615
- let currentInputIds = Set(currentInputs.map { normalizeBluetoothDeviceId($0.uid) })
616
- let previousInputIds = Set(previousRoute.inputs.map { normalizeBluetoothDeviceId($0.uid) })
617
-
618
- // Check for DISCONNECTED devices (were in previous route but not in current available)
619
- for previousInputPort in previousRoute.inputs {
620
- let normalizedPreviousId = normalizeBluetoothDeviceId(previousInputPort.uid)
621
- if !currentInputIds.contains(normalizedPreviousId) {
622
- Logger.debug("AudioDeviceManager", "Detected disconnection of device: \(previousInputPort.portName) (Normalized ID: \(normalizedPreviousId))")
623
- // Keep existing disconnection delegate method unchanged
624
- delegate?.audioDeviceManager(self, didDetectDisconnectionOfDevice: normalizedPreviousId)
625
- }
626
- }
627
-
628
- // Check for CONNECTED devices (are in current available but were not in previous route)
629
- for currentInput in currentInputs {
630
- let normalizedCurrentId = normalizeBluetoothDeviceId(currentInput.uid)
631
- if !previousInputIds.contains(normalizedCurrentId) {
632
- Logger.debug("AudioDeviceManager", "Detected connection of device: \(currentInput.portName) (Normalized ID: \(normalizedCurrentId))")
633
- // Emit connection event via notification
634
- NotificationCenter.default.post(
635
- name: NSNotification.Name("DeviceConnected"),
636
- object: nil,
637
- userInfo: ["deviceId": normalizedCurrentId]
638
- )
639
- }
640
- }
641
- }
642
-
643
- /// Normalizes Bluetooth device IDs by removing profile suffixes
644
- public func normalizeBluetoothDeviceId(_ deviceId: String) -> String {
645
- // For Bluetooth devices with MAC addresses and profile suffixes (like -tsco)
646
- if deviceId.contains(":") && deviceId.contains("-") {
647
- // Split by the hyphen and take the first part (the MAC address)
648
- return deviceId.split(separator: "-").first.map(String.init) ?? deviceId
649
- }
650
- return deviceId
651
- }
652
- }
653
-
654
- // Add structure for AudioDeviceCapabilities if not defined elsewhere
655
- struct AudioDeviceCapabilities {
656
- let sampleRates: [Int]
657
- let channelCounts: [Int]
658
- let bitDepths: [Int]
659
- // Add boolean flags if needed
660
- }
661
-
662
- // Add structure for AudioDevice if not defined elsewhere
663
- struct AudioDevice {
664
- let id: String
665
- let name: String
666
- let type: String
667
- let isDefault: Bool
668
- let capabilities: AudioDeviceCapabilities
669
- let isAvailable: Bool
670
- }