@siteed/expo-audio-studio 2.18.6 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/README.md +13 -297
  2. package/index.d.ts +1 -0
  3. package/index.js +1 -0
  4. package/package.json +7 -136
  5. package/CHANGELOG.md +0 -501
  6. package/LICENSE +0 -21
  7. package/android/build.gradle +0 -129
  8. package/android/src/androidTest/assets/chorus.wav +0 -0
  9. package/android/src/androidTest/assets/jfk.wav +0 -0
  10. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  11. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  12. package/android/src/androidTest/java/net/siteed/audiostream/AudioProcessorInstrumentedTest.kt +0 -197
  13. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderInstrumentedTest.kt +0 -541
  14. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderPerformanceInstrumentedTest.kt +0 -234
  15. package/android/src/androidTest/java/net/siteed/audiostream/integration/AudioFocusStrategyIntegrationTest.kt +0 -332
  16. package/android/src/androidTest/java/net/siteed/audiostream/integration/BufferDurationIntegrationTest.kt +0 -324
  17. package/android/src/androidTest/java/net/siteed/audiostream/integration/CompressedOnlyOutputTest.kt +0 -253
  18. package/android/src/androidTest/java/net/siteed/audiostream/integration/DeviceDisconnectionFallbackTest.kt +0 -218
  19. package/android/src/androidTest/java/net/siteed/audiostream/integration/EventEmissionIntervalTest.kt +0 -120
  20. package/android/src/androidTest/java/net/siteed/audiostream/integration/M4aFormatTest.kt +0 -345
  21. package/android/src/androidTest/java/net/siteed/audiostream/integration/OutputControlIntegrationTest.kt +0 -340
  22. package/android/src/androidTest/java/net/siteed/audiostream/integration/PcmStreamingDurationTest.kt +0 -252
  23. package/android/src/androidTest/java/net/siteed/audiostream/integration/README.md +0 -95
  24. package/android/src/androidTest/java/net/siteed/audiostream/integration/run_integration_tests.sh +0 -43
  25. package/android/src/main/AndroidManifest.xml +0 -30
  26. package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +0 -188
  27. package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +0 -9
  28. package/android/src/main/java/net/siteed/audiostream/AudioDeviceManager.kt +0 -1741
  29. package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +0 -136
  30. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +0 -354
  31. package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +0 -439
  32. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +0 -2237
  33. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +0 -2141
  34. package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +0 -167
  35. package/android/src/main/java/net/siteed/audiostream/AudioTrimmer.kt +0 -1099
  36. package/android/src/main/java/net/siteed/audiostream/Constants.kt +0 -37
  37. package/android/src/main/java/net/siteed/audiostream/EventSender.kt +0 -7
  38. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +0 -1113
  39. package/android/src/main/java/net/siteed/audiostream/FFT.kt +0 -99
  40. package/android/src/main/java/net/siteed/audiostream/Features.kt +0 -98
  41. package/android/src/main/java/net/siteed/audiostream/LogUtils.kt +0 -93
  42. package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +0 -72
  43. package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +0 -68
  44. package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +0 -59
  45. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +0 -257
  46. package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +0 -19
  47. package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +0 -159
  48. package/android/src/main/res/drawable/ic_default_action_icon.xml +0 -16
  49. package/android/src/main/res/drawable/ic_microphone.xml +0 -13
  50. package/android/src/main/res/drawable/ic_pause.xml +0 -10
  51. package/android/src/main/res/drawable/ic_play.xml +0 -10
  52. package/android/src/main/res/drawable/ic_stop.xml +0 -10
  53. package/android/src/main/res/layout/notification_recording.xml +0 -37
  54. package/android/src/test/java/net/siteed/audiostream/AudioFileHandlerTest.kt +0 -279
  55. package/android/src/test/java/net/siteed/audiostream/AudioFocusStrategyTest.kt +0 -249
  56. package/android/src/test/java/net/siteed/audiostream/AudioFormatTest.kt +0 -151
  57. package/android/src/test/java/net/siteed/audiostream/AudioFormatUtilsTest.kt +0 -273
  58. package/android/src/test/java/net/siteed/audiostream/DeviceDisconnectionFallbackUnitTest.kt +0 -140
  59. package/android/src/test/resources/chorus.wav +0 -0
  60. package/android/src/test/resources/generate_test_audio.py +0 -94
  61. package/android/src/test/resources/jfk.wav +0 -0
  62. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  63. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  64. package/app.plugin.js +0 -3
  65. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +0 -4
  66. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  67. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +0 -210
  68. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  69. package/build/cjs/AudioAnalysis/extractAudioData.js +0 -21
  70. package/build/cjs/AudioAnalysis/extractAudioData.js.map +0 -1
  71. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +0 -92
  72. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  73. package/build/cjs/AudioAnalysis/extractPreview.js +0 -28
  74. package/build/cjs/AudioAnalysis/extractPreview.js.map +0 -1
  75. package/build/cjs/AudioAnalysis/extractWaveform.js +0 -18
  76. package/build/cjs/AudioAnalysis/extractWaveform.js.map +0 -1
  77. package/build/cjs/AudioDeviceManager.js +0 -689
  78. package/build/cjs/AudioDeviceManager.js.map +0 -1
  79. package/build/cjs/AudioRecorder.provider.js +0 -78
  80. package/build/cjs/AudioRecorder.provider.js.map +0 -1
  81. package/build/cjs/ExpoAudioStream.native.js +0 -8
  82. package/build/cjs/ExpoAudioStream.native.js.map +0 -1
  83. package/build/cjs/ExpoAudioStream.types.js +0 -11
  84. package/build/cjs/ExpoAudioStream.types.js.map +0 -1
  85. package/build/cjs/ExpoAudioStream.web.js +0 -708
  86. package/build/cjs/ExpoAudioStream.web.js.map +0 -1
  87. package/build/cjs/ExpoAudioStreamModule.js +0 -718
  88. package/build/cjs/ExpoAudioStreamModule.js.map +0 -1
  89. package/build/cjs/WebRecorder.web.js +0 -777
  90. package/build/cjs/WebRecorder.web.js.map +0 -1
  91. package/build/cjs/constants/platformLimitations.js +0 -99
  92. package/build/cjs/constants/platformLimitations.js.map +0 -1
  93. package/build/cjs/constants.js +0 -17
  94. package/build/cjs/constants.js.map +0 -1
  95. package/build/cjs/events.js +0 -29
  96. package/build/cjs/events.js.map +0 -1
  97. package/build/cjs/hooks/useAudioDevices.js +0 -179
  98. package/build/cjs/hooks/useAudioDevices.js.map +0 -1
  99. package/build/cjs/index.js +0 -58
  100. package/build/cjs/index.js.map +0 -1
  101. package/build/cjs/trimAudio.js +0 -76
  102. package/build/cjs/trimAudio.js.map +0 -1
  103. package/build/cjs/useAudioRecorder.js +0 -518
  104. package/build/cjs/useAudioRecorder.js.map +0 -1
  105. package/build/cjs/utils/BlobFix.js +0 -502
  106. package/build/cjs/utils/BlobFix.js.map +0 -1
  107. package/build/cjs/utils/audioProcessing.js +0 -136
  108. package/build/cjs/utils/audioProcessing.js.map +0 -1
  109. package/build/cjs/utils/cleanNativeOptions.js +0 -22
  110. package/build/cjs/utils/cleanNativeOptions.js.map +0 -1
  111. package/build/cjs/utils/concatenateBuffers.js +0 -25
  112. package/build/cjs/utils/concatenateBuffers.js.map +0 -1
  113. package/build/cjs/utils/convertPCMToFloat32.js +0 -124
  114. package/build/cjs/utils/convertPCMToFloat32.js.map +0 -1
  115. package/build/cjs/utils/crc32.js +0 -52
  116. package/build/cjs/utils/crc32.js.map +0 -1
  117. package/build/cjs/utils/encodingToBitDepth.js +0 -17
  118. package/build/cjs/utils/encodingToBitDepth.js.map +0 -1
  119. package/build/cjs/utils/getWavFileInfo.js +0 -96
  120. package/build/cjs/utils/getWavFileInfo.js.map +0 -1
  121. package/build/cjs/utils/writeWavHeader.js +0 -88
  122. package/build/cjs/utils/writeWavHeader.js.map +0 -1
  123. package/build/cjs/workers/InlineFeaturesExtractor.web.js +0 -859
  124. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +0 -1
  125. package/build/cjs/workers/inlineAudioWebWorker.web.js +0 -184
  126. package/build/cjs/workers/inlineAudioWebWorker.web.js.map +0 -1
  127. package/build/esm/AudioAnalysis/AudioAnalysis.types.js +0 -3
  128. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  129. package/build/esm/AudioAnalysis/extractAudioAnalysis.js +0 -202
  130. package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  131. package/build/esm/AudioAnalysis/extractAudioData.js +0 -14
  132. package/build/esm/AudioAnalysis/extractAudioData.js.map +0 -1
  133. package/build/esm/AudioAnalysis/extractMelSpectrogram.js +0 -89
  134. package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  135. package/build/esm/AudioAnalysis/extractPreview.js +0 -25
  136. package/build/esm/AudioAnalysis/extractPreview.js.map +0 -1
  137. package/build/esm/AudioAnalysis/extractWaveform.js +0 -11
  138. package/build/esm/AudioAnalysis/extractWaveform.js.map +0 -1
  139. package/build/esm/AudioDeviceManager.js +0 -682
  140. package/build/esm/AudioDeviceManager.js.map +0 -1
  141. package/build/esm/AudioRecorder.provider.js +0 -40
  142. package/build/esm/AudioRecorder.provider.js.map +0 -1
  143. package/build/esm/ExpoAudioStream.native.js +0 -6
  144. package/build/esm/ExpoAudioStream.native.js.map +0 -1
  145. package/build/esm/ExpoAudioStream.types.js +0 -8
  146. package/build/esm/ExpoAudioStream.types.js.map +0 -1
  147. package/build/esm/ExpoAudioStream.web.js +0 -704
  148. package/build/esm/ExpoAudioStream.web.js.map +0 -1
  149. package/build/esm/ExpoAudioStreamModule.js +0 -713
  150. package/build/esm/ExpoAudioStreamModule.js.map +0 -1
  151. package/build/esm/WebRecorder.web.js +0 -773
  152. package/build/esm/WebRecorder.web.js.map +0 -1
  153. package/build/esm/constants/platformLimitations.js +0 -90
  154. package/build/esm/constants/platformLimitations.js.map +0 -1
  155. package/build/esm/constants.js +0 -14
  156. package/build/esm/constants.js.map +0 -1
  157. package/build/esm/events.js +0 -21
  158. package/build/esm/events.js.map +0 -1
  159. package/build/esm/hooks/useAudioDevices.js +0 -176
  160. package/build/esm/hooks/useAudioDevices.js.map +0 -1
  161. package/build/esm/index.js +0 -20
  162. package/build/esm/index.js.map +0 -1
  163. package/build/esm/trimAudio.js +0 -69
  164. package/build/esm/trimAudio.js.map +0 -1
  165. package/build/esm/useAudioRecorder.js +0 -512
  166. package/build/esm/useAudioRecorder.js.map +0 -1
  167. package/build/esm/utils/BlobFix.js +0 -498
  168. package/build/esm/utils/BlobFix.js.map +0 -1
  169. package/build/esm/utils/audioProcessing.js +0 -133
  170. package/build/esm/utils/audioProcessing.js.map +0 -1
  171. package/build/esm/utils/cleanNativeOptions.js +0 -19
  172. package/build/esm/utils/cleanNativeOptions.js.map +0 -1
  173. package/build/esm/utils/concatenateBuffers.js +0 -21
  174. package/build/esm/utils/concatenateBuffers.js.map +0 -1
  175. package/build/esm/utils/convertPCMToFloat32.js +0 -120
  176. package/build/esm/utils/convertPCMToFloat32.js.map +0 -1
  177. package/build/esm/utils/crc32.js +0 -50
  178. package/build/esm/utils/crc32.js.map +0 -1
  179. package/build/esm/utils/encodingToBitDepth.js +0 -13
  180. package/build/esm/utils/encodingToBitDepth.js.map +0 -1
  181. package/build/esm/utils/getWavFileInfo.js +0 -92
  182. package/build/esm/utils/getWavFileInfo.js.map +0 -1
  183. package/build/esm/utils/writeWavHeader.js +0 -84
  184. package/build/esm/utils/writeWavHeader.js.map +0 -1
  185. package/build/esm/workers/InlineFeaturesExtractor.web.js +0 -856
  186. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +0 -1
  187. package/build/esm/workers/inlineAudioWebWorker.web.js +0 -181
  188. package/build/esm/workers/inlineAudioWebWorker.web.js.map +0 -1
  189. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +0 -196
  190. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  191. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts +0 -74
  192. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  193. package/build/types/AudioAnalysis/extractAudioData.d.ts +0 -3
  194. package/build/types/AudioAnalysis/extractAudioData.d.ts.map +0 -1
  195. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts +0 -14
  196. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
  197. package/build/types/AudioAnalysis/extractPreview.d.ts +0 -11
  198. package/build/types/AudioAnalysis/extractPreview.d.ts.map +0 -1
  199. package/build/types/AudioAnalysis/extractWaveform.d.ts +0 -8
  200. package/build/types/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  201. package/build/types/AudioDeviceManager.d.ts +0 -187
  202. package/build/types/AudioDeviceManager.d.ts.map +0 -1
  203. package/build/types/AudioRecorder.provider.d.ts +0 -11
  204. package/build/types/AudioRecorder.provider.d.ts.map +0 -1
  205. package/build/types/ExpoAudioStream.native.d.ts +0 -3
  206. package/build/types/ExpoAudioStream.native.d.ts.map +0 -1
  207. package/build/types/ExpoAudioStream.types.d.ts +0 -738
  208. package/build/types/ExpoAudioStream.types.d.ts.map +0 -1
  209. package/build/types/ExpoAudioStream.web.d.ts +0 -96
  210. package/build/types/ExpoAudioStream.web.d.ts.map +0 -1
  211. package/build/types/ExpoAudioStreamModule.d.ts +0 -3
  212. package/build/types/ExpoAudioStreamModule.d.ts.map +0 -1
  213. package/build/types/WebRecorder.web.d.ts +0 -198
  214. package/build/types/WebRecorder.web.d.ts.map +0 -1
  215. package/build/types/constants/platformLimitations.d.ts +0 -40
  216. package/build/types/constants/platformLimitations.d.ts.map +0 -1
  217. package/build/types/constants.d.ts +0 -11
  218. package/build/types/constants.d.ts.map +0 -1
  219. package/build/types/events.d.ts +0 -26
  220. package/build/types/events.d.ts.map +0 -1
  221. package/build/types/hooks/useAudioDevices.d.ts +0 -15
  222. package/build/types/hooks/useAudioDevices.d.ts.map +0 -1
  223. package/build/types/index.d.ts +0 -18
  224. package/build/types/index.d.ts.map +0 -1
  225. package/build/types/trimAudio.d.ts +0 -25
  226. package/build/types/trimAudio.d.ts.map +0 -1
  227. package/build/types/useAudioRecorder.d.ts +0 -22
  228. package/build/types/useAudioRecorder.d.ts.map +0 -1
  229. package/build/types/utils/BlobFix.d.ts +0 -9
  230. package/build/types/utils/BlobFix.d.ts.map +0 -1
  231. package/build/types/utils/audioProcessing.d.ts +0 -24
  232. package/build/types/utils/audioProcessing.d.ts.map +0 -1
  233. package/build/types/utils/cleanNativeOptions.d.ts +0 -15
  234. package/build/types/utils/cleanNativeOptions.d.ts.map +0 -1
  235. package/build/types/utils/concatenateBuffers.d.ts +0 -8
  236. package/build/types/utils/concatenateBuffers.d.ts.map +0 -1
  237. package/build/types/utils/convertPCMToFloat32.d.ts +0 -13
  238. package/build/types/utils/convertPCMToFloat32.d.ts.map +0 -1
  239. package/build/types/utils/crc32.d.ts +0 -7
  240. package/build/types/utils/crc32.d.ts.map +0 -1
  241. package/build/types/utils/encodingToBitDepth.d.ts +0 -5
  242. package/build/types/utils/encodingToBitDepth.d.ts.map +0 -1
  243. package/build/types/utils/getWavFileInfo.d.ts +0 -26
  244. package/build/types/utils/getWavFileInfo.d.ts.map +0 -1
  245. package/build/types/utils/writeWavHeader.d.ts +0 -34
  246. package/build/types/utils/writeWavHeader.d.ts.map +0 -1
  247. package/build/types/workers/InlineFeaturesExtractor.web.d.ts +0 -2
  248. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  249. package/build/types/workers/inlineAudioWebWorker.web.d.ts +0 -2
  250. package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  251. package/expo-module.config.json +0 -10
  252. package/ios/AudioAnalysisData.swift +0 -74
  253. package/ios/AudioDeviceManager.swift +0 -670
  254. package/ios/AudioNotificationManager.swift +0 -154
  255. package/ios/AudioProcessingHelpers.swift +0 -743
  256. package/ios/AudioProcessor.swift +0 -1151
  257. package/ios/AudioStreamError.swift +0 -7
  258. package/ios/AudioStreamManager.swift +0 -2369
  259. package/ios/AudioStreamManagerDelegate.swift +0 -16
  260. package/ios/DataPoint.swift +0 -54
  261. package/ios/DecodingConfig.swift +0 -59
  262. package/ios/ExpoAudioStream.podspec +0 -33
  263. package/ios/ExpoAudioStreamModule.swift +0 -1019
  264. package/ios/ExpoAudioStudioTests/AudioFileHandlerTests.swift +0 -338
  265. package/ios/ExpoAudioStudioTests/AudioFormatUtilsTests.swift +0 -331
  266. package/ios/ExpoAudioStudioTests/AudioTestHelpers.swift +0 -130
  267. package/ios/ExpoAudioStudioTests/CompressedOnlyOutputTests.swift +0 -294
  268. package/ios/ExpoAudioStudioTests/EventEmissionIntervalTests.swift +0 -105
  269. package/ios/ExpoAudioStudioTests/Info.plist +0 -22
  270. package/ios/ExpoAudioStudioTests/README.md +0 -39
  271. package/ios/ExpoAudioStudioTests/SimpleAudioTest.swift +0 -98
  272. package/ios/ExpoAudioStudioTests/TestAudioGenerator.swift +0 -75
  273. package/ios/FFT.swift +0 -62
  274. package/ios/Features.swift +0 -95
  275. package/ios/ISSUE_IOS.md +0 -68
  276. package/ios/Logger.swift +0 -39
  277. package/ios/NotificationExtension.swift +0 -15
  278. package/ios/RecordingResult.swift +0 -22
  279. package/ios/RecordingSettings.swift +0 -308
  280. package/ios/WaveformExtractor.swift +0 -105
  281. package/ios/tests/README.md +0 -41
  282. package/ios/tests/integration/buffer_and_fallback_test.swift +0 -178
  283. package/ios/tests/integration/buffer_duration_test.swift +0 -185
  284. package/ios/tests/integration/compressed_only_output_test.swift +0 -271
  285. package/ios/tests/integration/output_control_test.swift +0 -322
  286. package/ios/tests/integration/run_integration_tests.sh +0 -37
  287. package/ios/tests/opus_support_test_macos.swift +0 -154
  288. package/ios/tests/standalone/audio_processing_test.swift +0 -144
  289. package/ios/tests/standalone/audio_recording_test.swift +0 -277
  290. package/ios/tests/standalone/audio_streaming_test.swift +0 -249
  291. package/ios/tests/standalone/standalone_test.swift +0 -144
  292. package/plugin/build/index.cjs +0 -194
  293. package/plugin/build/index.d.cts +0 -22
  294. package/plugin/build/index.js +0 -194
  295. package/plugin/src/index.ts +0 -285
  296. package/plugin/tsconfig.json +0 -10
  297. package/plugin/tsconfig.tsbuildinfo +0 -1
  298. package/src/AudioAnalysis/AudioAnalysis.types.ts +0 -224
  299. package/src/AudioAnalysis/extractAudioAnalysis.ts +0 -344
  300. package/src/AudioAnalysis/extractAudioData.ts +0 -17
  301. package/src/AudioAnalysis/extractMelSpectrogram.ts +0 -154
  302. package/src/AudioAnalysis/extractPreview.ts +0 -34
  303. package/src/AudioAnalysis/extractWaveform.ts +0 -22
  304. package/src/AudioDeviceManager.ts +0 -803
  305. package/src/AudioRecorder.provider.tsx +0 -57
  306. package/src/ExpoAudioStream.native.ts +0 -6
  307. package/src/ExpoAudioStream.types.ts +0 -874
  308. package/src/ExpoAudioStream.web.ts +0 -905
  309. package/src/ExpoAudioStreamModule.ts +0 -990
  310. package/src/WebRecorder.web.ts +0 -1005
  311. package/src/constants/platformLimitations.ts +0 -118
  312. package/src/constants.ts +0 -18
  313. package/src/events.ts +0 -60
  314. package/src/hooks/useAudioDevices.ts +0 -213
  315. package/src/index.ts +0 -54
  316. package/src/trimAudio.ts +0 -94
  317. package/src/types/crc-32.d.ts +0 -9
  318. package/src/useAudioRecorder.tsx +0 -766
  319. package/src/utils/BlobFix.ts +0 -561
  320. package/src/utils/audioProcessing.ts +0 -205
  321. package/src/utils/cleanNativeOptions.ts +0 -18
  322. package/src/utils/concatenateBuffers.ts +0 -24
  323. package/src/utils/convertPCMToFloat32.ts +0 -170
  324. package/src/utils/crc32.ts +0 -59
  325. package/src/utils/encodingToBitDepth.ts +0 -18
  326. package/src/utils/getWavFileInfo.ts +0 -132
  327. package/src/utils/writeWavHeader.ts +0 -115
  328. package/src/workers/InlineFeaturesExtractor.web.tsx +0 -855
  329. package/src/workers/inlineAudioWebWorker.web.tsx +0 -180
