@mentra/bluetooth-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/README.md +278 -0
  2. package/android/build.gradle +167 -0
  3. package/android/lc3Lib/CMakeLists.txt +6 -0
  4. package/android/lc3Lib/build.gradle +109 -0
  5. package/android/lc3Lib/proguard-rules.pro +33 -0
  6. package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
  7. package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
  8. package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
  9. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
  10. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
  11. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
  12. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
  13. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
  14. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
  15. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
  16. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
  17. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
  18. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
  19. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
  20. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
  21. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
  22. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
  23. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
  24. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
  25. package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
  26. package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
  27. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
  28. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
  29. package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
  30. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
  31. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
  32. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
  33. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
  34. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
  35. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
  36. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
  37. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
  38. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
  39. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
  40. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
  41. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
  42. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
  43. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
  44. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
  45. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
  46. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
  47. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
  48. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
  49. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
  50. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
  51. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
  52. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
  53. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
  54. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
  55. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
  56. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
  57. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
  58. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
  59. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
  60. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
  61. package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
  62. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
  63. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
  64. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
  65. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
  66. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
  67. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
  68. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
  69. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
  70. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
  71. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
  72. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
  73. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
  74. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
  75. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
  76. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
  77. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
  78. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
  79. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
  80. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
  81. package/android/lc3Lib/src/main/java/com/mentra/lc3Lib/Lc3Cpp.java +38 -0
  82. package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
  83. package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
  84. package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
  85. package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
  86. package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
  87. package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
  88. package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
  89. package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
  90. package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
  91. package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
  92. package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
  93. package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
  94. package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
  95. package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
  96. package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
  97. package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
  98. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
  99. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
  100. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
  101. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
  102. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
  103. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
  104. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
  105. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
  106. package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
  107. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
  108. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
  109. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
  110. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
  111. package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
  112. package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
  113. package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
  114. package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
  115. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
  116. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
  117. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
  118. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
  119. package/android/settings.gradle +1 -0
  120. package/android/src/main/AndroidManifest.xml +40 -0
  121. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +528 -0
  122. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +610 -0
  123. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +1616 -0
  124. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +314 -0
  125. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +1710 -0
  126. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +554 -0
  127. package/android/src/main/java/com/mentra/bluetoothsdk/ObservableStore.kt +68 -0
  128. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +158 -0
  129. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +934 -0
  130. package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +175 -0
  131. package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +1109 -0
  132. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +3974 -0
  133. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +3551 -0
  134. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1327 -0
  135. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +6959 -0
  136. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1655 -0
  137. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +53675 -0
  138. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +202 -0
  139. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +224 -0
  140. package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +340 -0
  141. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +465 -0
  142. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +221 -0
  143. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AES.java +106 -0
  144. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
  145. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
  146. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +179 -0
  147. package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +44 -0
  148. package/android/src/main/java/com/mentra/bluetoothsdk/utils/G1Text.kt +436 -0
  149. package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
  150. package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleUploadService.java +76 -0
  151. package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +791 -0
  152. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +186 -0
  153. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +589 -0
  154. package/android/src/main/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitor.kt +303 -0
  155. package/android/src/main/java/com/mentra/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
  156. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
  157. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
  158. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
  159. package/android/src/main/res/values/strings.xml +4 -0
  160. package/app.plugin.js +1 -0
  161. package/build/BluetoothSdk.types.d.ts +498 -0
  162. package/build/BluetoothSdk.types.d.ts.map +1 -0
  163. package/build/BluetoothSdk.types.js +23 -0
  164. package/build/BluetoothSdk.types.js.map +1 -0
  165. package/build/BluetoothSdkModule.d.ts +71 -0
  166. package/build/BluetoothSdkModule.d.ts.map +1 -0
  167. package/build/BluetoothSdkModule.js +106 -0
  168. package/build/BluetoothSdkModule.js.map +1 -0
  169. package/build/index.d.ts +3 -0
  170. package/build/index.d.ts.map +1 -0
  171. package/build/index.js +5 -0
  172. package/build/index.js.map +1 -0
  173. package/expo-module.config.json +9 -0
  174. package/ios/BluetoothSdkModule.swift +665 -0
  175. package/ios/MentraBluetoothSDK.podspec +77 -0
  176. package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
  177. package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
  178. package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
  179. package/ios/Packages/CoreObjC/attdet.c +92 -0
  180. package/ios/Packages/CoreObjC/attdet.h +44 -0
  181. package/ios/Packages/CoreObjC/bits.c +375 -0
  182. package/ios/Packages/CoreObjC/bits.h +315 -0
  183. package/ios/Packages/CoreObjC/bwdet.c +129 -0
  184. package/ios/Packages/CoreObjC/bwdet.h +69 -0
  185. package/ios/Packages/CoreObjC/common.h +151 -0
  186. package/ios/Packages/CoreObjC/energy.c +70 -0
  187. package/ios/Packages/CoreObjC/energy.h +43 -0
  188. package/ios/Packages/CoreObjC/fastmath.h +158 -0
  189. package/ios/Packages/CoreObjC/lc3.c +704 -0
  190. package/ios/Packages/CoreObjC/lc3.h +313 -0
  191. package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
  192. package/ios/Packages/CoreObjC/lc3_private.h +163 -0
  193. package/ios/Packages/CoreObjC/ltpf.c +905 -0
  194. package/ios/Packages/CoreObjC/ltpf.h +111 -0
  195. package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
  196. package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
  197. package/ios/Packages/CoreObjC/makefile.mk +35 -0
  198. package/ios/Packages/CoreObjC/mdct.c +469 -0
  199. package/ios/Packages/CoreObjC/mdct.h +57 -0
  200. package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
  201. package/ios/Packages/CoreObjC/meson.build +61 -0
  202. package/ios/Packages/CoreObjC/plc.c +61 -0
  203. package/ios/Packages/CoreObjC/plc.h +57 -0
  204. package/ios/Packages/CoreObjC/rnnoise.h +114 -0
  205. package/ios/Packages/CoreObjC/sns.c +880 -0
  206. package/ios/Packages/CoreObjC/sns.h +103 -0
  207. package/ios/Packages/CoreObjC/spec.c +907 -0
  208. package/ios/Packages/CoreObjC/spec.h +119 -0
  209. package/ios/Packages/CoreObjC/tables.c +3457 -0
  210. package/ios/Packages/CoreObjC/tables.h +94 -0
  211. package/ios/Packages/CoreObjC/tns.c +457 -0
  212. package/ios/Packages/CoreObjC/tns.h +99 -0
  213. package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
  214. package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
  215. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +1659 -0
  216. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +162 -0
  217. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +1852 -0
  218. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +674 -0
  219. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +44 -0
  220. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  221. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
  222. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  223. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
  224. package/ios/Packages/VAD/Common/VAD/VADQuality.swift +41 -0
  225. package/ios/Packages/VAD/Common/VAD/VADState.swift +26 -0
  226. package/ios/Packages/VAD/Common/VAD/VADStrategy.swift +29 -0
  227. package/ios/Packages/VAD/Common/VAD/VADType.swift +14 -0
  228. package/ios/Packages/VAD/Data/Configuration.swift +68 -0
  229. package/ios/Packages/VAD/Data/FrameSize.swift +39 -0
  230. package/ios/Packages/VAD/Data/Record.swift +13 -0
  231. package/ios/Packages/VAD/Data/Result.swift +22 -0
  232. package/ios/Packages/VAD/Data/SampleRate.swift +48 -0
  233. package/ios/Packages/VAD/Silero/Model/silero_vad.onnx +0 -0
  234. package/ios/Packages/VAD/Silero/SileroVAD.swift +284 -0
  235. package/ios/Packages/VAD/Silero/SileroVADStrategy.swift +64 -0
  236. package/ios/Packages/libbz2/module.modulemap +5 -0
  237. package/ios/Packages/libbz2/shim.h +1 -0
  238. package/ios/Source/Bridge.swift +353 -0
  239. package/ios/Source/Bridging-Header.h +16 -0
  240. package/ios/Source/DeviceManager.swift +1478 -0
  241. package/ios/Source/DeviceStore.swift +295 -0
  242. package/ios/Source/MentraBluetoothSDK.swift +2439 -0
  243. package/ios/Source/ObservableStore.swift +88 -0
  244. package/ios/Source/PrivacyInfo.xcprivacy +23 -0
  245. package/ios/Source/controllers/ControllerManager.swift +187 -0
  246. package/ios/Source/controllers/R1.swift +827 -0
  247. package/ios/Source/services/PhoneMic.swift +662 -0
  248. package/ios/Source/sgcs/Frame.swift +651 -0
  249. package/ios/Source/sgcs/G1.swift +2505 -0
  250. package/ios/Source/sgcs/G2.swift +3730 -0
  251. package/ios/Source/sgcs/Mach1.swift +543 -0
  252. package/ios/Source/sgcs/MentraLive.swift +4708 -0
  253. package/ios/Source/sgcs/MentraNex.swift +2479 -0
  254. package/ios/Source/sgcs/SGCManager.swift +243 -0
  255. package/ios/Source/sgcs/Simulated.swift +270 -0
  256. package/ios/Source/sgcs/mentraos_ble.pb.swift +3811 -0
  257. package/ios/Source/stt/STTTools.swift +154 -0
  258. package/ios/Source/stt/SherpaOnnxTranscriber.swift +401 -0
  259. package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
  260. package/ios/Source/utils/Constants.swift +74 -0
  261. package/ios/Source/utils/Enums.swift +95 -0
  262. package/ios/Source/utils/G1Text.swift +2067 -0
  263. package/ios/Source/utils/JSCExperiment.swift +241 -0
  264. package/ios/Source/utils/MemoryMonitor.swift +44 -0
  265. package/ios/Source/utils/MessageChunker.swift +164 -0
  266. package/ios/Source/utils/Models.swift +135 -0
  267. package/ios/Source/utils/PhoneAudioMonitor.swift +226 -0
  268. package/ios/Source/utils/TarBz2Extractor.swift +206 -0
  269. package/package.json +78 -0
  270. package/plugin/build/index.d.ts +6 -0
  271. package/plugin/build/index.js +12 -0
  272. package/plugin/build/withAndroid.d.ts +4 -0
  273. package/plugin/build/withAndroid.js +80 -0
  274. package/plugin/build/withIos.d.ts +4 -0
  275. package/plugin/build/withIos.js +64 -0
  276. package/src/BluetoothSdk.types.ts +581 -0
  277. package/src/BluetoothSdkModule.ts +259 -0
  278. package/src/index.ts +4 -0
