@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,2383 @@
1
+ // swift-api-examples/SherpaOnnx.swift
2
+ // Copyright (c) 2023 Xiaomi Corporation
3
+
4
+ import Foundation // For NSString
5
+
6
+ // ⚠️ LOCAL DIVERGENCE FROM UPSTREAM (sherpa-onnx swift-api-examples) ⚠️
7
+ //
8
+ // Do NOT overwrite this block when syncing this file with upstream (see commit
9
+ // 973a4ad180 "Sync SherpaOnnx.swift wrapper with upstream v1.13.2"). Re-applying
10
+ // upstream's `toCPointer` reintroduces a use-after-free that crashes on launch.
11
+ //
12
+ // Upstream implements this as:
13
+ //
14
+ // let cs = (s as NSString).utf8String
15
+ // return UnsafePointer<Int8>(cs)
16
+ //
17
+ // `NSString.utf8String` returns a pointer with "a lifetime shorter than the
18
+ // string" — it is valid only until the surrounding autorelease pool drains. The
19
+ // config builders below (`sherpaOnnxOnlineModelConfig` et al.) bridge a Swift
20
+ // `String` to a temporary `NSString` that is autoreleased the moment the builder
21
+ // returns, so every `const char*` in the resulting config struct dangles before
22
+ // `SherpaOnnxCreate*` ever dereferences it. That surfaced as an EXC_BAD_ACCESS in
23
+ // `SherpaOnnxCreateOnlineRecognizer` (Sentry REACT-NATIVE-G).
24
+ //
25
+ // Instead we hand C a `strdup` copy that we own outright. sherpa-onnx copies
26
+ // every string into a C++ `std::string` during construction, so the copies only
27
+ // need to outlive the `SherpaOnnxCreate*` call. Ownership is tracked by
28
+ // `SherpaOnnxCStringScope` below; call sites that build a config and immediately
29
+ // construct an object should wrap both in `withSherpaOnnxCStrings { ... }` so the
30
+ // copies are freed once construction returns.
31
+ //
32
+ // Copies made outside any scope are intentionally leaked rather than freed early:
33
+ // a small one-time leak is strictly preferable to handing C a freed pointer.
34
+
35
+ /// Tracks `strdup` allocations made by `toCPointer` so they can be freed once the
36
+ /// C object that copied them has been constructed.
37
+ private enum SherpaOnnxCStringScope {
38
+ /// Allocation stack, keyed by thread so concurrent model loads cannot free
39
+ /// each other's strings. Nested scopes push additional frames.
40
+ private static let threadLocalKey = "com.glassly.sherpaonnx.cstrings"
41
+
42
+ private static var stack: [[UnsafeMutablePointer<Int8>]] {
43
+ get { Thread.current.threadDictionary[threadLocalKey] as? [[UnsafeMutablePointer<Int8>]] ?? [] }
44
+ set { Thread.current.threadDictionary[threadLocalKey] = newValue }
45
+ }
46
+
47
+ static func track(_ pointer: UnsafeMutablePointer<Int8>) {
48
+ var frames = stack
49
+ guard !frames.isEmpty else { return } // no active scope: intentionally leak
50
+ frames[frames.count - 1].append(pointer)
51
+ stack = frames
52
+ }
53
+
54
+ static func withScope<T>(_ body: () throws -> T) rethrows -> T {
55
+ stack.append([])
56
+ defer {
57
+ var frames = stack
58
+ let allocations = frames.popLast() ?? []
59
+ stack = frames
60
+ for pointer in allocations {
61
+ free(pointer)
62
+ }
63
+ }
64
+ return try body()
65
+ }
66
+ }
67
+
68
+ /// Run `body` with a scope that owns every C string produced by `toCPointer`
69
+ /// inside it, freeing them all when `body` returns.
70
+ ///
71
+ /// Wrap config construction *and* the matching `SherpaOnnxCreate*` call together:
72
+ ///
73
+ /// let recognizer = withSherpaOnnxCStrings {
74
+ /// var config = sherpaOnnxOnlineRecognizerConfig(...)
75
+ /// return SherpaOnnxRecognizer(config: &config)
76
+ /// }
77
+ ///
78
+ /// Do not let a config struct escape the scope — its pointers die with it.
79
+ func withSherpaOnnxCStrings<T>(_ body: () throws -> T) rethrows -> T {
80
+ return try SherpaOnnxCStringScope.withScope(body)
81
+ }
82
+
83
+ // Convert a String from swift to a `const char*` so that we can pass it to
84
+ // the C language.
85
+ //
86
+ // The returned pointer is owned by the innermost enclosing
87
+ // `withSherpaOnnxCStrings` scope and is freed when that scope exits. Outside any
88
+ // scope the allocation is leaked, which keeps the pointer valid indefinitely.
89
+ //
90
+ // - Parameters:
91
+ // - s: The String to convert.
92
+ // - Returns: A pointer that can be passed to C as `const char*`
93
+
94
+ func toCPointer(_ s: String) -> UnsafePointer<Int8>! {
95
+ guard let copy = strdup(s) else { return nil }
96
+ SherpaOnnxCStringScope.track(copy)
97
+ return UnsafePointer<Int8>(copy)
98
+ }
99
+
100
+ /// Return an instance of SherpaOnnxOnlineTransducerModelConfig.
101
+ ///
102
+ /// Please refer to
103
+ /// https://k2-fsa.github.io/sherpa/onnx/pretrained_models/online-transducer/index.html
104
+ /// to download the required `.onnx` files.
105
+ ///
106
+ /// - Parameters:
107
+ /// - encoder: Path to encoder.onnx
108
+ /// - decoder: Path to decoder.onnx
109
+ /// - joiner: Path to joiner.onnx
110
+ ///
111
+ /// - Returns: Return an instance of SherpaOnnxOnlineTransducerModelConfig
112
+ func sherpaOnnxOnlineTransducerModelConfig(
113
+ encoder: String = "",
114
+ decoder: String = "",
115
+ joiner: String = ""
116
+ ) -> SherpaOnnxOnlineTransducerModelConfig {
117
+ return SherpaOnnxOnlineTransducerModelConfig(
118
+ encoder: toCPointer(encoder),
119
+ decoder: toCPointer(decoder),
120
+ joiner: toCPointer(joiner)
121
+ )
122
+ }
123
+
124
+ /// Return an instance of SherpaOnnxOnlineParaformerModelConfig.
125
+ ///
126
+ /// Please refer to
127
+ /// https://k2-fsa.github.io/sherpa/onnx/pretrained_models/online-paraformer/index.html
128
+ /// to download the required `.onnx` files.
129
+ ///
130
+ /// - Parameters:
131
+ /// - encoder: Path to encoder.onnx
132
+ /// - decoder: Path to decoder.onnx
133
+ ///
134
+ /// - Returns: Return an instance of SherpaOnnxOnlineParaformerModelConfig
135
+ func sherpaOnnxOnlineParaformerModelConfig(
136
+ encoder: String = "",
137
+ decoder: String = ""
138
+ ) -> SherpaOnnxOnlineParaformerModelConfig {
139
+ return SherpaOnnxOnlineParaformerModelConfig(
140
+ encoder: toCPointer(encoder),
141
+ decoder: toCPointer(decoder)
142
+ )
143
+ }
144
+
145
+ func sherpaOnnxOnlineZipformer2CtcModelConfig(
146
+ model: String = ""
147
+ ) -> SherpaOnnxOnlineZipformer2CtcModelConfig {
148
+ return SherpaOnnxOnlineZipformer2CtcModelConfig(
149
+ model: toCPointer(model)
150
+ )
151
+ }
152
+
153
+ func sherpaOnnxOnlineNemoCtcModelConfig(
154
+ model: String = ""
155
+ ) -> SherpaOnnxOnlineNemoCtcModelConfig {
156
+ return SherpaOnnxOnlineNemoCtcModelConfig(
157
+ model: toCPointer(model)
158
+ )
159
+ }
160
+
161
+ func sherpaOnnxOnlineToneCtcModelConfig(
162
+ model: String = ""
163
+ ) -> SherpaOnnxOnlineToneCtcModelConfig {
164
+ return SherpaOnnxOnlineToneCtcModelConfig(
165
+ model: toCPointer(model)
166
+ )
167
+ }
168
+
169
+ /// Return an instance of SherpaOnnxOnlineModelConfig.
170
+ ///
171
+ /// Please refer to
172
+ /// https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html
173
+ /// to download the required `.onnx` files.
174
+ ///
175
+ /// - Parameters:
176
+ /// - tokens: Path to tokens.txt
177
+ /// - numThreads: Number of threads to use for neural network computation.
178
+ ///
179
+ /// - Returns: Return an instance of SherpaOnnxOnlineTransducerModelConfig
180
+ func sherpaOnnxOnlineModelConfig(
181
+ tokens: String,
182
+ transducer: SherpaOnnxOnlineTransducerModelConfig = sherpaOnnxOnlineTransducerModelConfig(),
183
+ paraformer: SherpaOnnxOnlineParaformerModelConfig = sherpaOnnxOnlineParaformerModelConfig(),
184
+ zipformer2Ctc: SherpaOnnxOnlineZipformer2CtcModelConfig =
185
+ sherpaOnnxOnlineZipformer2CtcModelConfig(),
186
+ numThreads: Int = 1,
187
+ provider: String = "cpu",
188
+ debug: Int = 0,
189
+ modelType: String = "",
190
+ modelingUnit: String = "cjkchar",
191
+ bpeVocab: String = "",
192
+ tokensBuf: String = "",
193
+ tokensBufSize: Int = 0,
194
+ nemoCtc: SherpaOnnxOnlineNemoCtcModelConfig = sherpaOnnxOnlineNemoCtcModelConfig(),
195
+ toneCtc: SherpaOnnxOnlineToneCtcModelConfig = sherpaOnnxOnlineToneCtcModelConfig()
196
+ ) -> SherpaOnnxOnlineModelConfig {
197
+ return SherpaOnnxOnlineModelConfig(
198
+ transducer: transducer,
199
+ paraformer: paraformer,
200
+ zipformer2_ctc: zipformer2Ctc,
201
+ tokens: toCPointer(tokens),
202
+ num_threads: Int32(numThreads),
203
+ provider: toCPointer(provider),
204
+ debug: Int32(debug),
205
+ model_type: toCPointer(modelType),
206
+ modeling_unit: toCPointer(modelingUnit),
207
+ bpe_vocab: toCPointer(bpeVocab),
208
+ tokens_buf: toCPointer(tokensBuf),
209
+ tokens_buf_size: Int32(tokensBufSize),
210
+ nemo_ctc: nemoCtc,
211
+ t_one_ctc: toneCtc
212
+ )
213
+ }
214
+
215
+ func sherpaOnnxFeatureConfig(
216
+ sampleRate: Int = 16000,
217
+ featureDim: Int = 80
218
+ ) -> SherpaOnnxFeatureConfig {
219
+ return SherpaOnnxFeatureConfig(
220
+ sample_rate: Int32(sampleRate),
221
+ feature_dim: Int32(featureDim)
222
+ )
223
+ }
224
+
225
+ func sherpaOnnxOnlineCtcFstDecoderConfig(
226
+ graph: String = "",
227
+ maxActive: Int = 3000
228
+ ) -> SherpaOnnxOnlineCtcFstDecoderConfig {
229
+ return SherpaOnnxOnlineCtcFstDecoderConfig(
230
+ graph: toCPointer(graph),
231
+ max_active: Int32(maxActive)
232
+ )
233
+ }
234
+
235
+ func sherpaOnnxHomophoneReplacerConfig(
236
+ dictDir: String = "",
237
+ lexicon: String = "",
238
+ ruleFsts: String = ""
239
+ ) -> SherpaOnnxHomophoneReplacerConfig {
240
+ return SherpaOnnxHomophoneReplacerConfig(
241
+ dict_dir: toCPointer(dictDir),
242
+ lexicon: toCPointer(lexicon),
243
+ rule_fsts: toCPointer(ruleFsts)
244
+ )
245
+ }
246
+
247
+ func sherpaOnnxOnlineRecognizerConfig(
248
+ featConfig: SherpaOnnxFeatureConfig,
249
+ modelConfig: SherpaOnnxOnlineModelConfig,
250
+ enableEndpoint: Bool = false,
251
+ rule1MinTrailingSilence: Float = 2.4,
252
+ rule2MinTrailingSilence: Float = 1.2,
253
+ rule3MinUtteranceLength: Float = 30,
254
+ decodingMethod: String = "greedy_search",
255
+ maxActivePaths: Int = 4,
256
+ hotwordsFile: String = "",
257
+ hotwordsScore: Float = 1.5,
258
+ ctcFstDecoderConfig: SherpaOnnxOnlineCtcFstDecoderConfig = sherpaOnnxOnlineCtcFstDecoderConfig(),
259
+ ruleFsts: String = "",
260
+ ruleFars: String = "",
261
+ blankPenalty: Float = 0.0,
262
+ hotwordsBuf: String = "",
263
+ hotwordsBufSize: Int = 0,
264
+ hr: SherpaOnnxHomophoneReplacerConfig = sherpaOnnxHomophoneReplacerConfig()
265
+ ) -> SherpaOnnxOnlineRecognizerConfig {
266
+ return SherpaOnnxOnlineRecognizerConfig(
267
+ feat_config: featConfig,
268
+ model_config: modelConfig,
269
+ decoding_method: toCPointer(decodingMethod),
270
+ max_active_paths: Int32(maxActivePaths),
271
+ enable_endpoint: enableEndpoint ? 1 : 0,
272
+ rule1_min_trailing_silence: rule1MinTrailingSilence,
273
+ rule2_min_trailing_silence: rule2MinTrailingSilence,
274
+ rule3_min_utterance_length: rule3MinUtteranceLength,
275
+ hotwords_file: toCPointer(hotwordsFile),
276
+ hotwords_score: hotwordsScore,
277
+ ctc_fst_decoder_config: ctcFstDecoderConfig,
278
+ rule_fsts: toCPointer(ruleFsts),
279
+ rule_fars: toCPointer(ruleFars),
280
+ blank_penalty: blankPenalty,
281
+ hotwords_buf: toCPointer(hotwordsBuf),
282
+ hotwords_buf_size: Int32(hotwordsBufSize),
283
+ hr: hr
284
+ )
285
+ }
286
+
287
+ /// Wrapper for recognition result.
288
+ ///
289
+ /// Usage:
290
+ ///
291
+ /// let result = recognizer.getResult()
292
+ /// print("text: \(result.text)")
293
+ ///
294
+ class SherpaOnnxOnlineRecongitionResult {
295
+ /// A pointer to the underlying counterpart in C
296
+ private let result: UnsafePointer<SherpaOnnxOnlineRecognizerResult>
297
+
298
+ private lazy var _text: String = {
299
+ guard let cstr = result.pointee.text else { return "" }
300
+ return String(cString: cstr)
301
+ }()
302
+
303
+ private lazy var _tokens: [String] = {
304
+ guard let tokensPointer = result.pointee.tokens_arr else { return [] }
305
+ return (0 ..< count).compactMap { index in
306
+ guard let ptr = tokensPointer[index] else { return nil }
307
+ return String(cString: ptr)
308
+ }
309
+ }()
310
+
311
+ private lazy var _timestamps: [Float] = {
312
+ guard let timestampsPointer = result.pointee.timestamps else { return [] }
313
+ return (0 ..< count).map { index in timestampsPointer[index] }
314
+ }()
315
+
316
+ init(result: UnsafePointer<SherpaOnnxOnlineRecognizerResult>) {
317
+ self.result = result
318
+ }
319
+
320
+ deinit {
321
+ SherpaOnnxDestroyOnlineRecognizerResult(result)
322
+ }
323
+
324
+ /// Return the actual recognition result.
325
+ /// For English models, it contains words separated by spaces.
326
+ /// For Chinese models, it contains Chinese words.
327
+ var text: String {
328
+ _text
329
+ }
330
+
331
+ var count: Int {
332
+ Int(result.pointee.count)
333
+ }
334
+
335
+ var tokens: [String] {
336
+ _tokens
337
+ }
338
+
339
+ var timestamps: [Float] {
340
+ _timestamps
341
+ }
342
+ }
343
+
344
+ class SherpaOnnxRecognizer {
345
+ /// A pointer to the underlying counterpart in C
346
+ private let recognizer: OpaquePointer
347
+ private var stream: OpaquePointer
348
+ private let lock = NSLock() // for thread-safe stream replacement
349
+
350
+ /// Constructor taking a model config
351
+ init(
352
+ config: UnsafePointer<SherpaOnnxOnlineRecognizerConfig>
353
+ ) {
354
+ recognizer = SherpaOnnxCreateOnlineRecognizer(config)
355
+ stream = SherpaOnnxCreateOnlineStream(recognizer)
356
+ }
357
+
358
+ deinit {
359
+ SherpaOnnxDestroyOnlineStream(stream)
360
+ SherpaOnnxDestroyOnlineRecognizer(recognizer)
361
+ }
362
+
363
+ /// Decode wave samples.
364
+ ///
365
+ /// - Parameters:
366
+ /// - samples: Audio samples normalized to the range [-1, 1]
367
+ /// - sampleRate: Sample rate of the input audio samples. Must match
368
+ /// the one expected by the model.
369
+ func acceptWaveform(samples: [Float], sampleRate: Int = 16000) {
370
+ SherpaOnnxOnlineStreamAcceptWaveform(stream, Int32(sampleRate), samples, Int32(samples.count))
371
+ }
372
+
373
+ func isReady() -> Bool {
374
+ return SherpaOnnxIsOnlineStreamReady(recognizer, stream) != 0
375
+ }
376
+
377
+ /// If there are enough number of feature frames, it invokes the neural
378
+ /// network computation and decoding. Otherwise, it is a no-op.
379
+ func decode() {
380
+ SherpaOnnxDecodeOnlineStream(recognizer, stream)
381
+ }
382
+
383
+ /// Get the decoding results so far
384
+ func getResult() -> SherpaOnnxOnlineRecongitionResult {
385
+ guard let result = SherpaOnnxGetOnlineStreamResult(recognizer, stream) else {
386
+ fatalError("SherpaOnnxGetOnlineStreamResult returned nil")
387
+ }
388
+ return SherpaOnnxOnlineRecongitionResult(result: result)
389
+ }
390
+
391
+ /// Reset the recognizer, which clears the neural network model state
392
+ /// and the state for decoding.
393
+ /// If hotwords is an empty string, it just recreates the decoding stream
394
+ /// If hotwords is not empty, it will create a new decoding stream with
395
+ /// the given hotWords appended to the default hotwords.
396
+ func reset(hotwords: String? = nil) {
397
+ guard let words = hotwords, !words.isEmpty else {
398
+ SherpaOnnxOnlineStreamReset(recognizer, stream)
399
+ return
400
+ }
401
+
402
+ words.withCString { cString in
403
+ guard let newStream = SherpaOnnxCreateOnlineStreamWithHotwords(recognizer, cString) else {
404
+ fatalError("SherpaOnnxCreateOnlineStreamWithHotwords returned nil")
405
+ }
406
+ lock.lock()
407
+ // lock while release and replace stream
408
+ SherpaOnnxDestroyOnlineStream(stream)
409
+ stream = newStream
410
+ lock.unlock()
411
+ }
412
+ }
413
+
414
+ /// Signal that no more audio samples would be available.
415
+ /// After this call, you cannot call acceptWaveform() any more.
416
+ func inputFinished() {
417
+ SherpaOnnxOnlineStreamInputFinished(stream)
418
+ }
419
+
420
+ /// Return true is an endpoint has been detected.
421
+ func isEndpoint() -> Bool {
422
+ return SherpaOnnxOnlineStreamIsEndpoint(recognizer, stream) != 0
423
+ }
424
+ }
425
+
426
+ // For offline APIs
427
+
428
+ func sherpaOnnxOfflineTransducerModelConfig(
429
+ encoder: String = "",
430
+ decoder: String = "",
431
+ joiner: String = ""
432
+ ) -> SherpaOnnxOfflineTransducerModelConfig {
433
+ return SherpaOnnxOfflineTransducerModelConfig(
434
+ encoder: toCPointer(encoder),
435
+ decoder: toCPointer(decoder),
436
+ joiner: toCPointer(joiner)
437
+ )
438
+ }
439
+
440
+ func sherpaOnnxOfflineParaformerModelConfig(
441
+ model: String = ""
442
+ ) -> SherpaOnnxOfflineParaformerModelConfig {
443
+ return SherpaOnnxOfflineParaformerModelConfig(
444
+ model: toCPointer(model)
445
+ )
446
+ }
447
+
448
+ func sherpaOnnxOfflineZipformerCtcModelConfig(
449
+ model: String = ""
450
+ ) -> SherpaOnnxOfflineZipformerCtcModelConfig {
451
+ return SherpaOnnxOfflineZipformerCtcModelConfig(
452
+ model: toCPointer(model)
453
+ )
454
+ }
455
+
456
+ func sherpaOnnxOfflineWenetCtcModelConfig(
457
+ model: String = ""
458
+ ) -> SherpaOnnxOfflineWenetCtcModelConfig {
459
+ return SherpaOnnxOfflineWenetCtcModelConfig(
460
+ model: toCPointer(model)
461
+ )
462
+ }
463
+
464
+ func sherpaOnnxOfflineOmnilingualAsrCtcModelConfig(
465
+ model: String = ""
466
+ ) -> SherpaOnnxOfflineOmnilingualAsrCtcModelConfig {
467
+ return SherpaOnnxOfflineOmnilingualAsrCtcModelConfig(
468
+ model: toCPointer(model)
469
+ )
470
+ }
471
+
472
+ func sherpaOnnxOfflineMedAsrCtcModelConfig(
473
+ model: String = ""
474
+ ) -> SherpaOnnxOfflineMedAsrCtcModelConfig {
475
+ return SherpaOnnxOfflineMedAsrCtcModelConfig(
476
+ model: toCPointer(model)
477
+ )
478
+ }
479
+
480
+ func sherpaOnnxOfflineFireRedAsrCtcModelConfig(
481
+ model: String = ""
482
+ ) -> SherpaOnnxOfflineFireRedAsrCtcModelConfig {
483
+ return SherpaOnnxOfflineFireRedAsrCtcModelConfig(
484
+ model: toCPointer(model)
485
+ )
486
+ }
487
+
488
+ func sherpaOnnxOfflineNemoEncDecCtcModelConfig(
489
+ model: String = ""
490
+ ) -> SherpaOnnxOfflineNemoEncDecCtcModelConfig {
491
+ return SherpaOnnxOfflineNemoEncDecCtcModelConfig(
492
+ model: toCPointer(model)
493
+ )
494
+ }
495
+
496
+ func sherpaOnnxOfflineDolphinModelConfig(
497
+ model: String = ""
498
+ ) -> SherpaOnnxOfflineDolphinModelConfig {
499
+ return SherpaOnnxOfflineDolphinModelConfig(
500
+ model: toCPointer(model)
501
+ )
502
+ }
503
+
504
+ func sherpaOnnxOfflineWhisperModelConfig(
505
+ encoder: String = "",
506
+ decoder: String = "",
507
+ language: String = "",
508
+ task: String = "transcribe",
509
+ tailPaddings: Int = -1,
510
+ enableTokenTimestamps: Bool = false,
511
+ enableSegmentTimestamps: Bool = false
512
+ ) -> SherpaOnnxOfflineWhisperModelConfig {
513
+ return SherpaOnnxOfflineWhisperModelConfig(
514
+ encoder: toCPointer(encoder),
515
+ decoder: toCPointer(decoder),
516
+ language: toCPointer(language),
517
+ task: toCPointer(task),
518
+ tail_paddings: Int32(tailPaddings),
519
+ enable_token_timestamps: enableTokenTimestamps ? 1 : 0,
520
+ enable_segment_timestamps: enableSegmentTimestamps ? 1 : 0
521
+ )
522
+ }
523
+
524
+ func sherpaOnnxOfflineCanaryModelConfig(
525
+ encoder: String = "",
526
+ decoder: String = "",
527
+ srcLang: String = "en",
528
+ tgtLang: String = "en",
529
+ usePnc: Bool = true
530
+ ) -> SherpaOnnxOfflineCanaryModelConfig {
531
+ return SherpaOnnxOfflineCanaryModelConfig(
532
+ encoder: toCPointer(encoder),
533
+ decoder: toCPointer(decoder),
534
+ src_lang: toCPointer(srcLang),
535
+ tgt_lang: toCPointer(tgtLang),
536
+ use_pnc: usePnc ? 1 : 0
537
+ )
538
+ }
539
+
540
+ func sherpaOnnxOfflineCohereTranscribeModelConfig(
541
+ encoder: String = "",
542
+ decoder: String = "",
543
+ language: String = "",
544
+ usePunct: Bool = true,
545
+ useInverseTextNormalization: Bool = true
546
+ ) -> SherpaOnnxOfflineCohereTranscribeModelConfig {
547
+ return SherpaOnnxOfflineCohereTranscribeModelConfig(
548
+ encoder: toCPointer(encoder),
549
+ decoder: toCPointer(decoder),
550
+ language: toCPointer(language),
551
+ use_punct: usePunct ? 1 : 0,
552
+ use_itn: useInverseTextNormalization ? 1 : 0
553
+ )
554
+ }
555
+
556
+ func sherpaOnnxOfflineFireRedAsrModelConfig(
557
+ encoder: String = "",
558
+ decoder: String = ""
559
+ ) -> SherpaOnnxOfflineFireRedAsrModelConfig {
560
+ return SherpaOnnxOfflineFireRedAsrModelConfig(
561
+ encoder: toCPointer(encoder),
562
+ decoder: toCPointer(decoder)
563
+ )
564
+ }
565
+
566
+ /// there are two versions of Moonshine
567
+ /// For v1, you need four models: preprocessor, encoder, uncachedDecoder, cachedDecoder
568
+ /// For v2, you need two models: encoder, mergedDecoder
569
+ func sherpaOnnxOfflineMoonshineModelConfig(
570
+ preprocessor: String = "",
571
+ encoder: String = "",
572
+ uncachedDecoder: String = "",
573
+ cachedDecoder: String = "",
574
+ mergedDecoder: String = ""
575
+ ) -> SherpaOnnxOfflineMoonshineModelConfig {
576
+ return SherpaOnnxOfflineMoonshineModelConfig(
577
+ preprocessor: toCPointer(preprocessor),
578
+ encoder: toCPointer(encoder),
579
+ uncached_decoder: toCPointer(uncachedDecoder),
580
+ cached_decoder: toCPointer(cachedDecoder),
581
+ merged_decoder: toCPointer(mergedDecoder)
582
+ )
583
+ }
584
+
585
+ func sherpaOnnxOfflineQwen3ASRModelConfig(
586
+ convFrontend: String = "",
587
+ encoder: String = "",
588
+ decoder: String = "",
589
+ tokenizer: String = "",
590
+ maxTotalLen: Int = 512,
591
+ maxNewTokens: Int = 128,
592
+ temperature: Float = 1e-6,
593
+ topP: Float = 0.8,
594
+ seed: Int = 42,
595
+ hotwords: String = ""
596
+ ) -> SherpaOnnxOfflineQwen3ASRModelConfig {
597
+ return SherpaOnnxOfflineQwen3ASRModelConfig(
598
+ conv_frontend: toCPointer(convFrontend),
599
+ encoder: toCPointer(encoder),
600
+ decoder: toCPointer(decoder),
601
+ tokenizer: toCPointer(tokenizer),
602
+ max_total_len: Int32(maxTotalLen),
603
+ max_new_tokens: Int32(maxNewTokens),
604
+ temperature: temperature,
605
+ top_p: topP,
606
+ seed: Int32(seed),
607
+ hotwords: toCPointer(hotwords)
608
+ )
609
+ }
610
+
611
+ func sherpaOnnxOfflineTdnnModelConfig(
612
+ model: String = ""
613
+ ) -> SherpaOnnxOfflineTdnnModelConfig {
614
+ return SherpaOnnxOfflineTdnnModelConfig(
615
+ model: toCPointer(model)
616
+ )
617
+ }
618
+
619
+ func sherpaOnnxOfflineSenseVoiceModelConfig(
620
+ model: String = "",
621
+ language: String = "",
622
+ useInverseTextNormalization: Bool = false
623
+ ) -> SherpaOnnxOfflineSenseVoiceModelConfig {
624
+ return SherpaOnnxOfflineSenseVoiceModelConfig(
625
+ model: toCPointer(model),
626
+ language: toCPointer(language),
627
+ use_itn: useInverseTextNormalization ? 1 : 0
628
+ )
629
+ }
630
+
631
+ func sherpaOnnxOfflineLMConfig(
632
+ model: String = "",
633
+ scale: Float = 1.0
634
+ ) -> SherpaOnnxOfflineLMConfig {
635
+ return SherpaOnnxOfflineLMConfig(
636
+ model: toCPointer(model),
637
+ scale: scale
638
+ )
639
+ }
640
+
641
+ func sherpaOnnxOfflineFunASRNanoModelConfig(
642
+ encoderAdaptor: String = "",
643
+ llm: String = "",
644
+ embedding: String = "",
645
+ tokenizer: String = "",
646
+ systemPrompt: String = "You are a helpful assistant.",
647
+ userPrompt: String = "语音转写:",
648
+ maxNewTokens: Int = 512,
649
+ temperature: Float = 1e-6,
650
+ topP: Float = 0.8,
651
+ seed: Int = 42,
652
+ language: String = "",
653
+ itn: Bool = true,
654
+ hotwords: String = ""
655
+ ) -> SherpaOnnxOfflineFunASRNanoModelConfig {
656
+ return SherpaOnnxOfflineFunASRNanoModelConfig(
657
+ encoder_adaptor: toCPointer(encoderAdaptor),
658
+ llm: toCPointer(llm),
659
+ embedding: toCPointer(embedding),
660
+ tokenizer: toCPointer(tokenizer),
661
+ system_prompt: toCPointer(systemPrompt),
662
+ user_prompt: toCPointer(userPrompt),
663
+ max_new_tokens: Int32(maxNewTokens),
664
+ temperature: temperature,
665
+ top_p: topP,
666
+ seed: Int32(seed),
667
+ language: toCPointer(language),
668
+ itn: itn ? 1 : 0,
669
+ hotwords: toCPointer(hotwords)
670
+ )
671
+ }
672
+
673
+ func sherpaOnnxOfflineModelConfig(
674
+ tokens: String,
675
+ transducer: SherpaOnnxOfflineTransducerModelConfig = sherpaOnnxOfflineTransducerModelConfig(),
676
+ paraformer: SherpaOnnxOfflineParaformerModelConfig = sherpaOnnxOfflineParaformerModelConfig(),
677
+ nemoCtc: SherpaOnnxOfflineNemoEncDecCtcModelConfig = sherpaOnnxOfflineNemoEncDecCtcModelConfig(),
678
+ whisper: SherpaOnnxOfflineWhisperModelConfig = sherpaOnnxOfflineWhisperModelConfig(),
679
+ tdnn: SherpaOnnxOfflineTdnnModelConfig = sherpaOnnxOfflineTdnnModelConfig(),
680
+ numThreads: Int = 1,
681
+ provider: String = "cpu",
682
+ debug: Int = 0,
683
+ modelType: String = "",
684
+ modelingUnit: String = "cjkchar",
685
+ bpeVocab: String = "",
686
+ teleSpeechCtc: String = "",
687
+ senseVoice: SherpaOnnxOfflineSenseVoiceModelConfig = sherpaOnnxOfflineSenseVoiceModelConfig(),
688
+ moonshine: SherpaOnnxOfflineMoonshineModelConfig = sherpaOnnxOfflineMoonshineModelConfig(),
689
+ fireRedAsr: SherpaOnnxOfflineFireRedAsrModelConfig = sherpaOnnxOfflineFireRedAsrModelConfig(),
690
+ dolphin: SherpaOnnxOfflineDolphinModelConfig = sherpaOnnxOfflineDolphinModelConfig(),
691
+ zipformerCtc: SherpaOnnxOfflineZipformerCtcModelConfig =
692
+ sherpaOnnxOfflineZipformerCtcModelConfig(),
693
+ canary: SherpaOnnxOfflineCanaryModelConfig = sherpaOnnxOfflineCanaryModelConfig(),
694
+ wenetCtc: SherpaOnnxOfflineWenetCtcModelConfig =
695
+ sherpaOnnxOfflineWenetCtcModelConfig(),
696
+ omnilingual: SherpaOnnxOfflineOmnilingualAsrCtcModelConfig =
697
+ sherpaOnnxOfflineOmnilingualAsrCtcModelConfig(),
698
+ medasr: SherpaOnnxOfflineMedAsrCtcModelConfig =
699
+ sherpaOnnxOfflineMedAsrCtcModelConfig(),
700
+ funasrNano: SherpaOnnxOfflineFunASRNanoModelConfig =
701
+ sherpaOnnxOfflineFunASRNanoModelConfig(),
702
+ fireRedAsrCtc: SherpaOnnxOfflineFireRedAsrCtcModelConfig =
703
+ sherpaOnnxOfflineFireRedAsrCtcModelConfig(),
704
+ qwen3Asr: SherpaOnnxOfflineQwen3ASRModelConfig =
705
+ sherpaOnnxOfflineQwen3ASRModelConfig(),
706
+ cohereTranscribe: SherpaOnnxOfflineCohereTranscribeModelConfig =
707
+ sherpaOnnxOfflineCohereTranscribeModelConfig()
708
+ ) -> SherpaOnnxOfflineModelConfig {
709
+ return SherpaOnnxOfflineModelConfig(
710
+ transducer: transducer,
711
+ paraformer: paraformer,
712
+ nemo_ctc: nemoCtc,
713
+ whisper: whisper,
714
+ tdnn: tdnn,
715
+ tokens: toCPointer(tokens),
716
+ num_threads: Int32(numThreads),
717
+ debug: Int32(debug),
718
+ provider: toCPointer(provider),
719
+ model_type: toCPointer(modelType),
720
+ modeling_unit: toCPointer(modelingUnit),
721
+ bpe_vocab: toCPointer(bpeVocab),
722
+ telespeech_ctc: toCPointer(teleSpeechCtc),
723
+ sense_voice: senseVoice,
724
+ moonshine: moonshine,
725
+ fire_red_asr: fireRedAsr,
726
+ dolphin: dolphin,
727
+ zipformer_ctc: zipformerCtc,
728
+ canary: canary,
729
+ wenet_ctc: wenetCtc,
730
+ omnilingual: omnilingual,
731
+ medasr: medasr,
732
+ funasr_nano: funasrNano,
733
+ fire_red_asr_ctc: fireRedAsrCtc,
734
+ qwen3_asr: qwen3Asr,
735
+ cohere_transcribe: cohereTranscribe
736
+ )
737
+ }
738
+
739
+ func sherpaOnnxOfflineRecognizerConfig(
740
+ featConfig: SherpaOnnxFeatureConfig,
741
+ modelConfig: SherpaOnnxOfflineModelConfig,
742
+ lmConfig: SherpaOnnxOfflineLMConfig = sherpaOnnxOfflineLMConfig(),
743
+ decodingMethod: String = "greedy_search",
744
+ maxActivePaths: Int = 4,
745
+ hotwordsFile: String = "",
746
+ hotwordsScore: Float = 1.5,
747
+ ruleFsts: String = "",
748
+ ruleFars: String = "",
749
+ blankPenalty: Float = 0.0,
750
+ hr: SherpaOnnxHomophoneReplacerConfig = sherpaOnnxHomophoneReplacerConfig()
751
+ ) -> SherpaOnnxOfflineRecognizerConfig {
752
+ return SherpaOnnxOfflineRecognizerConfig(
753
+ feat_config: featConfig,
754
+ model_config: modelConfig,
755
+ lm_config: lmConfig,
756
+ decoding_method: toCPointer(decodingMethod),
757
+ max_active_paths: Int32(maxActivePaths),
758
+ hotwords_file: toCPointer(hotwordsFile),
759
+ hotwords_score: hotwordsScore,
760
+ rule_fsts: toCPointer(ruleFsts),
761
+ rule_fars: toCPointer(ruleFars),
762
+ blank_penalty: blankPenalty,
763
+ hr: hr
764
+ )
765
+ }
766
+
767
+ class SherpaOnnxOfflineRecongitionResult {
768
+ /// A pointer to the underlying counterpart in C
769
+ let result: UnsafePointer<SherpaOnnxOfflineRecognizerResult>
770
+
771
+ private lazy var _text: String = {
772
+ guard let cstr = result.pointee.text else { return "" }
773
+ return String(cString: cstr)
774
+ }()
775
+
776
+ private lazy var _timestamps: [Float] = {
777
+ guard let p = result.pointee.timestamps else { return [] }
778
+ return (0 ..< result.pointee.count).map { p[Int($0)] }
779
+ }()
780
+
781
+ private lazy var _durations: [Float] = {
782
+ guard let p = result.pointee.durations else { return [] }
783
+ return (0 ..< result.pointee.count).map { p[Int($0)] }
784
+ }()
785
+
786
+ private lazy var _lang: String = {
787
+ guard let cstr = result.pointee.lang else { return "" }
788
+ return String(cString: cstr)
789
+ }()
790
+
791
+ private lazy var _emotion: String = {
792
+ guard let cstr = result.pointee.emotion else { return "" }
793
+ return String(cString: cstr)
794
+ }()
795
+
796
+ private lazy var _event: String = {
797
+ guard let cstr = result.pointee.event else { return "" }
798
+ return String(cString: cstr)
799
+ }()
800
+
801
+ private lazy var _segmentTimestamps: [Float] = {
802
+ guard let p = result.pointee.segment_timestamps else { return [] }
803
+ return (0 ..< result.pointee.segment_count).map { p[Int($0)] }
804
+ }()
805
+
806
+ private lazy var _segmentDurations: [Float] = {
807
+ guard let p = result.pointee.segment_durations else { return [] }
808
+ return (0 ..< result.pointee.segment_count).map { p[Int($0)] }
809
+ }()
810
+
811
+ private lazy var _segmentTexts: [String] = {
812
+ guard let arr = result.pointee.segment_texts_arr else { return [] }
813
+ return (0 ..< result.pointee.segment_count).compactMap { idx -> String? in
814
+ guard let ptr = arr[Int(idx)] else { return nil }
815
+ return String(cString: ptr)
816
+ }
817
+ }()
818
+
819
+ /// Return the actual recognition result.
820
+ /// For English models, it contains words separated by spaces.
821
+ /// For Chinese models, it contains Chinese words.
822
+ var text: String {
823
+ _text
824
+ }
825
+
826
+ var count: Int {
827
+ Int(result.pointee.count)
828
+ }
829
+
830
+ var timestamps: [Float] {
831
+ _timestamps
832
+ }
833
+
834
+ /// Non-empty for TDT models. Empty for all other non-TDT models
835
+ var durations: [Float] {
836
+ _durations
837
+ }
838
+
839
+ /// For SenseVoice models, it can be zh, en, ja, yue, ko
840
+ /// where zh is for Chinese
841
+ /// en is for English
842
+ /// ja is for Japanese
843
+ /// yue is for Cantonese
844
+ /// ko is for Korean
845
+ var lang: String {
846
+ _lang
847
+ }
848
+
849
+ /// for SenseVoice models
850
+ var emotion: String {
851
+ _emotion
852
+ }
853
+
854
+ /// for SenseVoice models
855
+ var event: String {
856
+ _event
857
+ }
858
+
859
+ /// Segment-level timestamps (for Whisper with segment timestamps enabled)
860
+ var segmentCount: Int {
861
+ Int(result.pointee.segment_count)
862
+ }
863
+
864
+ var segmentTimestamps: [Float] {
865
+ _segmentTimestamps
866
+ }
867
+
868
+ var segmentDurations: [Float] {
869
+ _segmentDurations
870
+ }
871
+
872
+ var segmentTexts: [String] {
873
+ _segmentTexts
874
+ }
875
+
876
+ init(result: UnsafePointer<SherpaOnnxOfflineRecognizerResult>) {
877
+ self.result = result
878
+ }
879
+
880
+ deinit {
881
+ SherpaOnnxDestroyOfflineRecognizerResult(result)
882
+ }
883
+ }
884
+
885
+ class SherpaOnnxOfflineRecognizer {
886
+ /// A pointer to the underlying counterpart in C
887
+ private let recognizer: OpaquePointer
888
+
889
+ init(
890
+ config: UnsafePointer<SherpaOnnxOfflineRecognizerConfig>
891
+ ) {
892
+ guard let ptr = SherpaOnnxCreateOfflineRecognizer(config) else {
893
+ fatalError("Failed to create SherpaOnnxOfflineRecognizer")
894
+ }
895
+ recognizer = ptr
896
+ }
897
+
898
+ deinit {
899
+ SherpaOnnxDestroyOfflineRecognizer(recognizer)
900
+ }
901
+
902
+ /// Decode wave samples.
903
+ ///
904
+ /// - Parameters:
905
+ /// - samples: Audio samples normalized to the range [-1, 1]
906
+ /// - sampleRate: Sample rate of the input audio samples. Must match
907
+ /// the one expected by the model.
908
+ func decode(samples: [Float], sampleRate: Int = 16000) -> SherpaOnnxOfflineRecongitionResult {
909
+ let stream = createStream()
910
+ stream.acceptWaveform(samples: samples, sampleRate: sampleRate)
911
+ decode(stream: stream)
912
+ return getResult(stream: stream)
913
+ }
914
+
915
+ func setConfig(config: UnsafePointer<SherpaOnnxOfflineRecognizerConfig>) {
916
+ SherpaOnnxOfflineRecognizerSetConfig(recognizer, config)
917
+ }
918
+
919
+ func createStream() -> SherpaOnnxOfflineStreamWrapper {
920
+ guard let stream = SherpaOnnxCreateOfflineStream(recognizer) else {
921
+ fatalError("Failed to create offline stream")
922
+ }
923
+
924
+ return SherpaOnnxOfflineStreamWrapper(stream: stream)
925
+ }
926
+
927
+ func decode(stream: SherpaOnnxOfflineStreamWrapper) {
928
+ SherpaOnnxDecodeOfflineStream(recognizer, stream.stream)
929
+ }
930
+
931
+ func getResult(stream: SherpaOnnxOfflineStreamWrapper) -> SherpaOnnxOfflineRecongitionResult {
932
+ guard let resultPtr = SherpaOnnxGetOfflineStreamResult(stream.stream) else {
933
+ fatalError("Failed to get offline recognition result")
934
+ }
935
+
936
+ return SherpaOnnxOfflineRecongitionResult(result: resultPtr)
937
+ }
938
+ }
939
+
940
+ class SherpaOnnxOfflineStreamWrapper {
941
+ let stream: OpaquePointer
942
+
943
+ init(stream: OpaquePointer) {
944
+ self.stream = stream
945
+ }
946
+
947
+ deinit {
948
+ SherpaOnnxDestroyOfflineStream(stream)
949
+ }
950
+
951
+ func setOption(key: String, value: String) {
952
+ SherpaOnnxOfflineStreamSetOption(stream, toCPointer(key), toCPointer(value))
953
+ }
954
+
955
+ func acceptWaveform(samples: [Float], sampleRate: Int = 16000) {
956
+ SherpaOnnxAcceptWaveformOffline(stream, Int32(sampleRate), samples, Int32(samples.count))
957
+ }
958
+ }
959
+
960
+ func sherpaOnnxSileroVadModelConfig(
961
+ model: String = "",
962
+ threshold: Float = 0.5,
963
+ minSilenceDuration: Float = 0.25,
964
+ minSpeechDuration: Float = 0.5,
965
+ windowSize: Int = 512,
966
+ maxSpeechDuration: Float = 5.0
967
+ ) -> SherpaOnnxSileroVadModelConfig {
968
+ return SherpaOnnxSileroVadModelConfig(
969
+ model: toCPointer(model),
970
+ threshold: threshold,
971
+ min_silence_duration: minSilenceDuration,
972
+ min_speech_duration: minSpeechDuration,
973
+ window_size: Int32(windowSize),
974
+ max_speech_duration: maxSpeechDuration
975
+ )
976
+ }
977
+
978
+ func sherpaOnnxTenVadModelConfig(
979
+ model: String = "",
980
+ threshold: Float = 0.5,
981
+ minSilenceDuration: Float = 0.25,
982
+ minSpeechDuration: Float = 0.5,
983
+ windowSize: Int = 256,
984
+ maxSpeechDuration: Float = 5.0
985
+ ) -> SherpaOnnxTenVadModelConfig {
986
+ return SherpaOnnxTenVadModelConfig(
987
+ model: toCPointer(model),
988
+ threshold: threshold,
989
+ min_silence_duration: minSilenceDuration,
990
+ min_speech_duration: minSpeechDuration,
991
+ window_size: Int32(windowSize),
992
+ max_speech_duration: maxSpeechDuration
993
+ )
994
+ }
995
+
996
+ func sherpaOnnxVadModelConfig(
997
+ sileroVad: SherpaOnnxSileroVadModelConfig = sherpaOnnxSileroVadModelConfig(),
998
+ sampleRate: Int32 = 16000,
999
+ numThreads: Int = 1,
1000
+ provider: String = "cpu",
1001
+ debug: Int = 0,
1002
+ tenVad: SherpaOnnxTenVadModelConfig = sherpaOnnxTenVadModelConfig()
1003
+ ) -> SherpaOnnxVadModelConfig {
1004
+ return SherpaOnnxVadModelConfig(
1005
+ silero_vad: sileroVad,
1006
+ sample_rate: sampleRate,
1007
+ num_threads: Int32(numThreads),
1008
+ provider: toCPointer(provider),
1009
+ debug: Int32(debug),
1010
+ ten_vad: tenVad
1011
+ )
1012
+ }
1013
+
1014
+ class SherpaOnnxCircularBufferWrapper {
1015
+ private let buffer: OpaquePointer
1016
+
1017
+ init(capacity: Int) {
1018
+ guard let ptr = SherpaOnnxCreateCircularBuffer(Int32(capacity)) else {
1019
+ fatalError("Failed to create SherpaOnnxCircularBuffer")
1020
+ }
1021
+ buffer = ptr
1022
+ }
1023
+
1024
+ deinit {
1025
+ SherpaOnnxDestroyCircularBuffer(buffer)
1026
+ }
1027
+
1028
+ func push(samples: [Float]) {
1029
+ guard !samples.isEmpty else { return }
1030
+ SherpaOnnxCircularBufferPush(buffer, samples, Int32(samples.count))
1031
+ }
1032
+
1033
+ func get(startIndex: Int, n: Int) -> [Float] {
1034
+ guard startIndex >= 0 else { return [] }
1035
+ guard n > 0 else { return [] }
1036
+
1037
+ guard let ptr = SherpaOnnxCircularBufferGet(buffer, Int32(startIndex), Int32(n)) else {
1038
+ return []
1039
+ }
1040
+ defer { SherpaOnnxCircularBufferFree(ptr) }
1041
+
1042
+ return Array(UnsafeBufferPointer(start: ptr, count: n))
1043
+ }
1044
+
1045
+ func pop(n: Int) {
1046
+ guard n > 0 else { return }
1047
+ SherpaOnnxCircularBufferPop(buffer, Int32(n))
1048
+ }
1049
+
1050
+ func size() -> Int {
1051
+ return Int(SherpaOnnxCircularBufferSize(buffer))
1052
+ }
1053
+
1054
+ func reset() {
1055
+ SherpaOnnxCircularBufferReset(buffer)
1056
+ }
1057
+ }
1058
+
1059
+ class SherpaOnnxSpeechSegmentWrapper {
1060
+ private let p: UnsafePointer<SherpaOnnxSpeechSegment>
1061
+
1062
+ init(p: UnsafePointer<SherpaOnnxSpeechSegment>) {
1063
+ self.p = p
1064
+ }
1065
+
1066
+ deinit {
1067
+ SherpaOnnxDestroySpeechSegment(p)
1068
+ }
1069
+
1070
+ var start: Int {
1071
+ Int(p.pointee.start)
1072
+ }
1073
+
1074
+ var n: Int {
1075
+ Int(p.pointee.n)
1076
+ }
1077
+
1078
+ lazy var samples: [Float] = Array(UnsafeBufferPointer(start: p.pointee.samples, count: n))
1079
+ }
1080
+
1081
+ class SherpaOnnxVoiceActivityDetectorWrapper {
1082
+ /// A pointer to the underlying counterpart in C
1083
+ private let vad: OpaquePointer
1084
+
1085
+ init(config: UnsafePointer<SherpaOnnxVadModelConfig>, buffer_size_in_seconds: Float) {
1086
+ guard let vad = SherpaOnnxCreateVoiceActivityDetector(config, buffer_size_in_seconds) else {
1087
+ fatalError("SherpaOnnxCreateVoiceActivityDetector returned nil")
1088
+ }
1089
+ self.vad = vad
1090
+ }
1091
+
1092
+ deinit {
1093
+ SherpaOnnxDestroyVoiceActivityDetector(vad)
1094
+ }
1095
+
1096
+ func acceptWaveform(samples: [Float]) {
1097
+ SherpaOnnxVoiceActivityDetectorAcceptWaveform(vad, samples, Int32(samples.count))
1098
+ }
1099
+
1100
+ func isEmpty() -> Bool {
1101
+ return SherpaOnnxVoiceActivityDetectorEmpty(vad) == 1
1102
+ }
1103
+
1104
+ func isSpeechDetected() -> Bool {
1105
+ return SherpaOnnxVoiceActivityDetectorDetected(vad) == 1
1106
+ }
1107
+
1108
+ func pop() {
1109
+ SherpaOnnxVoiceActivityDetectorPop(vad)
1110
+ }
1111
+
1112
+ func clear() {
1113
+ SherpaOnnxVoiceActivityDetectorClear(vad)
1114
+ }
1115
+
1116
+ func front() -> SherpaOnnxSpeechSegmentWrapper {
1117
+ guard let p = SherpaOnnxVoiceActivityDetectorFront(vad) else {
1118
+ fatalError("SherpaOnnxVoiceActivityDetectorFront returned nil")
1119
+ }
1120
+ return SherpaOnnxSpeechSegmentWrapper(p: p)
1121
+ }
1122
+
1123
+ func reset() {
1124
+ SherpaOnnxVoiceActivityDetectorReset(vad)
1125
+ }
1126
+
1127
+ func flush() {
1128
+ SherpaOnnxVoiceActivityDetectorFlush(vad)
1129
+ }
1130
+ }
1131
+
1132
+ /// offline tts
1133
+ func sherpaOnnxOfflineTtsVitsModelConfig(
1134
+ model: String = "",
1135
+ lexicon: String = "",
1136
+ tokens: String = "",
1137
+ dataDir: String = "",
1138
+ noiseScale: Float = 0.667,
1139
+ noiseScaleW: Float = 0.8,
1140
+ lengthScale: Float = 1.0,
1141
+ dictDir: String = ""
1142
+ ) -> SherpaOnnxOfflineTtsVitsModelConfig {
1143
+ return SherpaOnnxOfflineTtsVitsModelConfig(
1144
+ model: toCPointer(model),
1145
+ lexicon: toCPointer(lexicon),
1146
+ tokens: toCPointer(tokens),
1147
+ data_dir: toCPointer(dataDir),
1148
+ noise_scale: noiseScale,
1149
+ noise_scale_w: noiseScaleW,
1150
+ length_scale: lengthScale,
1151
+ dict_dir: toCPointer(dictDir)
1152
+ )
1153
+ }
1154
+
1155
+ func sherpaOnnxOfflineTtsMatchaModelConfig(
1156
+ acousticModel: String = "",
1157
+ vocoder: String = "",
1158
+ lexicon: String = "",
1159
+ tokens: String = "",
1160
+ dataDir: String = "",
1161
+ noiseScale: Float = 0.667,
1162
+ lengthScale: Float = 1.0,
1163
+ dictDir: String = ""
1164
+ ) -> SherpaOnnxOfflineTtsMatchaModelConfig {
1165
+ return SherpaOnnxOfflineTtsMatchaModelConfig(
1166
+ acoustic_model: toCPointer(acousticModel),
1167
+ vocoder: toCPointer(vocoder),
1168
+ lexicon: toCPointer(lexicon),
1169
+ tokens: toCPointer(tokens),
1170
+ data_dir: toCPointer(dataDir),
1171
+ noise_scale: noiseScale,
1172
+ length_scale: lengthScale,
1173
+ dict_dir: toCPointer(dictDir)
1174
+ )
1175
+ }
1176
+
1177
+ func sherpaOnnxOfflineTtsKokoroModelConfig(
1178
+ model: String = "",
1179
+ voices: String = "",
1180
+ tokens: String = "",
1181
+ dataDir: String = "",
1182
+ lengthScale: Float = 1.0,
1183
+ dictDir: String = "",
1184
+ lexicon: String = "",
1185
+ lang: String = ""
1186
+ ) -> SherpaOnnxOfflineTtsKokoroModelConfig {
1187
+ return SherpaOnnxOfflineTtsKokoroModelConfig(
1188
+ model: toCPointer(model),
1189
+ voices: toCPointer(voices),
1190
+ tokens: toCPointer(tokens),
1191
+ data_dir: toCPointer(dataDir),
1192
+ length_scale: lengthScale,
1193
+ dict_dir: toCPointer(dictDir),
1194
+ lexicon: toCPointer(lexicon),
1195
+ lang: toCPointer(lang)
1196
+ )
1197
+ }
1198
+
1199
+ func sherpaOnnxOfflineTtsKittenModelConfig(
1200
+ model: String = "",
1201
+ voices: String = "",
1202
+ tokens: String = "",
1203
+ dataDir: String = "",
1204
+ lengthScale: Float = 1.0
1205
+ ) -> SherpaOnnxOfflineTtsKittenModelConfig {
1206
+ return SherpaOnnxOfflineTtsKittenModelConfig(
1207
+ model: toCPointer(model),
1208
+ voices: toCPointer(voices),
1209
+ tokens: toCPointer(tokens),
1210
+ data_dir: toCPointer(dataDir),
1211
+ length_scale: lengthScale
1212
+ )
1213
+ }
1214
+
1215
+ func sherpaOnnxOfflineTtsZipvoiceModelConfig(
1216
+ tokens: String = "",
1217
+ encoder: String = "",
1218
+ decoder: String = "",
1219
+ vocoder: String = "",
1220
+ dataDir: String = "",
1221
+ lexicon: String = "",
1222
+ featScale: Float = 0.1,
1223
+ tShift: Float = 0.5,
1224
+ targetRms: Float = 0.1,
1225
+ guidanceScale: Float = 1.0
1226
+ ) -> SherpaOnnxOfflineTtsZipvoiceModelConfig {
1227
+ return SherpaOnnxOfflineTtsZipvoiceModelConfig(
1228
+ tokens: toCPointer(tokens),
1229
+ encoder: toCPointer(encoder),
1230
+ decoder: toCPointer(decoder),
1231
+ vocoder: toCPointer(vocoder),
1232
+ data_dir: toCPointer(dataDir),
1233
+ lexicon: toCPointer(lexicon),
1234
+ feat_scale: featScale,
1235
+ t_shift: tShift,
1236
+ target_rms: targetRms,
1237
+ guidance_scale: guidanceScale
1238
+ )
1239
+ }
1240
+
1241
+ func sherpaOnnxOfflineTtsPocketModelConfig(
1242
+ lmFlow: String = "",
1243
+ lmMain: String = "",
1244
+ encoder: String = "",
1245
+ decoder: String = "",
1246
+ textConditioner: String = "",
1247
+ vocabJson: String = "",
1248
+ tokenScoresJson: String = "",
1249
+ voiceEmbeddingCacheCapacity: Int = 50
1250
+ ) -> SherpaOnnxOfflineTtsPocketModelConfig {
1251
+ return SherpaOnnxOfflineTtsPocketModelConfig(
1252
+ lm_flow: toCPointer(lmFlow),
1253
+ lm_main: toCPointer(lmMain),
1254
+ encoder: toCPointer(encoder),
1255
+ decoder: toCPointer(decoder),
1256
+ text_conditioner: toCPointer(textConditioner),
1257
+ vocab_json: toCPointer(vocabJson),
1258
+ token_scores_json: toCPointer(tokenScoresJson),
1259
+ voice_embedding_cache_capacity: Int32(voiceEmbeddingCacheCapacity)
1260
+ )
1261
+ }
1262
+
1263
+ func sherpaOnnxOfflineTtsSupertonicModelConfig(
1264
+ durationPredictor: String = "",
1265
+ textEncoder: String = "",
1266
+ vectorEstimator: String = "",
1267
+ vocoder: String = "",
1268
+ ttsJson: String = "",
1269
+ unicodeIndexer: String = "",
1270
+ voiceStyle: String = ""
1271
+ ) -> SherpaOnnxOfflineTtsSupertonicModelConfig {
1272
+ return SherpaOnnxOfflineTtsSupertonicModelConfig(
1273
+ duration_predictor: toCPointer(durationPredictor),
1274
+ text_encoder: toCPointer(textEncoder),
1275
+ vector_estimator: toCPointer(vectorEstimator),
1276
+ vocoder: toCPointer(vocoder),
1277
+ tts_json: toCPointer(ttsJson),
1278
+ unicode_indexer: toCPointer(unicodeIndexer),
1279
+ voice_style: toCPointer(voiceStyle)
1280
+ )
1281
+ }
1282
+
1283
+ func sherpaOnnxOfflineTtsModelConfig(
1284
+ vits: SherpaOnnxOfflineTtsVitsModelConfig = sherpaOnnxOfflineTtsVitsModelConfig(),
1285
+ matcha: SherpaOnnxOfflineTtsMatchaModelConfig = sherpaOnnxOfflineTtsMatchaModelConfig(),
1286
+ kokoro: SherpaOnnxOfflineTtsKokoroModelConfig = sherpaOnnxOfflineTtsKokoroModelConfig(),
1287
+ numThreads: Int = 1,
1288
+ debug: Int = 0,
1289
+ provider: String = "cpu",
1290
+ kitten: SherpaOnnxOfflineTtsKittenModelConfig = sherpaOnnxOfflineTtsKittenModelConfig(),
1291
+ zipvoice: SherpaOnnxOfflineTtsZipvoiceModelConfig = sherpaOnnxOfflineTtsZipvoiceModelConfig(),
1292
+ pocket: SherpaOnnxOfflineTtsPocketModelConfig = sherpaOnnxOfflineTtsPocketModelConfig(),
1293
+ supertonic: SherpaOnnxOfflineTtsSupertonicModelConfig =
1294
+ sherpaOnnxOfflineTtsSupertonicModelConfig()
1295
+ ) -> SherpaOnnxOfflineTtsModelConfig {
1296
+ return SherpaOnnxOfflineTtsModelConfig(
1297
+ vits: vits,
1298
+ num_threads: Int32(numThreads),
1299
+ debug: Int32(debug),
1300
+ provider: toCPointer(provider),
1301
+ matcha: matcha,
1302
+ kokoro: kokoro,
1303
+ kitten: kitten,
1304
+ zipvoice: zipvoice,
1305
+ pocket: pocket,
1306
+ supertonic: supertonic
1307
+ )
1308
+ }
1309
+
1310
+ func sherpaOnnxOfflineTtsConfig(
1311
+ model: SherpaOnnxOfflineTtsModelConfig,
1312
+ ruleFsts: String = "",
1313
+ ruleFars: String = "",
1314
+ maxNumSentences: Int = 1,
1315
+ silenceScale: Float = 0.2
1316
+ ) -> SherpaOnnxOfflineTtsConfig {
1317
+ return SherpaOnnxOfflineTtsConfig(
1318
+ model: model,
1319
+ rule_fsts: toCPointer(ruleFsts),
1320
+ max_num_sentences: Int32(maxNumSentences),
1321
+ rule_fars: toCPointer(ruleFars),
1322
+ silence_scale: silenceScale
1323
+ )
1324
+ }
1325
+
1326
+ class SherpaOnnxWaveWrapper {
1327
+ let wave: UnsafePointer<SherpaOnnxWave>!
1328
+
1329
+ class func readWave(filename: String) -> SherpaOnnxWaveWrapper {
1330
+ let wave = SherpaOnnxReadWave(toCPointer(filename))
1331
+ return SherpaOnnxWaveWrapper(wave: wave)
1332
+ }
1333
+
1334
+ init(wave: UnsafePointer<SherpaOnnxWave>!) {
1335
+ self.wave = wave
1336
+ }
1337
+
1338
+ deinit {
1339
+ if let wave {
1340
+ SherpaOnnxFreeWave(wave)
1341
+ }
1342
+ }
1343
+
1344
+ var numSamples: Int {
1345
+ return Int(wave.pointee.num_samples)
1346
+ }
1347
+
1348
+ var sampleRate: Int {
1349
+ return Int(wave.pointee.sample_rate)
1350
+ }
1351
+
1352
+ var samples: [Float] {
1353
+ if numSamples == 0 {
1354
+ return []
1355
+ } else {
1356
+ return [Float](UnsafeBufferPointer(start: wave.pointee.samples, count: numSamples))
1357
+ }
1358
+ }
1359
+ }
1360
+
1361
+ class SherpaOnnxGeneratedAudioWrapper {
1362
+ /// A pointer to the underlying counterpart in C
1363
+ let audio: UnsafePointer<SherpaOnnxGeneratedAudio>!
1364
+
1365
+ init(audio: UnsafePointer<SherpaOnnxGeneratedAudio>!) {
1366
+ self.audio = audio
1367
+ }
1368
+
1369
+ deinit {
1370
+ if let audio {
1371
+ SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio)
1372
+ }
1373
+ }
1374
+
1375
+ var n: Int32 {
1376
+ return audio.pointee.n
1377
+ }
1378
+
1379
+ var sampleRate: Int32 {
1380
+ return audio.pointee.sample_rate
1381
+ }
1382
+
1383
+ var samples: [Float] {
1384
+ if let p = audio.pointee.samples {
1385
+ return [Float](UnsafeBufferPointer(start: p, count: Int(n)))
1386
+ } else {
1387
+ return []
1388
+ }
1389
+ }
1390
+
1391
+ func save(filename: String) -> Int32 {
1392
+ return SherpaOnnxWriteWave(audio.pointee.samples, n, sampleRate, toCPointer(filename))
1393
+ }
1394
+ }
1395
+
1396
+ typealias TtsCallbackWithArg = (
1397
+ @convention(c) (
1398
+ UnsafePointer<Float>?, // const float* samples
1399
+ Int32, // int32_t n
1400
+ UnsafeMutableRawPointer? // void *arg
1401
+ ) -> Int32
1402
+ )?
1403
+
1404
+ class SherpaOnnxCallbackPair {
1405
+ var cb: TtsCallbackWithArg
1406
+ var arg: UnsafeMutableRawPointer?
1407
+ init(cb: TtsCallbackWithArg, arg: UnsafeMutableRawPointer?) {
1408
+ self.cb = cb
1409
+ self.arg = arg
1410
+ }
1411
+ }
1412
+
1413
+ typealias TtsProgressCallbackWithArg =
1414
+ @convention(c) (
1415
+ UnsafePointer<Float>?, Int32, Float, UnsafeMutableRawPointer?
1416
+ ) -> Int32
1417
+
1418
+ struct SherpaOnnxGenerationConfigSwift {
1419
+ var silenceScale: Float = 0.2
1420
+ var speed: Float = 1.0
1421
+ var sid: Int = 0
1422
+ var referenceAudio: [Float] = []
1423
+ var referenceSampleRate: Int = 16000
1424
+ var referenceText: String = ""
1425
+ var numSteps: Int = 1
1426
+ var extra: [String: Any] = [:] // Any can be String, Int, Float, Double
1427
+
1428
+ /// Convert the extra dictionary into a JSON string
1429
+ func extraJsonString() -> String {
1430
+ var jsonCompatible: [String: Any] = [:]
1431
+
1432
+ for (key, value) in extra {
1433
+ switch value {
1434
+ case let v as String:
1435
+ jsonCompatible[key] = v
1436
+ case let v as Int:
1437
+ jsonCompatible[key] = v
1438
+ case let v as Float:
1439
+ jsonCompatible[key] = v
1440
+ case let v as Double:
1441
+ jsonCompatible[key] = v
1442
+ default:
1443
+ // ignore unsupported types
1444
+ print("Warning: unsupported type for key '\(key)' in extra")
1445
+ }
1446
+ }
1447
+
1448
+ guard let data = try? JSONSerialization.data(withJSONObject: jsonCompatible, options: []),
1449
+ let json = String(data: data, encoding: .utf8)
1450
+ else {
1451
+ return "{}"
1452
+ }
1453
+
1454
+ return json
1455
+ }
1456
+ }
1457
+
1458
+ final class SherpaOnnxGenerationConfigC {
1459
+ /// The underlying C struct
1460
+ var cConfig: SherpaOnnxGenerationConfig
1461
+
1462
+ /// Storage for reference audio so the pointer stays valid during the C call
1463
+ private let referenceAudioStorage: [Float]
1464
+
1465
+ /// Extra JSON string for C API
1466
+ let extraJson: String
1467
+
1468
+ init(_ swiftConfig: SherpaOnnxGenerationConfigSwift) {
1469
+ let referenceAudio = swiftConfig.referenceAudio
1470
+
1471
+ let extraJson = swiftConfig.extraJsonString()
1472
+ self.extraJson = extraJson
1473
+
1474
+ referenceAudioStorage = referenceAudio
1475
+
1476
+ cConfig = referenceAudioStorage.withUnsafeBufferPointer { buffer in
1477
+ SherpaOnnxGenerationConfig(
1478
+ silence_scale: swiftConfig.silenceScale,
1479
+ speed: swiftConfig.speed,
1480
+ sid: Int32(swiftConfig.sid),
1481
+ reference_audio: buffer.count > 0 ? buffer.baseAddress : nil,
1482
+ reference_audio_len: Int32(buffer.count),
1483
+ reference_sample_rate: Int32(swiftConfig.referenceSampleRate),
1484
+ reference_text: toCPointer(swiftConfig.referenceText),
1485
+ num_steps: Int32(swiftConfig.numSteps),
1486
+ extra: toCPointer(extraJson)
1487
+ )
1488
+ }
1489
+ }
1490
+ }
1491
+
1492
+ class SherpaOnnxOfflineTtsWrapper {
1493
+ /// A pointer to the underlying counterpart in C
1494
+ let tts: OpaquePointer!
1495
+
1496
+ /// Constructor taking a model config
1497
+ init(
1498
+ config: UnsafePointer<SherpaOnnxOfflineTtsConfig>!
1499
+ ) {
1500
+ tts = SherpaOnnxCreateOfflineTts(config)
1501
+ }
1502
+
1503
+ deinit {
1504
+ if let tts {
1505
+ SherpaOnnxDestroyOfflineTts(tts)
1506
+ }
1507
+ }
1508
+
1509
+ func generate(text: String, sid: Int = 0, speed: Float = 1.0) -> SherpaOnnxGeneratedAudioWrapper {
1510
+ let config = SherpaOnnxGenerationConfigSwift(speed: speed, sid: sid)
1511
+ return generateWithConfig(text: text, config: config, callback: nil, arg: nil)
1512
+ }
1513
+
1514
+ func generateWithCallbackWithArg(
1515
+ text: String, callback: TtsCallbackWithArg, arg: UnsafeMutableRawPointer, sid: Int = 0,
1516
+ speed: Float = 1.0
1517
+ ) -> SherpaOnnxGeneratedAudioWrapper {
1518
+ let config = SherpaOnnxGenerationConfigSwift(speed: speed, sid: sid)
1519
+
1520
+ let pair = SherpaOnnxCallbackPair(cb: callback, arg: arg)
1521
+ let unmanaged = Unmanaged.passRetained(pair)
1522
+ let wrapper: TtsProgressCallbackWithArg = { samples, n, _, rawArg in
1523
+ let p = Unmanaged<SherpaOnnxCallbackPair>.fromOpaque(rawArg!).takeUnretainedValue()
1524
+ return p.cb!(samples, n, p.arg)
1525
+ }
1526
+ let result = generateWithConfig(
1527
+ text: text, config: config, callback: wrapper, arg: unmanaged.toOpaque()
1528
+ )
1529
+ unmanaged.release()
1530
+ return result
1531
+ }
1532
+
1533
+ func generateWithConfig(
1534
+ text: String,
1535
+ config: SherpaOnnxGenerationConfigSwift,
1536
+ callback: TtsProgressCallbackWithArg?,
1537
+ arg: UnsafeMutableRawPointer?
1538
+ ) -> SherpaOnnxGeneratedAudioWrapper {
1539
+ let bridge = SherpaOnnxGenerationConfigC(config)
1540
+
1541
+ let audio: UnsafePointer<SherpaOnnxGeneratedAudio>? =
1542
+ withUnsafePointer(to: &bridge.cConfig) { configPtr in
1543
+ SherpaOnnxOfflineTtsGenerateWithConfig(
1544
+ tts,
1545
+ toCPointer(text),
1546
+ configPtr,
1547
+ callback,
1548
+ arg
1549
+ )
1550
+ }
1551
+
1552
+ return SherpaOnnxGeneratedAudioWrapper(audio: audio)
1553
+ }
1554
+ }
1555
+
1556
+ // spoken language identification
1557
+
1558
+ func sherpaOnnxSpokenLanguageIdentificationWhisperConfig(
1559
+ encoder: String,
1560
+ decoder: String,
1561
+ tailPaddings: Int = -1
1562
+ ) -> SherpaOnnxSpokenLanguageIdentificationWhisperConfig {
1563
+ return SherpaOnnxSpokenLanguageIdentificationWhisperConfig(
1564
+ encoder: toCPointer(encoder),
1565
+ decoder: toCPointer(decoder),
1566
+ tail_paddings: Int32(tailPaddings)
1567
+ )
1568
+ }
1569
+
1570
+ func sherpaOnnxSpokenLanguageIdentificationConfig(
1571
+ whisper: SherpaOnnxSpokenLanguageIdentificationWhisperConfig,
1572
+ numThreads: Int = 1,
1573
+ debug: Int = 0,
1574
+ provider: String = "cpu"
1575
+ ) -> SherpaOnnxSpokenLanguageIdentificationConfig {
1576
+ return SherpaOnnxSpokenLanguageIdentificationConfig(
1577
+ whisper: whisper,
1578
+ num_threads: Int32(numThreads),
1579
+ debug: Int32(debug),
1580
+ provider: toCPointer(provider)
1581
+ )
1582
+ }
1583
+
1584
+ class SherpaOnnxSpokenLanguageIdentificationResultWrapper {
1585
+ /// A pointer to the underlying counterpart in C
1586
+ let result: UnsafePointer<SherpaOnnxSpokenLanguageIdentificationResult>!
1587
+
1588
+ /// Return the detected language.
1589
+ /// en for English
1590
+ /// zh for Chinese
1591
+ /// es for Spanish
1592
+ /// de for German
1593
+ /// etc.
1594
+ var lang: String {
1595
+ return String(cString: result.pointee.lang)
1596
+ }
1597
+
1598
+ init(result: UnsafePointer<SherpaOnnxSpokenLanguageIdentificationResult>!) {
1599
+ self.result = result
1600
+ }
1601
+
1602
+ deinit {
1603
+ if let result {
1604
+ SherpaOnnxDestroySpokenLanguageIdentificationResult(result)
1605
+ }
1606
+ }
1607
+ }
1608
+
1609
+ class SherpaOnnxSpokenLanguageIdentificationWrapper {
1610
+ /// A pointer to the underlying counterpart in C
1611
+ let slid: OpaquePointer!
1612
+
1613
+ init(
1614
+ config: UnsafePointer<SherpaOnnxSpokenLanguageIdentificationConfig>!
1615
+ ) {
1616
+ slid = SherpaOnnxCreateSpokenLanguageIdentification(config)
1617
+ }
1618
+
1619
+ deinit {
1620
+ if let slid {
1621
+ SherpaOnnxDestroySpokenLanguageIdentification(slid)
1622
+ }
1623
+ }
1624
+
1625
+ func decode(samples: [Float], sampleRate: Int = 16000)
1626
+ -> SherpaOnnxSpokenLanguageIdentificationResultWrapper
1627
+ {
1628
+ let stream: OpaquePointer! = SherpaOnnxSpokenLanguageIdentificationCreateOfflineStream(slid)
1629
+ SherpaOnnxAcceptWaveformOffline(stream, Int32(sampleRate), samples, Int32(samples.count))
1630
+
1631
+ let result: UnsafePointer<SherpaOnnxSpokenLanguageIdentificationResult>? =
1632
+ SherpaOnnxSpokenLanguageIdentificationCompute(
1633
+ slid,
1634
+ stream
1635
+ )
1636
+
1637
+ SherpaOnnxDestroyOfflineStream(stream)
1638
+ return SherpaOnnxSpokenLanguageIdentificationResultWrapper(result: result)
1639
+ }
1640
+ }
1641
+
1642
+ // keyword spotting
1643
+
1644
+ class SherpaOnnxKeywordResultWrapper {
1645
+ /// A pointer to the underlying counterpart in C
1646
+ let result: UnsafePointer<SherpaOnnxKeywordResult>!
1647
+
1648
+ var keyword: String {
1649
+ return String(cString: result.pointee.keyword)
1650
+ }
1651
+
1652
+ var count: Int32 {
1653
+ return result.pointee.count
1654
+ }
1655
+
1656
+ var tokens: [String] {
1657
+ if let tokensPointer = result.pointee.tokens_arr {
1658
+ var tokens: [String] = []
1659
+ for index in 0 ..< count {
1660
+ if let tokenPointer = tokensPointer[Int(index)] {
1661
+ let token = String(cString: tokenPointer)
1662
+ tokens.append(token)
1663
+ }
1664
+ }
1665
+ return tokens
1666
+ } else {
1667
+ return []
1668
+ }
1669
+ }
1670
+
1671
+ init(result: UnsafePointer<SherpaOnnxKeywordResult>!) {
1672
+ self.result = result
1673
+ }
1674
+
1675
+ deinit {
1676
+ if let result {
1677
+ SherpaOnnxDestroyKeywordResult(result)
1678
+ }
1679
+ }
1680
+ }
1681
+
1682
+ func sherpaOnnxKeywordSpotterConfig(
1683
+ featConfig: SherpaOnnxFeatureConfig,
1684
+ modelConfig: SherpaOnnxOnlineModelConfig,
1685
+ keywordsFile: String,
1686
+ maxActivePaths: Int = 4,
1687
+ numTrailingBlanks: Int = 1,
1688
+ keywordsScore: Float = 1.0,
1689
+ keywordsThreshold: Float = 0.25,
1690
+ keywordsBuf: String = "",
1691
+ keywordsBufSize: Int = 0
1692
+ ) -> SherpaOnnxKeywordSpotterConfig {
1693
+ return SherpaOnnxKeywordSpotterConfig(
1694
+ feat_config: featConfig,
1695
+ model_config: modelConfig,
1696
+ max_active_paths: Int32(maxActivePaths),
1697
+ num_trailing_blanks: Int32(numTrailingBlanks),
1698
+ keywords_score: keywordsScore,
1699
+ keywords_threshold: keywordsThreshold,
1700
+ keywords_file: toCPointer(keywordsFile),
1701
+ keywords_buf: toCPointer(keywordsBuf),
1702
+ keywords_buf_size: Int32(keywordsBufSize)
1703
+ )
1704
+ }
1705
+
1706
+ class SherpaOnnxKeywordSpotterWrapper {
1707
+ /// A pointer to the underlying counterpart in C
1708
+ let spotter: OpaquePointer!
1709
+ var stream: OpaquePointer!
1710
+
1711
+ init(
1712
+ config: UnsafePointer<SherpaOnnxKeywordSpotterConfig>!
1713
+ ) {
1714
+ spotter = SherpaOnnxCreateKeywordSpotter(config)
1715
+ stream = SherpaOnnxCreateKeywordStream(spotter)
1716
+ }
1717
+
1718
+ deinit {
1719
+ if let stream {
1720
+ SherpaOnnxDestroyOnlineStream(stream)
1721
+ }
1722
+
1723
+ if let spotter {
1724
+ SherpaOnnxDestroyKeywordSpotter(spotter)
1725
+ }
1726
+ }
1727
+
1728
+ func acceptWaveform(samples: [Float], sampleRate: Int = 16000) {
1729
+ SherpaOnnxOnlineStreamAcceptWaveform(stream, Int32(sampleRate), samples, Int32(samples.count))
1730
+ }
1731
+
1732
+ func isReady() -> Bool {
1733
+ return SherpaOnnxIsKeywordStreamReady(spotter, stream) == 1 ? true : false
1734
+ }
1735
+
1736
+ func decode() {
1737
+ SherpaOnnxDecodeKeywordStream(spotter, stream)
1738
+ }
1739
+
1740
+ func reset() {
1741
+ SherpaOnnxResetKeywordStream(spotter, stream)
1742
+ }
1743
+
1744
+ func getResult() -> SherpaOnnxKeywordResultWrapper {
1745
+ let result: UnsafePointer<SherpaOnnxKeywordResult>? = SherpaOnnxGetKeywordResult(
1746
+ spotter, stream
1747
+ )
1748
+ return SherpaOnnxKeywordResultWrapper(result: result)
1749
+ }
1750
+
1751
+ /// Signal that no more audio samples would be available.
1752
+ /// After this call, you cannot call acceptWaveform() any more.
1753
+ func inputFinished() {
1754
+ SherpaOnnxOnlineStreamInputFinished(stream)
1755
+ }
1756
+ }
1757
+
1758
+ // Punctuation
1759
+
1760
+ func sherpaOnnxOfflinePunctuationModelConfig(
1761
+ ctTransformer: String,
1762
+ numThreads: Int = 1,
1763
+ debug: Int = 0,
1764
+ provider: String = "cpu"
1765
+ ) -> SherpaOnnxOfflinePunctuationModelConfig {
1766
+ return SherpaOnnxOfflinePunctuationModelConfig(
1767
+ ct_transformer: toCPointer(ctTransformer),
1768
+ num_threads: Int32(numThreads),
1769
+ debug: Int32(debug),
1770
+ provider: toCPointer(provider)
1771
+ )
1772
+ }
1773
+
1774
+ func sherpaOnnxOfflinePunctuationConfig(
1775
+ model: SherpaOnnxOfflinePunctuationModelConfig
1776
+ ) -> SherpaOnnxOfflinePunctuationConfig {
1777
+ return SherpaOnnxOfflinePunctuationConfig(
1778
+ model: model
1779
+ )
1780
+ }
1781
+
1782
+ class SherpaOnnxOfflinePunctuationWrapper {
1783
+ /// A pointer to the underlying counterpart in C
1784
+ let ptr: OpaquePointer!
1785
+
1786
+ /// Constructor taking a model config
1787
+ init(
1788
+ config: UnsafePointer<SherpaOnnxOfflinePunctuationConfig>!
1789
+ ) {
1790
+ ptr = SherpaOnnxCreateOfflinePunctuation(config)
1791
+ }
1792
+
1793
+ deinit {
1794
+ if let ptr {
1795
+ SherpaOnnxDestroyOfflinePunctuation(ptr)
1796
+ }
1797
+ }
1798
+
1799
+ func addPunct(text: String) -> String {
1800
+ let cText = SherpaOfflinePunctuationAddPunct(ptr, toCPointer(text))
1801
+ let ans = String(cString: cText!)
1802
+ SherpaOfflinePunctuationFreeText(cText)
1803
+ return ans
1804
+ }
1805
+ }
1806
+
1807
+ func sherpaOnnxOnlinePunctuationModelConfig(
1808
+ cnnBiLstm: String,
1809
+ bpeVocab: String,
1810
+ numThreads: Int = 1,
1811
+ debug: Int = 0,
1812
+ provider: String = "cpu"
1813
+ ) -> SherpaOnnxOnlinePunctuationModelConfig {
1814
+ return SherpaOnnxOnlinePunctuationModelConfig(
1815
+ cnn_bilstm: toCPointer(cnnBiLstm),
1816
+ bpe_vocab: toCPointer(bpeVocab),
1817
+ num_threads: Int32(numThreads),
1818
+ debug: Int32(debug),
1819
+ provider: toCPointer(provider)
1820
+ )
1821
+ }
1822
+
1823
+ func sherpaOnnxOnlinePunctuationConfig(
1824
+ model: SherpaOnnxOnlinePunctuationModelConfig
1825
+ ) -> SherpaOnnxOnlinePunctuationConfig {
1826
+ return SherpaOnnxOnlinePunctuationConfig(model: model)
1827
+ }
1828
+
1829
+ class SherpaOnnxOnlinePunctuationWrapper {
1830
+ /// A pointer to the underlying counterpart in C
1831
+ let ptr: OpaquePointer!
1832
+
1833
+ /// Constructor taking a model config
1834
+ init(
1835
+ config: UnsafePointer<SherpaOnnxOnlinePunctuationConfig>!
1836
+ ) {
1837
+ ptr = SherpaOnnxCreateOnlinePunctuation(config)
1838
+ }
1839
+
1840
+ deinit {
1841
+ if let ptr {
1842
+ SherpaOnnxDestroyOnlinePunctuation(ptr)
1843
+ }
1844
+ }
1845
+
1846
+ func addPunct(text: String) -> String {
1847
+ let cText = SherpaOnnxOnlinePunctuationAddPunct(ptr, toCPointer(text))
1848
+ let ans = String(cString: cText!)
1849
+ SherpaOnnxOnlinePunctuationFreeText(cText)
1850
+ return ans
1851
+ }
1852
+ }
1853
+
1854
+ func sherpaOnnxOfflineSpeakerSegmentationPyannoteModelConfig(model: String)
1855
+ -> SherpaOnnxOfflineSpeakerSegmentationPyannoteModelConfig
1856
+ {
1857
+ return SherpaOnnxOfflineSpeakerSegmentationPyannoteModelConfig(model: toCPointer(model))
1858
+ }
1859
+
1860
+ func sherpaOnnxOfflineSpeakerSegmentationModelConfig(
1861
+ pyannote: SherpaOnnxOfflineSpeakerSegmentationPyannoteModelConfig,
1862
+ numThreads: Int = 1,
1863
+ debug: Int = 0,
1864
+ provider: String = "cpu"
1865
+ ) -> SherpaOnnxOfflineSpeakerSegmentationModelConfig {
1866
+ return SherpaOnnxOfflineSpeakerSegmentationModelConfig(
1867
+ pyannote: pyannote,
1868
+ num_threads: Int32(numThreads),
1869
+ debug: Int32(debug),
1870
+ provider: toCPointer(provider)
1871
+ )
1872
+ }
1873
+
1874
+ func sherpaOnnxFastClusteringConfig(numClusters: Int = -1, threshold: Float = 0.5)
1875
+ -> SherpaOnnxFastClusteringConfig
1876
+ {
1877
+ return SherpaOnnxFastClusteringConfig(num_clusters: Int32(numClusters), threshold: threshold)
1878
+ }
1879
+
1880
+ func sherpaOnnxSpeakerEmbeddingExtractorConfig(
1881
+ model: String,
1882
+ numThreads: Int = 1,
1883
+ debug: Int = 0,
1884
+ provider: String = "cpu"
1885
+ ) -> SherpaOnnxSpeakerEmbeddingExtractorConfig {
1886
+ return SherpaOnnxSpeakerEmbeddingExtractorConfig(
1887
+ model: toCPointer(model),
1888
+ num_threads: Int32(numThreads),
1889
+ debug: Int32(debug),
1890
+ provider: toCPointer(provider)
1891
+ )
1892
+ }
1893
+
1894
+ func sherpaOnnxOfflineSpeakerDiarizationConfig(
1895
+ segmentation: SherpaOnnxOfflineSpeakerSegmentationModelConfig,
1896
+ embedding: SherpaOnnxSpeakerEmbeddingExtractorConfig,
1897
+ clustering: SherpaOnnxFastClusteringConfig,
1898
+ minDurationOn: Float = 0.3,
1899
+ minDurationOff: Float = 0.5
1900
+ ) -> SherpaOnnxOfflineSpeakerDiarizationConfig {
1901
+ return SherpaOnnxOfflineSpeakerDiarizationConfig(
1902
+ segmentation: segmentation,
1903
+ embedding: embedding,
1904
+ clustering: clustering,
1905
+ min_duration_on: minDurationOn,
1906
+ min_duration_off: minDurationOff
1907
+ )
1908
+ }
1909
+
1910
+ struct SherpaOnnxOfflineSpeakerDiarizationSegmentWrapper {
1911
+ var start: Float = 0
1912
+ var end: Float = 0
1913
+ var speaker: Int = 0
1914
+ }
1915
+
1916
+ class SherpaOnnxOfflineSpeakerDiarizationWrapper {
1917
+ /// A pointer to the underlying counterpart in C
1918
+ let impl: OpaquePointer!
1919
+
1920
+ init(
1921
+ config: UnsafePointer<SherpaOnnxOfflineSpeakerDiarizationConfig>!
1922
+ ) {
1923
+ impl = SherpaOnnxCreateOfflineSpeakerDiarization(config)
1924
+ }
1925
+
1926
+ deinit {
1927
+ if let impl {
1928
+ SherpaOnnxDestroyOfflineSpeakerDiarization(impl)
1929
+ }
1930
+ }
1931
+
1932
+ var sampleRate: Int {
1933
+ return Int(SherpaOnnxOfflineSpeakerDiarizationGetSampleRate(impl))
1934
+ }
1935
+
1936
+ /// only config.clustering is used. All other fields are ignored
1937
+ func setConfig(config: UnsafePointer<SherpaOnnxOfflineSpeakerDiarizationConfig>!) {
1938
+ SherpaOnnxOfflineSpeakerDiarizationSetConfig(impl, config)
1939
+ }
1940
+
1941
+ func process(samples: [Float]) -> [SherpaOnnxOfflineSpeakerDiarizationSegmentWrapper] {
1942
+ let result = SherpaOnnxOfflineSpeakerDiarizationProcess(
1943
+ impl, samples, Int32(samples.count)
1944
+ )
1945
+
1946
+ if result == nil {
1947
+ return []
1948
+ }
1949
+
1950
+ let numSegments = Int(SherpaOnnxOfflineSpeakerDiarizationResultGetNumSegments(result))
1951
+
1952
+ let p: UnsafePointer<SherpaOnnxOfflineSpeakerDiarizationSegment>? =
1953
+ SherpaOnnxOfflineSpeakerDiarizationResultSortByStartTime(result)
1954
+
1955
+ if p == nil {
1956
+ return []
1957
+ }
1958
+
1959
+ var ans: [SherpaOnnxOfflineSpeakerDiarizationSegmentWrapper] = []
1960
+ for i in 0 ..< numSegments {
1961
+ ans.append(
1962
+ SherpaOnnxOfflineSpeakerDiarizationSegmentWrapper(
1963
+ start: p![i].start, end: p![i].end, speaker: Int(p![i].speaker)
1964
+ )
1965
+ )
1966
+ }
1967
+
1968
+ SherpaOnnxOfflineSpeakerDiarizationDestroySegment(p)
1969
+ SherpaOnnxOfflineSpeakerDiarizationDestroyResult(result)
1970
+
1971
+ return ans
1972
+ }
1973
+ }
1974
+
1975
+ class SherpaOnnxOnlineStreamWrapper {
1976
+ /// A pointer to the underlying counterpart in C
1977
+ let impl: OpaquePointer!
1978
+ init(impl: OpaquePointer!) {
1979
+ self.impl = impl
1980
+ }
1981
+
1982
+ deinit {
1983
+ if let impl {
1984
+ SherpaOnnxDestroyOnlineStream(impl)
1985
+ }
1986
+ }
1987
+
1988
+ func acceptWaveform(samples: [Float], sampleRate: Int = 16000) {
1989
+ SherpaOnnxOnlineStreamAcceptWaveform(impl, Int32(sampleRate), samples, Int32(samples.count))
1990
+ }
1991
+
1992
+ func inputFinished() {
1993
+ SherpaOnnxOnlineStreamInputFinished(impl)
1994
+ }
1995
+ }
1996
+
1997
+ class SherpaOnnxSpeakerEmbeddingExtractorWrapper {
1998
+ /// A pointer to the underlying counterpart in C
1999
+ let impl: OpaquePointer!
2000
+
2001
+ init(
2002
+ config: UnsafePointer<SherpaOnnxSpeakerEmbeddingExtractorConfig>!
2003
+ ) {
2004
+ impl = SherpaOnnxCreateSpeakerEmbeddingExtractor(config)
2005
+ }
2006
+
2007
+ deinit {
2008
+ if let impl {
2009
+ SherpaOnnxDestroySpeakerEmbeddingExtractor(impl)
2010
+ }
2011
+ }
2012
+
2013
+ var dim: Int {
2014
+ return Int(SherpaOnnxSpeakerEmbeddingExtractorDim(impl))
2015
+ }
2016
+
2017
+ func createStream() -> SherpaOnnxOnlineStreamWrapper {
2018
+ let newStream = SherpaOnnxSpeakerEmbeddingExtractorCreateStream(impl)
2019
+ return SherpaOnnxOnlineStreamWrapper(impl: newStream)
2020
+ }
2021
+
2022
+ func isReady(stream: SherpaOnnxOnlineStreamWrapper) -> Bool {
2023
+ return SherpaOnnxSpeakerEmbeddingExtractorIsReady(impl, stream.impl) == 1 ? true : false
2024
+ }
2025
+
2026
+ func compute(stream: SherpaOnnxOnlineStreamWrapper) -> [Float] {
2027
+ if !isReady(stream: stream) {
2028
+ return []
2029
+ }
2030
+
2031
+ let p = SherpaOnnxSpeakerEmbeddingExtractorComputeEmbedding(impl, stream.impl)
2032
+
2033
+ defer {
2034
+ SherpaOnnxSpeakerEmbeddingExtractorDestroyEmbedding(p)
2035
+ }
2036
+
2037
+ return [Float](UnsafeBufferPointer(start: p, count: dim))
2038
+ }
2039
+ }
2040
+
2041
+ func sherpaOnnxOfflineSpeechDenoiserGtcrnModelConfig(model: String = "")
2042
+ -> SherpaOnnxOfflineSpeechDenoiserGtcrnModelConfig
2043
+ {
2044
+ return SherpaOnnxOfflineSpeechDenoiserGtcrnModelConfig(model: toCPointer(model))
2045
+ }
2046
+
2047
+ func sherpaOnnxOfflineSpeechDenoiserDpdfNetModelConfig(model: String = "")
2048
+ -> SherpaOnnxOfflineSpeechDenoiserDpdfNetModelConfig
2049
+ {
2050
+ return SherpaOnnxOfflineSpeechDenoiserDpdfNetModelConfig(model: toCPointer(model))
2051
+ }
2052
+
2053
+ func sherpaOnnxOfflineSpeechDenoiserModelConfig(
2054
+ gtcrn: SherpaOnnxOfflineSpeechDenoiserGtcrnModelConfig =
2055
+ sherpaOnnxOfflineSpeechDenoiserGtcrnModelConfig(),
2056
+ dpdfnet: SherpaOnnxOfflineSpeechDenoiserDpdfNetModelConfig =
2057
+ sherpaOnnxOfflineSpeechDenoiserDpdfNetModelConfig(),
2058
+ numThreads: Int = 1,
2059
+ provider: String = "cpu",
2060
+ debug: Int = 0
2061
+ ) -> SherpaOnnxOfflineSpeechDenoiserModelConfig {
2062
+ return SherpaOnnxOfflineSpeechDenoiserModelConfig(
2063
+ gtcrn: gtcrn,
2064
+ num_threads: Int32(numThreads),
2065
+ debug: Int32(debug),
2066
+ provider: toCPointer(provider),
2067
+ dpdfnet: dpdfnet
2068
+ )
2069
+ }
2070
+
2071
+ func sherpaOnnxOfflineSpeechDenoiserConfig(
2072
+ model: SherpaOnnxOfflineSpeechDenoiserModelConfig =
2073
+ sherpaOnnxOfflineSpeechDenoiserModelConfig()
2074
+ ) -> SherpaOnnxOfflineSpeechDenoiserConfig {
2075
+ return SherpaOnnxOfflineSpeechDenoiserConfig(
2076
+ model: model
2077
+ )
2078
+ }
2079
+
2080
+ class SherpaOnnxDenoisedAudioWrapper {
2081
+ /// A pointer to the underlying counterpart in C
2082
+ let audio: UnsafePointer<SherpaOnnxDenoisedAudio>!
2083
+
2084
+ init(audio: UnsafePointer<SherpaOnnxDenoisedAudio>!) {
2085
+ self.audio = audio
2086
+ }
2087
+
2088
+ deinit {
2089
+ if let audio {
2090
+ SherpaOnnxDestroyDenoisedAudio(audio)
2091
+ }
2092
+ }
2093
+
2094
+ var n: Int32 {
2095
+ guard let audio else {
2096
+ return 0
2097
+ }
2098
+ return audio.pointee.n
2099
+ }
2100
+
2101
+ var sampleRate: Int32 {
2102
+ guard let audio else {
2103
+ return 0
2104
+ }
2105
+ return audio.pointee.sample_rate
2106
+ }
2107
+
2108
+ var samples: [Float] {
2109
+ guard let audio else {
2110
+ return []
2111
+ }
2112
+
2113
+ if let p = audio.pointee.samples {
2114
+ var samples: [Float] = []
2115
+ for index in 0 ..< n {
2116
+ samples.append(p[Int(index)])
2117
+ }
2118
+ return samples
2119
+ } else {
2120
+ return []
2121
+ }
2122
+ }
2123
+
2124
+ func save(filename: String) -> Int32 {
2125
+ guard let audio else {
2126
+ return 0
2127
+ }
2128
+ return SherpaOnnxWriteWave(audio.pointee.samples, n, sampleRate, toCPointer(filename))
2129
+ }
2130
+ }
2131
+
2132
+ class SherpaOnnxOfflineSpeechDenoiserWrapper {
2133
+ /// A pointer to the underlying counterpart in C
2134
+ let impl: OpaquePointer!
2135
+
2136
+ /// Constructor taking a model config
2137
+ init(
2138
+ config: UnsafePointer<SherpaOnnxOfflineSpeechDenoiserConfig>!
2139
+ ) {
2140
+ impl = SherpaOnnxCreateOfflineSpeechDenoiser(config)
2141
+ }
2142
+
2143
+ deinit {
2144
+ if let impl {
2145
+ SherpaOnnxDestroyOfflineSpeechDenoiser(impl)
2146
+ }
2147
+ }
2148
+
2149
+ func run(samples: [Float], sampleRate: Int) -> SherpaOnnxDenoisedAudioWrapper {
2150
+ let audio: UnsafePointer<SherpaOnnxDenoisedAudio>? = SherpaOnnxOfflineSpeechDenoiserRun(
2151
+ impl, samples, Int32(samples.count), Int32(sampleRate)
2152
+ )
2153
+
2154
+ return SherpaOnnxDenoisedAudioWrapper(audio: audio)
2155
+ }
2156
+
2157
+ var sampleRate: Int {
2158
+ return Int(SherpaOnnxOfflineSpeechDenoiserGetSampleRate(impl))
2159
+ }
2160
+ }
2161
+
2162
+ func sherpaOnnxOnlineSpeechDenoiserConfig(
2163
+ model: SherpaOnnxOfflineSpeechDenoiserModelConfig =
2164
+ sherpaOnnxOfflineSpeechDenoiserModelConfig()
2165
+ ) -> SherpaOnnxOnlineSpeechDenoiserConfig {
2166
+ return SherpaOnnxOnlineSpeechDenoiserConfig(model: model)
2167
+ }
2168
+
2169
+ class SherpaOnnxOnlineSpeechDenoiserWrapper {
2170
+ let impl: OpaquePointer!
2171
+
2172
+ init(
2173
+ config: UnsafePointer<SherpaOnnxOnlineSpeechDenoiserConfig>!
2174
+ ) {
2175
+ impl = SherpaOnnxCreateOnlineSpeechDenoiser(config)
2176
+ }
2177
+
2178
+ deinit {
2179
+ if let impl {
2180
+ SherpaOnnxDestroyOnlineSpeechDenoiser(impl)
2181
+ }
2182
+ }
2183
+
2184
+ func run(samples: [Float], sampleRate: Int) -> SherpaOnnxDenoisedAudioWrapper {
2185
+ let audio: UnsafePointer<SherpaOnnxDenoisedAudio>? = SherpaOnnxOnlineSpeechDenoiserRun(
2186
+ impl, samples, Int32(samples.count), Int32(sampleRate)
2187
+ )
2188
+ return SherpaOnnxDenoisedAudioWrapper(audio: audio)
2189
+ }
2190
+
2191
+ func flush() -> SherpaOnnxDenoisedAudioWrapper {
2192
+ let audio: UnsafePointer<SherpaOnnxDenoisedAudio>? = SherpaOnnxOnlineSpeechDenoiserFlush(impl)
2193
+ return SherpaOnnxDenoisedAudioWrapper(audio: audio)
2194
+ }
2195
+
2196
+ func reset() {
2197
+ SherpaOnnxOnlineSpeechDenoiserReset(impl)
2198
+ }
2199
+
2200
+ var sampleRate: Int {
2201
+ return Int(SherpaOnnxOnlineSpeechDenoiserGetSampleRate(impl))
2202
+ }
2203
+
2204
+ var frameShiftInSamples: Int {
2205
+ return Int(SherpaOnnxOnlineSpeechDenoiserGetFrameShiftInSamples(impl))
2206
+ }
2207
+ }
2208
+
2209
+ func getSherpaOnnxVersion() -> String {
2210
+ return String(cString: SherpaOnnxGetVersionStr())
2211
+ }
2212
+
2213
+ func getSherpaOnnxGitSha1() -> String {
2214
+ return String(cString: SherpaOnnxGetGitSha1())
2215
+ }
2216
+
2217
+ func getSherpaOnnxGitDate() -> String {
2218
+ return String(cString: SherpaOnnxGetGitDate())
2219
+ }
2220
+
2221
+ // ---------------------------
2222
+ // Source separation
2223
+ // ---------------------------
2224
+
2225
+ struct AudioData {
2226
+ private enum Storage {
2227
+ case owned([Float])
2228
+ case wrapped(ManagedWave)
2229
+ }
2230
+
2231
+ private class ManagedWave {
2232
+ let pointer: UnsafePointer<SherpaOnnxMultiChannelWave>
2233
+ init(_ p: UnsafePointer<SherpaOnnxMultiChannelWave>) {
2234
+ pointer = p
2235
+ }
2236
+
2237
+ deinit { SherpaOnnxFreeMultiChannelWave(pointer) }
2238
+ }
2239
+
2240
+ private let storage: Storage
2241
+ let channelCount: Int
2242
+ let samplesPerChannel: Int
2243
+ let sampleRate: Int
2244
+
2245
+ init(samples: [Float], channelCount: Int, sampleRate: Int) {
2246
+ storage = .owned(samples)
2247
+ self.channelCount = channelCount
2248
+ self.sampleRate = sampleRate
2249
+ samplesPerChannel = channelCount > 0 ? samples.count / channelCount : 0
2250
+ }
2251
+
2252
+ init?(filename: String) {
2253
+ guard let ptr = SherpaOnnxReadWaveMultiChannel(filename) else { return nil }
2254
+ storage = .wrapped(ManagedWave(ptr))
2255
+ channelCount = Int(ptr.pointee.num_channels)
2256
+ samplesPerChannel = Int(ptr.pointee.num_samples)
2257
+ sampleRate = Int(ptr.pointee.sample_rate)
2258
+ }
2259
+
2260
+ func withUnsafeBufferPointer<R>(_ body: (UnsafeBufferPointer<Float>) -> R) -> R {
2261
+ switch storage {
2262
+ case let .owned(array):
2263
+ return array.withUnsafeBufferPointer(body)
2264
+ case let .wrapped(managed):
2265
+ let total = Int(managed.pointer.pointee.num_channels * managed.pointer.pointee.num_samples)
2266
+ // Ensure we start from the first channel's pointer
2267
+ return body(UnsafeBufferPointer(start: managed.pointer.pointee.samples[0], count: total))
2268
+ }
2269
+ }
2270
+
2271
+ @discardableResult
2272
+ func save(to filename: String) -> Bool {
2273
+ return withUnsafeBufferPointer { buf in
2274
+ guard let base = buf.baseAddress else { return false }
2275
+ // FIX: Explicitly type the array as Optional pointers to match C 'float* const*'
2276
+ var ptrs: [UnsafePointer<Float>?] = (0 ..< channelCount).map { base + ($0 * samplesPerChannel) }
2277
+
2278
+ return SherpaOnnxWriteWaveMultiChannel(
2279
+ &ptrs,
2280
+ Int32(samplesPerChannel),
2281
+ Int32(sampleRate),
2282
+ Int32(channelCount),
2283
+ filename
2284
+ ) == 1
2285
+ }
2286
+ }
2287
+ }
2288
+
2289
+ struct SourceSeparationConfig {
2290
+ struct Spleeter {
2291
+ var vocals: String
2292
+ var accompaniment: String
2293
+ }
2294
+
2295
+ struct Uvr { var model: String }
2296
+
2297
+ var spleeter: Spleeter?
2298
+ var uvr: Uvr?
2299
+ var numThreads: Int = 1
2300
+ var debug: Bool = false
2301
+ var provider: String = "cpu"
2302
+
2303
+ func withCConfig<R>(_ body: (UnsafePointer<SherpaOnnxOfflineSourceSeparationConfig>) -> R) -> R {
2304
+ var cConfig = SherpaOnnxOfflineSourceSeparationConfig()
2305
+ cConfig.model.num_threads = Int32(numThreads)
2306
+ cConfig.model.debug = debug ? 1 : 0
2307
+
2308
+ var s: [String: [Int8]] = [:]
2309
+ func b(_ k: String, _ v: String?) -> UnsafePointer<Int8>? {
2310
+ guard let v = v else { return nil }
2311
+ s[k] = Array(v.utf8CString)
2312
+ return s[k]!.withUnsafeBufferPointer { $0.baseAddress }
2313
+ }
2314
+
2315
+ cConfig.model.provider = b("provider", provider)
2316
+ cConfig.model.spleeter.vocals = b("spleeter.vocals", spleeter?.vocals)
2317
+ cConfig.model.spleeter.accompaniment = b("spleeter.accompaniment", spleeter?.accompaniment)
2318
+ cConfig.model.uvr.model = b("uvr.model", uvr?.model)
2319
+
2320
+ return body(&cConfig)
2321
+ }
2322
+ }
2323
+
2324
+ class SourceSeparator {
2325
+ private var engine: OpaquePointer?
2326
+
2327
+ init?(config: SourceSeparationConfig) {
2328
+ engine = config.withCConfig { SherpaOnnxCreateOfflineSourceSeparation($0) }
2329
+
2330
+ if engine == nil { return nil }
2331
+ }
2332
+
2333
+ deinit {
2334
+ if let e = engine {
2335
+ SherpaOnnxDestroyOfflineSourceSeparation(e)
2336
+ }
2337
+ }
2338
+
2339
+ func process(buffer: AudioData) -> [AudioData]? {
2340
+ guard let engine = engine else { return nil }
2341
+
2342
+ return buffer.withUnsafeBufferPointer { flatBuf in
2343
+ guard let base = flatBuf.baseAddress else { return nil }
2344
+ var ptrs: [UnsafePointer<Float>?] = (0 ..< buffer.channelCount).map {
2345
+ base + ($0 * buffer.samplesPerChannel)
2346
+ }
2347
+
2348
+ guard
2349
+ let raw = SherpaOnnxOfflineSourceSeparationProcess(
2350
+ engine,
2351
+ &ptrs,
2352
+ Int32(buffer.channelCount),
2353
+ Int32(buffer.samplesPerChannel),
2354
+ Int32(buffer.sampleRate)
2355
+ )
2356
+ else { return nil }
2357
+
2358
+ let stemCount = Int(raw.pointee.num_stems)
2359
+ let result = (0 ..< stemCount).map { i in
2360
+ let stem = raw.pointee.stems[i]
2361
+ let chs = Int(stem.num_channels)
2362
+ let n = Int(stem.n)
2363
+ var flat = [Float](repeating: 0, count: chs * n)
2364
+
2365
+ for c in 0 ..< chs {
2366
+ if let src = stem.samples[c] {
2367
+ let offset = c * n
2368
+ flat.withUnsafeMutableBufferPointer { dest in
2369
+ let destPtr = dest.baseAddress!.advanced(by: offset)
2370
+ destPtr.initialize(from: src, count: n)
2371
+ }
2372
+ }
2373
+ }
2374
+ return AudioData(
2375
+ samples: flat, channelCount: chs, sampleRate: Int(raw.pointee.sample_rate)
2376
+ )
2377
+ }
2378
+
2379
+ SherpaOnnxDestroySourceSeparationOutput(raw)
2380
+ return result
2381
+ }
2382
+ }
2383
+ }