@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,350 @@
|
|
|
1
|
+
// packages/audio-studio/src/AudioAnalysis/extractAudioAnalysis.ts
|
|
2
|
+
/**
|
|
3
|
+
* This module provides functions for extracting and analyzing audio data.
|
|
4
|
+
* - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.
|
|
5
|
+
* - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.
|
|
6
|
+
* - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.
|
|
7
|
+
*/
|
|
8
|
+
import { ConsoleLike } from '../AudioStudio.types'
|
|
9
|
+
import AudioStudioModule from '../AudioStudioModule'
|
|
10
|
+
import { isWeb } from '../constants'
|
|
11
|
+
import {
|
|
12
|
+
AudioAnalysis,
|
|
13
|
+
AudioFeaturesOptions,
|
|
14
|
+
DataPoint,
|
|
15
|
+
DecodingConfig,
|
|
16
|
+
} from './AudioAnalysis.types'
|
|
17
|
+
import { processAudioBuffer } from '../utils/audioProcessing'
|
|
18
|
+
import { cleanNativeOptions } from '../utils/cleanNativeOptions'
|
|
19
|
+
import { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'
|
|
20
|
+
import crc32 from '../utils/crc32'
|
|
21
|
+
import { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'
|
|
22
|
+
import { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'
|
|
23
|
+
import { wasmGlueJs } from '../workers/wasmGlueString.web'
|
|
24
|
+
|
|
25
|
+
function calculateCRC32ForDataPoint(data: Float32Array): number {
|
|
26
|
+
// Convert float array to byte array for CRC32
|
|
27
|
+
const byteArray = new Uint8Array(data.length * 4)
|
|
28
|
+
const dataView = new DataView(byteArray.buffer)
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < data.length; i++) {
|
|
31
|
+
dataView.setFloat32(i * 4, data[i], true)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return crc32.buf(byteArray)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ExtractWavAudioAnalysisProps {
|
|
38
|
+
fileUri?: string // should provide either fileUri or arrayBuffer
|
|
39
|
+
wavMetadata?: WavFileInfo
|
|
40
|
+
arrayBuffer?: ArrayBuffer
|
|
41
|
+
bitDepth?: number
|
|
42
|
+
durationMs?: number
|
|
43
|
+
sampleRate?: number
|
|
44
|
+
numberOfChannels?: number
|
|
45
|
+
position?: number // Optional number of bytes to skip. Default is 0
|
|
46
|
+
length?: number // Optional number of bytes to read.
|
|
47
|
+
segmentDurationMs?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.
|
|
48
|
+
features?: AudioFeaturesOptions
|
|
49
|
+
featuresExtratorUrl?: string
|
|
50
|
+
logger?: ConsoleLike
|
|
51
|
+
decodingOptions?: DecodingConfig
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Define base options interface with common properties
|
|
55
|
+
interface BaseExtractOptions {
|
|
56
|
+
fileUri?: string
|
|
57
|
+
arrayBuffer?: ArrayBuffer
|
|
58
|
+
/**
|
|
59
|
+
* Duration of each analysis segment in milliseconds. Defaults to 100ms if not specified.
|
|
60
|
+
*/
|
|
61
|
+
segmentDurationMs?: number
|
|
62
|
+
features?: AudioFeaturesOptions
|
|
63
|
+
decodingOptions?: DecodingConfig
|
|
64
|
+
logger?: ConsoleLike
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Time-based range options
|
|
68
|
+
interface TimeRangeOptions extends BaseExtractOptions {
|
|
69
|
+
startTimeMs?: number
|
|
70
|
+
endTimeMs?: number
|
|
71
|
+
position?: never
|
|
72
|
+
length?: never
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Byte-based range options
|
|
76
|
+
interface ByteRangeOptions extends BaseExtractOptions {
|
|
77
|
+
position?: number
|
|
78
|
+
length?: number
|
|
79
|
+
startTimeMs?: never
|
|
80
|
+
endTimeMs?: never
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Options for extracting audio analysis.
|
|
85
|
+
* - For time-based analysis, provide `startTimeMs` and `endTimeMs`.
|
|
86
|
+
* - For byte-based analysis, provide `position` and `length`.
|
|
87
|
+
* - Do not mix time and byte ranges.
|
|
88
|
+
*/
|
|
89
|
+
export type ExtractAudioAnalysisProps = TimeRangeOptions | ByteRangeOptions
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Extracts detailed audio analysis from the specified audio file or buffer.
|
|
93
|
+
* Supports either time-based or byte-based ranges for flexibility in analysis.
|
|
94
|
+
*
|
|
95
|
+
* @param props - The options for extraction, including file URI, ranges, and decoding settings.
|
|
96
|
+
* @returns A promise that resolves to the audio analysis data.
|
|
97
|
+
* @throws {Error} If both time and byte ranges are provided or if required parameters are missing.
|
|
98
|
+
*/
|
|
99
|
+
export async function extractAudioAnalysis(
|
|
100
|
+
props: ExtractAudioAnalysisProps
|
|
101
|
+
): Promise<AudioAnalysis> {
|
|
102
|
+
const {
|
|
103
|
+
fileUri,
|
|
104
|
+
arrayBuffer,
|
|
105
|
+
decodingOptions,
|
|
106
|
+
logger,
|
|
107
|
+
segmentDurationMs = 100,
|
|
108
|
+
features,
|
|
109
|
+
} = props
|
|
110
|
+
|
|
111
|
+
if (isWeb) {
|
|
112
|
+
try {
|
|
113
|
+
// Create AudioContext here
|
|
114
|
+
const audioContext = new (window.AudioContext ||
|
|
115
|
+
(window as any).webkitAudioContext)({
|
|
116
|
+
sampleRate: decodingOptions?.targetSampleRate ?? 16000,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const processedBuffer = await processAudioBuffer({
|
|
121
|
+
arrayBuffer,
|
|
122
|
+
fileUri,
|
|
123
|
+
targetSampleRate:
|
|
124
|
+
decodingOptions?.targetSampleRate ?? 16000,
|
|
125
|
+
targetChannels: decodingOptions?.targetChannels ?? 1,
|
|
126
|
+
normalizeAudio: decodingOptions?.normalizeAudio ?? false,
|
|
127
|
+
startTimeMs:
|
|
128
|
+
'startTimeMs' in props ? props.startTimeMs : undefined,
|
|
129
|
+
endTimeMs:
|
|
130
|
+
'endTimeMs' in props ? props.endTimeMs : undefined,
|
|
131
|
+
position: 'position' in props ? props.position : undefined,
|
|
132
|
+
length: 'length' in props ? props.length : undefined,
|
|
133
|
+
audioContext, // Pass the context we created
|
|
134
|
+
logger,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const channelData = processedBuffer.buffer.getChannelData(0)
|
|
138
|
+
|
|
139
|
+
// Create worker blob: WASM glue (defines createMelSpectrogramModule) + worker code
|
|
140
|
+
const blob = new Blob(
|
|
141
|
+
[wasmGlueJs, '\n', InlineFeaturesExtractor],
|
|
142
|
+
{ type: 'application/javascript' }
|
|
143
|
+
)
|
|
144
|
+
const workerUrl = URL.createObjectURL(blob)
|
|
145
|
+
const worker = new Worker(workerUrl)
|
|
146
|
+
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
worker.onmessage = (event) => {
|
|
149
|
+
if (event.data.error) {
|
|
150
|
+
reject(new Error(event.data.error))
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const result: AudioAnalysis = event.data.result
|
|
155
|
+
// Calculate CRC32 after worker completes if requested
|
|
156
|
+
if (features?.crc32) {
|
|
157
|
+
const samplesPerSegment = Math.floor(
|
|
158
|
+
(processedBuffer.sampleRate *
|
|
159
|
+
segmentDurationMs) /
|
|
160
|
+
1000
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
result.dataPoints = result.dataPoints.map(
|
|
164
|
+
(point: DataPoint, index: number) => {
|
|
165
|
+
const startSample =
|
|
166
|
+
index * samplesPerSegment
|
|
167
|
+
const segmentData = channelData.slice(
|
|
168
|
+
startSample,
|
|
169
|
+
startSample + samplesPerSegment
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
...point,
|
|
174
|
+
features: {
|
|
175
|
+
...point.features,
|
|
176
|
+
crc32: calculateCRC32ForDataPoint(
|
|
177
|
+
segmentData
|
|
178
|
+
),
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
URL.revokeObjectURL(workerUrl)
|
|
186
|
+
worker.terminate()
|
|
187
|
+
resolve(result)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
worker.onerror = (error) => {
|
|
191
|
+
URL.revokeObjectURL(workerUrl)
|
|
192
|
+
worker.terminate()
|
|
193
|
+
reject(error)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
worker.postMessage({
|
|
197
|
+
channelData,
|
|
198
|
+
sampleRate: processedBuffer.sampleRate,
|
|
199
|
+
segmentDurationMs,
|
|
200
|
+
bitDepth: decodingOptions?.targetBitDepth ?? 32,
|
|
201
|
+
numberOfChannels: processedBuffer.channels,
|
|
202
|
+
fullAudioDurationMs: processedBuffer.durationMs,
|
|
203
|
+
// enableLogging: !!logger,
|
|
204
|
+
features,
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
} finally {
|
|
208
|
+
await audioContext.close()
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
logger?.error('Failed to process audio:', error)
|
|
212
|
+
throw error
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
// Strip non-serializable fields — logger and arrayBuffer cause
|
|
216
|
+
// "Cannot convert '[object Object]' to a Kotlin type" on Android.
|
|
217
|
+
const {
|
|
218
|
+
logger: _logger,
|
|
219
|
+
arrayBuffer: _arrayBuffer,
|
|
220
|
+
...nativeOptions
|
|
221
|
+
} = props
|
|
222
|
+
// Clean undefined values to avoid Android Kotlin bridge crash
|
|
223
|
+
return await AudioStudioModule.extractAudioAnalysis(
|
|
224
|
+
cleanNativeOptions(nativeOptions)
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Analyzes WAV files without decoding, preserving original PCM values.
|
|
231
|
+
* Use this function when you need to ensure the analysis matches other software by avoiding any transformations.
|
|
232
|
+
*
|
|
233
|
+
* @param props - The options for WAV analysis, including file URI and range.
|
|
234
|
+
* @returns A promise that resolves to the audio analysis data.
|
|
235
|
+
*/
|
|
236
|
+
export const extractRawWavAnalysis = async ({
|
|
237
|
+
fileUri,
|
|
238
|
+
segmentDurationMs = 100, // Default to 100ms
|
|
239
|
+
arrayBuffer,
|
|
240
|
+
bitDepth,
|
|
241
|
+
durationMs,
|
|
242
|
+
sampleRate,
|
|
243
|
+
numberOfChannels,
|
|
244
|
+
features,
|
|
245
|
+
logger,
|
|
246
|
+
position = 0,
|
|
247
|
+
length,
|
|
248
|
+
}: ExtractWavAudioAnalysisProps): Promise<AudioAnalysis> => {
|
|
249
|
+
if (isWeb) {
|
|
250
|
+
if (!arrayBuffer && !fileUri) {
|
|
251
|
+
throw new Error('Either arrayBuffer or fileUri must be provided')
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!arrayBuffer) {
|
|
255
|
+
logger?.log(`fetching fileUri`, fileUri)
|
|
256
|
+
const response = await fetch(fileUri!)
|
|
257
|
+
|
|
258
|
+
if (!response.ok) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Failed to fetch fileUri: ${response.statusText}`
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
arrayBuffer = await response.arrayBuffer()
|
|
265
|
+
logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Create a new copy of the ArrayBuffer to avoid detachment issues
|
|
269
|
+
const bufferCopy = arrayBuffer.slice(0)
|
|
270
|
+
logger?.log(
|
|
271
|
+
`extractAudioAnalysis bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,
|
|
272
|
+
bufferCopy.slice(0, 100)
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
let actualBitDepth = bitDepth
|
|
276
|
+
if (!actualBitDepth) {
|
|
277
|
+
logger?.log(
|
|
278
|
+
`extractAudioAnalysis bitDepth not provided -- getting wav file info`
|
|
279
|
+
)
|
|
280
|
+
const fileInfo = await getWavFileInfo(bufferCopy)
|
|
281
|
+
actualBitDepth = fileInfo.bitDepth
|
|
282
|
+
}
|
|
283
|
+
logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)
|
|
284
|
+
|
|
285
|
+
const {
|
|
286
|
+
pcmValues: channelData,
|
|
287
|
+
min,
|
|
288
|
+
max,
|
|
289
|
+
} = await convertPCMToFloat32({
|
|
290
|
+
buffer: arrayBuffer,
|
|
291
|
+
bitDepth: actualBitDepth,
|
|
292
|
+
})
|
|
293
|
+
logger?.log(
|
|
294
|
+
`extractAudioAnalysis convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
// Apply position and length constraints to channelData if specified
|
|
298
|
+
const startIndex = position
|
|
299
|
+
const endIndex = length ? startIndex + length : channelData.length
|
|
300
|
+
const constrainedChannelData = channelData.slice(startIndex, endIndex)
|
|
301
|
+
|
|
302
|
+
return new Promise((resolve, reject) => {
|
|
303
|
+
const blob = new Blob([wasmGlueJs, '\n', InlineFeaturesExtractor], {
|
|
304
|
+
type: 'application/javascript',
|
|
305
|
+
})
|
|
306
|
+
const url = URL.createObjectURL(blob)
|
|
307
|
+
const worker = new Worker(url)
|
|
308
|
+
|
|
309
|
+
worker.onmessage = (event) => {
|
|
310
|
+
URL.revokeObjectURL(url)
|
|
311
|
+
worker.terminate()
|
|
312
|
+
resolve(event.data.result)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
worker.onerror = (error) => {
|
|
316
|
+
URL.revokeObjectURL(url)
|
|
317
|
+
worker.terminate()
|
|
318
|
+
reject(error)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
worker.postMessage({
|
|
322
|
+
command: 'process',
|
|
323
|
+
channelData: constrainedChannelData,
|
|
324
|
+
sampleRate,
|
|
325
|
+
segmentDurationMs,
|
|
326
|
+
logger,
|
|
327
|
+
bitDepth,
|
|
328
|
+
fullAudioDurationMs: durationMs,
|
|
329
|
+
numberOfChannels,
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
} else {
|
|
333
|
+
if (!fileUri) {
|
|
334
|
+
throw new Error('fileUri is required')
|
|
335
|
+
}
|
|
336
|
+
logger?.log(`extractAudioAnalysis`, {
|
|
337
|
+
fileUri,
|
|
338
|
+
segmentDurationMs,
|
|
339
|
+
})
|
|
340
|
+
const res = await AudioStudioModule.extractAudioAnalysis({
|
|
341
|
+
fileUri,
|
|
342
|
+
segmentDurationMs,
|
|
343
|
+
features,
|
|
344
|
+
position,
|
|
345
|
+
length,
|
|
346
|
+
})
|
|
347
|
+
logger?.log(`extractAudioAnalysis`, res)
|
|
348
|
+
return res
|
|
349
|
+
}
|
|
350
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ExtractAudioDataOptions } from '../AudioStudio.types'
|
|
2
|
+
import AudioStudioModule from '../AudioStudioModule'
|
|
3
|
+
import { isWeb } from '../constants'
|
|
4
|
+
import { cleanNativeOptions } from '../utils/cleanNativeOptions'
|
|
5
|
+
|
|
6
|
+
export const extractAudioData = async (props: ExtractAudioDataOptions) => {
|
|
7
|
+
if (isWeb) {
|
|
8
|
+
// Web implementation handles logger natively in AudioStudioModule.ts
|
|
9
|
+
return await AudioStudioModule.extractAudioData(props)
|
|
10
|
+
}
|
|
11
|
+
// Native: only pass serializable fields — logger causes crash on Android
|
|
12
|
+
const { logger: _logger, ...nativeOptions } = props
|
|
13
|
+
// Clean undefined values to avoid Android Kotlin bridge crash
|
|
14
|
+
return await AudioStudioModule.extractAudioData(
|
|
15
|
+
cleanNativeOptions(nativeOptions)
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @experimental This feature is experimental and currently only available on Android.
|
|
3
|
+
* The API may change in future versions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { AudioStudioModule } from '..'
|
|
7
|
+
import { isWeb } from '../constants'
|
|
8
|
+
import {
|
|
9
|
+
ExtractMelSpectrogramOptions,
|
|
10
|
+
MelSpectrogram,
|
|
11
|
+
} from './AudioAnalysis.types'
|
|
12
|
+
import { computeMelSpectrogramWasm } from './melSpectrogramWasm'
|
|
13
|
+
import {
|
|
14
|
+
processAudioBuffer,
|
|
15
|
+
ProcessedAudioData,
|
|
16
|
+
} from '../utils/audioProcessing'
|
|
17
|
+
import { cleanNativeOptions } from '../utils/cleanNativeOptions'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Maximum duration in milliseconds that extractMelSpectrogram will process in a single call.
|
|
21
|
+
* The C++ core requires the entire trimmed range as a contiguous float array in memory,
|
|
22
|
+
* so this bound prevents OOM on all platforms. Callers needing longer ranges can iterate
|
|
23
|
+
* in windows of this size using startTimeMs/endTimeMs.
|
|
24
|
+
*/
|
|
25
|
+
export const MAX_DURATION_MS = 30_000
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extracts a mel spectrogram from audio data
|
|
29
|
+
*
|
|
30
|
+
* @experimental This feature is experimental.
|
|
31
|
+
* Uses shared C++ implementation on all platforms (native on iOS/Android, WASM on web).
|
|
32
|
+
*/
|
|
33
|
+
export async function extractMelSpectrogram(
|
|
34
|
+
options: ExtractMelSpectrogramOptions
|
|
35
|
+
): Promise<MelSpectrogram> {
|
|
36
|
+
let {
|
|
37
|
+
fileUri,
|
|
38
|
+
arrayBuffer,
|
|
39
|
+
windowSizeMs,
|
|
40
|
+
hopLengthMs,
|
|
41
|
+
nMels,
|
|
42
|
+
fMin = 0,
|
|
43
|
+
fMax,
|
|
44
|
+
windowType = 'hann',
|
|
45
|
+
normalize = false,
|
|
46
|
+
logScale = true,
|
|
47
|
+
decodingOptions,
|
|
48
|
+
startTimeMs,
|
|
49
|
+
endTimeMs,
|
|
50
|
+
logger,
|
|
51
|
+
} = options
|
|
52
|
+
|
|
53
|
+
// Apply max duration guard
|
|
54
|
+
if (startTimeMs == null && endTimeMs == null) {
|
|
55
|
+
startTimeMs = 0
|
|
56
|
+
endTimeMs = MAX_DURATION_MS
|
|
57
|
+
logger?.warn?.(
|
|
58
|
+
`extractMelSpectrogram: no time range specified, defaulting to 0–${MAX_DURATION_MS}ms`
|
|
59
|
+
)
|
|
60
|
+
} else {
|
|
61
|
+
const start = startTimeMs ?? 0
|
|
62
|
+
const end = endTimeMs ?? start + MAX_DURATION_MS
|
|
63
|
+
if (end - start > MAX_DURATION_MS) {
|
|
64
|
+
endTimeMs = start + MAX_DURATION_MS
|
|
65
|
+
logger?.warn?.(
|
|
66
|
+
`extractMelSpectrogram: requested range ${end - start}ms exceeds max ${MAX_DURATION_MS}ms, clamping endTimeMs to ${endTimeMs}`
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (isWeb) {
|
|
72
|
+
// Create audio context
|
|
73
|
+
const audioContext = new (window.AudioContext ||
|
|
74
|
+
(window as any).webkitAudioContext)()
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// Process audio data using the existing utility
|
|
78
|
+
const processedAudio: ProcessedAudioData = await processAudioBuffer(
|
|
79
|
+
{
|
|
80
|
+
arrayBuffer,
|
|
81
|
+
fileUri,
|
|
82
|
+
targetSampleRate:
|
|
83
|
+
decodingOptions?.targetSampleRate || 16000,
|
|
84
|
+
targetChannels: decodingOptions?.targetChannels || 1,
|
|
85
|
+
normalizeAudio: decodingOptions?.normalizeAudio ?? false,
|
|
86
|
+
startTimeMs,
|
|
87
|
+
endTimeMs,
|
|
88
|
+
audioContext,
|
|
89
|
+
logger: options.logger,
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
// Calculate window and hop size in samples
|
|
94
|
+
const sampleRate = processedAudio.sampleRate
|
|
95
|
+
const windowSize = Math.floor((windowSizeMs * sampleRate) / 1000)
|
|
96
|
+
const hopLength = Math.floor((hopLengthMs * sampleRate) / 1000)
|
|
97
|
+
const maxFreq = fMax || sampleRate / 2
|
|
98
|
+
|
|
99
|
+
// Extract the mel spectrogram via WASM (same C++ as native)
|
|
100
|
+
const spectrogram = await computeMelSpectrogramWasm(
|
|
101
|
+
processedAudio.channelData,
|
|
102
|
+
sampleRate,
|
|
103
|
+
nMels,
|
|
104
|
+
windowSize,
|
|
105
|
+
hopLength,
|
|
106
|
+
fMin,
|
|
107
|
+
maxFreq,
|
|
108
|
+
windowType,
|
|
109
|
+
normalize,
|
|
110
|
+
logScale
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
const timeSteps = spectrogram.length
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
spectrogram,
|
|
117
|
+
sampleRate,
|
|
118
|
+
nMels,
|
|
119
|
+
timeSteps,
|
|
120
|
+
durationMs: processedAudio.durationMs,
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
logger?.error('Error extracting mel spectrogram:', error)
|
|
124
|
+
throw error
|
|
125
|
+
} finally {
|
|
126
|
+
// Close the audio context
|
|
127
|
+
await audioContext.close()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Strip logger/arrayBuffer (non-serializable) then clean undefined values
|
|
131
|
+
// to avoid Android "Cannot convert '[object Object]' to Kotlin type" crash
|
|
132
|
+
const {
|
|
133
|
+
logger: _logger,
|
|
134
|
+
arrayBuffer: _arrayBuffer,
|
|
135
|
+
...nativeOptions
|
|
136
|
+
} = options
|
|
137
|
+
return AudioStudioModule.extractMelSpectrogram(
|
|
138
|
+
cleanNativeOptions(nativeOptions)
|
|
139
|
+
)
|
|
140
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PreviewOptions, AudioAnalysis } from './AudioAnalysis.types'
|
|
2
|
+
import { extractAudioAnalysis } from './extractAudioAnalysis'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates a simplified preview of the audio waveform for quick visualization.
|
|
6
|
+
* Ideal for UI rendering with a specified number of points.
|
|
7
|
+
*
|
|
8
|
+
* @param options - The options for the preview, including file URI and time range.
|
|
9
|
+
* @returns A promise that resolves to the audio preview data.
|
|
10
|
+
*/
|
|
11
|
+
export async function extractPreview({
|
|
12
|
+
fileUri,
|
|
13
|
+
numberOfPoints = 100,
|
|
14
|
+
startTimeMs = 0,
|
|
15
|
+
endTimeMs = 30000, // First 30 seconds
|
|
16
|
+
decodingOptions,
|
|
17
|
+
logger,
|
|
18
|
+
}: PreviewOptions): Promise<AudioAnalysis> {
|
|
19
|
+
const durationMs = endTimeMs - startTimeMs
|
|
20
|
+
const segmentDurationMs = Math.floor(durationMs / numberOfPoints)
|
|
21
|
+
|
|
22
|
+
// Call extractAudioAnalysis with calculated parameters
|
|
23
|
+
const analysis = await extractAudioAnalysis({
|
|
24
|
+
fileUri,
|
|
25
|
+
startTimeMs,
|
|
26
|
+
endTimeMs,
|
|
27
|
+
logger,
|
|
28
|
+
segmentDurationMs,
|
|
29
|
+
decodingOptions,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Transform the result into AudioPreview format
|
|
33
|
+
return analysis
|
|
34
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import AudioStudioModule from '../AudioStudioModule'
|
|
2
|
+
|
|
3
|
+
export interface ExtractWaveformProps {
|
|
4
|
+
fileUri: string
|
|
5
|
+
numberOfSamples: number
|
|
6
|
+
offset?: number
|
|
7
|
+
length?: number
|
|
8
|
+
}
|
|
9
|
+
export const extractWaveform = async ({
|
|
10
|
+
fileUri,
|
|
11
|
+
numberOfSamples,
|
|
12
|
+
offset = 0,
|
|
13
|
+
length,
|
|
14
|
+
}: ExtractWaveformProps): Promise<unknown> => {
|
|
15
|
+
const res = await AudioStudioModule.extractAudioAnalysis({
|
|
16
|
+
fileUri,
|
|
17
|
+
numberOfSamples,
|
|
18
|
+
offset,
|
|
19
|
+
length,
|
|
20
|
+
})
|
|
21
|
+
return res
|
|
22
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Type declarations for the Emscripten-generated mel-spectrogram WASM module */
|
|
2
|
+
|
|
3
|
+
export interface MelSpectrogramWasmModule {
|
|
4
|
+
_mel_spectrogram_compute(
|
|
5
|
+
samples: number,
|
|
6
|
+
numSamples: number,
|
|
7
|
+
sampleRate: number,
|
|
8
|
+
fftLength: number,
|
|
9
|
+
windowSizeSamples: number,
|
|
10
|
+
hopLengthSamples: number,
|
|
11
|
+
nMels: number,
|
|
12
|
+
fMin: number,
|
|
13
|
+
fMax: number,
|
|
14
|
+
windowType: number,
|
|
15
|
+
logScale: number,
|
|
16
|
+
normalize: number
|
|
17
|
+
): number
|
|
18
|
+
|
|
19
|
+
_mel_spectrogram_free(resultPtr: number): void
|
|
20
|
+
|
|
21
|
+
_mel_spectrogram_init(
|
|
22
|
+
sampleRate: number,
|
|
23
|
+
fftLength: number,
|
|
24
|
+
windowSizeSamples: number,
|
|
25
|
+
hopLengthSamples: number,
|
|
26
|
+
nMels: number,
|
|
27
|
+
fMin: number,
|
|
28
|
+
fMax: number,
|
|
29
|
+
windowType: number
|
|
30
|
+
): void
|
|
31
|
+
|
|
32
|
+
_mel_spectrogram_compute_frame(
|
|
33
|
+
framePtr: number,
|
|
34
|
+
frameSize: number,
|
|
35
|
+
melOutputPtr: number
|
|
36
|
+
): number
|
|
37
|
+
|
|
38
|
+
_mel_spectrogram_get_n_mels(): number
|
|
39
|
+
|
|
40
|
+
_malloc(size: number): number
|
|
41
|
+
_free(ptr: number): void
|
|
42
|
+
|
|
43
|
+
HEAPF32: Float32Array
|
|
44
|
+
HEAPU8: Uint8Array
|
|
45
|
+
getValue(ptr: number, type: string): number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default function createMelSpectrogramModule(): Promise<MelSpectrogramWasmModule>
|