@siteed/audio-studio 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 (375) hide show
  1. package/CHANGELOG.md +535 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/android/build.gradle +143 -0
  5. package/android/src/androidTest/assets/chorus.wav +0 -0
  6. package/android/src/androidTest/assets/jfk.wav +0 -0
  7. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  8. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  9. package/android/src/androidTest/java/net/siteed/audiostudio/AudioProcessorInstrumentedTest.kt +197 -0
  10. package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderInstrumentedTest.kt +541 -0
  11. package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderPerformanceInstrumentedTest.kt +234 -0
  12. package/android/src/androidTest/java/net/siteed/audiostudio/integration/AudioFocusStrategyIntegrationTest.kt +332 -0
  13. package/android/src/androidTest/java/net/siteed/audiostudio/integration/BufferDurationIntegrationTest.kt +324 -0
  14. package/android/src/androidTest/java/net/siteed/audiostudio/integration/CompressedOnlyOutputTest.kt +253 -0
  15. package/android/src/androidTest/java/net/siteed/audiostudio/integration/DeviceDisconnectionFallbackTest.kt +218 -0
  16. package/android/src/androidTest/java/net/siteed/audiostudio/integration/EventEmissionIntervalTest.kt +120 -0
  17. package/android/src/androidTest/java/net/siteed/audiostudio/integration/M4aFormatTest.kt +345 -0
  18. package/android/src/androidTest/java/net/siteed/audiostudio/integration/OutputControlIntegrationTest.kt +340 -0
  19. package/android/src/androidTest/java/net/siteed/audiostudio/integration/PcmStreamingDurationTest.kt +252 -0
  20. package/android/src/androidTest/java/net/siteed/audiostudio/integration/README.md +95 -0
  21. package/android/src/androidTest/java/net/siteed/audiostudio/integration/run_integration_tests.sh +43 -0
  22. package/android/src/main/AndroidManifest.xml +30 -0
  23. package/android/src/main/CMakeLists.txt +29 -0
  24. package/android/src/main/java/net/siteed/audiostudio/AudioAnalysisData.kt +188 -0
  25. package/android/src/main/java/net/siteed/audiostudio/AudioDataEncoder.kt +9 -0
  26. package/android/src/main/java/net/siteed/audiostudio/AudioDeviceManager.kt +1741 -0
  27. package/android/src/main/java/net/siteed/audiostudio/AudioFeaturesNative.kt +26 -0
  28. package/android/src/main/java/net/siteed/audiostudio/AudioFileHandler.kt +136 -0
  29. package/android/src/main/java/net/siteed/audiostudio/AudioFormatUtils.kt +354 -0
  30. package/android/src/main/java/net/siteed/audiostudio/AudioNotificationsManager.kt +439 -0
  31. package/android/src/main/java/net/siteed/audiostudio/AudioProcessor.kt +2237 -0
  32. package/android/src/main/java/net/siteed/audiostudio/AudioRecorderManager.kt +2163 -0
  33. package/android/src/main/java/net/siteed/audiostudio/AudioRecordingService.kt +167 -0
  34. package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +1112 -0
  35. package/android/src/main/java/net/siteed/audiostudio/AudioTrimmer.kt +1099 -0
  36. package/android/src/main/java/net/siteed/audiostudio/Constants.kt +37 -0
  37. package/android/src/main/java/net/siteed/audiostudio/EventSender.kt +7 -0
  38. package/android/src/main/java/net/siteed/audiostudio/FFT.kt +100 -0
  39. package/android/src/main/java/net/siteed/audiostudio/Features.kt +98 -0
  40. package/android/src/main/java/net/siteed/audiostudio/LogUtils.kt +93 -0
  41. package/android/src/main/java/net/siteed/audiostudio/MelSpectrogramNative.kt +36 -0
  42. package/android/src/main/java/net/siteed/audiostudio/NotificationConfig.kt +72 -0
  43. package/android/src/main/java/net/siteed/audiostudio/PermissionUtils.kt +68 -0
  44. package/android/src/main/java/net/siteed/audiostudio/RecordingActionReceiver.kt +59 -0
  45. package/android/src/main/java/net/siteed/audiostudio/RecordingConfig.kt +259 -0
  46. package/android/src/main/java/net/siteed/audiostudio/WaveformConfig.kt +19 -0
  47. package/android/src/main/java/net/siteed/audiostudio/WaveformRenderer.kt +159 -0
  48. package/android/src/main/jni/AudioFeaturesJNI.cpp +152 -0
  49. package/android/src/main/jni/MelSpectrogramJNI.cpp +165 -0
  50. package/android/src/main/res/drawable/ic_default_action_icon.xml +16 -0
  51. package/android/src/main/res/drawable/ic_microphone.xml +13 -0
  52. package/android/src/main/res/drawable/ic_pause.xml +10 -0
  53. package/android/src/main/res/drawable/ic_play.xml +10 -0
  54. package/android/src/main/res/drawable/ic_stop.xml +10 -0
  55. package/android/src/main/res/layout/notification_recording.xml +37 -0
  56. package/android/src/test/java/net/siteed/audiostudio/AudioFileHandlerTest.kt +279 -0
  57. package/android/src/test/java/net/siteed/audiostudio/AudioFocusStrategyTest.kt +249 -0
  58. package/android/src/test/java/net/siteed/audiostudio/AudioFormatTest.kt +151 -0
  59. package/android/src/test/java/net/siteed/audiostudio/AudioFormatUtilsTest.kt +273 -0
  60. package/android/src/test/java/net/siteed/audiostudio/DeviceDisconnectionFallbackUnitTest.kt +140 -0
  61. package/android/src/test/resources/chorus.wav +0 -0
  62. package/android/src/test/resources/generate_test_audio.py +94 -0
  63. package/android/src/test/resources/jfk.wav +0 -0
  64. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  65. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  66. package/app.plugin.js +3 -0
  67. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +4 -0
  68. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  69. package/build/cjs/AudioAnalysis/audioFeaturesWasm.js +164 -0
  70. package/build/cjs/AudioAnalysis/audioFeaturesWasm.js.map +1 -0
  71. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +213 -0
  72. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  73. package/build/cjs/AudioAnalysis/extractAudioData.js +21 -0
  74. package/build/cjs/AudioAnalysis/extractAudioData.js.map +1 -0
  75. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +90 -0
  76. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  77. package/build/cjs/AudioAnalysis/extractPreview.js +28 -0
  78. package/build/cjs/AudioAnalysis/extractPreview.js.map +1 -0
  79. package/build/cjs/AudioAnalysis/extractWaveform.js +18 -0
  80. package/build/cjs/AudioAnalysis/extractWaveform.js.map +1 -0
  81. package/build/cjs/AudioAnalysis/melSpectrogramWasm.js +149 -0
  82. package/build/cjs/AudioAnalysis/melSpectrogramWasm.js.map +1 -0
  83. package/build/cjs/AudioDeviceManager.js +688 -0
  84. package/build/cjs/AudioDeviceManager.js.map +1 -0
  85. package/build/cjs/AudioRecorder.provider.js +78 -0
  86. package/build/cjs/AudioRecorder.provider.js.map +1 -0
  87. package/build/cjs/AudioStudio.native.js +8 -0
  88. package/build/cjs/AudioStudio.native.js.map +1 -0
  89. package/build/cjs/AudioStudio.types.js +11 -0
  90. package/build/cjs/AudioStudio.types.js.map +1 -0
  91. package/build/cjs/AudioStudio.web.js +708 -0
  92. package/build/cjs/AudioStudio.web.js.map +1 -0
  93. package/build/cjs/AudioStudioModule.js +718 -0
  94. package/build/cjs/AudioStudioModule.js.map +1 -0
  95. package/build/cjs/WebRecorder.web.js +865 -0
  96. package/build/cjs/WebRecorder.web.js.map +1 -0
  97. package/build/cjs/constants/platformLimitations.js +99 -0
  98. package/build/cjs/constants/platformLimitations.js.map +1 -0
  99. package/build/cjs/constants.js +20 -0
  100. package/build/cjs/constants.js.map +1 -0
  101. package/build/cjs/events.js +29 -0
  102. package/build/cjs/events.js.map +1 -0
  103. package/build/cjs/hooks/useAudioDevices.js +179 -0
  104. package/build/cjs/hooks/useAudioDevices.js.map +1 -0
  105. package/build/cjs/index.js +64 -0
  106. package/build/cjs/index.js.map +1 -0
  107. package/build/cjs/trimAudio.js +76 -0
  108. package/build/cjs/trimAudio.js.map +1 -0
  109. package/build/cjs/useAudioRecorder.js +535 -0
  110. package/build/cjs/useAudioRecorder.js.map +1 -0
  111. package/build/cjs/utils/BlobFix.js +502 -0
  112. package/build/cjs/utils/BlobFix.js.map +1 -0
  113. package/build/cjs/utils/audioProcessing.js +136 -0
  114. package/build/cjs/utils/audioProcessing.js.map +1 -0
  115. package/build/cjs/utils/cleanNativeOptions.js +22 -0
  116. package/build/cjs/utils/cleanNativeOptions.js.map +1 -0
  117. package/build/cjs/utils/concatenateBuffers.js +25 -0
  118. package/build/cjs/utils/concatenateBuffers.js.map +1 -0
  119. package/build/cjs/utils/convertPCMToFloat32.js +124 -0
  120. package/build/cjs/utils/convertPCMToFloat32.js.map +1 -0
  121. package/build/cjs/utils/crc32.js +52 -0
  122. package/build/cjs/utils/crc32.js.map +1 -0
  123. package/build/cjs/utils/encodingToBitDepth.js +17 -0
  124. package/build/cjs/utils/encodingToBitDepth.js.map +1 -0
  125. package/build/cjs/utils/getWavFileInfo.js +96 -0
  126. package/build/cjs/utils/getWavFileInfo.js.map +1 -0
  127. package/build/cjs/utils/writeWavHeader.js +88 -0
  128. package/build/cjs/utils/writeWavHeader.js.map +1 -0
  129. package/build/cjs/workers/InlineFeaturesExtractor.web.js +294 -0
  130. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +1 -0
  131. package/build/cjs/workers/inlineAudioWebWorker.web.js +190 -0
  132. package/build/cjs/workers/inlineAudioWebWorker.web.js.map +1 -0
  133. package/build/cjs/workers/wasmGlueString.web.js +27 -0
  134. package/build/cjs/workers/wasmGlueString.web.js.map +1 -0
  135. package/build/esm/AudioAnalysis/AudioAnalysis.types.js +3 -0
  136. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  137. package/build/esm/AudioAnalysis/audioFeaturesWasm.js +126 -0
  138. package/build/esm/AudioAnalysis/audioFeaturesWasm.js.map +1 -0
  139. package/build/esm/AudioAnalysis/extractAudioAnalysis.js +205 -0
  140. package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  141. package/build/esm/AudioAnalysis/extractAudioData.js +14 -0
  142. package/build/esm/AudioAnalysis/extractAudioData.js.map +1 -0
  143. package/build/esm/AudioAnalysis/extractMelSpectrogram.js +86 -0
  144. package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  145. package/build/esm/AudioAnalysis/extractPreview.js +25 -0
  146. package/build/esm/AudioAnalysis/extractPreview.js.map +1 -0
  147. package/build/esm/AudioAnalysis/extractWaveform.js +11 -0
  148. package/build/esm/AudioAnalysis/extractWaveform.js.map +1 -0
  149. package/build/esm/AudioAnalysis/melSpectrogramWasm.js +111 -0
  150. package/build/esm/AudioAnalysis/melSpectrogramWasm.js.map +1 -0
  151. package/build/esm/AudioDeviceManager.js +681 -0
  152. package/build/esm/AudioDeviceManager.js.map +1 -0
  153. package/build/esm/AudioRecorder.provider.js +40 -0
  154. package/build/esm/AudioRecorder.provider.js.map +1 -0
  155. package/build/esm/AudioStudio.native.js +6 -0
  156. package/build/esm/AudioStudio.native.js.map +1 -0
  157. package/build/esm/AudioStudio.types.js +8 -0
  158. package/build/esm/AudioStudio.types.js.map +1 -0
  159. package/build/esm/AudioStudio.web.js +704 -0
  160. package/build/esm/AudioStudio.web.js.map +1 -0
  161. package/build/esm/AudioStudioModule.js +713 -0
  162. package/build/esm/AudioStudioModule.js.map +1 -0
  163. package/build/esm/WebRecorder.web.js +861 -0
  164. package/build/esm/WebRecorder.web.js.map +1 -0
  165. package/build/esm/constants/platformLimitations.js +90 -0
  166. package/build/esm/constants/platformLimitations.js.map +1 -0
  167. package/build/esm/constants.js +17 -0
  168. package/build/esm/constants.js.map +1 -0
  169. package/build/esm/events.js +21 -0
  170. package/build/esm/events.js.map +1 -0
  171. package/build/esm/hooks/useAudioDevices.js +176 -0
  172. package/build/esm/hooks/useAudioDevices.js.map +1 -0
  173. package/build/esm/index.js +23 -0
  174. package/build/esm/index.js.map +1 -0
  175. package/build/esm/trimAudio.js +69 -0
  176. package/build/esm/trimAudio.js.map +1 -0
  177. package/build/esm/useAudioRecorder.js +529 -0
  178. package/build/esm/useAudioRecorder.js.map +1 -0
  179. package/build/esm/utils/BlobFix.js +498 -0
  180. package/build/esm/utils/BlobFix.js.map +1 -0
  181. package/build/esm/utils/audioProcessing.js +133 -0
  182. package/build/esm/utils/audioProcessing.js.map +1 -0
  183. package/build/esm/utils/cleanNativeOptions.js +19 -0
  184. package/build/esm/utils/cleanNativeOptions.js.map +1 -0
  185. package/build/esm/utils/concatenateBuffers.js +21 -0
  186. package/build/esm/utils/concatenateBuffers.js.map +1 -0
  187. package/build/esm/utils/convertPCMToFloat32.js +120 -0
  188. package/build/esm/utils/convertPCMToFloat32.js.map +1 -0
  189. package/build/esm/utils/crc32.js +50 -0
  190. package/build/esm/utils/crc32.js.map +1 -0
  191. package/build/esm/utils/encodingToBitDepth.js +13 -0
  192. package/build/esm/utils/encodingToBitDepth.js.map +1 -0
  193. package/build/esm/utils/getWavFileInfo.js +92 -0
  194. package/build/esm/utils/getWavFileInfo.js.map +1 -0
  195. package/build/esm/utils/writeWavHeader.js +84 -0
  196. package/build/esm/utils/writeWavHeader.js.map +1 -0
  197. package/build/esm/workers/InlineFeaturesExtractor.web.js +291 -0
  198. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +1 -0
  199. package/build/esm/workers/inlineAudioWebWorker.web.js +187 -0
  200. package/build/esm/workers/inlineAudioWebWorker.web.js.map +1 -0
  201. package/build/esm/workers/wasmGlueString.web.js +24 -0
  202. package/build/esm/workers/wasmGlueString.web.js.map +1 -0
  203. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +198 -0
  204. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  205. package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts +24 -0
  206. package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts.map +1 -0
  207. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts +74 -0
  208. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  209. package/build/types/AudioAnalysis/extractAudioData.d.ts +3 -0
  210. package/build/types/AudioAnalysis/extractAudioData.d.ts.map +1 -0
  211. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts +20 -0
  212. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +1 -0
  213. package/build/types/AudioAnalysis/extractPreview.d.ts +11 -0
  214. package/build/types/AudioAnalysis/extractPreview.d.ts.map +1 -0
  215. package/build/types/AudioAnalysis/extractWaveform.d.ts +8 -0
  216. package/build/types/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  217. package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts +16 -0
  218. package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts.map +1 -0
  219. package/build/types/AudioDeviceManager.d.ts +187 -0
  220. package/build/types/AudioDeviceManager.d.ts.map +1 -0
  221. package/build/types/AudioRecorder.provider.d.ts +11 -0
  222. package/build/types/AudioRecorder.provider.d.ts.map +1 -0
  223. package/build/types/AudioStudio.native.d.ts +3 -0
  224. package/build/types/AudioStudio.native.d.ts.map +1 -0
  225. package/build/types/AudioStudio.types.d.ts +760 -0
  226. package/build/types/AudioStudio.types.d.ts.map +1 -0
  227. package/build/types/AudioStudio.web.d.ts +96 -0
  228. package/build/types/AudioStudio.web.d.ts.map +1 -0
  229. package/build/types/AudioStudioModule.d.ts +3 -0
  230. package/build/types/AudioStudioModule.d.ts.map +1 -0
  231. package/build/types/WebRecorder.web.d.ts +208 -0
  232. package/build/types/WebRecorder.web.d.ts.map +1 -0
  233. package/build/types/constants/platformLimitations.d.ts +40 -0
  234. package/build/types/constants/platformLimitations.d.ts.map +1 -0
  235. package/build/types/constants.d.ts +14 -0
  236. package/build/types/constants.d.ts.map +1 -0
  237. package/build/types/events.d.ts +29 -0
  238. package/build/types/events.d.ts.map +1 -0
  239. package/build/types/hooks/useAudioDevices.d.ts +15 -0
  240. package/build/types/hooks/useAudioDevices.d.ts.map +1 -0
  241. package/build/types/index.d.ts +21 -0
  242. package/build/types/index.d.ts.map +1 -0
  243. package/build/types/trimAudio.d.ts +25 -0
  244. package/build/types/trimAudio.d.ts.map +1 -0
  245. package/build/types/useAudioRecorder.d.ts +22 -0
  246. package/build/types/useAudioRecorder.d.ts.map +1 -0
  247. package/build/types/utils/BlobFix.d.ts +9 -0
  248. package/build/types/utils/BlobFix.d.ts.map +1 -0
  249. package/build/types/utils/audioProcessing.d.ts +24 -0
  250. package/build/types/utils/audioProcessing.d.ts.map +1 -0
  251. package/build/types/utils/cleanNativeOptions.d.ts +15 -0
  252. package/build/types/utils/cleanNativeOptions.d.ts.map +1 -0
  253. package/build/types/utils/concatenateBuffers.d.ts +8 -0
  254. package/build/types/utils/concatenateBuffers.d.ts.map +1 -0
  255. package/build/types/utils/convertPCMToFloat32.d.ts +13 -0
  256. package/build/types/utils/convertPCMToFloat32.d.ts.map +1 -0
  257. package/build/types/utils/crc32.d.ts +7 -0
  258. package/build/types/utils/crc32.d.ts.map +1 -0
  259. package/build/types/utils/encodingToBitDepth.d.ts +5 -0
  260. package/build/types/utils/encodingToBitDepth.d.ts.map +1 -0
  261. package/build/types/utils/getWavFileInfo.d.ts +26 -0
  262. package/build/types/utils/getWavFileInfo.d.ts.map +1 -0
  263. package/build/types/utils/writeWavHeader.d.ts +34 -0
  264. package/build/types/utils/writeWavHeader.d.ts.map +1 -0
  265. package/build/types/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  266. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  267. package/build/types/workers/inlineAudioWebWorker.web.d.ts +2 -0
  268. package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  269. package/build/types/workers/wasmGlueString.web.d.ts +2 -0
  270. package/build/types/workers/wasmGlueString.web.d.ts.map +1 -0
  271. package/cpp/AudioFeatures.cpp +274 -0
  272. package/cpp/AudioFeatures.h +85 -0
  273. package/cpp/AudioFeaturesBridge.cpp +146 -0
  274. package/cpp/AudioFeaturesBridge.h +47 -0
  275. package/cpp/MelSpectrogram.cpp +227 -0
  276. package/cpp/MelSpectrogram.h +82 -0
  277. package/cpp/MelSpectrogramBridge.cpp +112 -0
  278. package/cpp/MelSpectrogramBridge.h +33 -0
  279. package/cpp/kiss_fft/COPYING +11 -0
  280. package/cpp/kiss_fft/_kiss_fft_guts.h +167 -0
  281. package/cpp/kiss_fft/kiss_fft.c +424 -0
  282. package/cpp/kiss_fft/kiss_fft.h +160 -0
  283. package/cpp/kiss_fft/kiss_fft_log.h +36 -0
  284. package/cpp/kiss_fft/kiss_fftr.c +155 -0
  285. package/cpp/kiss_fft/kiss_fftr.h +54 -0
  286. package/expo-module.config.json +10 -0
  287. package/ios/AudioAnalysisData.swift +74 -0
  288. package/ios/AudioDeviceManager.swift +670 -0
  289. package/ios/AudioFeaturesWrapper.h +21 -0
  290. package/ios/AudioFeaturesWrapper.mm +63 -0
  291. package/ios/AudioNotificationManager.swift +154 -0
  292. package/ios/AudioProcessingHelpers.swift +797 -0
  293. package/ios/AudioProcessor.swift +1191 -0
  294. package/ios/AudioStreamError.swift +7 -0
  295. package/ios/AudioStreamManager.swift +2369 -0
  296. package/ios/AudioStreamManagerDelegate.swift +16 -0
  297. package/ios/AudioStudio.podspec +39 -0
  298. package/ios/AudioStudioModule.swift +1111 -0
  299. package/ios/AudioStudioTests/AudioFileHandlerTests.swift +338 -0
  300. package/ios/AudioStudioTests/AudioFormatUtilsTests.swift +331 -0
  301. package/ios/AudioStudioTests/AudioTestHelpers.swift +130 -0
  302. package/ios/AudioStudioTests/CompressedOnlyOutputTests.swift +294 -0
  303. package/ios/AudioStudioTests/EventEmissionIntervalTests.swift +105 -0
  304. package/ios/AudioStudioTests/Info.plist +22 -0
  305. package/ios/AudioStudioTests/README.md +39 -0
  306. package/ios/AudioStudioTests/SimpleAudioTest.swift +98 -0
  307. package/ios/AudioStudioTests/TestAudioGenerator.swift +75 -0
  308. package/ios/DataPoint.swift +54 -0
  309. package/ios/DecodingConfig.swift +59 -0
  310. package/ios/FFT.swift +62 -0
  311. package/ios/Features.swift +95 -0
  312. package/ios/ISSUE_IOS.md +68 -0
  313. package/ios/Logger.swift +39 -0
  314. package/ios/MelSpectrogramWrapper.h +30 -0
  315. package/ios/MelSpectrogramWrapper.mm +97 -0
  316. package/ios/NotificationExtension.swift +15 -0
  317. package/ios/RecordingResult.swift +22 -0
  318. package/ios/RecordingSettings.swift +311 -0
  319. package/ios/WaveformExtractor.swift +105 -0
  320. package/ios/tests/README.md +41 -0
  321. package/ios/tests/integration/buffer_and_fallback_test.swift +178 -0
  322. package/ios/tests/integration/buffer_duration_test.swift +185 -0
  323. package/ios/tests/integration/compressed_only_output_test.swift +271 -0
  324. package/ios/tests/integration/output_control_test.swift +322 -0
  325. package/ios/tests/integration/run_integration_tests.sh +37 -0
  326. package/ios/tests/opus_support_test_macos.swift +154 -0
  327. package/ios/tests/standalone/audio_processing_test.swift +144 -0
  328. package/ios/tests/standalone/audio_recording_test.swift +277 -0
  329. package/ios/tests/standalone/audio_streaming_test.swift +249 -0
  330. package/ios/tests/standalone/standalone_test.swift +144 -0
  331. package/package.json +146 -0
  332. package/plugin/build/index.cjs +194 -0
  333. package/plugin/build/index.d.cts +22 -0
  334. package/plugin/build/index.js +194 -0
  335. package/plugin/src/index.ts +285 -0
  336. package/plugin/tsconfig.json +10 -0
  337. package/plugin/tsconfig.tsbuildinfo +1 -0
  338. package/prebuilt/wasm/mel-spectrogram.js +18 -0
  339. package/src/AudioAnalysis/AudioAnalysis.types.ts +226 -0
  340. package/src/AudioAnalysis/audio-features-wasm.d.ts +37 -0
  341. package/src/AudioAnalysis/audioFeaturesWasm.ts +200 -0
  342. package/src/AudioAnalysis/extractAudioAnalysis.ts +350 -0
  343. package/src/AudioAnalysis/extractAudioData.ts +17 -0
  344. package/src/AudioAnalysis/extractMelSpectrogram.ts +140 -0
  345. package/src/AudioAnalysis/extractPreview.ts +34 -0
  346. package/src/AudioAnalysis/extractWaveform.ts +22 -0
  347. package/src/AudioAnalysis/mel-spectrogram-wasm.d.ts +48 -0
  348. package/src/AudioAnalysis/melSpectrogramWasm.ts +179 -0
  349. package/src/AudioDeviceManager.ts +800 -0
  350. package/src/AudioRecorder.provider.tsx +57 -0
  351. package/src/AudioStudio.native.ts +6 -0
  352. package/src/AudioStudio.types.ts +899 -0
  353. package/src/AudioStudio.web.ts +911 -0
  354. package/src/AudioStudioModule.ts +984 -0
  355. package/src/WebRecorder.web.ts +1114 -0
  356. package/src/constants/platformLimitations.ts +118 -0
  357. package/src/constants.ts +21 -0
  358. package/src/events.ts +63 -0
  359. package/src/hooks/useAudioDevices.ts +213 -0
  360. package/src/index.ts +67 -0
  361. package/src/trimAudio.ts +94 -0
  362. package/src/types/crc-32.d.ts +9 -0
  363. package/src/useAudioRecorder.tsx +784 -0
  364. package/src/utils/BlobFix.ts +561 -0
  365. package/src/utils/audioProcessing.ts +205 -0
  366. package/src/utils/cleanNativeOptions.ts +18 -0
  367. package/src/utils/concatenateBuffers.ts +24 -0
  368. package/src/utils/convertPCMToFloat32.ts +170 -0
  369. package/src/utils/crc32.ts +59 -0
  370. package/src/utils/encodingToBitDepth.ts +18 -0
  371. package/src/utils/getWavFileInfo.ts +132 -0
  372. package/src/utils/writeWavHeader.ts +115 -0
  373. package/src/workers/InlineFeaturesExtractor.web.tsx +291 -0
  374. package/src/workers/inlineAudioWebWorker.web.tsx +186 -0
  375. package/src/workers/wasmGlueString.web.ts +23 -0