@@ -1,1151 +0,0 @@
1
- // packages/expo-audio-stream/ios/AudioProcessor.swift
2
-
3
- import Foundation
4
- import Accelerate
5
- import AVFoundation
6
- import QuartzCore
7
-
8
- // Constants
9
- private let SILENCE_THRESHOLD_RMS: Float = 0.01
10
-
11
- public struct TrimResult {
12
- let uri: String
13
- let filename: String
14
- let durationMs: Double
15
- let size: Int64
16
- let sampleRate: Int
17
- let channels: Int
18
- let bitDepth: Int
19
- let mimeType: String
20
- let requestedFormat: String
21
- let actualFormat: String
22
- let compression: [String: Any]?
23
-
24
- init(
25
- uri: String,
26
- filename: String,
27
- durationMs: Double,
28
- size: Int64,
29
- sampleRate: Int,
30
- channels: Int,
31
- bitDepth: Int,
32
- mimeType: String,
33
- requestedFormat: String,
34
- actualFormat: String,
35
- compression: [String: Any]?
36
- ) {
37
- self.uri = uri
38
- self.filename = filename
39
- self.durationMs = durationMs
40
- self.size = size
41
- self.sampleRate = sampleRate
42
- self.channels = channels
43
- self.bitDepth = bitDepth
44
- self.mimeType = mimeType
45
- self.requestedFormat = requestedFormat
46
- self.actualFormat = actualFormat
47
- self.compression = compression
48
- }
49
-
50
- func toDictionary() -> [String: Any] {
51
- var dict: [String: Any] = [
52
- "uri": uri,
53
- "filename": filename,
54
- "durationMs": durationMs,
55
- "size": size,
56
- "sampleRate": sampleRate,
57
- "channels": channels,
58
- "bitDepth": bitDepth,
59
- "mimeType": mimeType,
60
- "requestedFormat": requestedFormat,
61
- "actualFormat": actualFormat
62
- ]
63
- if let compression = compression {
64
- dict["compression"] = compression
65
- }
66
- return dict
67
- }
68
- }
69
-
70
- public class AudioProcessor {
71
- public private(set) var audioFile: AVAudioFile?
72
- private var result: (Any) -> Void
73
- private var reject: (String, String) -> Void
74
- private var waveformData = Array<Float>()
75
- private var progress: Float = 0.0
76
- private var channelCount: Int = 1
77
- private var currentProgress: Float = 0.0
78
- private let extractionQueue = DispatchQueue(label: "AudioProcessor", attributes: .concurrent)
79
- private var _abortExtraction: Bool = false
80
-
81
- // Add a counter for unique IDs
82
- private var uniqueIdCounter = 0
83
-
84
- public var abortExtraction: Bool {
85
- get { _abortExtraction }
86
- set { _abortExtraction = newValue }
87
- }
88
-
89
- // Initializer for file-based processing
90
- public init(url: URL, resolve: @escaping (Any) -> Void, reject: @escaping (String, String) -> Void) throws {
91
- self.audioFile = try AVAudioFile(forReading: url)
92
- self.result = resolve
93
- self.reject = reject
94
- }
95
-
96
- // Initializer for buffer-based processing
97
- public init(resolve: @escaping (Any) -> Void, reject: @escaping (String, String) -> Void) {
98
- self.result = resolve
99
- self.reject = reject
100
- }
101
-
102
-
103
- deinit {
104
- audioFile = nil
105
- }
106
-
107
- /// Error types for AudioProcessor
108
- public enum AudioProcessorError: Error {
109
- case fileInitializationFailed(String)
110
- case bufferCreationFailed
111
- case audioReadError(String)
112
- }
113
-
114
-
115
- /// Extracts and processes audio data from the audio file.
116
- /// - Parameters:
117
- /// - numberOfSamples: The number of samples to extract (for waveform).
118
- /// - offset: The offset to start reading from (in samples).
119
- /// - length: The length of the audio to read (in samples).
120
- /// - segmentDurationMs: The duration of each segment in milliseconds.
121
- /// - featureOptions: The features to extract.
122
- /// - bitDepth: The bit depth of the audio data.
123
- /// - numberOfChannels: The number of channels in the audio data.
124
- /// - position: The position to start reading from (in bytes).
125
- /// - byteLength: The length of the audio to read (in bytes).
126
- /// - Returns: An `AudioAnalysisData` object containing the extracted features.
127
- public func processAudioData(
128
- numberOfSamples: Int?,
129
- offset: Int? = 0,
130
- length: UInt? = nil,
131
- segmentDurationMs: Int = 100, // Default 100ms
132
- featureOptions: [String: Bool],
133
- bitDepth: Int,
134
- numberOfChannels: Int,
135
- position: Int? = nil,
136
- byteLength: Int? = nil
137
- ) -> AudioAnalysisData? {
138
- guard let audioFile = audioFile else {
139
- reject("FILE_NOT_INITIALIZED", "Audio file is not initialized.")
140
- return nil
141
- }
142
-
143
- let totalFrameCount = AVAudioFrameCount(audioFile.length)
144
- var framesPerBuffer: AVAudioFrameCount
145
- let _: Int // Changed from actualPointsPerSecond
146
-
147
- NSLog("""
148
- [AudioProcessor] Starting audio processing:
149
- - totalFrameCount: \(totalFrameCount)
150
- - bitDepth: \(bitDepth)
151
- - numberOfChannels: \(numberOfChannels)
152
- - position: \(position ?? -1)
153
- - byteLength: \(byteLength ?? -1)
154
- - offset: \(offset ?? -1)
155
- - length: \(length ?? 0)
156
- """)
157
-
158
- // Use position/byteLength if provided, otherwise fall back to offset/length
159
- let effectiveOffset: Int64 = if let position = position {
160
- Int64(position / (bitDepth / 8) / numberOfChannels)
161
- } else {
162
- Int64(offset ?? 0)
163
- }
164
-
165
- let effectiveLength: Int64 = if let byteLength = byteLength {
166
- Int64(byteLength / (bitDepth / 8) / numberOfChannels)
167
- } else if let length = length {
168
- Int64(length)
169
- } else {
170
- Int64(totalFrameCount) - effectiveOffset
171
- }
172
-
173
- NSLog("""
174
- [AudioProcessor] Calculated frame positions:
175
- - effectiveOffset: \(effectiveOffset)
176
- - effectiveLength: \(effectiveLength)
177
- - expectedEndFrame: \(effectiveOffset + effectiveLength)
178
- - totalFrameCount: \(totalFrameCount)
179
- """)
180
-
181
- // Validate frame boundaries
182
- if effectiveOffset < 0 || effectiveOffset >= Int64(totalFrameCount) {
183
- NSLog("[AudioProcessor] ERROR: Invalid offset value")
184
- reject("INVALID_OFFSET", "Offset value (\(effectiveOffset)) is outside valid range [0, \(totalFrameCount)]")
185
- return nil
186
- }
187
-
188
- if effectiveLength <= 0 {
189
- NSLog("[AudioProcessor] ERROR: Invalid length value")
190
- reject("INVALID_LENGTH", "Length value (\(effectiveLength)) must be positive")
191
- return nil
192
- }
193
-
194
- if effectiveOffset + effectiveLength > Int64(totalFrameCount) {
195
- NSLog("[AudioProcessor] ERROR: Requested range exceeds file length")
196
- reject("INVALID_RANGE", "Requested range [\(effectiveOffset), \(effectiveOffset + effectiveLength)] exceeds file length \(totalFrameCount)")
197
- return nil
198
- }
199
-
200
- var startFrame: AVAudioFramePosition = effectiveOffset
201
- let endFrame: AVAudioFramePosition = effectiveOffset + effectiveLength
202
-
203
- // Calculate frames per segment based on segment duration
204
- let framesPerSegment = AVAudioFrameCount(Float(audioFile.fileFormat.sampleRate) * Float(segmentDurationMs) / 1000.0)
205
-
206
- if let numberOfSamples = numberOfSamples {
207
- framesPerBuffer = AVAudioFrameCount(max(1, effectiveLength / Int64(numberOfSamples)))
208
- } else {
209
- framesPerBuffer = framesPerSegment
210
- }
211
-
212
- guard let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: framesPerBuffer) else {
213
- reject("BUFFER_CREATION_FAILED", "Failed to create AVAudioPCMBuffer.")
214
- return nil
215
- }
216
-
217
- channelCount = Int(audioFile.processingFormat.channelCount)
218
- let _ = Array(repeating: [Float](repeating: 0, count: Int(framesPerBuffer)), count: channelCount) // Changed from var data
219
-
220
- var channelData = [Float]()
221
- while startFrame < endFrame {
222
- let remainingFrames = endFrame - startFrame
223
- let currentFramesPerBuffer = min(AVAudioFrameCount(framesPerBuffer), AVAudioFrameCount(remainingFrames))
224
-
225
- if currentFramesPerBuffer <= 0 {
226
- break
227
- }
228
-
229
- if abortExtraction {
230
- audioFile.framePosition = startFrame
231
- abortExtraction = false
232
- return nil
233
- }
234
-
235
- do {
236
- audioFile.framePosition = startFrame
237
- try audioFile.read(into: buffer, frameCount: currentFramesPerBuffer)
238
- } catch {
239
- reject("AUDIO_READ_ERROR", "Couldn't read into buffer: \(error.localizedDescription)")
240
- return nil
241
- }
242
-
243
- //TODO: check if we need conversion based on bitDepth here
244
- guard let floatData = buffer.floatChannelData else {
245
- reject("BUFFER_DATA_ERROR", "Failed to retrieve float data from buffer.")
246
- return nil
247
- }
248
- for frame in 0..<Int(buffer.frameLength) {
249
- channelData.append(floatData[0][frame])
250
- }
251
-
252
- startFrame += AVAudioFramePosition(currentFramesPerBuffer)
253
- }
254
-
255
- NSLog("""
256
- [AudioProcessor] Audio processing completed:
257
- - processedFrames: \(endFrame - startFrame)
258
- - framesPerBuffer: \(framesPerBuffer)
259
- """)
260
-
261
- return processChannelData(
262
- channelData: channelData,
263
- sampleRate: Float(audioFile.fileFormat.sampleRate),
264
- segmentDurationMs: segmentDurationMs,
265
- featureOptions: featureOptions,
266
- bitDepth: bitDepth,
267
- numberOfChannels: numberOfChannels
268
- )
269
- }
270
-
271
- /// Processes audio data from a buffer.
272
- /// - Parameters:
273
- /// - data: The audio data buffer.
274
- /// - sampleRate: The sample rate of the audio data.
275
- /// - segmentDurationMs: The duration of each segment in milliseconds.
276
- /// - featureOptions: The features to extract.
277
- /// - bitDepth: The bit depth of the audio data.
278
- /// - numberOfChannels: The number of channels in the audio data.
279
- /// - Returns: An `AudioAnalysisData` object containing the extracted features.
280
- public func processAudioBuffer(
281
- data: Data,
282
- sampleRate: Float,
283
- segmentDurationMs: Int,
284
- featureOptions: [String: Bool],
285
- bitDepth: Int,
286
- numberOfChannels: Int
287
- ) -> AudioAnalysisData? {
288
- guard !data.isEmpty else {
289
- Logger.debug("AudioProcessor", "Data is empty, rejecting")
290
- reject("DATA_EMPTY", "The audio data is empty.")
291
- return nil
292
- }
293
-
294
- // Convert Data to Float array based on bit depth
295
- let floatData: [Float]
296
- switch bitDepth {
297
- case 16:
298
- floatData = data.withUnsafeBytes { bufferPointer in
299
- let int16Pointer = bufferPointer.bindMemory(to: Int16.self)
300
- return int16Pointer.map { Float($0) / Float(Int16.max) }
301
- }
302
- case 32:
303
- floatData = data.withUnsafeBytes { bufferPointer in
304
- let int32Pointer = bufferPointer.bindMemory(to: Int32.self)
305
- return int32Pointer.map { Float($0) / Float(Int32.max) }
306
- }
307
- default:
308
- Logger.debug("AudioProcessor", "Unsupported bit depth. Rejecting")
309
- reject("UNSUPPORTED_BIT_DEPTH", "Unsupported bit depth: \(bitDepth)")
310
- return nil
311
- }
312
-
313
- return processChannelData(
314
- channelData: floatData,
315
- sampleRate: sampleRate,
316
- segmentDurationMs: segmentDurationMs,
317
- featureOptions: featureOptions,
318
- bitDepth: bitDepth,
319
- numberOfChannels: numberOfChannels
320
- )
321
- }
322
-
323
- /// Processes the given audio channel data to extract features.
324
- /// - Parameters:
325
- /// - channelData: The audio channel data to process.
326
- /// - sampleRate: The sample rate of the audio data.
327
- /// - segmentDurationMs: The duration of each segment in milliseconds.
328
- /// - featureOptions: The features to extract.
329
- /// - bitDepth: The bit depth of the audio data.
330
- /// - numberOfChannels: The number of channels in the audio data.
331
- /// - Returns: An `AudioAnalysisData` object containing the extracted features.
332
- private func processChannelData(
333
- channelData: [Float],
334
- sampleRate: Float,
335
- segmentDurationMs: Int,
336
- featureOptions: [String: Bool],
337
- bitDepth: Int,
338
- numberOfChannels: Int
339
- ) -> AudioAnalysisData? {
340
- Logger.debug("AudioProcessor", "Processing audio data with sample rate: \(sampleRate), segmentDurationMs: \(segmentDurationMs), bitDepth: \(bitDepth), numberOfChannels: \(numberOfChannels)")
341
-
342
- let startTime = CACurrentMediaTime()
343
-
344
- let length = channelData.count
345
- // Calculate points per segment based on segment duration
346
- let samplesPerSegment = Int(Float(segmentDurationMs) * sampleRate / 1000.0)
347
- var dataPoints = [DataPoint]()
348
- var minAmplitude: Float = .greatestFiniteMagnitude
349
- var maxAmplitude: Float = -.greatestFiniteMagnitude
350
-
351
- // Calculate bytes per sample
352
- let bytesPerSample = bitDepth / 8
353
-
354
- // Process data in segments
355
- var i = 0
356
- while i < length {
357
- let segmentEnd = min(i + samplesPerSegment, length)
358
- let segment = Array(channelData[i..<segmentEnd])
359
-
360
- // Calculate byte positions and timing
361
- let startPosition = i * bytesPerSample * numberOfChannels
362
- let endPosition = segmentEnd * bytesPerSample * numberOfChannels
363
- let startTime = Float(i) / sampleRate
364
- let endTime = Float(segmentEnd) / sampleRate
365
-
366
- // Process segment and create data point
367
- let dataPoint = processSegment(
368
- segment,
369
- sampleRate: sampleRate,
370
- featureOptions: featureOptions,
371
- startTime: startTime,
372
- endTime: endTime,
373
- startPosition: startPosition,
374
- endPosition: endPosition
375
- )
376
- dataPoints.append(dataPoint)
377
-
378
- // Update min/max amplitudes
379
- minAmplitude = min(minAmplitude, segment.min() ?? minAmplitude)
380
- maxAmplitude = max(maxAmplitude, segment.max() ?? maxAmplitude)
381
-
382
- i += samplesPerSegment
383
- }
384
-
385
- let endTime = CACurrentMediaTime()
386
- let processingTimeMs = Float((endTime - startTime) * 1000)
387
-
388
- Logger.debug("AudioProcessor", "Processed \(dataPoints.count) data points in \(processingTimeMs) ms")
389
-
390
- return AudioAnalysisData(
391
- segmentDurationMs: segmentDurationMs,
392
- durationMs: Int(Float(length) / sampleRate * 1000),
393
- bitDepth: bitDepth,
394
- numberOfChannels: numberOfChannels,
395
- sampleRate: Int(sampleRate),
396
- samples: length,
397
- dataPoints: dataPoints,
398
- amplitudeRange: AudioAnalysisData.AmplitudeRange(
399
- min: minAmplitude,
400
- max: maxAmplitude
401
- ),
402
- rmsRange: AudioAnalysisData.AmplitudeRange(
403
- min: 0,
404
- max: 1
405
- ),
406
- speechAnalysis: nil,
407
- extractionTimeMs: processingTimeMs
408
- )
409
- }
410
-
411
- private func processSegment(
412
- _ segment: [Float],
413
- sampleRate: Float,
414
- featureOptions: [String: Bool],
415
- startTime: Float,
416
- endTime: Float,
417
- startPosition: Int,
418
- endPosition: Int
419
- ) -> DataPoint {
420
- let sumSquares: Float = segment.reduce(0) { $0 + $1 * $1 }
421
- let rms = sqrt(sumSquares / Float(segment.count))
422
- let silent = rms < SILENCE_THRESHOLD_RMS
423
- let dB = Float(20 * log10(Double(rms)))
424
-
425
- let features = computeFeatures(
426
- segmentData: segment,
427
- sampleRate: sampleRate,
428
- sumSquares: sumSquares,
429
- zeroCrossings: 0,
430
- segmentLength: segment.count,
431
- featureOptions: featureOptions
432
- )
433
-
434
-
435
- let dataPoint = DataPoint(
436
- id: Int(uniqueIdCounter),
437
- amplitude: segment.max() ?? 0,
438
- rms: rms,
439
- dB: dB,
440
- silent: silent,
441
- features: features,
442
- speech: SpeechFeatures(isActive: !silent),
443
- startTime: startTime,
444
- endTime: endTime,
445
- startPosition: startPosition,
446
- endPosition: endPosition,
447
- samples: segment.count
448
- )
449
- uniqueIdCounter += 1
450
- return dataPoint
451
- }
452
-
453
- private func computeFeatures(
454
- segmentData: [Float],
455
- sampleRate: Float,
456
- sumSquares: Float,
457
- zeroCrossings: Int,
458
- segmentLength: Int,
459
- featureOptions: [String: Bool]
460
- ) -> Features {
461
- let rms = sqrt(sumSquares / Float(segmentLength))
462
- let energy = featureOptions["energy"] == true ? sumSquares : 0
463
- let zcr = featureOptions["zcr"] == true ? Float(zeroCrossings) / Float(segmentLength) : 0
464
- let mfcc = featureOptions["mfcc"] == true ? extractMFCC(from: segmentData, sampleRate: sampleRate) : []
465
- let spectralCentroid = featureOptions["spectralCentroid"] == true ? extractSpectralCentroid(from: segmentData, sampleRate: sampleRate) : 0
466
- let spectralFlatness = featureOptions["spectralFlatness"] == true ? extractSpectralFlatness(from: segmentData) : 0
467
- let spectralRollOff = featureOptions["spectralRollOff"] == true ? extractSpectralRollOff(from: segmentData, sampleRate: sampleRate) : 0
468
- let spectralBandwidth = featureOptions["spectralBandwidth"] == true ? extractSpectralBandwidth(from: segmentData, sampleRate: sampleRate) : 0
469
- let chromagram = featureOptions["chromagram"] == true ? extractChromagram(from: segmentData, sampleRate: sampleRate) : []
470
- let tempo = featureOptions["tempo"] == true ? extractTempo(from: segmentData, sampleRate: sampleRate) : 0
471
- let hnr = featureOptions["hnr"] == true ? extractHNR(from: segmentData) : 0
472
- let melSpectrogram = featureOptions["melSpectrogram"] == true ? computeMelSpectrogram(from: segmentData, sampleRate: sampleRate) : []
473
- let spectralContrast = featureOptions["spectralContrast"] == true ? computeSpectralContrast(from: segmentData, sampleRate: sampleRate) : []
474
- let tonnetz = featureOptions["tonnetz"] == true ? computeTonnetz(from: segmentData, sampleRate: sampleRate) : []
475
- let pitch = featureOptions["pitch"] == true ? estimatePitch(from: segmentData, sampleRate: sampleRate) : 0
476
-
477
- // Calculate min and max amplitudes from the segment data
478
- let minAmplitude = segmentData.map(abs).min() ?? 0
479
- let maxAmplitude = segmentData.map(abs).max() ?? 0
480
-
481
- let crc32Value = featureOptions["crc32"] == true ?
482
- calculateCRC32(from: segmentData, count: segmentData.count) : nil
483
-
484
- return Features(
485
- energy: energy,
486
- mfcc: mfcc,
487
- rms: rms,
488
- minAmplitude: minAmplitude,
489
- maxAmplitude: maxAmplitude,
490
- zcr: zcr,
491
- spectralCentroid: spectralCentroid,
492
- spectralFlatness: spectralFlatness,
493
- spectralRollOff: spectralRollOff,
494
- spectralBandwidth: spectralBandwidth,
495
- chromagram: chromagram,
496
- tempo: tempo,
497
- hnr: hnr,
498
- melSpectrogram: melSpectrogram,
499
- spectralContrast: spectralContrast,
500
- tonnetz: tonnetz,
501
- pitch: pitch,
502
- crc32: crc32Value
503
- )
504
- }
505
-
506
- /// Processes audio data with time range support
507
- public func processAudioData(
508
- startTimeMs: Double? = nil,
509
- endTimeMs: Double? = nil,
510
- segmentDurationMs: Int = 100, // Default 100ms
511
- featureOptions: [String: Bool]
512
- ) -> AudioAnalysisData? {
513
- guard let audioFile = audioFile else {
514
- Logger.debug("AudioProcessor", "No audio file loaded")
515
- return nil
516
- }
517
-
518
- let startTime = CACurrentMediaTime()
519
- let sampleRate = Float(audioFile.fileFormat.sampleRate)
520
- let _ = AVAudioFrameCount(audioFile.length) // Changed from totalFrameCount
521
- let bitDepth = audioFile.fileFormat.settings[AVLinearPCMBitDepthKey] as? Int ?? 16
522
- let numberOfChannels = Int(audioFile.fileFormat.channelCount)
523
-
524
- // Convert time to frames
525
- let startFrame = startTimeMs.map { AVAudioFramePosition(Double($0) * Double(sampleRate) / 1000.0) } ?? 0
526
- let endFrame = endTimeMs.map { AVAudioFramePosition(Double($0) * Double(sampleRate) / 1000.0) } ?? audioFile.length
527
-
528
- // Validate frame range
529
- guard startFrame >= 0 && endFrame <= audioFile.length && startFrame < endFrame else {
530
- Logger.debug("AudioProcessor", "Invalid time range")
531
- return nil
532
- }
533
-
534
- // Calculate frames per buffer based on segment duration
535
- let framesPerBuffer = AVAudioFrameCount(Float(sampleRate) * Float(segmentDurationMs) / 1000.0)
536
-
537
- guard let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: framesPerBuffer) else {
538
- Logger.debug("AudioProcessor", "Failed to create buffer")
539
- return nil
540
- }
541
-
542
- var dataPoints: [DataPoint] = []
543
- var minAmplitude: Float = .greatestFiniteMagnitude
544
- var maxAmplitude: Float = -.greatestFiniteMagnitude
545
- var currentId = 0
546
-
547
- audioFile.framePosition = startFrame
548
- var currentFrame = startFrame
549
-
550
- while currentFrame < endFrame {
551
- let framesToRead = min(framesPerBuffer, AVAudioFrameCount(endFrame - currentFrame))
552
-
553
- do {
554
- try audioFile.read(into: buffer, frameCount: framesToRead)
555
-
556
- guard let channelData = buffer.floatChannelData else {
557
- continue
558
- }
559
-
560
- // Process each channel's data
561
- var summedData = [Float](repeating: 0, count: Int(framesToRead))
562
- for channel in 0..<numberOfChannels {
563
- let channelBuffer = UnsafeBufferPointer(start: channelData[channel], count: Int(framesToRead))
564
- for (index, sample) in channelBuffer.enumerated() {
565
- summedData[index] += sample
566
- }
567
- }
568
-
569
- // Average across channels
570
- for i in 0..<summedData.count {
571
- summedData[i] /= Float(numberOfChannels)
572
- }
573
-
574
- // Calculate both peak amplitude and RMS
575
- var localMax: Float = 0
576
- var rms: Float = 0
577
- vDSP_maxmgv(summedData, 1, &localMax, vDSP_Length(framesToRead))
578
-
579
- // Calculate RMS using vDSP
580
- var meanSquare: Float = 0
581
- vDSP_measqv(summedData, 1, &meanSquare, vDSP_Length(framesToRead))
582
- rms = sqrt(meanSquare)
583
-
584
- minAmplitude = min(minAmplitude, localMax)
585
- maxAmplitude = max(maxAmplitude, localMax)
586
-
587
- // Create data point
588
- let startTime = Float(currentFrame) / Float(sampleRate)
589
- let endTime = Float(currentFrame + Int64(framesToRead)) / Float(sampleRate)
590
-
591
- let dataPoint = DataPoint(
592
- id: currentId,
593
- amplitude: localMax, // Always use peak amplitude
594
- rms: rms, // Use calculated RMS value
595
- dB: Float(20 * log10(Double(rms))), // Use RMS for dB calculation
596
- silent: rms < SILENCE_THRESHOLD_RMS, // Use RMS for silence detection
597
- features: computeFeatures(
598
- segmentData: Array(summedData[0..<Int(framesToRead)]), // Fixed dangling pointer
599
- sampleRate: sampleRate,
600
- sumSquares: rms * rms,
601
- zeroCrossings: 0,
602
- segmentLength: Int(framesToRead),
603
- featureOptions: featureOptions
604
- ),
605
- speech: SpeechFeatures(isActive: rms >= SILENCE_THRESHOLD_RMS),
606
- startTime: startTime,
607
- endTime: endTime,
608
- startPosition: Int(currentFrame),
609
- endPosition: Int(currentFrame + Int64(framesToRead)),
610
- samples: Int(framesToRead)
611
- )
612
-
613
- dataPoints.append(dataPoint)
614
- currentId += 1
615
- } catch {
616
- Logger.debug("AudioProcessor", "Error reading audio data: \(error)")
617
- return nil
618
- }
619
-
620
- currentFrame += Int64(framesToRead)
621
- }
622
-
623
- let endTime = CACurrentMediaTime()
624
- let extractionTime = Float(endTime - startTime) * 1000 // Convert to milliseconds
625
-
626
- return AudioAnalysisData(
627
- segmentDurationMs: segmentDurationMs,
628
- durationMs: Int(Float(endFrame - startFrame) * 1000 / sampleRate),
629
- bitDepth: bitDepth,
630
- numberOfChannels: numberOfChannels,
631
- sampleRate: Int(sampleRate),
632
- samples: Int(endFrame - startFrame),
633
- dataPoints: dataPoints,
634
- amplitudeRange: AudioAnalysisData.AmplitudeRange(
635
- min: minAmplitude,
636
- max: maxAmplitude
637
- ),
638
- rmsRange: AudioAnalysisData.AmplitudeRange(
639
- min: 0,
640
- max: 1
641
- ),
642
- speechAnalysis: nil,
643
- extractionTimeMs: extractionTime
644
- )
645
- }
646
-
647
- /// Trims audio file to specified range
648
- public func trimAudio(
649
- mode: String,
650
- startTimeMs: Double?,
651
- endTimeMs: Double?,
652
- ranges: [[String: Double]]?,
653
- outputFileName: String?,
654
- outputFormat: [String: Any]?,
655
- decodingOptions: [String: Any]?,
656
- progressCallback: ((Float, Int64, Int64) -> Void)? = nil
657
- ) -> TrimResult? {
658
- // Log the input parameters
659
- Logger.debug("AudioProcessor", "Starting audio trim operation:")
660
- Logger.debug("AudioProcessor", "- Mode: \(mode)")
661
- if let start = startTimeMs, let end = endTimeMs {
662
- Logger.debug("AudioProcessor", "- Time range: \(start)ms to \(end)ms")
663
- }
664
- if let ranges = ranges {
665
- Logger.debug("AudioProcessor", "- Ranges count: \(ranges.count)")
666
- }
667
-
668
- // Log output format details
669
- if let format = outputFormat {
670
- let formatType = format["format"] as? String ?? "unknown"
671
- let bitrate = format["bitrate"] as? Int ?? 0
672
- Logger.debug("AudioProcessor", "- Output format: \(formatType), bitrate: \(bitrate)")
673
- }
674
-
675
- guard let audioFile = audioFile else { return nil }
676
-
677
- let inputFormat = audioFile.processingFormat
678
- let inputSampleRate = inputFormat.sampleRate
679
- let inputChannels = Int(inputFormat.channelCount)
680
- let totalDurationMs = Double(audioFile.length) / inputSampleRate * 1000
681
-
682
- // Compute ranges to keep
683
- let keepRanges = computeKeepRanges(
684
- mode: mode,
685
- startTimeMs: startTimeMs,
686
- endTimeMs: endTimeMs,
687
- ranges: ranges,
688
- totalDurationMs: totalDurationMs
689
- )
690
-
691
- guard !keepRanges.isEmpty else { return nil }
692
-
693
- // Output format setup
694
- let requestedFormat = outputFormat?["format"] as? String ?? "wav"
695
- let validFormats = ["wav", "aac"]
696
- let formatStr = validFormats.contains(requestedFormat.lowercased()) ? requestedFormat.lowercased() : "aac"
697
-
698
- if formatStr != requestedFormat.lowercased() {
699
- Logger.debug("AudioProcessor", "Unsupported format '\(requestedFormat)', falling back to 'aac'")
700
- }
701
-
702
- let targetSampleRate = outputFormat?["sampleRate"] as? Double ?? inputSampleRate
703
- let targetChannels = outputFormat?["channels"] as? Int ?? inputChannels
704
- let targetBitDepth = outputFormat?["bitDepth"] as? Int ?? 16
705
- let bitrate = outputFormat?["bitrate"] as? Int ?? 128000
706
-
707
- let fileExtension = formatStr == "wav" ? "wav" : "aac"
708
- let outputURL = FileManager.default.temporaryDirectory
709
- .appendingPathComponent(outputFileName ?? UUID().uuidString)
710
- .appendingPathExtension(fileExtension)
711
-
712
- let decodingConfig = DecodingConfig.fromDictionary(decodingOptions ?? [:])
713
- let needFormatChange = decodingConfig.targetSampleRate != nil || decodingConfig.targetChannels != nil || decodingConfig.targetBitDepth != nil
714
- let isWavInput = audioFile.fileFormat.settings[AVFormatIDKey] as? UInt32 == kAudioFormatLinearPCM
715
-
716
- do {
717
- if isWavInput && formatStr == "wav" && !needFormatChange {
718
- // Fast path: WAV-to-WAV with no format changes
719
- let outputFile = try AVAudioFile(forWriting: outputURL, settings: inputFormat.settings)
720
- var totalFrames: Int64 = 0
721
- for range in keepRanges {
722
- // Break down complex expression
723
- let startTimeInSeconds = range[0] / 1000
724
- let endTimeInSeconds = range[1] / 1000
725
- let startFramePosition = startTimeInSeconds * inputSampleRate
726
- let endFramePosition = endTimeInSeconds * inputSampleRate
727
- totalFrames += Int64(endFramePosition - startFramePosition)
728
- }
729
- var cumulativeFrames: Int64 = 0
730
-
731
- for range in keepRanges {
732
- // Break down complex expressions
733
- let startTimeInSeconds = range[0] / 1000
734
- let startFrame = AVAudioFramePosition(startTimeInSeconds * inputSampleRate)
735
-
736
- let endTimeInSeconds = range[1] / 1000
737
- let endFramePosition = endTimeInSeconds * inputSampleRate
738
- let frameCount = AVAudioFrameCount(endFramePosition - Double(startFrame))
739
-
740
- let buffer = AVAudioPCMBuffer(pcmFormat: inputFormat, frameCapacity: frameCount)!
741
- audioFile.framePosition = startFrame
742
- try audioFile.read(into: buffer, frameCount: frameCount)
743
- try outputFile.write(from: buffer)
744
- cumulativeFrames += Int64(frameCount)
745
- let progress = Float(cumulativeFrames) / Float(totalFrames) * 100
746
- progressCallback?(progress, Int64(frameCount) * Int64(inputFormat.streamDescription.pointee.mBytesPerFrame), totalFrames * Int64(inputFormat.streamDescription.pointee.mBytesPerFrame))
747
- }
748
-
749
- // When creating the output file
750
- Logger.debug("AudioProcessor", "Creating output file at: \(outputURL.path)")
751
-
752
- // After processing is complete
753
- Logger.debug("AudioProcessor", "Trim operation completed")
754
- Logger.debug("AudioProcessor", "- Output file: \(outputURL.path)")
755
- Logger.debug("AudioProcessor", "- File exists: \(FileManager.default.fileExists(atPath: outputURL.path))")
756
- Logger.debug("AudioProcessor", "- File size: \((try? FileManager.default.attributesOfItem(atPath: outputURL.path)[.size] as? Int64) ?? 0) bytes") // Fixed optional unwrapping
757
- Logger.debug("AudioProcessor", "- File extension: \(outputURL.pathExtension)")
758
-
759
- return createTrimResult(from: outputURL, keepRanges: keepRanges, formatStr: formatStr, sampleRate: Int(inputSampleRate), channels: inputChannels, bitDepth: 16, bitrate: bitrate)
760
- } else {
761
- // Non-fast path: Decode and re-encode
762
- let targetFormat = AVAudioFormat(
763
- commonFormat: .pcmFormatFloat32,
764
- sampleRate: targetSampleRate,
765
- channels: AVAudioChannelCount(targetChannels),
766
- interleaved: false
767
- )!
768
-
769
- var totalFrames: Int64 = 0
770
- for range in keepRanges {
771
- // Break down complex expression
772
- let startTimeInSeconds = range[0] / 1000
773
- let endTimeInSeconds = range[1] / 1000
774
- let startFramePosition = startTimeInSeconds * inputSampleRate
775
- let endFramePosition = endTimeInSeconds * inputSampleRate
776
- totalFrames += Int64(endFramePosition - startFramePosition)
777
- }
778
- var cumulativeFrames: Int64 = 0
779
-
780
- if formatStr == "wav" {
781
- let outputFile = try AVAudioFile(forWriting: outputURL, settings: [
782
- AVFormatIDKey: kAudioFormatLinearPCM,
783
- AVSampleRateKey: targetSampleRate,
784
- AVNumberOfChannelsKey: targetChannels,
785
- AVLinearPCMBitDepthKey: targetBitDepth,
786
- AVLinearPCMIsFloatKey: false,
787
- AVLinearPCMIsBigEndianKey: false
788
- ])
789
-
790
- for range in keepRanges {
791
- // Break down complex expressions
792
- let startTimeInSeconds = range[0] / 1000
793
- let startFrame = AVAudioFramePosition(startTimeInSeconds * inputSampleRate)
794
-
795
- let endTimeInSeconds = range[1] / 1000
796
- let endFramePosition = endTimeInSeconds * inputSampleRate
797
- let frameCount = AVAudioFrameCount(endFramePosition - Double(startFrame))
798
-
799
- let buffer = AVAudioPCMBuffer(pcmFormat: inputFormat, frameCapacity: frameCount)!
800
- audioFile.framePosition = startFrame
801
- try audioFile.read(into: buffer, frameCount: frameCount)
802
- let converter = AVAudioConverter(from: inputFormat, to: targetFormat)!
803
- let convertedBuffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: frameCount)!
804
- var error: NSError?
805
- _ = converter.convert(to: convertedBuffer, error: &error) { inNumPackets, outStatus in
806
- outStatus.pointee = .haveData
807
- return buffer
808
- }
809
- if let error = error {
810
- Logger.debug("AudioProcessor", "Format conversion failed: \(error.localizedDescription)")
811
- Logger.debug("AudioProcessor", "Skipping this buffer")
812
- continue
813
- }
814
- try outputFile.write(from: convertedBuffer)
815
- cumulativeFrames += Int64(frameCount)
816
- let progress = Float(cumulativeFrames) / Float(totalFrames) * 100
817
- progressCallback?(progress, 0, totalFrames * Int64(inputFormat.streamDescription.pointee.mBytesPerFrame))
818
- }
819
- return createTrimResult(from: outputURL, keepRanges: keepRanges, formatStr: formatStr, sampleRate: Int(targetSampleRate), channels: targetChannels, bitDepth: targetBitDepth, bitrate: bitrate)
820
- } else {
821
- // Use AAC instead of Opus (Opus support removed)
822
- Logger.debug("AudioProcessor", "Using AAC format instead of requested \(formatStr)")
823
-
824
- // Keep the existing AAC settings structure for consistency
825
- let outputSettings: [String: Any] = [
826
- AVFormatIDKey: kAudioFormatMPEG4AAC,
827
- AVSampleRateKey: targetSampleRate,
828
- AVNumberOfChannelsKey: targetChannels,
829
- AVEncoderBitRateKey: bitrate,
830
- AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
831
- ]
832
- let _ = AVFileType.m4a // Changed from fileType
833
-
834
- // 4. Update container extension logic for when Opus was selected
835
- let _ = "m4a" // Changed from tempFileExtension
836
-
837
- // 5. Update the MIME type logic for AAC only
838
- let _ = "audio/mp4" // Changed from mimeType
839
-
840
- let outputFile = try AVAudioFile(forWriting: outputURL, settings: outputSettings)
841
- var totalFrames: Int64 = 0
842
- for range in keepRanges {
843
- // Break down complex expressions
844
- let startTimeInSeconds = range[0] / 1000
845
- let startFrame = AVAudioFramePosition(startTimeInSeconds * inputSampleRate)
846
-
847
- let endTimeInSeconds = range[1] / 1000
848
- let endFramePosition = endTimeInSeconds * inputSampleRate
849
- let frameCount = AVAudioFrameCount(endFramePosition - Double(startFrame))
850
-
851
- let buffer = AVAudioPCMBuffer(pcmFormat: inputFormat, frameCapacity: frameCount)!
852
- audioFile.framePosition = startFrame
853
- try audioFile.read(into: buffer, frameCount: frameCount)
854
- try outputFile.write(from: buffer)
855
- totalFrames += Int64(frameCount)
856
- let progress = Float(cumulativeFrames) / Float(totalFrames) * 100
857
- progressCallback?(progress, 0, totalFrames * Int64(inputFormat.streamDescription.pointee.mBytesPerFrame))
858
- }
859
- return createTrimResult(
860
- from: outputURL,
861
- keepRanges: keepRanges,
862
- formatStr: formatStr,
863
- sampleRate: Int(targetSampleRate),
864
- channels: targetChannels,
865
- bitDepth: 16,
866
- bitrate: bitrate,
867
- compression: nil
868
- )
869
- }
870
- }
871
- } catch {
872
- reject("TRIM_ERROR", "Failed to trim audio: \(error.localizedDescription)")
873
- return nil
874
- }
875
- }
876
-
877
- private func computeKeepRanges(mode: String, startTimeMs: Double?, endTimeMs: Double?, ranges: [[String: Double]]?, totalDurationMs: Double) -> [[Double]] {
878
- switch mode {
879
- case "single":
880
- guard let start = startTimeMs, let end = endTimeMs else { return [] }
881
- return [[start, end]]
882
- case "keep":
883
- return ranges?.map { [$0["startTimeMs"] ?? 0, $0["endTimeMs"] ?? totalDurationMs] } ?? []
884
- case "remove":
885
- let removeRanges = ranges?.map { [$0["startTimeMs"] ?? 0, $0["endTimeMs"] ?? totalDurationMs] }.sorted { $0[0] < $1[0] } ?? []
886
- var keepRanges: [[Double]] = []
887
- var lastEnd = 0.0
888
- for range in removeRanges {
889
- if range[0] > lastEnd {
890
- keepRanges.append([lastEnd, range[0]])
891
- }
892
- lastEnd = max(lastEnd, range[1])
893
- }
894
- if lastEnd < totalDurationMs {
895
- keepRanges.append([lastEnd, totalDurationMs])
896
- }
897
- return keepRanges
898
- default:
899
- return []
900
- }
901
- }
902
-
903
- private func createTrimResult(from url: URL, keepRanges: [[Double]], formatStr: String, sampleRate: Int, channels: Int, bitDepth: Int, bitrate: Int, compression: [String: Any]? = nil) -> TrimResult {
904
- let durationMs = keepRanges.map { $0[1] - $0[0] }.reduce(0, +)
905
- let size = (try? FileManager.default.attributesOfItem(atPath: url.path)[.size] as? Int64 ?? 0) ?? 0
906
- let fileExtension = formatStr == "wav" ? "wav" : "aac"
907
- return TrimResult(
908
- uri: url.absoluteString,
909
- filename: url.lastPathComponent,
910
- durationMs: durationMs,
911
- size: size,
912
- sampleRate: sampleRate,
913
- channels: channels,
914
- bitDepth: bitDepth,
915
- mimeType: "audio/\(fileExtension)",
916
- requestedFormat: formatStr,
917
- actualFormat: fileExtension,
918
- compression: compression
919
- )
920
- }
921
-
922
- private func createSampleBuffer(from buffer: AVAudioPCMBuffer) -> CMSampleBuffer? {
923
- var formatDesc: CMAudioFormatDescription?
924
- CMAudioFormatDescriptionCreate(
925
- allocator: kCFAllocatorDefault,
926
- asbd: buffer.format.streamDescription,
927
- layoutSize: 0,
928
- layout: nil,
929
- magicCookieSize: 0,
930
- magicCookie: nil,
931
- extensions: nil,
932
- formatDescriptionOut: &formatDesc
933
- )
934
- guard let format = formatDesc else { return nil }
935
-
936
- var sampleBuffer: CMSampleBuffer?
937
- var timingInfo = CMSampleTimingInfo(
938
- duration: CMTime(value: 1, timescale: CMTimeScale(buffer.format.sampleRate)),
939
- presentationTimeStamp: .zero,
940
- decodeTimeStamp: .invalid
941
- )
942
-
943
- CMSampleBufferCreate(
944
- allocator: kCFAllocatorDefault,
945
- dataBuffer: nil,
946
- dataReady: false,
947
- makeDataReadyCallback: nil,
948
- refcon: nil,
949
- formatDescription: format,
950
- sampleCount: CMItemCount(buffer.frameLength),
951
- sampleTimingEntryCount: 1,
952
- sampleTimingArray: &timingInfo,
953
- sampleSizeEntryCount: 0,
954
- sampleSizeArray: nil,
955
- sampleBufferOut: &sampleBuffer
956
- )
957
- guard let sampleBuf = sampleBuffer else { return nil }
958
-
959
- var dataBuffer: CMBlockBuffer?
960
- CMBlockBufferCreateWithMemoryBlock(
961
- allocator: kCFAllocatorDefault,
962
- memoryBlock: UnsafeMutableRawPointer(buffer.floatChannelData![0]),
963
- blockLength: Int(buffer.frameLength * buffer.format.streamDescription.pointee.mBytesPerFrame),
964
- blockAllocator: kCFAllocatorNull,
965
- customBlockSource: nil,
966
- offsetToData: 0,
967
- dataLength: Int(buffer.frameLength * buffer.format.streamDescription.pointee.mBytesPerFrame),
968
- flags: 0,
969
- blockBufferOut: &dataBuffer
970
- )
971
- guard let blockBuf = dataBuffer else { return nil }
972
-
973
- CMSampleBufferSetDataBuffer(sampleBuf, newValue: blockBuf)
974
-
975
- return sampleBuf
976
- }
977
-
978
- /// Extracts a preview of the audio data with consistent time range support
979
- /// - Parameters:
980
- /// - numberOfPoints: The number of points to extract
981
- /// - startTimeMs: Optional start time in milliseconds
982
- /// - endTimeMs: Optional end time in milliseconds
983
- /// - featureOptions: The features to extract
984
- /// - Returns: An `AudioAnalysisData` object containing the extracted features
985
- public func extractPreview(
986
- numberOfPoints: Int,
987
- startTimeMs: Double? = nil,
988
- endTimeMs: Double? = nil,
989
- featureOptions: [String: Bool]
990
- ) -> AudioAnalysisData? {
991
- guard let audioFile = audioFile else {
992
- reject("FILE_NOT_INITIALIZED", "Audio file is not initialized.")
993
- return nil
994
- }
995
-
996
- let sampleRate = Float(audioFile.fileFormat.sampleRate)
997
- let totalDurationMs = Double(audioFile.length) / Double(sampleRate) * 1000
998
-
999
- // Calculate effective time range
1000
- let effectiveStartMs = startTimeMs ?? 0.0
1001
- let effectiveEndMs = min(endTimeMs ?? totalDurationMs, totalDurationMs)
1002
- let durationMs = effectiveEndMs - effectiveStartMs // This is the actual duration we want to use
1003
-
1004
- // Convert time to frames with proper offset
1005
- let startFrame = AVAudioFramePosition(effectiveStartMs * Double(sampleRate) / 1000.0)
1006
- let endFrame = AVAudioFramePosition(effectiveEndMs * Double(sampleRate) / 1000.0)
1007
- let samplesInRange = Int(endFrame - startFrame)
1008
-
1009
- guard samplesInRange > 0 else {
1010
- reject("INVALID_RANGE", "Invalid sample range: contains no samples")
1011
- return nil
1012
- }
1013
-
1014
- // Calculate exact samples per point to get the requested number of points
1015
- let samplesPerPoint = samplesInRange / numberOfPoints
1016
- var dataPoints = [DataPoint]()
1017
- dataPoints.reserveCapacity(numberOfPoints)
1018
-
1019
- var minAmplitude: Float = .greatestFiniteMagnitude
1020
- var maxAmplitude: Float = -.greatestFiniteMagnitude
1021
-
1022
- let bytesPerSample = audioFile.fileFormat.settings[AVLinearPCMBitDepthKey] as? Int ?? 16 / 8
1023
-
1024
- for i in 0..<numberOfPoints {
1025
- let pointStartFrame = startFrame + Int64(i * samplesPerPoint)
1026
- let pointEndFrame = startFrame + Int64((i + 1) * samplesPerPoint)
1027
- let framesToRead = AVAudioFrameCount(pointEndFrame - pointStartFrame)
1028
-
1029
- // Calculate byte positions
1030
- let startPosition = Int(pointStartFrame) * bytesPerSample * Int(audioFile.fileFormat.channelCount)
1031
- let endPosition = Int(pointEndFrame) * bytesPerSample * Int(audioFile.fileFormat.channelCount)
1032
- let segmentStartTime = Float(pointStartFrame) / sampleRate
1033
- let segmentEndTime = Float(pointEndFrame) / sampleRate
1034
-
1035
- do {
1036
- audioFile.framePosition = pointStartFrame
1037
- let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: framesToRead)!
1038
- try audioFile.read(into: buffer, frameCount: framesToRead)
1039
-
1040
- guard let floatData = buffer.floatChannelData else { continue }
1041
-
1042
- var sumSquares: Float = 0
1043
- var zeroCrossings = 0
1044
- var prevValue: Float = 0
1045
- var localMinAmplitude: Float = .greatestFiniteMagnitude
1046
- var localMaxAmplitude: Float = -.greatestFiniteMagnitude
1047
-
1048
- // Process samples for this point
1049
- for frame in 0..<Int(framesToRead) {
1050
- let value = floatData[0][frame]
1051
- sumSquares += value * value
1052
- if frame > 0 && value * prevValue < 0 {
1053
- zeroCrossings += 1
1054
- }
1055
- prevValue = value
1056
-
1057
- let absValue = abs(value)
1058
- localMinAmplitude = min(localMinAmplitude, absValue)
1059
- localMaxAmplitude = max(localMaxAmplitude, absValue)
1060
- }
1061
-
1062
- let features = computeFeatures(segmentData: Array(UnsafeBufferPointer(start: floatData[0], count: Int(framesToRead))),
1063
- sampleRate: sampleRate,
1064
- sumSquares: sumSquares,
1065
- zeroCrossings: zeroCrossings,
1066
- segmentLength: Int(framesToRead),
1067
- featureOptions: featureOptions)
1068
-
1069
- let rms = features.rms
1070
- let silent = rms < SILENCE_THRESHOLD_RMS
1071
- let dB = Float(20 * log10(Double(rms)))
1072
-
1073
- let dataPoint = DataPoint(
1074
- id: Int(uniqueIdCounter),
1075
- amplitude: localMaxAmplitude,
1076
- rms: rms,
1077
- dB: dB,
1078
- silent: silent,
1079
- features: features,
1080
- speech: SpeechFeatures(isActive: !silent),
1081
- startTime: segmentStartTime,
1082
- endTime: segmentEndTime,
1083
- startPosition: startPosition,
1084
- endPosition: endPosition,
1085
- samples: Int(framesToRead)
1086
- )
1087
- dataPoints.append(dataPoint)
1088
- uniqueIdCounter += 1
1089
-
1090
- minAmplitude = min(minAmplitude, localMinAmplitude)
1091
- maxAmplitude = max(maxAmplitude, localMaxAmplitude)
1092
- } catch {
1093
- reject("AUDIO_READ_ERROR", "Error reading audio data: \(error.localizedDescription)")
1094
- return nil
1095
- }
1096
- }
1097
-
1098
- let startTime = CACurrentMediaTime() // Start timing
1099
-
1100
- let bitDepth = audioFile.fileFormat.settings[AVLinearPCMBitDepthKey] as? Int ?? 16
1101
- let numberOfChannels = Int(audioFile.processingFormat.channelCount)
1102
-
1103
- NSLog("""
1104
- [AudioProcessor] Starting preview extraction:
1105
- - numberOfPoints: \(numberOfPoints)
1106
- - startTimeMs: \(String(describing: startTimeMs))
1107
- - endTimeMs: \(String(describing: endTimeMs))
1108
- - durationMs: \(durationMs)
1109
- - sampleRate: \(sampleRate)
1110
- - bitDepth: \(bitDepth)
1111
- - channels: \(numberOfChannels)
1112
- - samplesInRange: \(samplesInRange)
1113
- - samplesPerPoint: \(samplesPerPoint)
1114
- """)
1115
-
1116
- let endTime = CACurrentMediaTime()
1117
- let extractionTimeMs = Float((endTime - startTime) * 1000)
1118
-
1119
- NSLog("""
1120
- [AudioProcessor] Preview extraction completed:
1121
- - dataPoints generated: \(dataPoints.count)
1122
- - extractionTimeMs: \(String(format: "%.2f", extractionTimeMs))ms
1123
- - amplitudeRange: (min: \(String(format: "%.6f", minAmplitude)), max: \(String(format: "%.6f", maxAmplitude)))
1124
- """)
1125
-
1126
- return AudioAnalysisData(
1127
- segmentDurationMs: 100, // Default 100ms
1128
- durationMs: Int(durationMs), // Use actual duration of trimmed section
1129
- bitDepth: bitDepth,
1130
- numberOfChannels: numberOfChannels,
1131
- sampleRate: Int(sampleRate),
1132
- samples: samplesInRange,
1133
- dataPoints: dataPoints,
1134
- amplitudeRange: AudioAnalysisData.AmplitudeRange(
1135
- min: minAmplitude,
1136
- max: maxAmplitude
1137
- ),
1138
- rmsRange: AudioAnalysisData.AmplitudeRange(
1139
- min: 0,
1140
- max: 1
1141
- ),
1142
- speechAnalysis: nil,
1143
- extractionTimeMs: extractionTimeMs
1144
- )
1145
- }
1146
-
1147
- // Add this helper function to the AudioProcessor class
1148
- private func getDocumentsDirectory() -> URL {
1149
- return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
1150
- }
1151
- }