Fangjun Kuang
Committed by GitHub

Add Android demo for spoken language identification using Whisper multilingual models (#783)

正在显示 60 个修改的文件 包含 1696 行增加0 行删除
  1 +name: apk-slid
  2 +
  3 +on:
  4 + push:
  5 + tags:
  6 + - '*'
  7 +
  8 + workflow_dispatch:
  9 +
  10 +concurrency:
  11 + group: apk-slid-${{ github.ref }}
  12 + cancel-in-progress: true
  13 +
  14 +permissions:
  15 + contents: write
  16 +
  17 +jobs:
  18 + apk_slid:
  19 + if: github.repository_owner == 'csukuangfj' || github.repository_owner == 'k2-fsa'
  20 + runs-on: ${{ matrix.os }}
  21 + name: apk for slid ${{ matrix.index }}/${{ matrix.total }}
  22 + strategy:
  23 + fail-fast: false
  24 + matrix:
  25 + os: [ubuntu-latest]
  26 + total: ["1"]
  27 + index: ["0"]
  28 +
  29 + steps:
  30 + - uses: actions/checkout@v4
  31 + with:
  32 + fetch-depth: 0
  33 +
  34 + # https://github.com/actions/setup-java
  35 + - uses: actions/setup-java@v4
  36 + with:
  37 + distribution: 'temurin' # See 'Supported distributions' for available options
  38 + java-version: '21'
  39 +
  40 + - name: ccache
  41 + uses: hendrikmuhs/ccache-action@v1.2
  42 + with:
  43 + key: ${{ matrix.os }}-android
  44 +
  45 + - name: Display NDK HOME
  46 + shell: bash
  47 + run: |
  48 + echo "ANDROID_NDK_LATEST_HOME: ${ANDROID_NDK_LATEST_HOME}"
  49 + ls -lh ${ANDROID_NDK_LATEST_HOME}
  50 +
  51 + - name: Install Python dependencies
  52 + shell: bash
  53 + run: |
  54 + python3 -m pip install --upgrade pip jinja2
  55 +
  56 + - name: Setup build tool version variable
  57 + shell: bash
  58 + run: |
  59 + echo "---"
  60 + ls -lh /usr/local/lib/android/
  61 + echo "---"
  62 +
  63 + ls -lh /usr/local/lib/android/sdk
  64 + echo "---"
  65 +
  66 + ls -lh /usr/local/lib/android/sdk/build-tools
  67 + echo "---"
  68 +
  69 + BUILD_TOOL_VERSION=$(ls /usr/local/lib/android/sdk/build-tools/ | tail -n 1)
  70 + echo "BUILD_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
  71 + echo "Last build tool version is: $BUILD_TOOL_VERSION"
  72 +
  73 + - name: Generate build script
  74 + shell: bash
  75 + run: |
  76 + cd scripts/apk
  77 +
  78 + total=${{ matrix.total }}
  79 + index=${{ matrix.index }}
  80 +
  81 + ./generate-slid-apk-script.py --total $total --index $index
  82 +
  83 + chmod +x build-apk-slid.sh
  84 + mv -v ./build-apk-slid.sh ../..
  85 +
  86 + - name: build APK
  87 + shell: bash
  88 + run: |
  89 + export CMAKE_CXX_COMPILER_LAUNCHER=ccache
  90 + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
  91 + cmake --version
  92 +
  93 + export ANDROID_NDK=$ANDROID_NDK_LATEST_HOME
  94 + ./build-apk-slid.sh
  95 +
  96 + - name: Display APK
  97 + shell: bash
  98 + run: |
  99 + ls -lh ./apks/
  100 + du -h -d1 .
  101 +
  102 + # https://github.com/marketplace/actions/sign-android-release
  103 + - uses: r0adkll/sign-android-release@v1
  104 + name: Sign app APK
  105 + with:
  106 + releaseDirectory: ./apks
  107 + signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}
  108 + alias: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}
  109 + keyStorePassword: ${{ secrets.ANDROID_SIGNING_KEY_STORE_PASSWORD }}
  110 + env:
  111 + BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
  112 +
  113 + - name: Display APK for slid after signing
  114 + shell: bash
  115 + run: |
  116 + ls -lh ./apks/
  117 + du -h -d1 .
  118 +
  119 + - name: Rename APK for slid after signing
  120 + shell: bash
  121 + run: |
  122 + cd apks
  123 + rm -fv signingKey.jks
  124 + rm -fv *.apk.idsig
  125 + rm -fv *-aligned.apk
  126 +
  127 + all_apks=$(ls -1 *-signed.apk)
  128 + echo "----"
  129 + echo $all_apks
  130 + echo "----"
  131 + for apk in ${all_apks[@]}; do
  132 + n=$(echo $apk | sed -e s/-signed//)
  133 + mv -v $apk $n
  134 + done
  135 +
  136 + cd ..
  137 +
  138 + ls -lh ./apks/
  139 + du -h -d1 .
  140 +
  141 + - name: Display APK after rename
  142 + shell: bash
  143 + run: |
  144 + ls -lh ./apks/
  145 + du -h -d1 .
  146 +
  147 + - name: Publish to huggingface
  148 + env:
  149 + HF_TOKEN: ${{ secrets.HF_TOKEN }}
  150 + uses: nick-fields/retry@v3
  151 + with:
  152 + max_attempts: 20
  153 + timeout_seconds: 200
  154 + shell: bash
  155 + command: |
  156 + git config --global user.email "csukuangfj@gmail.com"
  157 + git config --global user.name "Fangjun Kuang"
  158 +
  159 + rm -rf huggingface
  160 + export GIT_LFS_SKIP_SMUDGE=1
  161 +
  162 + git clone https://huggingface.co/csukuangfj/sherpa-onnx-apk huggingface
  163 + cd huggingface
  164 + git fetch
  165 + git pull
  166 + git merge -m "merge remote" --ff origin main
  167 +
  168 + mkdir -p slid
  169 + cp -v ../apks/*.apk ./slid/
  170 + git status
  171 + git lfs track "*.apk"
  172 + git add .
  173 + git commit -m "add more apks"
  174 + git push https://csukuangfj:$HF_TOKEN@huggingface.co/csukuangfj/sherpa-onnx-apk main
  1 +*.iml
  2 +.gradle
  3 +/local.properties
  4 +/.idea/caches
  5 +/.idea/libraries
  6 +/.idea/modules.xml
  7 +/.idea/workspace.xml
  8 +/.idea/navEditor.xml
  9 +/.idea/assetWizardSettings.xml
  10 +.DS_Store
  11 +/build
  12 +/captures
  13 +.externalNativeBuild
  14 +.cxx
  15 +local.properties
  1 +plugins {
  2 + id("com.android.application")
  3 + id("org.jetbrains.kotlin.android")
  4 +}
  5 +
  6 +android {
  7 + namespace = "com.k2fsa.sherpa.onnx.slid"
  8 + compileSdk = 34
  9 +
  10 + defaultConfig {
  11 + applicationId = "com.k2fsa.sherpa.onnx.slid"
  12 + minSdk = 21
  13 + targetSdk = 34
  14 + versionCode = 1
  15 + versionName = "1.0"
  16 +
  17 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
  18 + vectorDrawables {
  19 + useSupportLibrary = true
  20 + }
  21 + }
  22 +
  23 + buildTypes {
  24 + release {
  25 + isMinifyEnabled = false
  26 + proguardFiles(
  27 + getDefaultProguardFile("proguard-android-optimize.txt"),
  28 + "proguard-rules.pro"
  29 + )
  30 + }
  31 + }
  32 + compileOptions {
  33 + sourceCompatibility = JavaVersion.VERSION_1_8
  34 + targetCompatibility = JavaVersion.VERSION_1_8
  35 + }
  36 + kotlinOptions {
  37 + jvmTarget = "1.8"
  38 + }
  39 + buildFeatures {
  40 + compose = true
  41 + }
  42 + composeOptions {
  43 + kotlinCompilerExtensionVersion = "1.5.1"
  44 + }
  45 + packaging {
  46 + resources {
  47 + excludes += "/META-INF/{AL2.0,LGPL2.1}"
  48 + }
  49 + }
  50 +}
  51 +
  52 +dependencies {
  53 +
  54 + implementation("androidx.core:core-ktx:1.12.0")
  55 + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
  56 + implementation("androidx.activity:activity-compose:1.8.2")
  57 + implementation(platform("androidx.compose:compose-bom:2023.08.00"))
  58 + implementation("androidx.compose.ui:ui")
  59 + implementation("androidx.compose.ui:ui-graphics")
  60 + implementation("androidx.compose.ui:ui-tooling-preview")
  61 + implementation("androidx.compose.material3:material3")
  62 + testImplementation("junit:junit:4.13.2")
  63 + androidTestImplementation("androidx.test.ext:junit:1.1.5")
  64 + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
  65 + androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
  66 + androidTestImplementation("androidx.compose.ui:ui-test-junit4")
  67 + debugImplementation("androidx.compose.ui:ui-tooling")
  68 + debugImplementation("androidx.compose.ui:ui-test-manifest")
  69 +}
  1 +# Add project specific ProGuard rules here.
  2 +# You can control the set of applied configuration files using the
  3 +# proguardFiles setting in build.gradle.
  4 +#
  5 +# For more details, see
  6 +# http://developer.android.com/guide/developing/tools/proguard.html
  7 +
  8 +# If your project uses WebView with JS, uncomment the following
  9 +# and specify the fully qualified class name to the JavaScript interface
  10 +# class:
  11 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  12 +# public *;
  13 +#}
  14 +
  15 +# Uncomment this to preserve the line number information for
  16 +# debugging stack traces.
  17 +#-keepattributes SourceFile,LineNumberTable
  18 +
  19 +# If you keep the line number information, uncomment this to
  20 +# hide the original source file name.
  21 +#-renamesourcefileattribute SourceFile
  1 +package com.k2fsa.sherpa.onnx.slid
  2 +
  3 +import androidx.test.platform.app.InstrumentationRegistry
  4 +import androidx.test.ext.junit.runners.AndroidJUnit4
  5 +
  6 +import org.junit.Test
  7 +import org.junit.runner.RunWith
  8 +
  9 +import org.junit.Assert.*
  10 +
  11 +/**
  12 + * Instrumented test, which will execute on an Android device.
  13 + *
  14 + * See [testing documentation](http://d.android.com/tools/testing).
  15 + */
  16 +@RunWith(AndroidJUnit4::class)
  17 +class ExampleInstrumentedTest {
  18 + @Test
  19 + fun useAppContext() {
  20 + // Context of the app under test.
  21 + val appContext = InstrumentationRegistry.getInstrumentation().targetContext
  22 + assertEquals("com.k2fsa.sherpa.onnx.slid", appContext.packageName)
  23 + }
  24 +}
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:tools="http://schemas.android.com/tools">
  4 +
  5 + <uses-permission android:name="android.permission.RECORD_AUDIO" />
  6 +
  7 + <application
  8 + android:allowBackup="true"
  9 + android:dataExtractionRules="@xml/data_extraction_rules"
  10 + android:fullBackupContent="@xml/backup_rules"
  11 + android:icon="@mipmap/ic_launcher"
  12 + android:label="@string/app_name"
  13 + android:roundIcon="@mipmap/ic_launcher_round"
  14 + android:supportsRtl="true"
  15 + android:theme="@style/Theme.SherpaOnnxSpokenLanguageIdentification"
  16 + tools:targetApi="31">
  17 + <activity
  18 + android:name=".MainActivity"
  19 + android:exported="true"
  20 + android:label="@string/app_name"
  21 + android:theme="@style/Theme.SherpaOnnxSpokenLanguageIdentification">
  22 + <intent-filter>
  23 + <action android:name="android.intent.action.MAIN" />
  24 +
  25 + <category android:name="android.intent.category.LAUNCHER" />
  26 + </intent-filter>
  27 + </activity>
  28 + </application>
  29 +
  30 +</manifest>
  1 +@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
  2 +
  3 +package com.k2fsa.sherpa.onnx.slid
  4 +
  5 +import android.Manifest
  6 +import android.app.Activity
  7 +import android.content.pm.PackageManager
  8 +import android.media.AudioFormat
  9 +import android.media.AudioRecord
  10 +import android.media.MediaRecorder
  11 +import android.util.Log
  12 +import androidx.compose.foundation.ExperimentalFoundationApi
  13 +import androidx.compose.foundation.layout.Box
  14 +import androidx.compose.foundation.layout.Column
  15 +import androidx.compose.foundation.layout.PaddingValues
  16 +import androidx.compose.ui.Modifier
  17 +import androidx.compose.foundation.layout.Spacer
  18 +import androidx.compose.foundation.layout.fillMaxSize
  19 +import androidx.compose.foundation.layout.height
  20 +import androidx.compose.foundation.layout.padding
  21 +import androidx.compose.material3.Button
  22 +import androidx.compose.material3.CenterAlignedTopAppBar
  23 +import androidx.compose.material3.ExperimentalMaterial3Api
  24 +import androidx.compose.material3.MaterialTheme
  25 +import androidx.compose.material3.Scaffold
  26 +import androidx.compose.material3.Text
  27 +import androidx.compose.material3.TopAppBarDefaults
  28 +import androidx.compose.runtime.Composable
  29 +import androidx.compose.runtime.getValue
  30 +import androidx.compose.runtime.mutableStateOf
  31 +import androidx.compose.runtime.remember
  32 +import androidx.compose.runtime.setValue
  33 +import androidx.compose.ui.Alignment
  34 +import androidx.compose.ui.platform.LocalContext
  35 +import androidx.compose.ui.text.font.FontWeight
  36 +import androidx.compose.ui.unit.dp
  37 +import androidx.compose.ui.unit.sp
  38 +import androidx.core.app.ActivityCompat
  39 +import kotlin.concurrent.thread
  40 +
  41 +@Composable
  42 +fun Home() {
  43 + Scaffold(
  44 + topBar = {
  45 + CenterAlignedTopAppBar(
  46 + colors = TopAppBarDefaults.topAppBarColors(
  47 + containerColor = MaterialTheme.colorScheme.primaryContainer,
  48 + titleContentColor = MaterialTheme.colorScheme.primary,
  49 + ),
  50 + title = {
  51 + Text(
  52 + "Next-gen Kaldi: Spoken language identification",
  53 + fontWeight = FontWeight.Bold,
  54 + fontSize = 13.sp,
  55 + )
  56 + },
  57 + )
  58 + },
  59 + content = {
  60 + MyApp(it)
  61 + },
  62 + )
  63 +}
  64 +
  65 +private var audioRecord: AudioRecord? = null
  66 +private val sampleRateInHz = 16000
  67 +
  68 +@Composable
  69 +fun MyApp(padding: PaddingValues) {
  70 + val activity = LocalContext.current as Activity
  71 + var isStarted by remember { mutableStateOf(false) }
  72 + var result by remember { mutableStateOf<String>("") }
  73 +
  74 + val onButtonClick: () -> Unit = {
  75 + isStarted = !isStarted
  76 + if (isStarted) {
  77 + result = ""
  78 + if (ActivityCompat.checkSelfPermission(
  79 + activity,
  80 + Manifest.permission.RECORD_AUDIO
  81 + ) != PackageManager.PERMISSION_GRANTED
  82 + ) {
  83 + Log.i(TAG, "Recording is not allowed")
  84 + } else {
  85 + val audioSource = MediaRecorder.AudioSource.MIC
  86 + val channelConfig = AudioFormat.CHANNEL_IN_MONO
  87 + val audioFormat = AudioFormat.ENCODING_PCM_16BIT
  88 + val numBytes =
  89 + AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)
  90 +
  91 + audioRecord = AudioRecord(
  92 + audioSource,
  93 + sampleRateInHz,
  94 + AudioFormat.CHANNEL_IN_MONO,
  95 + AudioFormat.ENCODING_PCM_16BIT,
  96 + numBytes * 2 // a sample has two bytes as we are using 16-bit PCM
  97 + )
  98 +
  99 + thread(true) {
  100 + Log.i(TAG, "processing samples")
  101 + val interval = 0.1 // i.e., 100 ms
  102 + val bufferSize = (interval * sampleRateInHz).toInt() // in samples
  103 + val buffer = ShortArray(bufferSize)
  104 + val sampleList = ArrayList<FloatArray>()
  105 + audioRecord?.let {
  106 + it.startRecording()
  107 + while (isStarted) {
  108 + val ret = it.read(buffer, 0, buffer.size)
  109 + ret.let { n ->
  110 + val samples = FloatArray(n) { buffer[it] / 32768.0f }
  111 + sampleList.add(samples)
  112 + }
  113 + }
  114 + }
  115 + Log.i(TAG, "Stop recording")
  116 + Log.i(TAG, "Start recognition")
  117 + val samples = Flatten(sampleList)
  118 + val stream = Slid.slid.createStream()
  119 + stream.acceptWaveform(samples, sampleRateInHz)
  120 + val lang = Slid.slid.compute(stream)
  121 +
  122 + result = Slid.localeMap.get(lang) ?: lang
  123 +
  124 + stream.release()
  125 + }
  126 + }
  127 + }
  128 + }
  129 +
  130 + Box(
  131 + modifier = Modifier.fillMaxSize(),
  132 + contentAlignment = Alignment.TopCenter
  133 + ) {
  134 + Column(
  135 + Modifier.padding(padding),
  136 + horizontalAlignment = Alignment.CenterHorizontally,
  137 + ) {
  138 + Spacer(modifier = Modifier.height(16.dp))
  139 + Button(onClick = onButtonClick) {
  140 + if (isStarted) {
  141 + Text("Stop")
  142 + } else {
  143 + Text("Start")
  144 + }
  145 + }
  146 +
  147 + Spacer(modifier = Modifier.height(16.dp))
  148 + if (result.isNotEmpty() && result.isNotBlank()) {
  149 + Text("Detected language: $result")
  150 + }
  151 + }
  152 + }
  153 +}
  154 +
  155 +fun Flatten(sampleList: ArrayList<FloatArray>): FloatArray {
  156 + var totalSamples = 0
  157 + for (a in sampleList) {
  158 + totalSamples += a.size
  159 + }
  160 + var i = 0
  161 + val samples = FloatArray(totalSamples)
  162 + for (a in sampleList) {
  163 + for (s in a) {
  164 + samples[i] = s
  165 + i += 1
  166 + }
  167 + }
  168 + Log.i(TAG, "$i, $totalSamples")
  169 +
  170 + return samples
  171 +}
  1 +package com.k2fsa.sherpa.onnx.slid
  2 +
  3 +import android.Manifest
  4 +import android.content.pm.PackageManager
  5 +import android.os.Bundle
  6 +import android.util.Log
  7 +import android.widget.Toast
  8 +import androidx.activity.ComponentActivity
  9 +import androidx.activity.compose.setContent
  10 +import androidx.compose.foundation.layout.fillMaxSize
  11 +import androidx.compose.material3.MaterialTheme
  12 +import androidx.compose.material3.Surface
  13 +import androidx.compose.material3.Text
  14 +import androidx.compose.runtime.Composable
  15 +import androidx.compose.ui.Modifier
  16 +import androidx.compose.ui.tooling.preview.Preview
  17 +import androidx.core.app.ActivityCompat
  18 +import com.k2fsa.sherpa.onnx.SpokenLanguageIdentification
  19 +import com.k2fsa.sherpa.onnx.slid.ui.theme.SherpaOnnxSpokenLanguageIdentificationTheme
  20 +
  21 +const val TAG = "sherpa-onnx"
  22 +private const val REQUEST_RECORD_AUDIO_PERMISSION = 200
  23 +
  24 +class MainActivity : ComponentActivity() {
  25 + private val permissions: Array<String> = arrayOf(Manifest.permission.RECORD_AUDIO)
  26 +
  27 + override fun onCreate(savedInstanceState: Bundle?) {
  28 + super.onCreate(savedInstanceState)
  29 + setContent {
  30 + SpokenLanguageIdentificationApp()
  31 + }
  32 + ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION)
  33 + Slid.initSlid(this.assets)
  34 + }
  35 + @Suppress("DEPRECATION")
  36 + @Deprecated("Deprecated in Java")
  37 + override fun onRequestPermissionsResult(
  38 + requestCode: Int,
  39 + permissions: Array<out String>,
  40 + grantResults: IntArray
  41 + ) {
  42 + super.onRequestPermissionsResult(requestCode, permissions, grantResults)
  43 + val permissionToRecordAccepted = if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
  44 + grantResults[0] == PackageManager.PERMISSION_GRANTED
  45 + } else {
  46 + false
  47 + }
  48 +
  49 + if (!permissionToRecordAccepted) {
  50 + Log.e(TAG, "Audio record is disallowed")
  51 + Toast.makeText(
  52 + this,
  53 + "This App needs access to the microphone",
  54 + Toast.LENGTH_SHORT
  55 + )
  56 + .show()
  57 + finish()
  58 + }
  59 + Log.i(TAG, "Audio record is permitted")
  60 + }
  61 +}
  62 +
  63 +@Composable
  64 +fun SpokenLanguageIdentificationApp() {
  65 + SherpaOnnxSpokenLanguageIdentificationTheme {
  66 + // A surface container using the 'background' color from the theme
  67 + Surface(
  68 + modifier = Modifier.fillMaxSize(),
  69 + color = MaterialTheme.colorScheme.background
  70 + ) {
  71 + Home()
  72 + }
  73 + }
  74 +}
  1 +../../../../../../../../../../SherpaOnnxAudioTagging/app/src/main/java/com/k2fsa/sherpa/onnx/audio/tagging/OfflineStream.kt
