@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,324 @@
|
|
|
1
|
+
package net.siteed.audiostudio.integration
|
|
2
|
+
|
|
3
|
+
import android.media.AudioFormat
|
|
4
|
+
import android.media.AudioRecord
|
|
5
|
+
import android.media.MediaRecorder
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
8
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
|
9
|
+
import org.junit.After
|
|
10
|
+
import org.junit.Before
|
|
11
|
+
import org.junit.Test
|
|
12
|
+
import org.junit.runner.RunWith
|
|
13
|
+
import java.util.concurrent.CountDownLatch
|
|
14
|
+
import java.util.concurrent.TimeUnit
|
|
15
|
+
import kotlin.math.abs
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Integration test for Buffer Duration feature
|
|
19
|
+
* This tests the ACTUAL behavior of Android AudioRecord with different buffer sizes
|
|
20
|
+
*/
|
|
21
|
+
@RunWith(AndroidJUnit4::class)
|
|
22
|
+
class BufferDurationIntegrationTest {
|
|
23
|
+
|
|
24
|
+
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
25
|
+
private val results = mutableListOf<TestResult>()
|
|
26
|
+
private var audioRecord: AudioRecord? = null
|
|
27
|
+
|
|
28
|
+
data class TestResult(
|
|
29
|
+
val name: String,
|
|
30
|
+
val passed: Boolean,
|
|
31
|
+
val message: String
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
@Before
|
|
35
|
+
fun setup() {
|
|
36
|
+
println("🧪 Buffer Duration Integration Test")
|
|
37
|
+
println("===================================\n")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@After
|
|
41
|
+
fun tearDown() {
|
|
42
|
+
audioRecord?.release()
|
|
43
|
+
printResults()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Test
|
|
47
|
+
fun testDefaultBufferSize() {
|
|
48
|
+
println("Test 1: Default Buffer Size")
|
|
49
|
+
println("---------------------------")
|
|
50
|
+
|
|
51
|
+
val sampleRate = 48000
|
|
52
|
+
val channelConfig = AudioFormat.CHANNEL_IN_MONO
|
|
53
|
+
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
|
54
|
+
|
|
55
|
+
// Get minimum buffer size
|
|
56
|
+
val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
|
|
57
|
+
println("✓ Android minimum buffer size: $minBufferSize bytes")
|
|
58
|
+
|
|
59
|
+
// Calculate frames from bytes (16-bit = 2 bytes per sample)
|
|
60
|
+
val minFrames = minBufferSize / 2
|
|
61
|
+
println("✓ Minimum frames: $minFrames")
|
|
62
|
+
|
|
63
|
+
// Test with default 1024 frames (2048 bytes)
|
|
64
|
+
val requestedBytes = 1024 * 2
|
|
65
|
+
val actualBufferSize = if (requestedBytes < minBufferSize) minBufferSize else requestedBytes
|
|
66
|
+
|
|
67
|
+
audioRecord = AudioRecord(
|
|
68
|
+
MediaRecorder.AudioSource.MIC,
|
|
69
|
+
sampleRate,
|
|
70
|
+
channelConfig,
|
|
71
|
+
audioFormat,
|
|
72
|
+
actualBufferSize
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
val state = audioRecord?.state
|
|
76
|
+
val passed = state == AudioRecord.STATE_INITIALIZED
|
|
77
|
+
|
|
78
|
+
results.add(TestResult(
|
|
79
|
+
name = "Default Buffer Size",
|
|
80
|
+
passed = passed,
|
|
81
|
+
message = "Requested: 1024 frames, Min required: $minFrames frames, State: ${if (passed) "INITIALIZED" else "UNINITIALIZED"}"
|
|
82
|
+
))
|
|
83
|
+
|
|
84
|
+
println("✓ Requested: 1024 frames (${requestedBytes} bytes)")
|
|
85
|
+
println("✓ Actual buffer: ${actualBufferSize / 2} frames ($actualBufferSize bytes)")
|
|
86
|
+
println("✓ Initialization: ${if (passed) "SUCCESS" else "FAILED"}\n")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@Test
|
|
90
|
+
fun testCustomBufferSizes() {
|
|
91
|
+
println("Test 2: Custom Buffer Sizes")
|
|
92
|
+
println("---------------------------")
|
|
93
|
+
|
|
94
|
+
val sampleRate = 48000
|
|
95
|
+
val channelConfig = AudioFormat.CHANNEL_IN_MONO
|
|
96
|
+
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
|
97
|
+
val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
|
|
98
|
+
|
|
99
|
+
val testCases = listOf(
|
|
100
|
+
0.01 to "10ms",
|
|
101
|
+
0.05 to "50ms",
|
|
102
|
+
0.1 to "100ms",
|
|
103
|
+
0.2 to "200ms",
|
|
104
|
+
0.5 to "500ms"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
for ((duration, name) in testCases) {
|
|
108
|
+
val requestedFrames = (duration * sampleRate).toInt()
|
|
109
|
+
val requestedBytes = requestedFrames * 2 // 16-bit = 2 bytes
|
|
110
|
+
val actualBufferSize = if (requestedBytes < minBufferSize) minBufferSize else requestedBytes
|
|
111
|
+
|
|
112
|
+
audioRecord?.release()
|
|
113
|
+
audioRecord = AudioRecord(
|
|
114
|
+
MediaRecorder.AudioSource.MIC,
|
|
115
|
+
sampleRate,
|
|
116
|
+
channelConfig,
|
|
117
|
+
audioFormat,
|
|
118
|
+
actualBufferSize
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
val state = audioRecord?.state
|
|
122
|
+
val passed = state == AudioRecord.STATE_INITIALIZED
|
|
123
|
+
|
|
124
|
+
// Test actual read behavior
|
|
125
|
+
if (passed) {
|
|
126
|
+
audioRecord?.startRecording()
|
|
127
|
+
val buffer = ByteArray(requestedBytes)
|
|
128
|
+
val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: -1
|
|
129
|
+
audioRecord?.stop()
|
|
130
|
+
|
|
131
|
+
val framesRead = if (bytesRead > 0) bytesRead / 2 else 0
|
|
132
|
+
|
|
133
|
+
results.add(TestResult(
|
|
134
|
+
name = "Buffer $name",
|
|
135
|
+
passed = bytesRead > 0,
|
|
136
|
+
message = "Requested: $requestedFrames frames, Read: $framesRead frames"
|
|
137
|
+
))
|
|
138
|
+
|
|
139
|
+
println(" $name: Requested $requestedFrames → Read $framesRead frames")
|
|
140
|
+
} else {
|
|
141
|
+
results.add(TestResult(
|
|
142
|
+
name = "Buffer $name",
|
|
143
|
+
passed = false,
|
|
144
|
+
message = "Failed to initialize AudioRecord"
|
|
145
|
+
))
|
|
146
|
+
println(" $name: Failed to initialize")
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
println()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Test
|
|
153
|
+
fun testBufferSizeLimits() {
|
|
154
|
+
println("Test 3: Buffer Size Limits")
|
|
155
|
+
println("--------------------------")
|
|
156
|
+
|
|
157
|
+
val sampleRate = 48000
|
|
158
|
+
val channelConfig = AudioFormat.CHANNEL_IN_MONO
|
|
159
|
+
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
|
160
|
+
|
|
161
|
+
val extremeCases = listOf(
|
|
162
|
+
100 to "Very small (100 frames)",
|
|
163
|
+
50000 to "Very large (50000 frames)"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
for ((frames, name) in extremeCases) {
|
|
167
|
+
val requestedBytes = frames * 2
|
|
168
|
+
val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
|
|
169
|
+
val actualBufferSize = if (requestedBytes < minBufferSize) minBufferSize else requestedBytes
|
|
170
|
+
|
|
171
|
+
audioRecord?.release()
|
|
172
|
+
audioRecord = AudioRecord(
|
|
173
|
+
MediaRecorder.AudioSource.MIC,
|
|
174
|
+
sampleRate,
|
|
175
|
+
channelConfig,
|
|
176
|
+
audioFormat,
|
|
177
|
+
actualBufferSize
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
val state = audioRecord?.state
|
|
181
|
+
val passed = state == AudioRecord.STATE_INITIALIZED
|
|
182
|
+
|
|
183
|
+
results.add(TestResult(
|
|
184
|
+
name = name,
|
|
185
|
+
passed = passed,
|
|
186
|
+
message = "Requested: $frames frames, Buffer size: ${actualBufferSize / 2} frames"
|
|
187
|
+
))
|
|
188
|
+
|
|
189
|
+
println(" $name: $frames → ${actualBufferSize / 2} frames")
|
|
190
|
+
}
|
|
191
|
+
println()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@Test
|
|
195
|
+
fun testBufferAccumulation() {
|
|
196
|
+
println("Test 4: Buffer Accumulation for Small Durations")
|
|
197
|
+
println("-----------------------------------------------")
|
|
198
|
+
|
|
199
|
+
val sampleRate = 48000
|
|
200
|
+
val channelConfig = AudioFormat.CHANNEL_IN_MONO
|
|
201
|
+
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
|
202
|
+
val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
|
|
203
|
+
|
|
204
|
+
// Test very small buffer (20ms = 960 frames)
|
|
205
|
+
val targetDuration = 0.02 // 20ms
|
|
206
|
+
val targetFrames = (targetDuration * sampleRate).toInt()
|
|
207
|
+
val targetBytes = targetFrames * 2
|
|
208
|
+
|
|
209
|
+
audioRecord?.release()
|
|
210
|
+
audioRecord = AudioRecord(
|
|
211
|
+
MediaRecorder.AudioSource.MIC,
|
|
212
|
+
sampleRate,
|
|
213
|
+
channelConfig,
|
|
214
|
+
audioFormat,
|
|
215
|
+
minBufferSize // Use minimum buffer size
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if (audioRecord?.state == AudioRecord.STATE_INITIALIZED) {
|
|
219
|
+
audioRecord?.startRecording()
|
|
220
|
+
|
|
221
|
+
// Accumulate small chunks
|
|
222
|
+
val accumulator = mutableListOf<ByteArray>()
|
|
223
|
+
var totalFrames = 0
|
|
224
|
+
val smallBuffer = ByteArray(targetBytes)
|
|
225
|
+
|
|
226
|
+
// Read multiple times to accumulate
|
|
227
|
+
repeat(5) {
|
|
228
|
+
val bytesRead = audioRecord?.read(smallBuffer, 0, smallBuffer.size) ?: -1
|
|
229
|
+
if (bytesRead > 0) {
|
|
230
|
+
accumulator.add(smallBuffer.copyOf(bytesRead))
|
|
231
|
+
totalFrames += bytesRead / 2
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
audioRecord?.stop()
|
|
236
|
+
|
|
237
|
+
val passed = totalFrames >= targetFrames
|
|
238
|
+
results.add(TestResult(
|
|
239
|
+
name = "Buffer Accumulation",
|
|
240
|
+
passed = passed,
|
|
241
|
+
message = "Target: $targetFrames frames, Accumulated: $totalFrames frames over ${accumulator.size} reads"
|
|
242
|
+
))
|
|
243
|
+
|
|
244
|
+
println("✓ Target frames: $targetFrames")
|
|
245
|
+
println("✓ Accumulated: $totalFrames frames")
|
|
246
|
+
println("✓ Number of reads: ${accumulator.size}")
|
|
247
|
+
} else {
|
|
248
|
+
results.add(TestResult(
|
|
249
|
+
name = "Buffer Accumulation",
|
|
250
|
+
passed = false,
|
|
251
|
+
message = "Failed to initialize AudioRecord"
|
|
252
|
+
))
|
|
253
|
+
}
|
|
254
|
+
println()
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@Test
|
|
258
|
+
fun testDifferentSampleRates() {
|
|
259
|
+
println("Test 5: Different Sample Rates")
|
|
260
|
+
println("------------------------------")
|
|
261
|
+
|
|
262
|
+
val channelConfig = AudioFormat.CHANNEL_IN_MONO
|
|
263
|
+
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
|
264
|
+
val bufferDuration = 0.1 // 100ms
|
|
265
|
+
|
|
266
|
+
val sampleRates = listOf(16000, 44100, 48000)
|
|
267
|
+
|
|
268
|
+
for (sampleRate in sampleRates) {
|
|
269
|
+
val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
|
|
270
|
+
val targetFrames = (bufferDuration * sampleRate).toInt()
|
|
271
|
+
val targetBytes = targetFrames * 2
|
|
272
|
+
val actualBufferSize = if (targetBytes < minBufferSize) minBufferSize else targetBytes
|
|
273
|
+
|
|
274
|
+
audioRecord?.release()
|
|
275
|
+
audioRecord = AudioRecord(
|
|
276
|
+
MediaRecorder.AudioSource.MIC,
|
|
277
|
+
sampleRate,
|
|
278
|
+
channelConfig,
|
|
279
|
+
audioFormat,
|
|
280
|
+
actualBufferSize
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
val state = audioRecord?.state
|
|
284
|
+
val passed = state == AudioRecord.STATE_INITIALIZED
|
|
285
|
+
|
|
286
|
+
results.add(TestResult(
|
|
287
|
+
name = "Sample Rate ${sampleRate}Hz",
|
|
288
|
+
passed = passed,
|
|
289
|
+
message = "Buffer duration: ${bufferDuration}s, Frames: ${actualBufferSize / 2}"
|
|
290
|
+
))
|
|
291
|
+
|
|
292
|
+
println(" ${sampleRate}Hz: ${if (passed) "SUCCESS" else "FAILED"} - ${actualBufferSize / 2} frames")
|
|
293
|
+
}
|
|
294
|
+
println()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private fun printResults() {
|
|
298
|
+
println("📊 Test Results")
|
|
299
|
+
println("===============")
|
|
300
|
+
|
|
301
|
+
val passed = results.count { it.passed }
|
|
302
|
+
val total = results.size
|
|
303
|
+
|
|
304
|
+
for (result in results) {
|
|
305
|
+
val status = if (result.passed) "✅" else "❌"
|
|
306
|
+
println("$status ${result.name}")
|
|
307
|
+
println(" ${result.message}")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
println("\nSummary: $passed/$total tests passed")
|
|
311
|
+
|
|
312
|
+
if (passed == total) {
|
|
313
|
+
println("🎉 All tests passed!")
|
|
314
|
+
} else {
|
|
315
|
+
println("⚠️ Some tests failed")
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
println("\n📝 Key Findings:")
|
|
319
|
+
println("- Android enforces minimum buffer size via getMinBufferSize()")
|
|
320
|
+
println("- Minimum varies by device and sample rate")
|
|
321
|
+
println("- Small buffers require accumulation strategy")
|
|
322
|
+
println("- AudioRecord handles buffer sizing more flexibly than iOS")
|
|
323
|
+
}
|
|
324
|
+
}
|
package/android/src/androidTest/java/net/siteed/audiostudio/integration/CompressedOnlyOutputTest.kt
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
package net.siteed.audiostudio.integration
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
5
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
import org.junit.runner.RunWith
|
|
8
|
+
import org.junit.Assert.*
|
|
9
|
+
import java.io.File
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Integration test for Compressed-Only Output (Issue #244)
|
|
13
|
+
*
|
|
14
|
+
* This test validates that when primary output is disabled and compressed output
|
|
15
|
+
* is enabled, the compressed file information is properly returned in the result.
|
|
16
|
+
*/
|
|
17
|
+
@RunWith(AndroidJUnit4::class)
|
|
18
|
+
class CompressedOnlyOutputTest {
|
|
19
|
+
|
|
20
|
+
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
21
|
+
|
|
22
|
+
@Test
|
|
23
|
+
fun testCompressedOnlyOutput_AAC() {
|
|
24
|
+
println("🧪 Test: Compressed-Only Output with AAC")
|
|
25
|
+
println("---------------------------------------")
|
|
26
|
+
|
|
27
|
+
// Configuration with primary disabled, compressed enabled
|
|
28
|
+
val config = Bundle().apply {
|
|
29
|
+
putInt("sampleRate", 44100)
|
|
30
|
+
putInt("channels", 1)
|
|
31
|
+
putString("encoding", "pcm_16bit")
|
|
32
|
+
|
|
33
|
+
val outputBundle = Bundle().apply {
|
|
34
|
+
val primaryBundle = Bundle().apply {
|
|
35
|
+
putBoolean("enabled", false)
|
|
36
|
+
}
|
|
37
|
+
val compressedBundle = Bundle().apply {
|
|
38
|
+
putBoolean("enabled", true)
|
|
39
|
+
putString("format", "aac")
|
|
40
|
+
putInt("bitrate", 128000)
|
|
41
|
+
}
|
|
42
|
+
putBundle("primary", primaryBundle)
|
|
43
|
+
putBundle("compressed", compressedBundle)
|
|
44
|
+
}
|
|
45
|
+
putBundle("output", outputBundle)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Simulate recording result
|
|
49
|
+
val result = simulateRecording(config)
|
|
50
|
+
|
|
51
|
+
// Verify compression info is present
|
|
52
|
+
val compressionBundle = result.getBundle("compression")
|
|
53
|
+
assertNotNull("Compression info should not be null", compressionBundle)
|
|
54
|
+
|
|
55
|
+
// Verify compressed file URI is provided
|
|
56
|
+
val compressedFileUri = compressionBundle?.getString("compressedFileUri")
|
|
57
|
+
assertNotNull("Compressed file URI should not be null", compressedFileUri)
|
|
58
|
+
assertNotEquals("Compressed file URI should not be empty", "", compressedFileUri)
|
|
59
|
+
|
|
60
|
+
// Verify format and bitrate
|
|
61
|
+
assertEquals("aac", compressionBundle?.getString("format"))
|
|
62
|
+
assertEquals(128000, compressionBundle?.getInt("bitrate"))
|
|
63
|
+
|
|
64
|
+
println("✅ Compression info properly returned")
|
|
65
|
+
println("✅ Compressed file URI: $compressedFileUri")
|
|
66
|
+
println("✅ Format: ${compressionBundle?.getString("format")}")
|
|
67
|
+
println()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Test
|
|
71
|
+
fun testCompressedOnlyOutput_Opus() {
|
|
72
|
+
println("🧪 Test: Compressed-Only Output with Opus")
|
|
73
|
+
println("----------------------------------------")
|
|
74
|
+
|
|
75
|
+
val config = Bundle().apply {
|
|
76
|
+
putInt("sampleRate", 48000)
|
|
77
|
+
putInt("channels", 1)
|
|
78
|
+
putString("encoding", "pcm_16bit")
|
|
79
|
+
|
|
80
|
+
val outputBundle = Bundle().apply {
|
|
81
|
+
val primaryBundle = Bundle().apply {
|
|
82
|
+
putBoolean("enabled", false)
|
|
83
|
+
}
|
|
84
|
+
val compressedBundle = Bundle().apply {
|
|
85
|
+
putBoolean("enabled", true)
|
|
86
|
+
putString("format", "opus")
|
|
87
|
+
putInt("bitrate", 64000)
|
|
88
|
+
}
|
|
89
|
+
putBundle("primary", primaryBundle)
|
|
90
|
+
putBundle("compressed", compressedBundle)
|
|
91
|
+
}
|
|
92
|
+
putBundle("output", outputBundle)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
val result = simulateRecording(config)
|
|
96
|
+
val compressionBundle = result.getBundle("compression")
|
|
97
|
+
|
|
98
|
+
assertNotNull("Compression info should not be null", compressionBundle)
|
|
99
|
+
assertEquals("opus", compressionBundle?.getString("format"))
|
|
100
|
+
assertEquals(64000, compressionBundle?.getInt("bitrate"))
|
|
101
|
+
|
|
102
|
+
println("✅ Opus compression properly configured")
|
|
103
|
+
println()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@Test
|
|
107
|
+
fun testFileAccessibility() {
|
|
108
|
+
println("🧪 Test: Verify Compressed File Accessibility")
|
|
109
|
+
println("--------------------------------------------")
|
|
110
|
+
|
|
111
|
+
val config = Bundle().apply {
|
|
112
|
+
putInt("sampleRate", 44100)
|
|
113
|
+
putInt("channels", 1)
|
|
114
|
+
putString("encoding", "pcm_16bit")
|
|
115
|
+
|
|
116
|
+
val outputBundle = Bundle().apply {
|
|
117
|
+
val primaryBundle = Bundle().apply {
|
|
118
|
+
putBoolean("enabled", false)
|
|
119
|
+
}
|
|
120
|
+
val compressedBundle = Bundle().apply {
|
|
121
|
+
putBoolean("enabled", true)
|
|
122
|
+
putString("format", "aac")
|
|
123
|
+
}
|
|
124
|
+
putBundle("primary", primaryBundle)
|
|
125
|
+
putBundle("compressed", compressedBundle)
|
|
126
|
+
}
|
|
127
|
+
putBundle("output", outputBundle)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
val result = simulateRecording(config)
|
|
131
|
+
|
|
132
|
+
// Check main result structure
|
|
133
|
+
val fileUri = result.getString("fileUri", "")
|
|
134
|
+
val filename = result.getString("filename", "")
|
|
135
|
+
|
|
136
|
+
// Check compression structure
|
|
137
|
+
val compressionBundle = result.getBundle("compression")
|
|
138
|
+
val compressedUri = compressionBundle?.getString("compressedFileUri")
|
|
139
|
+
val compressedSize = compressionBundle?.getLong("size", 0L) ?: 0L
|
|
140
|
+
|
|
141
|
+
// When primary is disabled, we should have access to compressed file
|
|
142
|
+
val hasAccessToCompressed = !compressedUri.isNullOrEmpty() || fileUri.isNotEmpty()
|
|
143
|
+
|
|
144
|
+
assertTrue("Should have access to compressed file", hasAccessToCompressed)
|
|
145
|
+
assertTrue("Compressed file should have size > 0", compressedSize > 0)
|
|
146
|
+
|
|
147
|
+
println("✅ Compressed file is accessible")
|
|
148
|
+
println("✅ File size reported: $compressedSize bytes")
|
|
149
|
+
println()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Test
|
|
153
|
+
fun testBugScenario() {
|
|
154
|
+
println("🧪 Test: Current Bug Scenario")
|
|
155
|
+
println("-----------------------------")
|
|
156
|
+
|
|
157
|
+
// This test demonstrates the current bug
|
|
158
|
+
val config = Bundle().apply {
|
|
159
|
+
putInt("sampleRate", 44100)
|
|
160
|
+
putInt("channels", 1)
|
|
161
|
+
putString("encoding", "pcm_16bit")
|
|
162
|
+
|
|
163
|
+
val outputBundle = Bundle().apply {
|
|
164
|
+
val primaryBundle = Bundle().apply {
|
|
165
|
+
putBoolean("enabled", false)
|
|
166
|
+
}
|
|
167
|
+
val compressedBundle = Bundle().apply {
|
|
168
|
+
putBoolean("enabled", true)
|
|
169
|
+
putString("format", "aac")
|
|
170
|
+
putInt("bitrate", 128000)
|
|
171
|
+
}
|
|
172
|
+
putBundle("primary", primaryBundle)
|
|
173
|
+
putBundle("compressed", compressedBundle)
|
|
174
|
+
}
|
|
175
|
+
putBundle("output", outputBundle)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Current buggy behavior
|
|
179
|
+
val buggyResult = simulateBuggyRecording(config)
|
|
180
|
+
|
|
181
|
+
// This is what currently happens - compression is null
|
|
182
|
+
val compressionBundle = buggyResult.getBundle("compression")
|
|
183
|
+
|
|
184
|
+
if (compressionBundle == null) {
|
|
185
|
+
println("❌ BUG CONFIRMED: Compression info is null when primary is disabled")
|
|
186
|
+
println(" This prevents users from accessing the compressed file")
|
|
187
|
+
} else {
|
|
188
|
+
println("✅ Bug appears to be fixed - compression info is present")
|
|
189
|
+
}
|
|
190
|
+
println()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Simulates the expected correct behavior after fix
|
|
195
|
+
*/
|
|
196
|
+
private fun simulateRecording(config: Bundle): Bundle {
|
|
197
|
+
val outputConfig = config.getBundle("output")
|
|
198
|
+
val primaryEnabled = outputConfig?.getBundle("primary")?.getBoolean("enabled", true) ?: true
|
|
199
|
+
val compressedConfig = outputConfig?.getBundle("compressed")
|
|
200
|
+
val compressedEnabled = compressedConfig?.getBoolean("enabled", false) ?: false
|
|
201
|
+
|
|
202
|
+
return Bundle().apply {
|
|
203
|
+
if (!primaryEnabled) {
|
|
204
|
+
// Expected behavior after fix
|
|
205
|
+
putString("fileUri", "")
|
|
206
|
+
putString("filename", "stream-only")
|
|
207
|
+
putLong("durationMs", 5000)
|
|
208
|
+
putLong("size", 0)
|
|
209
|
+
putString("mimeType", "audio/wav")
|
|
210
|
+
|
|
211
|
+
// FIXED: Include compression info when compressed is enabled
|
|
212
|
+
if (compressedEnabled) {
|
|
213
|
+
val compressionBundle = Bundle().apply {
|
|
214
|
+
putString("compressedFileUri", "file:///storage/emulated/0/Android/data/test/files/recording.aac")
|
|
215
|
+
putString("format", compressedConfig?.getString("format") ?: "aac")
|
|
216
|
+
putInt("bitrate", compressedConfig?.getInt("bitrate") ?: 128000)
|
|
217
|
+
putLong("size", 40000)
|
|
218
|
+
putString("mimeType", "audio/aac")
|
|
219
|
+
}
|
|
220
|
+
putBundle("compression", compressionBundle)
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
// Normal behavior
|
|
224
|
+
putString("fileUri", "file:///storage/emulated/0/Android/data/test/files/recording.wav")
|
|
225
|
+
putString("filename", "recording.wav")
|
|
226
|
+
putLong("durationMs", 5000)
|
|
227
|
+
putLong("size", 240000)
|
|
228
|
+
putString("mimeType", "audio/wav")
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Simulates the current buggy behavior
|
|
235
|
+
*/
|
|
236
|
+
private fun simulateBuggyRecording(config: Bundle): Bundle {
|
|
237
|
+
val outputConfig = config.getBundle("output")
|
|
238
|
+
val primaryEnabled = outputConfig?.getBundle("primary")?.getBoolean("enabled", true) ?: true
|
|
239
|
+
|
|
240
|
+
return Bundle().apply {
|
|
241
|
+
if (!primaryEnabled) {
|
|
242
|
+
// Current buggy behavior
|
|
243
|
+
putString("fileUri", "")
|
|
244
|
+
putString("filename", "stream-only")
|
|
245
|
+
putLong("durationMs", 5000)
|
|
246
|
+
putLong("size", 0)
|
|
247
|
+
putString("mimeType", "audio/wav")
|
|
248
|
+
// BUG: compression is null even when compressed output is enabled
|
|
249
|
+
putBundle("compression", null)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|