Fangjun Kuang
Committed by GitHub

Build text-to-speech engine APKs (#509)

  1 +name: apk-tts-engine
  2 +
  3 +on:
  4 + push:
  5 + branches:
  6 + - apk-tts
  7 + tags:
  8 + - '*'
  9 +
  10 + workflow_dispatch:
  11 +
  12 +concurrency:
  13 + group: apk-tts-engine-${{ github.ref }}
  14 + cancel-in-progress: true
  15 +
  16 +permissions:
  17 + contents: write
  18 +
  19 +jobs:
  20 + apk_tts_engine:
  21 + if: github.repository_owner == 'csukuangfj' || github.repository_owner == 'k2-fsa'
  22 + runs-on: ${{ matrix.os }}
  23 + name: apk for tts engine ${{ matrix.index }}/${{ matrix.total }}
  24 + strategy:
  25 + fail-fast: false
  26 + matrix:
  27 + os: [ubuntu-latest]
  28 + total: ["40"]
  29 + index: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39"]
  30 +
  31 + steps:
  32 + - uses: actions/checkout@v4
  33 + with:
  34 + fetch-depth: 0
  35 +
  36 + # https://github.com/actions/setup-java
  37 + - uses: actions/setup-java@v4
  38 + with:
  39 + distribution: 'temurin' # See 'Supported distributions' for available options
  40 + java-version: '21'
  41 +
  42 + - name: ccache
  43 + uses: hendrikmuhs/ccache-action@v1.2
  44 + with:
  45 + key: ${{ matrix.os }}-android
  46 +
  47 + - name: Display NDK HOME
  48 + shell: bash
  49 + run: |
  50 + echo "ANDROID_NDK_LATEST_HOME: ${ANDROID_NDK_LATEST_HOME}"
  51 + ls -lh ${ANDROID_NDK_LATEST_HOME}
  52 +
  53 + - name: Install Python dependencies
  54 + shell: bash
  55 + run: |
  56 + python3 -m pip install --upgrade pip jinja2 iso639-lang
  57 +
  58 + - name: Generate build script
  59 + shell: bash
  60 + run: |
  61 + cd scripts/apk
  62 +
  63 + total=${{ matrix.total }}
  64 + index=${{ matrix.index }}
  65 +
  66 + ./generate-tts-apk-script.py --total $total --index $index
  67 +
  68 + chmod +x build-apk-tts-engine.sh
  69 + mv -v ./build-apk-tts-engine.sh ../..
  70 +
  71 + - name: build APK for TTS engine
  72 + shell: bash
  73 + run: |
  74 + export CMAKE_CXX_COMPILER_LAUNCHER=ccache
  75 + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
  76 + cmake --version
  77 +
  78 + export ANDROID_NDK=$ANDROID_NDK_LATEST_HOME
  79 + ./build-apk-tts-engine.sh
  80 +
  81 + - name: Display APK for TTS engine
  82 + shell: bash
  83 + run: |
  84 + ls -lh ./apks/
  85 + du -h -d1 .
  86 +
  87 + - uses: actions/upload-artifact@v3
  88 + if: false
  89 + with:
  90 + name: tts-engine-apk
  91 + path: ./apks/*.apk
  92 +
  93 + - name: Publish to huggingface
  94 + if: true
  95 + env:
  96 + HF_TOKEN: ${{ secrets.HF_TOKEN }}
  97 + uses: nick-fields/retry@v2
  98 + with:
  99 + max_attempts: 20
  100 + timeout_seconds: 200
  101 + shell: bash
  102 + command: |
  103 + git config --global user.email "csukuangfj@gmail.com"
  104 + git config --global user.name "Fangjun Kuang"
  105 +
  106 + rm -rf huggingface
  107 + export GIT_LFS_SKIP_SMUDGE=1
  108 +
  109 + git clone https://huggingface.co/csukuangfj/sherpa-onnx-apk huggingface
  110 + cd huggingface
  111 + git fetch
  112 + git pull
  113 + git merge -m "merge remote" --ff origin main
  114 +
  115 + mkdir -p tts
  116 + cp -v ../apks/*.apk ./tts-engine/
  117 + git status
  118 + git lfs track "*.apk"
  119 + git add .
  120 + git commit -m "add more tts engine apks"
  121 + git push https://csukuangfj:$HF_TOKEN@huggingface.co/csukuangfj/sherpa-onnx-apk main
@@ -48,7 +48,7 @@ jobs: @@ -48,7 +48,7 @@ jobs:
48 - name: Install Python dependencies 48 - name: Install Python dependencies
49 shell: bash 49 shell: bash
50 run: | 50 run: |
51 - python3 -m pip install --upgrade pip jinja2 51 + python3 -m pip install --upgrade pip jinja2 iso639-lang
52 52
53 - name: Generate build script 53 - name: Generate build script
54 shell: bash 54 shell: bash
1 cmake_minimum_required(VERSION 3.13 FATAL_ERROR) 1 cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
2 project(sherpa-onnx) 2 project(sherpa-onnx)
3 3
4 -set(SHERPA_ONNX_VERSION "1.9.6") 4 +set(SHERPA_ONNX_VERSION "1.9.7")
5 5
6 # Disable warning about 6 # Disable warning about
7 # 7 #
@@ -12,11 +12,98 @@ class GetSampleText : Activity() { @@ -12,11 +12,98 @@ class GetSampleText : Activity() {
12 var result = TextToSpeech.LANG_AVAILABLE 12 var result = TextToSpeech.LANG_AVAILABLE
13 var text: String = "" 13 var text: String = ""
14 when(TtsEngine.lang) { 14 when(TtsEngine.lang) {
  15 + "ara" -> {
  16 + text = "هذا هو محرك تحويل النص إلى كلام باستخدام الجيل القادم من كالدي"
  17 + }
  18 + "cat" -> {
  19 + text = "Aquest és un motor de text a veu que utilitza Kaldi de nova generació"
  20 + }
  21 + "ces" -> {
  22 + text = "Toto je převodník textu na řeč využívající novou generaci kaldi"
  23 + }
  24 + "dan" -> {
  25 + text = "Dette er en tekst til tale-motor, der bruger næste generation af kaldi"
  26 + }
  27 + "deu" -> {
  28 + text = "Dies ist eine Text-to-Speech-Engine, die Kaldi der nächsten Generation verwendet"
  29 + }
  30 + "ell" -> {
  31 + text = "Αυτή είναι μια μηχανή κειμένου σε ομιλία που χρησιμοποιεί kaldi επόμενης γενιάς"
  32 + }
15 "eng" -> { 33 "eng" -> {
16 - text = "This is a text-to-speech engine with next generation Kaldi" 34 + text = "This is a text-to-speech engine using next generation Kaldi"
  35 + }
  36 + "fin" -> {
  37 + text = "Tämä on tekstistä puheeksi -moottori, joka käyttää seuraavan sukupolven kaldia"
  38 + }
  39 + "fra" -> {
  40 + text = "Il s'agit d'un moteur de synthèse vocale utilisant Kaldi de nouvelle génération."
  41 + }
  42 + "hun" -> {
  43 + text = "Ez egy szövegfelolvasó motor a következő generációs kaldi használatával"
  44 + }
  45 + "isl" -> {
  46 + text = "Þetta er texta í tal vél sem notar næstu kynslóð kaldi"
  47 + }
  48 + "ita" -> {
  49 + text = "Questo è un motore di sintesi vocale che utilizza kaldi di nuova generazione"
  50 + }
  51 + "kat" -> {
  52 + text = "ეს არის ტექსტიდან მეტყველების ძრავა შემდეგი თაობის კალდის გამოყენებით"
  53 + }
  54 + "kaz" -> {
  55 + text = "Бұл келесі буын kaldi көмегімен мәтіннен сөйлеуге арналған қозғалтқыш"
  56 + }
  57 + "ltz" -> {
  58 + text = "Dëst ass en Text-zu-Speech-Motor mat der nächster Generatioun Kaldi"
  59 + }
  60 + "nep" -> {
  61 + text = "यो अर्को पुस्ता काल्डी प्रयोग गरेर स्पीच इन्जिनको पाठ हो"
  62 + }
  63 + "nld" -> {
  64 + text = "Dit is een tekst-naar-spraak-engine die gebruik maakt van Kaldi van de volgende generatie"
  65 + }
  66 + "nor" -> {
  67 + text = "Dette er en tekst til tale-motor som bruker neste generasjons kaldi"
  68 + }
  69 + "pol" -> {
  70 + text = "Jest to silnik syntezatora mowy wykorzystujący Kaldi nowej generacji"
  71 + }
  72 + "por" -> {
  73 + text = "Este é um mecanismo de conversão de texto em fala usando Kaldi de próxima geração"
  74 + }
  75 + "ron" -> {
  76 + text = "Acesta este un motor text to speech care folosește generația următoare de kadi"
  77 + }
  78 + "rus" -> {
  79 + text = "Это движок преобразования текста в речь, использующий Kaldi следующего поколения."
  80 + }
  81 + "slk" -> {
  82 + text = "Toto je nástroj na prevod textu na reč využívajúci kaldi novej generácie"
  83 + }
  84 + "spa" -> {
  85 + text = "Este es un motor de texto a voz que utiliza kaldi de próxima generación."
  86 + }
  87 + "srp" -> {
  88 + text = "Ово је механизам за претварање текста у говор који користи калди следеће генерације"
  89 + }
  90 + "swa" -> {
  91 + text = "Haya ni maandishi kwa injini ya hotuba kwa kutumia kizazi kijacho kaldi"
  92 + }
  93 + "swe" -> {
  94 + text = "Detta är en text till tal-motor som använder nästa generations kaldi"
  95 + }
  96 + "tur" -> {
  97 + text = "Bu, yeni nesil kaldi'yi kullanan bir metinden konuşmaya motorudur"
  98 + }
  99 + "ukr" -> {
  100 + text = "Це механізм перетворення тексту на мовлення, який використовує kaldi нового покоління"
  101 + }
  102 + "vie" -> {
  103 + 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"
17 } 104 }
18 "zho", "cmn" -> { 105 "zho", "cmn" -> {
19 - text = "使用新一代 Kaldi 进行语音合成" 106 + text = "使用新一代卡尔迪的语音合成引擎"
20 } 107 }
21 else -> { 108 else -> {
22 result = TextToSpeech.LANG_NOT_SUPPORTED 109 result = TextToSpeech.LANG_NOT_SUPPORTED
  1 +#!/usr/bin/env bash
  2 +#
  3 +# Auto generated! Please DO NOT EDIT!
  4 +
  5 +# Please set the environment variable ANDROID_NDK
  6 +# before running this script
  7 +
  8 +# Inside the $ANDROID_NDK directory, you can find a binary ndk-build
  9 +# and some other files like the file "build/cmake/android.toolchain.cmake"
  10 +
  11 +set -ex
  12 +
  13 +log() {
  14 + # This function is from espnet
  15 + local fname=${BASH_SOURCE[1]##*/}
  16 + echo -e "$(date '+%Y-%m-%d %H:%M:%S') (${fname}:${BASH_LINENO[0]}:${FUNCNAME[1]}) $*"
  17 +}
  18 +
  19 +SHERPA_ONNX_VERSION=$(grep "SHERPA_ONNX_VERSION" ./CMakeLists.txt | cut -d " " -f 2 | cut -d '"' -f 2)
  20 +
  21 +log "Building TTS engine APK for sherpa-onnx v${SHERPA_ONNX_VERSION}"
  22 +
  23 +log "====================arm64-v8a================="
  24 +./build-android-arm64-v8a.sh
  25 +log "====================armv7-eabi================"
  26 +./build-android-armv7-eabi.sh
  27 +log "====================x86-64===================="
  28 +./build-android-x86-64.sh
  29 +log "====================x86===================="
  30 +./build-android-x86.sh
  31 +
  32 +mkdir -p apks
  33 +
  34 +{% for tts_model in tts_model_list %}
  35 +pushd ./android/SherpaOnnxTtsEngine/app/src/main/assets/
  36 +model_dir={{ tts_model.model_dir }}
  37 +model_name={{ tts_model.model_name }}
  38 +lang={{ tts_model.lang }}
  39 +lang_iso_639_3={{ tts_model.lang_iso_639_3 }}
  40 +
  41 +wget -qq https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/$model_dir.tar.bz2
  42 +tar xf $model_dir.tar.bz2
  43 +rm $model_dir.tar.bz2
  44 +
  45 +popd
  46 +# Now we are at the project root directory
  47 +
  48 +git checkout .
  49 +pushd android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine
  50 +sed -i.bak s/"modelDir = null"/"modelDir = \"$model_dir\""/ ./TtsEngine.kt
  51 +sed -i.bak s/"modelName = null"/"modelName = \"$model_name\""/ ./TtsEngine.kt
  52 +sed -i.bak s/"lang = null"/"lang = \"$lang_iso_639_3\""/ ./TtsEngine.kt
  53 +
  54 +{% if tts_model.rule_fsts %}
  55 + rule_fsts={{ tts_model.rule_fsts }}
  56 + sed -i.bak s%"ruleFsts = null"%"ruleFsts = \"$rule_fsts\""% ./TtsEngine.kt
  57 +{% endif %}
  58 +
  59 +{% if tts_model.data_dir %}
  60 + data_dir={{ tts_model.data_dir }}
  61 + sed -i.bak s%"dataDir = null"%"dataDir = \"$data_dir\""% ./TtsEngine.kt
  62 +{% else %}
  63 + sed -i.bak s/"lexicon = null"/"lexicon = \"lexicon.txt\""/ ./TtsEngine.kt
  64 +{% endif %}
  65 +
  66 +git diff
  67 +popd
  68 +
  69 +for arch in arm64-v8a armeabi-v7a x86_64 x86; do
  70 + log "------------------------------------------------------------"
  71 + log "build tts apk for $arch"
  72 + log "------------------------------------------------------------"
  73 + src_arch=$arch
  74 + if [ $arch == "armeabi-v7a" ]; then
  75 + src_arch=armv7-eabi
  76 + elif [ $arch == "x86_64" ]; then
  77 + src_arch=x86-64
  78 + fi
  79 +
  80 + ls -lh ./build-android-$src_arch/install/lib/*.so
  81 +
  82 + cp -v ./build-android-$src_arch/install/lib/*.so ./android/SherpaOnnxTtsEngine/app/src/main/jniLibs/$arch/
  83 +
  84 + pushd ./android/SherpaOnnxTtsEngine
  85 + ./gradlew build
  86 + popd
  87 +
  88 + mv android/SherpaOnnxTtsEngine/app/build/outputs/apk/debug/app-debug.apk ./apks/sherpa-onnx-${SHERPA_ONNX_VERSION}-$arch-$lang-tts-engine-$model_dir.apk
  89 + ls -lh apks
  90 + rm -v ./android/SherpaOnnxTtsEngine/app/src/main/jniLibs/$arch/*.so
  91 +done
  92 +
  93 +rm -rf ./android/SherpaOnnxTtsEngine/app/src/main/assets/$model_dir
  94 +{% endfor %}
  95 +
  96 +git checkout .
  97 +
  98 +ls -lh apks/
@@ -18,7 +18,7 @@ log() { @@ -18,7 +18,7 @@ log() {
18 18
19 SHERPA_ONNX_VERSION=$(grep "SHERPA_ONNX_VERSION" ./CMakeLists.txt | cut -d " " -f 2 | cut -d '"' -f 2) 19 SHERPA_ONNX_VERSION=$(grep "SHERPA_ONNX_VERSION" ./CMakeLists.txt | cut -d " " -f 2 | cut -d '"' -f 2)
20 20
21 -log "Building APK for sherpa-onnx v${SHERPA_ONNX_VERSION}" 21 +log "Building TTS APK for sherpa-onnx v${SHERPA_ONNX_VERSION}"
22 22
23 log "====================arm64-v8a=================" 23 log "====================arm64-v8a================="
24 ./build-android-arm64-v8a.sh 24 ./build-android-arm64-v8a.sh
@@ -6,6 +6,9 @@ from typing import List, Optional @@ -6,6 +6,9 @@ from typing import List, Optional
6 6
7 import jinja2 7 import jinja2
8 8
  9 +# pip install iso639-lang
  10 +from iso639 import Lang
  11 +
9 12
10 def get_args(): 13 def get_args():
11 parser = argparse.ArgumentParser() 14 parser = argparse.ArgumentParser()
@@ -31,6 +34,12 @@ class TtsModel: @@ -31,6 +34,12 @@ class TtsModel:
31 lang: str = "" # en, zh, fr, de, etc. 34 lang: str = "" # en, zh, fr, de, etc.
32 rule_fsts: Optional[List[str]] = None 35 rule_fsts: Optional[List[str]] = None
33 data_dir: Optional[str] = None 36 data_dir: Optional[str] = None
  37 + lang_iso_639_3: str = ""
  38 +
  39 +
  40 +def convert_lang_to_iso_639_3(models: List[TtsModel]):
  41 + for m in models:
  42 + m.lang_iso_639_3 = Lang(m.lang).pt3
34 43
35 44
36 def get_coqui_models() -> List[TtsModel]: 45 def get_coqui_models() -> List[TtsModel]:
@@ -234,15 +243,12 @@ def main(): @@ -234,15 +243,12 @@ def main():
234 index = args.index 243 index = args.index
235 total = args.total 244 total = args.total
236 assert 0 <= index < total, (index, total) 245 assert 0 <= index < total, (index, total)
237 - environment = jinja2.Environment()  
238 - with open("./build-apk-tts.sh.in") as f:  
239 - s = f.read()  
240 - template = environment.from_string(s)  
241 d = dict() 246 d = dict()
242 247
243 all_model_list = get_vits_models() 248 all_model_list = get_vits_models()
244 all_model_list += get_piper_models() 249 all_model_list += get_piper_models()
245 all_model_list += get_coqui_models() 250 all_model_list += get_coqui_models()
  251 + convert_lang_to_iso_639_3(all_model_list)
246 252
247 num_models = len(all_model_list) 253 num_models = len(all_model_list)
248 254
@@ -262,9 +268,16 @@ def main(): @@ -262,9 +268,16 @@ def main():
262 d["tts_model_list"].append(all_model_list[s]) 268 d["tts_model_list"].append(all_model_list[s])
263 print(f"{s}/{num_models}") 269 print(f"{s}/{num_models}")
264 270
265 - s = template.render(**d)  
266 - with open("./build-apk-tts.sh", "w") as f:  
267 - print(s, file=f) 271 + filename_list = ["./build-apk-tts.sh", "./build-apk-tts-engine.sh"]
  272 + for filename in filename_list:
  273 + environment = jinja2.Environment()
  274 + with open(f"{filename}.in") as f:
  275 + s = f.read()
  276 + template = environment.from_string(s)
  277 +
  278 + s = template.render(**d)
  279 + with open(filename, "w") as f:
  280 + print(s, file=f)
268 281
269 282
270 if __name__ == "__main__": 283 if __name__ == "__main__":