@glassly/bluetooth-sdk 0.1.0-dev.26
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 +563 -0
- package/android/build.gradle +450 -0
- package/android/gradle.properties.example +9 -0
- package/android/lc3Lib/CMakeLists.txt +6 -0
- package/android/lc3Lib/build.gradle +212 -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/glassly/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 +4 -0
- package/android/silero/LICENSE.md +17 -0
- package/android/silero/build.gradle +50 -0
- package/android/silero/proguard-rules.pro +24 -0
- package/android/silero/src/androidTest/assets/hello.wav +0 -0
- package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
- package/android/silero/src/main/assets/LICENSE +21 -0
- package/android/silero/src/main/assets/silero_vad.onnx +0 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
- package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
- package/android/src/main/AndroidManifest.xml +42 -0
- package/android/src/main/assets/G2Firmware/README.md +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2325 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1088 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2528 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +360 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +8 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +937 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3878 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6428 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1241 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1280 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10817 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2011 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
- package/android/src/test/resources/avif_with_exif.avif +0 -0
- package/app.plugin.js +3 -0
- package/build/BluetoothSdk.types.d.ts +1354 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -0
- package/build/BluetoothSdk.types.js +41 -0
- package/build/BluetoothSdk.types.js.map +1 -0
- package/build/_internal.d.ts +17 -0
- package/build/_internal.d.ts.map +1 -0
- package/build/_internal.js +16 -0
- package/build/_internal.js.map +1 -0
- package/build/_private/BluetoothSdkModule.d.ts +172 -0
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
- package/build/_private/BluetoothSdkModule.js +300 -0
- package/build/_private/BluetoothSdkModule.js.map +1 -0
- package/build/_private/LocalNetworkModule.d.ts +38 -0
- package/build/_private/LocalNetworkModule.d.ts.map +1 -0
- package/build/_private/LocalNetworkModule.js +3 -0
- package/build/_private/LocalNetworkModule.js.map +1 -0
- package/build/_private/cameraRequestPayload.d.ts +4 -0
- package/build/_private/cameraRequestPayload.d.ts.map +1 -0
- package/build/_private/cameraRequestPayload.js +32 -0
- package/build/_private/cameraRequestPayload.js.map +1 -0
- package/build/_private/photoRequestPayload.d.ts +6 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -0
- package/build/_private/photoRequestPayload.js +86 -0
- package/build/_private/photoRequestPayload.js.map +1 -0
- package/build/_private/sdkOtaManifest.d.ts +11 -0
- package/build/_private/sdkOtaManifest.d.ts.map +1 -0
- package/build/_private/sdkOtaManifest.js +15 -0
- package/build/_private/sdkOtaManifest.js.map +1 -0
- package/build/changelogs.d.ts +7 -0
- package/build/changelogs.d.ts.map +1 -0
- package/build/changelogs.js +46 -0
- package/build/changelogs.js.map +1 -0
- package/build/debug.d.ts +3 -0
- package/build/debug.d.ts.map +1 -0
- package/build/debug.js +8 -0
- package/build/debug.js.map +1 -0
- package/build/generated/changelogCatalog.d.ts +9 -0
- package/build/generated/changelogCatalog.d.ts.map +1 -0
- package/build/generated/changelogCatalog.js +12 -0
- package/build/generated/changelogCatalog.js.map +1 -0
- package/build/generated/releaseMetadata.d.ts +12 -0
- package/build/generated/releaseMetadata.d.ts.map +1 -0
- package/build/generated/releaseMetadata.js +10 -0
- package/build/generated/releaseMetadata.js.map +1 -0
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +171 -0
- package/build/index.js.map +1 -0
- package/build/ota-server/OtaServer.types.d.ts +14 -0
- package/build/ota-server/OtaServer.types.d.ts.map +1 -0
- package/build/ota-server/OtaServer.types.js +2 -0
- package/build/ota-server/OtaServer.types.js.map +1 -0
- package/build/ota-server/OtaServerModule.d.ts +21 -0
- package/build/ota-server/OtaServerModule.d.ts.map +1 -0
- package/build/ota-server/OtaServerModule.js +3 -0
- package/build/ota-server/OtaServerModule.js.map +1 -0
- package/build/ota-server/OtaServerModule.web.d.ts +10 -0
- package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
- package/build/ota-server/OtaServerModule.web.js +12 -0
- package/build/ota-server/OtaServerModule.web.js.map +1 -0
- package/build/ota-server/index.d.ts +3 -0
- package/build/ota-server/index.d.ts.map +1 -0
- package/build/ota-server/index.js +3 -0
- package/build/ota-server/index.js.map +1 -0
- package/build/ota-transport/index.d.ts +67 -0
- package/build/ota-transport/index.d.ts.map +1 -0
- package/build/ota-transport/index.js +58 -0
- package/build/ota-transport/index.js.map +1 -0
- package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
- package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiver.types.js +2 -0
- package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
- package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.js +3 -0
- package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
- package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
- package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
- package/build/photo-receiver/index.d.ts +3 -0
- package/build/photo-receiver/index.d.ts.map +1 -0
- package/build/photo-receiver/index.js +3 -0
- package/build/photo-receiver/index.js.map +1 -0
- package/build/react/index.d.ts +5 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +4 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useBluetooth.d.ts +100 -0
- package/build/react/useBluetooth.d.ts.map +1 -0
- package/build/react/useBluetooth.js +167 -0
- package/build/react/useBluetooth.js.map +1 -0
- package/build/react/useBluetoothEvent.d.ts +6 -0
- package/build/react/useBluetoothEvent.d.ts.map +1 -0
- package/build/react/useBluetoothEvent.js +21 -0
- package/build/react/useBluetoothEvent.js.map +1 -0
- package/build/react/useBluetoothScan.d.ts +22 -0
- package/build/react/useBluetoothScan.d.ts.map +1 -0
- package/build/react/useBluetoothScan.js +135 -0
- package/build/react/useBluetoothScan.js.map +1 -0
- package/build/react/useBluetoothStatus.d.ts +16 -0
- package/build/react/useBluetoothStatus.d.ts.map +1 -0
- package/build/react/useBluetoothStatus.js +137 -0
- package/build/react/useBluetoothStatus.js.map +1 -0
- package/build/react/useGlassesConnection.d.ts +29 -0
- package/build/react/useGlassesConnection.d.ts.map +1 -0
- package/build/react/useGlassesConnection.js +153 -0
- package/build/react/useGlassesConnection.js.map +1 -0
- package/build/types/index.d.ts +3 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/index.js +2 -0
- package/build/types/index.js.map +1 -0
- package/expo-module.config.json +14 -0
- package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
- package/ios/BluetoothSdkModule.swift +1034 -0
- package/ios/GlasslyBluetoothSDK.podspec +106 -0
- package/ios/LocalIPv4.swift +150 -0
- package/ios/LocalOtaServer.swift +309 -0
- package/ios/LocalPhotoUploadServer.swift +754 -0
- package/ios/OtaServerModule.swift +147 -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/include/PcmConverter.h +22 -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 +2383 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
- package/ios/Packages/libbz2/module.modulemap +5 -0
- package/ios/Packages/libbz2/shim.h +1 -0
- package/ios/PhotoReceiverModule.swift +129 -0
- package/ios/Source/BluetoothSDK.swift +2303 -0
- package/ios/Source/BluetoothSDKDebug.swift +12 -0
- package/ios/Source/BluetoothSdkDefaults.swift +29 -0
- package/ios/Source/Bridge.swift +708 -0
- package/ios/Source/Bridging-Header.h +16 -0
- package/ios/Source/DeviceManager.swift +2109 -0
- package/ios/Source/DeviceStore.swift +336 -0
- package/ios/Source/GeneratedChangelogCatalog.swift +7 -0
- package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
- package/ios/Source/ObservableStore.swift +118 -0
- package/ios/Source/OtaManifest.swift +211 -0
- package/ios/Source/PrivacyInfo.xcprivacy +23 -0
- package/ios/Source/ReleaseChangelog.swift +65 -0
- package/ios/Source/Resources/G2Firmware/README.md +10 -0
- package/ios/Source/audio/AudioModels.swift +178 -0
- package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
- package/ios/Source/camera/CameraModels.swift +866 -0
- package/ios/Source/connection/ScanSession.swift +27 -0
- package/ios/Source/controllers/ControllerManager.swift +184 -0
- package/ios/Source/controllers/R1.swift +829 -0
- package/ios/Source/errors/BluetoothSdkError.swift +19 -0
- package/ios/Source/events/BluetoothEvents.swift +342 -0
- package/ios/Source/internal/BleTraceLogger.swift +192 -0
- package/ios/Source/internal/BluetoothAvailability.swift +78 -0
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
- package/ios/Source/internal/ValueParsing.swift +99 -0
- package/ios/Source/requests/DisplayRequests.swift +82 -0
- package/ios/Source/services/PhoneMic.swift +662 -0
- package/ios/Source/sgcs/Ar99.swift +2451 -0
- package/ios/Source/sgcs/Frame.swift +649 -0
- package/ios/Source/sgcs/G1.swift +2554 -0
- package/ios/Source/sgcs/G2.swift +6887 -0
- package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
- package/ios/Source/sgcs/G2CfwRenderer.swift +1279 -0
- package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
- package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
- package/ios/Source/sgcs/G2Flasher.swift +773 -0
- package/ios/Source/sgcs/Mach1.swift +541 -0
- package/ios/Source/sgcs/MentraLive.swift +7011 -0
- package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
- package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
- package/ios/Source/sgcs/MentraNex.swift +3221 -0
- package/ios/Source/sgcs/Nimo.swift +1886 -0
- package/ios/Source/sgcs/SGCManager.swift +608 -0
- package/ios/Source/sgcs/Simulated.swift +274 -0
- package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
- package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
- package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
- package/ios/Source/status/DeviceStatus.swift +981 -0
- package/ios/Source/status/RuntimeState.swift +359 -0
- package/ios/Source/status/WifiHotspotStatus.swift +345 -0
- package/ios/Source/streaming/StreamModels.swift +693 -0
- package/ios/Source/stt/STTTools.swift +191 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
- package/ios/Source/tts/TTSTools.swift +171 -0
- package/ios/Source/types/DeviceModels.swift +177 -0
- package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
- package/ios/Source/utils/BinaryCodec.swift +61 -0
- package/ios/Source/utils/BleJsonCompact.swift +405 -0
- package/ios/Source/utils/BleWireProtocol.swift +92 -0
- package/ios/Source/utils/Constants.swift +78 -0
- package/ios/Source/utils/Enums.swift +95 -0
- package/ios/Source/utils/G1Text.swift +2096 -0
- package/ios/Source/utils/JSCExperiment.swift +241 -0
- package/ios/Source/utils/MemoryMonitor.swift +44 -0
- package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
- package/ios/Source/utils/MessageChunker.swift +294 -0
- package/ios/Source/utils/Models.swift +135 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
- package/ios/Source/utils/TarBz2Extractor.swift +257 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
- package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
- package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
- package/ios/Tests/ReleaseChangelogTests.swift +13 -0
- package/package.json +129 -0
- package/plugin/build/index.d.ts +10 -0
- package/plugin/build/index.js +12 -0
- package/plugin/build/withAndroid.d.ts +3 -0
- package/plugin/build/withAndroid.js +190 -0
- package/plugin/build/withIos.d.ts +5 -0
- package/plugin/build/withIos.js +108 -0
- package/scripts/inject-ios-sdk-version.mjs +65 -0
- package/scripts/public-ota-api.test.mjs +37 -0
- package/scripts/verify-release-package.mjs +45 -0
- package/scripts/write-release-metadata.mjs +152 -0
- package/scripts/write-release-metadata.test.mjs +37 -0
- package/src/BluetoothSdk.types.ts +1623 -0
- package/src/_internal.ts +16 -0
- package/src/_private/BluetoothSdkModule.ts +684 -0
- package/src/_private/LocalNetworkModule.ts +50 -0
- package/src/_private/cameraRequestPayload.ts +34 -0
- package/src/_private/photoRequestPayload.ts +90 -0
- package/src/_private/sdkOtaManifest.ts +18 -0
- package/src/changelogs.ts +46 -0
- package/src/debug.ts +9 -0
- package/src/generated/changelogCatalog.ts +13 -0
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +321 -0
- package/src/ota-server/OtaServer.types.ts +10 -0
- package/src/ota-server/OtaServerModule.ts +23 -0
- package/src/ota-server/OtaServerModule.web.ts +17 -0
- package/src/ota-server/index.ts +2 -0
- package/src/ota-transport/index.ts +116 -0
- package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
- package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
- package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
- package/src/photo-receiver/index.ts +2 -0
- package/src/react/index.ts +21 -0
- package/src/react/useBluetooth.ts +309 -0
- package/src/react/useBluetoothEvent.ts +35 -0
- package/src/react/useBluetoothScan.ts +173 -0
- package/src/react/useBluetoothStatus.ts +180 -0
- package/src/react/useGlassesConnection.ts +202 -0
- package/src/types/index.ts +12 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
class STTTools {
|
|
4
|
+
// MARK: - SherpaOnnxTranscriber / STT Model Management
|
|
5
|
+
|
|
6
|
+
/// How stale a SpeakerIdEngine match may be and still label a transcript.
|
|
7
|
+
private static let speakerMatchWindowMs: Int64 = 5000
|
|
8
|
+
|
|
9
|
+
/// Native-side join with SpeakerIdEngine: stamp the most recent
|
|
10
|
+
/// identification match onto the transcript. Fallback stays `0`/unknown
|
|
11
|
+
/// when the engine is off or nothing matched, so downstream schemas are
|
|
12
|
+
/// unchanged; the resolved name is additive.
|
|
13
|
+
private static func speakerFields() -> [String: Any] {
|
|
14
|
+
guard let match = SpeakerIdEngine.shared.recentMatch(withinMs: speakerMatchWindowMs) else {
|
|
15
|
+
return ["speakerId": 0]
|
|
16
|
+
}
|
|
17
|
+
return [
|
|
18
|
+
"speakerId": match.profileId,
|
|
19
|
+
"resolvedSpeakerId": match.profileId,
|
|
20
|
+
"resolvedSpeakerName": match.name,
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static func didReceivePartialTranscription(_ text: String) {
|
|
25
|
+
// Send partial result to server witgetConnectedBluetoothNameh proper formatting
|
|
26
|
+
let transcriptionLanguage =
|
|
27
|
+
UserDefaults.standard.string(forKey: "STTModelLanguageCode") ?? "en-US"
|
|
28
|
+
// Bridge.log("Glassly: Sending partial transcription: \(text), \(transcriptionLanguage)")
|
|
29
|
+
var transcription: [String: Any] = [
|
|
30
|
+
"type": "local_transcription",
|
|
31
|
+
"text": transcriptionLanguage == "en-US" ? text.lowercased() : text,
|
|
32
|
+
"isFinal": false,
|
|
33
|
+
"startTime": Int(Date().timeIntervalSince1970 * 1000) - 1000, // 1 second ago
|
|
34
|
+
"endTime": Int(Date().timeIntervalSince1970 * 1000),
|
|
35
|
+
"transcribeLanguage": transcriptionLanguage,
|
|
36
|
+
"provider": "sherpa-onnx",
|
|
37
|
+
]
|
|
38
|
+
transcription.merge(speakerFields()) { _, new in new }
|
|
39
|
+
|
|
40
|
+
Bridge.sendLocalTranscription(transcription: transcription)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static func didReceiveFinalTranscription(_ text: String) {
|
|
44
|
+
// Send final result to server with proper formatting
|
|
45
|
+
let transcriptionLanguage =
|
|
46
|
+
UserDefaults.standard.string(forKey: "STTModelLanguageCode") ?? "en-US"
|
|
47
|
+
Bridge.log("Glassly: Sending final transcription: \(text), \(transcriptionLanguage)")
|
|
48
|
+
if !text.isEmpty {
|
|
49
|
+
var transcription: [String: Any] = [
|
|
50
|
+
"type": "local_transcription",
|
|
51
|
+
"text": transcriptionLanguage == "en-US" ? text.lowercased() : text,
|
|
52
|
+
"isFinal": true,
|
|
53
|
+
"startTime": Int(Date().timeIntervalSince1970 * 1000) - 2000, // 2 seconds ago
|
|
54
|
+
"endTime": Int(Date().timeIntervalSince1970 * 1000),
|
|
55
|
+
"transcribeLanguage": transcriptionLanguage,
|
|
56
|
+
"provider": "sherpa-onnx",
|
|
57
|
+
]
|
|
58
|
+
let fields = speakerFields()
|
|
59
|
+
transcription.merge(fields) { _, new in new }
|
|
60
|
+
if let name = fields["resolvedSpeakerName"] as? String {
|
|
61
|
+
Bridge.log("SpeakerID: local final stamped as \(name)")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Bridge.sendLocalTranscription(transcription: transcription)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static func setSttModelDetails(_ path: String, _ languageCode: String) {
|
|
69
|
+
UserDefaults.standard.set(path, forKey: "STTModelPath")
|
|
70
|
+
UserDefaults.standard.set(languageCode, forKey: "STTModelLanguageCode")
|
|
71
|
+
UserDefaults.standard.synchronize()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static func getSttModelPath() -> String {
|
|
75
|
+
return UserDefaults.standard.string(forKey: "STTModelPath") ?? ""
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static func checkSTTModelAvailable() -> Bool {
|
|
79
|
+
guard let modelPath = UserDefaults.standard.string(forKey: "STTModelPath") else {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let fileManager = FileManager.default
|
|
84
|
+
|
|
85
|
+
// Check for tokens.txt (required for all models)
|
|
86
|
+
let tokensPath = (modelPath as NSString).appendingPathComponent("tokens.txt")
|
|
87
|
+
if !fileManager.fileExists(atPath: tokensPath) {
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check for CTC model
|
|
92
|
+
if firstExistingFile(in: modelPath, candidates: ["model.int8.onnx", "model.onnx"]) != nil {
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check for transducer model
|
|
97
|
+
let transducerFiles = [
|
|
98
|
+
["encoder.onnx", "encoder.int8.onnx"],
|
|
99
|
+
["decoder.onnx", "decoder.int8.onnx"],
|
|
100
|
+
["joiner.onnx", "joiner.int8.onnx"],
|
|
101
|
+
]
|
|
102
|
+
for candidates in transducerFiles {
|
|
103
|
+
if firstExistingFile(in: modelPath, candidates: candidates) == nil {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return true
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static func validateSTTModel(_ path: String) -> Bool {
|
|
112
|
+
// do {
|
|
113
|
+
let fileManager = FileManager.default
|
|
114
|
+
|
|
115
|
+
// Check for tokens.txt (required for all models)
|
|
116
|
+
let tokensPath = (path as NSString).appendingPathComponent("tokens.txt")
|
|
117
|
+
if !fileManager.fileExists(atPath: tokensPath) {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check for CTC model
|
|
122
|
+
if firstExistingFile(in: path, candidates: ["model.int8.onnx", "model.onnx"]) != nil {
|
|
123
|
+
return true
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check for transducer model
|
|
127
|
+
let transducerFiles = [
|
|
128
|
+
["encoder.onnx", "encoder.int8.onnx"],
|
|
129
|
+
["decoder.onnx", "decoder.int8.onnx"],
|
|
130
|
+
["joiner.onnx", "joiner.int8.onnx"],
|
|
131
|
+
]
|
|
132
|
+
var allTransducerFilesPresent = true
|
|
133
|
+
|
|
134
|
+
for candidates in transducerFiles {
|
|
135
|
+
if firstExistingFile(in: path, candidates: candidates) == nil {
|
|
136
|
+
allTransducerFilesPresent = false
|
|
137
|
+
break
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return allTransducerFilesPresent
|
|
142
|
+
// } catch {
|
|
143
|
+
// Bridge.log("STT_ERROR: \(error.localizedDescription)")
|
|
144
|
+
// return false
|
|
145
|
+
// }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static func extractTarBz2(sourcePath: String, destinationPath: String) -> Bool {
|
|
149
|
+
do {
|
|
150
|
+
let fileManager = FileManager.default
|
|
151
|
+
|
|
152
|
+
// Create destination directory if it doesn't exist
|
|
153
|
+
try fileManager.createDirectory(
|
|
154
|
+
atPath: destinationPath,
|
|
155
|
+
withIntermediateDirectories: true,
|
|
156
|
+
attributes: nil
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
// Use the Swift TarBz2Extractor with SWCompression
|
|
160
|
+
var extractionError: NSError?
|
|
161
|
+
let success = TarBz2Extractor.extractTarBz2From(
|
|
162
|
+
sourcePath,
|
|
163
|
+
to: destinationPath,
|
|
164
|
+
error: &extractionError
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if !success || extractionError != nil {
|
|
168
|
+
print(
|
|
169
|
+
"EXTRACTION_ERROR: \(extractionError?.localizedDescription ?? "Failed to extract tar.bz2")"
|
|
170
|
+
)
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
} catch {
|
|
175
|
+
Bridge.log("EXTRACTION_ERROR: \(error.localizedDescription)")
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private static func firstExistingFile(in directory: String, candidates: [String]) -> String? {
|
|
182
|
+
let fileManager = FileManager.default
|
|
183
|
+
for candidate in candidates {
|
|
184
|
+
let path = (directory as NSString).appendingPathComponent(candidate)
|
|
185
|
+
if fileManager.fileExists(atPath: path) {
|
|
186
|
+
return path
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return nil
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,466 @@
|
|
|
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
|
+
private static func firstExistingFile(in directory: String, candidates: [String]) -> String? {
|
|
58
|
+
let fileManager = FileManager.default
|
|
59
|
+
for candidate in candidates {
|
|
60
|
+
let path = (directory as NSString).appendingPathComponent(candidate)
|
|
61
|
+
if fileManager.fileExists(atPath: path) {
|
|
62
|
+
return path
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return nil
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Heuristically detect an NVIDIA Nemotron model directory.
|
|
70
|
+
*
|
|
71
|
+
* Nemotron is file-identical to a Zipformer transducer (encoder/decoder/joiner + tokens.txt),
|
|
72
|
+
* so filenames alone cannot distinguish the two. We check the on-disk directory name first โ
|
|
73
|
+
* STTModelManager names the folder after the model id โ and fall back to the README.md that
|
|
74
|
+
* ships inside every upstream archive.
|
|
75
|
+
*
|
|
76
|
+
* This is advisory only: sherpa-onnx does the authoritative dispatch from ONNX metadata. A
|
|
77
|
+
* false negative costs nothing but a less precise log line.
|
|
78
|
+
*/
|
|
79
|
+
private static func isNemotronModelDir(_ directory: String) -> Bool {
|
|
80
|
+
if (directory as NSString).lastPathComponent.range(of: "nemotron", options: .caseInsensitive) != nil {
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let readmePath = (directory as NSString).appendingPathComponent("README.md")
|
|
85
|
+
guard let readme = try? String(contentsOfFile: readmePath, encoding: .utf8) else {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
// Archive READMEs are small; cap the scan so a malformed file can't stall startup.
|
|
89
|
+
return readme.prefix(8192).range(of: "nemotron", options: .caseInsensitive) != nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Constructor that accepts a UIViewController to load model assets.
|
|
94
|
+
*/
|
|
95
|
+
init(context: UIViewController) {
|
|
96
|
+
self.context = context
|
|
97
|
+
transcriptionSessionStart = Date()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
deinit {
|
|
101
|
+
shutdown()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Initialize the Sherpa-ONNX recognizer.
|
|
106
|
+
* Loads models and configuration, sets up processing thread.
|
|
107
|
+
*/
|
|
108
|
+
func initialize() {
|
|
109
|
+
do {
|
|
110
|
+
var tokensPath: String
|
|
111
|
+
var modelType = "unknown"
|
|
112
|
+
let fileManager = FileManager.default
|
|
113
|
+
|
|
114
|
+
// Check if we have a custom model path set
|
|
115
|
+
if let customPath = SherpaOnnxTranscriber.customModelPath {
|
|
116
|
+
// Detect model type based on available files
|
|
117
|
+
let ctcModelPath = Self.firstExistingFile(
|
|
118
|
+
in: customPath,
|
|
119
|
+
candidates: ["model.int8.onnx", "model.onnx"]
|
|
120
|
+
)
|
|
121
|
+
let transducerEncoderPath = Self.firstExistingFile(
|
|
122
|
+
in: customPath,
|
|
123
|
+
candidates: ["encoder.int8.onnx", "encoder.onnx"]
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
tokensPath = (customPath as NSString).appendingPathComponent("tokens.txt")
|
|
127
|
+
|
|
128
|
+
// Verify tokens file exists
|
|
129
|
+
guard fileManager.fileExists(atPath: tokensPath) else {
|
|
130
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 1, userInfo: [
|
|
131
|
+
NSLocalizedDescriptionKey: "tokens.txt not found at path: \(customPath)",
|
|
132
|
+
])
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if let ctcModelPath {
|
|
136
|
+
// CTC model detected
|
|
137
|
+
modelType = "ctc"
|
|
138
|
+
Bridge.log("Detected CTC model at \(customPath)")
|
|
139
|
+
|
|
140
|
+
// Build the config and construct the recognizer inside one
|
|
141
|
+
// C-string scope: the config's `const char*` fields point at
|
|
142
|
+
// buffers owned by that scope (see toCPointer in
|
|
143
|
+
// SherpaOnnx.swift). The config must not escape it.
|
|
144
|
+
recognizer = withSherpaOnnxCStrings {
|
|
145
|
+
// Create CTC model config using Zipformer2Ctc
|
|
146
|
+
let nemoCtc = sherpaOnnxOnlineNemoCtcModelConfig(
|
|
147
|
+
model: ctcModelPath
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
// Create model config with CTC
|
|
151
|
+
let modelConfig = sherpaOnnxOnlineModelConfig(
|
|
152
|
+
tokens: tokensPath,
|
|
153
|
+
numThreads: 1,
|
|
154
|
+
nemoCtc: nemoCtc
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
// Configure recognizer
|
|
158
|
+
let 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
|
+
return SherpaOnnxRecognizer(config: &config)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
} else if let transducerEncoderPath {
|
|
174
|
+
// Transducer model detected. NVIDIA Nemotron ships the same
|
|
175
|
+
// encoder/decoder/joiner triplet as a Zipformer transducer and is loaded
|
|
176
|
+
// through this very same config โ sherpa-onnx sniffs the ONNX metadata and
|
|
177
|
+
// dispatches to its NeMo transducer implementation on its own. We detect it
|
|
178
|
+
// only to log it distinctly; there is no Nemotron-specific config struct.
|
|
179
|
+
//
|
|
180
|
+
// Critically, we must NOT set a modelType override for Nemotron:
|
|
181
|
+
// "transducer" would force the generic Zipformer path and mis-decode.
|
|
182
|
+
// Leaving it unset lets sherpa-onnx auto-detect.
|
|
183
|
+
let isNemotron = Self.isNemotronModelDir(customPath)
|
|
184
|
+
modelType = isNemotron ? "nemotron" : "transducer"
|
|
185
|
+
Bridge.log("Detected \(modelType) model at \(customPath)")
|
|
186
|
+
|
|
187
|
+
let decoderPath = Self.firstExistingFile(
|
|
188
|
+
in: customPath,
|
|
189
|
+
candidates: ["decoder.int8.onnx", "decoder.onnx"]
|
|
190
|
+
)
|
|
191
|
+
let joinerPath = Self.firstExistingFile(
|
|
192
|
+
in: customPath,
|
|
193
|
+
candidates: ["joiner.int8.onnx", "joiner.onnx"]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
// Verify all transducer files exist
|
|
197
|
+
guard let decoderPath,
|
|
198
|
+
let joinerPath
|
|
199
|
+
else {
|
|
200
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 1, userInfo: [
|
|
201
|
+
NSLocalizedDescriptionKey: "Transducer model files incomplete at path: \(customPath)",
|
|
202
|
+
])
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Build the config and construct the recognizer inside one
|
|
206
|
+
// C-string scope: the config's `const char*` fields point at
|
|
207
|
+
// buffers owned by that scope (see toCPointer in
|
|
208
|
+
// SherpaOnnx.swift). The config must not escape it.
|
|
209
|
+
recognizer = withSherpaOnnxCStrings {
|
|
210
|
+
// Create Sherpa-ONNX transducer model config
|
|
211
|
+
let transducer = sherpaOnnxOnlineTransducerModelConfig(
|
|
212
|
+
encoder: transducerEncoderPath,
|
|
213
|
+
decoder: decoderPath,
|
|
214
|
+
joiner: joinerPath
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
// Create model config
|
|
218
|
+
let modelConfig = sherpaOnnxOnlineModelConfig(
|
|
219
|
+
tokens: tokensPath,
|
|
220
|
+
transducer: transducer,
|
|
221
|
+
numThreads: 1
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
// Configure recognizer
|
|
225
|
+
let featureConfig = sherpaOnnxFeatureConfig()
|
|
226
|
+
|
|
227
|
+
var config = sherpaOnnxOnlineRecognizerConfig(
|
|
228
|
+
featConfig: featureConfig,
|
|
229
|
+
modelConfig: modelConfig,
|
|
230
|
+
enableEndpoint: true,
|
|
231
|
+
rule1MinTrailingSilence: 1.2,
|
|
232
|
+
rule2MinTrailingSilence: 0.8,
|
|
233
|
+
rule3MinUtteranceLength: 10.0
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
// Create recognizer with the wrapper
|
|
237
|
+
return SherpaOnnxRecognizer(config: &config)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
} else {
|
|
241
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 1, userInfo: [
|
|
242
|
+
NSLocalizedDescriptionKey: "No valid model files found at path: \(customPath)",
|
|
243
|
+
])
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
Bridge.log("No Sherpa ONNX model available. Transcription will be disabled.")
|
|
247
|
+
Bridge.log("Please download a model using the model downloader in settings.")
|
|
248
|
+
recognizer = nil
|
|
249
|
+
isRunning = false
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if recognizer == nil {
|
|
254
|
+
throw NSError(domain: "SherpaOnnxTranscriber", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to create recognizer"])
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
startProcessingTask()
|
|
258
|
+
isRunning = true
|
|
259
|
+
|
|
260
|
+
Bridge.log("Sherpa-ONNX ASR initialized successfully with \(modelType) model")
|
|
261
|
+
|
|
262
|
+
} catch {
|
|
263
|
+
Bridge.log("Failed to initialize Sherpa-ONNX: \(error.localizedDescription)")
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Handle transcription results - send only to delegate
|
|
269
|
+
*/
|
|
270
|
+
private func handleTranscriptionResult(text: String, isFinal: Bool) {
|
|
271
|
+
// Forward to delegate if set
|
|
272
|
+
DispatchQueue.main.async { [weak self] in
|
|
273
|
+
if isFinal {
|
|
274
|
+
STTTools.didReceiveFinalTranscription(text)
|
|
275
|
+
} else {
|
|
276
|
+
STTTools.didReceivePartialTranscription(text)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Feed PCM audio data (16-bit little endian) into the transcriber.
|
|
283
|
+
* This method should be called continuously with short chunks (e.g., 100-300ms).
|
|
284
|
+
*
|
|
285
|
+
* Audio is queued directly; microphone VAD gating is not applied in the SDK.
|
|
286
|
+
*/
|
|
287
|
+
func acceptAudio(pcm16le: Data) {
|
|
288
|
+
guard isRunning else {
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
queueAudioData(pcm16le)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private func queueAudioData(_ pcm16le: Data) {
|
|
296
|
+
pcmQueue.async { [weak self] in
|
|
297
|
+
guard let self = self else { return }
|
|
298
|
+
|
|
299
|
+
let queueSizeBefore = self.pcmBuffers.count
|
|
300
|
+
self.pcmBuffers.append(pcm16le)
|
|
301
|
+
|
|
302
|
+
// Keep queue size manageable
|
|
303
|
+
if self.pcmBuffers.count > Self.QUEUE_CAPACITY {
|
|
304
|
+
let removedBuffer = self.pcmBuffers.removeFirst()
|
|
305
|
+
Bridge.log("โ ๏ธ Audio queue overflow - dropped buffer of \(removedBuffer.count) bytes")
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Start a background task to continuously consume audio and decode using Sherpa.
|
|
312
|
+
*/
|
|
313
|
+
private func startProcessingTask() {
|
|
314
|
+
Bridge.log("๐ Starting Sherpa-ONNX processing task...")
|
|
315
|
+
|
|
316
|
+
processingQueue = DispatchQueue(label: "com.augmentos.sherpaonnx.processor", qos: .userInitiated)
|
|
317
|
+
|
|
318
|
+
let workItem = DispatchWorkItem { [weak self] in
|
|
319
|
+
self?.runLoop()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
processingTask = workItem
|
|
323
|
+
processingQueue?.async(execute: workItem)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Main processing loop that handles transcription in real-time.
|
|
328
|
+
* Pulls audio from queue, feeds into Sherpa, emits partial/final results.
|
|
329
|
+
*/
|
|
330
|
+
private func runLoop() {
|
|
331
|
+
Bridge.log("๐ Sherpa-ONNX processing loop started")
|
|
332
|
+
|
|
333
|
+
while isRunning {
|
|
334
|
+
// Pull data from queue
|
|
335
|
+
var audioData: Data?
|
|
336
|
+
|
|
337
|
+
pcmQueue.sync {
|
|
338
|
+
if !self.pcmBuffers.isEmpty {
|
|
339
|
+
audioData = self.pcmBuffers.removeFirst()
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if let data = audioData {
|
|
344
|
+
// Synchronize access to recognizer to prevent race conditions
|
|
345
|
+
objc_sync_enter(self)
|
|
346
|
+
defer { objc_sync_exit(self) }
|
|
347
|
+
|
|
348
|
+
guard let recognizer = recognizer else {
|
|
349
|
+
Bridge.log("โ ๏ธ Recognizer not available, skipping audio chunk")
|
|
350
|
+
continue
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
do {
|
|
354
|
+
// Convert PCM to float [-1.0, 1.0]
|
|
355
|
+
let floatBuf = toFloatArray(from: data)
|
|
356
|
+
|
|
357
|
+
// Pass audio data to the Sherpa-ONNX stream
|
|
358
|
+
recognizer.acceptWaveform(samples: floatBuf, sampleRate: Self.SAMPLE_RATE)
|
|
359
|
+
|
|
360
|
+
// Decode continuously while model is ready
|
|
361
|
+
var decodeCount = 0
|
|
362
|
+
while recognizer.isReady() {
|
|
363
|
+
recognizer.decode()
|
|
364
|
+
decodeCount += 1
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// If utterance endpoint detected
|
|
368
|
+
if recognizer.isEndpoint() {
|
|
369
|
+
let result = recognizer.getResult()
|
|
370
|
+
let finalText = result.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
371
|
+
|
|
372
|
+
if !finalText.isEmpty {
|
|
373
|
+
handleTranscriptionResult(text: finalText, isFinal: true)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
recognizer.reset() // Start new utterance
|
|
377
|
+
lastPartialResult = ""
|
|
378
|
+
} else {
|
|
379
|
+
// Emit partial results if changed
|
|
380
|
+
let result = recognizer.getResult()
|
|
381
|
+
let partial = result.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
382
|
+
|
|
383
|
+
if partial != lastPartialResult, !partial.isEmpty {
|
|
384
|
+
handleTranscriptionResult(text: partial, isFinal: false)
|
|
385
|
+
lastPartialResult = partial
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} catch {
|
|
389
|
+
Bridge.log("โ Error processing audio: \(error.localizedDescription)")
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
// Sleep briefly to avoid tight CPU loop if no audio is available
|
|
393
|
+
Thread.sleep(forTimeInterval: 0.01)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
Bridge.log("ASR processing thread stopped")
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Convert 16-bit PCM byte data (little-endian) to float array [-1.0, 1.0].
|
|
402
|
+
*/
|
|
403
|
+
private func toFloatArray(from pcmData: Data) -> [Float] {
|
|
404
|
+
let count = pcmData.count / 2
|
|
405
|
+
var samples = [Float](repeating: 0, count: count)
|
|
406
|
+
|
|
407
|
+
pcmData.withUnsafeBytes { (bufferPointer: UnsafeRawBufferPointer) in
|
|
408
|
+
if let address = bufferPointer.baseAddress {
|
|
409
|
+
let int16Pointer = address.bindMemory(to: Int16.self, capacity: count)
|
|
410
|
+
|
|
411
|
+
for i in 0 ..< count {
|
|
412
|
+
// Convert from little-endian if needed
|
|
413
|
+
var sample = int16Pointer[i]
|
|
414
|
+
if CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderBigEndian.rawValue) {
|
|
415
|
+
sample = Int16(littleEndian: sample)
|
|
416
|
+
}
|
|
417
|
+
samples[i] = Float(sample) / 32768.0
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return samples
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Stop transcription processing.
|
|
427
|
+
* This shuts down the processing thread and releases Sherpa-ONNX resources.
|
|
428
|
+
*/
|
|
429
|
+
func shutdown() {
|
|
430
|
+
Bridge.log("๐ Shutting down SherpaOnnxTranscriber...")
|
|
431
|
+
|
|
432
|
+
isRunning = false
|
|
433
|
+
processingTask?.cancel()
|
|
434
|
+
|
|
435
|
+
// Synchronize access to recognizer during shutdown
|
|
436
|
+
objc_sync_enter(self)
|
|
437
|
+
defer { objc_sync_exit(self) }
|
|
438
|
+
|
|
439
|
+
// The recognizer will be automatically cleaned up by ARC when set to nil
|
|
440
|
+
if recognizer != nil {
|
|
441
|
+
Bridge.log("๐งน Cleaning up Sherpa-ONNX recognizer")
|
|
442
|
+
recognizer = nil
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Clear any remaining audio buffers
|
|
446
|
+
pcmQueue.sync {
|
|
447
|
+
let remainingBuffers = self.pcmBuffers.count
|
|
448
|
+
if remainingBuffers > 0 {
|
|
449
|
+
Bridge.log("๐๏ธ Clearing \(remainingBuffers) remaining audio buffers")
|
|
450
|
+
}
|
|
451
|
+
self.pcmBuffers.removeAll()
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
Bridge.log("โ
SherpaOnnxTranscriber shutdown complete")
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Restarts the transcriber after a model change.
|
|
459
|
+
* Shuts down existing resources, clears buffers, and reinitializes the recognizer.
|
|
460
|
+
*/
|
|
461
|
+
func restart() {
|
|
462
|
+
Bridge.log("โป๏ธ Restarting SherpaOnnxTranscriber...")
|
|
463
|
+
shutdown()
|
|
464
|
+
initialize()
|
|
465
|
+
}
|
|
466
|
+
}
|