@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,185 @@
|
|
|
1
|
+
#!/usr/bin/env swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import AVFoundation
|
|
5
|
+
|
|
6
|
+
// Integration test for Buffer Duration feature
|
|
7
|
+
// This tests the ACTUAL behavior of AVAudioEngine with different buffer sizes
|
|
8
|
+
|
|
9
|
+
print("🧪 Buffer Duration Integration Test")
|
|
10
|
+
print("===================================\n")
|
|
11
|
+
|
|
12
|
+
class BufferDurationTest {
|
|
13
|
+
let audioEngine = AVAudioEngine()
|
|
14
|
+
var results: [(name: String, passed: Bool, message: String)] = []
|
|
15
|
+
|
|
16
|
+
func runAllTests() {
|
|
17
|
+
testDefaultBufferSize()
|
|
18
|
+
testCustomBufferSizes()
|
|
19
|
+
testBufferSizeLimits()
|
|
20
|
+
printResults()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func testDefaultBufferSize() {
|
|
24
|
+
print("Test 1: Default Buffer Size (1024 frames requested)")
|
|
25
|
+
print("-------------------------------------------------")
|
|
26
|
+
|
|
27
|
+
let inputNode = audioEngine.inputNode
|
|
28
|
+
let expectation = DispatchSemaphore(value: 0)
|
|
29
|
+
var receivedFrames: AVAudioFrameCount = 0
|
|
30
|
+
|
|
31
|
+
inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputNode.inputFormat(forBus: 0)) { buffer, _ in
|
|
32
|
+
receivedFrames = buffer.frameLength
|
|
33
|
+
expectation.signal()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
audioEngine.prepare()
|
|
37
|
+
do {
|
|
38
|
+
try audioEngine.start()
|
|
39
|
+
_ = expectation.wait(timeout: .now() + 2)
|
|
40
|
+
audioEngine.stop()
|
|
41
|
+
} catch {
|
|
42
|
+
print("Error: \(error)")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
inputNode.removeTap(onBus: 0)
|
|
46
|
+
|
|
47
|
+
// iOS enforces minimum of ~4800 frames
|
|
48
|
+
let passed = receivedFrames >= 4800
|
|
49
|
+
results.append((
|
|
50
|
+
name: "Default Buffer Size",
|
|
51
|
+
passed: passed,
|
|
52
|
+
message: "Requested: 1024, Received: \(receivedFrames) frames (iOS minimum: ~4800)"
|
|
53
|
+
))
|
|
54
|
+
|
|
55
|
+
print("✓ Requested: 1024 frames")
|
|
56
|
+
print("✓ Received: \(receivedFrames) frames")
|
|
57
|
+
print("✓ iOS enforces minimum buffer size\n")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func testCustomBufferSizes() {
|
|
61
|
+
print("Test 2: Custom Buffer Sizes")
|
|
62
|
+
print("---------------------------")
|
|
63
|
+
|
|
64
|
+
let inputNode = audioEngine.inputNode
|
|
65
|
+
let sampleRate = inputNode.inputFormat(forBus: 0).sampleRate
|
|
66
|
+
|
|
67
|
+
let testCases: [(duration: Double, name: String)] = [
|
|
68
|
+
(0.01, "10ms"),
|
|
69
|
+
(0.05, "50ms"),
|
|
70
|
+
(0.1, "100ms"),
|
|
71
|
+
(0.2, "200ms"),
|
|
72
|
+
(0.5, "500ms")
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
for testCase in testCases {
|
|
76
|
+
let requestedFrames = AVAudioFrameCount(testCase.duration * sampleRate)
|
|
77
|
+
let expectation = DispatchSemaphore(value: 0)
|
|
78
|
+
var receivedFrames: AVAudioFrameCount = 0
|
|
79
|
+
|
|
80
|
+
inputNode.removeTap(onBus: 0)
|
|
81
|
+
inputNode.installTap(onBus: 0, bufferSize: requestedFrames, format: inputNode.inputFormat(forBus: 0)) { buffer, _ in
|
|
82
|
+
receivedFrames = buffer.frameLength
|
|
83
|
+
expectation.signal()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
do {
|
|
87
|
+
try audioEngine.start()
|
|
88
|
+
_ = expectation.wait(timeout: .now() + 2)
|
|
89
|
+
audioEngine.stop()
|
|
90
|
+
} catch {
|
|
91
|
+
print("Error: \(error)")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let expectedFrames: AVAudioFrameCount = requestedFrames < 4800 ? 4800 : requestedFrames
|
|
95
|
+
let tolerance: AVAudioFrameCount = expectedFrames > 10000 ? AVAudioFrameCount(Double(expectedFrames) * 0.2) : 100
|
|
96
|
+
let passed = abs(Int32(receivedFrames) - Int32(expectedFrames)) <= Int32(tolerance)
|
|
97
|
+
|
|
98
|
+
results.append((
|
|
99
|
+
name: "Buffer \(testCase.name)",
|
|
100
|
+
passed: passed,
|
|
101
|
+
message: "Requested: \(requestedFrames), Expected: \(expectedFrames), Received: \(receivedFrames)"
|
|
102
|
+
))
|
|
103
|
+
|
|
104
|
+
print(" \(testCase.name): Requested \(requestedFrames) → Received \(receivedFrames) frames")
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
inputNode.removeTap(onBus: 0)
|
|
108
|
+
print()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func testBufferSizeLimits() {
|
|
112
|
+
print("Test 3: Buffer Size Limits")
|
|
113
|
+
print("--------------------------")
|
|
114
|
+
|
|
115
|
+
let inputNode = audioEngine.inputNode
|
|
116
|
+
|
|
117
|
+
let extremeCases: [(size: AVAudioFrameCount, name: String)] = [
|
|
118
|
+
(100, "Very small (100 frames)"),
|
|
119
|
+
(50000, "Very large (50000 frames)")
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
for testCase in extremeCases {
|
|
123
|
+
let expectation = DispatchSemaphore(value: 0)
|
|
124
|
+
var receivedFrames: AVAudioFrameCount = 0
|
|
125
|
+
|
|
126
|
+
inputNode.removeTap(onBus: 0)
|
|
127
|
+
inputNode.installTap(onBus: 0, bufferSize: testCase.size, format: inputNode.inputFormat(forBus: 0)) { buffer, _ in
|
|
128
|
+
receivedFrames = buffer.frameLength
|
|
129
|
+
expectation.signal()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
do {
|
|
133
|
+
try audioEngine.start()
|
|
134
|
+
_ = expectation.wait(timeout: .now() + 2)
|
|
135
|
+
audioEngine.stop()
|
|
136
|
+
} catch {
|
|
137
|
+
print("Error: \(error)")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let passed = receivedFrames >= 4800 && receivedFrames <= 50000
|
|
141
|
+
results.append((
|
|
142
|
+
name: testCase.name,
|
|
143
|
+
passed: passed,
|
|
144
|
+
message: "Requested: \(testCase.size), Received: \(receivedFrames)"
|
|
145
|
+
))
|
|
146
|
+
|
|
147
|
+
print(" \(testCase.name): \(testCase.size) → \(receivedFrames) frames")
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
inputNode.removeTap(onBus: 0)
|
|
151
|
+
print()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
func printResults() {
|
|
155
|
+
print("📊 Test Results")
|
|
156
|
+
print("===============")
|
|
157
|
+
|
|
158
|
+
let passed = results.filter { $0.passed }.count
|
|
159
|
+
let total = results.count
|
|
160
|
+
|
|
161
|
+
for result in results {
|
|
162
|
+
let status = result.passed ? "✅" : "❌"
|
|
163
|
+
print("\(status) \(result.name)")
|
|
164
|
+
print(" \(result.message)")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
print("\nSummary: \(passed)/\(total) tests passed")
|
|
168
|
+
|
|
169
|
+
if passed == total {
|
|
170
|
+
print("🎉 All tests passed!")
|
|
171
|
+
} else {
|
|
172
|
+
print("⚠️ Some tests failed")
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
print("\n📝 Key Findings:")
|
|
176
|
+
print("- iOS AVAudioEngine enforces a minimum buffer size of ~4800 frames")
|
|
177
|
+
print("- Requests below 4800 frames are ignored")
|
|
178
|
+
print("- Larger buffer sizes generally work as requested")
|
|
179
|
+
print("- Buffer accumulation is needed for small buffer durations")
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Run the test
|
|
184
|
+
let test = BufferDurationTest()
|
|
185
|
+
test.runAllTests()
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import AVFoundation
|
|
5
|
+
|
|
6
|
+
// Integration test for Compressed-Only Output (Issue #244)
|
|
7
|
+
// This tests that when primary output is disabled and compressed is enabled,
|
|
8
|
+
// the compressed file info is properly returned in the result
|
|
9
|
+
|
|
10
|
+
print("🧪 Compressed-Only Output Integration Test (Issue #244)")
|
|
11
|
+
print("=====================================================\n")
|
|
12
|
+
|
|
13
|
+
// Add the parent directory to the module search path
|
|
14
|
+
let srcPath = URL(fileURLWithPath: #file)
|
|
15
|
+
.deletingLastPathComponent()
|
|
16
|
+
.deletingLastPathComponent()
|
|
17
|
+
.deletingLastPathComponent()
|
|
18
|
+
.path
|
|
19
|
+
|
|
20
|
+
// Import the module
|
|
21
|
+
#if canImport(AudioStudio)
|
|
22
|
+
import AudioStudio
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
// Helper to load Swift files
|
|
26
|
+
func loadSwiftFile(_ filename: String) {
|
|
27
|
+
let filePath = "\(srcPath)/\(filename).swift"
|
|
28
|
+
let fileURL = URL(fileURLWithPath: filePath)
|
|
29
|
+
|
|
30
|
+
do {
|
|
31
|
+
let _ = try String(contentsOf: fileURL, encoding: .utf8)
|
|
32
|
+
// In a real scenario, we'd compile and load this
|
|
33
|
+
// For testing, we'll simulate the behavior
|
|
34
|
+
} catch {
|
|
35
|
+
print("Warning: Could not load \(filename).swift")
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Test class
|
|
40
|
+
class CompressedOnlyOutputTest {
|
|
41
|
+
var results: [(name: String, passed: Bool, message: String)] = []
|
|
42
|
+
|
|
43
|
+
func runAllTests() {
|
|
44
|
+
print("📋 Test Scenarios:")
|
|
45
|
+
print("1. Primary disabled, Compressed enabled (AAC)")
|
|
46
|
+
print("2. Primary disabled, Compressed enabled (Opus)")
|
|
47
|
+
print("3. Verify compressed file URI is returned")
|
|
48
|
+
print("4. Verify file size and format info")
|
|
49
|
+
print("\n")
|
|
50
|
+
|
|
51
|
+
testCompressedOnlyAAC()
|
|
52
|
+
testCompressedOnlyOpus()
|
|
53
|
+
testCompressedFileAccess()
|
|
54
|
+
printResults()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
func testCompressedOnlyAAC() {
|
|
58
|
+
print("Test 1: Compressed-Only Output with AAC")
|
|
59
|
+
print("---------------------------------------")
|
|
60
|
+
|
|
61
|
+
// Simulate recording configuration
|
|
62
|
+
let config = [
|
|
63
|
+
"sampleRate": 44100,
|
|
64
|
+
"channels": 1,
|
|
65
|
+
"encoding": "pcm_16bit",
|
|
66
|
+
"output": [
|
|
67
|
+
"primary": ["enabled": false],
|
|
68
|
+
"compressed": [
|
|
69
|
+
"enabled": true,
|
|
70
|
+
"format": "aac",
|
|
71
|
+
"bitrate": 128000
|
|
72
|
+
]
|
|
73
|
+
]
|
|
74
|
+
] as [String : Any]
|
|
75
|
+
|
|
76
|
+
// Expected behavior: Should return compression info with file URI
|
|
77
|
+
let mockResult = simulateRecording(config: config)
|
|
78
|
+
|
|
79
|
+
let hasCompressionInfo = mockResult["compression"] != nil
|
|
80
|
+
let compressionDict = mockResult["compression"] as? [String: Any]
|
|
81
|
+
let hasCompressedUri = compressionDict?["compressedFileUri"] != nil
|
|
82
|
+
let format = compressionDict?["format"] as? String
|
|
83
|
+
|
|
84
|
+
let passed = hasCompressionInfo && hasCompressedUri && format == "aac"
|
|
85
|
+
|
|
86
|
+
results.append((
|
|
87
|
+
name: "AAC Compressed-Only",
|
|
88
|
+
passed: passed,
|
|
89
|
+
message: "Compression info: \(hasCompressionInfo), URI: \(hasCompressedUri), Format: \(format ?? "nil")"
|
|
90
|
+
))
|
|
91
|
+
|
|
92
|
+
if passed {
|
|
93
|
+
print("✅ Compression info properly returned")
|
|
94
|
+
print("✅ Compressed file URI: \(compressionDict?["compressedFileUri"] ?? "nil")")
|
|
95
|
+
print("✅ Format: \(format ?? "nil")")
|
|
96
|
+
} else {
|
|
97
|
+
print("❌ FAIL: Compression info missing or incomplete")
|
|
98
|
+
}
|
|
99
|
+
print()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func testCompressedOnlyOpus() {
|
|
103
|
+
print("Test 2: Compressed-Only Output with Opus (fallback to AAC on iOS)")
|
|
104
|
+
print("-----------------------------------------------------------------")
|
|
105
|
+
|
|
106
|
+
let config = [
|
|
107
|
+
"sampleRate": 48000,
|
|
108
|
+
"channels": 1,
|
|
109
|
+
"encoding": "pcm_16bit",
|
|
110
|
+
"output": [
|
|
111
|
+
"primary": ["enabled": false],
|
|
112
|
+
"compressed": [
|
|
113
|
+
"enabled": true,
|
|
114
|
+
"format": "opus", // Should fallback to AAC on iOS
|
|
115
|
+
"bitrate": 64000
|
|
116
|
+
]
|
|
117
|
+
]
|
|
118
|
+
] as [String : Any]
|
|
119
|
+
|
|
120
|
+
let mockResult = simulateRecording(config: config)
|
|
121
|
+
|
|
122
|
+
let compressionDict = mockResult["compression"] as? [String: Any]
|
|
123
|
+
let format = compressionDict?["format"] as? String
|
|
124
|
+
let bitrate = compressionDict?["bitrate"] as? Int
|
|
125
|
+
|
|
126
|
+
// On iOS, Opus should fallback to AAC
|
|
127
|
+
let passed = format == "aac" && bitrate != nil
|
|
128
|
+
|
|
129
|
+
results.append((
|
|
130
|
+
name: "Opus→AAC Fallback",
|
|
131
|
+
passed: passed,
|
|
132
|
+
message: "Format: \(format ?? "nil"), Bitrate: \(bitrate ?? 0)"
|
|
133
|
+
))
|
|
134
|
+
|
|
135
|
+
if passed {
|
|
136
|
+
print("✅ Opus correctly fell back to AAC")
|
|
137
|
+
print("✅ Bitrate preserved: \(bitrate ?? 0)")
|
|
138
|
+
} else {
|
|
139
|
+
print("❌ FAIL: Incorrect format or missing bitrate")
|
|
140
|
+
}
|
|
141
|
+
print()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
func testCompressedFileAccess() {
|
|
145
|
+
print("Test 3: Verify Compressed File Accessibility")
|
|
146
|
+
print("-------------------------------------------")
|
|
147
|
+
|
|
148
|
+
let config = [
|
|
149
|
+
"sampleRate": 44100,
|
|
150
|
+
"channels": 1,
|
|
151
|
+
"encoding": "pcm_16bit",
|
|
152
|
+
"output": [
|
|
153
|
+
"primary": ["enabled": false],
|
|
154
|
+
"compressed": ["enabled": true, "format": "aac"]
|
|
155
|
+
]
|
|
156
|
+
] as [String : Any]
|
|
157
|
+
|
|
158
|
+
let mockResult = simulateRecording(config: config)
|
|
159
|
+
|
|
160
|
+
// Check main result structure
|
|
161
|
+
let fileUri = mockResult["fileUri"] as? String ?? ""
|
|
162
|
+
let _ = mockResult["filename"] as? String ?? ""
|
|
163
|
+
let _ = mockResult["mimeType"] as? String ?? ""
|
|
164
|
+
|
|
165
|
+
// Check compression structure
|
|
166
|
+
let compressionDict = mockResult["compression"] as? [String: Any]
|
|
167
|
+
let compressedUri = compressionDict?["compressedFileUri"] as? String
|
|
168
|
+
let compressedSize = compressionDict?["size"] as? Int64
|
|
169
|
+
|
|
170
|
+
// When primary is disabled, we should either:
|
|
171
|
+
// 1. Get compression info with the compressed file URI
|
|
172
|
+
// 2. Or use compressed URI as main fileUri (like web does)
|
|
173
|
+
let hasAccessToCompressed = (compressedUri != nil && !compressedUri!.isEmpty) ||
|
|
174
|
+
(!fileUri.isEmpty && fileUri != "")
|
|
175
|
+
|
|
176
|
+
let passed = hasAccessToCompressed && compressedSize != nil
|
|
177
|
+
|
|
178
|
+
results.append((
|
|
179
|
+
name: "File Accessibility",
|
|
180
|
+
passed: passed,
|
|
181
|
+
message: "Main URI: '\(fileUri)', Compressed URI: '\(compressedUri ?? "nil")', Size: \(compressedSize ?? 0)"
|
|
182
|
+
))
|
|
183
|
+
|
|
184
|
+
if passed {
|
|
185
|
+
print("✅ Compressed file is accessible")
|
|
186
|
+
print("✅ File size reported: \(compressedSize ?? 0) bytes")
|
|
187
|
+
} else {
|
|
188
|
+
print("❌ FAIL: Cannot access compressed file")
|
|
189
|
+
print(" Main fileUri: '\(fileUri)'")
|
|
190
|
+
print(" Compressed URI: '\(compressedUri ?? "nil")'")
|
|
191
|
+
}
|
|
192
|
+
print()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Helper to simulate recording
|
|
196
|
+
func simulateRecording(config: [String: Any]) -> [String: Any] {
|
|
197
|
+
// This simulates the current BUGGY behavior
|
|
198
|
+
// After the fix, this should return proper compression info
|
|
199
|
+
|
|
200
|
+
let outputConfig = config["output"] as? [String: Any]
|
|
201
|
+
let primaryConfig = outputConfig?["primary"] as? [String: Any]
|
|
202
|
+
let compressedConfig = outputConfig?["compressed"] as? [String: Any]
|
|
203
|
+
|
|
204
|
+
let primaryEnabled = primaryConfig?["enabled"] as? Bool ?? true
|
|
205
|
+
let compressedEnabled = compressedConfig?["enabled"] as? Bool ?? false
|
|
206
|
+
|
|
207
|
+
if !primaryEnabled {
|
|
208
|
+
// Current buggy behavior - returns nil compression
|
|
209
|
+
let result: [String: Any] = [
|
|
210
|
+
"fileUri": "",
|
|
211
|
+
"filename": "stream-only",
|
|
212
|
+
"durationMs": 5000,
|
|
213
|
+
"size": 0,
|
|
214
|
+
"mimeType": "audio/wav"
|
|
215
|
+
]
|
|
216
|
+
// BUG: compression should be included but is currently nil
|
|
217
|
+
return result
|
|
218
|
+
} else {
|
|
219
|
+
// Normal behavior when primary is enabled
|
|
220
|
+
var result: [String: Any] = [
|
|
221
|
+
"fileUri": "file:///mock/recording.wav",
|
|
222
|
+
"filename": "recording.wav",
|
|
223
|
+
"durationMs": 5000,
|
|
224
|
+
"size": 240000,
|
|
225
|
+
"mimeType": "audio/wav"
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
if compressedEnabled {
|
|
229
|
+
result["compression"] = [
|
|
230
|
+
"compressedFileUri": "file:///mock/recording.aac",
|
|
231
|
+
"format": "aac",
|
|
232
|
+
"bitrate": 128000,
|
|
233
|
+
"size": 40000,
|
|
234
|
+
"mimeType": "audio/aac"
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return result
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
func printResults() {
|
|
243
|
+
print("\n📊 Test Results")
|
|
244
|
+
print("===============")
|
|
245
|
+
|
|
246
|
+
let passed = results.filter { $0.passed }.count
|
|
247
|
+
let total = results.count
|
|
248
|
+
|
|
249
|
+
for result in results {
|
|
250
|
+
let status = result.passed ? "✅" : "❌"
|
|
251
|
+
print("\(status) \(result.name)")
|
|
252
|
+
print(" \(result.message)")
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
print("\nSummary: \(passed)/\(total) tests passed")
|
|
256
|
+
|
|
257
|
+
if passed == total {
|
|
258
|
+
print("🎉 All tests passed!")
|
|
259
|
+
} else {
|
|
260
|
+
print("⚠️ Some tests failed - Fix needed!")
|
|
261
|
+
print("\n🔧 Required Fix:")
|
|
262
|
+
print("- When primary output is disabled, compression info must be included")
|
|
263
|
+
print("- Compressed file URI must be accessible to users")
|
|
264
|
+
print("- This affects iOS and Android (Web works correctly)")
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Run the test
|
|
270
|
+
let test = CompressedOnlyOutputTest()
|
|
271
|
+
test.runAllTests()
|