@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.
Files changed (548) hide show
  1. package/README.md +563 -0
  2. package/android/build.gradle +450 -0
  3. package/android/gradle.properties.example +9 -0
  4. package/android/lc3Lib/CMakeLists.txt +6 -0
  5. package/android/lc3Lib/build.gradle +212 -0
  6. package/android/lc3Lib/proguard-rules.pro +33 -0
  7. package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
  8. package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
  9. package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
  10. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
  11. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
  12. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
  13. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
  14. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
  15. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
  16. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
  17. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
  18. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
  19. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
  20. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
  21. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
  22. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
  23. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
  24. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
  25. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
  26. package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
  27. package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
  28. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
  29. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
  30. package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
  31. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
  32. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
  33. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
  34. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
  35. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
  36. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
  37. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
  38. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
  39. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
  40. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
  41. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
  42. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
  43. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
  44. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
  45. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
  46. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
  47. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
  48. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
  49. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
  50. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
  51. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
  52. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
  53. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
  54. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
  55. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
  56. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
  57. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
  58. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
  59. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
  60. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
  61. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
  62. package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
  63. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
  64. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
  65. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
  66. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
  67. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
  68. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
  69. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
  70. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
  71. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
  72. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
  73. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
  74. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
  75. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
  76. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
  77. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
  78. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
  79. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
  80. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
  81. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
  82. package/android/lc3Lib/src/main/java/com/glassly/lc3Lib/Lc3Cpp.java +38 -0
  83. package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
  84. package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
  85. package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
  86. package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
  87. package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
  88. package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
  89. package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
  90. package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
  91. package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
  92. package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
  93. package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
  94. package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
  95. package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
  96. package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
  97. package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
  98. package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
  99. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
  100. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
  101. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
  102. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
  103. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
  104. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
  105. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
  106. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
  107. package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
  108. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
  109. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
  110. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
  111. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
  112. package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
  113. package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
  114. package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
  115. package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
  116. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
  117. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
  118. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
  119. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
  120. package/android/settings.gradle +4 -0
  121. package/android/silero/LICENSE.md +17 -0
  122. package/android/silero/build.gradle +50 -0
  123. package/android/silero/proguard-rules.pro +24 -0
  124. package/android/silero/src/androidTest/assets/hello.wav +0 -0
  125. package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
  126. package/android/silero/src/main/assets/LICENSE +21 -0
  127. package/android/silero/src/main/assets/silero_vad.onnx +0 -0
  128. package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
  129. package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
  130. package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
  131. package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
  132. package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
  133. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
  134. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
  135. package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
  136. package/android/src/main/AndroidManifest.xml +42 -0
  137. package/android/src/main/assets/G2Firmware/README.md +11 -0
  138. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2325 -0
  139. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
  140. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
  141. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
  142. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1088 -0
  143. package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
  144. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2528 -0
  145. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +360 -0
  146. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +8 -0
  147. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
  148. package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
  149. package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
  150. package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
  151. package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
  152. package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
  153. package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
  154. package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
  155. package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  156. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
  157. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +937 -0
  158. package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
  159. package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
  160. package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
  161. package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
  162. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
  163. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
  164. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  165. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
  166. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
  167. package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
  168. package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
  169. package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
  170. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
  171. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
  172. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
  173. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3878 -0
  174. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
  175. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6428 -0
  176. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
  177. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1241 -0
  178. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
  179. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
  180. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
  181. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
  182. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1280 -0
  183. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10817 -0
  184. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
  185. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
  186. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2011 -0
  187. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
  188. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
  189. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
  190. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
  191. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
  192. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
  193. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
  194. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
  195. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
  196. package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
  197. package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
  198. package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
  199. package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
  200. package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
  201. package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
  202. package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
  203. package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
  204. package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
  205. package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
  206. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
  207. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
  208. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  209. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
  210. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
  211. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
  212. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
  213. package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
  214. package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
  215. package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  216. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
  217. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
  218. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
  219. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
  220. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
  221. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
  222. package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
  223. package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
  224. package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
  225. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
  226. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
  227. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
  228. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
  229. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
  230. package/android/src/main/res/values/strings.xml +4 -0
  231. package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
  232. package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
  233. package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
  234. package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
  235. package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
  236. package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
  237. package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
  238. package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
  239. package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  240. package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
  241. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
  242. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
  243. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
  244. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
  245. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
  246. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
  247. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
  248. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
  249. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
  250. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
  251. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
  252. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
  253. package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
  254. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
  255. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
  256. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
  257. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
  258. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
  259. package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
  260. package/android/src/test/resources/avif_with_exif.avif +0 -0
  261. package/app.plugin.js +3 -0
  262. package/build/BluetoothSdk.types.d.ts +1354 -0
  263. package/build/BluetoothSdk.types.d.ts.map +1 -0
  264. package/build/BluetoothSdk.types.js +41 -0
  265. package/build/BluetoothSdk.types.js.map +1 -0
  266. package/build/_internal.d.ts +17 -0
  267. package/build/_internal.d.ts.map +1 -0
  268. package/build/_internal.js +16 -0
  269. package/build/_internal.js.map +1 -0
  270. package/build/_private/BluetoothSdkModule.d.ts +172 -0
  271. package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
  272. package/build/_private/BluetoothSdkModule.js +300 -0
  273. package/build/_private/BluetoothSdkModule.js.map +1 -0
  274. package/build/_private/LocalNetworkModule.d.ts +38 -0
  275. package/build/_private/LocalNetworkModule.d.ts.map +1 -0
  276. package/build/_private/LocalNetworkModule.js +3 -0
  277. package/build/_private/LocalNetworkModule.js.map +1 -0
  278. package/build/_private/cameraRequestPayload.d.ts +4 -0
  279. package/build/_private/cameraRequestPayload.d.ts.map +1 -0
  280. package/build/_private/cameraRequestPayload.js +32 -0
  281. package/build/_private/cameraRequestPayload.js.map +1 -0
  282. package/build/_private/photoRequestPayload.d.ts +6 -0
  283. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  284. package/build/_private/photoRequestPayload.js +86 -0
  285. package/build/_private/photoRequestPayload.js.map +1 -0
  286. package/build/_private/sdkOtaManifest.d.ts +11 -0
  287. package/build/_private/sdkOtaManifest.d.ts.map +1 -0
  288. package/build/_private/sdkOtaManifest.js +15 -0
  289. package/build/_private/sdkOtaManifest.js.map +1 -0
  290. package/build/changelogs.d.ts +7 -0
  291. package/build/changelogs.d.ts.map +1 -0
  292. package/build/changelogs.js +46 -0
  293. package/build/changelogs.js.map +1 -0
  294. package/build/debug.d.ts +3 -0
  295. package/build/debug.d.ts.map +1 -0
  296. package/build/debug.js +8 -0
  297. package/build/debug.js.map +1 -0
  298. package/build/generated/changelogCatalog.d.ts +9 -0
  299. package/build/generated/changelogCatalog.d.ts.map +1 -0
  300. package/build/generated/changelogCatalog.js +12 -0
  301. package/build/generated/changelogCatalog.js.map +1 -0
  302. package/build/generated/releaseMetadata.d.ts +12 -0
  303. package/build/generated/releaseMetadata.d.ts.map +1 -0
  304. package/build/generated/releaseMetadata.js +10 -0
  305. package/build/generated/releaseMetadata.js.map +1 -0
  306. package/build/index.d.ts +6 -0
  307. package/build/index.d.ts.map +1 -0
  308. package/build/index.js +171 -0
  309. package/build/index.js.map +1 -0
  310. package/build/ota-server/OtaServer.types.d.ts +14 -0
  311. package/build/ota-server/OtaServer.types.d.ts.map +1 -0
  312. package/build/ota-server/OtaServer.types.js +2 -0
  313. package/build/ota-server/OtaServer.types.js.map +1 -0
  314. package/build/ota-server/OtaServerModule.d.ts +21 -0
  315. package/build/ota-server/OtaServerModule.d.ts.map +1 -0
  316. package/build/ota-server/OtaServerModule.js +3 -0
  317. package/build/ota-server/OtaServerModule.js.map +1 -0
  318. package/build/ota-server/OtaServerModule.web.d.ts +10 -0
  319. package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
  320. package/build/ota-server/OtaServerModule.web.js +12 -0
  321. package/build/ota-server/OtaServerModule.web.js.map +1 -0
  322. package/build/ota-server/index.d.ts +3 -0
  323. package/build/ota-server/index.d.ts.map +1 -0
  324. package/build/ota-server/index.js +3 -0
  325. package/build/ota-server/index.js.map +1 -0
  326. package/build/ota-transport/index.d.ts +67 -0
  327. package/build/ota-transport/index.d.ts.map +1 -0
  328. package/build/ota-transport/index.js +58 -0
  329. package/build/ota-transport/index.js.map +1 -0
  330. package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
  331. package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
  332. package/build/photo-receiver/PhotoReceiver.types.js +2 -0
  333. package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
  334. package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
  335. package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
  336. package/build/photo-receiver/PhotoReceiverModule.js +3 -0
  337. package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
  338. package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
  339. package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
  340. package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
  341. package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
  342. package/build/photo-receiver/index.d.ts +3 -0
  343. package/build/photo-receiver/index.d.ts.map +1 -0
  344. package/build/photo-receiver/index.js +3 -0
  345. package/build/photo-receiver/index.js.map +1 -0
  346. package/build/react/index.d.ts +5 -0
  347. package/build/react/index.d.ts.map +1 -0
  348. package/build/react/index.js +4 -0
  349. package/build/react/index.js.map +1 -0
  350. package/build/react/useBluetooth.d.ts +100 -0
  351. package/build/react/useBluetooth.d.ts.map +1 -0
  352. package/build/react/useBluetooth.js +167 -0
  353. package/build/react/useBluetooth.js.map +1 -0
  354. package/build/react/useBluetoothEvent.d.ts +6 -0
  355. package/build/react/useBluetoothEvent.d.ts.map +1 -0
  356. package/build/react/useBluetoothEvent.js +21 -0
  357. package/build/react/useBluetoothEvent.js.map +1 -0
  358. package/build/react/useBluetoothScan.d.ts +22 -0
  359. package/build/react/useBluetoothScan.d.ts.map +1 -0
  360. package/build/react/useBluetoothScan.js +135 -0
  361. package/build/react/useBluetoothScan.js.map +1 -0
  362. package/build/react/useBluetoothStatus.d.ts +16 -0
  363. package/build/react/useBluetoothStatus.d.ts.map +1 -0
  364. package/build/react/useBluetoothStatus.js +137 -0
  365. package/build/react/useBluetoothStatus.js.map +1 -0
  366. package/build/react/useGlassesConnection.d.ts +29 -0
  367. package/build/react/useGlassesConnection.d.ts.map +1 -0
  368. package/build/react/useGlassesConnection.js +153 -0
  369. package/build/react/useGlassesConnection.js.map +1 -0
  370. package/build/types/index.d.ts +3 -0
  371. package/build/types/index.d.ts.map +1 -0
  372. package/build/types/index.js +2 -0
  373. package/build/types/index.js.map +1 -0
  374. package/expo-module.config.json +14 -0
  375. package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
  376. package/ios/BluetoothSdkModule.swift +1034 -0
  377. package/ios/GlasslyBluetoothSDK.podspec +106 -0
  378. package/ios/LocalIPv4.swift +150 -0
  379. package/ios/LocalOtaServer.swift +309 -0
  380. package/ios/LocalPhotoUploadServer.swift +754 -0
  381. package/ios/OtaServerModule.swift +147 -0
  382. package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
  383. package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
  384. package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
  385. package/ios/Packages/CoreObjC/attdet.c +92 -0
  386. package/ios/Packages/CoreObjC/attdet.h +44 -0
  387. package/ios/Packages/CoreObjC/bits.c +375 -0
  388. package/ios/Packages/CoreObjC/bits.h +315 -0
  389. package/ios/Packages/CoreObjC/bwdet.c +129 -0
  390. package/ios/Packages/CoreObjC/bwdet.h +69 -0
  391. package/ios/Packages/CoreObjC/common.h +151 -0
  392. package/ios/Packages/CoreObjC/energy.c +70 -0
  393. package/ios/Packages/CoreObjC/energy.h +43 -0
  394. package/ios/Packages/CoreObjC/fastmath.h +158 -0
  395. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  396. package/ios/Packages/CoreObjC/lc3.c +704 -0
  397. package/ios/Packages/CoreObjC/lc3.h +313 -0
  398. package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
  399. package/ios/Packages/CoreObjC/lc3_private.h +163 -0
  400. package/ios/Packages/CoreObjC/ltpf.c +905 -0
  401. package/ios/Packages/CoreObjC/ltpf.h +111 -0
  402. package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
  403. package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
  404. package/ios/Packages/CoreObjC/makefile.mk +35 -0
  405. package/ios/Packages/CoreObjC/mdct.c +469 -0
  406. package/ios/Packages/CoreObjC/mdct.h +57 -0
  407. package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
  408. package/ios/Packages/CoreObjC/meson.build +61 -0
  409. package/ios/Packages/CoreObjC/plc.c +61 -0
  410. package/ios/Packages/CoreObjC/plc.h +57 -0
  411. package/ios/Packages/CoreObjC/rnnoise.h +114 -0
  412. package/ios/Packages/CoreObjC/sns.c +880 -0
  413. package/ios/Packages/CoreObjC/sns.h +103 -0
  414. package/ios/Packages/CoreObjC/spec.c +907 -0
  415. package/ios/Packages/CoreObjC/spec.h +119 -0
  416. package/ios/Packages/CoreObjC/tables.c +3457 -0
  417. package/ios/Packages/CoreObjC/tables.h +94 -0
  418. package/ios/Packages/CoreObjC/tns.c +457 -0
  419. package/ios/Packages/CoreObjC/tns.h +99 -0
  420. package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
  421. package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
  422. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +2383 -0
  423. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
  424. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  425. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  426. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  427. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  428. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  429. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  430. package/ios/Packages/libbz2/module.modulemap +5 -0
  431. package/ios/Packages/libbz2/shim.h +1 -0
  432. package/ios/PhotoReceiverModule.swift +129 -0
  433. package/ios/Source/BluetoothSDK.swift +2303 -0
  434. package/ios/Source/BluetoothSDKDebug.swift +12 -0
  435. package/ios/Source/BluetoothSdkDefaults.swift +29 -0
  436. package/ios/Source/Bridge.swift +708 -0
  437. package/ios/Source/Bridging-Header.h +16 -0
  438. package/ios/Source/DeviceManager.swift +2109 -0
  439. package/ios/Source/DeviceStore.swift +336 -0
  440. package/ios/Source/GeneratedChangelogCatalog.swift +7 -0
  441. package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
  442. package/ios/Source/ObservableStore.swift +118 -0
  443. package/ios/Source/OtaManifest.swift +211 -0
  444. package/ios/Source/PrivacyInfo.xcprivacy +23 -0
  445. package/ios/Source/ReleaseChangelog.swift +65 -0
  446. package/ios/Source/Resources/G2Firmware/README.md +10 -0
  447. package/ios/Source/audio/AudioModels.swift +178 -0
  448. package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
  449. package/ios/Source/camera/CameraModels.swift +866 -0
  450. package/ios/Source/connection/ScanSession.swift +27 -0
  451. package/ios/Source/controllers/ControllerManager.swift +184 -0
  452. package/ios/Source/controllers/R1.swift +829 -0
  453. package/ios/Source/errors/BluetoothSdkError.swift +19 -0
  454. package/ios/Source/events/BluetoothEvents.swift +342 -0
  455. package/ios/Source/internal/BleTraceLogger.swift +192 -0
  456. package/ios/Source/internal/BluetoothAvailability.swift +78 -0
  457. package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
  458. package/ios/Source/internal/ValueParsing.swift +99 -0
  459. package/ios/Source/requests/DisplayRequests.swift +82 -0
  460. package/ios/Source/services/PhoneMic.swift +662 -0
  461. package/ios/Source/sgcs/Ar99.swift +2451 -0
  462. package/ios/Source/sgcs/Frame.swift +649 -0
  463. package/ios/Source/sgcs/G1.swift +2554 -0
  464. package/ios/Source/sgcs/G2.swift +6887 -0
  465. package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
  466. package/ios/Source/sgcs/G2CfwRenderer.swift +1279 -0
  467. package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
  468. package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
  469. package/ios/Source/sgcs/G2Flasher.swift +773 -0
  470. package/ios/Source/sgcs/Mach1.swift +541 -0
  471. package/ios/Source/sgcs/MentraLive.swift +7011 -0
  472. package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
  473. package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
  474. package/ios/Source/sgcs/MentraNex.swift +3221 -0
  475. package/ios/Source/sgcs/Nimo.swift +1886 -0
  476. package/ios/Source/sgcs/SGCManager.swift +608 -0
  477. package/ios/Source/sgcs/Simulated.swift +274 -0
  478. package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
  479. package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
  480. package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
  481. package/ios/Source/status/DeviceStatus.swift +981 -0
  482. package/ios/Source/status/RuntimeState.swift +359 -0
  483. package/ios/Source/status/WifiHotspotStatus.swift +345 -0
  484. package/ios/Source/streaming/StreamModels.swift +693 -0
  485. package/ios/Source/stt/STTTools.swift +191 -0
  486. package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
  487. package/ios/Source/tts/TTSTools.swift +171 -0
  488. package/ios/Source/types/DeviceModels.swift +177 -0
  489. package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
  490. package/ios/Source/utils/BinaryCodec.swift +61 -0
  491. package/ios/Source/utils/BleJsonCompact.swift +405 -0
  492. package/ios/Source/utils/BleWireProtocol.swift +92 -0
  493. package/ios/Source/utils/Constants.swift +78 -0
  494. package/ios/Source/utils/Enums.swift +95 -0
  495. package/ios/Source/utils/G1Text.swift +2096 -0
  496. package/ios/Source/utils/JSCExperiment.swift +241 -0
  497. package/ios/Source/utils/MemoryMonitor.swift +44 -0
  498. package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
  499. package/ios/Source/utils/MessageChunker.swift +294 -0
  500. package/ios/Source/utils/Models.swift +135 -0
  501. package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
  502. package/ios/Source/utils/TarBz2Extractor.swift +257 -0
  503. package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
  504. package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
  505. package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
  506. package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
  507. package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
  508. package/ios/Tests/ReleaseChangelogTests.swift +13 -0
  509. package/package.json +129 -0
  510. package/plugin/build/index.d.ts +10 -0
  511. package/plugin/build/index.js +12 -0
  512. package/plugin/build/withAndroid.d.ts +3 -0
  513. package/plugin/build/withAndroid.js +190 -0
  514. package/plugin/build/withIos.d.ts +5 -0
  515. package/plugin/build/withIos.js +108 -0
  516. package/scripts/inject-ios-sdk-version.mjs +65 -0
  517. package/scripts/public-ota-api.test.mjs +37 -0
  518. package/scripts/verify-release-package.mjs +45 -0
  519. package/scripts/write-release-metadata.mjs +152 -0
  520. package/scripts/write-release-metadata.test.mjs +37 -0
  521. package/src/BluetoothSdk.types.ts +1623 -0
  522. package/src/_internal.ts +16 -0
  523. package/src/_private/BluetoothSdkModule.ts +684 -0
  524. package/src/_private/LocalNetworkModule.ts +50 -0
  525. package/src/_private/cameraRequestPayload.ts +34 -0
  526. package/src/_private/photoRequestPayload.ts +90 -0
  527. package/src/_private/sdkOtaManifest.ts +18 -0
  528. package/src/changelogs.ts +46 -0
  529. package/src/debug.ts +9 -0
  530. package/src/generated/changelogCatalog.ts +13 -0
  531. package/src/generated/releaseMetadata.ts +20 -0
  532. package/src/index.ts +321 -0
  533. package/src/ota-server/OtaServer.types.ts +10 -0
  534. package/src/ota-server/OtaServerModule.ts +23 -0
  535. package/src/ota-server/OtaServerModule.web.ts +17 -0
  536. package/src/ota-server/index.ts +2 -0
  537. package/src/ota-transport/index.ts +116 -0
  538. package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
  539. package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
  540. package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
  541. package/src/photo-receiver/index.ts +2 -0
  542. package/src/react/index.ts +21 -0
  543. package/src/react/useBluetooth.ts +309 -0
  544. package/src/react/useBluetoothEvent.ts +35 -0
  545. package/src/react/useBluetoothScan.ts +173 -0
  546. package/src/react/useBluetoothStatus.ts +180 -0
  547. package/src/react/useGlassesConnection.ts +202 -0
  548. package/src/types/index.ts +12 -0
