@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,92 @@
1
+ package com.glassly.bluetoothsdk.sgcs.ar99.ota;
2
+
3
+ import java.io.ByteArrayOutputStream;
4
+
5
+ public final class OtaProtocol {
6
+ private OtaProtocol() {}
7
+
8
+ public static byte[] buildFrame(byte cmd, byte paramType, byte[] payload) {
9
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
10
+ out.write(OtaCommandConstants.OTA.SVC_ID & 0xFF);
11
+ out.write(cmd & 0xFF);
12
+ out.write(paramType & 0xFF);
13
+ int len = payload != null ? payload.length : 0;
14
+ out.write(len & 0xFF);
15
+ out.write((len >> 8) & 0xFF);
16
+ if (payload != null && payload.length > 0) {
17
+ try {
18
+ out.write(payload);
19
+ } catch (Exception ignored) {
20
+ }
21
+ }
22
+ return out.toByteArray();
23
+ }
24
+
25
+ public static byte[] buildRequestUpgrade(int packageSize, int blockSize, long crc32) {
26
+ // Wire layout matches the vendor bring-up frame: type marker, then TLV
27
+ // 0x0A/len4 = package size (LE). CRC is validated per-block in SEND_IMAGE_DATA;
28
+ // blockSize is negotiated separately in CONNECT_NEGOTIATION.
29
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
30
+ payload.write(0x09);
31
+ payload.write(0x01);
32
+ payload.write(0x00);
33
+ payload.write(0x01);
34
+ payload.write(0x0A);
35
+ payload.write(0x04);
36
+ payload.write(packageSize & 0xFF);
37
+ payload.write((packageSize >> 8) & 0xFF);
38
+ payload.write((packageSize >> 16) & 0xFF);
39
+ payload.write((packageSize >> 24) & 0xFF);
40
+ payload.write(0x00);
41
+ return buildFrame(OtaCommandConstants.OTA.REQUEST_UPGRADE, (byte) 0x80, payload.toByteArray());
42
+ }
43
+
44
+ public static byte[] buildConnectNegotiation(int blockSize) {
45
+ return buildFrame(OtaCommandConstants.OTA.CONNECT_NEGOTIATION, (byte) 0x80, null);
46
+ }
47
+
48
+ public static byte[] buildNegotiationResult() {
49
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
50
+ payload.write(0x01);
51
+ payload.write(0x01);
52
+ payload.write(0x00);
53
+ payload.write(0x01);
54
+ return buildFrame(OtaCommandConstants.OTA.NEGOTIATION_RESULT, (byte) 0x80, payload.toByteArray());
55
+ }
56
+
57
+ public static byte[] buildSendImageData(int psn, byte[] data) {
58
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
59
+ payload.write(psn & 0xFF);
60
+ long crc32 = OtaCrc32Util.calculate(data);
61
+ payload.write((byte) (crc32 & 0xFF));
62
+ payload.write((byte) ((crc32 >> 8) & 0xFF));
63
+ payload.write((byte) ((crc32 >> 16) & 0xFF));
64
+ payload.write((byte) ((crc32 >> 24) & 0xFF));
65
+ if (data != null && data.length > 0) {
66
+ try {
67
+ payload.write(data);
68
+ } catch (Exception ignored) {
69
+ }
70
+ }
71
+ return buildFrame(OtaCommandConstants.OTA.SEND_IMAGE_DATA, (byte) 0x80, payload.toByteArray());
72
+ }
73
+
74
+ public static FrameHeader parseFrameHeader(byte[] frame) {
75
+ if (frame == null || frame.length < 5) return null;
76
+ FrameHeader header = new FrameHeader();
77
+ header.svcId = frame[0];
78
+ header.cmd = frame[1];
79
+ header.paramType = frame[2];
80
+ header.paramLen = OtaByteUtils.readShortLE(frame, 3) & 0xFFFF;
81
+ header.payloadOffset = 5;
82
+ return header;
83
+ }
84
+
85
+ public static class FrameHeader {
86
+ public byte svcId;
87
+ public byte cmd;
88
+ public byte paramType;
89
+ public int paramLen;
90
+ public int payloadOffset;
91
+ }
92
+ }
@@ -0,0 +1,525 @@
1
+ package com.glassly.bluetoothsdk.speakerid
2
+
3
+ import android.util.Base64
4
+ import com.glassly.bluetoothsdk.Bridge
5
+ import com.k2fsa.sherpa.onnx.SpeakerEmbeddingExtractor
6
+ import com.k2fsa.sherpa.onnx.SpeakerEmbeddingExtractorConfig
7
+ import java.io.File
8
+ import java.nio.ByteBuffer
9
+ import java.nio.ByteOrder
10
+ import java.util.concurrent.Executors
11
+ import java.util.concurrent.atomic.AtomicBoolean
12
+ import kotlin.math.sqrt
13
+
14
+ /**
15
+ * On-device speaker identification over the canonical 16 kHz mono PCM stream.
16
+ *
17
+ * Taps DeviceManager.handlePcm (the single audio choke point), windows voiced
18
+ * audio with a local energy gate, computes a speaker embedding per
19
+ * utterance-ish window (sherpa-onnx CAM++ model), matches against enrolled
20
+ * profile centroids by cosine similarity, and emits `speaker_detected` bridge
21
+ * events. Raw audio never leaves the phone for speaker ID; enrollment audio is
22
+ * discarded as soon as its embeddings are computed.
23
+ *
24
+ * Profiles are pushed in from JS (`setSpeakerProfiles`) — MMKV in the engine
25
+ * layer is the source of truth; native holds them in memory only.
26
+ *
27
+ * Ported 1:1 from iOS SpeakerIdEngine.swift.
28
+ */
29
+ object SpeakerIdEngine {
30
+ private const val TAG = "SpeakerIdEngine"
31
+
32
+ // Benchmarked cross-speaker ceiling for this model is ~0.46 (the MAX over
33
+ // clean-audio pairs; typical impostors score far lower), while on-device
34
+ // same-speaker scores run below the 0.67+ clean bench. Owner-only voice
35
+ // recognition favors recall: a borderline accept just labels a line "You",
36
+ // and the JS segment resolver's overlap voting + conflict damping still
37
+ // filter isolated stray windows — so sit at the edge of the impostor range
38
+ // rather than above it. (With one enrolled profile the margin rule is
39
+ // vacuous.)
40
+ private const val MATCH_THRESHOLD = 0.45f
41
+ private const val MATCH_MARGIN = 0.08f
42
+ private const val WINDOW_VOICED_TARGET_MS = 3000.0
43
+ private const val END_OF_SPEECH_SILENCE_MS = 500.0
44
+ private const val MIN_VOICED_MS = 1000.0
45
+ private const val MAX_WINDOW_SAMPLES = 16000 * 10
46
+ private const val VOICED_RMS_THRESHOLD = 0.01f
47
+ private const val ENROLL_CONSISTENCY_THRESHOLD = 0.6f
48
+ private const val ENROLL_CHUNK_COUNT = 3
49
+
50
+ private const val SAMPLE_RATE = 16000
51
+ private const val PREFS_NAME = "GlasslyPrefs"
52
+ private const val KEY_MODEL_PATH = "SpeakerIdModelPath"
53
+
54
+ data class Profile(val id: String, val name: String, val embedding: FloatArray)
55
+
56
+ data class Match(
57
+ val profileId: String,
58
+ val name: String,
59
+ val similarity: Float,
60
+ /** Epoch ms when the window producing this match ended. */
61
+ val endMs: Long,
62
+ )
63
+
64
+ private val executor = Executors.newSingleThreadExecutor { runnable ->
65
+ Thread(runnable, "SpeakerIdProcessor").apply { isDaemon = true }
66
+ }
67
+
68
+ private val identifyEnabled = AtomicBoolean(false)
69
+ private val enrolling = AtomicBoolean(false)
70
+
71
+ // Everything below is touched on the executor thread only.
72
+ private var extractor: SpeakerEmbeddingExtractor? = null
73
+ private var extractorLoadAttempted = false
74
+ @Volatile private var profiles: List<Profile> = emptyList()
75
+
76
+ private var windowSamples = ArrayList<Float>()
77
+ private var windowStartMs = 0L
78
+ private var windowVoicedMs = 0.0
79
+ private var trailingSilenceMs = 0.0
80
+
81
+ private class EnrollmentSession(
82
+ val targetVoicedMs: Double,
83
+ val source: String,
84
+ ) {
85
+ val samples = ArrayList<Float>()
86
+ var voicedMs = 0.0
87
+ var lastProgressAt = 0L
88
+ }
89
+
90
+ private var enrollment: EnrollmentSession? = null
91
+ private var enrollmentEnded: (() -> Unit)? = null
92
+
93
+ @Volatile private var lastMatch: Match? = null
94
+
95
+ // MARK: - Public API (any thread)
96
+
97
+ fun setIdentifyEnabled(enabled: Boolean) {
98
+ identifyEnabled.set(enabled)
99
+ Bridge.log("SpeakerID: identify ${if (enabled) "enabled" else "disabled"}")
100
+ executor.execute {
101
+ if (enabled) ensureExtractor() else resetWindow()
102
+ }
103
+ }
104
+
105
+ fun setProfiles(next: List<Profile>) {
106
+ profiles = next
107
+ Bridge.log(
108
+ "SpeakerID: profiles set — ${next.size}: " +
109
+ next.joinToString(", ") { "${it.name}(${it.embedding.size}d)" }
110
+ )
111
+ }
112
+
113
+ fun setModelPath(path: String) {
114
+ try {
115
+ val prefs = Bridge.getContext().getSharedPreferences(PREFS_NAME, android.content.Context.MODE_PRIVATE)
116
+ prefs.edit().putString(KEY_MODEL_PATH, path).apply()
117
+ Bridge.log("SpeakerID: model path set → $path")
118
+ } catch (e: Exception) {
119
+ Bridge.log("SpeakerID: failed to persist model path: ${e.message}")
120
+ }
121
+ executor.execute {
122
+ // Force a reload on next use — the model may have just arrived.
123
+ extractor?.release()
124
+ extractor = null
125
+ extractorLoadAttempted = false
126
+ }
127
+ }
128
+
129
+ fun isModelAvailable(): Boolean {
130
+ val path = resolvedModelPath() ?: return false
131
+ return File(path).let { it.exists() && it.length() > 0 }
132
+ }
133
+
134
+ /**
135
+ * [onEnded] fires (on the engine thread) whenever the enrollment session
136
+ * finishes or fails, so the caller can release the mic requirement.
137
+ */
138
+ fun startEnrollment(targetSpeechSeconds: Double, source: String, onEnded: () -> Unit) {
139
+ enrolling.set(true)
140
+ executor.execute {
141
+ ensureExtractor()
142
+ if (extractor == null) {
143
+ Bridge.log("SpeakerID: enrollment aborted — model unavailable")
144
+ finishEnrollment(ok = false, reason = "model_unavailable", embeddings = emptyList(), source = source)
145
+ onEnded()
146
+ return@execute
147
+ }
148
+ Bridge.log("SpeakerID: enrollment started — target ${targetSpeechSeconds}s voiced, mic=$source")
149
+ enrollmentEnded = onEnded
150
+ enrollment = EnrollmentSession(
151
+ targetVoicedMs = maxOf(5.0, targetSpeechSeconds) * 1000.0,
152
+ source = source,
153
+ )
154
+ }
155
+ }
156
+
157
+ fun cancelEnrollment() {
158
+ enrolling.set(false)
159
+ executor.execute {
160
+ enrollment = null
161
+ val onEnded = enrollmentEnded
162
+ enrollmentEnded = null
163
+ onEnded?.invoke()
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Latest accepted identification, if it ended within [withinMs] of now.
169
+ * Used by the local STT path to stamp transcripts (native-side join).
170
+ */
171
+ fun recentMatch(withinMs: Long): Match? {
172
+ val match = lastMatch ?: return null
173
+ return if (System.currentTimeMillis() - match.endMs <= withinMs) match else null
174
+ }
175
+
176
+ /** Called from DeviceManager.handlePcm with 16 kHz mono s16le PCM. */
177
+ fun acceptAudio(pcm16le: ByteArray) {
178
+ if (!identifyEnabled.get() && !enrolling.get()) return
179
+ if (pcm16le.size < 2) return
180
+ val copy = pcm16le.copyOf()
181
+ executor.execute { process(copy) }
182
+ }
183
+
184
+ // MARK: - Processing (executor thread only)
185
+
186
+ private fun process(pcmData: ByteArray) {
187
+ val samples = toFloatArray(pcmData)
188
+ if (samples.isEmpty()) return
189
+ val chunkMs = samples.size.toDouble() / SAMPLE_RATE * 1000.0
190
+ val rms = rms(samples)
191
+ val voiced = rms > VOICED_RMS_THRESHOLD
192
+
193
+ if (enrollment != null) {
194
+ processEnrollment(samples, chunkMs, rms, voiced)
195
+ return
196
+ }
197
+ if (!identifyEnabled.get()) return
198
+ processIdentification(samples, chunkMs, voiced)
199
+ }
200
+
201
+ private fun processIdentification(samples: FloatArray, chunkMs: Double, voiced: Boolean) {
202
+ val nowMs = System.currentTimeMillis()
203
+
204
+ if (windowSamples.isEmpty()) {
205
+ if (!voiced) return
206
+ windowStartMs = nowMs - chunkMs.toLong()
207
+ windowVoicedMs = 0.0
208
+ trailingSilenceMs = 0.0
209
+ }
210
+
211
+ // Accumulate VOICED samples only — enrollment computes centroids from
212
+ // voiced-only audio, so identification must embed like-for-like.
213
+ // Windows padded with inter-phrase silence score far below the same
214
+ // speaker's centroid (observed 0.1–0.48 vs the 0.67+ bench).
215
+ if (voiced) {
216
+ if (windowSamples.size < MAX_WINDOW_SAMPLES) {
217
+ windowSamples.ensureCapacity(windowSamples.size + samples.size)
218
+ for (s in samples) windowSamples.add(s)
219
+ }
220
+ windowVoicedMs += chunkMs
221
+ trailingSilenceMs = 0.0
222
+ } else {
223
+ trailingSilenceMs += chunkMs
224
+ }
225
+
226
+ val endOfSpeech = trailingSilenceMs >= END_OF_SPEECH_SILENCE_MS
227
+ val windowFull = windowVoicedMs >= WINDOW_VOICED_TARGET_MS
228
+ if (!endOfSpeech && !windowFull) return
229
+
230
+ val startMs = windowStartMs
231
+ val voicedMs = windowVoicedMs
232
+ val window = windowSamples.toFloatArray()
233
+ resetWindow()
234
+
235
+ if (voicedMs < MIN_VOICED_MS) {
236
+ Bridge.log("SpeakerID: window skipped — only ${voicedMs.toInt()}ms voiced (<${MIN_VOICED_MS.toInt()}ms)")
237
+ return
238
+ }
239
+ identify(window, startMs, nowMs, voicedMs)
240
+ }
241
+
242
+ private fun identify(window: FloatArray, startMs: Long, endMs: Long, voicedMs: Double) {
243
+ ensureExtractor()
244
+ val extractor = extractor
245
+ if (extractor == null) {
246
+ Bridge.log("SpeakerID: window dropped — extractor not loaded (model missing?)")
247
+ return
248
+ }
249
+ val currentProfiles = profiles
250
+ if (currentProfiles.isEmpty()) {
251
+ Bridge.log("SpeakerID: window dropped — no enrolled profiles pushed")
252
+ return
253
+ }
254
+
255
+ val stream = extractor.createStream()
256
+ val embedding: FloatArray
257
+ try {
258
+ stream.acceptWaveform(window, SAMPLE_RATE)
259
+ stream.inputFinished()
260
+ if (!extractor.isReady(stream)) {
261
+ Bridge.log("SpeakerID: window dropped — extractor not ready (${window.size} samples)")
262
+ return
263
+ }
264
+ embedding = l2Normalize(extractor.compute(stream))
265
+ } finally {
266
+ stream.release()
267
+ }
268
+ if (embedding.isEmpty()) {
269
+ Bridge.log("SpeakerID: window dropped — empty embedding")
270
+ return
271
+ }
272
+
273
+ var best: Profile? = null
274
+ var bestCos = -1f
275
+ var second = -1f
276
+ for (profile in currentProfiles) {
277
+ if (profile.embedding.size != embedding.size) continue
278
+ val cos = dot(profile.embedding, embedding)
279
+ if (best == null || cos > bestCos) {
280
+ second = bestCos
281
+ best = profile
282
+ bestCos = cos
283
+ } else if (cos > second) {
284
+ second = cos
285
+ }
286
+ }
287
+
288
+ var matched: Profile? = null
289
+ if (best != null && bestCos >= MATCH_THRESHOLD) {
290
+ // Margin rule: reject ambiguous windows where the runner-up is
291
+ // close (overlapping talkers produce garbage embeddings).
292
+ if (second < 0 || bestCos - second >= MATCH_MARGIN) {
293
+ matched = best
294
+ }
295
+ }
296
+
297
+ if (matched != null) {
298
+ lastMatch = Match(matched.id, matched.name, bestCos, endMs)
299
+ }
300
+
301
+ val durationMs = endMs - startMs
302
+ val speech = "speech=${voicedMs.toInt()}ms window=${durationMs}ms"
303
+ if (matched != null) {
304
+ Bridge.log(
305
+ "SpeakerID: matched ${matched.name} cos=${"%.3f".format(bestCos)} " +
306
+ "second=${"%.3f".format(second)} $speech"
307
+ )
308
+ } else if (best != null) {
309
+ val reason =
310
+ if (bestCos < MATCH_THRESHOLD) "below threshold $MATCH_THRESHOLD"
311
+ else "margin to runner-up < $MATCH_MARGIN"
312
+ Bridge.log(
313
+ "SpeakerID: no match — best ${best.name} cos=${"%.3f".format(bestCos)} " +
314
+ "second=${"%.3f".format(second)} ($reason) $speech"
315
+ )
316
+ }
317
+
318
+ Bridge.sendSpeakerDetected(
319
+ speakerId = matched?.id,
320
+ name = matched?.name,
321
+ similarity = (if (matched != null) bestCos else maxOf(bestCos, 0f)).toDouble(),
322
+ startMs = startMs,
323
+ endMs = endMs,
324
+ )
325
+ }
326
+
327
+ // MARK: - Enrollment (executor thread only)
328
+
329
+ private fun processEnrollment(samples: FloatArray, chunkMs: Double, rms: Float, voiced: Boolean) {
330
+ val session = enrollment ?: return
331
+ if (voiced) {
332
+ session.samples.ensureCapacity(session.samples.size + samples.size)
333
+ for (s in samples) session.samples.add(s)
334
+ session.voicedMs += chunkMs
335
+ }
336
+
337
+ val now = System.currentTimeMillis()
338
+ if (now - session.lastProgressAt >= 250) {
339
+ session.lastProgressAt = now
340
+ Bridge.sendSpeakerEnrollProgress(
341
+ voicedSeconds = session.voicedMs / 1000.0,
342
+ targetSeconds = session.targetVoicedMs / 1000.0,
343
+ level = rms.toDouble(),
344
+ )
345
+ }
346
+
347
+ if (session.voicedMs >= session.targetVoicedMs) {
348
+ enrollment = null
349
+ enrolling.set(false)
350
+ completeEnrollment(session)
351
+ }
352
+ }
353
+
354
+ private fun completeEnrollment(session: EnrollmentSession) {
355
+ val extractor = extractor
356
+ if (extractor == null) {
357
+ finishEnrollment(ok = false, reason = "model_unavailable", embeddings = emptyList(), source = session.source)
358
+ return
359
+ }
360
+
361
+ // One embedding per prompt-sized chunk; internal consistency check
362
+ // catches wind/noise/multiple talkers.
363
+ val chunkSize = session.samples.size / ENROLL_CHUNK_COUNT
364
+ if (chunkSize < SAMPLE_RATE) {
365
+ finishEnrollment(ok = false, reason = "too_short", embeddings = emptyList(), source = session.source)
366
+ return
367
+ }
368
+
369
+ val embeddings = ArrayList<FloatArray>()
370
+ for (i in 0 until ENROLL_CHUNK_COUNT) {
371
+ val start = i * chunkSize
372
+ val end = if (i == ENROLL_CHUNK_COUNT - 1) session.samples.size else start + chunkSize
373
+ val chunk = FloatArray(end - start) { session.samples[start + it] }
374
+ val stream = extractor.createStream()
375
+ try {
376
+ stream.acceptWaveform(chunk, SAMPLE_RATE)
377
+ stream.inputFinished()
378
+ if (!extractor.isReady(stream)) continue
379
+ val embedding = l2Normalize(extractor.compute(stream))
380
+ if (embedding.isNotEmpty()) embeddings.add(embedding)
381
+ } finally {
382
+ stream.release()
383
+ }
384
+ }
385
+
386
+ if (embeddings.size < 2) {
387
+ finishEnrollment(ok = false, reason = "too_short", embeddings = emptyList(), source = session.source)
388
+ return
389
+ }
390
+
391
+ for (i in embeddings.indices) {
392
+ for (j in i + 1 until embeddings.size) {
393
+ val cos = dot(embeddings[i], embeddings[j])
394
+ if (cos < ENROLL_CONSISTENCY_THRESHOLD) {
395
+ Bridge.log(
396
+ "SpeakerID: enrollment chunks $i/$j inconsistent — cos=${"%.3f".format(cos)} " +
397
+ "< $ENROLL_CONSISTENCY_THRESHOLD (noise / multiple talkers?)"
398
+ )
399
+ finishEnrollment(ok = false, reason = "inconsistent", embeddings = emptyList(), source = session.source)
400
+ return
401
+ }
402
+ }
403
+ }
404
+
405
+ finishEnrollment(
406
+ ok = true,
407
+ reason = null,
408
+ embeddings = embeddings.map { encodeBase64(it) },
409
+ source = session.source,
410
+ dim = embeddings[0].size,
411
+ )
412
+ }
413
+
414
+ private fun finishEnrollment(ok: Boolean, reason: String?, embeddings: List<String>, source: String, dim: Int = 0) {
415
+ enrolling.set(false)
416
+ Bridge.log(
417
+ if (ok) "SpeakerID: enrollment ok — ${embeddings.size} embeddings (dim=$dim, mic=$source)"
418
+ else "SpeakerID: enrollment failed — ${reason ?: "unknown"} (mic=$source)"
419
+ )
420
+ Bridge.sendSpeakerEnrollResult(ok = ok, reason = reason, embeddings = embeddings, dim = dim, source = source)
421
+ val onEnded = enrollmentEnded
422
+ enrollmentEnded = null
423
+ onEnded?.invoke()
424
+ }
425
+
426
+ // MARK: - Model loading (executor thread only)
427
+
428
+ private fun ensureExtractor() {
429
+ if (extractor != null || extractorLoadAttempted) return
430
+ extractorLoadAttempted = true
431
+ val modelPath = resolvedModelPath()
432
+ if (modelPath == null || !File(modelPath).exists()) {
433
+ Bridge.log("SpeakerID: model not downloaded yet — engine idle")
434
+ return
435
+ }
436
+ try {
437
+ val config = SpeakerEmbeddingExtractorConfig(modelPath, 2, false, "cpu")
438
+ extractor = SpeakerEmbeddingExtractor(null, config)
439
+ Bridge.log("SpeakerID: embedding extractor initialized (dim=${extractor?.dim()})")
440
+ } catch (e: Exception) {
441
+ Bridge.log("SpeakerID: failed to initialize extractor: ${e.message}")
442
+ extractor = null
443
+ } catch (e: LinkageError) {
444
+ Bridge.log("SpeakerID: sherpa-onnx runtime unavailable: ${e.message}")
445
+ extractor = null
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Same Documents-relative reconstruction as the STT model path (the app
451
+ * id changes between dev builds; iOS parity).
452
+ */
453
+ private fun resolvedModelPath(): String? {
454
+ return try {
455
+ val context = Bridge.getContext()
456
+ val prefs = context.getSharedPreferences(PREFS_NAME, android.content.Context.MODE_PRIVATE)
457
+ val storedPath = prefs.getString(KEY_MODEL_PATH, null) ?: return null
458
+ if (storedPath.contains("/Documents/")) {
459
+ File(context.filesDir, storedPath.substringAfter("/Documents/")).absolutePath
460
+ } else {
461
+ storedPath
462
+ }
463
+ } catch (e: Exception) {
464
+ null
465
+ }
466
+ }
467
+
468
+ // MARK: - Helpers
469
+
470
+ private fun resetWindow() {
471
+ windowSamples = ArrayList()
472
+ windowStartMs = 0
473
+ windowVoicedMs = 0.0
474
+ trailingSilenceMs = 0.0
475
+ }
476
+
477
+ private fun toFloatArray(pcmData: ByteArray): FloatArray {
478
+ val count = pcmData.size / 2
479
+ val samples = FloatArray(count)
480
+ val buffer = ByteBuffer.wrap(pcmData).order(ByteOrder.LITTLE_ENDIAN)
481
+ for (i in 0 until count) {
482
+ samples[i] = buffer.short / 32768.0f
483
+ }
484
+ return samples
485
+ }
486
+
487
+ private fun rms(samples: FloatArray): Float {
488
+ if (samples.isEmpty()) return 0f
489
+ var sum = 0f
490
+ for (s in samples) sum += s * s
491
+ return sqrt(sum / samples.size)
492
+ }
493
+
494
+ private fun dot(a: FloatArray, b: FloatArray): Float {
495
+ if (a.size != b.size) return 0f
496
+ var sum = 0f
497
+ for (i in a.indices) sum += a[i] * b[i]
498
+ return sum
499
+ }
500
+
501
+ private fun l2Normalize(v: FloatArray): FloatArray {
502
+ var sum = 0f
503
+ for (x in v) sum += x * x
504
+ val norm = sqrt(sum)
505
+ if (norm <= 0f) return v
506
+ return FloatArray(v.size) { v[it] / norm }
507
+ }
508
+
509
+ private fun encodeBase64(embedding: FloatArray): String {
510
+ val bytes = ByteBuffer.allocate(embedding.size * 4).order(ByteOrder.LITTLE_ENDIAN)
511
+ for (f in embedding) bytes.putFloat(f)
512
+ return Base64.encodeToString(bytes.array(), Base64.NO_WRAP)
513
+ }
514
+
515
+ fun decodeEmbedding(base64: String): FloatArray? {
516
+ return try {
517
+ val bytes = Base64.decode(base64, Base64.DEFAULT)
518
+ if (bytes.isEmpty() || bytes.size % 4 != 0) return null
519
+ val buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)
520
+ FloatArray(bytes.size / 4) { buffer.getFloat(it * 4) }
521
+ } catch (e: IllegalArgumentException) {
522
+ null
523
+ }
524
+ }
525
+ }