@@ -0,0 +1,465 @@
1
+ package com.mentra.bluetoothsdk.stt
2
+
3
+ import android.content.Context
4
+ import android.content.SharedPreferences
5
+ import android.os.Handler
6
+ import android.os.Looper
7
+ import android.util.Log
8
+ import com.k2fsa.sherpa.onnx.*
9
+ import com.mentra.bluetoothsdk.Bridge
10
+ import java.io.File
11
+ import java.nio.ByteBuffer
12
+ import java.nio.ByteOrder
13
+ import java.util.concurrent.ArrayBlockingQueue
14
+ import java.util.concurrent.BlockingQueue
15
+ import java.util.concurrent.atomic.AtomicBoolean
16
+
17
+ /**
18
+ * SherpaOnnxTranscriber handles real-time audio transcription using Sherpa-ONNX.
19
+ *
20
+ * It works fully offline and processes PCM audio in real-time to provide partial and final ASR
21
+ * results. This class runs on a background thread, processes short PCM chunks, and emits
22
+ * transcribed text using a listener.
23
+ *
24
+ * Ported from iOS SherpaOnnxTranscriber.swift to match iOS functionality 1:1
25
+ */
26
+ class SherpaOnnxTranscriber(private val context: Context) {
27
+ companion object {
28
+ private const val TAG = "SherpaOnnxTranscriber"
29
+ private const val SAMPLE_RATE = 16000 // Sherpa-ONNX model's required sample rate
30
+ private const val QUEUE_CAPACITY = 100 // Max number of audio buffers to keep in queue
31
+ private const val PREFS_NAME = "MentraPrefs"
32
+ private const val KEY_STT_MODEL_PATH = "STTModelPath"
33
+ private const val KEY_STT_MODEL_LANGUAGE = "STTModelLanguageCode"
34
+ }
35
+
36
+ /** Interface to receive transcription results from Sherpa-ONNX */
37
+ interface TranscriptListener {
38
+ /** Called with live partial transcription (not final yet) */
39
+ fun onPartialResult(text: String, language: String)
40
+
41
+ /** Called when an utterance ends and final text is available */
42
+ fun onFinalResult(text: String, language: String)
43
+ }
44
+
45
+ private val pcmQueue: BlockingQueue<ByteArray> = ArrayBlockingQueue(QUEUE_CAPACITY)
46
+ private val running = AtomicBoolean(false)
47
+ private val mainHandler = Handler(Looper.getMainLooper())
48
+
49
+ private var workerThread: Thread? = null
50
+ private var recognizer: OnlineRecognizer? = null
51
+ private var stream: OnlineStream? = null
52
+
53
+ private var lastPartialResult = ""
54
+ @Volatile private var transcriptListener: TranscriptListener? = null
55
+
56
+ private val restartLock = Any()
57
+ private var restartRunning = false
58
+
59
+ private val prefs: SharedPreferences by lazy {
60
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
61
+ }
62
+
63
+ /**
64
+ * Get the custom model path from SharedPreferences Resolves relative paths from Documents
65
+ * directory
66
+ */
67
+ private fun getCustomModelPath(): String? {
68
+ val storedPath = prefs.getString(KEY_STT_MODEL_PATH, null) ?: return null
69
+
70
+ // Always resolve to absolute path if it contains "Documents/"
71
+ // This handles app ID changes between dev builds
72
+ if (storedPath.contains("/Documents/")) {
73
+ val documentsDir =
74
+ context.filesDir // In Android, this is the app's private files directory
75
+ val relativePath = storedPath.substringAfter("/Documents/")
76
+ val fixedPath = File(documentsDir, relativePath).absolutePath
77
+ Bridge.log("Reconstructed STTModelPath: $fixedPath")
78
+ return fixedPath
79
+ }
80
+
81
+ Bridge.log("STTModelPath (raw): $storedPath")
82
+ return storedPath
83
+ }
84
+
85
+ /** Get the model language code from SharedPreferences */
86
+ private fun getModelLanguage(): String {
87
+ return prefs.getString(KEY_STT_MODEL_LANGUAGE, "en-US") ?: "en-US"
88
+ }
89
+
90
+ /**
91
+ * Initialize the Sherpa-ONNX recognizer. Loads models and configuration, sets up processing
92
+ * thread.
93
+ */
94
+ fun initialize() {
95
+ try {
96
+ val modelPath = getCustomModelPath()
97
+
98
+ if (modelPath == null) {
99
+ Bridge.log("No Sherpa ONNX model available. Transcription will be disabled.")
100
+ Bridge.log("Please download a model using the model downloader in settings.")
101
+ recognizer = null
102
+ stream = null
103
+ return
104
+ }
105
+
106
+ val modelDir = File(modelPath)
107
+ if (!modelDir.exists() || !modelDir.isDirectory) {
108
+ Bridge.log("Model path does not exist or is not a directory: $modelPath")
109
+ recognizer = null
110
+ stream = null
111
+ return
112
+ }
113
+
114
+ val tokensPath = File(modelDir, "tokens.txt").absolutePath
115
+ val tokensFile = File(tokensPath)
116
+
117
+ if (!tokensFile.exists()) {
118
+ throw IllegalStateException("tokens.txt not found at path: $modelPath")
119
+ }
120
+
121
+ val modelType: String
122
+ val modelConfig = OnlineModelConfig()
123
+
124
+ // Detect model type based on available files
125
+ val ctcModelFile = File(modelDir, "model.int8.onnx")
126
+ val transducerEncoderFile = File(modelDir, "encoder.onnx")
127
+
128
+ when {
129
+ ctcModelFile.exists() -> {
130
+ // CTC model detected
131
+ modelType = "ctc"
132
+ Bridge.log("Detected CTC model at $modelPath")
133
+
134
+ val nemoCtc = OnlineNeMoCtcModelConfig()
135
+ nemoCtc.model = ctcModelFile.absolutePath
136
+
137
+ modelConfig.tokens = tokensPath
138
+ modelConfig.numThreads = 1
139
+ modelConfig.neMoCtc = nemoCtc
140
+ }
141
+ transducerEncoderFile.exists() -> {
142
+ // Transducer model detected
143
+ modelType = "transducer"
144
+ Bridge.log("Detected transducer model at $modelPath")
145
+
146
+ val decoderFile = File(modelDir, "decoder.onnx")
147
+ val joinerFile = File(modelDir, "joiner.onnx")
148
+
149
+ if (!decoderFile.exists() || !joinerFile.exists()) {
150
+ throw IllegalStateException(
151
+ "Transducer model files incomplete at path: $modelPath"
152
+ )
153
+ }
154
+
155
+ val transducer = OnlineTransducerModelConfig()
156
+ transducer.encoder = transducerEncoderFile.absolutePath
157
+ transducer.decoder = decoderFile.absolutePath
158
+ transducer.joiner = joinerFile.absolutePath
159
+
160
+ modelConfig.tokens = tokensPath
161
+ modelConfig.transducer = transducer
162
+ modelConfig.numThreads = 1
163
+ }
164
+ else -> {
165
+ throw IllegalStateException("No valid model files found at path: $modelPath")
166
+ }
167
+ }
168
+
169
+ // Configure recognizer
170
+ val config = OnlineRecognizerConfig()
171
+ config.modelConfig = modelConfig
172
+ config.decodingMethod = "greedy_search"
173
+ config.enableEndpoint = true
174
+
175
+ // Create recognizer (pass null for AssetManager since we're using file paths)
176
+ recognizer =
177
+ try {
178
+ OnlineRecognizer(null, config)
179
+ } catch (e: Exception) {
180
+ Log.e(
181
+ TAG,
182
+ "Failed to create OnlineRecognizer - model file may be corrupted or incomplete",
183
+ e
184
+ )
185
+ null
186
+ }
187
+
188
+ if (recognizer == null) {
189
+ throw IllegalStateException("Failed to create recognizer")
190
+ }
191
+
192
+ stream = recognizer?.createStream("")
193
+
194
+ startProcessingThread()
195
+ running.set(true)
196
+
197
+ Bridge.log("Sherpa-ONNX ASR initialized successfully with $modelType model")
198
+ } catch (e: Exception) {
199
+ Bridge.log("Failed to initialize Sherpa-ONNX: ${e.message}")
200
+ Log.e(TAG, "Failed to initialize Sherpa-ONNX", e)
201
+
202
+ // Clean up any partially initialized resources
203
+ stream?.let {
204
+ try {
205
+ it.release()
206
+ } catch (releaseEx: Exception) {
207
+ Log.e(TAG, "Error releasing stream after initialization failure", releaseEx)
208
+ }
209
+ }
210
+ stream = null
211
+
212
+ recognizer?.let {
213
+ try {
214
+ it.release()
215
+ } catch (releaseEx: Exception) {
216
+ Log.e(TAG, "Error releasing recognizer after initialization failure", releaseEx)
217
+ }
218
+ }
219
+ recognizer = null
220
+
221
+ running.set(false)
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Feed PCM audio data (16-bit little endian) into the transcriber. This method should be called
227
+ * continuously with short chunks (e.g., 100-300ms).
228
+ *
229
+ * Note: Audio passed to this method is assumed to have already passed VAD elsewhere, so it's
230
+ * directly queued for processing without additional VAD checks.
231
+ */
232
+ fun acceptAudio(pcm16le: ByteArray) {
233
+ if (!running.get()) {
234
+ // Bridge.log("⚠️ Ignoring audio - transcriber not running")
235
+ return
236
+ }
237
+
238
+ // Directly queue the audio data for processing
239
+ if (!pcmQueue.offer(pcm16le.copyOf())) {
240
+ // Queue is full, drop oldest and try again
241
+ pcmQueue.poll()
242
+ pcmQueue.offer(pcm16le.copyOf())
243
+ Bridge.log("⚠️ Audio queue overflow - dropped buffer")
244
+ }
245
+ }
246
+
247
+ /** Start a background thread to continuously consume audio and decode using Sherpa */
248
+ private fun startProcessingThread() {
249
+ Bridge.log("🚀 Starting Sherpa-ONNX processing thread...")
250
+
251
+ workerThread =
252
+ Thread({ runLoop() }, "SherpaOnnxProcessor").apply {
253
+ isDaemon = true
254
+ start()
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Main processing loop that handles transcription in real-time. Pulls audio from queue, feeds
260
+ * into Sherpa, emits partial/final results.
261
+ */
262
+ private fun runLoop() {
263
+ Bridge.log("🔄 Sherpa-ONNX processing loop started")
264
+
265
+ while (running.get()) {
266
+ try {
267
+ val currentRecognizer = recognizer
268
+ val currentStream = stream
269
+
270
+ if (currentRecognizer == null || currentStream == null) {
271
+ Bridge.log("⚠️ Recognizer or stream not available, skipping audio chunk")
272
+ Thread.sleep(100)
273
+ continue
274
+ }
275
+
276
+ // Pull data from queue with timeout
277
+ val audioData = pcmQueue.poll(50, java.util.concurrent.TimeUnit.MILLISECONDS)
278
+
279
+ if (audioData != null) {
280
+ // Convert PCM to float [-1.0, 1.0]
281
+ val floatBuf = toFloatArray(audioData)
282
+
283
+ // Pass audio data to the Sherpa-ONNX stream
284
+ currentStream.acceptWaveform(floatBuf, SAMPLE_RATE)
285
+
286
+ // Decode continuously while model is ready
287
+ var decodeCount = 0
288
+ while (currentRecognizer.isReady(currentStream)) {
289
+ currentRecognizer.decode(currentStream)
290
+ decodeCount++
291
+ }
292
+
293
+ // If utterance endpoint detected
294
+ if (currentRecognizer.isEndpoint(currentStream)) {
295
+ val result = currentRecognizer.getResult(currentStream)
296
+ val finalText = result.text.trim()
297
+
298
+ if (finalText.isNotEmpty()) {
299
+ handleTranscriptionResult(finalText, isFinal = true)
300
+ }
301
+
302
+ currentRecognizer.reset(currentStream) // Start new utterance
303
+ lastPartialResult = ""
304
+ } else {
305
+ // Emit partial results if changed
306
+ val result = currentRecognizer.getResult(currentStream)
307
+ val partial = result.text.trim()
308
+
309
+ if (partial != lastPartialResult && partial.isNotEmpty()) {
310
+ handleTranscriptionResult(partial, isFinal = false)
311
+ lastPartialResult = partial
312
+ }
313
+ }
314
+ }
315
+ } catch (e: InterruptedException) {
316
+ Thread.currentThread().interrupt()
317
+ Bridge.log("Processing thread interrupted")
318
+ break
319
+ } catch (e: Exception) {
320
+ Log.e(TAG, "Error processing audio", e)
321
+ // Attempt stream reset to recover
322
+ try {
323
+ recognizer?.let { rec -> stream?.let { str -> rec.reset(str) } }
324
+ } catch (resetEx: Exception) {
325
+ Log.e(TAG, "Failed to reset stream after error", resetEx)
326
+ }
327
+ }
328
+ }
329
+
330
+ Bridge.log("ASR processing thread stopped")
331
+ }
332
+
333
+ /** Convert 16-bit PCM byte data (little-endian) to float array [-1.0, 1.0] */
334
+ private fun toFloatArray(pcmData: ByteArray): FloatArray {
335
+ val count = pcmData.size / 2
336
+ val samples = FloatArray(count)
337
+
338
+ val buffer = ByteBuffer.wrap(pcmData).order(ByteOrder.LITTLE_ENDIAN)
339
+
340
+ for (i in 0 until count) {
341
+ samples[i] = buffer.short / 32768.0f
342
+ }
343
+
344
+ return samples
345
+ }
346
+
347
+ /** Handle transcription results - send to listener on main thread */
348
+ private fun handleTranscriptionResult(text: String, isFinal: Boolean) {
349
+ val language = getModelLanguage()
350
+
351
+ // Forward to listener on main thread
352
+ mainHandler.post {
353
+ transcriptListener?.let { listener ->
354
+ if (isFinal) {
355
+ listener.onFinalResult(text, language)
356
+ } else {
357
+ listener.onPartialResult(text, language)
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Stop transcription processing. This shuts down the processing thread and releases Sherpa-ONNX
365
+ * resources.
366
+ */
367
+ fun shutdown() {
368
+ Bridge.log("🛑 Shutting down SherpaOnnxTranscriber...")
369
+
370
+ running.set(false)
371
+
372
+ workerThread?.let { thread ->
373
+ thread.interrupt()
374
+ try {
375
+ thread.join(500)
376
+ } catch (e: InterruptedException) {
377
+ Thread.currentThread().interrupt()
378
+ }
379
+ }
380
+ workerThread = null
381
+
382
+ try {
383
+ stream?.let {
384
+ Bridge.log("🧹 Cleaning up Sherpa-ONNX stream")
385
+ it.release()
386
+ }
387
+ stream = null
388
+
389
+ recognizer?.let {
390
+ Bridge.log("🧹 Cleaning up Sherpa-ONNX recognizer")
391
+ it.release()
392
+ }
393
+ recognizer = null
394
+ } catch (e: Exception) {
395
+ Log.e(TAG, "Error releasing Sherpa resources", e)
396
+ }
397
+
398
+ // Clear any remaining audio buffers
399
+ val remainingBuffers = pcmQueue.size
400
+ if (remainingBuffers > 0) {
401
+ Bridge.log("🗑️ Clearing $remainingBuffers remaining audio buffers")
402
+ }
403
+ pcmQueue.clear()
404
+
405
+ Bridge.log("✅ SherpaOnnxTranscriber shutdown complete")
406
+ }
407
+
408
+ /**
409
+ * Restarts the transcriber after a model change. Shuts down existing resources, clears buffers,
410
+ * and reinitializes the recognizer.
411
+ */
412
+ fun restart() {
413
+ synchronized(restartLock) {
414
+ if (restartRunning) {
415
+ Bridge.log("Restart already in progress, skipping")
416
+ return
417
+ }
418
+ restartRunning = true
419
+ }
420
+
421
+ try {
422
+ Bridge.log("♻️ Restarting SherpaOnnxTranscriber...")
423
+ shutdown()
424
+ // Small delay to ensure cleanup completes
425
+ Thread.sleep(100)
426
+ initialize()
427
+ } catch (e: Exception) {
428
+ Log.e(TAG, "Error during transcriber restart", e)
429
+ } finally {
430
+ synchronized(restartLock) { restartRunning = false }
431
+ }
432
+ }
433
+
434
+ /** Register a listener to receive partial and final transcription updates */
435
+ fun setTranscriptListener(listener: TranscriptListener?) {
436
+ transcriptListener = listener
437
+ }
438
+
439
+ /** Check if the transcriber was successfully initialized */
440
+ fun isInitialized(): Boolean {
441
+ return recognizer != null && stream != null
442
+ }
443
+
444
+ /** Handle microphone state changes */
445
+ fun microphoneStateChanged(state: Boolean) {
446
+ if (!state) {
447
+ // Microphone turned off - clear queue and reset stream
448
+ pcmQueue.clear()
449
+
450
+ recognizer?.let { rec ->
451
+ stream?.let { str ->
452
+ try {
453
+ rec.reset(str)
454
+ lastPartialResult = ""
455
+ Log.d(TAG, "Microphone off — stream reset")
456
+ } catch (e: Exception) {
457
+ Log.e(TAG, "Error resetting stream on mic off", e)
458
+ }
459
+ }
460
+ }
461
+ } else {
462
+ Log.d(TAG, "Microphone on")
463
+ }
464
+ }
465
+ }
@@ -0,0 +1,221 @@
1
+ package com.mentra.bluetoothsdk.stt
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import com.konovalov.vad.silero.Vad
6
+ import com.konovalov.vad.silero.VadSilero
7
+ import com.konovalov.vad.silero.config.FrameSize
8
+ import com.konovalov.vad.silero.config.Mode
9
+ import com.konovalov.vad.silero.config.SampleRate
10
+ import java.nio.ByteBuffer
11
+ import java.nio.ByteOrder
12
+
13
+ /**
14
+ * VadGateSpeechPolicy handles Voice Activity Detection using Silero VAD.
15
+ *
16
+ * Features:
17
+ * - Dynamic VAD check throttling (adjusts frequency based on silence duration)
18
+ * - 8-second silence timeout before declaring "not speaking"
19
+ * - Bypass modes for debugging and PCM streaming
20
+ * - 512-sample frame size (matches Sherpa-ONNX requirements)
21
+ *
22
+ * Ported from android_core VadGateSpeechPolicy.java
23
+ */
24
+ class VadGateSpeechPolicy(private val context: Context) {
25
+ companion object {
26
+ private const val TAG = "VadGateSpeechPolicy"
27
+
28
+ // Total required silence duration
29
+ private const val REQUIRED_SILENCE_DURATION_MS = 8000L
30
+
31
+ // Dynamic VAD check intervals
32
+ private const val INITIAL_SILENCE_VAD_INTERVAL_MS = 50L // Check frequently at first
33
+ private const val MEDIUM_SILENCE_VAD_INTERVAL_MS = 100L // Medium frequency after some time
34
+ private const val LONG_SILENCE_VAD_INTERVAL_MS =
35
+ 200L // Less frequent after extended silence
36
+
37
+ // Thresholds for switching intervals
38
+ private const val MEDIUM_SILENCE_THRESHOLD_MS =
39
+ 20000L // Switch to medium interval after 20s
40
+ private const val LONG_SILENCE_THRESHOLD_MS = 60000L // Switch to long interval after 60s
41
+
42
+ private const val FRAME_SIZE = 512
43
+ }
44
+
45
+ private var vad: VadSilero? = null
46
+ private var isCurrentlySpeech = false
47
+ private var bypassVadForDebugging = false
48
+ private var bypassVadForPCM = false
49
+
50
+ // Timestamp of the last detected speech
51
+ private var lastSpeechDetectedTime = 0L
52
+ // Throttle timer for silence VAD checks
53
+ private var lastVadCheckTime = 0L
54
+
55
+ /** Initialize the Silero VAD model */
56
+ fun init(blockSizeSamples: Int) {
57
+ startVad()
58
+ }
59
+
60
+ private fun startVad() {
61
+ try {
62
+ // Set internal silence duration very low; external logic manages the full required
63
+ // silence duration
64
+ vad =
65
+ Vad.builder()
66
+ .setContext(context)
67
+ .setSampleRate(SampleRate.SAMPLE_RATE_16K)
68
+ .setFrameSize(FrameSize.FRAME_SIZE_512)
69
+ .setMode(Mode.NORMAL)
70
+ .setSilenceDurationMs(50)
71
+ .setSpeechDurationMs(50)
72
+ .build()
73
+
74
+ Log.d(TAG, "VAD initialized successfully")
75
+ } catch (e: Exception) {
76
+ Log.e(TAG, "Failed to initialize VAD", e)
77
+ vad = null
78
+ }
79
+ }
80
+
81
+ /** Check if audio should be passed to the recognizer based on VAD state */
82
+ fun shouldPassAudioToRecognizer(): Boolean {
83
+ // CRITICAL: Handle VAD null case
84
+ if (vad == null) {
85
+ return bypassVadForDebugging || bypassVadForPCM || true
86
+ }
87
+
88
+ return bypassVadForDebugging || bypassVadForPCM || isCurrentlySpeech
89
+ }
90
+
91
+ /** Process audio bytes through VAD to detect speech */
92
+ fun processAudioBytes(bytes: ByteArray, offset: Int, length: Int) {
93
+ val now = System.currentTimeMillis()
94
+
95
+ // If in speech state and it hasn't been 8 seconds since the last speech was detected, skip
96
+ // processing
97
+ if (isCurrentlySpeech && (now - lastSpeechDetectedTime < REQUIRED_SILENCE_DURATION_MS)) {
98
+ return
99
+ }
100
+
101
+ // Calculate silence duration
102
+ val silenceDuration = now - lastSpeechDetectedTime
103
+
104
+ // Determine which VAD interval to use based on silence duration
105
+ val currentVadInterval =
106
+ when {
107
+ silenceDuration < MEDIUM_SILENCE_THRESHOLD_MS -> INITIAL_SILENCE_VAD_INTERVAL_MS
108
+ silenceDuration < LONG_SILENCE_THRESHOLD_MS -> MEDIUM_SILENCE_VAD_INTERVAL_MS
109
+ else -> LONG_SILENCE_VAD_INTERVAL_MS
110
+ }
111
+
112
+ // During silence, throttle VAD checks based on the dynamic interval
113
+ if (!isCurrentlySpeech && (now - lastVadCheckTime < currentVadInterval)) {
114
+ return
115
+ }
116
+ lastVadCheckTime = now
117
+
118
+ val audioBytesFull = bytesToShort(bytes)
119
+ val totalSamples = audioBytesFull.size
120
+
121
+ if (totalSamples % FRAME_SIZE != 0) {
122
+ Log.e(
123
+ TAG,
124
+ "Invalid audio frame size: $totalSamples samples. Needs to be multiple of $FRAME_SIZE."
125
+ )
126
+ return
127
+ }
128
+
129
+ val currentVad =
130
+ vad
131
+ ?: run {
132
+ Log.w(TAG, "VAD not initialized, skipping audio processing")
133
+ return
134
+ }
135
+
136
+ var previousSpeechState = isCurrentlySpeech
137
+
138
+ // Process each 512-sample frame
139
+ val numFrames = totalSamples / FRAME_SIZE
140
+ for (i in 0 until numFrames) {
141
+ val currentTime = System.currentTimeMillis()
142
+ val startIdx = i * FRAME_SIZE
143
+ val audioFrame = audioBytesFull.copyOfRange(startIdx, startIdx + FRAME_SIZE)
144
+ val detectedSpeech = currentVad.isSpeech(audioFrame)
145
+
146
+ if (detectedSpeech) {
147
+ isCurrentlySpeech = true
148
+ // Update the last speech detection timestamp
149
+ lastSpeechDetectedTime = currentTime
150
+ } else {
151
+ // If no speech detected, and 8 seconds have elapsed since the last speech, mark as
152
+ // silence
153
+ if (currentTime - lastSpeechDetectedTime >= REQUIRED_SILENCE_DURATION_MS) {
154
+ isCurrentlySpeech = false
155
+ }
156
+ }
157
+
158
+ if (isCurrentlySpeech != previousSpeechState) {
159
+ Log.d(
160
+ TAG,
161
+ "Speech detection changed to: ${if (isCurrentlySpeech) "SPEECH" else "SILENCE"}"
162
+ )
163
+ previousSpeechState = isCurrentlySpeech
164
+ }
165
+ }
166
+ }
167
+
168
+ /** Convert byte array (PCM16LE) to short array */
169
+ private fun bytesToShort(bytes: ByteArray): ShortArray {
170
+ val shorts = ShortArray(bytes.size / 2)
171
+ ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts)
172
+ return shorts
173
+ }
174
+
175
+ /** Stop VAD and release resources */
176
+ fun stop() {
177
+ try {
178
+ vad?.close()
179
+ vad = null
180
+ Log.d(TAG, "VAD stopped and resources released")
181
+ } catch (e: Exception) {
182
+ Log.e(TAG, "Error stopping VAD", e)
183
+ }
184
+ }
185
+
186
+ /** Reset VAD state */
187
+ fun reset() {
188
+ isCurrentlySpeech = false
189
+ lastSpeechDetectedTime = 0
190
+ lastVadCheckTime = 0
191
+ }
192
+
193
+ /** Change bypass VAD for debugging state */
194
+ fun changeBypassVadForDebugging(bypass: Boolean) {
195
+ bypassVadForDebugging = bypass
196
+ Log.d(TAG, "Bypass VAD for debugging: $bypass")
197
+ }
198
+
199
+ /** Change bypass VAD for PCM streaming state */
200
+ fun changeBypassVadForPCM(bypass: Boolean) {
201
+ Log.d(TAG, "VAD PCM Bypass State Change: $bypassVadForPCM -> $bypass")
202
+ bypassVadForPCM = bypass
203
+ }
204
+
205
+ /** Handle microphone state changes */
206
+ fun microphoneStateChanged(state: Boolean) {
207
+ if (!state) {
208
+ // Microphone turned off: force immediate silence
209
+ isCurrentlySpeech = false
210
+ lastSpeechDetectedTime = 0
211
+ lastVadCheckTime = 0
212
+
213
+ // Optionally flush the VAD's internal state by processing a silent frame
214
+ vad?.let { vadInstance ->
215
+ val silentFrame = ShortArray(FRAME_SIZE)
216
+ vadInstance.isSpeech(silentFrame)
217
+ }
218
+ Log.d(TAG, "Microphone turned off; forced state to SILENCE.")
219
+ }
220
+ }
221
+ }