@@ -0,0 +1,573 @@
1
+ //
2
+ // SpeakerIdEngine.swift
3
+ //
4
+ // On-device speaker identification over the canonical 16 kHz mono PCM stream.
5
+ //
6
+ // Taps DeviceManager.handlePcm (the single audio choke point), windows voiced
7
+ // audio with a local energy gate, computes a speaker embedding per
8
+ // utterance-ish window (sherpa-onnx CAM++ model), matches against enrolled
9
+ // profile centroids by cosine similarity, and emits `speaker_detected`
10
+ // bridge events. Raw audio never leaves the phone for speaker ID; enrollment
11
+ // audio is discarded as soon as its embeddings are computed.
12
+ //
13
+ // Profiles are pushed in from JS (`setSpeakerProfiles`) — MMKV in the engine
14
+ // layer is the source of truth; native holds them in memory only.
15
+ //
16
+
17
+ import Foundation
18
+
19
+ #if !SWIFT_PACKAGE || GLASSLY_FEATURE_LOCAL_STT
20
+
21
+ final class SpeakerIdEngine {
22
+ static let shared = SpeakerIdEngine()
23
+
24
+ struct Profile {
25
+ let id: String
26
+ let name: String
27
+ /// L2-normalized centroid.
28
+ let embedding: [Float]
29
+ }
30
+
31
+ struct Match {
32
+ let profileId: String
33
+ let name: String
34
+ let similarity: Float
35
+ /// Epoch ms when the window producing this match ended.
36
+ let endMs: Int64
37
+ }
38
+
39
+ // MARK: - Tuning (see notes/speaker-id-plan.md §2)
40
+
41
+ /// Cosine threshold for accepting a match (normalized embeddings).
42
+ /// Benchmarked cross-speaker ceiling for this model is ~0.46 (the MAX
43
+ /// over clean-audio pairs; typical impostors score far lower), while
44
+ /// on-device same-speaker scores run below the 0.67+ clean bench.
45
+ /// Owner-only voice recognition favors recall: a borderline accept just
46
+ /// labels a line "You", and the JS segment resolver's overlap voting +
47
+ /// conflict damping still filter isolated stray windows — so sit at the
48
+ /// edge of the impostor range rather than above it. (With one enrolled
49
+ /// profile the margin rule below is vacuous.)
50
+ private static let matchThreshold: Float = 0.45
51
+ /// Reject when the runner-up profile is within this margin of the best.
52
+ private static let matchMargin: Float = 0.08
53
+ /// Flush a window once this much voiced audio accumulated.
54
+ private static let windowVoicedTargetMs = 3000.0
55
+ /// End-of-speech hangover: flush after this much trailing silence.
56
+ private static let endOfSpeechSilenceMs = 500.0
57
+ /// Windows with less voiced audio than this are skipped (unreliable).
58
+ private static let minVoicedMs = 1000.0
59
+ /// Hard cap on buffered window samples (10 s) to bound memory.
60
+ private static let maxWindowSamples = 16000 * 10
61
+ /// RMS energy gate on [-1, 1] floats.
62
+ private static let voicedRmsThreshold: Float = 0.01
63
+ /// Enrollment chunks: pairwise cosine consistency floor.
64
+ private static let enrollConsistencyThreshold: Float = 0.6
65
+ private static let enrollChunkCount = 3
66
+
67
+ private static let sampleRate = 16000
68
+ private static let modelPathKey = "SpeakerIdModelPath"
69
+
70
+ // MARK: - State
71
+
72
+ private let queue = DispatchQueue(label: "com.glassly.speakerid.processor", qos: .utility)
73
+
74
+ /// Cheap gate read on the audio thread before hopping queues.
75
+ private let activeFlag = NSLock()
76
+ private var _identifyEnabled = false
77
+ private var _enrolling = false
78
+ private var isActive: Bool {
79
+ activeFlag.lock()
80
+ defer { activeFlag.unlock() }
81
+ return _identifyEnabled || _enrolling
82
+ }
83
+
84
+ // Everything below is touched on `queue` only.
85
+ private var extractor: SherpaOnnxSpeakerEmbeddingExtractorWrapper?
86
+ private var extractorLoadAttempted = false
87
+ private var profiles: [Profile] = []
88
+
89
+ private var windowSamples: [Float] = []
90
+ private var windowStartMs: Int64 = 0
91
+ private var windowVoicedMs = 0.0
92
+ private var trailingSilenceMs = 0.0
93
+
94
+ private struct EnrollmentSession {
95
+ var targetVoicedMs: Double
96
+ var source: String
97
+ var samples: [Float] = []
98
+ var voicedMs = 0.0
99
+ var lastProgressAt = Date(timeIntervalSince1970: 0)
100
+ var lastLevel: Float = 0
101
+ }
102
+
103
+ private var enrollment: EnrollmentSession?
104
+
105
+ private let matchLock = NSLock()
106
+ private var _lastMatch: Match?
107
+
108
+ // MARK: - Public API (any thread unless noted)
109
+
110
+ func setIdentifyEnabled(_ enabled: Bool) {
111
+ activeFlag.lock()
112
+ _identifyEnabled = enabled
113
+ activeFlag.unlock()
114
+ Bridge.log("SpeakerID: identify \(enabled ? "enabled" : "disabled")")
115
+ queue.async { [weak self] in
116
+ guard let self else { return }
117
+ if enabled {
118
+ self.ensureExtractor()
119
+ } else {
120
+ self.resetWindow()
121
+ }
122
+ }
123
+ }
124
+
125
+ func setProfiles(_ profiles: [Profile]) {
126
+ queue.async { [weak self] in
127
+ self?.profiles = profiles
128
+ Bridge.log(
129
+ "SpeakerID: profiles set — \(profiles.count): "
130
+ + profiles.map { "\($0.name)(\($0.embedding.count)d)" }.joined(separator: ", ")
131
+ )
132
+ }
133
+ }
134
+
135
+ static func setModelPath(_ path: String) {
136
+ UserDefaults.standard.set(path, forKey: modelPathKey)
137
+ Bridge.log("SpeakerID: model path set → \(path)")
138
+ SpeakerIdEngine.shared.queue.async {
139
+ // Force a reload on next use — the model may have just arrived.
140
+ SpeakerIdEngine.shared.extractor = nil
141
+ SpeakerIdEngine.shared.extractorLoadAttempted = false
142
+ }
143
+ }
144
+
145
+ static func isModelAvailable() -> Bool {
146
+ guard let path = resolvedModelPath() else { return false }
147
+ return FileManager.default.fileExists(atPath: path)
148
+ }
149
+
150
+ /// `onEnded` fires (on an arbitrary queue) whenever the enrollment session
151
+ /// finishes or fails, so the caller can release the mic requirement.
152
+ func startEnrollment(targetSpeechSeconds: Double, source: String, onEnded: @escaping () -> Void) {
153
+ activeFlag.lock()
154
+ _enrolling = true
155
+ activeFlag.unlock()
156
+ queue.async { [weak self] in
157
+ guard let self else { return }
158
+ self.ensureExtractor()
159
+ guard self.extractor != nil else {
160
+ Bridge.log("SpeakerID: enrollment aborted — model unavailable")
161
+ self.finishEnrollment(ok: false, reason: "model_unavailable", embeddings: [], source: source)
162
+ onEnded()
163
+ return
164
+ }
165
+ Bridge.log("SpeakerID: enrollment started — target \(targetSpeechSeconds)s voiced, mic=\(source)")
166
+ self.enrollmentEnded = onEnded
167
+ self.enrollment = EnrollmentSession(
168
+ targetVoicedMs: max(5.0, targetSpeechSeconds) * 1000.0,
169
+ source: source
170
+ )
171
+ }
172
+ }
173
+
174
+ private var enrollmentEnded: (() -> Void)?
175
+
176
+ func cancelEnrollment() {
177
+ activeFlag.lock()
178
+ _enrolling = false
179
+ activeFlag.unlock()
180
+ queue.async { [weak self] in
181
+ guard let self else { return }
182
+ self.enrollment = nil
183
+ let onEnded = self.enrollmentEnded
184
+ self.enrollmentEnded = nil
185
+ onEnded?()
186
+ }
187
+ }
188
+
189
+ /// Latest accepted identification, if it ended within `withinMs` of now.
190
+ /// Used by the local STT path to stamp transcripts (native-side join).
191
+ func recentMatch(withinMs: Int64) -> Match? {
192
+ matchLock.lock()
193
+ defer { matchLock.unlock() }
194
+ guard let match = _lastMatch else { return nil }
195
+ let now = Int64(Date().timeIntervalSince1970 * 1000)
196
+ return now - match.endMs <= withinMs ? match : nil
197
+ }
198
+
199
+ /// Called from DeviceManager.handlePcm with 16 kHz mono s16le PCM.
200
+ func acceptAudio(pcm16le: Data) {
201
+ guard isActive, pcm16le.count >= 2 else { return }
202
+ queue.async { [weak self] in
203
+ self?.process(pcm16le)
204
+ }
205
+ }
206
+
207
+ // MARK: - Processing (queue only)
208
+
209
+ private func process(_ pcmData: Data) {
210
+ let samples = Self.toFloatArray(pcmData)
211
+ guard !samples.isEmpty else { return }
212
+ let chunkMs = Double(samples.count) / Double(Self.sampleRate) * 1000.0
213
+ let rms = Self.rms(samples)
214
+ let voiced = rms > Self.voicedRmsThreshold
215
+
216
+ if enrollment != nil {
217
+ processEnrollment(samples: samples, chunkMs: chunkMs, rms: rms, voiced: voiced)
218
+ return
219
+ }
220
+
221
+ activeFlag.lock()
222
+ let identify = _identifyEnabled
223
+ activeFlag.unlock()
224
+ guard identify else { return }
225
+ processIdentification(samples: samples, chunkMs: chunkMs, voiced: voiced)
226
+ }
227
+
228
+ private func processIdentification(samples: [Float], chunkMs: Double, voiced: Bool) {
229
+ let nowMs = Int64(Date().timeIntervalSince1970 * 1000)
230
+
231
+ if windowSamples.isEmpty {
232
+ guard voiced else { return }
233
+ windowStartMs = nowMs - Int64(chunkMs)
234
+ windowVoicedMs = 0
235
+ trailingSilenceMs = 0
236
+ }
237
+
238
+ // Accumulate VOICED samples only — enrollment computes centroids from
239
+ // voiced-only audio, so identification must embed like-for-like.
240
+ // Windows padded with inter-phrase silence score far below the same
241
+ // speaker's centroid (observed 0.1–0.48 vs the 0.67+ bench).
242
+ if voiced {
243
+ if windowSamples.count < Self.maxWindowSamples {
244
+ windowSamples.append(contentsOf: samples)
245
+ }
246
+ windowVoicedMs += chunkMs
247
+ trailingSilenceMs = 0
248
+ } else {
249
+ trailingSilenceMs += chunkMs
250
+ }
251
+
252
+ let endOfSpeech = trailingSilenceMs >= Self.endOfSpeechSilenceMs
253
+ let windowFull = windowVoicedMs >= Self.windowVoicedTargetMs
254
+ guard endOfSpeech || windowFull else { return }
255
+
256
+ let startMs = windowStartMs
257
+ let voicedMs = windowVoicedMs
258
+ let window = windowSamples
259
+ resetWindow()
260
+
261
+ guard voicedMs >= Self.minVoicedMs else {
262
+ Bridge.log("SpeakerID: window skipped — only \(Int(voicedMs))ms voiced (<\(Int(Self.minVoicedMs))ms)")
263
+ return
264
+ }
265
+ identify(window: window, startMs: startMs, endMs: nowMs, voicedMs: voicedMs)
266
+ }
267
+
268
+ private func identify(window: [Float], startMs: Int64, endMs: Int64, voicedMs: Double) {
269
+ ensureExtractor()
270
+ guard let extractor else {
271
+ Bridge.log("SpeakerID: window dropped — extractor not loaded (model missing?)")
272
+ return
273
+ }
274
+ guard !profiles.isEmpty else {
275
+ Bridge.log("SpeakerID: window dropped — no enrolled profiles pushed")
276
+ return
277
+ }
278
+
279
+ let stream = extractor.createStream()
280
+ stream.acceptWaveform(samples: window, sampleRate: Self.sampleRate)
281
+ stream.inputFinished()
282
+ guard extractor.isReady(stream: stream) else {
283
+ Bridge.log("SpeakerID: window dropped — extractor not ready (\(window.count) samples)")
284
+ return
285
+ }
286
+ let embedding = Self.l2Normalize(extractor.compute(stream: stream))
287
+ guard !embedding.isEmpty else {
288
+ Bridge.log("SpeakerID: window dropped — empty embedding")
289
+ return
290
+ }
291
+
292
+ var best: (profile: Profile, similarity: Float)?
293
+ var second: Float = -1
294
+ for profile in profiles where profile.embedding.count == embedding.count {
295
+ let cos = Self.dot(profile.embedding, embedding)
296
+ if best == nil || cos > best!.similarity {
297
+ second = best?.similarity ?? -1
298
+ best = (profile, cos)
299
+ } else if cos > second {
300
+ second = cos
301
+ }
302
+ }
303
+
304
+ var matched: Profile?
305
+ var similarity: Float = 0
306
+ if let best, best.similarity >= Self.matchThreshold {
307
+ // Margin rule: reject ambiguous windows where the runner-up is
308
+ // close (overlapping talkers produce garbage embeddings).
309
+ if second < 0 || best.similarity - second >= Self.matchMargin {
310
+ matched = best.profile
311
+ similarity = best.similarity
312
+ }
313
+ }
314
+
315
+ if let matched {
316
+ matchLock.lock()
317
+ _lastMatch = Match(profileId: matched.id, name: matched.name, similarity: similarity, endMs: endMs)
318
+ matchLock.unlock()
319
+ }
320
+
321
+ let durationMs = Int(endMs - startMs)
322
+ let speech = "speech=\(Int(voicedMs))ms window=\(durationMs)ms"
323
+ if let matched {
324
+ Bridge.log(
325
+ "SpeakerID: matched \(matched.name) cos=\(String(format: "%.3f", similarity)) "
326
+ + "second=\(String(format: "%.3f", second)) \(speech)"
327
+ )
328
+ } else if let best {
329
+ let reason = best.similarity < Self.matchThreshold
330
+ ? "below threshold \(Self.matchThreshold)"
331
+ : "margin to runner-up < \(Self.matchMargin)"
332
+ Bridge.log(
333
+ "SpeakerID: no match — best \(best.profile.name) cos=\(String(format: "%.3f", best.similarity)) "
334
+ + "second=\(String(format: "%.3f", second)) (\(reason)) \(speech)"
335
+ )
336
+ }
337
+
338
+ Bridge.sendSpeakerDetected(
339
+ speakerId: matched?.id,
340
+ name: matched?.name,
341
+ similarity: Double(matched != nil ? similarity : (best?.similarity ?? 0)),
342
+ startMs: startMs,
343
+ endMs: endMs
344
+ )
345
+ }
346
+
347
+ // MARK: - Enrollment (queue only)
348
+
349
+ private func processEnrollment(samples: [Float], chunkMs: Double, rms: Float, voiced: Bool) {
350
+ guard var session = enrollment else { return }
351
+ if voiced {
352
+ session.samples.append(contentsOf: samples)
353
+ session.voicedMs += chunkMs
354
+ }
355
+ session.lastLevel = rms
356
+
357
+ let now = Date()
358
+ if now.timeIntervalSince(session.lastProgressAt) >= 0.25 {
359
+ session.lastProgressAt = now
360
+ Bridge.sendSpeakerEnrollProgress(
361
+ voicedSeconds: session.voicedMs / 1000.0,
362
+ targetSeconds: session.targetVoicedMs / 1000.0,
363
+ level: Double(rms)
364
+ )
365
+ }
366
+
367
+ if session.voicedMs >= session.targetVoicedMs {
368
+ enrollment = nil
369
+ activeFlag.lock()
370
+ _enrolling = false
371
+ activeFlag.unlock()
372
+ completeEnrollment(session)
373
+ } else {
374
+ enrollment = session
375
+ }
376
+ }
377
+
378
+ private func completeEnrollment(_ session: EnrollmentSession) {
379
+ guard let extractor else {
380
+ finishEnrollment(ok: false, reason: "model_unavailable", embeddings: [], source: session.source)
381
+ return
382
+ }
383
+
384
+ // One embedding per prompt-sized chunk; internal consistency check
385
+ // catches wind/noise/multiple talkers.
386
+ let chunkCount = Self.enrollChunkCount
387
+ let chunkSize = session.samples.count / chunkCount
388
+ guard chunkSize >= Self.sampleRate else {
389
+ finishEnrollment(ok: false, reason: "too_short", embeddings: [], source: session.source)
390
+ return
391
+ }
392
+
393
+ var embeddings: [[Float]] = []
394
+ for i in 0 ..< chunkCount {
395
+ let start = i * chunkSize
396
+ let end = i == chunkCount - 1 ? session.samples.count : start + chunkSize
397
+ let chunk = Array(session.samples[start ..< end])
398
+ let stream = extractor.createStream()
399
+ stream.acceptWaveform(samples: chunk, sampleRate: Self.sampleRate)
400
+ stream.inputFinished()
401
+ guard extractor.isReady(stream: stream) else { continue }
402
+ let embedding = Self.l2Normalize(extractor.compute(stream: stream))
403
+ if !embedding.isEmpty {
404
+ embeddings.append(embedding)
405
+ }
406
+ }
407
+
408
+ guard embeddings.count >= 2 else {
409
+ finishEnrollment(ok: false, reason: "too_short", embeddings: [], source: session.source)
410
+ return
411
+ }
412
+
413
+ for i in 0 ..< embeddings.count {
414
+ for j in (i + 1) ..< embeddings.count {
415
+ let cos = Self.dot(embeddings[i], embeddings[j])
416
+ if cos < Self.enrollConsistencyThreshold {
417
+ Bridge.log(
418
+ "SpeakerID: enrollment chunks \(i)/\(j) inconsistent — cos=\(String(format: "%.3f", cos)) "
419
+ + "< \(Self.enrollConsistencyThreshold) (noise / multiple talkers?)"
420
+ )
421
+ finishEnrollment(ok: false, reason: "inconsistent", embeddings: [], source: session.source)
422
+ return
423
+ }
424
+ }
425
+ }
426
+
427
+ let encoded = embeddings.map { Self.base64(from: $0) }
428
+ finishEnrollment(ok: true, reason: nil, embeddings: encoded, source: session.source, dim: embeddings[0].count)
429
+ }
430
+
431
+ private func finishEnrollment(ok: Bool, reason: String?, embeddings: [String], source: String, dim: Int = 0) {
432
+ activeFlag.lock()
433
+ _enrolling = false
434
+ activeFlag.unlock()
435
+ Bridge.log(
436
+ ok
437
+ ? "SpeakerID: enrollment ok — \(embeddings.count) embeddings (dim=\(dim), mic=\(source))"
438
+ : "SpeakerID: enrollment failed — \(reason ?? "unknown") (mic=\(source))"
439
+ )
440
+ Bridge.sendSpeakerEnrollResult(ok: ok, reason: reason, embeddings: embeddings, dim: dim, source: source)
441
+ let onEnded = enrollmentEnded
442
+ enrollmentEnded = nil
443
+ onEnded?()
444
+ }
445
+
446
+ // MARK: - Model loading (queue only)
447
+
448
+ private func ensureExtractor() {
449
+ guard extractor == nil, !extractorLoadAttempted else { return }
450
+ extractorLoadAttempted = true
451
+ guard let modelPath = Self.resolvedModelPath(),
452
+ FileManager.default.fileExists(atPath: modelPath)
453
+ else {
454
+ Bridge.log("SpeakerID: model not downloaded yet — engine idle")
455
+ return
456
+ }
457
+ // Config and construction share one C-string scope; the config's
458
+ // `const char*` fields point at buffers owned by it (see toCPointer in
459
+ // SherpaOnnx.swift) and must not outlive it.
460
+ extractor = withSherpaOnnxCStrings {
461
+ var config = sherpaOnnxSpeakerEmbeddingExtractorConfig(model: modelPath, numThreads: 2)
462
+ return SherpaOnnxSpeakerEmbeddingExtractorWrapper(config: &config)
463
+ }
464
+ Bridge.log("SpeakerID: embedding extractor initialized (dim=\(extractor?.dim ?? -1))")
465
+ }
466
+
467
+ /// Same Documents-relative reconstruction as the STT model path (the app
468
+ /// container id changes between dev builds).
469
+ private static func resolvedModelPath() -> String? {
470
+ guard let storedPath = UserDefaults.standard.string(forKey: modelPathKey) else { return nil }
471
+ let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
472
+ if let range = storedPath.range(of: "/Documents/") {
473
+ let relativePath = String(storedPath[range.upperBound...])
474
+ return documentsURL.appendingPathComponent(relativePath).path
475
+ }
476
+ return storedPath
477
+ }
478
+
479
+ // MARK: - Helpers
480
+
481
+ private func resetWindow() {
482
+ windowSamples = []
483
+ windowStartMs = 0
484
+ windowVoicedMs = 0
485
+ trailingSilenceMs = 0
486
+ }
487
+
488
+ private static func toFloatArray(_ pcmData: Data) -> [Float] {
489
+ let count = pcmData.count / 2
490
+ var samples = [Float](repeating: 0, count: count)
491
+ pcmData.withUnsafeBytes { (bufferPointer: UnsafeRawBufferPointer) in
492
+ guard let address = bufferPointer.baseAddress else { return }
493
+ let int16Pointer = address.bindMemory(to: Int16.self, capacity: count)
494
+ for i in 0 ..< count {
495
+ samples[i] = Float(Int16(littleEndian: int16Pointer[i])) / 32768.0
496
+ }
497
+ }
498
+ return samples
499
+ }
500
+
501
+ private static func rms(_ samples: [Float]) -> Float {
502
+ guard !samples.isEmpty else { return 0 }
503
+ var sum: Float = 0
504
+ for s in samples { sum += s * s }
505
+ return (sum / Float(samples.count)).squareRoot()
506
+ }
507
+
508
+ private static func dot(_ a: [Float], _ b: [Float]) -> Float {
509
+ guard a.count == b.count else { return 0 }
510
+ var sum: Float = 0
511
+ for i in 0 ..< a.count { sum += a[i] * b[i] }
512
+ return sum
513
+ }
514
+
515
+ private static func l2Normalize(_ v: [Float]) -> [Float] {
516
+ var sum: Float = 0
517
+ for x in v { sum += x * x }
518
+ let norm = sum.squareRoot()
519
+ guard norm > 0 else { return v }
520
+ return v.map { $0 / norm }
521
+ }
522
+
523
+ private static func base64(from embedding: [Float]) -> String {
524
+ embedding.withUnsafeBufferPointer { buffer in
525
+ Data(buffer: buffer).base64EncodedString()
526
+ }
527
+ }
528
+
529
+ static func embedding(fromBase64 base64: String) -> [Float]? {
530
+ guard let data = Data(base64Encoded: base64), data.count % 4 == 0, !data.isEmpty else { return nil }
531
+ let count = data.count / 4
532
+ var floats = [Float](repeating: 0, count: count)
533
+ _ = floats.withUnsafeMutableBytes { data.copyBytes(to: $0) }
534
+ return floats
535
+ }
536
+ }
537
+
538
+ #else
539
+
540
+ /// Stub for SWIFT_PACKAGE builds without GLASSLY_FEATURE_LOCAL_STT — the
541
+ /// sherpa-onnx runtime isn't linked, so speaker ID is unavailable.
542
+ final class SpeakerIdEngine {
543
+ static let shared = SpeakerIdEngine()
544
+
545
+ struct Profile {
546
+ let id: String
547
+ let name: String
548
+ let embedding: [Float]
549
+ }
550
+
551
+ struct Match {
552
+ let profileId: String
553
+ let name: String
554
+ let similarity: Float
555
+ let endMs: Int64
556
+ }
557
+
558
+ func setIdentifyEnabled(_: Bool) {}
559
+ func setProfiles(_: [Profile]) {}
560
+ static func setModelPath(_: String) {}
561
+ static func isModelAvailable() -> Bool { false }
562
+ func startEnrollment(targetSpeechSeconds _: Double, source: String, onEnded: @escaping () -> Void) {
563
+ Bridge.sendSpeakerEnrollResult(ok: false, reason: "unsupported", embeddings: [], dim: 0, source: source)
564
+ onEnded()
565
+ }
566
+
567
+ func cancelEnrollment() {}
568
+ func recentMatch(withinMs _: Int64) -> Match? { nil }
569
+ func acceptAudio(pcm16le _: Data) {}
570
+ static func embedding(fromBase64 _: String) -> [Float]? { nil }
571
+ }
572
+
573
+ #endif