@@ -0,0 +1,439 @@
1
+ package net.siteed.audiostudio
2
+
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.PendingIntent
7
+ import android.content.Context
8
+ import android.content.Intent
9
+ import android.graphics.Color
10
+ import android.os.Build
11
+ import android.os.Handler
12
+ import android.os.Looper
13
+ import android.os.SystemClock
14
+ import android.util.Log
15
+ import android.view.View
16
+ import android.widget.RemoteViews
17
+ import androidx.core.app.NotificationCompat
18
+ import java.lang.ref.WeakReference
19
+ import java.util.Locale
20
+ import java.util.concurrent.atomic.AtomicBoolean
21
+ import java.util.Objects
22
+ import net.siteed.audiostudio.LogUtils
23
+
24
+ class AudioNotificationManager private constructor(context: Context) {
25
+ companion object {
26
+ private const val CLASS_NAME = "AudioNotificationManager"
27
+ private const val WAVEFORM_UPDATE_INTERVAL = 100L
28
+ private const val UPDATE_INTERVAL = 1000L
29
+
30
+ @Volatile
31
+ private var instance: AudioNotificationManager? = null
32
+
33
+ fun getInstance(context: Context): AudioNotificationManager {
34
+ return instance ?: synchronized(this) {
35
+ instance ?: AudioNotificationManager(context).also { instance = it }
36
+ }
37
+ }
38
+ }
39
+
40
+ private val contextRef = WeakReference(context.applicationContext)
41
+ private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
42
+ private val mainHandler = Handler(Looper.getMainLooper())
43
+ private val isUpdating = AtomicBoolean(false)
44
+ private val isPaused = AtomicBoolean(false)
45
+
46
+ private var lastRemoteViewsUpdate = 0L
47
+ private var consecutiveUpdateFailures = 0
48
+ private var lastSuccessfulUpdate: Long = 0
49
+ private val maxUpdateFailures = 3
50
+ private val remoteViewsRefreshInterval = 10000L // Refresh RemoteViews every 10 seconds
51
+
52
+ private lateinit var notificationBuilder: NotificationCompat.Builder
53
+ private lateinit var remoteViews: RemoteViews
54
+ private lateinit var recordingConfig: RecordingConfig
55
+ private var recordingStartTime: Long = 0
56
+ private var pausedDuration: Long = 0
57
+ private var lastPauseTime: Long = 0
58
+ private var lastWaveformUpdate: Long = 0
59
+ private val waveformRenderer = WaveformRenderer()
60
+ private var lastNotificationHash: Int? = null
61
+
62
+ fun initialize(config: RecordingConfig) {
63
+ recordingConfig = config
64
+ createNotificationChannel()
65
+ initializeNotification()
66
+ }
67
+
68
+ private fun createNotificationChannel() {
69
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
70
+ val channel = NotificationChannel(
71
+ recordingConfig.notification.channelId,
72
+ recordingConfig.notification.channelName,
73
+ NotificationManager.IMPORTANCE_LOW
74
+ ).apply {
75
+ description = recordingConfig.notification.channelDescription
76
+ enableLights(false)
77
+ enableVibration(false)
78
+ setSound(null, null)
79
+ setShowBadge(false)
80
+ vibrationPattern = null
81
+ }
82
+
83
+ notificationManager.createNotificationChannel(channel)
84
+ }
85
+ }
86
+
87
+ private fun initializeNotification() {
88
+ val context = contextRef.get() ?: return
89
+ try {
90
+ remoteViews = RemoteViews(context.packageName, R.layout.notification_recording)
91
+ remoteViews.apply {
92
+ setTextViewText(R.id.notification_title, recordingConfig.notification?.title)
93
+ setTextViewText(R.id.notification_text, recordingConfig.notification?.text)
94
+ setTextViewText(R.id.notification_duration, formatDuration(0))
95
+ setViewVisibility(
96
+ R.id.notification_waveform,
97
+ if (recordingConfig.showWaveformInNotification &&
98
+ recordingConfig.notification?.waveform != null) View.VISIBLE else View.GONE
99
+ )
100
+ }
101
+
102
+ buildNotification(context)
103
+ } catch (e: Exception) {
104
+ LogUtils.e(CLASS_NAME, "Failed to initialize notification", e)
105
+ }
106
+ }
107
+
108
+ private fun buildNotification(context: Context) {
109
+ val iconResId = recordingConfig.notification?.icon?.let {
110
+ getResourceIdByName(it)
111
+ } ?: R.drawable.ic_microphone
112
+
113
+ val pendingIntent = PendingIntent.getActivity(
114
+ context,
115
+ 0,
116
+ context.packageManager.getLaunchIntentForPackage(context.packageName),
117
+ PendingIntent.FLAG_IMMUTABLE
118
+ )
119
+
120
+ // Configure notification builder with settings optimized for recording service
121
+ // and wearable device compatibility
122
+ notificationBuilder = NotificationCompat.Builder(context, recordingConfig.notification.channelId)
123
+ .setSmallIcon(iconResId)
124
+ .setContentIntent(pendingIntent)
125
+ .setOngoing(true) // Notification cannot be dismissed by user
126
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
127
+ .setCategory(NotificationCompat.CATEGORY_SERVICE)
128
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
129
+ .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
130
+ .setCustomContentView(remoteViews)
131
+ .setCustomBigContentView(remoteViews)
132
+ .setStyle(NotificationCompat.DecoratedCustomViewStyle())
133
+ // Prevent repeated alerts and vibrations
134
+ .setOnlyAlertOnce(true) // Only alert on first notification
135
+ .setVibrate(null) // Disable vibration
136
+ .setDefaults(0) // Clear all default notification behaviors
137
+ .setLocalOnly(true) // Prevent notification from appearing on wearable devices
138
+
139
+ addNotificationActions(context)
140
+ }
141
+
142
+ private fun addNotificationActions(context: Context) {
143
+ // Clear existing actions first
144
+ notificationBuilder.clearActions()
145
+
146
+ // Create pause action
147
+ val pauseIntent = Intent(context, RecordingActionReceiver::class.java).apply {
148
+ action = RecordingActionReceiver.ACTION_PAUSE_RECORDING
149
+ }
150
+ val pausePendingIntent = PendingIntent.getBroadcast(
151
+ context,
152
+ 0,
153
+ pauseIntent,
154
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
155
+ )
156
+
157
+ // Create resume action
158
+ val resumeIntent = Intent(context, RecordingActionReceiver::class.java).apply {
159
+ action = RecordingActionReceiver.ACTION_RESUME_RECORDING
160
+ }
161
+ val resumePendingIntent = PendingIntent.getBroadcast(
162
+ context,
163
+ 1,
164
+ resumeIntent,
165
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
166
+ )
167
+
168
+ // Add only one pause/resume action based on current state (if enabled)
169
+ if (recordingConfig.notification.showPauseResumeActions) {
170
+ if (isPaused.get()) {
171
+ notificationBuilder.addAction(
172
+ R.drawable.ic_play,
173
+ "Resume",
174
+ resumePendingIntent
175
+ )
176
+ } else {
177
+ notificationBuilder.addAction(
178
+ R.drawable.ic_pause,
179
+ "Pause",
180
+ pausePendingIntent
181
+ )
182
+ }
183
+ }
184
+
185
+ // Add configured custom actions (only if they don't already exist)
186
+ val existingActions = mutableSetOf<String>()
187
+ recordingConfig.notification.actions.forEach { action ->
188
+ if (existingActions.add(action.intentAction)) { // Only add if action is unique
189
+ val intent = Intent(context, RecordingActionReceiver::class.java).apply {
190
+ this.action = action.intentAction
191
+ }
192
+ val pendingIntent = PendingIntent.getBroadcast(
193
+ context,
194
+ action.intentAction.hashCode(),
195
+ intent,
196
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
197
+ )
198
+ val actionIconResId = action.icon?.let { getResourceIdByName(it) }
199
+ ?: R.drawable.ic_default_action_icon
200
+ notificationBuilder.addAction(actionIconResId, action.title, pendingIntent)
201
+ }
202
+ }
203
+ }
204
+
205
+ private fun updateNotificationActions() {
206
+ val context = contextRef.get() ?: return
207
+ try {
208
+ // Clear existing actions
209
+ notificationBuilder.clearActions()
210
+ // Add updated actions
211
+ addNotificationActions(context)
212
+
213
+ // Update the notification
214
+ val updatedNotification = notificationBuilder
215
+ .setCustomContentView(remoteViews)
216
+ .setCustomBigContentView(remoteViews)
217
+ .build()
218
+
219
+ notificationManager.notify(recordingConfig.notification.notificationId, updatedNotification)
220
+ } catch (e: Exception) {
221
+ LogUtils.e(CLASS_NAME, "Failed to update notification actions", e)
222
+ }
223
+ }
224
+
225
+ fun startUpdates(startTime: Long) {
226
+ recordingStartTime = startTime
227
+ pausedDuration = 0
228
+ isPaused.set(false)
229
+ updateNotificationActions() // Update actions when starting
230
+ if (!isUpdating.getAndSet(true)) {
231
+ scheduleUpdate()
232
+ }
233
+ }
234
+
235
+ private fun scheduleUpdate() {
236
+ mainHandler.postDelayed({
237
+ if (isUpdating.get() && !isPaused.get()) {
238
+ updateNotification()
239
+ scheduleUpdate()
240
+ }
241
+ }, UPDATE_INTERVAL)
242
+ }
243
+
244
+ fun updateNotification(audioData: FloatArray? = null) {
245
+ val context = contextRef.get() ?: return
246
+
247
+ try {
248
+ val currentTime = SystemClock.elapsedRealtime()
249
+
250
+ // Calculate current notification state
251
+ val recordingDuration = if (isPaused.get()) {
252
+ lastPauseTime - recordingStartTime - pausedDuration
253
+ } else {
254
+ System.currentTimeMillis() - recordingStartTime - pausedDuration
255
+ }
256
+
257
+ // Create a hash of the current notification state
258
+ val currentHash = Objects.hash(
259
+ recordingConfig.notification.title,
260
+ recordingConfig.notification.text,
261
+ formatDuration(recordingDuration),
262
+ isPaused.get()
263
+ )
264
+
265
+ val needsRemoteViewsRefresh = currentTime - lastRemoteViewsUpdate >= remoteViewsRefreshInterval ||
266
+ consecutiveUpdateFailures >= maxUpdateFailures
267
+
268
+ // Only update if content changed or refresh needed
269
+ if (currentHash == lastNotificationHash && !needsRemoteViewsRefresh) {
270
+ // Update waveform only if needed
271
+ if (shouldUpdateWaveform(audioData, currentTime)) {
272
+ updateWaveformOnly(audioData)
273
+ }
274
+ return
275
+ }
276
+
277
+ lastNotificationHash = currentHash
278
+
279
+ // Only recreate RemoteViews periodically or after failures
280
+ if (needsRemoteViewsRefresh) {
281
+ remoteViews = RemoteViews(context.packageName, R.layout.notification_recording)
282
+ lastRemoteViewsUpdate = currentTime
283
+ consecutiveUpdateFailures = 0
284
+ }
285
+
286
+ // Update RemoteViews content
287
+ remoteViews.apply {
288
+ setTextViewText(R.id.notification_title, recordingConfig.notification.title)
289
+ setTextViewText(R.id.notification_text, recordingConfig.notification.text)
290
+ setTextViewText(R.id.notification_duration, formatDuration(recordingDuration))
291
+
292
+ // Update waveform if needed
293
+ if (recordingConfig.showWaveformInNotification &&
294
+ audioData != null &&
295
+ audioData.isNotEmpty() &&
296
+ currentTime - lastWaveformUpdate >= WAVEFORM_UPDATE_INTERVAL
297
+ ) {
298
+ try {
299
+ val waveformBitmap = waveformRenderer.generateWaveform(audioData, recordingConfig.notification.waveform)
300
+ setImageViewBitmap(R.id.notification_waveform, waveformBitmap)
301
+ lastWaveformUpdate = currentTime
302
+ } catch (e: Exception) {
303
+ LogUtils.e(CLASS_NAME, "Error generating waveform", e)
304
+ }
305
+ }
306
+ }
307
+
308
+ // Only rebuild notification if RemoteViews was refreshed
309
+ if (needsRemoteViewsRefresh) {
310
+ notificationBuilder
311
+ .setCustomContentView(remoteViews)
312
+ .setCustomBigContentView(remoteViews)
313
+ .setOnlyAlertOnce(true)
314
+ .setOngoing(true)
315
+ addNotificationActions(context)
316
+ }
317
+
318
+ // Update the notification with disabled alerts
319
+ notificationManager.notify(
320
+ recordingConfig.notification.notificationId,
321
+ notificationBuilder
322
+ .setOnlyAlertOnce(true)
323
+ .setVibrate(null)
324
+ .setDefaults(0)
325
+ .build()
326
+ )
327
+
328
+ lastSuccessfulUpdate = currentTime
329
+ consecutiveUpdateFailures = 0
330
+
331
+ } catch (e: Exception) {
332
+ LogUtils.e(CLASS_NAME, "Error updating notification", e)
333
+ consecutiveUpdateFailures++
334
+
335
+ if (consecutiveUpdateFailures >= maxUpdateFailures) {
336
+ reinitializeNotification()
337
+ }
338
+ }
339
+ }
340
+
341
+ private fun shouldUpdateWaveform(audioData: FloatArray?, currentTime: Long): Boolean {
342
+ return recordingConfig.showWaveformInNotification &&
343
+ audioData != null &&
344
+ audioData.isNotEmpty() &&
345
+ currentTime - lastWaveformUpdate >= WAVEFORM_UPDATE_INTERVAL
346
+ }
347
+
348
+ private fun updateWaveformOnly(audioData: FloatArray?) {
349
+ if (audioData == null) return
350
+
351
+ try {
352
+ val waveformBitmap = waveformRenderer.generateWaveform(audioData, recordingConfig.notification.waveform)
353
+ remoteViews.setImageViewBitmap(R.id.notification_waveform, waveformBitmap)
354
+ lastWaveformUpdate = SystemClock.elapsedRealtime()
355
+
356
+ notificationManager.notify(
357
+ recordingConfig.notification.notificationId,
358
+ notificationBuilder
359
+ .setCustomContentView(remoteViews)
360
+ .setCustomBigContentView(remoteViews)
361
+ .setOnlyAlertOnce(true)
362
+ .setVibrate(null)
363
+ .setDefaults(0)
364
+ .build()
365
+ )
366
+ } catch (e: Exception) {
367
+ LogUtils.e(CLASS_NAME, "Error updating waveform", e)
368
+ }
369
+ }
370
+
371
+ private fun reinitializeNotification() {
372
+ try {
373
+ val context = contextRef.get() ?: return
374
+
375
+ // Force a RemoteViews refresh
376
+ remoteViews = RemoteViews(context.packageName, R.layout.notification_recording)
377
+ lastRemoteViewsUpdate = SystemClock.elapsedRealtime()
378
+
379
+ buildNotification(context)
380
+
381
+ notificationManager.notify(
382
+ recordingConfig.notification.notificationId,
383
+ notificationBuilder.build()
384
+ )
385
+
386
+ consecutiveUpdateFailures = 0
387
+ LogUtils.d(CLASS_NAME, "Successfully reinitialized notification")
388
+ } catch (e: Exception) {
389
+ LogUtils.e(CLASS_NAME, "Failed to reinitialize notification", e)
390
+ }
391
+ }
392
+
393
+ fun getNotification(): Notification = notificationBuilder.build()
394
+
395
+ fun pauseUpdates() {
396
+ isPaused.set(true)
397
+ lastPauseTime = System.currentTimeMillis()
398
+ updateNotificationActions() // Update actions when pausing
399
+ }
400
+
401
+ fun resumeUpdates() {
402
+ pausedDuration += System.currentTimeMillis() - lastPauseTime
403
+ isPaused.set(false)
404
+ updateNotificationActions() // Update actions when resuming
405
+ scheduleUpdate()
406
+ }
407
+
408
+ fun stopUpdates() {
409
+ isUpdating.set(false)
410
+ mainHandler.removeCallbacksAndMessages(null)
411
+ notificationManager.cancel(recordingConfig.notification.notificationId)
412
+ cleanup()
413
+ }
414
+
415
+ private fun cleanup() {
416
+ recordingStartTime = 0
417
+ pausedDuration = 0
418
+ lastPauseTime = 0
419
+ lastWaveformUpdate = 0
420
+ lastSuccessfulUpdate = 0
421
+ lastRemoteViewsUpdate = 0
422
+ consecutiveUpdateFailures = 0
423
+ isPaused.set(false)
424
+ isUpdating.set(false)
425
+ }
426
+
427
+ private fun getResourceIdByName(resourceName: String): Int {
428
+ val context = contextRef.get() ?: return R.drawable.ic_default_action_icon
429
+ return context.resources.getIdentifier(resourceName, "drawable", context.packageName)
430
+ .takeIf { it != 0 } ?: R.drawable.ic_default_action_icon
431
+ }
432
+
433
+ private fun formatDuration(durationMs: Long): String {
434
+ val totalSeconds = durationMs / 1000
435
+ val minutes = totalSeconds / 60
436
+ val seconds = totalSeconds % 60
437
+ return String.format(Locale.US, "%02d:%02d", minutes, seconds)
438
+ }
439
+ }