@@ -69,3 +69,34 @@ class SpokenLanguageIdentification ( @@ -69,3 +69,34 @@ class SpokenLanguageIdentification (
69 } 69 }
70 } 70 }
71 } 71 }
  72 +// please refer to
  73 +// https://k2-fsa.github.io/sherpa/onnx/spolken-language-identification/pretrained_models.html#whisper
  74 +// to download more models
  75 +fun getSpokenLanguageIdentificationConfig(type: Int, numThreads: Int=1): SpokenLanguageIdentificationConfig? {
  76 + when (type) {
  77 + 0 -> {
  78 + val modelDir = "sherpa-onnx-whisper-tiny"
  79 + return SpokenLanguageIdentificationConfig(
  80 + whisper = SpokenLanguageIdentificationWhisperConfig(
  81 + encoder = "$modelDir/tiny-encoder.int8.onnx",
  82 + decoder = "$modelDir/tiny-decoder.int8.onnx",
  83 + ),
  84 + numThreads = numThreads,
  85 + debug = true,
  86 + )
  87 + }
  88 +
  89 + 1 -> {
  90 + val modelDir = "sherpa-onnx-whisper-base"
  91 + return SpokenLanguageIdentificationConfig(
  92 + whisper = SpokenLanguageIdentificationWhisperConfig(
  93 + encoder = "$modelDir/tiny-encoder.int8.onnx",
  94 + decoder = "$modelDir/tiny-decoder.int8.onnx",
  95 + ),
  96 + numThreads = 1,
  97 + debug = true,
  98 + )
  99 + }
  100 + }
  101 + return null
  102 +}
  1 +package com.k2fsa.sherpa.onnx.slid
  2 +
  3 +import android.content.res.AssetManager
  4 +import android.util.Log
  5 +import com.k2fsa.sherpa.onnx.SpokenLanguageIdentification
  6 +import com.k2fsa.sherpa.onnx.getSpokenLanguageIdentificationConfig
  7 +import java.util.Locale
  8 +
  9 +
  10 +object Slid {
  11 + private var _slid: SpokenLanguageIdentification? = null
  12 +
  13 + private var _localeMap = mutableMapOf<String, String>()
  14 + val slid: SpokenLanguageIdentification
  15 + get() {
  16 + return _slid!!
  17 + }
  18 + val localeMap : Map<String, String>
  19 + get() {
  20 + return _localeMap
  21 + }
  22 +
  23 + fun initSlid(assetManager: AssetManager? = null, numThreads: Int = 1) {
  24 + synchronized(this) {
  25 + if (_slid == null) {
  26 +
  27 + Log.i(TAG, "Initializing slid")
  28 + val config =
  29 + getSpokenLanguageIdentificationConfig(type = 0, numThreads = numThreads)!!
  30 + _slid = SpokenLanguageIdentification(assetManager, config)
  31 + }
  32 +
  33 + if (_localeMap.isEmpty()) {
  34 + val allLang = Locale.getISOLanguages();
  35 + for (lang in allLang) {
  36 + val locale = Locale(lang)
  37 + _localeMap[lang] = locale.displayName
  38 + }
  39 + }
  40 + }
  41 + }
  42 +}
  1 +package com.k2fsa.sherpa.onnx.slid.ui.theme
  2 +
  3 +import androidx.compose.ui.graphics.Color
  4 +
  5 +val Purple80 = Color(0xFFD0BCFF)
  6 +val PurpleGrey80 = Color(0xFFCCC2DC)
  7 +val Pink80 = Color(0xFFEFB8C8)
  8 +
  9 +val Purple40 = Color(0xFF6650a4)
  10 +val PurpleGrey40 = Color(0xFF625b71)
  11 +val Pink40 = Color(0xFF7D5260)
  1 +package com.k2fsa.sherpa.onnx.slid.ui.theme
  2 +
  3 +import android.app.Activity
  4 +import android.os.Build
  5 +import androidx.compose.foundation.isSystemInDarkTheme
  6 +import androidx.compose.material3.MaterialTheme
  7 +import androidx.compose.material3.darkColorScheme
  8 +import androidx.compose.material3.dynamicDarkColorScheme
  9 +import androidx.compose.material3.dynamicLightColorScheme
  10 +import androidx.compose.material3.lightColorScheme
  11 +import androidx.compose.runtime.Composable
  12 +import androidx.compose.runtime.SideEffect
  13 +import androidx.compose.ui.graphics.toArgb
  14 +import androidx.compose.ui.platform.LocalContext
  15 +import androidx.compose.ui.platform.LocalView
  16 +import androidx.core.view.WindowCompat
  17 +
  18 +private val DarkColorScheme = darkColorScheme(
  19 + primary = Purple80,
  20 + secondary = PurpleGrey80,
  21 + tertiary = Pink80
  22 +)
  23 +
  24 +private val LightColorScheme = lightColorScheme(
  25 + primary = Purple40,
  26 + secondary = PurpleGrey40,
  27 + tertiary = Pink40
  28 +
  29 + /* Other default colors to override
  30 + background = Color(0xFFFFFBFE),
  31 + surface = Color(0xFFFFFBFE),
  32 + onPrimary = Color.White,
  33 + onSecondary = Color.White,
  34 + onTertiary = Color.White,
  35 + onBackground = Color(0xFF1C1B1F),
  36 + onSurface = Color(0xFF1C1B1F),
  37 + */
  38 +)
  39 +
  40 +@Composable
  41 +fun SherpaOnnxSpokenLanguageIdentificationTheme(
  42 + darkTheme: Boolean = isSystemInDarkTheme(),
  43 + // Dynamic color is available on Android 12+
  44 + dynamicColor: Boolean = true,
  45 + content: @Composable () -> Unit
  46 +) {
  47 + val colorScheme = when {
  48 + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
  49 + val context = LocalContext.current
  50 + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
  51 + }
  52 +
  53 + darkTheme -> DarkColorScheme
  54 + else -> LightColorScheme
  55 + }
  56 + val view = LocalView.current
  57 + if (!view.isInEditMode) {
  58 + SideEffect {
  59 + val window = (view.context as Activity).window
  60 + window.statusBarColor = colorScheme.primary.toArgb()
  61 + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
  62 + }
  63 + }
  64 +
  65 + MaterialTheme(
  66 + colorScheme = colorScheme,
  67 + typography = Typography,
  68 + content = content
  69 + )
  70 +}
  1 +package com.k2fsa.sherpa.onnx.slid.ui.theme
  2 +
  3 +import androidx.compose.material3.Typography
  4 +import androidx.compose.ui.text.TextStyle
  5 +import androidx.compose.ui.text.font.FontFamily
  6 +import androidx.compose.ui.text.font.FontWeight
  7 +import androidx.compose.ui.unit.sp
  8 +
  9 +// Set of Material typography styles to start with
  10 +val Typography = Typography(
  11 + bodyLarge = TextStyle(
  12 + fontFamily = FontFamily.Default,
  13 + fontWeight = FontWeight.Normal,
  14 + fontSize = 16.sp,
  15 + lineHeight = 24.sp,
  16 + letterSpacing = 0.5.sp
  17 + )
  18 + /* Other default text styles to override
  19 + titleLarge = TextStyle(
  20 + fontFamily = FontFamily.Default,
  21 + fontWeight = FontWeight.Normal,
  22 + fontSize = 22.sp,
  23 + lineHeight = 28.sp,
  24 + letterSpacing = 0.sp
  25 + ),
  26 + labelSmall = TextStyle(
  27 + fontFamily = FontFamily.Default,
  28 + fontWeight = FontWeight.Medium,
  29 + fontSize = 11.sp,
  30 + lineHeight = 16.sp,
  31 + letterSpacing = 0.5.sp
  32 + )
  33 + */
  34 +)
  1 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:aapt="http://schemas.android.com/aapt"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportWidth="108"
  6 + android:viewportHeight="108">
  7 + <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
  8 + <aapt:attr name="android:fillColor">
  9 + <gradient
  10 + android:endX="85.84757"
  11 + android:endY="92.4963"
  12 + android:startX="42.9492"
  13 + android:startY="49.59793"
  14 + android:type="linear">
  15 + <item
  16 + android:color="#44000000"
  17 + android:offset="0.0" />
  18 + <item
  19 + android:color="#00000000"
  20 + android:offset="1.0" />
  21 + </gradient>
  22 + </aapt:attr>
  23 + </path>
  24 + <path
  25 + android:fillColor="#FFFFFF"
  26 + android:fillType="nonZero"
  27 + android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
  28 + android:strokeWidth="1"
  29 + android:strokeColor="#00000000" />
  30 +</vector>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportWidth="108"
  6 + android:viewportHeight="108">
  7 + <path
  8 + android:fillColor="#3DDC84"
  9 + android:pathData="M0,0h108v108h-108z" />
  10 + <path
  11 + android:fillColor="#00000000"
  12 + android:pathData="M9,0L9,108"
  13 + android:strokeWidth="0.8"
  14 + android:strokeColor="#33FFFFFF" />
  15 + <path
  16 + android:fillColor="#00000000"
  17 + android:pathData="M19,0L19,108"
  18 + android:strokeWidth="0.8"
  19 + android:strokeColor="#33FFFFFF" />
  20 + <path
  21 + android:fillColor="#00000000"
  22 + android:pathData="M29,0L29,108"
  23 + android:strokeWidth="0.8"
  24 + android:strokeColor="#33FFFFFF" />
  25 + <path
  26 + android:fillColor="#00000000"
  27 + android:pathData="M39,0L39,108"
  28 + android:strokeWidth="0.8"
  29 + android:strokeColor="#33FFFFFF" />
  30 + <path
  31 + android:fillColor="#00000000"
  32 + android:pathData="M49,0L49,108"
  33 + android:strokeWidth="0.8"
  34 + android:strokeColor="#33FFFFFF" />
  35 + <path
  36 + android:fillColor="#00000000"
  37 + android:pathData="M59,0L59,108"
  38 + android:strokeWidth="0.8"
  39 + android:strokeColor="#33FFFFFF" />
  40 + <path
  41 + android:fillColor="#00000000"
  42 + android:pathData="M69,0L69,108"
  43 + android:strokeWidth="0.8"
  44 + android:strokeColor="#33FFFFFF" />
  45 + <path
  46 + android:fillColor="#00000000"
  47 + android:pathData="M79,0L79,108"
  48 + android:strokeWidth="0.8"
  49 + android:strokeColor="#33FFFFFF" />
  50 + <path
  51 + android:fillColor="#00000000"
  52 + android:pathData="M89,0L89,108"
  53 + android:strokeWidth="0.8"
  54 + android:strokeColor="#33FFFFFF" />
  55 + <path
  56 + android:fillColor="#00000000"
  57 + android:pathData="M99,0L99,108"
  58 + android:strokeWidth="0.8"
  59 + android:strokeColor="#33FFFFFF" />
  60 + <path
  61 + android:fillColor="#00000000"
  62 + android:pathData="M0,9L108,9"
  63 + android:strokeWidth="0.8"
  64 + android:strokeColor="#33FFFFFF" />
  65 + <path
  66 + android:fillColor="#00000000"
  67 + android:pathData="M0,19L108,19"
  68 + android:strokeWidth="0.8"
  69 + android:strokeColor="#33FFFFFF" />
  70 + <path
  71 + android:fillColor="#00000000"
  72 + android:pathData="M0,29L108,29"
  73 + android:strokeWidth="0.8"
  74 + android:strokeColor="#33FFFFFF" />
  75 + <path
  76 + android:fillColor="#00000000"
  77 + android:pathData="M0,39L108,39"
  78 + android:strokeWidth="0.8"
  79 + android:strokeColor="#33FFFFFF" />
  80 + <path
  81 + android:fillColor="#00000000"
  82 + android:pathData="M0,49L108,49"
  83 + android:strokeWidth="0.8"
  84 + android:strokeColor="#33FFFFFF" />
  85 + <path
  86 + android:fillColor="#00000000"
  87 + android:pathData="M0,59L108,59"
  88 + android:strokeWidth="0.8"
  89 + android:strokeColor="#33FFFFFF" />
  90 + <path
  91 + android:fillColor="#00000000"
  92 + android:pathData="M0,69L108,69"
  93 + android:strokeWidth="0.8"
  94 + android:strokeColor="#33FFFFFF" />
  95 + <path
  96 + android:fillColor="#00000000"
  97 + android:pathData="M0,79L108,79"
  98 + android:strokeWidth="0.8"
  99 + android:strokeColor="#33FFFFFF" />
  100 + <path
  101 + android:fillColor="#00000000"
  102 + android:pathData="M0,89L108,89"
  103 + android:strokeWidth="0.8"
  104 + android:strokeColor="#33FFFFFF" />
  105 + <path
  106 + android:fillColor="#00000000"
  107 + android:pathData="M0,99L108,99"
  108 + android:strokeWidth="0.8"
  109 + android:strokeColor="#33FFFFFF" />
  110 + <path
  111 + android:fillColor="#00000000"
  112 + android:pathData="M19,29L89,29"
  113 + android:strokeWidth="0.8"
  114 + android:strokeColor="#33FFFFFF" />
  115 + <path
  116 + android:fillColor="#00000000"
  117 + android:pathData="M19,39L89,39"
  118 + android:strokeWidth="0.8"
  119 + android:strokeColor="#33FFFFFF" />
  120 + <path
  121 + android:fillColor="#00000000"
  122 + android:pathData="M19,49L89,49"
  123 + android:strokeWidth="0.8"
  124 + android:strokeColor="#33FFFFFF" />
  125 + <path
  126 + android:fillColor="#00000000"
  127 + android:pathData="M19,59L89,59"
  128 + android:strokeWidth="0.8"
  129 + android:strokeColor="#33FFFFFF" />
  130 + <path
  131 + android:fillColor="#00000000"
  132 + android:pathData="M19,69L89,69"
  133 + android:strokeWidth="0.8"
  134 + android:strokeColor="#33FFFFFF" />
  135 + <path
  136 + android:fillColor="#00000000"
  137 + android:pathData="M19,79L89,79"
  138 + android:strokeWidth="0.8"
  139 + android:strokeColor="#33FFFFFF" />
  140 + <path
  141 + android:fillColor="#00000000"
  142 + android:pathData="M29,19L29,89"
  143 + android:strokeWidth="0.8"
  144 + android:strokeColor="#33FFFFFF" />
  145 + <path
  146 + android:fillColor="#00000000"
  147 + android:pathData="M39,19L39,89"
  148 + android:strokeWidth="0.8"
  149 + android:strokeColor="#33FFFFFF" />
  150 + <path
  151 + android:fillColor="#00000000"
  152 + android:pathData="M49,19L49,89"
  153 + android:strokeWidth="0.8"
  154 + android:strokeColor="#33FFFFFF" />
  155 + <path
  156 + android:fillColor="#00000000"
  157 + android:pathData="M59,19L59,89"
  158 + android:strokeWidth="0.8"
  159 + android:strokeColor="#33FFFFFF" />
  160 + <path
  161 + android:fillColor="#00000000"
  162 + android:pathData="M69,19L69,89"
  163 + android:strokeWidth="0.8"
  164 + android:strokeColor="#33FFFFFF" />
  165 + <path
  166 + android:fillColor="#00000000"
  167 + android:pathData="M79,19L79,89"
  168 + android:strokeWidth="0.8"
  169 + android:strokeColor="#33FFFFFF" />
  170 +</vector>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 + <monochrome android:drawable="@drawable/ic_launcher_foreground" />
  6 +</adaptive-icon>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 + <monochrome android:drawable="@drawable/ic_launcher_foreground" />
  6 +</adaptive-icon>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <color name="purple_200">#FFBB86FC</color>
  4 + <color name="purple_500">#FF6200EE</color>
  5 + <color name="purple_700">#FF3700B3</color>
  6 + <color name="teal_200">#FF03DAC5</color>
  7 + <color name="teal_700">#FF018786</color>
  8 + <color name="black">#FF000000</color>
  9 + <color name="white">#FFFFFFFF</color>
  10 +</resources>
  1 +<resources>
  2 + <string name="app_name">SherpaOnnxSpokenLanguageIdentification</string>
  3 +</resources>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 +
  4 + <style name="Theme.SherpaOnnxSpokenLanguageIdentification" parent="android:Theme.Material.Light.NoActionBar" />
  5 +</resources>
  1 +<?xml version="1.0" encoding="utf-8"?><!--
  2 + Sample backup rules file; uncomment and customize as necessary.
  3 + See https://developer.android.com/guide/topics/data/autobackup
  4 + for details.
  5 + Note: This file is ignored for devices older that API 31
  6 + See https://developer.android.com/about/versions/12/backup-restore
  7 +-->
  8 +<full-backup-content>
  9 + <!--
  10 + <include domain="sharedpref" path="."/>
  11 + <exclude domain="sharedpref" path="device.xml"/>
  12 +-->
  13 +</full-backup-content>
  1 +<?xml version="1.0" encoding="utf-8"?><!--
  2 + Sample data extraction rules file; uncomment and customize as necessary.
  3 + See https://developer.android.com/about/versions/12/backup-restore#xml-changes
  4 + for details.
  5 +-->
  6 +<data-extraction-rules>
  7 + <cloud-backup>
  8 + <!-- TODO: Use <include> and <exclude> to control what is backed up.
  9 + <include .../>
  10 + <exclude .../>
  11 + -->
  12 + </cloud-backup>
  13 + <!--
  14 + <device-transfer>
  15 + <include .../>
  16 + <exclude .../>
  17 + </device-transfer>
  18 + -->
  19 +</data-extraction-rules>
  1 +package com.k2fsa.sherpa.onnx.slid
  2 +
  3 +import org.junit.Test
  4 +
  5 +import org.junit.Assert.*
  6 +
  7 +/**
  8 + * Example local unit test, which will execute on the development machine (host).
  9 + *
  10 + * See [testing documentation](http://d.android.com/tools/testing).
  11 + */
  12 +class ExampleUnitTest {
  13 + @Test
  14 + fun addition_isCorrect() {
  15 + assertEquals(4, 2 + 2)
  16 + }
  17 +}
  1 +// Top-level build file where you can add configuration options common to all sub-projects/modules.
  2 +plugins {
  3 + id("com.android.application") version "8.2.0" apply false
  4 + id("org.jetbrains.kotlin.android") version "1.9.0" apply false
  5 +}
  1 +# Project-wide Gradle settings.
  2 +# IDE (e.g. Android Studio) users:
  3 +# Gradle settings configured through the IDE *will override*
  4 +# any settings specified in this file.
  5 +# For more details on how to configure your build environment visit
  6 +# http://www.gradle.org/docs/current/userguide/build_environment.html
  7 +# Specifies the JVM arguments used for the daemon process.
  8 +# The setting is particularly useful for tweaking memory settings.
  9 +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
  10 +# When configured, Gradle will run in incubating parallel mode.
  11 +# This option should only be used with decoupled projects. More details, visit
  12 +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
  13 +# org.gradle.parallel=true
  14 +# AndroidX package structure to make it clearer which packages are bundled with the
  15 +# Android operating system, and which are packaged with your app's APK
  16 +# https://developer.android.com/topic/libraries/support-library/androidx-rn
  17 +android.useAndroidX=true
  18 +# Kotlin code style for this project: "official" or "obsolete":
  19 +kotlin.code.style=official
  20 +# Enables namespacing of each library's R class so that its R class includes only the
  21 +# resources declared in the library itself and none from the library's dependencies,
  22 +# thereby reducing the size of the R class for that library
  23 +android.nonTransitiveRClass=true
  1 +#Wed Apr 17 19:48:00 CST 2024
  2 +distributionBase=GRADLE_USER_HOME
  3 +distributionPath=wrapper/dists
  4 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
  5 +zipStoreBase=GRADLE_USER_HOME
  6 +zipStorePath=wrapper/dists
  1 +#!/usr/bin/env sh
  2 +
  3 +#
  4 +# Copyright 2015 the original author or authors.
  5 +#
  6 +# Licensed under the Apache License, Version 2.0 (the "License");
  7 +# you may not use this file except in compliance with the License.
  8 +# You may obtain a copy of the License at
  9 +#
  10 +# https://www.apache.org/licenses/LICENSE-2.0
  11 +#
  12 +# Unless required by applicable law or agreed to in writing, software
  13 +# distributed under the License is distributed on an "AS IS" BASIS,
  14 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 +# See the License for the specific language governing permissions and
  16 +# limitations under the License.
  17 +#
  18 +
  19 +##############################################################################
  20 +##
  21 +## Gradle start up script for UN*X
  22 +##
  23 +##############################################################################
  24 +
  25 +# Attempt to set APP_HOME
  26 +# Resolve links: $0 may be a link
  27 +PRG="$0"
  28 +# Need this for relative symlinks.
  29 +while [ -h "$PRG" ] ; do
  30 + ls=`ls -ld "$PRG"`
  31 + link=`expr "$ls" : '.*-> \(.*\)$'`
  32 + if expr "$link" : '/.*' > /dev/null; then
  33 + PRG="$link"
  34 + else
  35 + PRG=`dirname "$PRG"`"/$link"
  36 + fi
  37 +done
  38 +SAVED="`pwd`"
  39 +cd "`dirname \"$PRG\"`/" >/dev/null
  40 +APP_HOME="`pwd -P`"
  41 +cd "$SAVED" >/dev/null
  42 +
  43 +APP_NAME="Gradle"
  44 +APP_BASE_NAME=`basename "$0"`
  45 +
  46 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  47 +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
  48 +
  49 +# Use the maximum available, or set MAX_FD != -1 to use that value.
  50 +MAX_FD="maximum"
  51 +
  52 +warn () {
  53 + echo "$*"
  54 +}
  55 +
  56 +die () {
  57 + echo
  58 + echo "$*"
  59 + echo
  60 + exit 1
  61 +}
  62 +
  63 +# OS specific support (must be 'true' or 'false').
  64 +cygwin=false
  65 +msys=false
  66 +darwin=false
  67 +nonstop=false
  68 +case "`uname`" in
  69 + CYGWIN* )
  70 + cygwin=true
  71 + ;;
  72 + Darwin* )
  73 + darwin=true
  74 + ;;
  75 + MINGW* )
  76 + msys=true
  77 + ;;
  78 + NONSTOP* )
  79 + nonstop=true
  80 + ;;
  81 +esac
  82 +
  83 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
  84 +
  85 +
  86 +# Determine the Java command to use to start the JVM.
  87 +if [ -n "$JAVA_HOME" ] ; then
  88 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
  89 + # IBM's JDK on AIX uses strange locations for the executables
  90 + JAVACMD="$JAVA_HOME/jre/sh/java"
  91 + else
  92 + JAVACMD="$JAVA_HOME/bin/java"
  93 + fi
  94 + if [ ! -x "$JAVACMD" ] ; then
  95 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
  96 +
  97 +Please set the JAVA_HOME variable in your environment to match the
  98 +location of your Java installation."
  99 + fi
  100 +else
  101 + JAVACMD="java"
  102 + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  103 +
  104 +Please set the JAVA_HOME variable in your environment to match the
  105 +location of your Java installation."
  106 +fi
  107 +
  108 +# Increase the maximum file descriptors if we can.
  109 +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
  110 + MAX_FD_LIMIT=`ulimit -H -n`
  111 + if [ $? -eq 0 ] ; then
  112 + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
  113 + MAX_FD="$MAX_FD_LIMIT"
  114 + fi
  115 + ulimit -n $MAX_FD
  116 + if [ $? -ne 0 ] ; then
  117 + warn "Could not set maximum file descriptor limit: $MAX_FD"
  118 + fi
  119 + else
  120 + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
  121 + fi
  122 +fi
  123 +
  124 +# For Darwin, add options to specify how the application appears in the dock
  125 +if $darwin; then
  126 + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
  127 +fi
  128 +
  129 +# For Cygwin or MSYS, switch paths to Windows format before running java
  130 +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
  131 + APP_HOME=`cygpath --path --mixed "$APP_HOME"`
  132 + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
  133 +
  134 + JAVACMD=`cygpath --unix "$JAVACMD"`
  135 +
  136 + # We build the pattern for arguments to be converted via cygpath
  137 + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
  138 + SEP=""
  139 + for dir in $ROOTDIRSRAW ; do
  140 + ROOTDIRS="$ROOTDIRS$SEP$dir"
  141 + SEP="|"
  142 + done
  143 + OURCYGPATTERN="(^($ROOTDIRS))"
  144 + # Add a user-defined pattern to the cygpath arguments
  145 + if [ "$GRADLE_CYGPATTERN" != "" ] ; then
  146 + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
  147 + fi
  148 + # Now convert the arguments - kludge to limit ourselves to /bin/sh
  149 + i=0
  150 + for arg in "$@" ; do
  151 + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
  152 + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
  153 +
  154 + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
  155 + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
  156 + else
  157 + eval `echo args$i`="\"$arg\""
  158 + fi
  159 + i=`expr $i + 1`
  160 + done
  161 + case $i in
  162 + 0) set -- ;;
  163 + 1) set -- "$args0" ;;
  164 + 2) set -- "$args0" "$args1" ;;
  165 + 3) set -- "$args0" "$args1" "$args2" ;;
  166 + 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
  167 + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
  168 + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
  169 + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
  170 + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
  171 + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
  172 + esac
  173 +fi
  174 +
  175 +# Escape application args
  176 +save () {
  177 + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
  178 + echo " "
  179 +}
  180 +APP_ARGS=`save "$@"`
  181 +
  182 +# Collect all arguments for the java command, following the shell quoting and substitution rules
  183 +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
  184 +
  185 +exec "$JAVACMD" "$@"
  1 +@rem
  2 +@rem Copyright 2015 the original author or authors.
  3 +@rem
  4 +@rem Licensed under the Apache License, Version 2.0 (the "License");
  5 +@rem you may not use this file except in compliance with the License.
  6 +@rem You may obtain a copy of the License at
  7 +@rem
  8 +@rem https://www.apache.org/licenses/LICENSE-2.0
  9 +@rem
  10 +@rem Unless required by applicable law or agreed to in writing, software
  11 +@rem distributed under the License is distributed on an "AS IS" BASIS,
  12 +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +@rem See the License for the specific language governing permissions and
  14 +@rem limitations under the License.
  15 +@rem
  16 +
  17 +@if "%DEBUG%" == "" @echo off
  18 +@rem ##########################################################################
  19 +@rem
  20 +@rem Gradle startup script for Windows
  21 +@rem
  22 +@rem ##########################################################################
  23 +
  24 +@rem Set local scope for the variables with windows NT shell
  25 +if "%OS%"=="Windows_NT" setlocal
  26 +
  27 +set DIRNAME=%~dp0
  28 +if "%DIRNAME%" == "" set DIRNAME=.
  29 +set APP_BASE_NAME=%~n0
  30 +set APP_HOME=%DIRNAME%
  31 +
  32 +@rem Resolve any "." and ".." in APP_HOME to make it shorter.
  33 +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
  34 +
  35 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  36 +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
  37 +
  38 +@rem Find java.exe
  39 +if defined JAVA_HOME goto findJavaFromJavaHome
  40 +
  41 +set JAVA_EXE=java.exe
  42 +%JAVA_EXE% -version >NUL 2>&1
  43 +if "%ERRORLEVEL%" == "0" goto execute
  44 +
  45 +echo.
  46 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  47 +echo.
  48 +echo Please set the JAVA_HOME variable in your environment to match the
  49 +echo location of your Java installation.
  50 +
  51 +goto fail
  52 +
  53 +:findJavaFromJavaHome
  54 +set JAVA_HOME=%JAVA_HOME:"=%
  55 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
  56 +
  57 +if exist "%JAVA_EXE%" goto execute
  58 +
  59 +echo.
  60 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
  61 +echo.
  62 +echo Please set the JAVA_HOME variable in your environment to match the
  63 +echo location of your Java installation.
  64 +
  65 +goto fail
  66 +
  67 +:execute
  68 +@rem Setup the command line
  69 +
  70 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
  71 +
  72 +
  73 +@rem Execute Gradle
  74 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
  75 +
  76 +:end
  77 +@rem End local scope for the variables with windows NT shell
  78 +if "%ERRORLEVEL%"=="0" goto mainEnd
  79 +
  80 +:fail
  81 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
  82 +rem the _cmd.exe /c_ return code!
  83 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
  84 +exit /b 1
  85 +
  86 +:mainEnd
  87 +if "%OS%"=="Windows_NT" endlocal
  88 +
  89 +:omega
  1 +pluginManagement {
  2 + repositories {
  3 + google()
  4 + mavenCentral()
  5 + gradlePluginPortal()
  6 + }
  7 +}
  8 +dependencyResolutionManagement {
  9 + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
  10 + repositories {
  11 + google()
  12 + mavenCentral()
  13 + }
  14 +}
  15 +
  16 +rootProject.name = "SherpaOnnxSpokenLanguageIdentification"
  17 +include(":app")
  1 +../android/SherpaOnnxSpokenLanguageIdentification/app/src/main/java/com/k2fsa/sherpa/onnx/slid/SpokenLanguageIdentification.kt
  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 spoken language identification 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 model in model_list %}
  35 +pushd ./android/SherpaOnnxSpokenLanguageIdentification/app/src/main/assets/
  36 +model_name={{ model.model_name }}
  37 +short_name={{ model.short_name }}
  38 +type={{ model.idx }}
  39 +
  40 +curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/${model_name}.tar.bz2
  41 +tar xvf ${model_name}.tar.bz2
  42 +rm -rfv $model_name/*-encoder.onnx
  43 +rm -rfv $model_name/*-decoder.onnx
  44 +rm -rfv $model_name/*.py
  45 +rm -rfv $model_name/*.txt
  46 +rm -rfv $model_name/*.md
  47 +rm -rfv $model_name/test_wavs
  48 +rm -rf *.tar.bz2
  49 +ls -lh $model_name
  50 +
  51 +popd
  52 +# Now we are at the project root directory
  53 +
  54 +git checkout .
  55 +pushd android/SherpaOnnxSpokenLanguageIdentification/app/src/main/java/com/k2fsa/sherpa/onnx/slid/
  56 +sed -i.bak s/"type = 0/type = $type/" ./slid.kt
  57 +git diff
  58 +popd
  59 +
  60 +for arch in arm64-v8a armeabi-v7a x86_64 x86; do
  61 + log "------------------------------------------------------------"
  62 + log "build spoken language identification apk for $arch"
  63 + log "------------------------------------------------------------"
  64 + src_arch=$arch
  65 + if [ $arch == "armeabi-v7a" ]; then
  66 + src_arch=armv7-eabi
  67 + elif [ $arch == "x86_64" ]; then
  68 + src_arch=x86-64
  69 + fi
  70 +
  71 + ls -lh ./build-android-$src_arch/install/lib/*.so
  72 +
  73 + cp -v ./build-android-$src_arch/install/lib/*.so ./android/SherpaOnnxSpokenLanguageIdentification/app/src/main/jniLibs/$arch/
  74 +
  75 + pushd ./android/SherpaOnnxSpokenLanguageIdentification
  76 + sed -i.bak s/2048/9012/g ./gradle.properties
  77 + git diff ./gradle.properties
  78 + ./gradlew assembleRelease
  79 + popd
  80 +
  81 + mv android/SherpaOnnxSpokenLanguageIdentification/app/build/outputs/apk/release/app-release-unsigned.apk ./apks/sherpa-onnx-${SHERPA_ONNX_VERSION}-$arch-audio-tagging-$short_name.apk
  82 + ls -lh apks
  83 + rm -v ./android/SherpaOnnxSpokenLanguageIdentification/app/src/main/jniLibs/$arch/*.so
  84 +done
  85 +
  86 +rm -rf ./android/SherpaOnnxSpokenLanguageIdentification/app/src/main/assets/$model_name
  87 +{% endfor %}
  88 +
  89 +git checkout .
  90 +
  91 +ls -lh apks/
  1 +#!/usr/bin/env python3
  2 +
  3 +import argparse
  4 +from dataclasses import dataclass
  5 +from typing import List, Optional
  6 +
  7 +import jinja2
  8 +
  9 +
  10 +def get_args():
  11 + parser = argparse.ArgumentParser()
  12 + parser.add_argument(
  13 + "--total",
  14 + type=int,
  15 + default=1,
  16 + help="Number of runners",
  17 + )
  18 + parser.add_argument(
  19 + "--index",
  20 + type=int,
  21 + default=0,
  22 + help="Index of the current runner",
  23 + )
  24 + return parser.parse_args()
  25 +
  26 +
  27 +@dataclass
  28 +class SlidModel:
  29 + model_name: str
  30 + idx: int
  31 + short_name: str = ""
  32 +
  33 +
  34 +def get_models():
  35 + # see https://k2-fsa.github.io/sherpa/onnx/spolken-language-identification/pretrained_models.html#pre-trained-models
  36 + whisper_models = [
  37 + SlidModel(
  38 + model_name="sherpa-onnx-whisper-tiny",
  39 + idx=0,
  40 + short_name="whisper_tiny",
  41 + ),
  42 + ]
  43 +
  44 + return whisper_models
  45 +
  46 +
  47 +def main():
  48 + args = get_args()
  49 + index = args.index
  50 + total = args.total
  51 + assert 0 <= index < total, (index, total)
  52 +
  53 + all_model_list = get_models()
  54 +
  55 + num_models = len(all_model_list)
  56 +
  57 + num_per_runner = num_models // total
  58 + if num_per_runner <= 0:
  59 + raise ValueError(f"num_models: {num_models}, num_runners: {total}")
  60 +
  61 + start = index * num_per_runner
  62 + end = start + num_per_runner
  63 +
  64 + remaining = num_models - args.total * num_per_runner
  65 +
  66 + print(f"{index}/{total}: {start}-{end}/{num_models}")
  67 +
  68 + d = dict()
  69 + d["model_list"] = all_model_list[start:end]
  70 + if index < remaining:
  71 + s = args.total * num_per_runner + index
  72 + d["model_list"].append(all_model_list[s])
  73 + print(f"{s}/{num_models}")
  74 +
  75 + filename_list = [
  76 + "./build-apk-slid.sh",
  77 + ]
  78 + for filename in filename_list:
  79 + environment = jinja2.Environment()
  80 + with open(f"{filename}.in") as f:
  81 + s = f.read()
  82 + template = environment.from_string(s)
  83 +
  84 + s = template.render(**d)
  85 + with open(filename, "w") as f:
  86 + print(s, file=f)
  87 +
  88 +
  89 +if __name__ == "__main__":
  90 + main()
@@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
4 4
5 #include "sherpa-onnx/csrc/audio-tagging-impl.h" 5 #include "sherpa-onnx/csrc/audio-tagging-impl.h"
6 6
  7 +#include <memory>
  8 +
7 #if __ANDROID_API__ >= 9 9 #if __ANDROID_API__ >= 9
8 #include "android/asset_manager.h" 10 #include "android/asset_manager.h"
9 #include "android/asset_manager_jni.h" 11 #include "android/asset_manager_jni.h"
@@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
4 4
5 #include "sherpa-onnx/csrc/audio-tagging.h" 5 #include "sherpa-onnx/csrc/audio-tagging.h"
6 6
  7 +#include <string>
  8 +
7 #if __ANDROID_API__ >= 9 9 #if __ANDROID_API__ >= 9
8 #include "android/asset_manager.h" 10 #include "android/asset_manager.h"
9 #include "android/asset_manager_jni.h" 11 #include "android/asset_manager_jni.h"
@@ -70,6 +70,23 @@ class OfflineWhisperModel::Impl { @@ -70,6 +70,23 @@ class OfflineWhisperModel::Impl {
70 InitDecoder(buf.data(), buf.size()); 70 InitDecoder(buf.data(), buf.size());
71 } 71 }
72 } 72 }
  73 +
  74 + Impl(AAssetManager *mgr, const SpokenLanguageIdentificationConfig &config)
  75 + : lid_config_(config),
  76 + env_(ORT_LOGGING_LEVEL_ERROR),
  77 + sess_opts_(GetSessionOptions(config)),
  78 + allocator_{} {
  79 + debug_ = config_.debug;
  80 + {
  81 + auto buf = ReadFile(mgr, config.whisper.encoder);
  82 + InitEncoder(buf.data(), buf.size());
  83 + }
  84 +
  85 + {
  86 + auto buf = ReadFile(mgr, config.whisper.decoder);
  87 + InitDecoder(buf.data(), buf.size());
  88 + }
  89 + }
73 #endif 90 #endif
74 91
75 std::pair<Ort::Value, Ort::Value> ForwardEncoder(Ort::Value features) { 92 std::pair<Ort::Value, Ort::Value> ForwardEncoder(Ort::Value features) {
@@ -326,6 +343,11 @@ OfflineWhisperModel::OfflineWhisperModel( @@ -326,6 +343,11 @@ OfflineWhisperModel::OfflineWhisperModel(
326 OfflineWhisperModel::OfflineWhisperModel(AAssetManager *mgr, 343 OfflineWhisperModel::OfflineWhisperModel(AAssetManager *mgr,
327 const OfflineModelConfig &config) 344 const OfflineModelConfig &config)
328 : impl_(std::make_unique<Impl>(mgr, config)) {} 345 : impl_(std::make_unique<Impl>(mgr, config)) {}
  346 +
  347 +OfflineWhisperModel::OfflineWhisperModel(
  348 + AAssetManager *mgr, const SpokenLanguageIdentificationConfig &config)
  349 + : impl_(std::make_unique<Impl>(mgr, config)) {}
  350 +
329 #endif 351 #endif
330 352
331 OfflineWhisperModel::~OfflineWhisperModel() = default; 353 OfflineWhisperModel::~OfflineWhisperModel() = default;
@@ -31,6 +31,8 @@ class OfflineWhisperModel { @@ -31,6 +31,8 @@ class OfflineWhisperModel {
31 31
32 #if __ANDROID_API__ >= 9 32 #if __ANDROID_API__ >= 9
33 OfflineWhisperModel(AAssetManager *mgr, const OfflineModelConfig &config); 33 OfflineWhisperModel(AAssetManager *mgr, const OfflineModelConfig &config);
  34 + OfflineWhisperModel(AAssetManager *mgr,
  35 + const SpokenLanguageIdentificationConfig &config);
34 #endif 36 #endif
35 37
36 ~OfflineWhisperModel(); 38 ~OfflineWhisperModel();
@@ -5,6 +5,11 @@ @@ -5,6 +5,11 @@
5 5
6 #include <memory> 6 #include <memory>
7 7
  8 +#if __ANDROID_API__ >= 9
  9 +#include "android/asset_manager.h"
  10 +#include "android/asset_manager_jni.h"
  11 +#endif
  12 +
8 #include "sherpa-onnx/csrc/macros.h" 13 #include "sherpa-onnx/csrc/macros.h"
9 #include "sherpa-onnx/csrc/onnx-utils.h" 14 #include "sherpa-onnx/csrc/onnx-utils.h"
10 #include "sherpa-onnx/csrc/spoken-language-identification-whisper-impl.h" 15 #include "sherpa-onnx/csrc/spoken-language-identification-whisper-impl.h"
@@ -85,4 +90,34 @@ SpokenLanguageIdentificationImpl::Create( @@ -85,4 +90,34 @@ SpokenLanguageIdentificationImpl::Create(
85 return nullptr; 90 return nullptr;
86 } 91 }
87 92
  93 +#if __ANDROID_API__ >= 9
  94 +std::unique_ptr<SpokenLanguageIdentificationImpl>
  95 +SpokenLanguageIdentificationImpl::Create(
  96 + AAssetManager *mgr, const SpokenLanguageIdentificationConfig &config) {
  97 + ModelType model_type = ModelType::kUnknown;
  98 + {
  99 + if (config.whisper.encoder.empty()) {
  100 + SHERPA_ONNX_LOGE("Only whisper models are supported at present");
  101 + exit(-1);
  102 + }
  103 + auto buffer = ReadFile(mgr, config.whisper.encoder);
  104 +
  105 + model_type = GetModelType(buffer.data(), buffer.size(), config.debug);
  106 + }
  107 +
  108 + switch (model_type) {
  109 + case ModelType::kWhisper:
  110 + return std::make_unique<SpokenLanguageIdentificationWhisperImpl>(mgr,
  111 + config);
  112 + case ModelType::kUnknown:
  113 + SHERPA_ONNX_LOGE(
  114 + "Unknown model type for spoken language identification!");
  115 + return nullptr;
  116 + }
  117 +
  118 + // unreachable code
  119 + return nullptr;
  120 +}
  121 +#endif
  122 +
88 } // namespace sherpa_onnx 123 } // namespace sherpa_onnx
@@ -7,6 +7,11 @@ @@ -7,6 +7,11 @@
7 #include <memory> 7 #include <memory>
8 #include <string> 8 #include <string>
9 9
  10 +#if __ANDROID_API__ >= 9
  11 +#include "android/asset_manager.h"
  12 +#include "android/asset_manager_jni.h"
  13 +#endif
  14 +
10 #include "sherpa-onnx/csrc/spoken-language-identification.h" 15 #include "sherpa-onnx/csrc/spoken-language-identification.h"
11 16
12 namespace sherpa_onnx { 17 namespace sherpa_onnx {
@@ -18,6 +23,11 @@ class SpokenLanguageIdentificationImpl { @@ -18,6 +23,11 @@ class SpokenLanguageIdentificationImpl {
18 static std::unique_ptr<SpokenLanguageIdentificationImpl> Create( 23 static std::unique_ptr<SpokenLanguageIdentificationImpl> Create(
19 const SpokenLanguageIdentificationConfig &config); 24 const SpokenLanguageIdentificationConfig &config);
20 25
  26 +#if __ANDROID_API__ >= 9
  27 + static std::unique_ptr<SpokenLanguageIdentificationImpl> Create(
  28 + AAssetManager *mgr, const SpokenLanguageIdentificationConfig &config);
  29 +#endif
  30 +
21 virtual std::unique_ptr<OfflineStream> CreateStream() const = 0; 31 virtual std::unique_ptr<OfflineStream> CreateStream() const = 0;
22 32
23 virtual std::string Compute(OfflineStream *s) const = 0; 33 virtual std::string Compute(OfflineStream *s) const = 0;
@@ -11,6 +11,11 @@ @@ -11,6 +11,11 @@
11 #include <utility> 11 #include <utility>
12 #include <vector> 12 #include <vector>
13 13
  14 +#if __ANDROID_API__ >= 9
  15 +#include "android/asset_manager.h"
  16 +#include "android/asset_manager_jni.h"
  17 +#endif
  18 +
14 #include "sherpa-onnx/csrc/offline-whisper-model.h" 19 #include "sherpa-onnx/csrc/offline-whisper-model.h"
15 #include "sherpa-onnx/csrc/spoken-language-identification-impl.h" 20 #include "sherpa-onnx/csrc/spoken-language-identification-impl.h"
16 #include "sherpa-onnx/csrc/transpose.h" 21 #include "sherpa-onnx/csrc/transpose.h"
@@ -26,6 +31,15 @@ class SpokenLanguageIdentificationWhisperImpl @@ -26,6 +31,15 @@ class SpokenLanguageIdentificationWhisperImpl
26 Check(); 31 Check();
27 } 32 }
28 33
  34 +#if __ANDROID_API__ >= 9
  35 + SpokenLanguageIdentificationWhisperImpl(
  36 + AAssetManager *mgr, const SpokenLanguageIdentificationConfig &config)
  37 + : config_(config),
  38 + model_(std::make_unique<OfflineWhisperModel>(mgr, config)) {
  39 + Check();
  40 + }
  41 +#endif
  42 +
29 std::unique_ptr<OfflineStream> CreateStream() const override { 43 std::unique_ptr<OfflineStream> CreateStream() const override {
30 return std::make_unique<OfflineStream>(WhisperTag{}); 44 return std::make_unique<OfflineStream>(WhisperTag{});
31 } 45 }
@@ -6,6 +6,11 @@ @@ -6,6 +6,11 @@
6 6
7 #include <string> 7 #include <string>
8 8
  9 +#if __ANDROID_API__ >= 9
  10 +#include "android/asset_manager.h"
  11 +#include "android/asset_manager_jni.h"
  12 +#endif
  13 +
9 #include "sherpa-onnx/csrc/file-utils.h" 14 #include "sherpa-onnx/csrc/file-utils.h"
10 #include "sherpa-onnx/csrc/macros.h" 15 #include "sherpa-onnx/csrc/macros.h"
11 #include "sherpa-onnx/csrc/spoken-language-identification-impl.h" 16 #include "sherpa-onnx/csrc/spoken-language-identification-impl.h"
@@ -103,6 +108,12 @@ SpokenLanguageIdentification::SpokenLanguageIdentification( @@ -103,6 +108,12 @@ SpokenLanguageIdentification::SpokenLanguageIdentification(
103 const SpokenLanguageIdentificationConfig &config) 108 const SpokenLanguageIdentificationConfig &config)
104 : impl_(SpokenLanguageIdentificationImpl::Create(config)) {} 109 : impl_(SpokenLanguageIdentificationImpl::Create(config)) {}
105 110
  111 +#if __ANDROID_API__ >= 9
  112 +SpokenLanguageIdentification::SpokenLanguageIdentification(
  113 + AAssetManager *mgr, const SpokenLanguageIdentificationConfig &config)
  114 + : impl_(SpokenLanguageIdentificationImpl::Create(mgr, config)) {}
  115 +#endif
  116 +
106 SpokenLanguageIdentification::~SpokenLanguageIdentification() = default; 117 SpokenLanguageIdentification::~SpokenLanguageIdentification() = default;
107 118
108 std::unique_ptr<OfflineStream> SpokenLanguageIdentification::CreateStream() 119 std::unique_ptr<OfflineStream> SpokenLanguageIdentification::CreateStream()
@@ -7,6 +7,11 @@ @@ -7,6 +7,11 @@
7 #include <memory> 7 #include <memory>
8 #include <string> 8 #include <string>
9 9
  10 +#if __ANDROID_API__ >= 9
  11 +#include "android/asset_manager.h"
  12 +#include "android/asset_manager_jni.h"
  13 +#endif
  14 +
10 #include "sherpa-onnx/csrc/offline-stream.h" 15 #include "sherpa-onnx/csrc/offline-stream.h"
11 #include "sherpa-onnx/csrc/parse-options.h" 16 #include "sherpa-onnx/csrc/parse-options.h"
12 17
@@ -70,6 +75,11 @@ class SpokenLanguageIdentification { @@ -70,6 +75,11 @@ class SpokenLanguageIdentification {
70 explicit SpokenLanguageIdentification( 75 explicit SpokenLanguageIdentification(
71 const SpokenLanguageIdentificationConfig &config); 76 const SpokenLanguageIdentificationConfig &config);
72 77
  78 +#if __ANDROID_API__ >= 9
  79 + SpokenLanguageIdentification(
  80 + AAssetManager *mgr, const SpokenLanguageIdentificationConfig &config);
  81 +#endif
  82 +
73 ~SpokenLanguageIdentification(); 83 ~SpokenLanguageIdentification();
74 84
75 // Create a stream to accept audio samples and compute features 85 // Create a stream to accept audio samples and compute features
@@ -56,6 +56,32 @@ static SpokenLanguageIdentificationConfig GetSpokenLanguageIdentificationConfig( @@ -56,6 +56,32 @@ static SpokenLanguageIdentificationConfig GetSpokenLanguageIdentificationConfig(
56 56
57 SHERPA_ONNX_EXTERN_C 57 SHERPA_ONNX_EXTERN_C
58 JNIEXPORT jlong JNICALL 58 JNIEXPORT jlong JNICALL
  59 +Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_newFromAsset(
  60 + JNIEnv *env, jobject /*obj*/, jobject asset_manager, jobject _config) {
  61 +#if __ANDROID_API__ >= 9
  62 + AAssetManager *mgr = AAssetManager_fromJava(env, asset_manager);
  63 + if (!mgr) {
  64 + SHERPA_ONNX_LOGE("Failed to get asset manager: %p", mgr);
  65 + }
  66 +#endif
  67 +
  68 + auto config =
  69 + sherpa_onnx::GetSpokenLanguageIdentificationConfig(env, _config);
  70 + SHERPA_ONNX_LOGE("spoken language identification newFromAsset config:\n%s",
  71 + config.ToString().c_str());
  72 +
  73 + auto slid = new sherpa_onnx::SpokenLanguageIdentification(
  74 +#if __ANDROID_API__ >= 9
  75 + mgr,
  76 +#endif
  77 + config);
  78 + SHERPA_ONNX_LOGE("slid %p", slid);
  79 +
  80 + return (jlong)slid;
  81 +}
  82 +
  83 +SHERPA_ONNX_EXTERN_C
  84 +JNIEXPORT jlong JNICALL
59 Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_newFromFile( 85 Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_newFromFile(
60 JNIEnv *env, jobject /*obj*/, jobject _config) { 86 JNIEnv *env, jobject /*obj*/, jobject _config) {
61 auto config = 87 auto config =
@@ -74,6 +100,14 @@ Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_newFromFile( @@ -74,6 +100,14 @@ Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_newFromFile(
74 } 100 }
75 101
76 SHERPA_ONNX_EXTERN_C 102 SHERPA_ONNX_EXTERN_C
  103 +JNIEXPORT void JNICALL
  104 +Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_delete(JNIEnv *env,
  105 + jobject /*obj*/,
  106 + jlong ptr) {
  107 + delete reinterpret_cast<sherpa_onnx::SpokenLanguageIdentification *>(ptr);
  108 +}
  109 +
  110 +SHERPA_ONNX_EXTERN_C
77 JNIEXPORT jlong JNICALL 111 JNIEXPORT jlong JNICALL
78 Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_createStream( 112 Java_com_k2fsa_sherpa_onnx_SpokenLanguageIdentification_createStream(
79 JNIEnv *env, jobject /*obj*/, jlong ptr) { 113 JNIEnv *env, jobject /*obj*/, jlong ptr) {