@mentra/bluetooth-sdk 0.1.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 +278 -0
- package/android/build.gradle +167 -0
- package/android/lc3Lib/CMakeLists.txt +6 -0
- package/android/lc3Lib/build.gradle +109 -0
- package/android/lc3Lib/proguard-rules.pro +33 -0
- package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
- package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
- package/android/lc3Lib/src/main/java/com/mentra/lc3Lib/Lc3Cpp.java +38 -0
- package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
- package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
- package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
- package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
- package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
- package/android/settings.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +40 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +528 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +610 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +1616 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +314 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +1710 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +554 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/ObservableStore.kt +68 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +158 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +934 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +175 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +1109 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +3974 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +3551 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1327 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +6959 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1655 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +53675 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +202 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +224 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +340 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +465 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +221 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/AES.java +106 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +179 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +44 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/G1Text.kt +436 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleUploadService.java +76 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +791 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +186 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +589 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitor.kt +303 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/app.plugin.js +1 -0
- package/build/BluetoothSdk.types.d.ts +498 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -0
- package/build/BluetoothSdk.types.js +23 -0
- package/build/BluetoothSdk.types.js.map +1 -0
- package/build/BluetoothSdkModule.d.ts +71 -0
- package/build/BluetoothSdkModule.d.ts.map +1 -0
- package/build/BluetoothSdkModule.js +106 -0
- package/build/BluetoothSdkModule.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +5 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/BluetoothSdkModule.swift +665 -0
- package/ios/MentraBluetoothSDK.podspec +77 -0
- package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
- package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
- package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
- package/ios/Packages/CoreObjC/attdet.c +92 -0
- package/ios/Packages/CoreObjC/attdet.h +44 -0
- package/ios/Packages/CoreObjC/bits.c +375 -0
- package/ios/Packages/CoreObjC/bits.h +315 -0
- package/ios/Packages/CoreObjC/bwdet.c +129 -0
- package/ios/Packages/CoreObjC/bwdet.h +69 -0
- package/ios/Packages/CoreObjC/common.h +151 -0
- package/ios/Packages/CoreObjC/energy.c +70 -0
- package/ios/Packages/CoreObjC/energy.h +43 -0
- package/ios/Packages/CoreObjC/fastmath.h +158 -0
- package/ios/Packages/CoreObjC/lc3.c +704 -0
- package/ios/Packages/CoreObjC/lc3.h +313 -0
- package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
- package/ios/Packages/CoreObjC/lc3_private.h +163 -0
- package/ios/Packages/CoreObjC/ltpf.c +905 -0
- package/ios/Packages/CoreObjC/ltpf.h +111 -0
- package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
- package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
- package/ios/Packages/CoreObjC/makefile.mk +35 -0
- package/ios/Packages/CoreObjC/mdct.c +469 -0
- package/ios/Packages/CoreObjC/mdct.h +57 -0
- package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
- package/ios/Packages/CoreObjC/meson.build +61 -0
- package/ios/Packages/CoreObjC/plc.c +61 -0
- package/ios/Packages/CoreObjC/plc.h +57 -0
- package/ios/Packages/CoreObjC/rnnoise.h +114 -0
- package/ios/Packages/CoreObjC/sns.c +880 -0
- package/ios/Packages/CoreObjC/sns.h +103 -0
- package/ios/Packages/CoreObjC/spec.c +907 -0
- package/ios/Packages/CoreObjC/spec.h +119 -0
- package/ios/Packages/CoreObjC/tables.c +3457 -0
- package/ios/Packages/CoreObjC/tables.h +94 -0
- package/ios/Packages/CoreObjC/tns.c +457 -0
- package/ios/Packages/CoreObjC/tns.h +99 -0
- package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
- package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
- package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +1659 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +162 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +1852 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +674 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +44 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
- package/ios/Packages/VAD/Common/VAD/VADQuality.swift +41 -0
- package/ios/Packages/VAD/Common/VAD/VADState.swift +26 -0
- package/ios/Packages/VAD/Common/VAD/VADStrategy.swift +29 -0
- package/ios/Packages/VAD/Common/VAD/VADType.swift +14 -0
- package/ios/Packages/VAD/Data/Configuration.swift +68 -0
- package/ios/Packages/VAD/Data/FrameSize.swift +39 -0
- package/ios/Packages/VAD/Data/Record.swift +13 -0
- package/ios/Packages/VAD/Data/Result.swift +22 -0
- package/ios/Packages/VAD/Data/SampleRate.swift +48 -0
- package/ios/Packages/VAD/Silero/Model/silero_vad.onnx +0 -0
- package/ios/Packages/VAD/Silero/SileroVAD.swift +284 -0
- package/ios/Packages/VAD/Silero/SileroVADStrategy.swift +64 -0
- package/ios/Packages/libbz2/module.modulemap +5 -0
- package/ios/Packages/libbz2/shim.h +1 -0
- package/ios/Source/Bridge.swift +353 -0
- package/ios/Source/Bridging-Header.h +16 -0
- package/ios/Source/DeviceManager.swift +1478 -0
- package/ios/Source/DeviceStore.swift +295 -0
- package/ios/Source/MentraBluetoothSDK.swift +2439 -0
- package/ios/Source/ObservableStore.swift +88 -0
- package/ios/Source/PrivacyInfo.xcprivacy +23 -0
- package/ios/Source/controllers/ControllerManager.swift +187 -0
- package/ios/Source/controllers/R1.swift +827 -0
- package/ios/Source/services/PhoneMic.swift +662 -0
- package/ios/Source/sgcs/Frame.swift +651 -0
- package/ios/Source/sgcs/G1.swift +2505 -0
- package/ios/Source/sgcs/G2.swift +3730 -0
- package/ios/Source/sgcs/Mach1.swift +543 -0
- package/ios/Source/sgcs/MentraLive.swift +4708 -0
- package/ios/Source/sgcs/MentraNex.swift +2479 -0
- package/ios/Source/sgcs/SGCManager.swift +243 -0
- package/ios/Source/sgcs/Simulated.swift +270 -0
- package/ios/Source/sgcs/mentraos_ble.pb.swift +3811 -0
- package/ios/Source/stt/STTTools.swift +154 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +401 -0
- package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
- package/ios/Source/utils/Constants.swift +74 -0
- package/ios/Source/utils/Enums.swift +95 -0
- package/ios/Source/utils/G1Text.swift +2067 -0
- package/ios/Source/utils/JSCExperiment.swift +241 -0
- package/ios/Source/utils/MemoryMonitor.swift +44 -0
- package/ios/Source/utils/MessageChunker.swift +164 -0
- package/ios/Source/utils/Models.swift +135 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +226 -0
- package/ios/Source/utils/TarBz2Extractor.swift +206 -0
- package/package.json +78 -0
- package/plugin/build/index.d.ts +6 -0
- package/plugin/build/index.js +12 -0
- package/plugin/build/withAndroid.d.ts +4 -0
- package/plugin/build/withAndroid.js +80 -0
- package/plugin/build/withIos.d.ts +4 -0
- package/plugin/build/withIos.js +64 -0
- package/src/BluetoothSdk.types.ts +581 -0
- package/src/BluetoothSdkModule.ts +259 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
class STTTools {
|
|
4
|
+
// MARK: - SherpaOnnxTranscriber / STT Model Management
|
|
5
|
+
|
|
6
|
+
static func didReceivePartialTranscription(_ text: String) {
|
|
7
|
+
// Send partial result to server witgetConnectedBluetoothNameh proper formatting
|
|
8
|
+
let transcriptionLanguage =
|
|
9
|
+
UserDefaults.standard.string(forKey: "STTModelLanguageCode") ?? "en-US"
|
|
10
|
+
Bridge.log("Mentra: Sending partial transcription: \(text), \(transcriptionLanguage)")
|
|
11
|
+
let transcription: [String: Any] = [
|
|
12
|
+
"type": "local_transcription",
|
|
13
|
+
"text": transcriptionLanguage == "en-US" ? text.lowercased() : text,
|
|
14
|
+
"isFinal": false,
|
|
15
|
+
"startTime": Int(Date().timeIntervalSince1970 * 1000) - 1000, // 1 second ago
|
|
16
|
+
"endTime": Int(Date().timeIntervalSince1970 * 1000),
|
|
17
|
+
"speakerId": 0,
|
|
18
|
+
"transcribeLanguage": transcriptionLanguage,
|
|
19
|
+
"provider": "sherpa-onnx",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
Bridge.sendLocalTranscription(transcription: transcription)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static func didReceiveFinalTranscription(_ text: String) {
|
|
26
|
+
// Send final result to server with proper formatting
|
|
27
|
+
let transcriptionLanguage =
|
|
28
|
+
UserDefaults.standard.string(forKey: "STTModelLanguageCode") ?? "en-US"
|
|
29
|
+
Bridge.log("Mentra: Sending final transcription: \(text), \(transcriptionLanguage)")
|
|
30
|
+
if !text.isEmpty {
|
|
31
|
+
let transcription: [String: Any] = [
|
|
32
|
+
"type": "local_transcription",
|
|
33
|
+
"text": transcriptionLanguage == "en-US" ? text.lowercased() : text,
|
|
34
|
+
"isFinal": true,
|
|
35
|
+
"startTime": Int(Date().timeIntervalSince1970 * 1000) - 2000, // 2 seconds ago
|
|
36
|
+
"endTime": Int(Date().timeIntervalSince1970 * 1000),
|
|
37
|
+
"speakerId": 0,
|
|
38
|
+
"transcribeLanguage": transcriptionLanguage,
|
|
39
|
+
"provider": "sherpa-onnx",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
Bridge.sendLocalTranscription(transcription: transcription)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static func setSttModelDetails(_ path: String, _ languageCode: String) {
|
|
47
|
+
UserDefaults.standard.set(path, forKey: "STTModelPath")
|
|
48
|
+
UserDefaults.standard.set(languageCode, forKey: "STTModelLanguageCode")
|
|
49
|
+
UserDefaults.standard.synchronize()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static func getSttModelPath() -> String {
|
|
53
|
+
return UserDefaults.standard.string(forKey: "STTModelPath") ?? ""
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static func checkSTTModelAvailable() -> Bool {
|
|
57
|
+
guard let modelPath = UserDefaults.standard.string(forKey: "STTModelPath") else {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let fileManager = FileManager.default
|
|
62
|
+
|
|
63
|
+
// Check for tokens.txt (required for all models)
|
|
64
|
+
let tokensPath = (modelPath as NSString).appendingPathComponent("tokens.txt")
|
|
65
|
+
if !fileManager.fileExists(atPath: tokensPath) {
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check for CTC model
|
|
70
|
+
let ctcModelPath = (modelPath as NSString).appendingPathComponent("model.int8.onnx")
|
|
71
|
+
if fileManager.fileExists(atPath: ctcModelPath) {
|
|
72
|
+
return true
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check for transducer model
|
|
76
|
+
let transducerFiles = ["encoder.onnx", "decoder.onnx", "joiner.onnx"]
|
|
77
|
+
for file in transducerFiles {
|
|
78
|
+
let filePath = (modelPath as NSString).appendingPathComponent(file)
|
|
79
|
+
if !fileManager.fileExists(atPath: filePath) {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static func validateSTTModel(_ path: String) -> Bool {
|
|
88
|
+
// do {
|
|
89
|
+
let fileManager = FileManager.default
|
|
90
|
+
|
|
91
|
+
// Check for tokens.txt (required for all models)
|
|
92
|
+
let tokensPath = (path as NSString).appendingPathComponent("tokens.txt")
|
|
93
|
+
if !fileManager.fileExists(atPath: tokensPath) {
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check for CTC model
|
|
98
|
+
let ctcModelPath = (path as NSString).appendingPathComponent("model.int8.onnx")
|
|
99
|
+
if fileManager.fileExists(atPath: ctcModelPath) {
|
|
100
|
+
return true
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check for transducer model
|
|
104
|
+
let transducerFiles = ["encoder.onnx", "decoder.onnx", "joiner.onnx"]
|
|
105
|
+
var allTransducerFilesPresent = true
|
|
106
|
+
|
|
107
|
+
for file in transducerFiles {
|
|
108
|
+
let filePath = (path as NSString).appendingPathComponent(file)
|
|
109
|
+
if !fileManager.fileExists(atPath: filePath) {
|
|
110
|
+
allTransducerFilesPresent = false
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return allTransducerFilesPresent
|
|
116
|
+
// } catch {
|
|
117
|
+
// Bridge.log("STT_ERROR: \(error.localizedDescription)")
|
|
118
|
+
// return false
|
|
119
|
+
// }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static func extractTarBz2(sourcePath: String, destinationPath: String) -> Bool {
|
|
123
|
+
do {
|
|
124
|
+
let fileManager = FileManager.default
|
|
125
|
+
|
|
126
|
+
// Create destination directory if it doesn't exist
|
|
127
|
+
try fileManager.createDirectory(
|
|
128
|
+
atPath: destinationPath,
|
|
129
|
+
withIntermediateDirectories: true,
|
|
130
|
+
attributes: nil
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
// Use the Swift TarBz2Extractor with SWCompression
|
|
134
|
+
var extractionError: NSError?
|
|
135
|
+
let success = TarBz2Extractor.extractTarBz2From(
|
|
136
|
+
sourcePath,
|
|
137
|
+
to: destinationPath,
|
|
138
|
+
error: &extractionError
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if !success || extractionError != nil {
|
|
142
|
+
print(
|
|
143
|
+
"EXTRACTION_ERROR: \(extractionError?.localizedDescription ?? "Failed to extract tar.bz2")"
|
|
144
|
+
)
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
} catch {
|
|
149
|
+
Bridge.log("EXTRACTION_ERROR: \(error.localizedDescription)")
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
152
|
+
return true
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SherpaOnnxTranscriber handles real-time audio transcription using Sherpa-ONNX.
|
|
6
|
+
*
|
|
7
|
+
* It works fully offline and processes PCM audio in real-time to provide partial and final ASR results.
|
|
8
|
+
* This class runs on a background thread, processes short PCM chunks, and emits transcribed text using a delegate.
|
|
9
|
+
*/
|
|
10
|
+
class SherpaOnnxTranscriber {
|
|
11
|
+
private static let TAG = "SherpaOnnxTranscriber"
|
|
12
|
+
|
|
13
|
+
private static let SAMPLE_RATE = 16000 // Sherpa-ONNX model's required sample rate
|
|
14
|
+
private static let QUEUE_CAPACITY = 100 // Max number of audio buffers to keep in queue
|
|
15
|
+
|
|
16
|
+
private let pcmQueue = DispatchQueue(label: "com.augmentos.sherpaonnx.pcmQueue", qos: .userInteractive)
|
|
17
|
+
private var pcmBuffers = [Data]()
|
|
18
|
+
private var isRunning = false
|
|
19
|
+
private var processingQueue: DispatchQueue?
|
|
20
|
+
private var processingTask: DispatchWorkItem?
|
|
21
|
+
|
|
22
|
+
/// The underlying Sherpa-ONNX objects
|
|
23
|
+
private var recognizer: SherpaOnnxRecognizer?
|
|
24
|
+
|
|
25
|
+
private var lastPartialResult = ""
|
|
26
|
+
|
|
27
|
+
/// Parent context
|
|
28
|
+
private weak var context: UIViewController?
|
|
29
|
+
|
|
30
|
+
/// Session start time for relative timestamps
|
|
31
|
+
private var transcriptionSessionStart: Date
|
|
32
|
+
|
|
33
|
+
/// Dynamic model path support
|
|
34
|
+
private static var customModelPath: String? {
|
|
35
|
+
guard let storedPath = UserDefaults.standard.string(forKey: "STTModelPath") else {
|
|
36
|
+
return nil
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Always resolve current Documents directory
|
|
40
|
+
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
41
|
+
|
|
42
|
+
// Extract relative subpath after "Documents/"
|
|
43
|
+
// NOTE: Doing this because the application id changes between the development builds and files can't be found.
|
|
44
|
+
if let range = storedPath.range(of: "/Documents/") {
|
|
45
|
+
let relativePath = String(storedPath[range.upperBound...]) // e.g. "stt_models/..."
|
|
46
|
+
let fixedPath = documentsURL.appendingPathComponent(relativePath).path
|
|
47
|
+
|
|
48
|
+
Bridge.log("Reconstructed STTModelPath: \(fixedPath)")
|
|
49
|
+
return fixedPath
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// If nothing matched, just return as-is
|
|
53
|
+
Bridge.log("STTModelPath (raw): \(storedPath)")
|
|
54
|
+
return storedPath
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Constructor that accepts a UIViewController to load model assets.
|
|
59
|
+
*/
|
|
60
|
+
init(context: UIViewController) {
|
|
61
|
+
self.context = context
|
|
62
|
+
transcriptionSessionStart = Date()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
deinit {
|
|
66
|
+
shutdown()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Initialize the Sherpa-ONNX recognizer.
|
|
71
|
+
* Loads models and configuration, sets up processing thread.
|
|
72
|
+
*/
|
|
73
|
+
func initialize() {
|
|
74
|
+
do {
|
|
75
|
+
var tokensPath: String
|
|
76
|
+
var modelType = "unknown"
|
|
77
|
+
let fileManager = FileManager.default
|
|
78
|
+
|
|
79
|
+
// Check if we have a custom model path set
|
|
80
|
+
if let customPath = SherpaOnnxTranscriber.customModelPath {
|
|
81
|
+
// Detect model type based on available files
|
|
82
|
+
let ctcModelPath = (customPath as NSString).appendingPathComponent("model.int8.onnx")
|
|
83
|
+
let transducerEncoderPath = (customPath as NSString).appendingPathComponent("encoder.onnx")
|
|
84
|
+
|
|
85
|
+
tokensPath = (customPath as NSString).appendingPathComponent("tokens.txt")
|
|
86
|
+
|
|
87
|
+
// Verify tokens file exists
|
|
88
|
+
guard fileManager.fileExists(atPath: tokensPath) else {
|
|
89
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 1, userInfo: [
|
|
90
|
+
NSLocalizedDescriptionKey: "tokens.txt not found at path: \(customPath)",
|
|
91
|
+
])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if fileManager.fileExists(atPath: ctcModelPath) {
|
|
95
|
+
// CTC model detected
|
|
96
|
+
modelType = "ctc"
|
|
97
|
+
Bridge.log("Detected CTC model at \(customPath)")
|
|
98
|
+
|
|
99
|
+
// Create CTC model config using Zipformer2Ctc
|
|
100
|
+
var nemoCtc = sherpaOnnxOnlineNemoCtcModelConfig(
|
|
101
|
+
model: ctcModelPath
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
// Create model config with CTC
|
|
105
|
+
var modelConfig = sherpaOnnxOnlineModelConfig(
|
|
106
|
+
tokens: tokensPath,
|
|
107
|
+
numThreads: 1,
|
|
108
|
+
nemoCtc: nemoCtc
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
// Configure recognizer
|
|
112
|
+
var featureConfig = sherpaOnnxFeatureConfig()
|
|
113
|
+
|
|
114
|
+
var config = sherpaOnnxOnlineRecognizerConfig(
|
|
115
|
+
featConfig: featureConfig,
|
|
116
|
+
modelConfig: modelConfig,
|
|
117
|
+
enableEndpoint: true,
|
|
118
|
+
rule1MinTrailingSilence: 1.2,
|
|
119
|
+
rule2MinTrailingSilence: 0.8,
|
|
120
|
+
rule3MinUtteranceLength: 10.0
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
// Create recognizer with the wrapper
|
|
124
|
+
recognizer = SherpaOnnxRecognizer(config: &config)
|
|
125
|
+
|
|
126
|
+
} else if fileManager.fileExists(atPath: transducerEncoderPath) {
|
|
127
|
+
// Transducer model detected
|
|
128
|
+
modelType = "transducer"
|
|
129
|
+
Bridge.log("Detected transducer model at \(customPath)")
|
|
130
|
+
|
|
131
|
+
let decoderPath = (customPath as NSString).appendingPathComponent("decoder.onnx")
|
|
132
|
+
let joinerPath = (customPath as NSString).appendingPathComponent("joiner.onnx")
|
|
133
|
+
|
|
134
|
+
// Verify all transducer files exist
|
|
135
|
+
guard fileManager.fileExists(atPath: decoderPath),
|
|
136
|
+
fileManager.fileExists(atPath: joinerPath)
|
|
137
|
+
else {
|
|
138
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 1, userInfo: [
|
|
139
|
+
NSLocalizedDescriptionKey: "Transducer model files incomplete at path: \(customPath)",
|
|
140
|
+
])
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Create Sherpa-ONNX transducer model config
|
|
144
|
+
var transducer = sherpaOnnxOnlineTransducerModelConfig(
|
|
145
|
+
encoder: transducerEncoderPath,
|
|
146
|
+
decoder: decoderPath,
|
|
147
|
+
joiner: joinerPath
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
// Create model config
|
|
151
|
+
var modelConfig = sherpaOnnxOnlineModelConfig(
|
|
152
|
+
tokens: tokensPath,
|
|
153
|
+
transducer: transducer,
|
|
154
|
+
numThreads: 1
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
// Configure recognizer
|
|
158
|
+
var featureConfig = sherpaOnnxFeatureConfig()
|
|
159
|
+
|
|
160
|
+
var config = sherpaOnnxOnlineRecognizerConfig(
|
|
161
|
+
featConfig: featureConfig,
|
|
162
|
+
modelConfig: modelConfig,
|
|
163
|
+
enableEndpoint: true,
|
|
164
|
+
rule1MinTrailingSilence: 1.2,
|
|
165
|
+
rule2MinTrailingSilence: 0.8,
|
|
166
|
+
rule3MinUtteranceLength: 10.0
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
// Create recognizer with the wrapper
|
|
170
|
+
recognizer = SherpaOnnxRecognizer(config: &config)
|
|
171
|
+
|
|
172
|
+
} else {
|
|
173
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 1, userInfo: [
|
|
174
|
+
NSLocalizedDescriptionKey: "No valid model files found at path: \(customPath)",
|
|
175
|
+
])
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
Bridge.log("No Sherpa ONNX model available. Transcription will be disabled.")
|
|
179
|
+
Bridge.log("Please download a model using the model downloader in settings.")
|
|
180
|
+
recognizer = nil
|
|
181
|
+
isRunning = false
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if recognizer == nil {
|
|
186
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to create recognizer"])
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
startProcessingTask()
|
|
190
|
+
isRunning = true
|
|
191
|
+
|
|
192
|
+
Bridge.log("Sherpa-ONNX ASR initialized successfully with \(modelType) model")
|
|
193
|
+
|
|
194
|
+
} catch {
|
|
195
|
+
Bridge.log("Failed to initialize Sherpa-ONNX: \(error.localizedDescription)")
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Handle transcription results - send only to delegate
|
|
201
|
+
*/
|
|
202
|
+
private func handleTranscriptionResult(text: String, isFinal: Bool) {
|
|
203
|
+
// Forward to delegate if set
|
|
204
|
+
DispatchQueue.main.async { [weak self] in
|
|
205
|
+
if isFinal {
|
|
206
|
+
STTTools.didReceiveFinalTranscription(text)
|
|
207
|
+
} else {
|
|
208
|
+
STTTools.didReceivePartialTranscription(text)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Feed PCM audio data (16-bit little endian) into the transcriber.
|
|
215
|
+
* This method should be called continuously with short chunks (e.g., 100-300ms).
|
|
216
|
+
*
|
|
217
|
+
* Note: Audio passed to this method is assumed to have already passed VAD elsewhere,
|
|
218
|
+
* so it's directly queued for processing without additional VAD checks.
|
|
219
|
+
*/
|
|
220
|
+
func acceptAudio(pcm16le: Data) {
|
|
221
|
+
guard isRunning else {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Directly queue the audio data for processing
|
|
226
|
+
// No VAD check here as it's assumed to be done upstream
|
|
227
|
+
queueAudioData(pcm16le)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private func queueAudioData(_ pcm16le: Data) {
|
|
231
|
+
pcmQueue.async { [weak self] in
|
|
232
|
+
guard let self = self else { return }
|
|
233
|
+
|
|
234
|
+
let queueSizeBefore = self.pcmBuffers.count
|
|
235
|
+
self.pcmBuffers.append(pcm16le)
|
|
236
|
+
|
|
237
|
+
// Keep queue size manageable
|
|
238
|
+
if self.pcmBuffers.count > Self.QUEUE_CAPACITY {
|
|
239
|
+
let removedBuffer = self.pcmBuffers.removeFirst()
|
|
240
|
+
Bridge.log("โ ๏ธ Audio queue overflow - dropped buffer of \(removedBuffer.count) bytes")
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Start a background task to continuously consume audio and decode using Sherpa.
|
|
247
|
+
*/
|
|
248
|
+
private func startProcessingTask() {
|
|
249
|
+
Bridge.log("๐ Starting Sherpa-ONNX processing task...")
|
|
250
|
+
|
|
251
|
+
processingQueue = DispatchQueue(label: "com.augmentos.sherpaonnx.processor", qos: .userInitiated)
|
|
252
|
+
|
|
253
|
+
let workItem = DispatchWorkItem { [weak self] in
|
|
254
|
+
self?.runLoop()
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
processingTask = workItem
|
|
258
|
+
processingQueue?.async(execute: workItem)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Main processing loop that handles transcription in real-time.
|
|
263
|
+
* Pulls audio from queue, feeds into Sherpa, emits partial/final results.
|
|
264
|
+
*/
|
|
265
|
+
private func runLoop() {
|
|
266
|
+
Bridge.log("๐ Sherpa-ONNX processing loop started")
|
|
267
|
+
|
|
268
|
+
while isRunning {
|
|
269
|
+
// Pull data from queue
|
|
270
|
+
var audioData: Data?
|
|
271
|
+
|
|
272
|
+
pcmQueue.sync {
|
|
273
|
+
if !self.pcmBuffers.isEmpty {
|
|
274
|
+
audioData = self.pcmBuffers.removeFirst()
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if let data = audioData {
|
|
279
|
+
// Synchronize access to recognizer to prevent race conditions
|
|
280
|
+
objc_sync_enter(self)
|
|
281
|
+
defer { objc_sync_exit(self) }
|
|
282
|
+
|
|
283
|
+
guard let recognizer = recognizer else {
|
|
284
|
+
Bridge.log("โ ๏ธ Recognizer not available, skipping audio chunk")
|
|
285
|
+
continue
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
do {
|
|
289
|
+
// Convert PCM to float [-1.0, 1.0]
|
|
290
|
+
let floatBuf = toFloatArray(from: data)
|
|
291
|
+
|
|
292
|
+
// Pass audio data to the Sherpa-ONNX stream
|
|
293
|
+
recognizer.acceptWaveform(samples: floatBuf, sampleRate: Self.SAMPLE_RATE)
|
|
294
|
+
|
|
295
|
+
// Decode continuously while model is ready
|
|
296
|
+
var decodeCount = 0
|
|
297
|
+
while recognizer.isReady() {
|
|
298
|
+
recognizer.decode()
|
|
299
|
+
decodeCount += 1
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// If utterance endpoint detected
|
|
303
|
+
if recognizer.isEndpoint() {
|
|
304
|
+
let result = recognizer.getResult()
|
|
305
|
+
let finalText = result.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
306
|
+
|
|
307
|
+
if !finalText.isEmpty {
|
|
308
|
+
handleTranscriptionResult(text: finalText, isFinal: true)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
recognizer.reset() // Start new utterance
|
|
312
|
+
lastPartialResult = ""
|
|
313
|
+
} else {
|
|
314
|
+
// Emit partial results if changed
|
|
315
|
+
let result = recognizer.getResult()
|
|
316
|
+
let partial = result.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
317
|
+
|
|
318
|
+
if partial != lastPartialResult, !partial.isEmpty {
|
|
319
|
+
handleTranscriptionResult(text: partial, isFinal: false)
|
|
320
|
+
lastPartialResult = partial
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
Bridge.log("โ Error processing audio: \(error.localizedDescription)")
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
// Sleep briefly to avoid tight CPU loop if no audio is available
|
|
328
|
+
Thread.sleep(forTimeInterval: 0.01)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
Bridge.log("ASR processing thread stopped")
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Convert 16-bit PCM byte data (little-endian) to float array [-1.0, 1.0].
|
|
337
|
+
*/
|
|
338
|
+
private func toFloatArray(from pcmData: Data) -> [Float] {
|
|
339
|
+
let count = pcmData.count / 2
|
|
340
|
+
var samples = [Float](repeating: 0, count: count)
|
|
341
|
+
|
|
342
|
+
pcmData.withUnsafeBytes { (bufferPointer: UnsafeRawBufferPointer) in
|
|
343
|
+
if let address = bufferPointer.baseAddress {
|
|
344
|
+
let int16Pointer = address.bindMemory(to: Int16.self, capacity: count)
|
|
345
|
+
|
|
346
|
+
for i in 0 ..< count {
|
|
347
|
+
// Convert from little-endian if needed
|
|
348
|
+
var sample = int16Pointer[i]
|
|
349
|
+
if CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderBigEndian.rawValue) {
|
|
350
|
+
sample = Int16(littleEndian: sample)
|
|
351
|
+
}
|
|
352
|
+
samples[i] = Float(sample) / 32768.0
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return samples
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Stop transcription processing.
|
|
362
|
+
* This shuts down the processing thread and releases Sherpa-ONNX resources.
|
|
363
|
+
*/
|
|
364
|
+
func shutdown() {
|
|
365
|
+
Bridge.log("๐ Shutting down SherpaOnnxTranscriber...")
|
|
366
|
+
|
|
367
|
+
isRunning = false
|
|
368
|
+
processingTask?.cancel()
|
|
369
|
+
|
|
370
|
+
// Synchronize access to recognizer during shutdown
|
|
371
|
+
objc_sync_enter(self)
|
|
372
|
+
defer { objc_sync_exit(self) }
|
|
373
|
+
|
|
374
|
+
// The recognizer will be automatically cleaned up by ARC when set to nil
|
|
375
|
+
if recognizer != nil {
|
|
376
|
+
Bridge.log("๐งน Cleaning up Sherpa-ONNX recognizer")
|
|
377
|
+
recognizer = nil
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Clear any remaining audio buffers
|
|
381
|
+
pcmQueue.sync {
|
|
382
|
+
let remainingBuffers = self.pcmBuffers.count
|
|
383
|
+
if remainingBuffers > 0 {
|
|
384
|
+
Bridge.log("๐๏ธ Clearing \(remainingBuffers) remaining audio buffers")
|
|
385
|
+
}
|
|
386
|
+
self.pcmBuffers.removeAll()
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
Bridge.log("โ
SherpaOnnxTranscriber shutdown complete")
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Restarts the transcriber after a model change.
|
|
394
|
+
* Shuts down existing resources, clears buffers, and reinitializes the recognizer.
|
|
395
|
+
*/
|
|
396
|
+
func restart() {
|
|
397
|
+
Bridge.log("โป๏ธ Restarting SherpaOnnxTranscriber...")
|
|
398
|
+
shutdown()
|
|
399
|
+
initialize()
|
|
400
|
+
}
|
|
401
|
+
}
|