@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,171 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Utilities for offline Sherpa-ONNX Supertonic 3 TTS.
|
|
4
|
+
///
|
|
5
|
+
/// Supertonic 3 is a single multilingual model bundle (31 languages) with
|
|
6
|
+
/// 10 preset voice styles packed into voice.bin. Speaker IDs are indexed
|
|
7
|
+
/// alphabetically by JSON filename, so the order is F1, F2, F3, F4, F5,
|
|
8
|
+
/// M1, M2, M3, M4, M5 (sids 0..9). We default to F1 (sid 0) for a female
|
|
9
|
+
/// voice.
|
|
10
|
+
class TTSTools {
|
|
11
|
+
private static let kFileDurationPredictor = "duration_predictor.int8.onnx"
|
|
12
|
+
private static let kFileTextEncoder = "text_encoder.int8.onnx"
|
|
13
|
+
private static let kFileVectorEstimator = "vector_estimator.int8.onnx"
|
|
14
|
+
private static let kFileVocoder = "vocoder.int8.onnx"
|
|
15
|
+
private static let kFileTtsJson = "tts.json"
|
|
16
|
+
private static let kFileUnicodeIndexer = "unicode_indexer.bin"
|
|
17
|
+
private static let kFileVoiceStyle = "voice.bin"
|
|
18
|
+
|
|
19
|
+
private static let kRequiredFiles: [String] = [
|
|
20
|
+
kFileDurationPredictor,
|
|
21
|
+
kFileTextEncoder,
|
|
22
|
+
kFileVectorEstimator,
|
|
23
|
+
kFileVocoder,
|
|
24
|
+
kFileTtsJson,
|
|
25
|
+
kFileUnicodeIndexer,
|
|
26
|
+
kFileVoiceStyle,
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
private static let kSupportedLangs: Set<String> = [
|
|
30
|
+
"en", "ko", "ja", "ar", "bg", "cs", "da", "de", "el", "es", "et",
|
|
31
|
+
"fi", "fr", "hi", "hr", "hu", "id", "it", "lt", "lv", "nl", "pl",
|
|
32
|
+
"pt", "ro", "ru", "sk", "sl", "sv", "tr", "uk", "vi",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
static func setTtsModelDetails(_ path: String, _ languageCode: String) {
|
|
36
|
+
UserDefaults.standard.set(path, forKey: "TTSModelPath")
|
|
37
|
+
UserDefaults.standard.set(languageCode, forKey: "TTSModelLanguageCode")
|
|
38
|
+
UserDefaults.standard.synchronize()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static func getTtsModelPath() -> String {
|
|
42
|
+
return UserDefaults.standard.string(forKey: "TTSModelPath") ?? ""
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static func getTtsModelLanguage() -> String {
|
|
46
|
+
return UserDefaults.standard.string(forKey: "TTSModelLanguageCode") ?? "en-US"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static func checkTTSModelAvailable() -> Bool {
|
|
50
|
+
guard let modelPath = UserDefaults.standard.string(forKey: "TTSModelPath") else {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
return validateTTSModel(modelPath)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static func validateTTSModel(_ path: String) -> Bool {
|
|
57
|
+
let fileManager = FileManager.default
|
|
58
|
+
var isDirectory: ObjCBool = false
|
|
59
|
+
if !fileManager.fileExists(atPath: path, isDirectory: &isDirectory) || !isDirectory.boolValue {
|
|
60
|
+
Bridge.log("TTS model path does not exist or is not a directory: \(path)")
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for name in kRequiredFiles {
|
|
65
|
+
let filePath = (path as NSString).appendingPathComponent(name)
|
|
66
|
+
if !fileManager.fileExists(atPath: filePath) {
|
|
67
|
+
Bridge.log("TTS model missing required file '\(name)' at: \(path)")
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
let size = (try? fileManager.attributesOfItem(atPath: filePath)[.size] as? UInt64) ?? 0
|
|
71
|
+
if size == 0 {
|
|
72
|
+
Bridge.log("TTS model file '\(name)' is empty at: \(path)")
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static func generateTtsAudio(
|
|
81
|
+
text: String,
|
|
82
|
+
modelPath: String,
|
|
83
|
+
outputPath: String,
|
|
84
|
+
speakerId: Int,
|
|
85
|
+
speed: Double
|
|
86
|
+
) -> Bool {
|
|
87
|
+
guard !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
|
88
|
+
Bridge.log("TTS_ERROR: text is empty")
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
guard validateTTSModel(modelPath) else {
|
|
92
|
+
Bridge.log("TTS_ERROR: model is invalid: \(modelPath)")
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
do {
|
|
97
|
+
let outputURL = URL(fileURLWithPath: outputPath)
|
|
98
|
+
try FileManager.default.createDirectory(
|
|
99
|
+
at: outputURL.deletingLastPathComponent(),
|
|
100
|
+
withIntermediateDirectories: true,
|
|
101
|
+
attributes: nil
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
// Build the config and construct the engine inside one C-string
|
|
105
|
+
// scope: the config's `const char*` fields point at buffers owned by
|
|
106
|
+
// that scope (see toCPointer in SherpaOnnx.swift). Only construction
|
|
107
|
+
// goes inside — `tts` itself is safe to use afterwards, since
|
|
108
|
+
// sherpa-onnx copies every string during construction.
|
|
109
|
+
let tts = withSherpaOnnxCStrings { () -> SherpaOnnxOfflineTtsWrapper in
|
|
110
|
+
let supertonic = sherpaOnnxOfflineTtsSupertonicModelConfig(
|
|
111
|
+
durationPredictor: (modelPath as NSString).appendingPathComponent(kFileDurationPredictor),
|
|
112
|
+
textEncoder: (modelPath as NSString).appendingPathComponent(kFileTextEncoder),
|
|
113
|
+
vectorEstimator: (modelPath as NSString).appendingPathComponent(kFileVectorEstimator),
|
|
114
|
+
vocoder: (modelPath as NSString).appendingPathComponent(kFileVocoder),
|
|
115
|
+
ttsJson: (modelPath as NSString).appendingPathComponent(kFileTtsJson),
|
|
116
|
+
unicodeIndexer: (modelPath as NSString).appendingPathComponent(kFileUnicodeIndexer),
|
|
117
|
+
voiceStyle: (modelPath as NSString).appendingPathComponent(kFileVoiceStyle)
|
|
118
|
+
)
|
|
119
|
+
let modelConfig = sherpaOnnxOfflineTtsModelConfig(
|
|
120
|
+
numThreads: 8,
|
|
121
|
+
supertonic: supertonic
|
|
122
|
+
)
|
|
123
|
+
var config = sherpaOnnxOfflineTtsConfig(model: modelConfig, maxNumSentences: 1)
|
|
124
|
+
return SherpaOnnxOfflineTtsWrapper(config: &config)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The new wrapper dropped numSpeakers() in v1.13.2; call the C API
|
|
128
|
+
// directly. The C signature accepts the underlying OpaquePointer.
|
|
129
|
+
let numSpeakers = max(1, Int(SherpaOnnxOfflineTtsNumSpeakers(tts.tts)))
|
|
130
|
+
let sid = max(0, min(speakerId, numSpeakers - 1))
|
|
131
|
+
let clampedSpeed = Float(min(max(speed, 0.5), 2.0))
|
|
132
|
+
let lang = languageTagToSupertonicLang(getTtsModelLanguage())
|
|
133
|
+
|
|
134
|
+
// generate(...) no longer accepts `extra`; that field moved onto
|
|
135
|
+
// SherpaOnnxGenerationConfigSwift and is delivered via
|
|
136
|
+
// generateWithConfig(...).
|
|
137
|
+
//
|
|
138
|
+
// numSteps controls the Supertonic flow-matching vocoder's
|
|
139
|
+
// iteration count. The Swift wrapper defaults to 1, which produces
|
|
140
|
+
// unintelligible robot audio — sherpa-onnx's actual C-side default
|
|
141
|
+
// is 5. Pin it here so we don't inherit the wrapper's bug.
|
|
142
|
+
let genConfig = SherpaOnnxGenerationConfigSwift(
|
|
143
|
+
speed: clampedSpeed,
|
|
144
|
+
sid: sid,
|
|
145
|
+
numSteps: 5,
|
|
146
|
+
extra: ["lang": lang]
|
|
147
|
+
)
|
|
148
|
+
let audio = tts.generateWithConfig(
|
|
149
|
+
text: text,
|
|
150
|
+
config: genConfig,
|
|
151
|
+
callback: nil,
|
|
152
|
+
arg: nil
|
|
153
|
+
)
|
|
154
|
+
let saved = audio.save(filename: outputPath) == 1
|
|
155
|
+
Bridge.log(
|
|
156
|
+
"TTS generated \(outputPath): saved=\(saved) sid=\(sid) lang=\(lang) " +
|
|
157
|
+
"sr=\(audio.sampleRate) samples=\(audio.n) speed=\(clampedSpeed)"
|
|
158
|
+
)
|
|
159
|
+
return saved
|
|
160
|
+
} catch {
|
|
161
|
+
Bridge.log("TTS_ERROR: \(error.localizedDescription)")
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// Convert a BCP-47 tag (e.g. "en-US", "fr-FR") to a Supertonic 2-letter code.
|
|
167
|
+
private static func languageTagToSupertonicLang(_ tag: String) -> String {
|
|
168
|
+
let primary = tag.split(separator: "-").first.map(String.init)?.lowercased() ?? "en"
|
|
169
|
+
return kSupportedLangs.contains(primary) ? primary : "en"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public struct BluetoothSDKConfiguration {
|
|
4
|
+
public static let `default` = BluetoothSDKConfiguration()
|
|
5
|
+
|
|
6
|
+
public let analytics: BluetoothSdkAnalyticsConfiguration
|
|
7
|
+
|
|
8
|
+
public init(analytics: BluetoothSdkAnalyticsConfiguration = BluetoothSdkAnalyticsConfiguration()) {
|
|
9
|
+
self.analytics = analytics
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public enum DeviceModel: String {
|
|
14
|
+
case g1
|
|
15
|
+
case g2
|
|
16
|
+
case mentraLive
|
|
17
|
+
case mentraNex
|
|
18
|
+
case mach1
|
|
19
|
+
case z100
|
|
20
|
+
case frame
|
|
21
|
+
case nimo
|
|
22
|
+
case ar99
|
|
23
|
+
case simulated
|
|
24
|
+
case r1
|
|
25
|
+
|
|
26
|
+
public var deviceType: String {
|
|
27
|
+
switch self {
|
|
28
|
+
case .g1:
|
|
29
|
+
DeviceTypes.G1
|
|
30
|
+
case .g2:
|
|
31
|
+
DeviceTypes.G2
|
|
32
|
+
case .mentraLive:
|
|
33
|
+
DeviceTypes.LIVE
|
|
34
|
+
case .mentraNex:
|
|
35
|
+
DeviceTypes.NEX
|
|
36
|
+
case .mach1:
|
|
37
|
+
DeviceTypes.MACH1
|
|
38
|
+
case .z100:
|
|
39
|
+
DeviceTypes.Z100
|
|
40
|
+
case .frame:
|
|
41
|
+
DeviceTypes.FRAME
|
|
42
|
+
case .nimo:
|
|
43
|
+
DeviceTypes.NIMO
|
|
44
|
+
case .ar99:
|
|
45
|
+
DeviceTypes.AR99
|
|
46
|
+
case .simulated:
|
|
47
|
+
DeviceTypes.SIMULATED
|
|
48
|
+
case .r1:
|
|
49
|
+
ControllerTypes.R1
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public static func fromDeviceType(_ deviceType: String?) -> DeviceModel {
|
|
54
|
+
switch deviceType {
|
|
55
|
+
case DeviceTypes.G1:
|
|
56
|
+
.g1
|
|
57
|
+
case DeviceTypes.G2:
|
|
58
|
+
.g2
|
|
59
|
+
case DeviceTypes.LIVE:
|
|
60
|
+
.mentraLive
|
|
61
|
+
case DeviceTypes.NEX:
|
|
62
|
+
.mentraNex
|
|
63
|
+
case DeviceTypes.MACH1:
|
|
64
|
+
.mach1
|
|
65
|
+
case DeviceTypes.Z100:
|
|
66
|
+
.z100
|
|
67
|
+
case DeviceTypes.FRAME:
|
|
68
|
+
.frame
|
|
69
|
+
case DeviceTypes.NIMO:
|
|
70
|
+
.nimo
|
|
71
|
+
case DeviceTypes.AR99:
|
|
72
|
+
.ar99
|
|
73
|
+
case DeviceTypes.SIMULATED:
|
|
74
|
+
.simulated
|
|
75
|
+
case ControllerTypes.R1:
|
|
76
|
+
.r1
|
|
77
|
+
default:
|
|
78
|
+
.mentraLive
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
84
|
+
public let model: DeviceModel
|
|
85
|
+
public let name: String
|
|
86
|
+
/// CoreBluetooth identifier when available.
|
|
87
|
+
public let identifier: String?
|
|
88
|
+
public let projectName: String?
|
|
89
|
+
public let rssi: Int?
|
|
90
|
+
/// Stable app-facing scan-result key. Do not parse; use typed fields instead.
|
|
91
|
+
public let id: String
|
|
92
|
+
public let pairingMode: Bool?
|
|
93
|
+
public let pairingCode: String?
|
|
94
|
+
public let securePairingCapable: Bool?
|
|
95
|
+
|
|
96
|
+
public init(
|
|
97
|
+
model: DeviceModel,
|
|
98
|
+
name: String,
|
|
99
|
+
identifier: String? = nil,
|
|
100
|
+
projectName: String? = nil,
|
|
101
|
+
rssi: Int? = nil,
|
|
102
|
+
id: String? = nil,
|
|
103
|
+
pairingMode: Bool? = nil,
|
|
104
|
+
pairingCode: String? = nil,
|
|
105
|
+
securePairingCapable: Bool? = nil
|
|
106
|
+
) {
|
|
107
|
+
self.model = model
|
|
108
|
+
self.name = name
|
|
109
|
+
self.identifier = identifier
|
|
110
|
+
self.projectName = projectName
|
|
111
|
+
self.rssi = rssi
|
|
112
|
+
self.id = id ?? identifier.flatMap { $0.isEmpty ? nil : $0 } ?? "\(model.deviceType):\(name)"
|
|
113
|
+
self.pairingMode = pairingMode
|
|
114
|
+
self.pairingCode = pairingCode
|
|
115
|
+
self.securePairingCapable = securePairingCapable
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public var description: String {
|
|
119
|
+
"Device(model: \(model), name: \(name))"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var dictionary: [String: Any] {
|
|
123
|
+
var values: [String: Any] = [
|
|
124
|
+
"id": id,
|
|
125
|
+
"model": model.deviceType,
|
|
126
|
+
"name": name,
|
|
127
|
+
]
|
|
128
|
+
if let identifier, !identifier.isEmpty {
|
|
129
|
+
values["address"] = identifier
|
|
130
|
+
}
|
|
131
|
+
if let projectName, !projectName.isEmpty {
|
|
132
|
+
values["projectName"] = projectName
|
|
133
|
+
}
|
|
134
|
+
if let rssi {
|
|
135
|
+
values["rssi"] = rssi
|
|
136
|
+
}
|
|
137
|
+
if let pairingMode {
|
|
138
|
+
values["pairingMode"] = pairingMode
|
|
139
|
+
}
|
|
140
|
+
if let pairingCode, !pairingCode.isEmpty {
|
|
141
|
+
values["pairingCode"] = pairingCode
|
|
142
|
+
}
|
|
143
|
+
if let securePairingCapable {
|
|
144
|
+
values["securePairingCapable"] = securePairingCapable
|
|
145
|
+
}
|
|
146
|
+
return values
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
init?(values: [String: Any]) {
|
|
150
|
+
guard let model = stringValue(values, "model") else { return nil }
|
|
151
|
+
guard let name = stringValue(values, "name") else { return nil }
|
|
152
|
+
let identifier = stringValue(values, "address").flatMap { $0.isEmpty ? nil : $0 }
|
|
153
|
+
let projectName = stringValue(values, "projectName").flatMap { $0.isEmpty ? nil : $0 }
|
|
154
|
+
let rssi = intValue(values["rssi"])
|
|
155
|
+
self.init(
|
|
156
|
+
model: DeviceModel.fromDeviceType(model),
|
|
157
|
+
name: name,
|
|
158
|
+
identifier: identifier,
|
|
159
|
+
projectName: projectName,
|
|
160
|
+
rssi: rssi,
|
|
161
|
+
id: stringValue(values, "id"),
|
|
162
|
+
pairingMode: boolValue(values, "pairingMode"),
|
|
163
|
+
pairingCode: stringValue(values, "pairingCode").flatMap { $0.isEmpty ? nil : $0 },
|
|
164
|
+
securePairingCapable: boolValue(values, "securePairingCapable")
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public struct ConnectOptions {
|
|
170
|
+
public let saveAsDefault: Bool
|
|
171
|
+
public let cancelExistingConnectionAttempt: Bool
|
|
172
|
+
|
|
173
|
+
public init(saveAsDefault: Bool = true, cancelExistingConnectionAttempt: Bool = true) {
|
|
174
|
+
self.saveAsDefault = saveAsDefault
|
|
175
|
+
self.cancelExistingConnectionAttempt = cancelExistingConnectionAttempt
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
//
|
|
2
|
+
// AudioSessionMonitor.swift
|
|
3
|
+
// AOS
|
|
4
|
+
//
|
|
5
|
+
// Monitors iOS AVAudioSession for Bluetooth audio device connections
|
|
6
|
+
// Used to detect when Mentra Live glasses are paired/connected for audio
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import AVFoundation
|
|
10
|
+
import Foundation
|
|
11
|
+
import UIKit
|
|
12
|
+
|
|
13
|
+
class AudioSessionMonitor {
|
|
14
|
+
/// Singleton instance
|
|
15
|
+
private static var instance: AudioSessionMonitor?
|
|
16
|
+
|
|
17
|
+
// Current monitoring state
|
|
18
|
+
static var isMonitoring = false
|
|
19
|
+
static var devicePattern: String?
|
|
20
|
+
static var callback: ((Bool, String?) -> Void)?
|
|
21
|
+
|
|
22
|
+
private init() {
|
|
23
|
+
Bridge.log("AudioMonitor: Initialized")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static func getInstance() -> AudioSessionMonitor {
|
|
27
|
+
if instance == nil {
|
|
28
|
+
instance = AudioSessionMonitor()
|
|
29
|
+
}
|
|
30
|
+
return instance!
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Check if AVAudioSession is configured for Bluetooth
|
|
34
|
+
/// Returns true if session allows Bluetooth audio
|
|
35
|
+
/// NOTE: We don't configure the session - PhoneMic.swift handles that when recording
|
|
36
|
+
static func isAudioSessionConfigured() -> Bool {
|
|
37
|
+
let session = AVAudioSession.sharedInstance()
|
|
38
|
+
|
|
39
|
+
// Check if category supports Bluetooth
|
|
40
|
+
let category = session.category
|
|
41
|
+
let supportsBluetooth =
|
|
42
|
+
category == .playAndRecord || category == .record || category == .multiRoute
|
|
43
|
+
|
|
44
|
+
Bridge.log(
|
|
45
|
+
"AudioMonitor: Audio session category: \(category.rawValue), supports BT: \(supportsBluetooth)"
|
|
46
|
+
)
|
|
47
|
+
return supportsBluetooth
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Check if a Bluetooth audio device matching the pattern is currently the active audio route
|
|
51
|
+
/// Returns true if device is actively routing audio
|
|
52
|
+
static func isAudioDeviceConnected(devicePattern: String) -> Bool {
|
|
53
|
+
let session = AVAudioSession.sharedInstance()
|
|
54
|
+
let outputs = session.currentRoute.outputs
|
|
55
|
+
|
|
56
|
+
Bridge.log("AudioMonitor: Checking active route, output count: \(outputs.count)")
|
|
57
|
+
for output in outputs {
|
|
58
|
+
Bridge.log("AudioMonitor: - \(output.portName) (type: \(output.portType.rawValue))")
|
|
59
|
+
if output.portType == .bluetoothHFP || output.portType == .bluetoothA2DP {
|
|
60
|
+
if output.portName.localizedCaseInsensitiveContains(devicePattern) {
|
|
61
|
+
Bridge.log("AudioMonitor: ✅ Found active audio device: \(output.portName)")
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Bridge.log("AudioMonitor: No active audio device matching '\(devicePattern)'")
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static func isOtherAudioDeviceConnected(devicePattern: String) -> Bool {
|
|
72
|
+
let connected = isAudioDeviceConnected(devicePattern: devicePattern)
|
|
73
|
+
if connected {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// do we have multiple devices connected?
|
|
78
|
+
let session = AVAudioSession.sharedInstance()
|
|
79
|
+
let outputs = session.currentRoute.outputs
|
|
80
|
+
|
|
81
|
+
Bridge.log("AudioMonitor: Checking active route, output count: \(outputs.count)")
|
|
82
|
+
if outputs.count > 1 {
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Check if a Bluetooth device matching the pattern is paired (appears in availableInputs)
|
|
90
|
+
/// Returns true if device is found (paired), WITHOUT activating it
|
|
91
|
+
/// This avoids switching A2DP music playback to HFP microphone mode
|
|
92
|
+
static func isDevicePaired(devicePattern: String) -> Bool {
|
|
93
|
+
let session = AVAudioSession.sharedInstance()
|
|
94
|
+
|
|
95
|
+
// Check if already active (using A2DP for music or HFP for calls)
|
|
96
|
+
if isAudioDeviceConnected(devicePattern: devicePattern) {
|
|
97
|
+
Bridge.log("AudioMonitor: Device '\(devicePattern)' already active")
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Try to find in availableInputs (includes paired devices)
|
|
102
|
+
guard let availableInputs = session.availableInputs else {
|
|
103
|
+
Bridge.log("AudioMonitor: ❌ availableInputs is nil")
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Bridge.log("AudioMonitor: availableInputs count: \(availableInputs.count)")
|
|
108
|
+
for input in availableInputs {
|
|
109
|
+
Bridge.log("AudioMonitor: - \(input.portName) (type: \(input.portType.rawValue))")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let bluetoothInput = availableInputs.first { input in
|
|
113
|
+
input.portType == .bluetoothHFP
|
|
114
|
+
&& input.portName.localizedCaseInsensitiveContains(devicePattern)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if let btInput = bluetoothInput {
|
|
118
|
+
Bridge.log(
|
|
119
|
+
"AudioMonitor: ✅ Found paired device '\(btInput.portName)' (not activating to preserve A2DP)"
|
|
120
|
+
)
|
|
121
|
+
return true
|
|
122
|
+
} else {
|
|
123
|
+
Bridge.log(
|
|
124
|
+
"AudioMonitor: ❌ Bluetooth HFP device '\(devicePattern)' not found in availableInputs"
|
|
125
|
+
)
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Start monitoring for audio route changes
|
|
131
|
+
/// Callback will be called when device matching pattern connects/disconnects
|
|
132
|
+
static func startMonitoring(devicePattern: String, callback: @escaping (Bool, String?) -> Void) {
|
|
133
|
+
guard !isMonitoring else {
|
|
134
|
+
Bridge.log("AudioMonitor: Already monitoring")
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
self.devicePattern = devicePattern
|
|
139
|
+
self.callback = callback
|
|
140
|
+
|
|
141
|
+
// Register for route change notifications
|
|
142
|
+
NotificationCenter.default.addObserver(
|
|
143
|
+
self,
|
|
144
|
+
selector: #selector(handleRouteChange),
|
|
145
|
+
name: AVAudioSession.routeChangeNotification,
|
|
146
|
+
object: nil
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
// Register for app foreground notifications
|
|
150
|
+
// This handles the case where user pairs in Settings and returns to app
|
|
151
|
+
NotificationCenter.default.addObserver(
|
|
152
|
+
self,
|
|
153
|
+
selector: #selector(handleAppBecameActive),
|
|
154
|
+
name: UIApplication.didBecomeActiveNotification,
|
|
155
|
+
object: nil
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
isMonitoring = true
|
|
159
|
+
Bridge.log("AudioMonitor: Started monitoring for '\(devicePattern)'")
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Stop monitoring for audio route changes
|
|
163
|
+
static func stopMonitoring() {
|
|
164
|
+
guard isMonitoring else {
|
|
165
|
+
Bridge.log("AudioMonitor: Not currently monitoring")
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
NotificationCenter.default.removeObserver(
|
|
170
|
+
self,
|
|
171
|
+
name: AVAudioSession.routeChangeNotification,
|
|
172
|
+
object: nil
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
NotificationCenter.default.removeObserver(
|
|
176
|
+
self,
|
|
177
|
+
name: UIApplication.didBecomeActiveNotification,
|
|
178
|
+
object: nil
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
AudioSessionMonitor.isMonitoring = false
|
|
182
|
+
AudioSessionMonitor.devicePattern = nil
|
|
183
|
+
AudioSessionMonitor.callback = nil
|
|
184
|
+
|
|
185
|
+
Bridge.log("AudioMonitor: Stopped monitoring")
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@objc private func handleRouteChange(notification: Notification) {
|
|
189
|
+
guard let userInfo = notification.userInfo,
|
|
190
|
+
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
|
|
191
|
+
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue),
|
|
192
|
+
let pattern = AudioSessionMonitor.devicePattern
|
|
193
|
+
else {
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Bridge.log("AudioMonitor: Route change detected: \(reason.rawValue)")
|
|
198
|
+
|
|
199
|
+
switch reason {
|
|
200
|
+
case .newDeviceAvailable:
|
|
201
|
+
// When a new device becomes available, try to set it as preferred
|
|
202
|
+
// This handles the case where user pairs in Settings and returns to app
|
|
203
|
+
Bridge.log("AudioMonitor: New device available, attempting to activate '\(pattern)'")
|
|
204
|
+
|
|
205
|
+
// Add small delay to let iOS populate availableInputs
|
|
206
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
207
|
+
if AudioSessionMonitor.isDevicePaired(devicePattern: pattern) {
|
|
208
|
+
let session = AVAudioSession.sharedInstance()
|
|
209
|
+
let deviceName = session.availableInputs?.first(where: {
|
|
210
|
+
$0.portName.localizedCaseInsensitiveContains(pattern)
|
|
211
|
+
})?.portName
|
|
212
|
+
Bridge.log(
|
|
213
|
+
"AudioMonitor: ✅ Successfully detected newly paired device '\(pattern)'"
|
|
214
|
+
)
|
|
215
|
+
AudioSessionMonitor.callback?(true, deviceName)
|
|
216
|
+
} else {
|
|
217
|
+
Bridge.log("AudioMonitor: New device available but not matching '\(pattern)'")
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
case .oldDeviceUnavailable:
|
|
222
|
+
// Check if our device disconnected
|
|
223
|
+
if !AudioSessionMonitor.isAudioDeviceConnected(devicePattern: pattern) {
|
|
224
|
+
Bridge.log("AudioMonitor: Device '\(pattern)' disconnected")
|
|
225
|
+
AudioSessionMonitor.callback?(false, nil)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
default:
|
|
229
|
+
break
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@objc private static func handleAppBecameActive() {
|
|
234
|
+
guard let pattern = AudioSessionMonitor.devicePattern else { return }
|
|
235
|
+
|
|
236
|
+
Bridge.log("AudioMonitor: App became active, checking for paired device '\(pattern)'")
|
|
237
|
+
|
|
238
|
+
// Don't reconfigure session - it's already configured from when monitoring started
|
|
239
|
+
// Just wait a bit for iOS to update availableInputs after returning from background
|
|
240
|
+
// iOS needs time to populate availableInputs after app foreground
|
|
241
|
+
// Use retry with progressive delays: 100ms, 400ms, 500ms = 1s total
|
|
242
|
+
AudioSessionMonitor.attemptActivateDevice(pattern: pattern, attempt: 0, maxAttempts: 3)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/// Try to detect if the audio device is paired with retry logic
|
|
246
|
+
/// Delays: [100ms, 400ms, 500ms] = 1 second total
|
|
247
|
+
static func attemptActivateDevice(pattern: String, attempt: Int, maxAttempts: Int) {
|
|
248
|
+
// Progressive delays in seconds (total: 1 second)
|
|
249
|
+
let delays: [TimeInterval] = [0.1, 0.4, 0.5]
|
|
250
|
+
|
|
251
|
+
if attempt >= maxAttempts {
|
|
252
|
+
Bridge.log(
|
|
253
|
+
"AudioMonitor: ❌ Failed to find paired device '\(pattern)' after \(maxAttempts) attempts"
|
|
254
|
+
)
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let delay = attempt < delays.count ? delays[attempt] : 0.5
|
|
259
|
+
|
|
260
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
|
|
261
|
+
Bridge.log(
|
|
262
|
+
"AudioMonitor: Attempt \(attempt + 1)/\(maxAttempts) to detect '\(pattern)'..."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
if AudioSessionMonitor.isDevicePaired(devicePattern: pattern) {
|
|
266
|
+
let session = AVAudioSession.sharedInstance()
|
|
267
|
+
// Try to get device name from availableInputs
|
|
268
|
+
let deviceName = session.availableInputs?.first(where: {
|
|
269
|
+
$0.portName.localizedCaseInsensitiveContains(pattern)
|
|
270
|
+
})?.portName
|
|
271
|
+
Bridge.log("AudioMonitor: ✅ Found paired device on attempt \(attempt + 1)")
|
|
272
|
+
AudioSessionMonitor.callback?(true, deviceName)
|
|
273
|
+
} else {
|
|
274
|
+
Bridge.log(
|
|
275
|
+
"AudioMonitor: Attempt \(attempt + 1) failed, retrying in \(delays[min(attempt + 1, delays.count - 1)])s..."
|
|
276
|
+
)
|
|
277
|
+
AudioSessionMonitor.attemptActivateDevice(
|
|
278
|
+
pattern: pattern, attempt: attempt + 1, maxAttempts: maxAttempts
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import Compression
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/// Byte-level codecs shared by glasses transports.
|
|
5
|
+
enum BinaryCodec {
|
|
6
|
+
/// CRC-16/CCITT-FALSE: polynomial 0x1021, initial value 0xFFFF.
|
|
7
|
+
static func crc16CCITT<S: Sequence>(_ bytes: S) -> UInt16 where S.Element == UInt8 {
|
|
8
|
+
var crc: UInt32 = 0xFFFF
|
|
9
|
+
for byte in bytes {
|
|
10
|
+
crc ^= UInt32(byte) << 8
|
|
11
|
+
for _ in 0 ..< 8 {
|
|
12
|
+
crc = (crc & 0x8000) != 0
|
|
13
|
+
? ((crc << 1) ^ 0x1021) & 0xFFFF
|
|
14
|
+
: (crc << 1) & 0xFFFF
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return UInt16(crc)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static func crc16CCITTLittleEndian<S: Sequence>(_ bytes: S) -> [UInt8]
|
|
21
|
+
where S.Element == UInt8
|
|
22
|
+
{
|
|
23
|
+
let crc = crc16CCITT(bytes)
|
|
24
|
+
return [UInt8(crc & 0xFF), UInt8((crc >> 8) & 0xFF)]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// RFC1950 zlib stream. Apple's Compression framework emits raw DEFLATE,
|
|
28
|
+
/// so add the zlib header and Adler-32 trailer here in one shared implementation.
|
|
29
|
+
static func zlibRFC1950(_ data: Data) -> Data? {
|
|
30
|
+
guard !data.isEmpty else { return nil }
|
|
31
|
+
let capacity = data.count + data.count / 16 + 256
|
|
32
|
+
var deflated = Data(count: capacity)
|
|
33
|
+
let written = deflated.withUnsafeMutableBytes { destination -> Int in
|
|
34
|
+
data.withUnsafeBytes { source -> Int in
|
|
35
|
+
guard let destinationBase = destination.bindMemory(to: UInt8.self).baseAddress,
|
|
36
|
+
let sourceBase = source.bindMemory(to: UInt8.self).baseAddress
|
|
37
|
+
else { return 0 }
|
|
38
|
+
return compression_encode_buffer(
|
|
39
|
+
destinationBase, capacity, sourceBase, data.count, nil, COMPRESSION_ZLIB
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
guard written > 0 else { return nil }
|
|
44
|
+
deflated.removeSubrange(written ..< deflated.count)
|
|
45
|
+
|
|
46
|
+
var output = Data([0x78, 0x9C])
|
|
47
|
+
output.append(deflated)
|
|
48
|
+
var a: UInt32 = 1
|
|
49
|
+
var b: UInt32 = 0
|
|
50
|
+
for byte in data {
|
|
51
|
+
a = (a + UInt32(byte)) % 65521
|
|
52
|
+
b = (b + a) % 65521
|
|
53
|
+
}
|
|
54
|
+
let checksum = (b << 16) | a
|
|
55
|
+
output.append(contentsOf: [
|
|
56
|
+
UInt8((checksum >> 24) & 0xFF), UInt8((checksum >> 16) & 0xFF),
|
|
57
|
+
UInt8((checksum >> 8) & 0xFF), UInt8(checksum & 0xFF),
|
|
58
|
+
])
|
|
59
|
+
return output
|
|
60
|
+
}
|
|
61
|
+
}
|