@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.
- package/CHANGELOG.md +535 -0
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/android/build.gradle +143 -0
- package/android/src/androidTest/assets/chorus.wav +0 -0
- package/android/src/androidTest/assets/jfk.wav +0 -0
- package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
- package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/AudioProcessorInstrumentedTest.kt +197 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderInstrumentedTest.kt +541 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/AudioRecorderPerformanceInstrumentedTest.kt +234 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/AudioFocusStrategyIntegrationTest.kt +332 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/BufferDurationIntegrationTest.kt +324 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/CompressedOnlyOutputTest.kt +253 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/DeviceDisconnectionFallbackTest.kt +218 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/EventEmissionIntervalTest.kt +120 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/M4aFormatTest.kt +345 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/OutputControlIntegrationTest.kt +340 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/PcmStreamingDurationTest.kt +252 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/README.md +95 -0
- package/android/src/androidTest/java/net/siteed/audiostudio/integration/run_integration_tests.sh +43 -0
- package/android/src/main/AndroidManifest.xml +30 -0
- package/android/src/main/CMakeLists.txt +29 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioAnalysisData.kt +188 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioDataEncoder.kt +9 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioDeviceManager.kt +1741 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioFeaturesNative.kt +26 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioFileHandler.kt +136 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioFormatUtils.kt +354 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioNotificationsManager.kt +439 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioProcessor.kt +2237 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioRecorderManager.kt +2163 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioRecordingService.kt +167 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +1112 -0
- package/android/src/main/java/net/siteed/audiostudio/AudioTrimmer.kt +1099 -0
- package/android/src/main/java/net/siteed/audiostudio/Constants.kt +37 -0
- package/android/src/main/java/net/siteed/audiostudio/EventSender.kt +7 -0
- package/android/src/main/java/net/siteed/audiostudio/FFT.kt +100 -0
- package/android/src/main/java/net/siteed/audiostudio/Features.kt +98 -0
- package/android/src/main/java/net/siteed/audiostudio/LogUtils.kt +93 -0
- package/android/src/main/java/net/siteed/audiostudio/MelSpectrogramNative.kt +36 -0
- package/android/src/main/java/net/siteed/audiostudio/NotificationConfig.kt +72 -0
- package/android/src/main/java/net/siteed/audiostudio/PermissionUtils.kt +68 -0
- package/android/src/main/java/net/siteed/audiostudio/RecordingActionReceiver.kt +59 -0
- package/android/src/main/java/net/siteed/audiostudio/RecordingConfig.kt +259 -0
- package/android/src/main/java/net/siteed/audiostudio/WaveformConfig.kt +19 -0
- package/android/src/main/java/net/siteed/audiostudio/WaveformRenderer.kt +159 -0
- package/android/src/main/jni/AudioFeaturesJNI.cpp +152 -0
- package/android/src/main/jni/MelSpectrogramJNI.cpp +165 -0
- package/android/src/main/res/drawable/ic_default_action_icon.xml +16 -0
- package/android/src/main/res/drawable/ic_microphone.xml +13 -0
- package/android/src/main/res/drawable/ic_pause.xml +10 -0
- package/android/src/main/res/drawable/ic_play.xml +10 -0
- package/android/src/main/res/drawable/ic_stop.xml +10 -0
- package/android/src/main/res/layout/notification_recording.xml +37 -0
- package/android/src/test/java/net/siteed/audiostudio/AudioFileHandlerTest.kt +279 -0
- package/android/src/test/java/net/siteed/audiostudio/AudioFocusStrategyTest.kt +249 -0
- package/android/src/test/java/net/siteed/audiostudio/AudioFormatTest.kt +151 -0
- package/android/src/test/java/net/siteed/audiostudio/AudioFormatUtilsTest.kt +273 -0
- package/android/src/test/java/net/siteed/audiostudio/DeviceDisconnectionFallbackUnitTest.kt +140 -0
- package/android/src/test/resources/chorus.wav +0 -0
- package/android/src/test/resources/generate_test_audio.py +94 -0
- package/android/src/test/resources/jfk.wav +0 -0
- package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
- package/android/src/test/resources/recorder_hello_world.wav +0 -0
- package/app.plugin.js +3 -0
- package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +4 -0
- package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
- package/build/cjs/AudioAnalysis/audioFeaturesWasm.js +164 -0
- package/build/cjs/AudioAnalysis/audioFeaturesWasm.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +213 -0
- package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractAudioData.js +21 -0
- package/build/cjs/AudioAnalysis/extractAudioData.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +90 -0
- package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractPreview.js +28 -0
- package/build/cjs/AudioAnalysis/extractPreview.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractWaveform.js +18 -0
- package/build/cjs/AudioAnalysis/extractWaveform.js.map +1 -0
- package/build/cjs/AudioAnalysis/melSpectrogramWasm.js +149 -0
- package/build/cjs/AudioAnalysis/melSpectrogramWasm.js.map +1 -0
- package/build/cjs/AudioDeviceManager.js +688 -0
- package/build/cjs/AudioDeviceManager.js.map +1 -0
- package/build/cjs/AudioRecorder.provider.js +78 -0
- package/build/cjs/AudioRecorder.provider.js.map +1 -0
- package/build/cjs/AudioStudio.native.js +8 -0
- package/build/cjs/AudioStudio.native.js.map +1 -0
- package/build/cjs/AudioStudio.types.js +11 -0
- package/build/cjs/AudioStudio.types.js.map +1 -0
- package/build/cjs/AudioStudio.web.js +708 -0
- package/build/cjs/AudioStudio.web.js.map +1 -0
- package/build/cjs/AudioStudioModule.js +718 -0
- package/build/cjs/AudioStudioModule.js.map +1 -0
- package/build/cjs/WebRecorder.web.js +865 -0
- package/build/cjs/WebRecorder.web.js.map +1 -0
- package/build/cjs/constants/platformLimitations.js +99 -0
- package/build/cjs/constants/platformLimitations.js.map +1 -0
- package/build/cjs/constants.js +20 -0
- package/build/cjs/constants.js.map +1 -0
- package/build/cjs/events.js +29 -0
- package/build/cjs/events.js.map +1 -0
- package/build/cjs/hooks/useAudioDevices.js +179 -0
- package/build/cjs/hooks/useAudioDevices.js.map +1 -0
- package/build/cjs/index.js +64 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/trimAudio.js +76 -0
- package/build/cjs/trimAudio.js.map +1 -0
- package/build/cjs/useAudioRecorder.js +535 -0
- package/build/cjs/useAudioRecorder.js.map +1 -0
- package/build/cjs/utils/BlobFix.js +502 -0
- package/build/cjs/utils/BlobFix.js.map +1 -0
- package/build/cjs/utils/audioProcessing.js +136 -0
- package/build/cjs/utils/audioProcessing.js.map +1 -0
- package/build/cjs/utils/cleanNativeOptions.js +22 -0
- package/build/cjs/utils/cleanNativeOptions.js.map +1 -0
- package/build/cjs/utils/concatenateBuffers.js +25 -0
- package/build/cjs/utils/concatenateBuffers.js.map +1 -0
- package/build/cjs/utils/convertPCMToFloat32.js +124 -0
- package/build/cjs/utils/convertPCMToFloat32.js.map +1 -0
- package/build/cjs/utils/crc32.js +52 -0
- package/build/cjs/utils/crc32.js.map +1 -0
- package/build/cjs/utils/encodingToBitDepth.js +17 -0
- package/build/cjs/utils/encodingToBitDepth.js.map +1 -0
- package/build/cjs/utils/getWavFileInfo.js +96 -0
- package/build/cjs/utils/getWavFileInfo.js.map +1 -0
- package/build/cjs/utils/writeWavHeader.js +88 -0
- package/build/cjs/utils/writeWavHeader.js.map +1 -0
- package/build/cjs/workers/InlineFeaturesExtractor.web.js +294 -0
- package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +1 -0
- package/build/cjs/workers/inlineAudioWebWorker.web.js +190 -0
- package/build/cjs/workers/inlineAudioWebWorker.web.js.map +1 -0
- package/build/cjs/workers/wasmGlueString.web.js +27 -0
- package/build/cjs/workers/wasmGlueString.web.js.map +1 -0
- package/build/esm/AudioAnalysis/AudioAnalysis.types.js +3 -0
- package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
- package/build/esm/AudioAnalysis/audioFeaturesWasm.js +126 -0
- package/build/esm/AudioAnalysis/audioFeaturesWasm.js.map +1 -0
- package/build/esm/AudioAnalysis/extractAudioAnalysis.js +205 -0
- package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
- package/build/esm/AudioAnalysis/extractAudioData.js +14 -0
- package/build/esm/AudioAnalysis/extractAudioData.js.map +1 -0
- package/build/esm/AudioAnalysis/extractMelSpectrogram.js +86 -0
- package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
- package/build/esm/AudioAnalysis/extractPreview.js +25 -0
- package/build/esm/AudioAnalysis/extractPreview.js.map +1 -0
- package/build/esm/AudioAnalysis/extractWaveform.js +11 -0
- package/build/esm/AudioAnalysis/extractWaveform.js.map +1 -0
- package/build/esm/AudioAnalysis/melSpectrogramWasm.js +111 -0
- package/build/esm/AudioAnalysis/melSpectrogramWasm.js.map +1 -0
- package/build/esm/AudioDeviceManager.js +681 -0
- package/build/esm/AudioDeviceManager.js.map +1 -0
- package/build/esm/AudioRecorder.provider.js +40 -0
- package/build/esm/AudioRecorder.provider.js.map +1 -0
- package/build/esm/AudioStudio.native.js +6 -0
- package/build/esm/AudioStudio.native.js.map +1 -0
- package/build/esm/AudioStudio.types.js +8 -0
- package/build/esm/AudioStudio.types.js.map +1 -0
- package/build/esm/AudioStudio.web.js +704 -0
- package/build/esm/AudioStudio.web.js.map +1 -0
- package/build/esm/AudioStudioModule.js +713 -0
- package/build/esm/AudioStudioModule.js.map +1 -0
- package/build/esm/WebRecorder.web.js +861 -0
- package/build/esm/WebRecorder.web.js.map +1 -0
- package/build/esm/constants/platformLimitations.js +90 -0
- package/build/esm/constants/platformLimitations.js.map +1 -0
- package/build/esm/constants.js +17 -0
- package/build/esm/constants.js.map +1 -0
- package/build/esm/events.js +21 -0
- package/build/esm/events.js.map +1 -0
- package/build/esm/hooks/useAudioDevices.js +176 -0
- package/build/esm/hooks/useAudioDevices.js.map +1 -0
- package/build/esm/index.js +23 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/trimAudio.js +69 -0
- package/build/esm/trimAudio.js.map +1 -0
- package/build/esm/useAudioRecorder.js +529 -0
- package/build/esm/useAudioRecorder.js.map +1 -0
- package/build/esm/utils/BlobFix.js +498 -0
- package/build/esm/utils/BlobFix.js.map +1 -0
- package/build/esm/utils/audioProcessing.js +133 -0
- package/build/esm/utils/audioProcessing.js.map +1 -0
- package/build/esm/utils/cleanNativeOptions.js +19 -0
- package/build/esm/utils/cleanNativeOptions.js.map +1 -0
- package/build/esm/utils/concatenateBuffers.js +21 -0
- package/build/esm/utils/concatenateBuffers.js.map +1 -0
- package/build/esm/utils/convertPCMToFloat32.js +120 -0
- package/build/esm/utils/convertPCMToFloat32.js.map +1 -0
- package/build/esm/utils/crc32.js +50 -0
- package/build/esm/utils/crc32.js.map +1 -0
- package/build/esm/utils/encodingToBitDepth.js +13 -0
- package/build/esm/utils/encodingToBitDepth.js.map +1 -0
- package/build/esm/utils/getWavFileInfo.js +92 -0
- package/build/esm/utils/getWavFileInfo.js.map +1 -0
- package/build/esm/utils/writeWavHeader.js +84 -0
- package/build/esm/utils/writeWavHeader.js.map +1 -0
- package/build/esm/workers/InlineFeaturesExtractor.web.js +291 -0
- package/build/esm/workers/InlineFeaturesExtractor.web.js.map +1 -0
- package/build/esm/workers/inlineAudioWebWorker.web.js +187 -0
- package/build/esm/workers/inlineAudioWebWorker.web.js.map +1 -0
- package/build/esm/workers/wasmGlueString.web.js +24 -0
- package/build/esm/workers/wasmGlueString.web.js.map +1 -0
- package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +198 -0
- package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
- package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts +24 -0
- package/build/types/AudioAnalysis/audioFeaturesWasm.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts +74 -0
- package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractAudioData.d.ts +3 -0
- package/build/types/AudioAnalysis/extractAudioData.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts +20 -0
- package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractPreview.d.ts +11 -0
- package/build/types/AudioAnalysis/extractPreview.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractWaveform.d.ts +8 -0
- package/build/types/AudioAnalysis/extractWaveform.d.ts.map +1 -0
- package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts +16 -0
- package/build/types/AudioAnalysis/melSpectrogramWasm.d.ts.map +1 -0
- package/build/types/AudioDeviceManager.d.ts +187 -0
- package/build/types/AudioDeviceManager.d.ts.map +1 -0
- package/build/types/AudioRecorder.provider.d.ts +11 -0
- package/build/types/AudioRecorder.provider.d.ts.map +1 -0
- package/build/types/AudioStudio.native.d.ts +3 -0
- package/build/types/AudioStudio.native.d.ts.map +1 -0
- package/build/types/AudioStudio.types.d.ts +760 -0
- package/build/types/AudioStudio.types.d.ts.map +1 -0
- package/build/types/AudioStudio.web.d.ts +96 -0
- package/build/types/AudioStudio.web.d.ts.map +1 -0
- package/build/types/AudioStudioModule.d.ts +3 -0
- package/build/types/AudioStudioModule.d.ts.map +1 -0
- package/build/types/WebRecorder.web.d.ts +208 -0
- package/build/types/WebRecorder.web.d.ts.map +1 -0
- package/build/types/constants/platformLimitations.d.ts +40 -0
- package/build/types/constants/platformLimitations.d.ts.map +1 -0
- package/build/types/constants.d.ts +14 -0
- package/build/types/constants.d.ts.map +1 -0
- package/build/types/events.d.ts +29 -0
- package/build/types/events.d.ts.map +1 -0
- package/build/types/hooks/useAudioDevices.d.ts +15 -0
- package/build/types/hooks/useAudioDevices.d.ts.map +1 -0
- package/build/types/index.d.ts +21 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/trimAudio.d.ts +25 -0
- package/build/types/trimAudio.d.ts.map +1 -0
- package/build/types/useAudioRecorder.d.ts +22 -0
- package/build/types/useAudioRecorder.d.ts.map +1 -0
- package/build/types/utils/BlobFix.d.ts +9 -0
- package/build/types/utils/BlobFix.d.ts.map +1 -0
- package/build/types/utils/audioProcessing.d.ts +24 -0
- package/build/types/utils/audioProcessing.d.ts.map +1 -0
- package/build/types/utils/cleanNativeOptions.d.ts +15 -0
- package/build/types/utils/cleanNativeOptions.d.ts.map +1 -0
- package/build/types/utils/concatenateBuffers.d.ts +8 -0
- package/build/types/utils/concatenateBuffers.d.ts.map +1 -0
- package/build/types/utils/convertPCMToFloat32.d.ts +13 -0
- package/build/types/utils/convertPCMToFloat32.d.ts.map +1 -0
- package/build/types/utils/crc32.d.ts +7 -0
- package/build/types/utils/crc32.d.ts.map +1 -0
- package/build/types/utils/encodingToBitDepth.d.ts +5 -0
- package/build/types/utils/encodingToBitDepth.d.ts.map +1 -0
- package/build/types/utils/getWavFileInfo.d.ts +26 -0
- package/build/types/utils/getWavFileInfo.d.ts.map +1 -0
- package/build/types/utils/writeWavHeader.d.ts +34 -0
- package/build/types/utils/writeWavHeader.d.ts.map +1 -0
- package/build/types/workers/InlineFeaturesExtractor.web.d.ts +2 -0
- package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
- package/build/types/workers/inlineAudioWebWorker.web.d.ts +2 -0
- package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
- package/build/types/workers/wasmGlueString.web.d.ts +2 -0
- package/build/types/workers/wasmGlueString.web.d.ts.map +1 -0
- package/cpp/AudioFeatures.cpp +274 -0
- package/cpp/AudioFeatures.h +85 -0
- package/cpp/AudioFeaturesBridge.cpp +146 -0
- package/cpp/AudioFeaturesBridge.h +47 -0
- package/cpp/MelSpectrogram.cpp +227 -0
- package/cpp/MelSpectrogram.h +82 -0
- package/cpp/MelSpectrogramBridge.cpp +112 -0
- package/cpp/MelSpectrogramBridge.h +33 -0
- package/cpp/kiss_fft/COPYING +11 -0
- package/cpp/kiss_fft/_kiss_fft_guts.h +167 -0
- package/cpp/kiss_fft/kiss_fft.c +424 -0
- package/cpp/kiss_fft/kiss_fft.h +160 -0
- package/cpp/kiss_fft/kiss_fft_log.h +36 -0
- package/cpp/kiss_fft/kiss_fftr.c +155 -0
- package/cpp/kiss_fft/kiss_fftr.h +54 -0
- package/expo-module.config.json +10 -0
- package/ios/AudioAnalysisData.swift +74 -0
- package/ios/AudioDeviceManager.swift +670 -0
- package/ios/AudioFeaturesWrapper.h +21 -0
- package/ios/AudioFeaturesWrapper.mm +63 -0
- package/ios/AudioNotificationManager.swift +154 -0
- package/ios/AudioProcessingHelpers.swift +797 -0
- package/ios/AudioProcessor.swift +1191 -0
- package/ios/AudioStreamError.swift +7 -0
- package/ios/AudioStreamManager.swift +2369 -0
- package/ios/AudioStreamManagerDelegate.swift +16 -0
- package/ios/AudioStudio.podspec +39 -0
- package/ios/AudioStudioModule.swift +1111 -0
- package/ios/AudioStudioTests/AudioFileHandlerTests.swift +338 -0
- package/ios/AudioStudioTests/AudioFormatUtilsTests.swift +331 -0
- package/ios/AudioStudioTests/AudioTestHelpers.swift +130 -0
- package/ios/AudioStudioTests/CompressedOnlyOutputTests.swift +294 -0
- package/ios/AudioStudioTests/EventEmissionIntervalTests.swift +105 -0
- package/ios/AudioStudioTests/Info.plist +22 -0
- package/ios/AudioStudioTests/README.md +39 -0
- package/ios/AudioStudioTests/SimpleAudioTest.swift +98 -0
- package/ios/AudioStudioTests/TestAudioGenerator.swift +75 -0
- package/ios/DataPoint.swift +54 -0
- package/ios/DecodingConfig.swift +59 -0
- package/ios/FFT.swift +62 -0
- package/ios/Features.swift +95 -0
- package/ios/ISSUE_IOS.md +68 -0
- package/ios/Logger.swift +39 -0
- package/ios/MelSpectrogramWrapper.h +30 -0
- package/ios/MelSpectrogramWrapper.mm +97 -0
- package/ios/NotificationExtension.swift +15 -0
- package/ios/RecordingResult.swift +22 -0
- package/ios/RecordingSettings.swift +311 -0
- package/ios/WaveformExtractor.swift +105 -0
- package/ios/tests/README.md +41 -0
- package/ios/tests/integration/buffer_and_fallback_test.swift +178 -0
- package/ios/tests/integration/buffer_duration_test.swift +185 -0
- package/ios/tests/integration/compressed_only_output_test.swift +271 -0
- package/ios/tests/integration/output_control_test.swift +322 -0
- package/ios/tests/integration/run_integration_tests.sh +37 -0
- package/ios/tests/opus_support_test_macos.swift +154 -0
- package/ios/tests/standalone/audio_processing_test.swift +144 -0
- package/ios/tests/standalone/audio_recording_test.swift +277 -0
- package/ios/tests/standalone/audio_streaming_test.swift +249 -0
- package/ios/tests/standalone/standalone_test.swift +144 -0
- package/package.json +146 -0
- package/plugin/build/index.cjs +194 -0
- package/plugin/build/index.d.cts +22 -0
- package/plugin/build/index.js +194 -0
- package/plugin/src/index.ts +285 -0
- package/plugin/tsconfig.json +10 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/prebuilt/wasm/mel-spectrogram.js +18 -0
- package/src/AudioAnalysis/AudioAnalysis.types.ts +226 -0
- package/src/AudioAnalysis/audio-features-wasm.d.ts +37 -0
- package/src/AudioAnalysis/audioFeaturesWasm.ts +200 -0
- package/src/AudioAnalysis/extractAudioAnalysis.ts +350 -0
- package/src/AudioAnalysis/extractAudioData.ts +17 -0
- package/src/AudioAnalysis/extractMelSpectrogram.ts +140 -0
- package/src/AudioAnalysis/extractPreview.ts +34 -0
- package/src/AudioAnalysis/extractWaveform.ts +22 -0
- package/src/AudioAnalysis/mel-spectrogram-wasm.d.ts +48 -0
- package/src/AudioAnalysis/melSpectrogramWasm.ts +179 -0
- package/src/AudioDeviceManager.ts +800 -0
- package/src/AudioRecorder.provider.tsx +57 -0
- package/src/AudioStudio.native.ts +6 -0
- package/src/AudioStudio.types.ts +899 -0
- package/src/AudioStudio.web.ts +911 -0
- package/src/AudioStudioModule.ts +984 -0
- package/src/WebRecorder.web.ts +1114 -0
- package/src/constants/platformLimitations.ts +118 -0
- package/src/constants.ts +21 -0
- package/src/events.ts +63 -0
- package/src/hooks/useAudioDevices.ts +213 -0
- package/src/index.ts +67 -0
- package/src/trimAudio.ts +94 -0
- package/src/types/crc-32.d.ts +9 -0
- package/src/useAudioRecorder.tsx +784 -0
- package/src/utils/BlobFix.ts +561 -0
- package/src/utils/audioProcessing.ts +205 -0
- package/src/utils/cleanNativeOptions.ts +18 -0
- package/src/utils/concatenateBuffers.ts +24 -0
- package/src/utils/convertPCMToFloat32.ts +170 -0
- package/src/utils/crc32.ts +59 -0
- package/src/utils/encodingToBitDepth.ts +18 -0
- package/src/utils/getWavFileInfo.ts +132 -0
- package/src/utils/writeWavHeader.ts +115 -0
- package/src/workers/InlineFeaturesExtractor.web.tsx +291 -0
- package/src/workers/inlineAudioWebWorker.web.tsx +186 -0
- package/src/workers/wasmGlueString.web.ts +23 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Constants used throughout the AudioStudio module
|
|
5
|
+
*/
|
|
6
|
+
object Constants {
|
|
7
|
+
// Event names
|
|
8
|
+
const val TAG = "AudioStudio"
|
|
9
|
+
const val AUDIO_EVENT_NAME = "AudioData"
|
|
10
|
+
const val AUDIO_ANALYSIS_EVENT_NAME = "AudioAnalysis"
|
|
11
|
+
const val RECORDING_INTERRUPTED_EVENT_NAME = "onRecordingInterrupted"
|
|
12
|
+
const val TRIM_PROGRESS_EVENT = "TrimProgress"
|
|
13
|
+
const val DEVICE_CHANGED_EVENT = "deviceChangedEvent"
|
|
14
|
+
|
|
15
|
+
// Audio constants
|
|
16
|
+
const val DEFAULT_SAMPLE_RATE = 16000 // Default sample rate for audio recording
|
|
17
|
+
const val DEFAULT_CHANNEL_CONFIG = 1 // Mono
|
|
18
|
+
const val DEFAULT_AUDIO_FORMAT = 16 // 16-bit PCM
|
|
19
|
+
const val DEFAULT_INTERVAL = 1000L
|
|
20
|
+
const val DEFAULT_INTERVAL_ANALYSIS = 500L
|
|
21
|
+
const val MIN_INTERVAL = 10L // Minimum interval in ms for emitting audio data
|
|
22
|
+
const val WAV_HEADER_SIZE = 44
|
|
23
|
+
const val RIFF_HEADER = 0x52494646 // "RIFF"
|
|
24
|
+
const val WAVE_HEADER = 0x57415645 // "WAVE"
|
|
25
|
+
const val FMT_CHUNK_ID = 0x666d7420 // "fmt "
|
|
26
|
+
const val DATA_CHUNK_ID = 0x64617461 // "data"
|
|
27
|
+
const val INFO_CHUNK_ID = 0x494E464F // "info"
|
|
28
|
+
|
|
29
|
+
// Device constants
|
|
30
|
+
const val DEVICE_TYPE_BUILTIN_MIC = "builtin_mic"
|
|
31
|
+
const val DEVICE_TYPE_BLUETOOTH = "bluetooth"
|
|
32
|
+
const val DEVICE_TYPE_USB = "usb"
|
|
33
|
+
const val DEVICE_TYPE_WIRED_HEADSET = "wired_headset"
|
|
34
|
+
const val DEVICE_TYPE_WIRED_HEADPHONES = "wired_headphones"
|
|
35
|
+
const val DEVICE_TYPE_SPEAKER = "speaker"
|
|
36
|
+
const val DEVICE_TYPE_UNKNOWN = "unknown"
|
|
37
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// packages/audio-studio/android/src/main/java/net/siteed/audiostudio/FFT.kt
|
|
2
|
+
package net.siteed.audiostudio
|
|
3
|
+
|
|
4
|
+
import kotlin.math.PI
|
|
5
|
+
import kotlin.math.cos
|
|
6
|
+
import kotlin.math.sin
|
|
7
|
+
import kotlin.math.sqrt
|
|
8
|
+
|
|
9
|
+
@Deprecated("Use MelSpectrogramNative C++ implementation instead")
|
|
10
|
+
class FFT(private val n: Int) {
|
|
11
|
+
private val cosTable = FloatArray(n / 2)
|
|
12
|
+
private val sinTable = FloatArray(n / 2)
|
|
13
|
+
private val hannWindow = FloatArray(n)
|
|
14
|
+
|
|
15
|
+
init {
|
|
16
|
+
// Precompute trig tables
|
|
17
|
+
for (i in 0 until n / 2) {
|
|
18
|
+
cosTable[i] = cos(2.0 * PI * i / n).toFloat()
|
|
19
|
+
sinTable[i] = sin(2.0 * PI * i / n).toFloat()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Precompute normalized Hann window to match vDSP
|
|
23
|
+
val normalizationFactor = sqrt(2.0f / n) // Match vDSP normalization
|
|
24
|
+
for (i in hannWindow.indices) {
|
|
25
|
+
hannWindow[i] = normalizationFactor * 0.5f * (1 - cos(2.0 * PI * i / (n - 1))).toFloat()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fun processSegment(segment: FloatArray): FloatArray {
|
|
30
|
+
// Pad or truncate input to match FFT length
|
|
31
|
+
val paddedSegment = if (segment.size < n) {
|
|
32
|
+
segment + FloatArray(n - segment.size)
|
|
33
|
+
} else {
|
|
34
|
+
segment.copyOf(n)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Apply normalized Hann window
|
|
38
|
+
for (i in paddedSegment.indices) {
|
|
39
|
+
paddedSegment[i] *= hannWindow[i]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Perform FFT
|
|
43
|
+
realForward(paddedSegment)
|
|
44
|
+
|
|
45
|
+
return paddedSegment
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fun realForward(data: FloatArray) {
|
|
49
|
+
realForwardRecursive(data)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private fun realForwardRecursive(data: FloatArray) {
|
|
53
|
+
val n = data.size
|
|
54
|
+
if (n <= 1) return
|
|
55
|
+
|
|
56
|
+
val even = FloatArray(n / 2)
|
|
57
|
+
val odd = FloatArray(n / 2)
|
|
58
|
+
|
|
59
|
+
for (i in 0 until n / 2) {
|
|
60
|
+
even[i] = data[2 * i]
|
|
61
|
+
odd[i] = data[2 * i + 1]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
realForwardRecursive(even)
|
|
65
|
+
realForwardRecursive(odd)
|
|
66
|
+
|
|
67
|
+
for (i in 0 until n / 2) {
|
|
68
|
+
val t = cosTable[i] * odd[i] - sinTable[i] * even[i]
|
|
69
|
+
val u = sinTable[i] * odd[i] + cosTable[i] * even[i]
|
|
70
|
+
data[i] = even[i] + t
|
|
71
|
+
data[i + n / 2] = even[i] - t
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
fun realInverse(powerSpectrum: FloatArray, output: FloatArray) {
|
|
76
|
+
// Copy power spectrum to complex format for inverse FFT
|
|
77
|
+
val complexData = FloatArray(n * 2)
|
|
78
|
+
for (i in 0 until n/2 + 1) {
|
|
79
|
+
complexData[2 * i] = powerSpectrum[i]
|
|
80
|
+
if (2 * i + 1 < complexData.size) {
|
|
81
|
+
complexData[2 * i + 1] = 0f
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Conjugate for inverse FFT
|
|
86
|
+
for (i in 0 until n) {
|
|
87
|
+
if (2 * i + 1 < complexData.size) {
|
|
88
|
+
complexData[2 * i + 1] = -complexData[2 * i + 1]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Perform forward FFT (which is inverse when input is conjugated)
|
|
93
|
+
realForward(complexData)
|
|
94
|
+
|
|
95
|
+
// Copy real part to output and conjugate again
|
|
96
|
+
for (i in 0 until n) {
|
|
97
|
+
output[i] = complexData[2 * i] / n
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import androidx.core.os.bundleOf
|
|
5
|
+
|
|
6
|
+
data class Features(
|
|
7
|
+
val energy: Float = 0f,
|
|
8
|
+
val mfcc: List<Float> = emptyList(),
|
|
9
|
+
val rms: Float = 0f,
|
|
10
|
+
val minAmplitude: Float = 0f,
|
|
11
|
+
val maxAmplitude: Float = 0f,
|
|
12
|
+
val zcr: Float = 0f,
|
|
13
|
+
val spectralCentroid: Float = 0f,
|
|
14
|
+
val spectralFlatness: Float = 0f,
|
|
15
|
+
val spectralRolloff: Float = 0f,
|
|
16
|
+
val spectralBandwidth: Float = 0f,
|
|
17
|
+
val tempo: Float = 0f,
|
|
18
|
+
val hnr: Float = 0f,
|
|
19
|
+
val melSpectrogram: List<Float> = emptyList(),
|
|
20
|
+
val chromagram: List<Float> = emptyList(),
|
|
21
|
+
val spectralContrast: List<Float> = emptyList(),
|
|
22
|
+
val tonnetz: List<Float> = emptyList(),
|
|
23
|
+
val pitch: Float = 0f,
|
|
24
|
+
val crc32: Long? = null
|
|
25
|
+
) {
|
|
26
|
+
fun toDictionary(): Map<String, Any> {
|
|
27
|
+
val baseMap = mapOf(
|
|
28
|
+
"energy" to energy,
|
|
29
|
+
"mfcc" to mfcc,
|
|
30
|
+
"rms" to rms,
|
|
31
|
+
"minAmplitude" to minAmplitude,
|
|
32
|
+
"maxAmplitude" to maxAmplitude,
|
|
33
|
+
"zcr" to zcr,
|
|
34
|
+
"spectralCentroid" to spectralCentroid,
|
|
35
|
+
"spectralFlatness" to spectralFlatness,
|
|
36
|
+
"spectralRolloff" to spectralRolloff,
|
|
37
|
+
"spectralBandwidth" to spectralBandwidth,
|
|
38
|
+
"tempo" to tempo,
|
|
39
|
+
"hnr" to hnr,
|
|
40
|
+
"melSpectrogram" to melSpectrogram,
|
|
41
|
+
"chromagram" to chromagram,
|
|
42
|
+
"spectralContrast" to spectralContrast,
|
|
43
|
+
"tonnetz" to tonnetz,
|
|
44
|
+
"pitch" to pitch,
|
|
45
|
+
"crc32" to (crc32 ?: 0)
|
|
46
|
+
)
|
|
47
|
+
return baseMap.filterValues { it != null }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fun toBundle(): Bundle {
|
|
51
|
+
return bundleOf(
|
|
52
|
+
"energy" to energy,
|
|
53
|
+
"mfcc" to mfcc,
|
|
54
|
+
"rms" to rms,
|
|
55
|
+
"minAmplitude" to minAmplitude,
|
|
56
|
+
"maxAmplitude" to maxAmplitude,
|
|
57
|
+
"zcr" to zcr,
|
|
58
|
+
"spectralCentroid" to spectralCentroid,
|
|
59
|
+
"spectralFlatness" to spectralFlatness,
|
|
60
|
+
"spectralRolloff" to spectralRolloff,
|
|
61
|
+
"spectralBandwidth" to spectralBandwidth,
|
|
62
|
+
"tempo" to tempo,
|
|
63
|
+
"hnr" to hnr,
|
|
64
|
+
"melSpectrogram" to melSpectrogram,
|
|
65
|
+
"chromagram" to chromagram,
|
|
66
|
+
"spectralContrast" to spectralContrast,
|
|
67
|
+
"tonnetz" to tonnetz,
|
|
68
|
+
"pitch" to pitch,
|
|
69
|
+
"crc32" to (crc32 ?: 0)
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
companion object {
|
|
74
|
+
fun parseFeatureOptions(options: Map<*, *>?): Map<String, Boolean> {
|
|
75
|
+
return options?.let { map ->
|
|
76
|
+
mapOf(
|
|
77
|
+
"energy" to (map["energy"] as? Boolean ?: false),
|
|
78
|
+
"mfcc" to (map["mfcc"] as? Boolean ?: false),
|
|
79
|
+
"rms" to (map["rms"] as? Boolean ?: false),
|
|
80
|
+
"zcr" to (map["zcr"] as? Boolean ?: false),
|
|
81
|
+
"dB" to (map["dB"] as? Boolean ?: false),
|
|
82
|
+
"spectralCentroid" to (map["spectralCentroid"] as? Boolean ?: false),
|
|
83
|
+
"spectralFlatness" to (map["spectralFlatness"] as? Boolean ?: false),
|
|
84
|
+
"spectralRolloff" to (map["spectralRolloff"] as? Boolean ?: false),
|
|
85
|
+
"spectralBandwidth" to (map["spectralBandwidth"] as? Boolean ?: false),
|
|
86
|
+
"chromagram" to (map["chromagram"] as? Boolean ?: false),
|
|
87
|
+
"tempo" to (map["tempo"] as? Boolean ?: false),
|
|
88
|
+
"hnr" to (map["hnr"] as? Boolean ?: false),
|
|
89
|
+
"melSpectrogram" to (map["melSpectrogram"] as? Boolean ?: false),
|
|
90
|
+
"spectralContrast" to (map["spectralContrast"] as? Boolean ?: false),
|
|
91
|
+
"tonnetz" to (map["tonnetz"] as? Boolean ?: false),
|
|
92
|
+
"pitch" to (map["pitch"] as? Boolean ?: false),
|
|
93
|
+
"crc32" to (map["crc32"] as? Boolean ?: false)
|
|
94
|
+
)
|
|
95
|
+
} ?: emptyMap()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility class for standardized logging across the AudioStudio library.
|
|
7
|
+
* Provides consistent logging format and tags for easier filtering in logcat.
|
|
8
|
+
*/
|
|
9
|
+
object LogUtils {
|
|
10
|
+
// Format: [AudioStudio:ClassName]
|
|
11
|
+
private const val TAG_PREFIX = "AudioStudio"
|
|
12
|
+
|
|
13
|
+
// Check if we're running in a test environment
|
|
14
|
+
private val isInTest: Boolean by lazy {
|
|
15
|
+
try {
|
|
16
|
+
Class.forName("org.junit.Test")
|
|
17
|
+
true
|
|
18
|
+
} catch (e: ClassNotFoundException) {
|
|
19
|
+
false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Logs a debug message with a consistent format.
|
|
25
|
+
*
|
|
26
|
+
* @param className The name of the class generating the log
|
|
27
|
+
* @param message The message to log
|
|
28
|
+
*/
|
|
29
|
+
fun d(className: String, message: String) {
|
|
30
|
+
if (isInTest) {
|
|
31
|
+
println("D/$TAG_PREFIX:$className: $message")
|
|
32
|
+
} else {
|
|
33
|
+
Log.d("$TAG_PREFIX:$className", message)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Logs an error message with a consistent format.
|
|
39
|
+
*
|
|
40
|
+
* @param className The name of the class generating the log
|
|
41
|
+
* @param message The message to log
|
|
42
|
+
* @param throwable Optional throwable to include in the log
|
|
43
|
+
*/
|
|
44
|
+
fun e(className: String, message: String, throwable: Throwable? = null) {
|
|
45
|
+
if (isInTest) {
|
|
46
|
+
println("E/$TAG_PREFIX:$className: $message")
|
|
47
|
+
throwable?.printStackTrace()
|
|
48
|
+
} else {
|
|
49
|
+
Log.e("$TAG_PREFIX:$className", message, throwable)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Logs a warning message with a consistent format.
|
|
55
|
+
*
|
|
56
|
+
* @param className The name of the class generating the log
|
|
57
|
+
* @param message The message to log
|
|
58
|
+
* @param throwable Optional throwable to include in the log
|
|
59
|
+
*/
|
|
60
|
+
fun w(className: String, message: String, throwable: Throwable? = null) {
|
|
61
|
+
if (isInTest) {
|
|
62
|
+
println("W/$TAG_PREFIX:$className: $message")
|
|
63
|
+
throwable?.printStackTrace()
|
|
64
|
+
} else {
|
|
65
|
+
Log.w("$TAG_PREFIX:$className", message, throwable)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Logs an info message with a consistent format.
|
|
71
|
+
*
|
|
72
|
+
* @param className The name of the class generating the log
|
|
73
|
+
* @param message The message to log
|
|
74
|
+
*/
|
|
75
|
+
fun i(className: String, message: String) {
|
|
76
|
+
if (isInTest) {
|
|
77
|
+
println("I/$TAG_PREFIX:$className: $message")
|
|
78
|
+
} else {
|
|
79
|
+
Log.i("$TAG_PREFIX:$className", message)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Creates a formatted tag for direct use with Android Log methods.
|
|
85
|
+
* Use this if you need to use the Android Log methods directly.
|
|
86
|
+
*
|
|
87
|
+
* @param className The name of the class generating the log
|
|
88
|
+
* @return A formatted tag string
|
|
89
|
+
*/
|
|
90
|
+
fun tag(className: String): String {
|
|
91
|
+
return "$TAG_PREFIX:$className"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
object MelSpectrogramNative {
|
|
4
|
+
init {
|
|
5
|
+
System.loadLibrary("audio-studio-cpp")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
external fun compute(
|
|
9
|
+
samples: FloatArray,
|
|
10
|
+
sampleRate: Int,
|
|
11
|
+
fftLength: Int,
|
|
12
|
+
windowSizeSamples: Int,
|
|
13
|
+
hopLengthSamples: Int,
|
|
14
|
+
nMels: Int,
|
|
15
|
+
fMin: Float,
|
|
16
|
+
fMax: Float,
|
|
17
|
+
windowType: Int,
|
|
18
|
+
logScale: Boolean,
|
|
19
|
+
normalize: Boolean
|
|
20
|
+
): Array<FloatArray>
|
|
21
|
+
|
|
22
|
+
external fun init(
|
|
23
|
+
sampleRate: Int,
|
|
24
|
+
fftLength: Int,
|
|
25
|
+
windowSizeSamples: Int,
|
|
26
|
+
hopLengthSamples: Int,
|
|
27
|
+
nMels: Int,
|
|
28
|
+
fMin: Float,
|
|
29
|
+
fMax: Float,
|
|
30
|
+
windowType: Int
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
external fun computeFrame(frame: FloatArray, melOutput: FloatArray): Boolean
|
|
34
|
+
|
|
35
|
+
external fun getNMels(): Int
|
|
36
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
data class NotificationConfig(
|
|
4
|
+
val title: String = "Recording...",
|
|
5
|
+
val text: String = "",
|
|
6
|
+
val icon: String? = null,
|
|
7
|
+
val channelId: String = "audio_recording_channel",
|
|
8
|
+
val notificationId: Int = 1,
|
|
9
|
+
val actions: List<NotificationAction> = emptyList(),
|
|
10
|
+
val channelName: String = "Audio Recording",
|
|
11
|
+
val channelDescription: String = "Shows audio recording status",
|
|
12
|
+
val waveform: WaveformConfig? = null,
|
|
13
|
+
val lightColor: String = "#FF0000",
|
|
14
|
+
val priority: String = "high",
|
|
15
|
+
val accentColor: String? = null,
|
|
16
|
+
val showPauseResumeActions: Boolean = true
|
|
17
|
+
) {
|
|
18
|
+
companion object {
|
|
19
|
+
fun fromMap(map: Map<String, Any?>?): NotificationConfig {
|
|
20
|
+
if (map == null) return NotificationConfig()
|
|
21
|
+
|
|
22
|
+
val androidMap = map["android"] as? Map<String, Any?> ?: emptyMap()
|
|
23
|
+
|
|
24
|
+
return NotificationConfig(
|
|
25
|
+
title = map["title"] as? String ?: "Recording...",
|
|
26
|
+
text = map["text"] as? String ?: "",
|
|
27
|
+
icon = map["icon"] as? String,
|
|
28
|
+
channelId = androidMap["channelId"] as? String ?: "audio_recording_channel",
|
|
29
|
+
notificationId = (androidMap["notificationId"] as? Number)?.toInt() ?: 1,
|
|
30
|
+
actions = parseNotificationActions(androidMap["actions"] as? List<Map<String, Any?>>),
|
|
31
|
+
channelName = androidMap["channelName"] as? String ?: "Audio Recording",
|
|
32
|
+
channelDescription = androidMap["channelDescription"] as? String ?: "Shows audio recording status",
|
|
33
|
+
waveform = parseWaveformConfig(androidMap["waveform"] as? Map<String, Any?>),
|
|
34
|
+
lightColor = androidMap["lightColor"] as? String ?: "#FF0000",
|
|
35
|
+
priority = androidMap["priority"] as? String ?: "high",
|
|
36
|
+
accentColor = androidMap["accentColor"] as? String,
|
|
37
|
+
showPauseResumeActions = androidMap["showPauseResumeActions"] as? Boolean ?: true
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private fun parseNotificationActions(actionsList: List<Map<String, Any?>>?): List<NotificationAction> {
|
|
42
|
+
return actionsList?.mapNotNull { actionMap ->
|
|
43
|
+
if (actionMap["title"] != null && actionMap["identifier"] != null) {
|
|
44
|
+
NotificationAction(
|
|
45
|
+
title = actionMap["title"] as String,
|
|
46
|
+
icon = actionMap["icon"] as? String,
|
|
47
|
+
intentAction = actionMap["identifier"] as String
|
|
48
|
+
)
|
|
49
|
+
} else null
|
|
50
|
+
} ?: emptyList()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private fun parseWaveformConfig(waveformMap: Map<String, Any?>?): WaveformConfig? {
|
|
54
|
+
if (waveformMap == null) return null
|
|
55
|
+
|
|
56
|
+
return WaveformConfig(
|
|
57
|
+
color = waveformMap["color"] as? String ?: "#FFFFFF",
|
|
58
|
+
opacity = (waveformMap["opacity"] as? Number)?.toFloat() ?: 1.0f,
|
|
59
|
+
strokeWidth = (waveformMap["strokeWidth"] as? Number)?.toFloat() ?: 1.5f,
|
|
60
|
+
style = waveformMap["style"] as? String ?: "stroke",
|
|
61
|
+
mirror = waveformMap["mirror"] as? Boolean ?: true,
|
|
62
|
+
height = (waveformMap["height"] as? Number)?.toInt() ?: 64
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
data class NotificationAction(
|
|
69
|
+
val title: String,
|
|
70
|
+
val icon: String? = null,
|
|
71
|
+
val intentAction: String
|
|
72
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.pm.PackageManager
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import androidx.core.content.ContextCompat
|
|
7
|
+
import android.Manifest
|
|
8
|
+
import android.util.Log
|
|
9
|
+
|
|
10
|
+
class PermissionUtils(private val context: Context) {
|
|
11
|
+
fun checkRecordingPermission(enableBackgroundAudio: Boolean = true): Boolean {
|
|
12
|
+
val hasRecordPermission = ContextCompat.checkSelfPermission(
|
|
13
|
+
context,
|
|
14
|
+
Manifest.permission.RECORD_AUDIO
|
|
15
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
16
|
+
|
|
17
|
+
Log.d(Constants.TAG, "RECORD_AUDIO permission: $hasRecordPermission")
|
|
18
|
+
|
|
19
|
+
// Check for foreground service permission on Android 14+ only if background audio is enabled
|
|
20
|
+
val hasForegroundService = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && enableBackgroundAudio) {
|
|
21
|
+
val result = ContextCompat.checkSelfPermission(
|
|
22
|
+
context,
|
|
23
|
+
Manifest.permission.FOREGROUND_SERVICE_MICROPHONE
|
|
24
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
25
|
+
Log.d(Constants.TAG, "FOREGROUND_SERVICE_MICROPHONE permission: $result (Android 14+, background audio enabled)")
|
|
26
|
+
result
|
|
27
|
+
} else {
|
|
28
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
29
|
+
Log.d(Constants.TAG, "FOREGROUND_SERVICE_MICROPHONE not required (background audio disabled)")
|
|
30
|
+
} else {
|
|
31
|
+
Log.d(Constants.TAG, "FOREGROUND_SERVICE_MICROPHONE not required (Android < 14)")
|
|
32
|
+
}
|
|
33
|
+
true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
val result = hasRecordPermission && hasForegroundService
|
|
37
|
+
Log.d(Constants.TAG, "Final recording permission result: $result")
|
|
38
|
+
return result
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Overload the original method for backward compatibility
|
|
42
|
+
fun checkRecordingPermission(): Boolean {
|
|
43
|
+
return checkRecordingPermission(true)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fun checkNotificationPermission(): Boolean {
|
|
47
|
+
val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
48
|
+
val hasPermission = ContextCompat.checkSelfPermission(
|
|
49
|
+
context,
|
|
50
|
+
Manifest.permission.POST_NOTIFICATIONS
|
|
51
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
52
|
+
Log.d(Constants.TAG, "POST_NOTIFICATIONS permission: $hasPermission (Android 13+)")
|
|
53
|
+
hasPermission
|
|
54
|
+
} else {
|
|
55
|
+
Log.d(Constants.TAG, "POST_NOTIFICATIONS not required (Android < 13)")
|
|
56
|
+
true
|
|
57
|
+
}
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fun checkPhoneStatePermission(): Boolean {
|
|
62
|
+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
63
|
+
context.checkSelfPermission(android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
|
|
64
|
+
} else {
|
|
65
|
+
true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
import android.content.BroadcastReceiver
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import expo.modules.kotlin.Promise
|
|
8
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
9
|
+
|
|
10
|
+
class RecordingActionReceiver : BroadcastReceiver() {
|
|
11
|
+
companion object {
|
|
12
|
+
const val ACTION_PAUSE_RECORDING = "net.siteed.audiostudio.PAUSE_RECORDING"
|
|
13
|
+
const val ACTION_RESUME_RECORDING = "net.siteed.audiostudio.RESUME_RECORDING"
|
|
14
|
+
private val isProcessingAction = AtomicBoolean(false)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
override fun onReceive(context: Context, intent: Intent) {
|
|
18
|
+
when (intent.action) {
|
|
19
|
+
ACTION_PAUSE_RECORDING, ACTION_RESUME_RECORDING -> handleRecordingAction(intent.action)
|
|
20
|
+
else -> Log.w("RecordingActionReceiver", "Unknown action: ${intent.action}")
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private fun handleRecordingAction(action: String?) {
|
|
25
|
+
if (!isProcessingAction.compareAndSet(false, true)) {
|
|
26
|
+
Log.d("RecordingActionReceiver", "Action already in progress, skipping")
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
val audioRecorderManager = AudioRecorderManager.getInstance()
|
|
32
|
+
if (audioRecorderManager == null) {
|
|
33
|
+
Log.e("RecordingActionReceiver", "AudioRecorderManager instance is null")
|
|
34
|
+
isProcessingAction.set(false)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
val notificationPromise = object : Promise {
|
|
39
|
+
override fun resolve(value: Any?) {
|
|
40
|
+
Log.d("RecordingActionReceiver", "$action completed successfully")
|
|
41
|
+
isProcessingAction.set(false)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun reject(code: String, message: String?, cause: Throwable?) {
|
|
45
|
+
Log.e("RecordingActionReceiver", "$action failed: $message", cause)
|
|
46
|
+
isProcessingAction.set(false)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
when (action) {
|
|
51
|
+
ACTION_PAUSE_RECORDING -> audioRecorderManager.pauseRecording(notificationPromise)
|
|
52
|
+
ACTION_RESUME_RECORDING -> audioRecorderManager.resumeRecording(notificationPromise)
|
|
53
|
+
}
|
|
54
|
+
} catch (e: Exception) {
|
|
55
|
+
Log.e("RecordingActionReceiver", "Error processing $action", e)
|
|
56
|
+
isProcessingAction.set(false)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|