@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,165 @@
|
|
|
1
|
+
#include <jni.h>
|
|
2
|
+
#include <android/log.h>
|
|
3
|
+
#include "MelSpectrogram.h"
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <cmath>
|
|
6
|
+
#include <mutex>
|
|
7
|
+
|
|
8
|
+
#define LOG_TAG "MelSpectrogramJNI"
|
|
9
|
+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
|
10
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
11
|
+
|
|
12
|
+
// Cache processor across JNI calls — avoids rebuilding FFT plan,
|
|
13
|
+
// window, and filterbank when config is unchanged.
|
|
14
|
+
static std::unique_ptr<MelSpectrogramProcessor> cachedProcessor;
|
|
15
|
+
static std::mutex cachedMutex;
|
|
16
|
+
|
|
17
|
+
extern "C" JNIEXPORT jobjectArray JNICALL
|
|
18
|
+
Java_net_siteed_audiostudio_MelSpectrogramNative_compute(
|
|
19
|
+
JNIEnv* env, jobject /* thiz */,
|
|
20
|
+
jfloatArray jSamples, jint sampleRate, jint fftLength,
|
|
21
|
+
jint windowSizeSamples, jint hopLengthSamples,
|
|
22
|
+
jint nMels, jfloat fMin, jfloat fMax,
|
|
23
|
+
jint windowType, jboolean logScale, jboolean normalize)
|
|
24
|
+
{
|
|
25
|
+
jfloat* samples = env->GetFloatArrayElements(jSamples, nullptr);
|
|
26
|
+
if (!samples) {
|
|
27
|
+
LOGE("Failed to get float array elements");
|
|
28
|
+
return nullptr;
|
|
29
|
+
}
|
|
30
|
+
jint numSamples = env->GetArrayLength(jSamples);
|
|
31
|
+
|
|
32
|
+
LOGI("compute: numSamples=%d, sampleRate=%d, fftLength=%d, windowSize=%d, hop=%d, nMels=%d",
|
|
33
|
+
numSamples, sampleRate, fftLength, windowSizeSamples, hopLengthSamples, nMels);
|
|
34
|
+
|
|
35
|
+
MelSpectrogramConfig config;
|
|
36
|
+
config.sampleRate = sampleRate;
|
|
37
|
+
config.fftLength = fftLength;
|
|
38
|
+
config.windowSizeSamples = windowSizeSamples;
|
|
39
|
+
config.hopLengthSamples = hopLengthSamples;
|
|
40
|
+
config.nMels = nMels;
|
|
41
|
+
config.fMin = fMin;
|
|
42
|
+
config.fMax = fMax;
|
|
43
|
+
config.windowType = windowType;
|
|
44
|
+
config.logScale = logScale;
|
|
45
|
+
config.normalize = normalize;
|
|
46
|
+
|
|
47
|
+
// Reuse processor if config matches
|
|
48
|
+
std::lock_guard<std::mutex> lock(cachedMutex);
|
|
49
|
+
if (!cachedProcessor || !(cachedProcessor->config() == config)) {
|
|
50
|
+
cachedProcessor = std::make_unique<MelSpectrogramProcessor>(config);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
MelSpectrogramResult result = cachedProcessor->compute(samples, numSamples);
|
|
54
|
+
|
|
55
|
+
env->ReleaseFloatArrayElements(jSamples, samples, JNI_ABORT);
|
|
56
|
+
|
|
57
|
+
LOGI("compute done: timeSteps=%d, nMels=%d", result.timeSteps, result.nMels);
|
|
58
|
+
|
|
59
|
+
if (result.timeSteps <= 0) {
|
|
60
|
+
return nullptr;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create float[][] result
|
|
64
|
+
jclass floatArrayClass = env->FindClass("[F");
|
|
65
|
+
if (!floatArrayClass) {
|
|
66
|
+
LOGE("Failed to find float[] class");
|
|
67
|
+
return nullptr;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
jobjectArray jResult = env->NewObjectArray(result.timeSteps, floatArrayClass, nullptr);
|
|
71
|
+
if (!jResult) {
|
|
72
|
+
LOGE("Failed to allocate result array");
|
|
73
|
+
return nullptr;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (int i = 0; i < result.timeSteps; ++i) {
|
|
77
|
+
jfloatArray row = env->NewFloatArray(result.nMels);
|
|
78
|
+
if (!row) {
|
|
79
|
+
LOGE("Failed to allocate row %d", i);
|
|
80
|
+
return jResult; // Return partial result rather than leak
|
|
81
|
+
}
|
|
82
|
+
env->SetFloatArrayRegion(row, 0, result.nMels,
|
|
83
|
+
result.data.data() + i * result.nMels);
|
|
84
|
+
env->SetObjectArrayElement(jResult, i, row);
|
|
85
|
+
env->DeleteLocalRef(row);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return jResult;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
extern "C" JNIEXPORT void JNICALL
|
|
92
|
+
Java_net_siteed_audiostudio_MelSpectrogramNative_init(
|
|
93
|
+
JNIEnv* env, jobject /* thiz */,
|
|
94
|
+
jint sampleRate, jint fftLength, jint windowSizeSamples,
|
|
95
|
+
jint hopLengthSamples, jint nMels, jfloat fMin, jfloat fMax,
|
|
96
|
+
jint windowType)
|
|
97
|
+
{
|
|
98
|
+
MelSpectrogramConfig config;
|
|
99
|
+
config.sampleRate = sampleRate;
|
|
100
|
+
config.fftLength = fftLength;
|
|
101
|
+
config.windowSizeSamples = windowSizeSamples;
|
|
102
|
+
config.hopLengthSamples = hopLengthSamples;
|
|
103
|
+
config.nMels = nMels;
|
|
104
|
+
config.fMin = fMin;
|
|
105
|
+
config.fMax = fMax;
|
|
106
|
+
config.windowType = windowType;
|
|
107
|
+
config.logScale = false;
|
|
108
|
+
config.normalize = false;
|
|
109
|
+
|
|
110
|
+
std::lock_guard<std::mutex> lock(cachedMutex);
|
|
111
|
+
if (!cachedProcessor || !(cachedProcessor->config() == config)) {
|
|
112
|
+
cachedProcessor = std::make_unique<MelSpectrogramProcessor>(config);
|
|
113
|
+
}
|
|
114
|
+
LOGI("init: sampleRate=%d, fftLength=%d, nMels=%d", sampleRate, fftLength, nMels);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
118
|
+
Java_net_siteed_audiostudio_MelSpectrogramNative_computeFrame(
|
|
119
|
+
JNIEnv* env, jobject /* thiz */,
|
|
120
|
+
jfloatArray jFrame, jfloatArray jMelOutput)
|
|
121
|
+
{
|
|
122
|
+
std::lock_guard<std::mutex> lock(cachedMutex);
|
|
123
|
+
if (!cachedProcessor) {
|
|
124
|
+
LOGE("computeFrame: processor not initialized, call init() first");
|
|
125
|
+
return JNI_FALSE;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
jfloat* frame = env->GetFloatArrayElements(jFrame, nullptr);
|
|
129
|
+
if (!frame) {
|
|
130
|
+
LOGE("computeFrame: failed to get frame array");
|
|
131
|
+
return JNI_FALSE;
|
|
132
|
+
}
|
|
133
|
+
jint frameSize = env->GetArrayLength(jFrame);
|
|
134
|
+
|
|
135
|
+
jfloat* melOutput = env->GetFloatArrayElements(jMelOutput, nullptr);
|
|
136
|
+
if (!melOutput) {
|
|
137
|
+
env->ReleaseFloatArrayElements(jFrame, frame, JNI_ABORT);
|
|
138
|
+
LOGE("computeFrame: failed to get melOutput array");
|
|
139
|
+
return JNI_FALSE;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
cachedProcessor->computeFrame(frame, frameSize, melOutput);
|
|
143
|
+
|
|
144
|
+
// Always apply log scaling (log(max(1e-10, val))) regardless of config.logScale
|
|
145
|
+
const int nMels = cachedProcessor->config().nMels;
|
|
146
|
+
for (int i = 0; i < nMels; ++i) {
|
|
147
|
+
melOutput[i] = std::log(std::max(1e-10f, melOutput[i]));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
env->ReleaseFloatArrayElements(jFrame, frame, JNI_ABORT);
|
|
151
|
+
env->ReleaseFloatArrayElements(jMelOutput, melOutput, 0); // 0 = copy back
|
|
152
|
+
|
|
153
|
+
return JNI_TRUE;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
extern "C" JNIEXPORT jint JNICALL
|
|
157
|
+
Java_net_siteed_audiostudio_MelSpectrogramNative_getNMels(
|
|
158
|
+
JNIEnv* env, jobject /* thiz */)
|
|
159
|
+
{
|
|
160
|
+
std::lock_guard<std::mutex> lock(cachedMutex);
|
|
161
|
+
if (!cachedProcessor) {
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
return cachedProcessor->config().nMels;
|
|
165
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="24dp"
|
|
4
|
+
android:height="24dp"
|
|
5
|
+
android:viewportWidth="24"
|
|
6
|
+
android:viewportHeight="24">
|
|
7
|
+
<path
|
|
8
|
+
android:fillColor="#FFFFFF"
|
|
9
|
+
android:pathData="M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM12,20C7.58,20 4,16.42 4,12C4,7.58 7.58,4 12,4C16.42,4 20,7.58 20,12C20,16.42 16.42,20 12,20Z"/>
|
|
10
|
+
<path
|
|
11
|
+
android:fillColor="#FFFFFF"
|
|
12
|
+
android:pathData="M12,17L12,7"/>
|
|
13
|
+
<path
|
|
14
|
+
android:fillColor="#FFFFFF"
|
|
15
|
+
android:pathData="M7,12L17,12"/>
|
|
16
|
+
</vector>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="24dp"
|
|
4
|
+
android:height="24dp"
|
|
5
|
+
android:viewportWidth="24"
|
|
6
|
+
android:viewportHeight="24">
|
|
7
|
+
<path
|
|
8
|
+
android:fillColor="#FFFFFF"
|
|
9
|
+
android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14z"/>
|
|
10
|
+
<path
|
|
11
|
+
android:fillColor="#FFFFFF"
|
|
12
|
+
android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/>
|
|
13
|
+
</vector>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="24dp"
|
|
4
|
+
android:height="24dp"
|
|
5
|
+
android:viewportWidth="24"
|
|
6
|
+
android:viewportHeight="24">
|
|
7
|
+
<path
|
|
8
|
+
android:fillColor="#FFFFFF"
|
|
9
|
+
android:pathData="M6,4h4v16H6zM14,4h4v16h-4z"/>
|
|
10
|
+
</vector>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="24dp"
|
|
4
|
+
android:height="24dp"
|
|
5
|
+
android:viewportWidth="24"
|
|
6
|
+
android:viewportHeight="24">
|
|
7
|
+
<path
|
|
8
|
+
android:fillColor="#FFFFFF"
|
|
9
|
+
android:pathData="M8,5v14l11,-7z"/>
|
|
10
|
+
</vector>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="24dp"
|
|
4
|
+
android:height="24dp"
|
|
5
|
+
android:viewportWidth="24"
|
|
6
|
+
android:viewportHeight="24">
|
|
7
|
+
<path
|
|
8
|
+
android:fillColor="#FFFFFF"
|
|
9
|
+
android:pathData="M6,6h12v12H6z"/>
|
|
10
|
+
</vector>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
android:id="@+id/notification_layout"
|
|
3
|
+
android:layout_width="match_parent"
|
|
4
|
+
android:layout_height="wrap_content"
|
|
5
|
+
android:gravity="center"
|
|
6
|
+
android:orientation="vertical"
|
|
7
|
+
android:padding="8dp">
|
|
8
|
+
|
|
9
|
+
<TextView
|
|
10
|
+
android:id="@+id/notification_title"
|
|
11
|
+
android:layout_width="wrap_content"
|
|
12
|
+
android:layout_height="wrap_content"
|
|
13
|
+
android:layout_marginBottom="4dp"
|
|
14
|
+
android:textSize="16sp"
|
|
15
|
+
android:textStyle="bold" />
|
|
16
|
+
|
|
17
|
+
<TextView
|
|
18
|
+
android:id="@+id/notification_text"
|
|
19
|
+
android:layout_width="wrap_content"
|
|
20
|
+
android:layout_height="wrap_content"
|
|
21
|
+
android:layout_marginBottom="4dp"
|
|
22
|
+
android:textSize="14sp" />
|
|
23
|
+
|
|
24
|
+
<ImageView
|
|
25
|
+
android:id="@+id/notification_waveform"
|
|
26
|
+
android:layout_width="match_parent"
|
|
27
|
+
android:layout_height="64dp"
|
|
28
|
+
android:scaleType="fitCenter" />
|
|
29
|
+
|
|
30
|
+
<TextView
|
|
31
|
+
android:id="@+id/notification_duration"
|
|
32
|
+
android:layout_width="wrap_content"
|
|
33
|
+
android:layout_height="wrap_content"
|
|
34
|
+
android:layout_marginTop="4dp"
|
|
35
|
+
android:textSize="14sp" />
|
|
36
|
+
|
|
37
|
+
</LinearLayout>
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
import org.junit.Test
|
|
4
|
+
import org.junit.Assert.*
|
|
5
|
+
import org.junit.Before
|
|
6
|
+
import org.junit.After
|
|
7
|
+
import java.io.ByteArrayOutputStream
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.nio.ByteBuffer
|
|
10
|
+
import java.nio.ByteOrder
|
|
11
|
+
import kotlin.test.assertNotNull
|
|
12
|
+
|
|
13
|
+
class AudioFileHandlerTest {
|
|
14
|
+
private lateinit var tempDir: File
|
|
15
|
+
private lateinit var audioFileHandler: AudioFileHandler
|
|
16
|
+
|
|
17
|
+
@Before
|
|
18
|
+
fun setUp() {
|
|
19
|
+
// Create a temporary directory for testing
|
|
20
|
+
tempDir = File(System.getProperty("java.io.tmpdir"), "audio_test_${System.currentTimeMillis()}")
|
|
21
|
+
tempDir.mkdirs()
|
|
22
|
+
audioFileHandler = AudioFileHandler(tempDir)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@After
|
|
26
|
+
fun tearDown() {
|
|
27
|
+
// Clean up temporary directory
|
|
28
|
+
tempDir.deleteRecursively()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Test
|
|
32
|
+
fun testWriteWavHeader_writesCorrectHeaderFormat() {
|
|
33
|
+
// Given
|
|
34
|
+
val outputStream = ByteArrayOutputStream()
|
|
35
|
+
val sampleRate = 44100
|
|
36
|
+
val channels = 2
|
|
37
|
+
val bitDepth = 16
|
|
38
|
+
|
|
39
|
+
// When
|
|
40
|
+
audioFileHandler.writeWavHeader(outputStream, sampleRate, channels, bitDepth)
|
|
41
|
+
val header = outputStream.toByteArray()
|
|
42
|
+
|
|
43
|
+
// Then
|
|
44
|
+
assertEquals("WAV header should be 44 bytes", 44, header.size)
|
|
45
|
+
|
|
46
|
+
// Check RIFF header
|
|
47
|
+
assertEquals("Should start with RIFF", "RIFF", String(header.sliceArray(0..3)))
|
|
48
|
+
assertEquals("Should contain WAVE identifier", "WAVE", String(header.sliceArray(8..11)))
|
|
49
|
+
assertEquals("Should contain fmt chunk", "fmt ", String(header.sliceArray(12..15)))
|
|
50
|
+
assertEquals("Should contain data chunk", "data", String(header.sliceArray(36..39)))
|
|
51
|
+
|
|
52
|
+
// Check audio format (PCM = 1)
|
|
53
|
+
val audioFormat = ByteBuffer.wrap(header.sliceArray(20..21)).order(ByteOrder.LITTLE_ENDIAN).short
|
|
54
|
+
assertEquals("Audio format should be 1 (PCM)", 1, audioFormat.toInt())
|
|
55
|
+
|
|
56
|
+
// Check channels
|
|
57
|
+
val headerChannels = ByteBuffer.wrap(header.sliceArray(22..23)).order(ByteOrder.LITTLE_ENDIAN).short
|
|
58
|
+
assertEquals("Channels should match", channels, headerChannels.toInt())
|
|
59
|
+
|
|
60
|
+
// Check sample rate
|
|
61
|
+
val headerSampleRate = ByteBuffer.wrap(header.sliceArray(24..27)).order(ByteOrder.LITTLE_ENDIAN).int
|
|
62
|
+
assertEquals("Sample rate should match", sampleRate, headerSampleRate)
|
|
63
|
+
|
|
64
|
+
// Check bit depth
|
|
65
|
+
val headerBitDepth = ByteBuffer.wrap(header.sliceArray(34..35)).order(ByteOrder.LITTLE_ENDIAN).short
|
|
66
|
+
assertEquals("Bit depth should match", bitDepth, headerBitDepth.toInt())
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Test
|
|
70
|
+
fun testWriteWavHeader_calculatesCorrectByteRate() {
|
|
71
|
+
// Given
|
|
72
|
+
val outputStream = ByteArrayOutputStream()
|
|
73
|
+
val sampleRate = 48000
|
|
74
|
+
val channels = 1
|
|
75
|
+
val bitDepth = 24
|
|
76
|
+
val expectedByteRate = sampleRate * channels * bitDepth / 8
|
|
77
|
+
|
|
78
|
+
// When
|
|
79
|
+
audioFileHandler.writeWavHeader(outputStream, sampleRate, channels, bitDepth)
|
|
80
|
+
val header = outputStream.toByteArray()
|
|
81
|
+
|
|
82
|
+
// Then
|
|
83
|
+
val byteRate = ByteBuffer.wrap(header.sliceArray(28..31)).order(ByteOrder.LITTLE_ENDIAN).int
|
|
84
|
+
assertEquals("Byte rate should be correctly calculated", expectedByteRate, byteRate)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@Test
|
|
88
|
+
fun testCreateAudioFile_createsFileWithCorrectExtension() {
|
|
89
|
+
// Given
|
|
90
|
+
val extension = "wav"
|
|
91
|
+
|
|
92
|
+
// When
|
|
93
|
+
val file = audioFileHandler.createAudioFile(extension)
|
|
94
|
+
|
|
95
|
+
// Then
|
|
96
|
+
assertNotNull("File should not be null", file)
|
|
97
|
+
assertTrue("File should exist", file.exists())
|
|
98
|
+
assertTrue("File should have correct extension", file.name.endsWith(".$extension"))
|
|
99
|
+
assertTrue("File should have correct prefix", file.name.startsWith("recording_"))
|
|
100
|
+
|
|
101
|
+
// Clean up
|
|
102
|
+
file.delete()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@Test
|
|
106
|
+
fun testCreateAudioFile_createsUniqueFiles() {
|
|
107
|
+
// Given
|
|
108
|
+
val extension = "wav"
|
|
109
|
+
|
|
110
|
+
// When
|
|
111
|
+
val file1 = audioFileHandler.createAudioFile(extension)
|
|
112
|
+
Thread.sleep(10) // Small delay to ensure different timestamps
|
|
113
|
+
val file2 = audioFileHandler.createAudioFile(extension)
|
|
114
|
+
|
|
115
|
+
// Then
|
|
116
|
+
assertNotEquals("Files should have unique names", file1.name, file2.name)
|
|
117
|
+
assertTrue("First file should exist", file1.exists())
|
|
118
|
+
assertTrue("Second file should exist", file2.exists())
|
|
119
|
+
|
|
120
|
+
// Clean up
|
|
121
|
+
file1.delete()
|
|
122
|
+
file2.delete()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@Test
|
|
126
|
+
fun testDeleteFile_deletesExistingFile() {
|
|
127
|
+
// Given
|
|
128
|
+
val file = audioFileHandler.createAudioFile("wav")
|
|
129
|
+
assertTrue("File should exist before deletion", file.exists())
|
|
130
|
+
|
|
131
|
+
// When
|
|
132
|
+
val result = audioFileHandler.deleteFile(file)
|
|
133
|
+
|
|
134
|
+
// Then
|
|
135
|
+
assertTrue("Delete should return true", result)
|
|
136
|
+
assertFalse("File should not exist after deletion", file.exists())
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@Test
|
|
140
|
+
fun testDeleteFile_handlesNullFile() {
|
|
141
|
+
// When
|
|
142
|
+
val result = audioFileHandler.deleteFile(null)
|
|
143
|
+
|
|
144
|
+
// Then
|
|
145
|
+
assertFalse("Delete should return false for null file", result)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Test
|
|
149
|
+
fun testDeleteFile_handlesNonExistentFile() {
|
|
150
|
+
// Given
|
|
151
|
+
val nonExistentFile = File(tempDir, "non_existent.wav")
|
|
152
|
+
assertFalse("File should not exist", nonExistentFile.exists())
|
|
153
|
+
|
|
154
|
+
// When
|
|
155
|
+
val result = audioFileHandler.deleteFile(nonExistentFile)
|
|
156
|
+
|
|
157
|
+
// Then
|
|
158
|
+
assertFalse("Delete should return false for non-existent file", result)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@Test
|
|
162
|
+
fun testClearAudioStorage_deletesAllFiles() {
|
|
163
|
+
// Given
|
|
164
|
+
val file1 = audioFileHandler.createAudioFile("wav")
|
|
165
|
+
val file2 = audioFileHandler.createAudioFile("mp3")
|
|
166
|
+
val file3 = audioFileHandler.createAudioFile("aac")
|
|
167
|
+
|
|
168
|
+
assertTrue("File 1 should exist", file1.exists())
|
|
169
|
+
assertTrue("File 2 should exist", file2.exists())
|
|
170
|
+
assertTrue("File 3 should exist", file3.exists())
|
|
171
|
+
|
|
172
|
+
// When
|
|
173
|
+
audioFileHandler.clearAudioStorage()
|
|
174
|
+
|
|
175
|
+
// Then
|
|
176
|
+
assertFalse("File 1 should be deleted", file1.exists())
|
|
177
|
+
assertFalse("File 2 should be deleted", file2.exists())
|
|
178
|
+
assertFalse("File 3 should be deleted", file3.exists())
|
|
179
|
+
assertEquals("Directory should be empty", 0, tempDir.listFiles()?.size ?: 0)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@Test
|
|
183
|
+
fun testUpdateWavHeader_updatesFileSizeCorrectly() {
|
|
184
|
+
// Given
|
|
185
|
+
val file = audioFileHandler.createAudioFile("wav")
|
|
186
|
+
val outputStream = file.outputStream()
|
|
187
|
+
|
|
188
|
+
// Write header
|
|
189
|
+
audioFileHandler.writeWavHeader(outputStream, 44100, 2, 16)
|
|
190
|
+
|
|
191
|
+
// Write some audio data (1 second of silence)
|
|
192
|
+
val audioDataSize = 44100 * 2 * 2 // sampleRate * channels * bytesPerSample
|
|
193
|
+
val audioData = ByteArray(audioDataSize)
|
|
194
|
+
outputStream.write(audioData)
|
|
195
|
+
outputStream.close()
|
|
196
|
+
|
|
197
|
+
// When
|
|
198
|
+
audioFileHandler.updateWavHeader(file)
|
|
199
|
+
|
|
200
|
+
// Then
|
|
201
|
+
val updatedHeader = file.inputStream().use { it.readNBytes(44) }
|
|
202
|
+
|
|
203
|
+
// Check file size field (bytes 4-7)
|
|
204
|
+
val fileSize = ByteBuffer.wrap(updatedHeader.sliceArray(4..7)).order(ByteOrder.LITTLE_ENDIAN).int
|
|
205
|
+
assertEquals("File size should be data size + 36", audioDataSize + 36, fileSize)
|
|
206
|
+
|
|
207
|
+
// Check data size field (bytes 40-43)
|
|
208
|
+
val dataSize = ByteBuffer.wrap(updatedHeader.sliceArray(40..43)).order(ByteOrder.LITTLE_ENDIAN).int
|
|
209
|
+
assertEquals("Data size should match audio data size", audioDataSize, dataSize)
|
|
210
|
+
|
|
211
|
+
// Clean up
|
|
212
|
+
file.delete()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@Test
|
|
216
|
+
fun testLoadRealWavFile_readsHeaderCorrectly() {
|
|
217
|
+
// Given - Load a real WAV file from test resources
|
|
218
|
+
val resourceStream = javaClass.classLoader?.getResourceAsStream("jfk.wav")
|
|
219
|
+
assertNotNull("Test resource jfk.wav should exist", resourceStream)
|
|
220
|
+
|
|
221
|
+
val testFile = File(tempDir, "test_jfk.wav")
|
|
222
|
+
resourceStream?.use { input ->
|
|
223
|
+
testFile.outputStream().use { output ->
|
|
224
|
+
input.copyTo(output)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// When - Read the WAV header
|
|
229
|
+
val header = testFile.inputStream().use { it.readNBytes(44) }
|
|
230
|
+
|
|
231
|
+
// Then - Validate header structure
|
|
232
|
+
assertEquals("Should start with RIFF", "RIFF", String(header.sliceArray(0..3)))
|
|
233
|
+
assertEquals("Should contain WAVE identifier", "WAVE", String(header.sliceArray(8..11)))
|
|
234
|
+
assertEquals("Should contain fmt chunk", "fmt ", String(header.sliceArray(12..15)))
|
|
235
|
+
|
|
236
|
+
// Extract audio properties
|
|
237
|
+
val channels = ByteBuffer.wrap(header.sliceArray(22..23)).order(ByteOrder.LITTLE_ENDIAN).short
|
|
238
|
+
val sampleRate = ByteBuffer.wrap(header.sliceArray(24..27)).order(ByteOrder.LITTLE_ENDIAN).int
|
|
239
|
+
val bitDepth = ByteBuffer.wrap(header.sliceArray(34..35)).order(ByteOrder.LITTLE_ENDIAN).short
|
|
240
|
+
|
|
241
|
+
// JFK.wav is known to be mono, 16kHz, 16-bit
|
|
242
|
+
assertEquals("JFK audio should be mono", 1, channels.toInt())
|
|
243
|
+
assertEquals("JFK audio should be 16kHz", 16000, sampleRate)
|
|
244
|
+
assertEquals("JFK audio should be 16-bit", 16, bitDepth.toInt())
|
|
245
|
+
|
|
246
|
+
// Clean up
|
|
247
|
+
testFile.delete()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@Test
|
|
251
|
+
fun testProcessMultipleRealWavFiles() {
|
|
252
|
+
// Test with different real WAV files to ensure compatibility
|
|
253
|
+
val testFiles = listOf("jfk.wav", "recorder_hello_world.wav", "osr_us_000_0010_8k.wav")
|
|
254
|
+
|
|
255
|
+
for (fileName in testFiles) {
|
|
256
|
+
val resourceStream = javaClass.classLoader?.getResourceAsStream(fileName)
|
|
257
|
+
if (resourceStream != null) {
|
|
258
|
+
val testFile = File(tempDir, "test_$fileName")
|
|
259
|
+
resourceStream.use { input ->
|
|
260
|
+
testFile.outputStream().use { output ->
|
|
261
|
+
input.copyTo(output)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Verify file was created and has content
|
|
266
|
+
assertTrue("$fileName should exist", testFile.exists())
|
|
267
|
+
assertTrue("$fileName should have more than just header", testFile.length() > 44)
|
|
268
|
+
|
|
269
|
+
// Read and validate header
|
|
270
|
+
val header = testFile.inputStream().use { it.readNBytes(44) }
|
|
271
|
+
assertEquals("$fileName should have RIFF header", "RIFF", String(header.sliceArray(0..3)))
|
|
272
|
+
assertEquals("$fileName should have WAVE format", "WAVE", String(header.sliceArray(8..11)))
|
|
273
|
+
|
|
274
|
+
// Clean up
|
|
275
|
+
testFile.delete()
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|