Refactor the JNI interface to make it more modular and maintainable (#802)
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
package com.k2fsa.sherpa.onnx.tts.engine
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.speech.tts.TextToSpeech
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class CheckVoiceData : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val intent = Intent().apply {
|
||||
putStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES, arrayListOf(TtsEngine.lang))
|
||||
putStringArrayListExtra(
|
||||
TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES,
|
||||
arrayListOf(TtsEngine.lang)
|
||||
)
|
||||
putStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES, arrayListOf())
|
||||
}
|
||||
setResult(TextToSpeech.Engine.CHECK_VOICE_DATA_PASS, intent)
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.k2fsa.sherpa.onnx.tts.engine
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.speech.tts.TextToSpeech
|
||||
|
||||
@@ -12,120 +11,168 @@ fun getSampleText(lang: String): String {
|
||||
"ara" -> {
|
||||
text = "هذا هو محرك تحويل النص إلى كلام باستخدام الجيل القادم من كالدي"
|
||||
}
|
||||
|
||||
"ben" -> {
|
||||
text = "এটি একটি টেক্সট-টু-স্পীচ ইঞ্জিন যা পরবর্তী প্রজন্মের কালডি ব্যবহার করে"
|
||||
}
|
||||
|
||||
"bul" -> {
|
||||
text = "Това е машина за преобразуване на текст в реч, използваща Kaldi от следващо поколение"
|
||||
text =
|
||||
"Това е машина за преобразуване на текст в реч, използваща Kaldi от следващо поколение"
|
||||
}
|
||||
|
||||
"cat" -> {
|
||||
text = "Aquest és un motor de text a veu que utilitza Kaldi de nova generació"
|
||||
}
|
||||
|
||||
"ces" -> {
|
||||
text = "Toto je převodník textu na řeč využívající novou generaci kaldi"
|
||||
}
|
||||
|
||||
"dan" -> {
|
||||
text = "Dette er en tekst til tale-motor, der bruger næste generation af kaldi"
|
||||
}
|
||||
|
||||
"deu" -> {
|
||||
text = "Dies ist eine Text-to-Speech-Engine, die Kaldi der nächsten Generation verwendet"
|
||||
text =
|
||||
"Dies ist eine Text-to-Speech-Engine, die Kaldi der nächsten Generation verwendet"
|
||||
}
|
||||
|
||||
"ell" -> {
|
||||
text = "Αυτή είναι μια μηχανή κειμένου σε ομιλία που χρησιμοποιεί kaldi επόμενης γενιάς"
|
||||
}
|
||||
|
||||
"eng" -> {
|
||||
text = "This is a text-to-speech engine using next generation Kaldi"
|
||||
}
|
||||
|
||||
"est" -> {
|
||||
text = "See on teksti kõneks muutmise mootor, mis kasutab järgmise põlvkonna Kaldi"
|
||||
}
|
||||
|
||||
"fin" -> {
|
||||
text = "Tämä on tekstistä puheeksi -moottori, joka käyttää seuraavan sukupolven kaldia"
|
||||
}
|
||||
|
||||
"fra" -> {
|
||||
text = "Il s'agit d'un moteur de synthèse vocale utilisant Kaldi de nouvelle génération"
|
||||
}
|
||||
|
||||
"gle" -> {
|
||||
text = "Is inneall téacs-go-hurlabhra é seo a úsáideann Kaldi den chéad ghlúin eile"
|
||||
}
|
||||
|
||||
"hrv" -> {
|
||||
text = "Ovo je mehanizam za pretvaranje teksta u govor koji koristi Kaldi sljedeće generacije"
|
||||
text =
|
||||
"Ovo je mehanizam za pretvaranje teksta u govor koji koristi Kaldi sljedeće generacije"
|
||||
}
|
||||
|
||||
"hun" -> {
|
||||
text = "Ez egy szövegfelolvasó motor a következő generációs kaldi használatával"
|
||||
}
|
||||
|
||||
"isl" -> {
|
||||
text = "Þetta er texta í tal vél sem notar næstu kynslóð kaldi"
|
||||
}
|
||||
|
||||
"ita" -> {
|
||||
text = "Questo è un motore di sintesi vocale che utilizza kaldi di nuova generazione"
|
||||
}
|
||||
|
||||
"kat" -> {
|
||||
text = "ეს არის ტექსტიდან მეტყველების ძრავა შემდეგი თაობის კალდის გამოყენებით"
|
||||
}
|
||||
|
||||
"kaz" -> {
|
||||
text = "Бұл келесі буын kaldi көмегімен мәтіннен сөйлеуге арналған қозғалтқыш"
|
||||
}
|
||||
|
||||
"mlt" -> {
|
||||
text = "Din hija magna text-to-speech li tuża Kaldi tal-ġenerazzjoni li jmiss"
|
||||
}
|
||||
|
||||
"lav" -> {
|
||||
text = "Šis ir teksta pārvēršanas runā dzinējs, kas izmanto nākamās paaudzes Kaldi"
|
||||
}
|
||||
|
||||
"lit" -> {
|
||||
text = "Tai teksto į kalbą variklis, kuriame naudojamas naujos kartos Kaldi"
|
||||
}
|
||||
|
||||
"ltz" -> {
|
||||
text = "Dëst ass en Text-zu-Speech-Motor mat der nächster Generatioun Kaldi"
|
||||
}
|
||||
|
||||
"nep" -> {
|
||||
text = "यो अर्को पुस्ता काल्डी प्रयोग गरेर स्पीच इन्जिनको पाठ हो"
|
||||
}
|
||||
|
||||
"nld" -> {
|
||||
text = "Dit is een tekst-naar-spraak-engine die gebruik maakt van Kaldi van de volgende generatie"
|
||||
text =
|
||||
"Dit is een tekst-naar-spraak-engine die gebruik maakt van Kaldi van de volgende generatie"
|
||||
}
|
||||
|
||||
"nor" -> {
|
||||
text = "Dette er en tekst til tale-motor som bruker neste generasjons kaldi"
|
||||
}
|
||||
|
||||
"pol" -> {
|
||||
text = "Jest to silnik syntezatora mowy wykorzystujący Kaldi nowej generacji"
|
||||
}
|
||||
|
||||
"por" -> {
|
||||
text = "Este é um mecanismo de conversão de texto em fala usando Kaldi de próxima geração"
|
||||
text =
|
||||
"Este é um mecanismo de conversão de texto em fala usando Kaldi de próxima geração"
|
||||
}
|
||||
|
||||
"ron" -> {
|
||||
text = "Acesta este un motor text to speech care folosește generația următoare de kadi"
|
||||
}
|
||||
|
||||
"rus" -> {
|
||||
text = "Это движок преобразования текста в речь, использующий Kaldi следующего поколения."
|
||||
text =
|
||||
"Это движок преобразования текста в речь, использующий Kaldi следующего поколения."
|
||||
}
|
||||
|
||||
"slk" -> {
|
||||
text = "Toto je nástroj na prevod textu na reč využívajúci kaldi novej generácie"
|
||||
}
|
||||
|
||||
"slv" -> {
|
||||
text = "To je mehanizem za pretvorbo besedila v govor, ki uporablja Kaldi naslednje generacije"
|
||||
text =
|
||||
"To je mehanizem za pretvorbo besedila v govor, ki uporablja Kaldi naslednje generacije"
|
||||
}
|
||||
|
||||
"spa" -> {
|
||||
text = "Este es un motor de texto a voz que utiliza kaldi de próxima generación."
|
||||
}
|
||||
|
||||
"srp" -> {
|
||||
text = "Ово је механизам за претварање текста у говор који користи калди следеће генерације"
|
||||
text =
|
||||
"Ово је механизам за претварање текста у говор који користи калди следеће генерације"
|
||||
}
|
||||
|
||||
"swa" -> {
|
||||
text = "Haya ni maandishi kwa injini ya hotuba kwa kutumia kizazi kijacho kaldi"
|
||||
}
|
||||
|
||||
"swe" -> {
|
||||
text = "Detta är en text till tal-motor som använder nästa generations kaldi"
|
||||
}
|
||||
|
||||
"tur" -> {
|
||||
text = "Bu, yeni nesil kaldi'yi kullanan bir metinden konuşmaya motorudur"
|
||||
}
|
||||
|
||||
"ukr" -> {
|
||||
text = "Це механізм перетворення тексту на мовлення, який використовує kaldi нового покоління"
|
||||
text =
|
||||
"Це механізм перетворення тексту на мовлення, який використовує kaldi нового покоління"
|
||||
}
|
||||
|
||||
"vie" -> {
|
||||
text = "Đây là công cụ chuyển văn bản thành giọng nói sử dụng kaldi thế hệ tiếp theo"
|
||||
}
|
||||
|
||||
"zho", "cmn" -> {
|
||||
text = "使用新一代卡尔迪的语音合成引擎"
|
||||
}
|
||||
@@ -137,13 +184,13 @@ class GetSampleText : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
var result = TextToSpeech.LANG_AVAILABLE
|
||||
var text: String = getSampleText(TtsEngine.lang ?: "")
|
||||
val text: String = getSampleText(TtsEngine.lang ?: "")
|
||||
if (text.isEmpty()) {
|
||||
result = TextToSpeech.LANG_NOT_SUPPORTED
|
||||
}
|
||||
|
||||
val intent = Intent().apply{
|
||||
if(result == TextToSpeech.LANG_AVAILABLE) {
|
||||
val intent = Intent().apply {
|
||||
if (result == TextToSpeech.LANG_AVAILABLE) {
|
||||
putExtra(TextToSpeech.Engine.EXTRA_SAMPLE_TEXT, text)
|
||||
} else {
|
||||
putExtra("sampleText", text)
|
||||
|
||||
@@ -26,20 +26,16 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.k2fsa.sherpa.onnx.tts.engine.ui.theme.SherpaOnnxTtsEngineTheme
|
||||
import java.io.File
|
||||
import java.lang.NumberFormatException
|
||||
|
||||
const val TAG = "sherpa-onnx-tts-engine"
|
||||
|
||||
@@ -76,7 +72,7 @@ class MainActivity : ComponentActivity() {
|
||||
val testTextContent = getSampleText(TtsEngine.lang ?: "")
|
||||
|
||||
var testText by remember { mutableStateOf(testTextContent) }
|
||||
|
||||
|
||||
val numSpeakers = TtsEngine.tts!!.numSpeakers()
|
||||
if (numSpeakers > 1) {
|
||||
OutlinedTextField(
|
||||
@@ -88,7 +84,7 @@ class MainActivity : ComponentActivity() {
|
||||
try {
|
||||
TtsEngine.speakerId = it.toString().toInt()
|
||||
} catch (ex: NumberFormatException) {
|
||||
Log.i(TAG, "Invalid input: ${it}")
|
||||
Log.i(TAG, "Invalid input: $it")
|
||||
TtsEngine.speakerId = 0
|
||||
}
|
||||
}
|
||||
@@ -119,7 +115,7 @@ class MainActivity : ComponentActivity() {
|
||||
Button(
|
||||
modifier = Modifier.padding(20.dp),
|
||||
onClick = {
|
||||
Log.i(TAG, "Clicked, text: ${testText}")
|
||||
Log.i(TAG, "Clicked, text: $testText")
|
||||
if (testText.isBlank() || testText.isEmpty()) {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
@@ -136,7 +132,7 @@ class MainActivity : ComponentActivity() {
|
||||
val filename =
|
||||
application.filesDir.absolutePath + "/generated.wav"
|
||||
val ok =
|
||||
audio.samples.size > 0 && audio.save(filename)
|
||||
audio.samples.isNotEmpty() && audio.save(filename)
|
||||
|
||||
if (ok) {
|
||||
stopMediaPlayer()
|
||||
|
||||
@@ -4,8 +4,10 @@ import android.content.Context
|
||||
import android.content.res.AssetManager
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.k2fsa.sherpa.onnx.*
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import com.k2fsa.sherpa.onnx.OfflineTts
|
||||
import com.k2fsa.sherpa.onnx.getOfflineTtsConfig
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
@@ -21,8 +23,8 @@ object TtsEngine {
|
||||
var lang: String? = null
|
||||
|
||||
|
||||
val speedState: MutableState<Float> = mutableStateOf(1.0F)
|
||||
val speakerIdState: MutableState<Int> = mutableStateOf(0)
|
||||
val speedState: MutableState<Float> = mutableFloatStateOf(1.0F)
|
||||
val speakerIdState: MutableState<Int> = mutableIntStateOf(0)
|
||||
|
||||
var speed: Float
|
||||
get() = speedState.value
|
||||
@@ -113,15 +115,15 @@ object TtsEngine {
|
||||
|
||||
if (dataDir != null) {
|
||||
val newDir = copyDataDir(context, modelDir!!)
|
||||
modelDir = newDir + "/" + modelDir
|
||||
dataDir = newDir + "/" + dataDir
|
||||
modelDir = "$newDir/$modelDir"
|
||||
dataDir = "$newDir/$dataDir"
|
||||
assets = null
|
||||
}
|
||||
|
||||
if (dictDir != null) {
|
||||
val newDir = copyDataDir(context, modelDir!!)
|
||||
modelDir = newDir + "/" + modelDir
|
||||
dictDir = modelDir + "/" + "dict"
|
||||
modelDir = "$newDir/$modelDir"
|
||||
dictDir = "$modelDir/dict"
|
||||
ruleFsts = "$modelDir/phone.fst,$modelDir/date.fst,$modelDir/number.fst"
|
||||
assets = null
|
||||
}
|
||||
@@ -132,18 +134,18 @@ object TtsEngine {
|
||||
dictDir = dictDir ?: "",
|
||||
ruleFsts = ruleFsts ?: "",
|
||||
ruleFars = ruleFars ?: ""
|
||||
)!!
|
||||
)
|
||||
|
||||
tts = OfflineTts(assetManager = assets, config = config)
|
||||
}
|
||||
|
||||
|
||||
private fun copyDataDir(context: Context, dataDir: String): String {
|
||||
println("data dir is $dataDir")
|
||||
Log.i(TAG, "data dir is $dataDir")
|
||||
copyAssets(context, dataDir)
|
||||
|
||||
val newDataDir = context.getExternalFilesDir(null)!!.absolutePath
|
||||
println("newDataDir: $newDataDir")
|
||||
Log.i(TAG, "newDataDir: $newDataDir")
|
||||
return newDataDir
|
||||
}
|
||||
|
||||
@@ -158,12 +160,12 @@ object TtsEngine {
|
||||
val dir = File(fullPath)
|
||||
dir.mkdirs()
|
||||
for (asset in assets.iterator()) {
|
||||
val p: String = if (path == "") "" else path + "/"
|
||||
val p: String = if (path == "") "" else "$path/"
|
||||
copyAssets(context, p + asset)
|
||||
}
|
||||
}
|
||||
} catch (ex: IOException) {
|
||||
Log.e(TAG, "Failed to copy $path. ${ex.toString()}")
|
||||
Log.e(TAG, "Failed to copy $path. $ex")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +185,7 @@ object TtsEngine {
|
||||
ostream.flush()
|
||||
ostream.close()
|
||||
} catch (ex: Exception) {
|
||||
Log.e(TAG, "Failed to copy $filename, ${ex.toString()}")
|
||||
Log.e(TAG, "Failed to copy $filename, $ex")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.speech.tts.SynthesisRequest
|
||||
import android.speech.tts.TextToSpeech
|
||||
import android.speech.tts.TextToSpeechService
|
||||
import android.util.Log
|
||||
import com.k2fsa.sherpa.onnx.*
|
||||
|
||||
/*
|
||||
https://developer.android.com/reference/java/util/Locale#getISO3Language()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.k2fsa.sherpa.onnx.tts.engine
|
||||
|
||||
import android.app.Application
|
||||
import android.os.FileUtils.ProgressListener
|
||||
import android.speech.tts.TextToSpeech
|
||||
import android.speech.tts.TextToSpeech.OnInitListener
|
||||
import android.speech.tts.UtteranceProgressListener
|
||||
@@ -27,7 +26,7 @@ class TtsViewModel : ViewModel() {
|
||||
private val onInitListener = object : OnInitListener {
|
||||
override fun onInit(status: Int) {
|
||||
when (status) {
|
||||
TextToSpeech.SUCCESS -> Log.i(TAG, "Init tts succeded")
|
||||
TextToSpeech.SUCCESS -> Log.i(TAG, "Init tts succeeded")
|
||||
TextToSpeech.ERROR -> Log.i(TAG, "Init tts failed")
|
||||
else -> Log.i(TAG, "Unknown status $status")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user