@siteed/expo-audio-studio 2.18.6 → 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/README.md +13 -297
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +6 -135
- package/CHANGELOG.md +0 -501
- package/LICENSE +0 -21
- package/android/build.gradle +0 -129
- 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/audiostream/AudioProcessorInstrumentedTest.kt +0 -197
- package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderInstrumentedTest.kt +0 -541
- package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderPerformanceInstrumentedTest.kt +0 -234
- package/android/src/androidTest/java/net/siteed/audiostream/integration/AudioFocusStrategyIntegrationTest.kt +0 -332
- package/android/src/androidTest/java/net/siteed/audiostream/integration/BufferDurationIntegrationTest.kt +0 -324
- package/android/src/androidTest/java/net/siteed/audiostream/integration/CompressedOnlyOutputTest.kt +0 -253
- package/android/src/androidTest/java/net/siteed/audiostream/integration/DeviceDisconnectionFallbackTest.kt +0 -218
- package/android/src/androidTest/java/net/siteed/audiostream/integration/EventEmissionIntervalTest.kt +0 -120
- package/android/src/androidTest/java/net/siteed/audiostream/integration/M4aFormatTest.kt +0 -345
- package/android/src/androidTest/java/net/siteed/audiostream/integration/OutputControlIntegrationTest.kt +0 -340
- package/android/src/androidTest/java/net/siteed/audiostream/integration/PcmStreamingDurationTest.kt +0 -252
- package/android/src/androidTest/java/net/siteed/audiostream/integration/README.md +0 -95
- package/android/src/androidTest/java/net/siteed/audiostream/integration/run_integration_tests.sh +0 -43
- package/android/src/main/AndroidManifest.xml +0 -30
- package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +0 -188
- package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +0 -9
- package/android/src/main/java/net/siteed/audiostream/AudioDeviceManager.kt +0 -1741
- package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +0 -136
- package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +0 -354
- package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +0 -439
- package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +0 -2237
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +0 -2141
- package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +0 -167
- package/android/src/main/java/net/siteed/audiostream/AudioTrimmer.kt +0 -1099
- package/android/src/main/java/net/siteed/audiostream/Constants.kt +0 -37
- package/android/src/main/java/net/siteed/audiostream/EventSender.kt +0 -7
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +0 -1113
- package/android/src/main/java/net/siteed/audiostream/FFT.kt +0 -99
- package/android/src/main/java/net/siteed/audiostream/Features.kt +0 -98
- package/android/src/main/java/net/siteed/audiostream/LogUtils.kt +0 -93
- package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +0 -72
- package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +0 -68
- package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +0 -59
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +0 -257
- package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +0 -19
- package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +0 -159
- package/android/src/main/res/drawable/ic_default_action_icon.xml +0 -16
- package/android/src/main/res/drawable/ic_microphone.xml +0 -13
- package/android/src/main/res/drawable/ic_pause.xml +0 -10
- package/android/src/main/res/drawable/ic_play.xml +0 -10
- package/android/src/main/res/drawable/ic_stop.xml +0 -10
- package/android/src/main/res/layout/notification_recording.xml +0 -37
- package/android/src/test/java/net/siteed/audiostream/AudioFileHandlerTest.kt +0 -279
- package/android/src/test/java/net/siteed/audiostream/AudioFocusStrategyTest.kt +0 -249
- package/android/src/test/java/net/siteed/audiostream/AudioFormatTest.kt +0 -151
- package/android/src/test/java/net/siteed/audiostream/AudioFormatUtilsTest.kt +0 -273
- package/android/src/test/java/net/siteed/audiostream/DeviceDisconnectionFallbackUnitTest.kt +0 -140
- package/android/src/test/resources/chorus.wav +0 -0
- package/android/src/test/resources/generate_test_audio.py +0 -94
- 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 +0 -3
- package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +0 -4
- package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
- package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +0 -210
- package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
- package/build/cjs/AudioAnalysis/extractAudioData.js +0 -21
- package/build/cjs/AudioAnalysis/extractAudioData.js.map +0 -1
- package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +0 -92
- package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
- package/build/cjs/AudioAnalysis/extractPreview.js +0 -28
- package/build/cjs/AudioAnalysis/extractPreview.js.map +0 -1
- package/build/cjs/AudioAnalysis/extractWaveform.js +0 -18
- package/build/cjs/AudioAnalysis/extractWaveform.js.map +0 -1
- package/build/cjs/AudioDeviceManager.js +0 -689
- package/build/cjs/AudioDeviceManager.js.map +0 -1
- package/build/cjs/AudioRecorder.provider.js +0 -78
- package/build/cjs/AudioRecorder.provider.js.map +0 -1
- package/build/cjs/ExpoAudioStream.native.js +0 -8
- package/build/cjs/ExpoAudioStream.native.js.map +0 -1
- package/build/cjs/ExpoAudioStream.types.js +0 -11
- package/build/cjs/ExpoAudioStream.types.js.map +0 -1
- package/build/cjs/ExpoAudioStream.web.js +0 -708
- package/build/cjs/ExpoAudioStream.web.js.map +0 -1
- package/build/cjs/ExpoAudioStreamModule.js +0 -718
- package/build/cjs/ExpoAudioStreamModule.js.map +0 -1
- package/build/cjs/WebRecorder.web.js +0 -777
- package/build/cjs/WebRecorder.web.js.map +0 -1
- package/build/cjs/constants/platformLimitations.js +0 -99
- package/build/cjs/constants/platformLimitations.js.map +0 -1
- package/build/cjs/constants.js +0 -17
- package/build/cjs/constants.js.map +0 -1
- package/build/cjs/events.js +0 -29
- package/build/cjs/events.js.map +0 -1
- package/build/cjs/hooks/useAudioDevices.js +0 -179
- package/build/cjs/hooks/useAudioDevices.js.map +0 -1
- package/build/cjs/index.js +0 -58
- package/build/cjs/index.js.map +0 -1
- package/build/cjs/trimAudio.js +0 -76
- package/build/cjs/trimAudio.js.map +0 -1
- package/build/cjs/useAudioRecorder.js +0 -518
- package/build/cjs/useAudioRecorder.js.map +0 -1
- package/build/cjs/utils/BlobFix.js +0 -502
- package/build/cjs/utils/BlobFix.js.map +0 -1
- package/build/cjs/utils/audioProcessing.js +0 -136
- package/build/cjs/utils/audioProcessing.js.map +0 -1
- package/build/cjs/utils/cleanNativeOptions.js +0 -22
- package/build/cjs/utils/cleanNativeOptions.js.map +0 -1
- package/build/cjs/utils/concatenateBuffers.js +0 -25
- package/build/cjs/utils/concatenateBuffers.js.map +0 -1
- package/build/cjs/utils/convertPCMToFloat32.js +0 -124
- package/build/cjs/utils/convertPCMToFloat32.js.map +0 -1
- package/build/cjs/utils/crc32.js +0 -52
- package/build/cjs/utils/crc32.js.map +0 -1
- package/build/cjs/utils/encodingToBitDepth.js +0 -17
- package/build/cjs/utils/encodingToBitDepth.js.map +0 -1
- package/build/cjs/utils/getWavFileInfo.js +0 -96
- package/build/cjs/utils/getWavFileInfo.js.map +0 -1
- package/build/cjs/utils/writeWavHeader.js +0 -88
- package/build/cjs/utils/writeWavHeader.js.map +0 -1
- package/build/cjs/workers/InlineFeaturesExtractor.web.js +0 -859
- package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +0 -1
- package/build/cjs/workers/inlineAudioWebWorker.web.js +0 -184
- package/build/cjs/workers/inlineAudioWebWorker.web.js.map +0 -1
- package/build/esm/AudioAnalysis/AudioAnalysis.types.js +0 -3
- package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
- package/build/esm/AudioAnalysis/extractAudioAnalysis.js +0 -202
- package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
- package/build/esm/AudioAnalysis/extractAudioData.js +0 -14
- package/build/esm/AudioAnalysis/extractAudioData.js.map +0 -1
- package/build/esm/AudioAnalysis/extractMelSpectrogram.js +0 -89
- package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
- package/build/esm/AudioAnalysis/extractPreview.js +0 -25
- package/build/esm/AudioAnalysis/extractPreview.js.map +0 -1
- package/build/esm/AudioAnalysis/extractWaveform.js +0 -11
- package/build/esm/AudioAnalysis/extractWaveform.js.map +0 -1
- package/build/esm/AudioDeviceManager.js +0 -682
- package/build/esm/AudioDeviceManager.js.map +0 -1
- package/build/esm/AudioRecorder.provider.js +0 -40
- package/build/esm/AudioRecorder.provider.js.map +0 -1
- package/build/esm/ExpoAudioStream.native.js +0 -6
- package/build/esm/ExpoAudioStream.native.js.map +0 -1
- package/build/esm/ExpoAudioStream.types.js +0 -8
- package/build/esm/ExpoAudioStream.types.js.map +0 -1
- package/build/esm/ExpoAudioStream.web.js +0 -704
- package/build/esm/ExpoAudioStream.web.js.map +0 -1
- package/build/esm/ExpoAudioStreamModule.js +0 -713
- package/build/esm/ExpoAudioStreamModule.js.map +0 -1
- package/build/esm/WebRecorder.web.js +0 -773
- package/build/esm/WebRecorder.web.js.map +0 -1
- package/build/esm/constants/platformLimitations.js +0 -90
- package/build/esm/constants/platformLimitations.js.map +0 -1
- package/build/esm/constants.js +0 -14
- package/build/esm/constants.js.map +0 -1
- package/build/esm/events.js +0 -21
- package/build/esm/events.js.map +0 -1
- package/build/esm/hooks/useAudioDevices.js +0 -176
- package/build/esm/hooks/useAudioDevices.js.map +0 -1
- package/build/esm/index.js +0 -20
- package/build/esm/index.js.map +0 -1
- package/build/esm/trimAudio.js +0 -69
- package/build/esm/trimAudio.js.map +0 -1
- package/build/esm/useAudioRecorder.js +0 -512
- package/build/esm/useAudioRecorder.js.map +0 -1
- package/build/esm/utils/BlobFix.js +0 -498
- package/build/esm/utils/BlobFix.js.map +0 -1
- package/build/esm/utils/audioProcessing.js +0 -133
- package/build/esm/utils/audioProcessing.js.map +0 -1
- package/build/esm/utils/cleanNativeOptions.js +0 -19
- package/build/esm/utils/cleanNativeOptions.js.map +0 -1
- package/build/esm/utils/concatenateBuffers.js +0 -21
- package/build/esm/utils/concatenateBuffers.js.map +0 -1
- package/build/esm/utils/convertPCMToFloat32.js +0 -120
- package/build/esm/utils/convertPCMToFloat32.js.map +0 -1
- package/build/esm/utils/crc32.js +0 -50
- package/build/esm/utils/crc32.js.map +0 -1
- package/build/esm/utils/encodingToBitDepth.js +0 -13
- package/build/esm/utils/encodingToBitDepth.js.map +0 -1
- package/build/esm/utils/getWavFileInfo.js +0 -92
- package/build/esm/utils/getWavFileInfo.js.map +0 -1
- package/build/esm/utils/writeWavHeader.js +0 -84
- package/build/esm/utils/writeWavHeader.js.map +0 -1
- package/build/esm/workers/InlineFeaturesExtractor.web.js +0 -856
- package/build/esm/workers/InlineFeaturesExtractor.web.js.map +0 -1
- package/build/esm/workers/inlineAudioWebWorker.web.js +0 -181
- package/build/esm/workers/inlineAudioWebWorker.web.js.map +0 -1
- package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +0 -196
- package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
- package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts +0 -74
- package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
- package/build/types/AudioAnalysis/extractAudioData.d.ts +0 -3
- package/build/types/AudioAnalysis/extractAudioData.d.ts.map +0 -1
- package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts +0 -14
- package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
- package/build/types/AudioAnalysis/extractPreview.d.ts +0 -11
- package/build/types/AudioAnalysis/extractPreview.d.ts.map +0 -1
- package/build/types/AudioAnalysis/extractWaveform.d.ts +0 -8
- package/build/types/AudioAnalysis/extractWaveform.d.ts.map +0 -1
- package/build/types/AudioDeviceManager.d.ts +0 -187
- package/build/types/AudioDeviceManager.d.ts.map +0 -1
- package/build/types/AudioRecorder.provider.d.ts +0 -11
- package/build/types/AudioRecorder.provider.d.ts.map +0 -1
- package/build/types/ExpoAudioStream.native.d.ts +0 -3
- package/build/types/ExpoAudioStream.native.d.ts.map +0 -1
- package/build/types/ExpoAudioStream.types.d.ts +0 -738
- package/build/types/ExpoAudioStream.types.d.ts.map +0 -1
- package/build/types/ExpoAudioStream.web.d.ts +0 -96
- package/build/types/ExpoAudioStream.web.d.ts.map +0 -1
- package/build/types/ExpoAudioStreamModule.d.ts +0 -3
- package/build/types/ExpoAudioStreamModule.d.ts.map +0 -1
- package/build/types/WebRecorder.web.d.ts +0 -198
- package/build/types/WebRecorder.web.d.ts.map +0 -1
- package/build/types/constants/platformLimitations.d.ts +0 -40
- package/build/types/constants/platformLimitations.d.ts.map +0 -1
- package/build/types/constants.d.ts +0 -11
- package/build/types/constants.d.ts.map +0 -1
- package/build/types/events.d.ts +0 -26
- package/build/types/events.d.ts.map +0 -1
- package/build/types/hooks/useAudioDevices.d.ts +0 -15
- package/build/types/hooks/useAudioDevices.d.ts.map +0 -1
- package/build/types/index.d.ts +0 -18
- package/build/types/index.d.ts.map +0 -1
- package/build/types/trimAudio.d.ts +0 -25
- package/build/types/trimAudio.d.ts.map +0 -1
- package/build/types/useAudioRecorder.d.ts +0 -22
- package/build/types/useAudioRecorder.d.ts.map +0 -1
- package/build/types/utils/BlobFix.d.ts +0 -9
- package/build/types/utils/BlobFix.d.ts.map +0 -1
- package/build/types/utils/audioProcessing.d.ts +0 -24
- package/build/types/utils/audioProcessing.d.ts.map +0 -1
- package/build/types/utils/cleanNativeOptions.d.ts +0 -15
- package/build/types/utils/cleanNativeOptions.d.ts.map +0 -1
- package/build/types/utils/concatenateBuffers.d.ts +0 -8
- package/build/types/utils/concatenateBuffers.d.ts.map +0 -1
- package/build/types/utils/convertPCMToFloat32.d.ts +0 -13
- package/build/types/utils/convertPCMToFloat32.d.ts.map +0 -1
- package/build/types/utils/crc32.d.ts +0 -7
- package/build/types/utils/crc32.d.ts.map +0 -1
- package/build/types/utils/encodingToBitDepth.d.ts +0 -5
- package/build/types/utils/encodingToBitDepth.d.ts.map +0 -1
- package/build/types/utils/getWavFileInfo.d.ts +0 -26
- package/build/types/utils/getWavFileInfo.d.ts.map +0 -1
- package/build/types/utils/writeWavHeader.d.ts +0 -34
- package/build/types/utils/writeWavHeader.d.ts.map +0 -1
- package/build/types/workers/InlineFeaturesExtractor.web.d.ts +0 -2
- package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
- package/build/types/workers/inlineAudioWebWorker.web.d.ts +0 -2
- package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
- package/expo-module.config.json +0 -10
- package/ios/AudioAnalysisData.swift +0 -74
- package/ios/AudioDeviceManager.swift +0 -670
- package/ios/AudioNotificationManager.swift +0 -154
- package/ios/AudioProcessingHelpers.swift +0 -743
- package/ios/AudioProcessor.swift +0 -1151
- package/ios/AudioStreamError.swift +0 -7
- package/ios/AudioStreamManager.swift +0 -2369
- package/ios/AudioStreamManagerDelegate.swift +0 -16
- package/ios/DataPoint.swift +0 -54
- package/ios/DecodingConfig.swift +0 -59
- package/ios/ExpoAudioStream.podspec +0 -33
- package/ios/ExpoAudioStreamModule.swift +0 -1019
- package/ios/ExpoAudioStudioTests/AudioFileHandlerTests.swift +0 -338
- package/ios/ExpoAudioStudioTests/AudioFormatUtilsTests.swift +0 -331
- package/ios/ExpoAudioStudioTests/AudioTestHelpers.swift +0 -130
- package/ios/ExpoAudioStudioTests/CompressedOnlyOutputTests.swift +0 -294
- package/ios/ExpoAudioStudioTests/EventEmissionIntervalTests.swift +0 -105
- package/ios/ExpoAudioStudioTests/Info.plist +0 -22
- package/ios/ExpoAudioStudioTests/README.md +0 -39
- package/ios/ExpoAudioStudioTests/SimpleAudioTest.swift +0 -98
- package/ios/ExpoAudioStudioTests/TestAudioGenerator.swift +0 -75
- package/ios/FFT.swift +0 -62
- package/ios/Features.swift +0 -95
- package/ios/ISSUE_IOS.md +0 -68
- package/ios/Logger.swift +0 -39
- package/ios/NotificationExtension.swift +0 -15
- package/ios/RecordingResult.swift +0 -22
- package/ios/RecordingSettings.swift +0 -308
- package/ios/WaveformExtractor.swift +0 -105
- package/ios/tests/README.md +0 -41
- package/ios/tests/integration/buffer_and_fallback_test.swift +0 -178
- package/ios/tests/integration/buffer_duration_test.swift +0 -185
- package/ios/tests/integration/compressed_only_output_test.swift +0 -271
- package/ios/tests/integration/output_control_test.swift +0 -322
- package/ios/tests/integration/run_integration_tests.sh +0 -37
- package/ios/tests/opus_support_test_macos.swift +0 -154
- package/ios/tests/standalone/audio_processing_test.swift +0 -144
- package/ios/tests/standalone/audio_recording_test.swift +0 -277
- package/ios/tests/standalone/audio_streaming_test.swift +0 -249
- package/ios/tests/standalone/standalone_test.swift +0 -144
- package/plugin/build/index.cjs +0 -194
- package/plugin/build/index.d.cts +0 -22
- package/plugin/build/index.js +0 -194
- package/plugin/src/index.ts +0 -285
- package/plugin/tsconfig.json +0 -10
- package/plugin/tsconfig.tsbuildinfo +0 -1
- package/src/AudioAnalysis/AudioAnalysis.types.ts +0 -224
- package/src/AudioAnalysis/extractAudioAnalysis.ts +0 -344
- package/src/AudioAnalysis/extractAudioData.ts +0 -17
- package/src/AudioAnalysis/extractMelSpectrogram.ts +0 -154
- package/src/AudioAnalysis/extractPreview.ts +0 -34
- package/src/AudioAnalysis/extractWaveform.ts +0 -22
- package/src/AudioDeviceManager.ts +0 -803
- package/src/AudioRecorder.provider.tsx +0 -57
- package/src/ExpoAudioStream.native.ts +0 -6
- package/src/ExpoAudioStream.types.ts +0 -874
- package/src/ExpoAudioStream.web.ts +0 -905
- package/src/ExpoAudioStreamModule.ts +0 -990
- package/src/WebRecorder.web.ts +0 -1005
- package/src/constants/platformLimitations.ts +0 -118
- package/src/constants.ts +0 -18
- package/src/events.ts +0 -60
- package/src/hooks/useAudioDevices.ts +0 -213
- package/src/index.ts +0 -54
- package/src/trimAudio.ts +0 -94
- package/src/types/crc-32.d.ts +0 -9
- package/src/useAudioRecorder.tsx +0 -766
- package/src/utils/BlobFix.ts +0 -561
- package/src/utils/audioProcessing.ts +0 -205
- package/src/utils/cleanNativeOptions.ts +0 -18
- package/src/utils/concatenateBuffers.ts +0 -24
- package/src/utils/convertPCMToFloat32.ts +0 -170
- package/src/utils/crc32.ts +0 -59
- package/src/utils/encodingToBitDepth.ts +0 -18
- package/src/utils/getWavFileInfo.ts +0 -132
- package/src/utils/writeWavHeader.ts +0 -115
- package/src/workers/InlineFeaturesExtractor.web.tsx +0 -855
- package/src/workers/inlineAudioWebWorker.web.tsx +0 -180
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"InlineFeaturesExtractor.web.js","sourceRoot":"","sources":["../../../src/workers/InlineFeaturesExtractor.web.tsx"],"names":[],"mappings":";;;AAAA,yEAAyE;AAC5D,QAAA,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAq1BtC,CAAA","sourcesContent":["// packages/expo-audio-studio/src/workers/InlineFeaturesExtractor.web.tsx\nexport const InlineFeaturesExtractor = `\n// Constants\nconst N_FFT = 1024; // Default FFT size\nconst MAX_FFT_SIZE = 8192; // Maximum FFT size to prevent memory issues\nconst N_CHROMA = 12;\n\n// FFT Implementation with normalized Hann window\nfunction FFT(n) {\n this.n = n;\n this.cosTable = new Float32Array(n / 2);\n this.sinTable = new Float32Array(n / 2);\n this.hannWindow = new Float32Array(n);\n \n // Match Android implementation with precomputed tables\n const normalizationFactor = Math.sqrt(2.0 / n);\n for (var i = 0; i < n / 2; i++) {\n this.cosTable[i] = Math.cos(2.0 * Math.PI * i / n);\n this.sinTable[i] = Math.sin(2.0 * Math.PI * i / n);\n }\n \n // Precompute normalized Hann window to match Android\n for (var i = 0; i < n; i++) {\n this.hannWindow[i] = normalizationFactor * 0.5 * (1 - Math.cos(2.0 * Math.PI * i / (n - 1)));\n }\n}\n\nFFT.prototype.transform = function(data) {\n const n = data.length;\n \n // Validate input length is power of 2\n if ((n & (n - 1)) !== 0) {\n throw new Error('FFT length must be power of 2');\n }\n\n // Use iterative bit reversal instead of recursive\n const bitReversedIndices = new Uint32Array(n);\n for (let i = 0; i < n; i++) {\n let reversed = 0;\n let j = i;\n let bits = Math.log2(n);\n while (bits--) {\n reversed = (reversed << 1) | (j & 1);\n j >>= 1;\n }\n bitReversedIndices[i] = reversed;\n }\n\n // Apply bit reversal\n for (let i = 0; i < n; i++) {\n const j = bitReversedIndices[i];\n if (i < j) {\n const temp = data[i];\n data[i] = data[j];\n data[j] = temp;\n }\n }\n\n // Iterative FFT computation with optimized memory usage\n for (let step = 1; step < n; step <<= 1) {\n const jump = step << 1;\n const angleStep = Math.PI / step;\n\n for (let group = 0; group < n; group += jump) {\n for (let pair = group; pair < group + step; pair++) {\n const match = pair + step;\n const angle = angleStep * (pair - group);\n \n const currentCos = Math.cos(angle);\n const currentSin = Math.sin(angle);\n\n const real = currentCos * data[match] - currentSin * data[match + 1];\n const imag = currentCos * data[match + 1] + currentSin * data[match];\n\n data[match] = data[pair] - real;\n data[match + 1] = data[pair + 1] - imag;\n data[pair] += real;\n data[pair + 1] += imag;\n }\n }\n }\n};\n\n// Add realInverse method\nFFT.prototype.realInverse = function(powerSpectrum, output) {\n const n = powerSpectrum.length;\n const complexData = new Float32Array(n * 2);\n \n // Copy power spectrum to complex format\n for (let i = 0; i < n/2 + 1; i++) {\n complexData[2 * i] = powerSpectrum[i];\n if (2 * i + 1 < complexData.length) {\n complexData[2 * i + 1] = 0;\n }\n }\n \n // Conjugate for inverse FFT\n for (let i = 0; i < n; i++) {\n if (2 * i + 1 < complexData.length) {\n complexData[2 * i + 1] = -complexData[2 * i + 1];\n }\n }\n \n this.transform(complexData);\n \n // Copy real part to output and scale\n for (let i = 0; i < n; i++) {\n output[i] = complexData[2 * i] / n;\n }\n};\n\n// Add helper functions to match Android\nfunction nextPowerOfTwo(n) {\n let value = 1;\n while (value < n) {\n value *= 2;\n }\n return value;\n}\n\nfunction applyHannWindow(samples) {\n const output = new Float32Array(samples.length);\n for (let i = 0; i < samples.length; i++) {\n const multiplier = 0.5 * (1 - Math.cos(2 * Math.PI * i / (samples.length - 1)));\n output[i] = samples[i] * multiplier;\n }\n return output;\n}\n\n// Update spectral feature computation to match Android\nfunction computeSpectralFeatures(segment, sampleRate, featureOptions = {}) {\n try {\n // Early return if no spectral features are requested\n if (!featureOptions.spectralCentroid && \n !featureOptions.spectralFlatness && \n !featureOptions.spectralRollOff && \n !featureOptions.spectralBandwidth &&\n !featureOptions.magnitudeSpectrum) {\n return {\n centroid: 0,\n flatness: 0,\n rollOff: 0,\n bandwidth: 0,\n magnitudeSpectrum: []\n };\n }\n\n // Ensure we have valid data\n if (!segment || segment.length === 0) {\n throw new Error('Invalid segment data');\n }\n\n // Process in fixed-size chunks\n const chunkSize = N_FFT;\n const numChunks = Math.ceil(segment.length / chunkSize);\n \n let results = {\n centroid: 0,\n flatness: 0,\n rollOff: 0,\n bandwidth: 0,\n magnitudeSpectrum: new Float32Array(N_FFT / 2 + 1).fill(0)\n };\n \n let validChunks = 0;\n \n // Iterate through chunks\n for (let i = 0; i < numChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, segment.length);\n const chunk = segment.slice(start, end);\n \n if (chunk.length < N_FFT / 4) continue; // Skip very small chunks\n\n // Process the chunk\n const paddedChunk = new Float32Array(N_FFT);\n paddedChunk.set(applyHannWindow(chunk));\n\n const fft = new FFT(N_FFT);\n fft.transform(paddedChunk);\n\n // Calculate magnitude spectrum\n const chunkMagnitudeSpectrum = new Float32Array(N_FFT / 2 + 1);\n let hasSignal = false;\n \n for (let j = 0; j < N_FFT / 2; j++) {\n const re = paddedChunk[2 * j];\n const im = paddedChunk[2 * j + 1];\n const magnitude = Math.sqrt(re * re + im * im);\n chunkMagnitudeSpectrum[j] = magnitude;\n if (magnitude > Number.EPSILON) hasSignal = true;\n }\n \n if (!hasSignal) continue;\n validChunks++;\n\n // Accumulate results\n if (featureOptions.spectralCentroid) {\n const centroid = computeSpectralCentroid(chunkMagnitudeSpectrum, sampleRate);\n if (!isNaN(centroid)) results.centroid += centroid;\n }\n \n if (featureOptions.spectralFlatness) {\n const flatness = computeSpectralFlatness(chunkMagnitudeSpectrum);\n if (!isNaN(flatness)) results.flatness += flatness;\n }\n \n if (featureOptions.spectralRollOff) {\n const rolloff = computeSpectralRollOff(chunkMagnitudeSpectrum, sampleRate);\n if (!isNaN(rolloff)) results.rollOff += rolloff;\n }\n \n if (featureOptions.spectralBandwidth && !isNaN(results.centroid)) {\n const bandwidth = computeSpectralBandwidth(chunkMagnitudeSpectrum, sampleRate, results.centroid);\n if (!isNaN(bandwidth)) results.bandwidth += bandwidth;\n }\n \n if (featureOptions.magnitudeSpectrum) {\n for (let j = 0; j < results.magnitudeSpectrum.length; j++) {\n results.magnitudeSpectrum[j] += chunkMagnitudeSpectrum[j];\n }\n }\n }\n\n // Average the accumulated results\n if (validChunks > 0) {\n results.centroid /= validChunks;\n results.flatness /= validChunks;\n results.rollOff /= validChunks;\n results.bandwidth /= validChunks;\n \n if (featureOptions.magnitudeSpectrum) {\n for (let i = 0; i < results.magnitudeSpectrum.length; i++) {\n results.magnitudeSpectrum[i] /= validChunks;\n }\n }\n }\n\n return results;\n } catch (error) {\n console.error('[Worker] Spectral feature computation error:', error);\n return {\n centroid: 0,\n flatness: 0,\n rollOff: 0,\n bandwidth: 0,\n magnitudeSpectrum: []\n };\n }\n}\n\nfunction computeSpectralCentroid(magnitudeSpectrum, sampleRate) {\n const sum = magnitudeSpectrum.reduce((a, b) => a + (b || 0), 0);\n if (sum <= Number.EPSILON) return 0;\n \n const weightedSum = magnitudeSpectrum.reduce((acc, value, index) => \n acc + (index * (sampleRate / N_FFT) * (value || 0)), 0);\n \n return weightedSum / sum;\n}\n\nfunction computeSpectralFlatness(powerSpectrum) {\n // Add small epsilon to avoid log(0)\n const epsilon = Number.EPSILON;\n const validSpectrum = powerSpectrum.map(v => Math.max(v, epsilon));\n \n const geometricMean = Math.exp(\n validSpectrum\n .map(v => Math.log(v))\n .reduce((a, b) => a + b) / validSpectrum.length\n );\n \n const arithmeticMean =\n validSpectrum.reduce((a, b) => a + b) / validSpectrum.length;\n \n return geometricMean / arithmeticMean;\n}\n\nfunction computeSpectralRollOff(magnitudeSpectrum, sampleRate) {\n const totalEnergy = magnitudeSpectrum.reduce((a, b) => a + b, 0);\n const rollOffThreshold = totalEnergy * 0.85;\n let cumulativeEnergy = 0;\n\n for (let i = 0; i < magnitudeSpectrum.length; i++) {\n cumulativeEnergy += magnitudeSpectrum[i];\n if (cumulativeEnergy >= rollOffThreshold) {\n return (i / magnitudeSpectrum.length) * (sampleRate / 2);\n }\n }\n\n return 0;\n}\n\nfunction computeSpectralBandwidth(magnitudeSpectrum, sampleRate, centroid) {\n const sum = magnitudeSpectrum.reduce((a, b) => a + (b || 0), 0);\n if (sum <= Number.EPSILON) return 0;\n\n const weightedSum = magnitudeSpectrum.reduce(\n (acc, value, index) => {\n const freq = index * sampleRate / (2 * magnitudeSpectrum.length);\n return acc + (value || 0) * Math.pow(freq - centroid, 2);\n }, 0\n );\n\n return Math.sqrt(weightedSum / sum);\n}\n\nfunction computeChroma(segmentData, sampleRate) {\n // Ensure we have valid input data\n if (!segmentData || segmentData.length === 0) {\n return new Array(N_CHROMA).fill(0);\n }\n\n const fftLength = nextPowerOfTwo(Math.max(segmentData.length, N_FFT));\n const windowed = applyHannWindow(segmentData);\n const padded = new Float32Array(fftLength);\n padded.set(windowed.slice(0, Math.min(windowed.length, fftLength)));\n\n const fft = new FFT(fftLength);\n try {\n fft.transform(padded);\n } catch (e) {\n console.error('[Worker] FFT transform failed in chromagram:', e);\n return new Array(N_CHROMA).fill(0);\n }\n\n const chroma = new Float32Array(N_CHROMA).fill(0);\n const freqsPerBin = sampleRate / fftLength;\n let totalEnergy = 0;\n\n // First pass: compute magnitudes and total energy\n for (let i = 0; i < fftLength / 2; i++) {\n const freq = i * freqsPerBin;\n if (freq > 20) { // Only consider frequencies above 20 Hz\n const re = padded[2 * i];\n const im = padded[2 * i + 1] || 0;\n const magnitude = Math.sqrt(re * re + im * im);\n \n if (magnitude > Number.EPSILON) {\n // Use a more stable pitch class calculation\n const midiNote = 69 + 12 * Math.log2(freq / 440.0);\n const pitchClass = Math.round(midiNote) % 12;\n \n if (pitchClass >= 0 && pitchClass < 12) {\n chroma[pitchClass] += magnitude;\n totalEnergy += magnitude;\n }\n }\n }\n }\n\n // Normalize chroma values only if we have energy\n if (totalEnergy > Number.EPSILON) {\n for (let i = 0; i < N_CHROMA; i++) {\n chroma[i] = chroma[i] / totalEnergy;\n }\n }\n\n // Convert to regular array and ensure no NaN values\n return Array.from(chroma, v => isNaN(v) ? 0 : v);\n}\n\nfunction extractHNR(segmentData) {\n const frameSize = segmentData.length;\n const autocorrelation = new Float32Array(frameSize);\n\n // Compute the autocorrelation iteratively\n for (let i = 0; i < frameSize; i++) {\n let sum = 0;\n for (let j = 0; j < frameSize - i; j++) {\n sum += segmentData[j] * segmentData[j + i];\n }\n autocorrelation[i] = sum;\n }\n\n // Find the maximum autocorrelation value iteratively\n let maxAutocorrelation = -Infinity;\n for (let i = 1; i < autocorrelation.length; i++) {\n if (autocorrelation[i] > maxAutocorrelation) {\n maxAutocorrelation = autocorrelation[i];\n }\n }\n\n // Compute the HNR\n return autocorrelation[0] !== 0\n ? 10 * Math.log10(maxAutocorrelation / (autocorrelation[0] - maxAutocorrelation))\n : 0;\n}\n\nfunction estimatePitch(segment, sampleRate) {\n // Early validation\n if (!segment || segment.length < 2 || !sampleRate) return 0;\n\n try {\n // Apply Hann window\n const windowed = applyHannWindow(segment);\n\n // Pad for FFT\n const fftLength = nextPowerOfTwo(segment.length * 2);\n const padded = new Float32Array(fftLength);\n padded.set(windowed);\n\n // Perform FFT\n const fft = new FFT(fftLength);\n fft.transform(padded);\n\n // Compute power spectrum\n const powerSpectrum = new Float32Array(fftLength / 2 + 1);\n for (let i = 0; i <= fftLength / 2; i++) {\n const re = padded[2 * i];\n const im = padded[2 * i + 1] || 0;\n powerSpectrum[i] = re * re + im * im;\n }\n\n // Find peak frequency\n let maxPower = 0;\n let peakIndex = 0;\n const minFreq = 50; // Minimum frequency to consider (Hz)\n const maxFreq = 1000; // Maximum frequency to consider (Hz)\n const minBin = Math.floor(minFreq * fftLength / sampleRate);\n const maxBin = Math.ceil(maxFreq * fftLength / sampleRate);\n\n for (let i = minBin; i <= maxBin; i++) {\n if (powerSpectrum[i] > maxPower) {\n maxPower = powerSpectrum[i];\n peakIndex = i;\n }\n }\n\n // Convert peak index to frequency\n const fundamentalFreq = peakIndex * sampleRate / fftLength;\n\n // Return 0 if the detected frequency is outside reasonable bounds\n return (fundamentalFreq >= minFreq && fundamentalFreq <= maxFreq) ? \n fundamentalFreq : 0;\n\n } catch (error) {\n console.error('[Worker] Pitch estimation error:', error);\n return 0;\n }\n}\n\n// Unique ID counter - the only state we need to maintain\nlet uniqueIdCounter = 0\nlet lastEmitTime = Date.now()\n\nself.onmessage = function (event) {\n // Extract enableLogging early so we can use it consistently\n const enableLogging = event.data.enableLogging || false;\n \n // Create consistent logger that only logs when enabled\n const logger = enableLogging ? {\n debug: (...args) => console.debug('[Worker]', ...args),\n log: (...args) => console.log('[Worker]', ...args),\n warn: (...args) => console.warn('[Worker]', ...args),\n error: (...args) => console.error('[Worker]', ...args)\n } : {\n debug: () => {},\n log: () => {},\n warn: () => {},\n error: () => {}\n };\n \n // Check if this is a reset command\n if (event.data.command === 'resetCounter') {\n const newValue = event.data.value;\n logger.log('Reset counter request received with value:', newValue);\n \n // Always respect explicit resets through the resetCounter command\n uniqueIdCounter = typeof newValue === 'number' ? newValue : 0;\n logger.log('Counter explicitly set to:', uniqueIdCounter);\n \n return; // Exit early, don't process audio\n }\n\n // Regular audio processing\n const {\n channelData,\n sampleRate,\n segmentDurationMs,\n algorithm,\n bitDepth,\n fullAudioDurationMs,\n numberOfChannels,\n features: _features,\n intervalAnalysis = 500,\n } = event.data\n\n // Calculate subChunkStartTime safely, defaulting to 0 if fullAudioDurationMs is not a valid number\n const subChunkStartTime = (typeof fullAudioDurationMs === 'number' && !isNaN(fullAudioDurationMs) && fullAudioDurationMs >= 0)\n ? fullAudioDurationMs / 1000\n : 0;\n\n const features = _features || {}\n const bytesPerSample = bitDepth / 8; // Calculate bytes per sample\n\n const SILENCE_THRESHOLD = 0.01\n const MIN_SILENCE_DURATION = 1.5 * sampleRate // 1.5 seconds of silence\n const SPEECH_INERTIA_DURATION = 0.1 * sampleRate // Speech inertia duration in samples\n const RMS_THRESHOLD = 0.01\n const ZCR_THRESHOLD = 0.1\n\n // Placeholder functions for feature extraction\n const extractMFCC = (segmentData, sampleRate) => {\n // Implement MFCC extraction logic here\n return []\n }\n\n const extractSpectralCentroid = (segmentData, sampleRate) => {\n const magnitudeSpectrum = segmentData.map((v) => v * v)\n const sum = magnitudeSpectrum.reduce((a, b) => a + b, 0)\n if (sum === 0) return 0\n\n const weightedSum = magnitudeSpectrum.reduce(\n (acc, value, index) => acc + index * value,\n 0\n )\n return (\n ((weightedSum / sum) * (sampleRate / 2)) / magnitudeSpectrum.length\n )\n }\n\n const extractSpectralFlatness = (segmentData) => {\n const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))\n const geometricMean = Math.exp(\n magnitudeSpectrum\n .map((v) => Math.log(v + Number.MIN_VALUE))\n .reduce((a, b) => a + b) / magnitudeSpectrum.length\n )\n const arithmeticMean =\n magnitudeSpectrum.reduce((a, b) => a + b) / magnitudeSpectrum.length\n return arithmeticMean === 0 ? 0 : geometricMean / arithmeticMean\n }\n\n const extractSpectralRollOff = (segmentData, sampleRate) => {\n const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))\n const totalEnergy = magnitudeSpectrum.reduce((a, b) => a + b, 0)\n const rollOffThreshold = totalEnergy * 0.85\n let cumulativeEnergy = 0\n\n for (let i = 0; i < magnitudeSpectrum.length; i++) {\n cumulativeEnergy += magnitudeSpectrum[i]\n if (cumulativeEnergy >= rollOffThreshold) {\n return (i / magnitudeSpectrum.length) * (sampleRate / 2)\n }\n }\n\n return 0\n }\n\n const extractSpectralBandwidth = (segmentData, sampleRate) => {\n const centroid = extractSpectralCentroid(segmentData, sampleRate)\n const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))\n const sum = magnitudeSpectrum.reduce((a, b) => a + b, 0)\n if (sum === 0) return 0\n\n const weightedSum = magnitudeSpectrum.reduce(\n (acc, value, index) => acc + value * Math.pow(index - centroid, 2),\n 0\n )\n return Math.sqrt(weightedSum / sum)\n }\n\n const extractChromagram = (segmentData, sampleRate) => {\n return [] // TODO implement\n }\n\n /**\n * Creates a features object based on requested features\n */\n function createFeaturesObject(\n features,\n maxAmp,\n rms,\n sumSquares,\n zeroCrossings,\n remainingSamples,\n spectralFeatures,\n channelData,\n startIdx,\n endIdx,\n sampleRate,\n numberOfChannels,\n bytesPerSample\n ) {\n // If no features are requested, return undefined\n if (!Object.values(features).some(function(v) { return v; })) {\n return undefined;\n }\n\n const result = {};\n \n if (features.energy) {\n result.energy = sumSquares;\n }\n if (features.rms) {\n result.rms = rms;\n }\n // Always include min/max amplitude if any features are requested\n result.minAmplitude = -maxAmp;\n result.maxAmplitude = maxAmp;\n \n if (features.zcr) {\n result.zcr = zeroCrossings / remainingSamples;\n }\n if (features.spectralCentroid) {\n result.spectralCentroid = spectralFeatures.centroid;\n }\n if (features.spectralFlatness) {\n result.spectralFlatness = spectralFeatures.flatness;\n }\n if (features.spectralRolloff) {\n result.spectralRolloff = spectralFeatures.rollOff;\n }\n if (features.spectralBandwidth) {\n result.spectralBandwidth = spectralFeatures.bandwidth;\n }\n if (features.chromagram) {\n result.chromagram = computeChroma(channelData.slice(startIdx, endIdx), sampleRate);\n }\n if (features.hnr) {\n result.hnr = extractHNR(channelData.slice(startIdx, endIdx));\n }\n if (features.pitch) {\n result.pitch = estimatePitch(channelData.slice(startIdx, endIdx), sampleRate);\n }\n \n return result;\n }\n\n function extractWaveform(\n channelData,\n sampleRate,\n segmentDurationMs,\n numberOfChannels,\n bytesPerSample\n ) {\n const logger = enableLogging ? {\n debug: (...args) => console.debug('[Worker]', ...args),\n log: (...args) => console.log('[Worker]', ...args),\n error: (...args) => console.error('[Worker]', ...args)\n } : {\n debug: () => {},\n log: () => {},\n error: () => {}\n }\n\n // Calculate amplitude range\n let min = Infinity\n let max = -Infinity\n for (let i = 0; i < channelData.length; i++) {\n min = Math.min(min, channelData[i])\n max = Math.max(max, channelData[i])\n }\n\n const totalSamples = channelData.length\n const durationMs = (totalSamples / sampleRate) * 1000\n \n // Calculate fixed segment sizes\n const samplesPerSegment = Math.floor(sampleRate * (segmentDurationMs / 1000));\n const numPoints = Math.floor(totalSamples / samplesPerSegment);\n const remainingSamples = totalSamples % samplesPerSegment;\n\n const dataPoints = []\n\n // Process full segments\n for (let i = 0; i < numPoints; i++) {\n const startIdx = i * samplesPerSegment\n const endIdx = startIdx + samplesPerSegment\n \n let sumSquares = 0\n let maxAmp = 0\n let zeroCrossings = 0\n\n // Calculate segment features\n for (let j = startIdx; j < endIdx; j++) {\n const value = channelData[j]\n sumSquares += value * value\n maxAmp = Math.max(maxAmp, Math.abs(value))\n if (j > 0 && value * channelData[j - 1] < 0) {\n zeroCrossings++\n }\n }\n\n const rms = Math.sqrt(sumSquares / samplesPerSegment)\n const startTime = subChunkStartTime + (startIdx / sampleRate)\n const endTime = subChunkStartTime + (endIdx / sampleRate)\n // Calculate byte positions correctly based on numberOfChannels and bytesPerSample\n const startPosition = startIdx * numberOfChannels * bytesPerSample\n const endPosition = endIdx * numberOfChannels * bytesPerSample\n\n var spectralFeatures = computeSpectralFeatures(channelData.slice(startIdx, endIdx), sampleRate, features);\n\n // Simply use the counter, increment after assigning\n const dataPoint = {\n id: uniqueIdCounter++,\n amplitude: maxAmp,\n rms,\n startTime,\n endTime,\n dB: 20 * Math.log10(rms + 1e-6),\n silent: rms < 0.01,\n startPosition,\n endPosition,\n samples: samplesPerSegment,\n }\n\n // Extract features if any are requested\n const extractedFeatures = createFeaturesObject(\n features,\n maxAmp,\n rms,\n sumSquares,\n zeroCrossings,\n samplesPerSegment,\n spectralFeatures,\n channelData,\n startIdx,\n endIdx,\n sampleRate,\n numberOfChannels,\n bytesPerSample\n );\n \n if (extractedFeatures) {\n dataPoint.features = extractedFeatures;\n }\n\n dataPoints.push(dataPoint)\n }\n\n // Handle remaining samples if they exist and are enough to process\n if (remainingSamples > samplesPerSegment / 4) { // Only process if we have at least 1/4 of a segment\n const startIdx = numPoints * samplesPerSegment\n const endIdx = totalSamples\n \n let sumSquares = 0\n let maxAmp = 0\n let zeroCrossings = 0\n\n for (let j = startIdx; j < endIdx; j++) {\n const value = channelData[j]\n sumSquares += value * value\n maxAmp = Math.max(maxAmp, Math.abs(value))\n if (j > 0 && value * channelData[j - 1] < 0) {\n zeroCrossings++\n }\n }\n\n const rms = Math.sqrt(sumSquares / remainingSamples)\n const startTime = subChunkStartTime + (startIdx / sampleRate);\n const endTime = subChunkStartTime + (endIdx / sampleRate);\n // Calculate byte positions correctly based on numberOfChannels and bytesPerSample\n const startPosition = startIdx * numberOfChannels * bytesPerSample\n const endPosition = endIdx * numberOfChannels * bytesPerSample\n\n var spectralFeatures = computeSpectralFeatures(channelData.slice(startIdx, endIdx), sampleRate, features);\n\n // Simply use the counter, increment after assigning\n const dataPoint = {\n id: uniqueIdCounter++,\n amplitude: maxAmp,\n rms,\n startTime,\n endTime,\n dB: 20 * Math.log10(rms + 1e-6),\n silent: rms < 0.01,\n startPosition,\n endPosition,\n samples: remainingSamples,\n }\n\n logger.debug('extractWaveform - dataPoint', dataPoint);\n // Extract features if any are requested\n const extractedFeatures = createFeaturesObject(\n features,\n maxAmp,\n rms,\n sumSquares,\n zeroCrossings,\n remainingSamples,\n spectralFeatures,\n channelData,\n startIdx,\n endIdx,\n sampleRate,\n numberOfChannels,\n bytesPerSample\n );\n \n if (extractedFeatures) {\n dataPoint.features = extractedFeatures;\n }\n\n dataPoints.push(dataPoint)\n }\n\n return {\n durationMs,\n dataPoints,\n amplitudeRange: { min, max },\n rmsRange: {\n min: 0,\n max: Math.max(Math.abs(min), Math.abs(max))\n }\n }\n }\n\n try {\n // Measure actual processing time using performance.now() for higher precision\n const processingStartTime = performance.now()\n \n const result = extractWaveform(\n channelData,\n sampleRate,\n segmentDurationMs,\n numberOfChannels || 1, // Default to 1 channel if not provided\n bytesPerSample\n )\n \n const processingEndTime = performance.now()\n const actualExtractionTimeMs = processingEndTime - processingStartTime\n\n // Send complete result immediately\n self.postMessage({\n command: 'features',\n result: {\n bitDepth,\n samples: channelData.length,\n numberOfChannels,\n sampleRate,\n segmentDurationMs,\n durationMs: result.durationMs,\n dataPoints: result.dataPoints,\n amplitudeRange: result.amplitudeRange,\n rmsRange: result.rmsRange,\n extractionTimeMs: actualExtractionTimeMs,\n }\n })\n } catch (error) {\n console.error('[Worker] Error', {\n message: error.message,\n stack: error.stack\n });\n \n self.postMessage({ \n error: {\n message: error.message,\n stack: error.stack,\n name: error.name\n }\n });\n }\n}\n`\n"]}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InlineAudioWebWorker = void 0;
|
|
4
|
-
// packages/expo-audio-stream/src/workers/inlineAudioWebWorker.web.tsx
|
|
5
|
-
exports.InlineAudioWebWorker = `
|
|
6
|
-
const DEFAULT_BIT_DEPTH = 32
|
|
7
|
-
const DEFAULT_SAMPLE_RATE = 44100
|
|
8
|
-
|
|
9
|
-
class RecorderProcessor extends AudioWorkletProcessor {
|
|
10
|
-
constructor() {
|
|
11
|
-
super()
|
|
12
|
-
this.currentChunk = [] // Float32Array
|
|
13
|
-
this.samplesSinceLastExport = 0
|
|
14
|
-
this.recordSampleRate = DEFAULT_SAMPLE_RATE
|
|
15
|
-
this.exportSampleRate = DEFAULT_SAMPLE_RATE
|
|
16
|
-
this.recordBitDepth = DEFAULT_BIT_DEPTH
|
|
17
|
-
this.exportBitDepth = DEFAULT_BIT_DEPTH
|
|
18
|
-
this.numberOfChannels = 1
|
|
19
|
-
this.isRecording = true
|
|
20
|
-
this.port.onmessage = this.handleMessage.bind(this)
|
|
21
|
-
this.enableLogging = false
|
|
22
|
-
this.exportIntervalSamples = 0
|
|
23
|
-
this.currentPosition = 0 // Track current position in seconds
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
handleMessage(event) {
|
|
27
|
-
switch (event.data.command) {
|
|
28
|
-
case 'init':
|
|
29
|
-
this.enableLogging = event.data.enableLogging || false
|
|
30
|
-
this.recordSampleRate = event.data.recordSampleRate
|
|
31
|
-
this.exportSampleRate =
|
|
32
|
-
event.data.exportSampleRate || event.data.recordSampleRate
|
|
33
|
-
this.exportIntervalSamples =
|
|
34
|
-
this.recordSampleRate * (event.data.interval / 1000)
|
|
35
|
-
if (event.data.numberOfChannels) {
|
|
36
|
-
this.numberOfChannels = event.data.numberOfChannels
|
|
37
|
-
}
|
|
38
|
-
if (event.data.recordBitDepth) {
|
|
39
|
-
this.recordBitDepth = event.data.recordBitDepth
|
|
40
|
-
}
|
|
41
|
-
this.exportBitDepth =
|
|
42
|
-
event.data.exportBitDepth || this.recordBitDepth
|
|
43
|
-
|
|
44
|
-
// Handle position parameter for device switching
|
|
45
|
-
if (typeof event.data.position === 'number' && event.data.position > 0) {
|
|
46
|
-
this.currentPosition = event.data.position
|
|
47
|
-
if (this.enableLogging) {
|
|
48
|
-
console.log('AudioWorklet initialized with position:', this.currentPosition)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
break
|
|
52
|
-
|
|
53
|
-
case 'stop':
|
|
54
|
-
this.isRecording = false
|
|
55
|
-
if (this.currentChunk.length > 0) {
|
|
56
|
-
this.processChunk()
|
|
57
|
-
}
|
|
58
|
-
break
|
|
59
|
-
|
|
60
|
-
case 'pause':
|
|
61
|
-
// Just a placeholder for pause handling
|
|
62
|
-
break
|
|
63
|
-
|
|
64
|
-
case 'resume':
|
|
65
|
-
// Just a placeholder for resume handling
|
|
66
|
-
break
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
process(inputs, _outputs, _parameters) {
|
|
71
|
-
if (!this.isRecording) return true
|
|
72
|
-
const input = inputs[0]
|
|
73
|
-
if (input.length > 0) {
|
|
74
|
-
const newBuffer = new Float32Array(input[0])
|
|
75
|
-
this.currentChunk.push(newBuffer)
|
|
76
|
-
this.samplesSinceLastExport += newBuffer.length
|
|
77
|
-
|
|
78
|
-
if (this.samplesSinceLastExport >= this.exportIntervalSamples) {
|
|
79
|
-
this.processChunk()
|
|
80
|
-
this.samplesSinceLastExport = 0
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return true
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
mergeBuffers(bufferArray, recLength) {
|
|
87
|
-
const result = new Float32Array(recLength)
|
|
88
|
-
let offset = 0
|
|
89
|
-
for (let i = 0; i < bufferArray.length; i++) {
|
|
90
|
-
result.set(bufferArray[i], offset)
|
|
91
|
-
offset += bufferArray[i].length
|
|
92
|
-
}
|
|
93
|
-
return result
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Keep basic resampling for sample rate conversion
|
|
97
|
-
resample(samples, targetSampleRate) {
|
|
98
|
-
if (this.recordSampleRate === targetSampleRate) {
|
|
99
|
-
return samples
|
|
100
|
-
}
|
|
101
|
-
const resampledBuffer = new Float32Array(
|
|
102
|
-
Math.ceil(
|
|
103
|
-
(samples.length * targetSampleRate) / this.recordSampleRate
|
|
104
|
-
)
|
|
105
|
-
)
|
|
106
|
-
const ratio = this.recordSampleRate / targetSampleRate
|
|
107
|
-
let offset = 0
|
|
108
|
-
for (let i = 0; i < resampledBuffer.length; i++) {
|
|
109
|
-
const nextOffset = Math.floor((i + 1) * ratio)
|
|
110
|
-
let accum = 0
|
|
111
|
-
let count = 0
|
|
112
|
-
for (let j = offset; j < nextOffset && j < samples.length; j++) {
|
|
113
|
-
accum += samples[j]
|
|
114
|
-
count++
|
|
115
|
-
}
|
|
116
|
-
resampledBuffer[i] = count > 0 ? accum / count : 0
|
|
117
|
-
offset = nextOffset
|
|
118
|
-
}
|
|
119
|
-
return resampledBuffer
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Keep bit depth conversion if needed
|
|
123
|
-
convertBitDepth(input, targetBitDepth) {
|
|
124
|
-
if (targetBitDepth === 32) {
|
|
125
|
-
const output = new Int32Array(input.length)
|
|
126
|
-
for (let i = 0; i < input.length; i++) {
|
|
127
|
-
const s = Math.max(-1, Math.min(1, input[i]))
|
|
128
|
-
output[i] = s < 0 ? s * 0x80000000 : s * 0x7fffffff
|
|
129
|
-
}
|
|
130
|
-
return output
|
|
131
|
-
} else if (targetBitDepth === 16) {
|
|
132
|
-
const output = new Int16Array(input.length)
|
|
133
|
-
for (let i = 0; i < input.length; i++) {
|
|
134
|
-
const s = Math.max(-1, Math.min(1, input[i]))
|
|
135
|
-
output[i] = s < 0 ? s * 0x8000 : s * 0x7fff
|
|
136
|
-
}
|
|
137
|
-
return output
|
|
138
|
-
}
|
|
139
|
-
return input
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
processChunk() {
|
|
143
|
-
if (this.currentChunk.length === 0) return
|
|
144
|
-
|
|
145
|
-
// Merge buffers
|
|
146
|
-
const chunkLength = this.currentChunk.reduce(
|
|
147
|
-
(acc, buf) => acc + buf.length,
|
|
148
|
-
0
|
|
149
|
-
)
|
|
150
|
-
const mergedChunk = this.mergeBuffers(this.currentChunk, chunkLength)
|
|
151
|
-
|
|
152
|
-
// Resample if needed
|
|
153
|
-
const resampledChunk = this.resample(mergedChunk, this.exportSampleRate)
|
|
154
|
-
|
|
155
|
-
// Convert bit depth if needed
|
|
156
|
-
const finalBuffer =
|
|
157
|
-
this.recordBitDepth !== this.exportBitDepth
|
|
158
|
-
? this.convertBitDepth(resampledChunk, this.exportBitDepth)
|
|
159
|
-
: resampledChunk
|
|
160
|
-
|
|
161
|
-
// Calculate the duration in seconds
|
|
162
|
-
const chunkDuration = finalBuffer.length / this.exportSampleRate
|
|
163
|
-
|
|
164
|
-
// Send processed chunk with the current position
|
|
165
|
-
this.port.postMessage({
|
|
166
|
-
command: 'newData',
|
|
167
|
-
recordedData: finalBuffer,
|
|
168
|
-
sampleRate: this.exportSampleRate,
|
|
169
|
-
bitDepth: this.exportBitDepth,
|
|
170
|
-
numberOfChannels: this.numberOfChannels,
|
|
171
|
-
position: this.currentPosition,
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
// Update the position
|
|
175
|
-
this.currentPosition += chunkDuration
|
|
176
|
-
|
|
177
|
-
// Clear the current chunk
|
|
178
|
-
this.currentChunk = []
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
registerProcessor('recorder-processor', RecorderProcessor)
|
|
183
|
-
`;
|
|
184
|
-
//# sourceMappingURL=inlineAudioWebWorker.web.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inlineAudioWebWorker.web.js","sourceRoot":"","sources":["../../../src/workers/inlineAudioWebWorker.web.tsx"],"names":[],"mappings":";;;AAAA,sEAAsE;AACzD,QAAA,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkLnC,CAAA","sourcesContent":["// packages/expo-audio-stream/src/workers/inlineAudioWebWorker.web.tsx\nexport const InlineAudioWebWorker = `\nconst DEFAULT_BIT_DEPTH = 32\nconst DEFAULT_SAMPLE_RATE = 44100\n\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super()\n this.currentChunk = [] // Float32Array\n this.samplesSinceLastExport = 0\n this.recordSampleRate = DEFAULT_SAMPLE_RATE\n this.exportSampleRate = DEFAULT_SAMPLE_RATE\n this.recordBitDepth = DEFAULT_BIT_DEPTH\n this.exportBitDepth = DEFAULT_BIT_DEPTH\n this.numberOfChannels = 1\n this.isRecording = true\n this.port.onmessage = this.handleMessage.bind(this)\n this.enableLogging = false\n this.exportIntervalSamples = 0\n this.currentPosition = 0 // Track current position in seconds\n }\n\n handleMessage(event) {\n switch (event.data.command) {\n case 'init':\n this.enableLogging = event.data.enableLogging || false\n this.recordSampleRate = event.data.recordSampleRate\n this.exportSampleRate =\n event.data.exportSampleRate || event.data.recordSampleRate\n this.exportIntervalSamples =\n this.recordSampleRate * (event.data.interval / 1000)\n if (event.data.numberOfChannels) {\n this.numberOfChannels = event.data.numberOfChannels\n }\n if (event.data.recordBitDepth) {\n this.recordBitDepth = event.data.recordBitDepth\n }\n this.exportBitDepth =\n event.data.exportBitDepth || this.recordBitDepth\n \n // Handle position parameter for device switching\n if (typeof event.data.position === 'number' && event.data.position > 0) {\n this.currentPosition = event.data.position\n if (this.enableLogging) {\n console.log('AudioWorklet initialized with position:', this.currentPosition)\n }\n }\n break\n\n case 'stop':\n this.isRecording = false\n if (this.currentChunk.length > 0) {\n this.processChunk()\n }\n break\n \n case 'pause':\n // Just a placeholder for pause handling\n break\n \n case 'resume':\n // Just a placeholder for resume handling\n break\n }\n }\n\n process(inputs, _outputs, _parameters) {\n if (!this.isRecording) return true\n const input = inputs[0]\n if (input.length > 0) {\n const newBuffer = new Float32Array(input[0])\n this.currentChunk.push(newBuffer)\n this.samplesSinceLastExport += newBuffer.length\n\n if (this.samplesSinceLastExport >= this.exportIntervalSamples) {\n this.processChunk()\n this.samplesSinceLastExport = 0\n }\n }\n return true\n }\n\n mergeBuffers(bufferArray, recLength) {\n const result = new Float32Array(recLength)\n let offset = 0\n for (let i = 0; i < bufferArray.length; i++) {\n result.set(bufferArray[i], offset)\n offset += bufferArray[i].length\n }\n return result\n }\n\n // Keep basic resampling for sample rate conversion\n resample(samples, targetSampleRate) {\n if (this.recordSampleRate === targetSampleRate) {\n return samples\n }\n const resampledBuffer = new Float32Array(\n Math.ceil(\n (samples.length * targetSampleRate) / this.recordSampleRate\n )\n )\n const ratio = this.recordSampleRate / targetSampleRate\n let offset = 0\n for (let i = 0; i < resampledBuffer.length; i++) {\n const nextOffset = Math.floor((i + 1) * ratio)\n let accum = 0\n let count = 0\n for (let j = offset; j < nextOffset && j < samples.length; j++) {\n accum += samples[j]\n count++\n }\n resampledBuffer[i] = count > 0 ? accum / count : 0\n offset = nextOffset\n }\n return resampledBuffer\n }\n\n // Keep bit depth conversion if needed\n convertBitDepth(input, targetBitDepth) {\n if (targetBitDepth === 32) {\n const output = new Int32Array(input.length)\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]))\n output[i] = s < 0 ? s * 0x80000000 : s * 0x7fffffff\n }\n return output\n } else if (targetBitDepth === 16) {\n const output = new Int16Array(input.length)\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]))\n output[i] = s < 0 ? s * 0x8000 : s * 0x7fff\n }\n return output\n }\n return input\n }\n\n processChunk() {\n if (this.currentChunk.length === 0) return\n\n // Merge buffers\n const chunkLength = this.currentChunk.reduce(\n (acc, buf) => acc + buf.length,\n 0\n )\n const mergedChunk = this.mergeBuffers(this.currentChunk, chunkLength)\n\n // Resample if needed\n const resampledChunk = this.resample(mergedChunk, this.exportSampleRate)\n\n // Convert bit depth if needed\n const finalBuffer =\n this.recordBitDepth !== this.exportBitDepth\n ? this.convertBitDepth(resampledChunk, this.exportBitDepth)\n : resampledChunk\n\n // Calculate the duration in seconds\n const chunkDuration = finalBuffer.length / this.exportSampleRate\n \n // Send processed chunk with the current position\n this.port.postMessage({\n command: 'newData',\n recordedData: finalBuffer,\n sampleRate: this.exportSampleRate,\n bitDepth: this.exportBitDepth,\n numberOfChannels: this.numberOfChannels,\n position: this.currentPosition,\n })\n \n // Update the position\n this.currentPosition += chunkDuration\n\n // Clear the current chunk\n this.currentChunk = []\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor)\n`\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AudioAnalysis.types.js","sourceRoot":"","sources":["../../../src/AudioAnalysis/AudioAnalysis.types.ts"],"names":[],"mappings":"AAAA,sEAAsE","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/AudioAnalysis.types.ts\n\nimport { BitDepth, ConsoleLike } from '../ExpoAudioStream.types'\n\n/**\n * Represents the configuration for decoding audio data.\n */\nexport interface DecodingConfig {\n /** Target sample rate for decoded audio (Android and Web) */\n targetSampleRate?: number\n /** Target number of channels (Android and Web) */\n targetChannels?: number\n /** Target bit depth (Android and Web) */\n targetBitDepth?: BitDepth\n /** Whether to normalize audio levels (Android and Web) */\n normalizeAudio?: boolean\n}\n\n/**\n * Represents speech-related features extracted from audio.\n */\nexport interface SpeechFeatures {\n isActive: boolean // Whether speech is detected in this segment\n speakerId?: number // Optional speaker identification\n // Could add more speech-related features here like:\n // confidence: number\n // language?: string\n // sentiment?: number\n // etc.\n}\n\n/**\n * Represents various audio features extracted from an audio signal.\n */\nexport interface AudioFeatures {\n energy?: number // The infinite integral of the squared signal, representing the overall energy of the audio.\n mfcc?: number[] // Mel-frequency cepstral coefficients, describing the short-term power spectrum of a sound.\n rms?: number // Root mean square value, indicating the amplitude of the audio signal.\n minAmplitude?: number // Minimum amplitude value in the audio signal.\n maxAmplitude?: number // Maximum amplitude value in the audio signal.\n zcr?: number // Zero-crossing rate, indicating the rate at which the signal changes sign.\n spectralCentroid?: number // The center of mass of the spectrum, indicating the brightness of the sound.\n spectralFlatness?: number // Measure of the flatness of the spectrum, indicating how noise-like the signal is.\n spectralRolloff?: number // The frequency below which a specified percentage (usually 85%) of the total spectral energy lies.\n spectralBandwidth?: number // The width of the spectrum, indicating the range of frequencies present.\n chromagram?: number[] // Chromagram, representing the 12 different pitch classes of the audio.\n tempo?: number // Estimated tempo of the audio signal, measured in beats per minute (BPM).\n hnr?: number // Harmonics-to-noise ratio, indicating the proportion of harmonics to noise in the audio signal.\n melSpectrogram?: number[] // Mel-scaled spectrogram representation of the audio.\n spectralContrast?: number[] // Spectral contrast features representing the difference between peaks and valleys.\n tonnetz?: number[] // Tonal network features representing harmonic relationships.\n pitch?: number // Pitch of the audio signal, measured in Hertz (Hz).\n crc32?: number // crc32 checksum of the audio signal, used to verify the integrity of the audio.\n}\n\n/**\n * Options to specify which audio features to extract.\n * Note: Advanced features (spectral features, chromagram, pitch, etc.) are experimental,\n * especially during live recording, due to high processing requirements.\n */\nexport interface AudioFeaturesOptions {\n // Basic features - well optimized\n energy?: boolean\n rms?: boolean\n zcr?: boolean\n\n // Advanced features - experimental, may impact performance in live recording\n mfcc?: boolean\n spectralCentroid?: boolean\n spectralFlatness?: boolean\n spectralRolloff?: boolean\n spectralBandwidth?: boolean\n chromagram?: boolean\n tempo?: boolean\n hnr?: boolean\n melSpectrogram?: boolean\n spectralContrast?: boolean\n tonnetz?: boolean\n pitch?: boolean\n\n // Utility\n crc32?: boolean\n}\n\n/**\n * Represents a single data point in the audio analysis.\n */\nexport interface DataPoint {\n id: number\n amplitude: number // Peak amplitude for the segment\n rms: number // Root mean square value\n dB: number // dBFS (decibels relative to full scale) computed from RMS value\n silent: boolean // Always computed\n features?: AudioFeatures\n speech?: SpeechFeatures\n startTime?: number\n endTime?: number\n // start / end position in bytes\n startPosition?: number\n endPosition?: number\n // number of audio samples for this point (samples size depends on bit depth)\n samples?: number\n}\n\n/**\n * Represents the complete data from the audio analysis.\n */\nexport interface AudioAnalysis {\n segmentDurationMs: number // Duration of each segment in milliseconds\n durationMs: number // Duration of the audio in milliseconds\n /**\n * Bit depth used for audio analysis processing.\n *\n * **Important**: This represents the internal processing bit depth, which may differ\n * from the recording bit depth. Audio is typically converted to 32-bit float for\n * analysis to ensure precision in calculations, regardless of the original recording format.\n *\n * Platform behavior:\n * - iOS: Always 32 (float processing)\n * - Android: Always 32 (float processing)\n * - Web: Always 32 (Web Audio API standard)\n *\n * The actual recorded file will maintain the requested bit depth (8, 16, or 32).\n */\n bitDepth: number\n samples: number // Size of the audio in bytes\n numberOfChannels: number // Number of audio channels\n sampleRate: number // Sample rate of the audio\n dataPoints: DataPoint[] // Array of data points from the analysis.\n amplitudeRange: {\n min: number\n max: number\n }\n rmsRange: {\n min: number\n max: number\n }\n extractionTimeMs: number // Time taken to extract/process the analysis in milliseconds\n // TODO: speaker changes into a broader speech analysis section\n speechAnalysis?: {\n speakerChanges: {\n timestamp: number\n speakerId: number\n }[]\n // Could add more speech analysis data here like:\n // dominantSpeaker?: number\n // totalSpeechDuration?: number\n // speakerStats?: { [speakerId: number]: { duration: number, segments: number } }\n }\n}\n\n/**\n * Options for specifying a time range within an audio file.\n */\nexport interface AudioRangeOptions {\n /** Start time in milliseconds */\n startTimeMs?: number\n /** End time in milliseconds */\n endTimeMs?: number\n}\n\n/**\n * Options for generating a quick preview of audio waveform.\n * This is optimized for UI rendering with a specified number of points.\n */\nexport interface PreviewOptions extends AudioRangeOptions {\n /** URI of the audio file to analyze */\n fileUri: string\n /**\n * Total number of points to generate for the preview.\n * @default 100\n */\n numberOfPoints?: number\n /**\n * Optional logger for debugging.\n */\n logger?: ConsoleLike\n /**\n * Optional configuration for decoding the audio file.\n * Defaults to:\n * - targetSampleRate: undefined (keep original)\n * - targetChannels: undefined (keep original)\n * - targetBitDepth: 16\n * - normalizeAudio: false\n */\n decodingOptions?: DecodingConfig\n}\n\n/**\n * Options for mel-spectrogram extraction\n *\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions.\n */\nexport interface ExtractMelSpectrogramOptions {\n fileUri?: string // Path to audio file\n arrayBuffer?: ArrayBuffer // Raw audio buffer\n windowSizeMs: number // Window size in ms (e.g., 25)\n hopLengthMs: number // Hop length in ms (e.g., 10)\n nMels: number // Number of mel filters (e.g., 60)\n fMin?: number // Min frequency (default: 0)\n fMax?: number // Max frequency (default: sampleRate / 2)\n windowType?: 'hann' | 'hamming' // Window function (default: 'hann')\n normalize?: boolean // Mean normalization (default: false)\n logScale?: boolean // Log scaling of mel energies (default: true)\n decodingOptions?: DecodingConfig // Audio decoding settings\n startTimeMs?: number // Optional start time\n endTimeMs?: number // Optional end time\n logger?: ConsoleLike\n}\n\n/**\n * Return type for mel spectrogram extraction\n *\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions.\n */\nexport interface MelSpectrogram {\n spectrogram: number[][] // 2D array [time][mel]\n sampleRate: number // Audio sample rate\n nMels: number // Number of mel filters\n timeSteps: number // Number of time frames\n durationMs: number // Audio duration in ms\n}\n"]}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import ExpoAudioStreamModule from '../ExpoAudioStreamModule';
|
|
2
|
-
import { isWeb } from '../constants';
|
|
3
|
-
import { processAudioBuffer } from '../utils/audioProcessing';
|
|
4
|
-
import { cleanNativeOptions } from '../utils/cleanNativeOptions';
|
|
5
|
-
import { convertPCMToFloat32 } from '../utils/convertPCMToFloat32';
|
|
6
|
-
import crc32 from '../utils/crc32';
|
|
7
|
-
import { getWavFileInfo } from '../utils/getWavFileInfo';
|
|
8
|
-
import { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web';
|
|
9
|
-
function calculateCRC32ForDataPoint(data) {
|
|
10
|
-
// Convert float array to byte array for CRC32
|
|
11
|
-
const byteArray = new Uint8Array(data.length * 4);
|
|
12
|
-
const dataView = new DataView(byteArray.buffer);
|
|
13
|
-
for (let i = 0; i < data.length; i++) {
|
|
14
|
-
dataView.setFloat32(i * 4, data[i], true);
|
|
15
|
-
}
|
|
16
|
-
return crc32.buf(byteArray);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Extracts detailed audio analysis from the specified audio file or buffer.
|
|
20
|
-
* Supports either time-based or byte-based ranges for flexibility in analysis.
|
|
21
|
-
*
|
|
22
|
-
* @param props - The options for extraction, including file URI, ranges, and decoding settings.
|
|
23
|
-
* @returns A promise that resolves to the audio analysis data.
|
|
24
|
-
* @throws {Error} If both time and byte ranges are provided or if required parameters are missing.
|
|
25
|
-
*/
|
|
26
|
-
export async function extractAudioAnalysis(props) {
|
|
27
|
-
const { fileUri, arrayBuffer, decodingOptions, logger, segmentDurationMs = 100, features, } = props;
|
|
28
|
-
if (isWeb) {
|
|
29
|
-
try {
|
|
30
|
-
// Create AudioContext here
|
|
31
|
-
const audioContext = new (window.AudioContext ||
|
|
32
|
-
window.webkitAudioContext)({
|
|
33
|
-
sampleRate: decodingOptions?.targetSampleRate ?? 16000,
|
|
34
|
-
});
|
|
35
|
-
try {
|
|
36
|
-
const processedBuffer = await processAudioBuffer({
|
|
37
|
-
arrayBuffer,
|
|
38
|
-
fileUri,
|
|
39
|
-
targetSampleRate: decodingOptions?.targetSampleRate ?? 16000,
|
|
40
|
-
targetChannels: decodingOptions?.targetChannels ?? 1,
|
|
41
|
-
normalizeAudio: decodingOptions?.normalizeAudio ?? false,
|
|
42
|
-
startTimeMs: 'startTimeMs' in props ? props.startTimeMs : undefined,
|
|
43
|
-
endTimeMs: 'endTimeMs' in props ? props.endTimeMs : undefined,
|
|
44
|
-
position: 'position' in props ? props.position : undefined,
|
|
45
|
-
length: 'length' in props ? props.length : undefined,
|
|
46
|
-
audioContext, // Pass the context we created
|
|
47
|
-
logger,
|
|
48
|
-
});
|
|
49
|
-
const channelData = processedBuffer.buffer.getChannelData(0);
|
|
50
|
-
// Create and initialize the worker
|
|
51
|
-
const blob = new Blob([InlineFeaturesExtractor], {
|
|
52
|
-
type: 'application/javascript',
|
|
53
|
-
});
|
|
54
|
-
const workerUrl = URL.createObjectURL(blob);
|
|
55
|
-
const worker = new Worker(workerUrl);
|
|
56
|
-
return new Promise((resolve, reject) => {
|
|
57
|
-
worker.onmessage = (event) => {
|
|
58
|
-
if (event.data.error) {
|
|
59
|
-
reject(new Error(event.data.error));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const result = event.data.result;
|
|
63
|
-
// Calculate CRC32 after worker completes if requested
|
|
64
|
-
if (features?.crc32) {
|
|
65
|
-
const samplesPerSegment = Math.floor((processedBuffer.sampleRate *
|
|
66
|
-
segmentDurationMs) /
|
|
67
|
-
1000);
|
|
68
|
-
result.dataPoints = result.dataPoints.map((point, index) => {
|
|
69
|
-
const startSample = index * samplesPerSegment;
|
|
70
|
-
const segmentData = channelData.slice(startSample, startSample + samplesPerSegment);
|
|
71
|
-
return {
|
|
72
|
-
...point,
|
|
73
|
-
features: {
|
|
74
|
-
...point.features,
|
|
75
|
-
crc32: calculateCRC32ForDataPoint(segmentData),
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
URL.revokeObjectURL(workerUrl);
|
|
81
|
-
worker.terminate();
|
|
82
|
-
resolve(result);
|
|
83
|
-
};
|
|
84
|
-
worker.onerror = (error) => {
|
|
85
|
-
URL.revokeObjectURL(workerUrl);
|
|
86
|
-
worker.terminate();
|
|
87
|
-
reject(error);
|
|
88
|
-
};
|
|
89
|
-
worker.postMessage({
|
|
90
|
-
channelData,
|
|
91
|
-
sampleRate: processedBuffer.sampleRate,
|
|
92
|
-
segmentDurationMs,
|
|
93
|
-
bitDepth: decodingOptions?.targetBitDepth ?? 32,
|
|
94
|
-
numberOfChannels: processedBuffer.channels,
|
|
95
|
-
fullAudioDurationMs: processedBuffer.durationMs,
|
|
96
|
-
// enableLogging: !!logger,
|
|
97
|
-
features,
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
finally {
|
|
102
|
-
await audioContext.close();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
logger?.error('Failed to process audio:', error);
|
|
107
|
-
throw error;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
// Strip non-serializable fields — logger and arrayBuffer cause
|
|
112
|
-
// "Cannot convert '[object Object]' to a Kotlin type" on Android.
|
|
113
|
-
const { logger: _logger, arrayBuffer: _arrayBuffer, ...nativeOptions } = props;
|
|
114
|
-
// Clean undefined values to avoid Android Kotlin bridge crash
|
|
115
|
-
return await ExpoAudioStreamModule.extractAudioAnalysis(cleanNativeOptions(nativeOptions));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Analyzes WAV files without decoding, preserving original PCM values.
|
|
120
|
-
* Use this function when you need to ensure the analysis matches other software by avoiding any transformations.
|
|
121
|
-
*
|
|
122
|
-
* @param props - The options for WAV analysis, including file URI and range.
|
|
123
|
-
* @returns A promise that resolves to the audio analysis data.
|
|
124
|
-
*/
|
|
125
|
-
export const extractRawWavAnalysis = async ({ fileUri, segmentDurationMs = 100, // Default to 100ms
|
|
126
|
-
arrayBuffer, bitDepth, durationMs, sampleRate, numberOfChannels, features, logger, position = 0, length, }) => {
|
|
127
|
-
if (isWeb) {
|
|
128
|
-
if (!arrayBuffer && !fileUri) {
|
|
129
|
-
throw new Error('Either arrayBuffer or fileUri must be provided');
|
|
130
|
-
}
|
|
131
|
-
if (!arrayBuffer) {
|
|
132
|
-
logger?.log(`fetching fileUri`, fileUri);
|
|
133
|
-
const response = await fetch(fileUri);
|
|
134
|
-
if (!response.ok) {
|
|
135
|
-
throw new Error(`Failed to fetch fileUri: ${response.statusText}`);
|
|
136
|
-
}
|
|
137
|
-
arrayBuffer = await response.arrayBuffer();
|
|
138
|
-
logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer);
|
|
139
|
-
}
|
|
140
|
-
// Create a new copy of the ArrayBuffer to avoid detachment issues
|
|
141
|
-
const bufferCopy = arrayBuffer.slice(0);
|
|
142
|
-
logger?.log(`extractAudioAnalysis bitDepth=${bitDepth} len=${bufferCopy.byteLength}`, bufferCopy.slice(0, 100));
|
|
143
|
-
let actualBitDepth = bitDepth;
|
|
144
|
-
if (!actualBitDepth) {
|
|
145
|
-
logger?.log(`extractAudioAnalysis bitDepth not provided -- getting wav file info`);
|
|
146
|
-
const fileInfo = await getWavFileInfo(bufferCopy);
|
|
147
|
-
actualBitDepth = fileInfo.bitDepth;
|
|
148
|
-
}
|
|
149
|
-
logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`);
|
|
150
|
-
const { pcmValues: channelData, min, max, } = await convertPCMToFloat32({
|
|
151
|
-
buffer: arrayBuffer,
|
|
152
|
-
bitDepth: actualBitDepth,
|
|
153
|
-
});
|
|
154
|
-
logger?.log(`extractAudioAnalysis convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`);
|
|
155
|
-
// Apply position and length constraints to channelData if specified
|
|
156
|
-
const startIndex = position;
|
|
157
|
-
const endIndex = length ? startIndex + length : channelData.length;
|
|
158
|
-
const constrainedChannelData = channelData.slice(startIndex, endIndex);
|
|
159
|
-
return new Promise((resolve, reject) => {
|
|
160
|
-
const blob = new Blob([InlineFeaturesExtractor], {
|
|
161
|
-
type: 'application/javascript',
|
|
162
|
-
});
|
|
163
|
-
const url = URL.createObjectURL(blob);
|
|
164
|
-
const worker = new Worker(url);
|
|
165
|
-
worker.onmessage = (event) => {
|
|
166
|
-
resolve(event.data.result);
|
|
167
|
-
};
|
|
168
|
-
worker.onerror = (error) => {
|
|
169
|
-
reject(error);
|
|
170
|
-
};
|
|
171
|
-
worker.postMessage({
|
|
172
|
-
command: 'process',
|
|
173
|
-
channelData: constrainedChannelData,
|
|
174
|
-
sampleRate,
|
|
175
|
-
segmentDurationMs,
|
|
176
|
-
logger,
|
|
177
|
-
bitDepth,
|
|
178
|
-
fullAudioDurationMs: durationMs,
|
|
179
|
-
numberOfChannels,
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
if (!fileUri) {
|
|
185
|
-
throw new Error('fileUri is required');
|
|
186
|
-
}
|
|
187
|
-
logger?.log(`extractAudioAnalysis`, {
|
|
188
|
-
fileUri,
|
|
189
|
-
segmentDurationMs,
|
|
190
|
-
});
|
|
191
|
-
const res = await ExpoAudioStreamModule.extractAudioAnalysis({
|
|
192
|
-
fileUri,
|
|
193
|
-
segmentDurationMs,
|
|
194
|
-
features,
|
|
195
|
-
position,
|
|
196
|
-
length,
|
|
197
|
-
});
|
|
198
|
-
logger?.log(`extractAudioAnalysis`, res);
|
|
199
|
-
return res;
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
//# sourceMappingURL=extractAudioAnalysis.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extractAudioAnalysis.js","sourceRoot":"","sources":["../../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts\n/**\n * This module provides functions for extracting and analyzing audio data.\n * - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.\n * - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.\n * - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.\n */\nimport { ConsoleLike } from '../ExpoAudioStream.types'\nimport ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { isWeb } from '../constants'\nimport {\n AudioAnalysis,\n AudioFeaturesOptions,\n DataPoint,\n DecodingConfig,\n} from './AudioAnalysis.types'\nimport { processAudioBuffer } from '../utils/audioProcessing'\nimport { cleanNativeOptions } from '../utils/cleanNativeOptions'\nimport { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'\nimport crc32 from '../utils/crc32'\nimport { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'\nimport { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'\n\nfunction calculateCRC32ForDataPoint(data: Float32Array): number {\n // Convert float array to byte array for CRC32\n const byteArray = new Uint8Array(data.length * 4)\n const dataView = new DataView(byteArray.buffer)\n\n for (let i = 0; i < data.length; i++) {\n dataView.setFloat32(i * 4, data[i], true)\n }\n\n return crc32.buf(byteArray)\n}\n\nexport interface ExtractWavAudioAnalysisProps {\n fileUri?: string // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo\n arrayBuffer?: ArrayBuffer\n bitDepth?: number\n durationMs?: number\n sampleRate?: number\n numberOfChannels?: number\n position?: number // Optional number of bytes to skip. Default is 0\n length?: number // Optional number of bytes to read.\n segmentDurationMs?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions\n featuresExtratorUrl?: string\n logger?: ConsoleLike\n decodingOptions?: DecodingConfig\n}\n\n// Define base options interface with common properties\ninterface BaseExtractOptions {\n fileUri?: string\n arrayBuffer?: ArrayBuffer\n /**\n * Duration of each analysis segment in milliseconds. Defaults to 100ms if not specified.\n */\n segmentDurationMs?: number\n features?: AudioFeaturesOptions\n decodingOptions?: DecodingConfig\n logger?: ConsoleLike\n}\n\n// Time-based range options\ninterface TimeRangeOptions extends BaseExtractOptions {\n startTimeMs?: number\n endTimeMs?: number\n position?: never\n length?: never\n}\n\n// Byte-based range options\ninterface ByteRangeOptions extends BaseExtractOptions {\n position?: number\n length?: number\n startTimeMs?: never\n endTimeMs?: never\n}\n\n/**\n * Options for extracting audio analysis.\n * - For time-based analysis, provide `startTimeMs` and `endTimeMs`.\n * - For byte-based analysis, provide `position` and `length`.\n * - Do not mix time and byte ranges.\n */\nexport type ExtractAudioAnalysisProps = TimeRangeOptions | ByteRangeOptions\n\n/**\n * Extracts detailed audio analysis from the specified audio file or buffer.\n * Supports either time-based or byte-based ranges for flexibility in analysis.\n *\n * @param props - The options for extraction, including file URI, ranges, and decoding settings.\n * @returns A promise that resolves to the audio analysis data.\n * @throws {Error} If both time and byte ranges are provided or if required parameters are missing.\n */\nexport async function extractAudioAnalysis(\n props: ExtractAudioAnalysisProps\n): Promise<AudioAnalysis> {\n const {\n fileUri,\n arrayBuffer,\n decodingOptions,\n logger,\n segmentDurationMs = 100,\n features,\n } = props\n\n if (isWeb) {\n try {\n // Create AudioContext here\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)({\n sampleRate: decodingOptions?.targetSampleRate ?? 16000,\n })\n\n try {\n const processedBuffer = await processAudioBuffer({\n arrayBuffer,\n fileUri,\n targetSampleRate:\n decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n startTimeMs:\n 'startTimeMs' in props ? props.startTimeMs : undefined,\n endTimeMs:\n 'endTimeMs' in props ? props.endTimeMs : undefined,\n position: 'position' in props ? props.position : undefined,\n length: 'length' in props ? props.length : undefined,\n audioContext, // Pass the context we created\n logger,\n })\n\n const channelData = processedBuffer.buffer.getChannelData(0)\n\n // Create and initialize the worker\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const workerUrl = URL.createObjectURL(blob)\n const worker = new Worker(workerUrl)\n\n return new Promise((resolve, reject) => {\n worker.onmessage = (event) => {\n if (event.data.error) {\n reject(new Error(event.data.error))\n return\n }\n\n const result: AudioAnalysis = event.data.result\n // Calculate CRC32 after worker completes if requested\n if (features?.crc32) {\n const samplesPerSegment = Math.floor(\n (processedBuffer.sampleRate *\n segmentDurationMs) /\n 1000\n )\n\n result.dataPoints = result.dataPoints.map(\n (point: DataPoint, index: number) => {\n const startSample =\n index * samplesPerSegment\n const segmentData = channelData.slice(\n startSample,\n startSample + samplesPerSegment\n )\n\n return {\n ...point,\n features: {\n ...point.features,\n crc32: calculateCRC32ForDataPoint(\n segmentData\n ),\n },\n }\n }\n )\n }\n\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n resolve(result)\n }\n\n worker.onerror = (error) => {\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n reject(error)\n }\n\n worker.postMessage({\n channelData,\n sampleRate: processedBuffer.sampleRate,\n segmentDurationMs,\n bitDepth: decodingOptions?.targetBitDepth ?? 32,\n numberOfChannels: processedBuffer.channels,\n fullAudioDurationMs: processedBuffer.durationMs,\n // enableLogging: !!logger,\n features,\n })\n })\n } finally {\n await audioContext.close()\n }\n } catch (error) {\n logger?.error('Failed to process audio:', error)\n throw error\n }\n } else {\n // Strip non-serializable fields — logger and arrayBuffer cause\n // \"Cannot convert '[object Object]' to a Kotlin type\" on Android.\n const {\n logger: _logger,\n arrayBuffer: _arrayBuffer,\n ...nativeOptions\n } = props\n // Clean undefined values to avoid Android Kotlin bridge crash\n return await ExpoAudioStreamModule.extractAudioAnalysis(\n cleanNativeOptions(nativeOptions)\n )\n }\n}\n\n/**\n * Analyzes WAV files without decoding, preserving original PCM values.\n * Use this function when you need to ensure the analysis matches other software by avoiding any transformations.\n *\n * @param props - The options for WAV analysis, including file URI and range.\n * @returns A promise that resolves to the audio analysis data.\n */\nexport const extractRawWavAnalysis = async ({\n fileUri,\n segmentDurationMs = 100, // Default to 100ms\n arrayBuffer,\n bitDepth,\n durationMs,\n sampleRate,\n numberOfChannels,\n features,\n logger,\n position = 0,\n length,\n}: ExtractWavAudioAnalysisProps): Promise<AudioAnalysis> => {\n if (isWeb) {\n if (!arrayBuffer && !fileUri) {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n if (!arrayBuffer) {\n logger?.log(`fetching fileUri`, fileUri)\n const response = await fetch(fileUri!)\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch fileUri: ${response.statusText}`\n )\n }\n\n arrayBuffer = await response.arrayBuffer()\n logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)\n }\n\n // Create a new copy of the ArrayBuffer to avoid detachment issues\n const bufferCopy = arrayBuffer.slice(0)\n logger?.log(\n `extractAudioAnalysis bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,\n bufferCopy.slice(0, 100)\n )\n\n let actualBitDepth = bitDepth\n if (!actualBitDepth) {\n logger?.log(\n `extractAudioAnalysis bitDepth not provided -- getting wav file info`\n )\n const fileInfo = await getWavFileInfo(bufferCopy)\n actualBitDepth = fileInfo.bitDepth\n }\n logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)\n\n const {\n pcmValues: channelData,\n min,\n max,\n } = await convertPCMToFloat32({\n buffer: arrayBuffer,\n bitDepth: actualBitDepth,\n })\n logger?.log(\n `extractAudioAnalysis convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`\n )\n\n // Apply position and length constraints to channelData if specified\n const startIndex = position\n const endIndex = length ? startIndex + length : channelData.length\n const constrainedChannelData = channelData.slice(startIndex, endIndex)\n\n return new Promise((resolve, reject) => {\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const url = URL.createObjectURL(blob)\n const worker = new Worker(url)\n\n worker.onmessage = (event) => {\n resolve(event.data.result)\n }\n\n worker.onerror = (error) => {\n reject(error)\n }\n\n worker.postMessage({\n command: 'process',\n channelData: constrainedChannelData,\n sampleRate,\n segmentDurationMs,\n logger,\n bitDepth,\n fullAudioDurationMs: durationMs,\n numberOfChannels,\n })\n })\n } else {\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n logger?.log(`extractAudioAnalysis`, {\n fileUri,\n segmentDurationMs,\n })\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n segmentDurationMs,\n features,\n position,\n length,\n })\n logger?.log(`extractAudioAnalysis`, res)\n return res\n }\n}\n"]}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import ExpoAudioStreamModule from '../ExpoAudioStreamModule';
|
|
2
|
-
import { isWeb } from '../constants';
|
|
3
|
-
import { cleanNativeOptions } from '../utils/cleanNativeOptions';
|
|
4
|
-
export const extractAudioData = async (props) => {
|
|
5
|
-
if (isWeb) {
|
|
6
|
-
// Web implementation handles logger natively in ExpoAudioStreamModule.ts
|
|
7
|
-
return await ExpoAudioStreamModule.extractAudioData(props);
|
|
8
|
-
}
|
|
9
|
-
// Native: only pass serializable fields — logger causes crash on Android
|
|
10
|
-
const { logger: _logger, ...nativeOptions } = props;
|
|
11
|
-
// Clean undefined values to avoid Android Kotlin bridge crash
|
|
12
|
-
return await ExpoAudioStreamModule.extractAudioData(cleanNativeOptions(nativeOptions));
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=extractAudioData.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extractAudioData.js","sourceRoot":"","sources":["../../../src/AudioAnalysis/extractAudioData.ts"],"names":[],"mappings":"AACA,OAAO,qBAAqB,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,KAA8B,EAAE,EAAE;IACrE,IAAI,KAAK,EAAE,CAAC;QACR,yEAAyE;QACzE,OAAO,MAAM,qBAAqB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;IAC9D,CAAC;IACD,yEAAyE;IACzE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE,GAAG,KAAK,CAAA;IACnD,8DAA8D;IAC9D,OAAO,MAAM,qBAAqB,CAAC,gBAAgB,CAC/C,kBAAkB,CAAC,aAAa,CAAC,CACpC,CAAA;AACL,CAAC,CAAA","sourcesContent":["import { ExtractAudioDataOptions } from '../ExpoAudioStream.types'\nimport ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { isWeb } from '../constants'\nimport { cleanNativeOptions } from '../utils/cleanNativeOptions'\n\nexport const extractAudioData = async (props: ExtractAudioDataOptions) => {\n if (isWeb) {\n // Web implementation handles logger natively in ExpoAudioStreamModule.ts\n return await ExpoAudioStreamModule.extractAudioData(props)\n }\n // Native: only pass serializable fields — logger causes crash on Android\n const { logger: _logger, ...nativeOptions } = props\n // Clean undefined values to avoid Android Kotlin bridge crash\n return await ExpoAudioStreamModule.extractAudioData(\n cleanNativeOptions(nativeOptions)\n )\n}\n"]}
|