@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,26 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
object AudioFeaturesNative {
|
|
4
|
+
init {
|
|
5
|
+
System.loadLibrary("audio-studio-cpp")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
external fun computeFrame(
|
|
9
|
+
samples: FloatArray,
|
|
10
|
+
sampleRate: Int,
|
|
11
|
+
fftLength: Int,
|
|
12
|
+
nMfcc: Int,
|
|
13
|
+
nMelFilters: Int,
|
|
14
|
+
computeMfcc: Boolean,
|
|
15
|
+
computeChroma: Boolean
|
|
16
|
+
): HashMap<String, Any>
|
|
17
|
+
|
|
18
|
+
external fun init(
|
|
19
|
+
sampleRate: Int,
|
|
20
|
+
fftLength: Int,
|
|
21
|
+
nMfcc: Int,
|
|
22
|
+
nMelFilters: Int,
|
|
23
|
+
computeMfcc: Boolean,
|
|
24
|
+
computeChroma: Boolean
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import java.io.File
|
|
5
|
+
import java.io.IOException
|
|
6
|
+
import java.io.OutputStream
|
|
7
|
+
import java.io.RandomAccessFile
|
|
8
|
+
import java.util.UUID
|
|
9
|
+
import net.siteed.audiostudio.LogUtils
|
|
10
|
+
|
|
11
|
+
class AudioFileHandler(private val filesDir: File) {
|
|
12
|
+
companion object {
|
|
13
|
+
private const val CLASS_NAME = "AudioFileHandler"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Method to write WAV file header
|
|
17
|
+
fun writeWavHeader(out: OutputStream, sampleRateInHz: Int, channels: Int, bitDepth: Int) {
|
|
18
|
+
val header = ByteArray(44)
|
|
19
|
+
val byteRate = sampleRateInHz * channels * bitDepth / 8
|
|
20
|
+
val blockAlign = channels * bitDepth / 8
|
|
21
|
+
|
|
22
|
+
// RIFF/WAVE header
|
|
23
|
+
"RIFF".toByteArray().copyInto(header, 0)
|
|
24
|
+
// (file size - 8) to be updated later
|
|
25
|
+
header[4] = 0 // Placeholder
|
|
26
|
+
header[5] = 0 // Placeholder
|
|
27
|
+
header[6] = 0 // Placeholder
|
|
28
|
+
header[7] = 0 // Placeholder
|
|
29
|
+
"WAVE".toByteArray().copyInto(header, 8)
|
|
30
|
+
"fmt ".toByteArray().copyInto(header, 12)
|
|
31
|
+
|
|
32
|
+
// 16 for PCM
|
|
33
|
+
header[16] = 16
|
|
34
|
+
header[17] = 0
|
|
35
|
+
header[18] = 0
|
|
36
|
+
header[19] = 0
|
|
37
|
+
|
|
38
|
+
// PCM format ID
|
|
39
|
+
header[20] = 1 // Audio format 1 for PCM (not compressed)
|
|
40
|
+
header[21] = 0
|
|
41
|
+
|
|
42
|
+
// Number of channels
|
|
43
|
+
header[22] = (channels and 0xff).toByte()
|
|
44
|
+
header[23] = (channels shr 8 and 0xff).toByte()
|
|
45
|
+
|
|
46
|
+
// Sample rate
|
|
47
|
+
header[24] = (sampleRateInHz and 0xff).toByte()
|
|
48
|
+
header[25] = (sampleRateInHz shr 8 and 0xff).toByte()
|
|
49
|
+
header[26] = (sampleRateInHz shr 16 and 0xff).toByte()
|
|
50
|
+
header[27] = (sampleRateInHz shr 24 and 0xff).toByte()
|
|
51
|
+
|
|
52
|
+
// Byte rate
|
|
53
|
+
header[28] = (byteRate and 0xff).toByte()
|
|
54
|
+
header[29] = (byteRate shr 8 and 0xff).toByte()
|
|
55
|
+
header[30] = (byteRate shr 16 and 0xff).toByte()
|
|
56
|
+
header[31] = (byteRate shr 24 and 0xff).toByte()
|
|
57
|
+
|
|
58
|
+
// Block align
|
|
59
|
+
header[32] = (blockAlign and 0xff).toByte()
|
|
60
|
+
header[33] = (blockAlign shr 8 and 0xff).toByte()
|
|
61
|
+
|
|
62
|
+
// Bits per sample
|
|
63
|
+
header[34] = (bitDepth and 0xff).toByte()
|
|
64
|
+
header[35] = (bitDepth shr 8 and 0xff).toByte()
|
|
65
|
+
|
|
66
|
+
// Data chunk
|
|
67
|
+
"data".toByteArray().copyInto(header, 36)
|
|
68
|
+
// Data size to be updated later
|
|
69
|
+
header[40] = 0 // Placeholder
|
|
70
|
+
header[41] = 0 // Placeholder
|
|
71
|
+
header[42] = 0 // Placeholder
|
|
72
|
+
header[43] = 0 // Placeholder
|
|
73
|
+
|
|
74
|
+
out.write(header, 0, 44)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fun updateWavHeader(file: File) {
|
|
78
|
+
try {
|
|
79
|
+
RandomAccessFile(file, "rw").use { raf ->
|
|
80
|
+
val fileSize = raf.length()
|
|
81
|
+
val dataSize = fileSize - 44 // Subtract the header size
|
|
82
|
+
|
|
83
|
+
raf.seek(4) // Write correct file size, excluding the first 8 bytes of the RIFF header
|
|
84
|
+
raf.writeInt(Integer.reverseBytes((dataSize + 36).toInt()))
|
|
85
|
+
|
|
86
|
+
raf.seek(40) // Go to the data size position
|
|
87
|
+
raf.writeInt(Integer.reverseBytes(dataSize.toInt())) // Write the size of the data segment
|
|
88
|
+
}
|
|
89
|
+
} catch (e: IOException) {
|
|
90
|
+
println("Could not update WAV header: ${e.message}")
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fun clearAudioStorage() {
|
|
95
|
+
filesDir.listFiles()?.forEach {
|
|
96
|
+
it.delete()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fun createAudioFile(extension: String): File {
|
|
101
|
+
val timestamp = System.currentTimeMillis()
|
|
102
|
+
val uuid = UUID.randomUUID().toString()
|
|
103
|
+
val filename = "recording_${timestamp}_${uuid}.${extension}"
|
|
104
|
+
|
|
105
|
+
return try {
|
|
106
|
+
File(filesDir, filename).apply {
|
|
107
|
+
parentFile?.mkdirs() // Create directories if they don't exist
|
|
108
|
+
createNewFile() // Create the file
|
|
109
|
+
}
|
|
110
|
+
} catch (e: Exception) {
|
|
111
|
+
LogUtils.e(CLASS_NAME, "Failed to create audio file", e)
|
|
112
|
+
throw e
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fun deleteFile(file: File?): Boolean {
|
|
117
|
+
return try {
|
|
118
|
+
if (file == null) {
|
|
119
|
+
LogUtils.w(CLASS_NAME, "Attempted to delete null file")
|
|
120
|
+
false
|
|
121
|
+
} else if (!file.exists()) {
|
|
122
|
+
LogUtils.w(CLASS_NAME, "File does not exist: ${file.absolutePath}")
|
|
123
|
+
false
|
|
124
|
+
} else {
|
|
125
|
+
val wasDeleted = file.delete()
|
|
126
|
+
if (!wasDeleted) {
|
|
127
|
+
LogUtils.w(CLASS_NAME, "Failed to delete file: ${file.absolutePath}")
|
|
128
|
+
}
|
|
129
|
+
wasDeleted
|
|
130
|
+
}
|
|
131
|
+
} catch (e: Exception) {
|
|
132
|
+
LogUtils.e(CLASS_NAME, "Error deleting file: ${file?.absolutePath}", e)
|
|
133
|
+
false
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
package net.siteed.audiostudio
|
|
2
|
+
|
|
3
|
+
import android.media.AudioFormat
|
|
4
|
+
import java.nio.ByteBuffer
|
|
5
|
+
import java.nio.ByteOrder
|
|
6
|
+
import kotlin.math.*
|
|
7
|
+
|
|
8
|
+
object AudioFormatUtils {
|
|
9
|
+
/**
|
|
10
|
+
* Converts a byte array of audio data to a float array based on the given encoding.
|
|
11
|
+
* @param audioData The raw audio data in bytes.
|
|
12
|
+
* @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
|
|
13
|
+
* @return A float array with normalized audio samples in the range [-1.0, 1.0].
|
|
14
|
+
*/
|
|
15
|
+
fun convertByteArrayToFloatArray(audioData: ByteArray, encoding: String): FloatArray {
|
|
16
|
+
return when (encoding) {
|
|
17
|
+
"pcm_8bit" -> {
|
|
18
|
+
val floatArray = FloatArray(audioData.size)
|
|
19
|
+
for (i in audioData.indices) {
|
|
20
|
+
// Convert unsigned 8-bit to float in range [-1.0, 1.0]
|
|
21
|
+
floatArray[i] = ((audioData[i].toInt() and 0xFF) - 128) / 128.0f
|
|
22
|
+
}
|
|
23
|
+
floatArray
|
|
24
|
+
}
|
|
25
|
+
"pcm_16bit" -> {
|
|
26
|
+
val floatArray = FloatArray(audioData.size / 2)
|
|
27
|
+
val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
|
|
28
|
+
for (i in floatArray.indices) {
|
|
29
|
+
floatArray[i] = buffer.short / 32768.0f // Normalize to [-1.0, 1.0]
|
|
30
|
+
}
|
|
31
|
+
floatArray
|
|
32
|
+
}
|
|
33
|
+
"pcm_32bit" -> {
|
|
34
|
+
val floatArray = FloatArray(audioData.size / 4)
|
|
35
|
+
val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
|
|
36
|
+
for (i in floatArray.indices) {
|
|
37
|
+
floatArray[i] = buffer.int / 2_147_483_648.0f // Normalize to [-1.0, 1.0]
|
|
38
|
+
}
|
|
39
|
+
floatArray
|
|
40
|
+
}
|
|
41
|
+
else -> {
|
|
42
|
+
// Default to 16-bit PCM if encoding is not recognized
|
|
43
|
+
val floatArray = FloatArray(audioData.size / 2)
|
|
44
|
+
val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
|
|
45
|
+
for (i in floatArray.indices) {
|
|
46
|
+
floatArray[i] = buffer.short / 32768.0f
|
|
47
|
+
}
|
|
48
|
+
floatArray
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Calculates the bit depth (number of bits per sample) based on the encoding string.
|
|
55
|
+
* @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
|
|
56
|
+
* @return The bit depth as an integer.
|
|
57
|
+
*/
|
|
58
|
+
fun getBitDepth(encoding: String): Int {
|
|
59
|
+
return when (encoding) {
|
|
60
|
+
"pcm_8bit" -> 8
|
|
61
|
+
"pcm_16bit" -> 16
|
|
62
|
+
"pcm_32bit" -> 32
|
|
63
|
+
else -> 16 // Default to 16-bit if not recognized
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Determines the AudioFormat encoding constant based on the encoding string.
|
|
69
|
+
* @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
|
|
70
|
+
* @return The corresponding AudioFormat constant.
|
|
71
|
+
*/
|
|
72
|
+
fun getAudioFormat(encoding: String): Int {
|
|
73
|
+
return when (encoding) {
|
|
74
|
+
"pcm_8bit" -> AudioFormat.ENCODING_PCM_8BIT
|
|
75
|
+
"pcm_16bit" -> AudioFormat.ENCODING_PCM_16BIT
|
|
76
|
+
"pcm_32bit" -> AudioFormat.ENCODING_PCM_FLOAT
|
|
77
|
+
else -> AudioFormat.ENCODING_PCM_16BIT // Default to 16-bit PCM
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Converts audio data between different bit depths
|
|
83
|
+
* @param audioData The raw audio data
|
|
84
|
+
* @param sourceBitDepth The original bit depth
|
|
85
|
+
* @param targetBitDepth The desired bit depth
|
|
86
|
+
* @return The converted audio data
|
|
87
|
+
*/
|
|
88
|
+
fun convertBitDepth(audioData: ByteArray, sourceBitDepth: Int, targetBitDepth: Int): ByteArray {
|
|
89
|
+
if (sourceBitDepth == targetBitDepth || audioData.isEmpty()) {
|
|
90
|
+
return audioData
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return when {
|
|
94
|
+
sourceBitDepth == 8 && targetBitDepth == 16 -> convert8to16(audioData)
|
|
95
|
+
sourceBitDepth == 16 && targetBitDepth == 8 -> convert16to8(audioData)
|
|
96
|
+
sourceBitDepth == 16 && targetBitDepth == 32 -> convert16to32(audioData)
|
|
97
|
+
sourceBitDepth == 32 && targetBitDepth == 16 -> convert32to16(audioData)
|
|
98
|
+
sourceBitDepth == 8 && targetBitDepth == 32 -> {
|
|
99
|
+
// Convert 8 -> 16 -> 32
|
|
100
|
+
val temp16 = convert8to16(audioData)
|
|
101
|
+
convert16to32(temp16)
|
|
102
|
+
}
|
|
103
|
+
sourceBitDepth == 32 && targetBitDepth == 8 -> {
|
|
104
|
+
// Convert 32 -> 16 -> 8
|
|
105
|
+
val temp16 = convert32to16(audioData)
|
|
106
|
+
convert16to8(temp16)
|
|
107
|
+
}
|
|
108
|
+
else -> throw IllegalArgumentException("Unsupported bit depth conversion: $sourceBitDepth to $targetBitDepth")
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private fun convert8to16(data: ByteArray): ByteArray {
|
|
113
|
+
val output = ByteBuffer.allocate(data.size * 2).order(ByteOrder.LITTLE_ENDIAN)
|
|
114
|
+
for (sample in data) {
|
|
115
|
+
// Convert unsigned 8-bit (0-255) to signed 16-bit (-32768 to 32767)
|
|
116
|
+
val unsigned = sample.toInt() and 0xFF
|
|
117
|
+
// Map [0, 255] to [-32768, 32767]
|
|
118
|
+
// Special case for 0 to map to -32768
|
|
119
|
+
val signed16 = when (unsigned) {
|
|
120
|
+
0 -> -32768
|
|
121
|
+
255 -> 32767
|
|
122
|
+
else -> {
|
|
123
|
+
val normalized = (unsigned - 128) / 128.0f
|
|
124
|
+
(normalized * 32768).toInt().coerceIn(-32768, 32767)
|
|
125
|
+
}
|
|
126
|
+
}.toShort()
|
|
127
|
+
output.putShort(signed16)
|
|
128
|
+
}
|
|
129
|
+
return output.array()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private fun convert16to8(data: ByteArray): ByteArray {
|
|
133
|
+
val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
|
|
134
|
+
val output = ByteArray(data.size / 2)
|
|
135
|
+
|
|
136
|
+
for (i in output.indices) {
|
|
137
|
+
// Convert signed 16-bit to unsigned 8-bit
|
|
138
|
+
val sample16 = input.getShort()
|
|
139
|
+
// Map [-32768, 32767] to [0, 255]
|
|
140
|
+
val normalized = sample16 / 32768.0f
|
|
141
|
+
val sample8 = ((normalized * 128) + 128).toInt().coerceIn(0, 255).toByte()
|
|
142
|
+
output[i] = sample8
|
|
143
|
+
}
|
|
144
|
+
return output
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private fun convert16to32(data: ByteArray): ByteArray {
|
|
148
|
+
val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
|
|
149
|
+
val output = ByteBuffer.allocate(data.size * 2).order(ByteOrder.LITTLE_ENDIAN)
|
|
150
|
+
|
|
151
|
+
while (input.hasRemaining()) {
|
|
152
|
+
val sample16 = input.getShort()
|
|
153
|
+
// Scale 16-bit to 32-bit range
|
|
154
|
+
val sample32 = (sample16.toInt() shl 16)
|
|
155
|
+
output.putInt(sample32)
|
|
156
|
+
}
|
|
157
|
+
return output.array()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private fun convert32to16(data: ByteArray): ByteArray {
|
|
161
|
+
val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
|
|
162
|
+
val output = ByteBuffer.allocate(data.size / 2).order(ByteOrder.LITTLE_ENDIAN)
|
|
163
|
+
|
|
164
|
+
while (input.hasRemaining()) {
|
|
165
|
+
val sample32 = input.getInt()
|
|
166
|
+
// Scale 32-bit to 16-bit range
|
|
167
|
+
val sample16 = (sample32 shr 16).toShort()
|
|
168
|
+
output.putShort(sample16)
|
|
169
|
+
}
|
|
170
|
+
return output.array()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Convert between different channel configurations
|
|
175
|
+
*/
|
|
176
|
+
fun convertChannels(data: ByteArray, fromChannels: Int, toChannels: Int, bitDepth: Int): ByteArray {
|
|
177
|
+
if (fromChannels == toChannels || data.isEmpty()) {
|
|
178
|
+
return data
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
val bytesPerSample = bitDepth / 8
|
|
182
|
+
val samplesPerFrame = fromChannels
|
|
183
|
+
val totalFrames = data.size / (bytesPerSample * samplesPerFrame)
|
|
184
|
+
|
|
185
|
+
return when {
|
|
186
|
+
fromChannels == 1 && toChannels == 2 -> monoToStereo(data, bitDepth, totalFrames)
|
|
187
|
+
fromChannels == 2 && toChannels == 1 -> stereoToMono(data, bitDepth, totalFrames)
|
|
188
|
+
else -> throw IllegalArgumentException("Unsupported channel conversion: $fromChannels to $toChannels")
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private fun monoToStereo(data: ByteArray, bitDepth: Int, totalFrames: Int): ByteArray {
|
|
193
|
+
val bytesPerSample = bitDepth / 8
|
|
194
|
+
val output = ByteBuffer.allocate(data.size * 2).order(ByteOrder.LITTLE_ENDIAN)
|
|
195
|
+
val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
|
|
196
|
+
|
|
197
|
+
for (i in 0 until totalFrames) {
|
|
198
|
+
when (bitDepth) {
|
|
199
|
+
16 -> {
|
|
200
|
+
val sample = input.getShort()
|
|
201
|
+
output.putShort(sample) // Left
|
|
202
|
+
output.putShort(sample) // Right
|
|
203
|
+
}
|
|
204
|
+
32 -> {
|
|
205
|
+
val sample = input.getInt()
|
|
206
|
+
output.putInt(sample) // Left
|
|
207
|
+
output.putInt(sample) // Right
|
|
208
|
+
}
|
|
209
|
+
8 -> {
|
|
210
|
+
val sample = input.get()
|
|
211
|
+
output.put(sample) // Left
|
|
212
|
+
output.put(sample) // Right
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return output.array()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private fun stereoToMono(data: ByteArray, bitDepth: Int, totalFrames: Int): ByteArray {
|
|
220
|
+
val bytesPerSample = bitDepth / 8
|
|
221
|
+
val output = ByteBuffer.allocate(data.size / 2).order(ByteOrder.LITTLE_ENDIAN)
|
|
222
|
+
val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
|
|
223
|
+
|
|
224
|
+
for (i in 0 until totalFrames) {
|
|
225
|
+
when (bitDepth) {
|
|
226
|
+
16 -> {
|
|
227
|
+
val left = input.getShort()
|
|
228
|
+
val right = input.getShort()
|
|
229
|
+
val mono = ((left + right) / 2).toShort()
|
|
230
|
+
output.putShort(mono)
|
|
231
|
+
}
|
|
232
|
+
32 -> {
|
|
233
|
+
val left = input.getInt()
|
|
234
|
+
val right = input.getInt()
|
|
235
|
+
val mono = ((left.toLong() + right.toLong()) / 2).toInt()
|
|
236
|
+
output.putInt(mono)
|
|
237
|
+
}
|
|
238
|
+
8 -> {
|
|
239
|
+
val left = input.get().toInt() and 0xFF
|
|
240
|
+
val right = input.get().toInt() and 0xFF
|
|
241
|
+
val mono = ((left + right) / 2).toByte()
|
|
242
|
+
output.put(mono)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return output.array()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Normalize audio to maximum amplitude
|
|
251
|
+
*/
|
|
252
|
+
fun normalizeAudio(data: ByteArray, bitDepth: Int): ByteArray {
|
|
253
|
+
if (data.isEmpty()) return data
|
|
254
|
+
|
|
255
|
+
val input = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)
|
|
256
|
+
var maxAmplitude = 0
|
|
257
|
+
|
|
258
|
+
// Find maximum amplitude
|
|
259
|
+
input.rewind()
|
|
260
|
+
when (bitDepth) {
|
|
261
|
+
16 -> {
|
|
262
|
+
while (input.hasRemaining()) {
|
|
263
|
+
val sample = abs(input.getShort().toInt())
|
|
264
|
+
maxAmplitude = maxOf(maxAmplitude, sample)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
32 -> {
|
|
268
|
+
while (input.hasRemaining()) {
|
|
269
|
+
val sample = abs(input.getInt())
|
|
270
|
+
maxAmplitude = maxOf(maxAmplitude, sample)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
8 -> {
|
|
274
|
+
while (input.hasRemaining()) {
|
|
275
|
+
val sample = abs((input.get().toInt() and 0xFF) - 128)
|
|
276
|
+
maxAmplitude = maxOf(maxAmplitude, sample)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// If already at max or silent, return as is
|
|
282
|
+
if (maxAmplitude == 0) return data
|
|
283
|
+
|
|
284
|
+
val maxValue = when (bitDepth) {
|
|
285
|
+
16 -> Short.MAX_VALUE.toInt()
|
|
286
|
+
32 -> Int.MAX_VALUE
|
|
287
|
+
8 -> 127
|
|
288
|
+
else -> throw IllegalArgumentException("Unsupported bit depth: $bitDepth")
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (maxAmplitude >= maxValue) return data
|
|
292
|
+
|
|
293
|
+
// Normalize
|
|
294
|
+
val scaleFactor = maxValue.toFloat() / maxAmplitude
|
|
295
|
+
val output = ByteBuffer.allocate(data.size).order(ByteOrder.LITTLE_ENDIAN)
|
|
296
|
+
input.rewind()
|
|
297
|
+
|
|
298
|
+
when (bitDepth) {
|
|
299
|
+
16 -> {
|
|
300
|
+
while (input.hasRemaining()) {
|
|
301
|
+
val sample = input.getShort()
|
|
302
|
+
val normalized = (sample * scaleFactor).toInt().coerceIn(-32768, 32767).toShort()
|
|
303
|
+
output.putShort(normalized)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
32 -> {
|
|
307
|
+
while (input.hasRemaining()) {
|
|
308
|
+
val sample = input.getInt()
|
|
309
|
+
val normalized = (sample * scaleFactor).toLong().coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()
|
|
310
|
+
output.putInt(normalized)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
8 -> {
|
|
314
|
+
while (input.hasRemaining()) {
|
|
315
|
+
val sample = (input.get().toInt() and 0xFF) - 128
|
|
316
|
+
val normalized = ((sample * scaleFactor).toInt() + 128).coerceIn(0, 255).toByte()
|
|
317
|
+
output.put(normalized)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return output.array()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Resample audio data to a different sample rate
|
|
327
|
+
*/
|
|
328
|
+
fun resampleAudio(samples: FloatArray, fromSampleRate: Int, toSampleRate: Int): FloatArray {
|
|
329
|
+
if (fromSampleRate == toSampleRate || samples.isEmpty()) {
|
|
330
|
+
return samples
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
val resampleRatio = toSampleRate.toDouble() / fromSampleRate
|
|
334
|
+
val newLength = (samples.size * resampleRatio).toInt()
|
|
335
|
+
val resampled = FloatArray(newLength)
|
|
336
|
+
|
|
337
|
+
for (i in resampled.indices) {
|
|
338
|
+
val sourceIndex = i / resampleRatio
|
|
339
|
+
val sourceIndexInt = sourceIndex.toInt()
|
|
340
|
+
val fraction = sourceIndex - sourceIndexInt
|
|
341
|
+
|
|
342
|
+
if (sourceIndexInt >= samples.size - 1) {
|
|
343
|
+
resampled[i] = samples.last()
|
|
344
|
+
} else {
|
|
345
|
+
// Linear interpolation
|
|
346
|
+
val sample1 = samples[sourceIndexInt]
|
|
347
|
+
val sample2 = samples[sourceIndexInt + 1]
|
|
348
|
+
resampled[i] = (sample1 * (1 - fraction) + sample2 * fraction).toFloat()
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return resampled
|
|
353
|
+
}
|
|
354
|
+
}
|