react-native-sherpa-onnx 0.4.0 → 0.4.1

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.
@@ -111,6 +111,46 @@ internal class SherpaOnnxTtsHelper(
111
111
  }
112
112
  }
113
113
 
114
+ /**
115
+ * libsherpa-onnx-jni looks up `invoke([F)Ljava/lang/Integer` (see sherpa-onnx `offline-tts.cc` CallCallback).
116
+ * Kotlin `Function1<*, Int>` compiles to `invoke([F)I`, so GetMethodID fails and JNI aborts.
117
+ * Using [java.lang.Integer] as the type parameter yields the boxed JVM signature the JNI expects.
118
+ * The cast is only for the Kotlin API (`generateWithCallback` still declares `Function1<FloatArray, Int>`).
119
+ */
120
+ /** Box for JNI: must be real [java.lang.Integer], not Kotlin [Int] (primitive `invoke([F)I` breaks sherpa JNI). */
121
+ @Suppress("DEPRECATION")
122
+ private fun boxForTtsJni(n: Int): java.lang.Integer = java.lang.Integer(n)
123
+
124
+ @Suppress("UNCHECKED_CAST")
125
+ private fun ttsChunkCallbackForJni(
126
+ sentenceChunkSizes: MutableList<Int>
127
+ ): kotlin.Function1<FloatArray, Int> {
128
+ val boxed =
129
+ object : kotlin.jvm.functions.Function1<FloatArray, java.lang.Integer> {
130
+ override fun invoke(chunk: FloatArray): java.lang.Integer {
131
+ sentenceChunkSizes.add(chunk.size)
132
+ return boxForTtsJni(chunk.size)
133
+ }
134
+ }
135
+ return boxed as kotlin.Function1<FloatArray, Int>
136
+ }
137
+
138
+ @Suppress("UNCHECKED_CAST")
139
+ private fun ttsStreamChunkCallbackForJni(
140
+ cancelled: AtomicBoolean,
141
+ onChunk: (FloatArray) -> Unit
142
+ ): kotlin.Function1<FloatArray, Int> {
143
+ val boxed =
144
+ object : kotlin.jvm.functions.Function1<FloatArray, java.lang.Integer> {
145
+ override fun invoke(chunk: FloatArray): java.lang.Integer {
146
+ if (cancelled.get()) return boxForTtsJni(0)
147
+ onChunk(chunk)
148
+ return boxForTtsJni(chunk.size)
149
+ }
150
+ }
151
+ return boxed as kotlin.Function1<FloatArray, Int>
152
+ }
153
+
114
154
  /** Single-thread executor for TTS init so the RN bridge thread is not blocked (avoids Inspector/dev WebSocket races in debug builds). */
115
155
  private val ttsInitExecutor = Executors.newSingleThreadExecutor()
116
156
 
@@ -453,6 +493,7 @@ internal class SherpaOnnxTtsHelper(
453
493
  }
454
494
  val sid = getSid(options)
455
495
  val speed = getSpeed(options)
496
+ val sentenceChunkSizes = mutableListOf<Int>()
456
497
  val audio = when {
457
498
  hasReferenceAudio(options) && (inst.isZipvoice || inst.isPocket) -> {
458
499
  if (inst.isZipvoice) {
@@ -467,7 +508,11 @@ internal class SherpaOnnxTtsHelper(
467
508
  }
468
509
  }
469
510
  val config = parseGenerationConfig(options) ?: GenerationConfig(speed = speed, sid = sid)
470
- inst.tts!!.generateWithConfig(text, config)
511
+ inst.tts!!.generateWithConfigAndCallback(
512
+ text,
513
+ config,
514
+ ttsChunkCallbackForJni(sentenceChunkSizes)
515
+ )
471
516
  }
472
517
  hasReferenceAudio(options) -> {
473
518
  Log.e("SherpaOnnxTts", "TTS_GENERATE_ERROR: Reference audio is not supported for this TTS model type")
@@ -485,12 +530,15 @@ internal class SherpaOnnxTtsHelper(
485
530
  )
486
531
  return
487
532
  }
488
- else -> dispatchGenerate(inst, text, sid, speed)
489
- ?: run {
533
+ else -> {
534
+ val tts = inst.tts
535
+ if (tts == null) {
490
536
  Log.e("SherpaOnnxTts", "TTS_GENERATE_ERROR: TTS not initialized")
491
537
  promise.reject("TTS_GENERATE_ERROR", "TTS not initialized")
492
538
  return
493
539
  }
540
+ tts.generateWithCallback(text, sid, speed, ttsChunkCallbackForJni(sentenceChunkSizes))
541
+ }
494
542
  }
495
543
  val map = Arguments.createMap()
496
544
  val samplesArray = Arguments.createArray()
@@ -564,18 +612,23 @@ internal class SherpaOnnxTtsHelper(
564
612
  when {
565
613
  hasReferenceAudio(options) && inst.isPocket -> {
566
614
  val config = parseGenerationConfig(options) ?: GenerationConfig(speed = speed, sid = sid)
567
- inst.tts!!.generateWithConfigAndCallback(text, config) { chunk ->
568
- if (inst.ttsStreamCancelled.get()) return@generateWithConfigAndCallback 0
569
- emitChunk(instanceId, requestId, chunk, sampleRate, 0f, false)
570
- chunk.size
571
- }
615
+ inst.tts!!.generateWithConfigAndCallback(
616
+ text,
617
+ config,
618
+ ttsStreamChunkCallbackForJni(inst.ttsStreamCancelled) { chunk ->
619
+ emitChunk(instanceId, requestId, chunk, sampleRate, 0f, false)
620
+ }
621
+ )
572
622
  }
573
623
  else -> {
574
- inst.tts!!.generateWithCallback(text, sid, speed) { chunk ->
575
- if (inst.ttsStreamCancelled.get()) return@generateWithCallback 0
576
- emitChunk(instanceId, requestId, chunk, sampleRate, 0f, false)
577
- chunk.size
578
- }
624
+ inst.tts!!.generateWithCallback(
625
+ text,
626
+ sid,
627
+ speed,
628
+ ttsStreamChunkCallbackForJni(inst.ttsStreamCancelled) { chunk ->
629
+ emitChunk(instanceId, requestId, chunk, sampleRate, 0f, false)
630
+ }
631
+ )
579
632
  }
580
633
  }
581
634
  if (!inst.ttsStreamCancelled.get()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-sherpa-onnx",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Offline Speech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD with sherpa-onnx for React NativeSpeech-to-Text with sherpa-onnx for React Native",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",