正在显示
68 个修改的文件
包含
1561 行增加
和
44 行删除
| @@ -38,6 +38,7 @@ jobs: | @@ -38,6 +38,7 @@ jobs: | ||
| 38 | shell: bash | 38 | shell: bash |
| 39 | run: | | 39 | run: | |
| 40 | export ANDROID_NDK=$ANDROID_NDK_LATEST_HOME | 40 | export ANDROID_NDK=$ANDROID_NDK_LATEST_HOME |
| 41 | + ./build-apk-vad.sh | ||
| 41 | ./build-apk-two-pass.sh | 42 | ./build-apk-two-pass.sh |
| 42 | ./build-apk.sh | 43 | ./build-apk.sh |
| 43 | 44 |
android/README.md
0 → 100644
android/SherpaOnnxVad/.gitignore
0 → 100644
android/SherpaOnnxVad/.idea/.gitignore
0 → 100644
android/SherpaOnnxVad/.idea/compiler.xml
0 → 100644
android/SherpaOnnxVad/.idea/gradle.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project version="4"> | ||
| 3 | + <component name="GradleMigrationSettings" migrationVersion="1" /> | ||
| 4 | + <component name="GradleSettings"> | ||
| 5 | + <option name="linkedExternalProjectsSettings"> | ||
| 6 | + <GradleProjectSettings> | ||
| 7 | + <option name="testRunner" value="GRADLE" /> | ||
| 8 | + <option name="distributionType" value="DEFAULT_WRAPPED" /> | ||
| 9 | + <option name="externalProjectPath" value="$PROJECT_DIR$" /> | ||
| 10 | + <option name="modules"> | ||
| 11 | + <set> | ||
| 12 | + <option value="$PROJECT_DIR$" /> | ||
| 13 | + <option value="$PROJECT_DIR$/app" /> | ||
| 14 | + </set> | ||
| 15 | + </option> | ||
| 16 | + </GradleProjectSettings> | ||
| 17 | + </option> | ||
| 18 | + </component> | ||
| 19 | +</project> |
android/SherpaOnnxVad/.idea/misc.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project version="4"> | ||
| 3 | + <component name="ExternalStorageConfigurationManager" enabled="true" /> | ||
| 4 | + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK"> | ||
| 5 | + <output url="file://$PROJECT_DIR$/build/classes" /> | ||
| 6 | + </component> | ||
| 7 | + <component name="ProjectType"> | ||
| 8 | + <option name="id" value="Android" /> | ||
| 9 | + </component> | ||
| 10 | +</project> |
android/SherpaOnnxVad/.idea/vcs.xml
0 → 100644
android/SherpaOnnxVad/app/.gitignore
0 → 100644
| 1 | +/build |
android/SherpaOnnxVad/app/build.gradle
0 → 100644
| 1 | +plugins { | ||
| 2 | + id 'com.android.application' | ||
| 3 | + id 'org.jetbrains.kotlin.android' | ||
| 4 | +} | ||
| 5 | + | ||
| 6 | +android { | ||
| 7 | + namespace 'com.k2fsa.sherpa.onnx' | ||
| 8 | + compileSdk 33 | ||
| 9 | + | ||
| 10 | + defaultConfig { | ||
| 11 | + applicationId "com.k2fsa.sherpa.onnx" | ||
| 12 | + minSdk 21 | ||
| 13 | + targetSdk 33 | ||
| 14 | + versionCode 1 | ||
| 15 | + versionName "1.0" | ||
| 16 | + | ||
| 17 | + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + buildTypes { | ||
| 21 | + release { | ||
| 22 | + minifyEnabled false | ||
| 23 | + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + compileOptions { | ||
| 27 | + sourceCompatibility JavaVersion.VERSION_1_8 | ||
| 28 | + targetCompatibility JavaVersion.VERSION_1_8 | ||
| 29 | + } | ||
| 30 | + kotlinOptions { | ||
| 31 | + jvmTarget = '1.8' | ||
| 32 | + } | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +dependencies { | ||
| 36 | + | ||
| 37 | + implementation 'androidx.core:core-ktx:1.7.0' | ||
| 38 | + implementation 'androidx.appcompat:appcompat:1.6.1' | ||
| 39 | + implementation 'com.google.android.material:material:1.9.0' | ||
| 40 | + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' | ||
| 41 | + testImplementation 'junit:junit:4.13.2' | ||
| 42 | + androidTestImplementation 'androidx.test.ext:junit:1.1.5' | ||
| 43 | + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' | ||
| 44 | +} |
android/SherpaOnnxVad/app/proguard-rules.pro
0 → 100644
| 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 |
android/SherpaOnnxVad/app/src/androidTest/java/com/k2fsa/sherpa/onnx/ExampleInstrumentedTest.kt
0 → 100644
| 1 | +package com.k2fsa.sherpa.onnx | ||
| 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", 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.SherpaOnnxVad" | ||
| 16 | + tools:targetApi="31"> | ||
| 17 | + <activity | ||
| 18 | + android:name=".MainActivity" | ||
| 19 | + android:exported="true"> | ||
| 20 | + <intent-filter> | ||
| 21 | + <action android:name="android.intent.action.MAIN" /> | ||
| 22 | + | ||
| 23 | + <category android:name="android.intent.category.LAUNCHER" /> | ||
| 24 | + </intent-filter> | ||
| 25 | + | ||
| 26 | + <meta-data | ||
| 27 | + android:name="android.app.lib_name" | ||
| 28 | + android:value="" /> | ||
| 29 | + </activity> | ||
| 30 | + </application> | ||
| 31 | + | ||
| 32 | +</manifest> |
| 1 | +*.onnx |
| 1 | +package com.k2fsa.sherpa.onnx | ||
| 2 | + | ||
| 3 | +import android.Manifest | ||
| 4 | +import android.content.pm.PackageManager | ||
| 5 | +import android.media.AudioFormat | ||
| 6 | +import android.media.AudioRecord | ||
| 7 | +import android.media.MediaRecorder | ||
| 8 | +import android.os.Bundle | ||
| 9 | +import android.util.Log | ||
| 10 | +import android.view.View | ||
| 11 | +import android.widget.Button | ||
| 12 | +import androidx.appcompat.app.AppCompatActivity | ||
| 13 | +import androidx.core.app.ActivityCompat | ||
| 14 | +import kotlin.concurrent.thread | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +private const val TAG = "sherpa-onnx" | ||
| 18 | +private const val REQUEST_RECORD_AUDIO_PERMISSION = 200 | ||
| 19 | + | ||
| 20 | +class MainActivity : AppCompatActivity() { | ||
| 21 | + | ||
| 22 | + private lateinit var recordButton: Button | ||
| 23 | + private lateinit var circle: View | ||
| 24 | + | ||
| 25 | + private lateinit var vad: Vad | ||
| 26 | + | ||
| 27 | + private var audioRecord: AudioRecord? = null | ||
| 28 | + private var recordingThread: Thread? = null | ||
| 29 | + private val audioSource = MediaRecorder.AudioSource.MIC | ||
| 30 | + private val sampleRateInHz = 16000 | ||
| 31 | + private val channelConfig = AudioFormat.CHANNEL_IN_MONO | ||
| 32 | + | ||
| 33 | + // Note: We don't use AudioFormat.ENCODING_PCM_FLOAT | ||
| 34 | + // since the AudioRecord.read(float[]) needs API level >= 23 | ||
| 35 | + // but we are targeting API level >= 21 | ||
| 36 | + private val audioFormat = AudioFormat.ENCODING_PCM_16BIT | ||
| 37 | + | ||
| 38 | + private val permissions: Array<String> = arrayOf(Manifest.permission.RECORD_AUDIO) | ||
| 39 | + | ||
| 40 | + @Volatile | ||
| 41 | + private var isRecording: Boolean = false | ||
| 42 | + | ||
| 43 | + override fun onRequestPermissionsResult( | ||
| 44 | + requestCode: Int, permissions: Array<String>, grantResults: IntArray | ||
| 45 | + ) { | ||
| 46 | + super.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||
| 47 | + val permissionToRecordAccepted = if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) { | ||
| 48 | + grantResults[0] == PackageManager.PERMISSION_GRANTED | ||
| 49 | + } else { | ||
| 50 | + false | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + if (!permissionToRecordAccepted) { | ||
| 54 | + Log.e(TAG, "Audio record is disallowed") | ||
| 55 | + finish() | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + Log.i(TAG, "Audio record is permitted") | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + override fun onCreate(savedInstanceState: Bundle?) { | ||
| 62 | + super.onCreate(savedInstanceState) | ||
| 63 | + setContentView(R.layout.activity_main) | ||
| 64 | + | ||
| 65 | + ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION) | ||
| 66 | + | ||
| 67 | + Log.i(TAG, "Start to initialize model") | ||
| 68 | + initVadModel() | ||
| 69 | + Log.i(TAG, "Finished initializing model") | ||
| 70 | + | ||
| 71 | + circle= findViewById(R.id.powerCircle) | ||
| 72 | + | ||
| 73 | + recordButton = findViewById(R.id.record_button) | ||
| 74 | + recordButton.setOnClickListener { onclick() } | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + private fun onclick() { | ||
| 78 | + if (!isRecording) { | ||
| 79 | + val ret = initMicrophone() | ||
| 80 | + if (!ret) { | ||
| 81 | + Log.e(TAG, "Failed to initialize microphone") | ||
| 82 | + return | ||
| 83 | + } | ||
| 84 | + Log.i(TAG, "state: ${audioRecord?.state}") | ||
| 85 | + audioRecord!!.startRecording() | ||
| 86 | + recordButton.setText(R.string.stop) | ||
| 87 | + isRecording = true | ||
| 88 | + | ||
| 89 | + vad.reset() | ||
| 90 | + recordingThread = thread(true) { | ||
| 91 | + processSamples() | ||
| 92 | + } | ||
| 93 | + Log.i(TAG, "Started recording") | ||
| 94 | + onVad(false) | ||
| 95 | + | ||
| 96 | + } else { | ||
| 97 | + isRecording = false | ||
| 98 | + | ||
| 99 | + audioRecord!!.stop() | ||
| 100 | + audioRecord!!.release() | ||
| 101 | + audioRecord = null | ||
| 102 | + | ||
| 103 | + recordButton.setText(R.string.start) | ||
| 104 | + onVad(false) | ||
| 105 | + Log.i(TAG, "Stopped recording") | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + private fun onVad(isSpeech: Boolean) { | ||
| 110 | + if(isSpeech) { | ||
| 111 | + circle.background = resources.getDrawable(R.drawable.red_circle) | ||
| 112 | + } else { | ||
| 113 | + circle.background = resources.getDrawable(R.drawable.black_circle) | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + private fun initVadModel() { | ||
| 118 | + val type = 0 | ||
| 119 | + println("Select VAD model type ${type}") | ||
| 120 | + val config = getVadModelConfig(type) | ||
| 121 | + | ||
| 122 | + vad = Vad( | ||
| 123 | + assetManager = application.assets, | ||
| 124 | + config = config!!, | ||
| 125 | + ) | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + private fun initMicrophone(): Boolean { | ||
| 129 | + if (ActivityCompat.checkSelfPermission( | ||
| 130 | + this, Manifest.permission.RECORD_AUDIO | ||
| 131 | + ) != PackageManager.PERMISSION_GRANTED | ||
| 132 | + ) { | ||
| 133 | + ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION) | ||
| 134 | + return false | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + val numBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat) | ||
| 138 | + Log.i( | ||
| 139 | + TAG, "buffer size in milliseconds: ${numBytes * 1000.0f / sampleRateInHz}" | ||
| 140 | + ) | ||
| 141 | + | ||
| 142 | + audioRecord = AudioRecord( | ||
| 143 | + audioSource, | ||
| 144 | + sampleRateInHz, | ||
| 145 | + channelConfig, | ||
| 146 | + audioFormat, | ||
| 147 | + numBytes * 2 // a sample has two bytes as we are using 16-bit PCM | ||
| 148 | + ) | ||
| 149 | + return true | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + private fun processSamples() { | ||
| 153 | + Log.i(TAG, "processing samples") | ||
| 154 | + | ||
| 155 | + val bufferSize = 512 // in samples | ||
| 156 | + val buffer = ShortArray(bufferSize) | ||
| 157 | + | ||
| 158 | + while (isRecording) { | ||
| 159 | + val ret = audioRecord?.read(buffer, 0, buffer.size) | ||
| 160 | + if (ret != null && ret > 0) { | ||
| 161 | + val samples = FloatArray(ret) { buffer[it] / 32768.0f } | ||
| 162 | + | ||
| 163 | + vad.acceptWaveform(samples) | ||
| 164 | + while(!vad.empty()) {vad.pop();} | ||
| 165 | + | ||
| 166 | + val isSpeechDetected = vad.isSpeechDetected() | ||
| 167 | + | ||
| 168 | + runOnUiThread { | ||
| 169 | + onVad(isSpeechDetected) | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | +} |
| 1 | +// Copyright (c) 2023 Xiaomi Corporation | ||
| 2 | +package com.k2fsa.sherpa.onnx | ||
| 3 | + | ||
| 4 | +import android.content.res.AssetManager | ||
| 5 | + | ||
| 6 | +data class SileroVadModelConfig( | ||
| 7 | + var model: String, | ||
| 8 | + var threshold: Float = 0.5F, | ||
| 9 | + var minSilenceDuration: Float = 0.25F, | ||
| 10 | + var minSpeechDuration: Float = 0.25F, | ||
| 11 | + var windowSize: Int = 512, | ||
| 12 | +) | ||
| 13 | + | ||
| 14 | +data class VadModelConfig( | ||
| 15 | + var sileroVadModelConfig: SileroVadModelConfig, | ||
| 16 | + var sampleRate: Int = 16000, | ||
| 17 | + var numThreads: Int = 1, | ||
| 18 | + var provider: String = "cpu", | ||
| 19 | + var debug: Boolean = false, | ||
| 20 | +) | ||
| 21 | + | ||
| 22 | +class Vad( | ||
| 23 | + assetManager: AssetManager? = null, | ||
| 24 | + var config: VadModelConfig, | ||
| 25 | +) { | ||
| 26 | + private val ptr: Long | ||
| 27 | + | ||
| 28 | + init { | ||
| 29 | + if (assetManager != null) { | ||
| 30 | + ptr = new(assetManager, config) | ||
| 31 | + } else { | ||
| 32 | + ptr = newFromFile(config) | ||
| 33 | + } | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + protected fun finalize() { | ||
| 37 | + delete(ptr) | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + fun acceptWaveform(samples: FloatArray) = acceptWaveform(ptr, samples) | ||
| 41 | + | ||
| 42 | + fun empty(): Boolean = empty(ptr) | ||
| 43 | + fun pop() = pop(ptr) | ||
| 44 | + | ||
| 45 | + // return an array containing | ||
| 46 | + // [start: Int, samples: FloatArray] | ||
| 47 | + fun front() = front(ptr) | ||
| 48 | + | ||
| 49 | + fun isSpeechDetected(): Boolean = isSpeechDetected(ptr) | ||
| 50 | + | ||
| 51 | + fun reset() = reset(ptr) | ||
| 52 | + | ||
| 53 | + private external fun delete(ptr: Long) | ||
| 54 | + | ||
| 55 | + private external fun new( | ||
| 56 | + assetManager: AssetManager, | ||
| 57 | + config: VadModelConfig, | ||
| 58 | + ): Long | ||
| 59 | + | ||
| 60 | + private external fun newFromFile( | ||
| 61 | + config: VadModelConfig, | ||
| 62 | + ): Long | ||
| 63 | + | ||
| 64 | + private external fun acceptWaveform(ptr: Long, samples: FloatArray) | ||
| 65 | + private external fun empty(ptr: Long): Boolean | ||
| 66 | + private external fun pop(ptr: Long) | ||
| 67 | + private external fun front(ptr: Long): Array<Any> | ||
| 68 | + private external fun isSpeechDetected(ptr: Long): Boolean | ||
| 69 | + private external fun reset(ptr: Long) | ||
| 70 | + | ||
| 71 | + companion object { | ||
| 72 | + init { | ||
| 73 | + System.loadLibrary("sherpa-onnx-jni") | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +// Please visit | ||
| 79 | +// https://github.com/snakers4/silero-vad/blob/master/files/silero_vad.onnx | ||
| 80 | +// to download silero_vad.onnx | ||
| 81 | +// and put it inside the assets/ | ||
| 82 | +// directory | ||
| 83 | +fun getVadModelConfig(type: Int): VadModelConfig? { | ||
| 84 | + when (type) { | ||
| 85 | + 0 -> { | ||
| 86 | + return VadModelConfig( | ||
| 87 | + sileroVadModelConfig = SileroVadModelConfig( | ||
| 88 | + model = "silero_vad.onnx", | ||
| 89 | + threshold = 0.5F, | ||
| 90 | + minSilenceDuration = 0.25F, | ||
| 91 | + minSpeechDuration = 0.25F, | ||
| 92 | + windowSize = 512, | ||
| 93 | + ), | ||
| 94 | + sampleRate = 16000, | ||
| 95 | + numThreads = 1, | ||
| 96 | + provider = "cpu", | ||
| 97 | + ) | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + return null; | ||
| 101 | +} |
| 1 | +*.so |
| 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 | +<selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | + <item> | ||
| 4 | + <shape android:shape="oval"> | ||
| 5 | + | ||
| 6 | + <solid android:color="#FF000000"/> | ||
| 7 | + | ||
| 8 | + <size | ||
| 9 | + android:width="300dp" | ||
| 10 | + android:height="300dp"/> | ||
| 11 | + </shape> | ||
| 12 | + </item> | ||
| 13 | +</selector> |
| 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 | +<selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | + <item> | ||
| 4 | + <shape android:shape="oval"> | ||
| 5 | + | ||
| 6 | + <solid android:color="#FFFF0000"/> | ||
| 7 | + | ||
| 8 | + <size | ||
| 9 | + android:width="300dp" | ||
| 10 | + android:height="300dp"/> | ||
| 11 | + </shape> | ||
| 12 | + </item> | ||
| 13 | +</selector> |
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | + xmlns:tools="http://schemas.android.com/tools" | ||
| 5 | + android:layout_width="match_parent" | ||
| 6 | + android:layout_height="match_parent" | ||
| 7 | + tools:context=".MainActivity"> | ||
| 8 | + <LinearLayout | ||
| 9 | + android:layout_width="match_parent" | ||
| 10 | + android:layout_height="match_parent" | ||
| 11 | + android:gravity="bottom" | ||
| 12 | + android:orientation="vertical" | ||
| 13 | + > | ||
| 14 | + | ||
| 15 | + <Space | ||
| 16 | + android:layout_width="match_parent" | ||
| 17 | + android:layout_height="10dp" /> | ||
| 18 | + | ||
| 19 | + <LinearLayout | ||
| 20 | + android:id="@+id/powerCircle" | ||
| 21 | + android:layout_width="wrap_content" | ||
| 22 | + android:layout_height="wrap_content" | ||
| 23 | + android:layout_gravity="center_horizontal" | ||
| 24 | + android:background="@drawable/black_circle" | ||
| 25 | + android:orientation="vertical" /> | ||
| 26 | + | ||
| 27 | + <Space | ||
| 28 | + android:layout_width="match_parent" | ||
| 29 | + android:layout_height="200dp" /> | ||
| 30 | + | ||
| 31 | + <Button | ||
| 32 | + android:id="@+id/record_button" | ||
| 33 | + android:layout_width="match_parent" | ||
| 34 | + android:layout_height="wrap_content" | ||
| 35 | + android:text="@string/start" /> | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + </LinearLayout> | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + | ||
| 43 | +</androidx.constraintlayout.widget.ConstraintLayout> |
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
| 1 | +<resources xmlns:tools="http://schemas.android.com/tools"> | ||
| 2 | + <!-- Base application theme. --> | ||
| 3 | + <style name="Theme.SherpaOnnxVad" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> | ||
| 4 | + <!-- Primary brand color. --> | ||
| 5 | + <item name="colorPrimary">@color/purple_200</item> | ||
| 6 | + <item name="colorPrimaryVariant">@color/purple_700</item> | ||
| 7 | + <item name="colorOnPrimary">@color/black</item> | ||
| 8 | + <!-- Secondary brand color. --> | ||
| 9 | + <item name="colorSecondary">@color/teal_200</item> | ||
| 10 | + <item name="colorSecondaryVariant">@color/teal_200</item> | ||
| 11 | + <item name="colorOnSecondary">@color/black</item> | ||
| 12 | + <!-- Status bar color. --> | ||
| 13 | + <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> | ||
| 14 | + <!-- Customize your theme here. --> | ||
| 15 | + </style> | ||
| 16 | +</resources> |
| 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 xmlns:tools="http://schemas.android.com/tools"> | ||
| 2 | + <!-- Base application theme. --> | ||
| 3 | + <style name="Theme.SherpaOnnxVad" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> | ||
| 4 | + <!-- Primary brand color. --> | ||
| 5 | + <item name="colorPrimary">@color/purple_500</item> | ||
| 6 | + <item name="colorPrimaryVariant">@color/purple_700</item> | ||
| 7 | + <item name="colorOnPrimary">@color/white</item> | ||
| 8 | + <!-- Secondary brand color. --> | ||
| 9 | + <item name="colorSecondary">@color/teal_200</item> | ||
| 10 | + <item name="colorSecondaryVariant">@color/teal_700</item> | ||
| 11 | + <item name="colorOnSecondary">@color/black</item> | ||
| 12 | + <!-- Status bar color. --> | ||
| 13 | + <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> | ||
| 14 | + <!-- Customize your theme here. --> | ||
| 15 | + </style> | ||
| 16 | +</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 | ||
| 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 | +} |
android/SherpaOnnxVad/build.gradle
0 → 100644
| 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 '7.3.1' apply false | ||
| 4 | + id 'com.android.library' version '7.3.1' apply false | ||
| 5 | + id 'org.jetbrains.kotlin.android' version '1.7.20' apply false | ||
| 6 | +} |
android/SherpaOnnxVad/gradle.properties
0 → 100644
| 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 |
android/SherpaOnnxVad/gradlew
0 → 100755
| 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" "$@" |
android/SherpaOnnxVad/gradlew.bat
0 → 100644
| 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 |
android/SherpaOnnxVad/settings.gradle
0 → 100644
| 1 | +pluginManagement { | ||
| 2 | + repositories { | ||
| 3 | + gradlePluginPortal() | ||
| 4 | + google() | ||
| 5 | + mavenCentral() | ||
| 6 | + } | ||
| 7 | +} | ||
| 8 | +dependencyResolutionManagement { | ||
| 9 | + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) | ||
| 10 | + repositories { | ||
| 11 | + google() | ||
| 12 | + mavenCentral() | ||
| 13 | + } | ||
| 14 | +} | ||
| 15 | +rootProject.name = "SherpaOnnxVad" | ||
| 16 | +include ':app' |
| @@ -45,7 +45,9 @@ sleep 1 | @@ -45,7 +45,9 @@ sleep 1 | ||
| 45 | onnxruntime_version=v1.16.0 | 45 | onnxruntime_version=v1.16.0 |
| 46 | 46 | ||
| 47 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/arm64-v8a/libonnxruntime.so ]; then | 47 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/arm64-v8a/libonnxruntime.so ]; then |
| 48 | - GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | 48 | + if [ ! -d android-onnxruntime-libs ]; then |
| 49 | + GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | ||
| 50 | + fi | ||
| 49 | pushd android-onnxruntime-libs | 51 | pushd android-onnxruntime-libs |
| 50 | git lfs pull --include "$onnxruntime_version/jni/arm64-v8a/libonnxruntime.so" | 52 | git lfs pull --include "$onnxruntime_version/jni/arm64-v8a/libonnxruntime.so" |
| 51 | ln -s $onnxruntime_version/jni . | 53 | ln -s $onnxruntime_version/jni . |
| @@ -46,7 +46,9 @@ sleep 1 | @@ -46,7 +46,9 @@ sleep 1 | ||
| 46 | onnxruntime_version=v1.16.0 | 46 | onnxruntime_version=v1.16.0 |
| 47 | 47 | ||
| 48 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/armeabi-v7a/libonnxruntime.so ]; then | 48 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/armeabi-v7a/libonnxruntime.so ]; then |
| 49 | - GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | 49 | + if [ ! -d android-onnxruntime-libs ]; then |
| 50 | + GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | ||
| 51 | + fi | ||
| 50 | pushd android-onnxruntime-libs | 52 | pushd android-onnxruntime-libs |
| 51 | git lfs pull --include "$onnxruntime_version/jni/armeabi-v7a/libonnxruntime.so" | 53 | git lfs pull --include "$onnxruntime_version/jni/armeabi-v7a/libonnxruntime.so" |
| 52 | ln -s $onnxruntime_version/jni . | 54 | ln -s $onnxruntime_version/jni . |
| @@ -46,7 +46,9 @@ sleep 1 | @@ -46,7 +46,9 @@ sleep 1 | ||
| 46 | onnxruntime_version=v1.16.0 | 46 | onnxruntime_version=v1.16.0 |
| 47 | 47 | ||
| 48 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/x86_64/libonnxruntime.so ]; then | 48 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/x86_64/libonnxruntime.so ]; then |
| 49 | - GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | 49 | + if [ ! -d android-onnxruntime-libs ]; then |
| 50 | + GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | ||
| 51 | + fi | ||
| 50 | pushd android-onnxruntime-libs | 52 | pushd android-onnxruntime-libs |
| 51 | git lfs pull --include "$onnxruntime_version/jni/x86_64/libonnxruntime.so" | 53 | git lfs pull --include "$onnxruntime_version/jni/x86_64/libonnxruntime.so" |
| 52 | ln -s $onnxruntime_version/jni . | 54 | ln -s $onnxruntime_version/jni . |
| @@ -46,7 +46,9 @@ sleep 1 | @@ -46,7 +46,9 @@ sleep 1 | ||
| 46 | onnxruntime_version=v1.16.0 | 46 | onnxruntime_version=v1.16.0 |
| 47 | 47 | ||
| 48 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/x86/libonnxruntime.so ]; then | 48 | if [ ! -f ./android-onnxruntime-libs/$onnxruntime_version/jni/x86/libonnxruntime.so ]; then |
| 49 | - GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | 49 | + if [ ! -d android-onnxruntime-libs ]; then |
| 50 | + GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/csukuangfj/android-onnxruntime-libs | ||
| 51 | + fi | ||
| 50 | pushd android-onnxruntime-libs | 52 | pushd android-onnxruntime-libs |
| 51 | git lfs pull --include "$onnxruntime_version/jni/x86/libonnxruntime.so" | 53 | git lfs pull --include "$onnxruntime_version/jni/x86/libonnxruntime.so" |
| 52 | ln -s $onnxruntime_version/jni . | 54 | ln -s $onnxruntime_version/jni . |
build-apk-vad.sh
0 → 100755
| 1 | +#!/usr/bin/env bash | ||
| 2 | + | ||
| 3 | +# Please set the environment variable ANDROID_NDK | ||
| 4 | +# before running this script | ||
| 5 | + | ||
| 6 | +# Inside the $ANDROID_NDK directory, you can find a binary ndk-build | ||
| 7 | +# and some other files like the file "build/cmake/android.toolchain.cmake" | ||
| 8 | + | ||
| 9 | +set -e | ||
| 10 | + | ||
| 11 | +log() { | ||
| 12 | + # This function is from espnet | ||
| 13 | + local fname=${BASH_SOURCE[1]##*/} | ||
| 14 | + echo -e "$(date '+%Y-%m-%d %H:%M:%S') (${fname}:${BASH_LINENO[0]}:${FUNCNAME[1]}) $*" | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +SHERPA_ONNX_VERSION=$(grep "SHERPA_ONNX_VERSION" ./CMakeLists.txt | cut -d " " -f 2 | cut -d '"' -f 2) | ||
| 18 | + | ||
| 19 | +log "Building APK for sherpa-onnx v${SHERPA_ONNX_VERSION}" | ||
| 20 | + | ||
| 21 | +log "====================arm64-v8a=================" | ||
| 22 | +./build-android-arm64-v8a.sh | ||
| 23 | +log "====================armv7-eabi================" | ||
| 24 | +./build-android-armv7-eabi.sh | ||
| 25 | +log "====================x86-64====================" | ||
| 26 | +./build-android-x86-64.sh | ||
| 27 | +log "====================x86====================" | ||
| 28 | +./build-android-x86.sh | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +mkdir -p apks | ||
| 32 | + | ||
| 33 | +log "https://github.com/snakers4/silero-vad/raw/master/files/silero_vad.onnx" | ||
| 34 | + | ||
| 35 | +# Download the model | ||
| 36 | +pushd ./android/SherpaOnnxVad/app/src/main/assets/ | ||
| 37 | +wget https://github.com/snakers4/silero-vad/raw/master/files/silero_vad.onnx | ||
| 38 | +popd | ||
| 39 | + | ||
| 40 | +for arch in arm64-v8a armeabi-v7a x86_64 x86; do | ||
| 41 | + log "------------------------------------------------------------" | ||
| 42 | + log "build apk for $arch" | ||
| 43 | + log "------------------------------------------------------------" | ||
| 44 | + src_arch=$arch | ||
| 45 | + if [ $arch == "armeabi-v7a" ]; then | ||
| 46 | + src_arch=armv7-eabi | ||
| 47 | + elif [ $arch == "x86_64" ]; then | ||
| 48 | + src_arch=x86-64 | ||
| 49 | + fi | ||
| 50 | + | ||
| 51 | + ls -lh ./build-android-$src_arch/install/lib/*.so | ||
| 52 | + | ||
| 53 | + cp -v ./build-android-$src_arch/install/lib/*.so ./android/SherpaOnnxVad/app/src/main/jniLibs/$arch/ | ||
| 54 | + | ||
| 55 | + pushd ./android/SherpaOnnxVad | ||
| 56 | + ./gradlew build | ||
| 57 | + popd | ||
| 58 | + | ||
| 59 | + mv android/SherpaOnnxVad/app/build/outputs/apk/debug/app-debug.apk ./apks/sherpa-onnx-${SHERPA_ONNX_VERSION}-$arch-silero-vad.apk | ||
| 60 | + ls -lh apks | ||
| 61 | + rm -v ./android/SherpaOnnxVad/app/src/main/jniLibs/$arch/*.so | ||
| 62 | +done | ||
| 63 | + | ||
| 64 | +rm -rf ./android/SherpaOnnxVad/app/src/main/assets/*.onnx | ||
| 65 | + | ||
| 66 | +ls -lh apks/ |
kotlin-api-examples/Vad.kt
0 → 120000
| 1 | +../android/SherpaOnnxVad/app/src/main/java/com/k2fsa/sherpa/onnx/Vad.kt |
| @@ -32,7 +32,7 @@ std::vector<std::vector<std::string>> SplitToBatches( | @@ -32,7 +32,7 @@ std::vector<std::vector<std::string>> SplitToBatches( | ||
| 32 | process_num += batch_size; | 32 | process_num += batch_size; |
| 33 | } | 33 | } |
| 34 | if (itr != input.cend()) { | 34 | if (itr != input.cend()) { |
| 35 | - outputs.emplace_back(itr, input.cend()); | 35 | + outputs.emplace_back(itr, input.cend()); |
| 36 | } | 36 | } |
| 37 | return outputs; | 37 | return outputs; |
| 38 | } | 38 | } |
| @@ -41,8 +41,8 @@ std::vector<std::string> LoadScpFile(const std::string &wav_scp_path) { | @@ -41,8 +41,8 @@ std::vector<std::string> LoadScpFile(const std::string &wav_scp_path) { | ||
| 41 | std::vector<std::string> wav_paths; | 41 | std::vector<std::string> wav_paths; |
| 42 | std::ifstream in(wav_scp_path); | 42 | std::ifstream in(wav_scp_path); |
| 43 | if (!in.is_open()) { | 43 | if (!in.is_open()) { |
| 44 | - fprintf(stderr, "Failed to open file: %s.\n", wav_scp_path.c_str()); | ||
| 45 | - return wav_paths; | 44 | + fprintf(stderr, "Failed to open file: %s.\n", wav_scp_path.c_str()); |
| 45 | + return wav_paths; | ||
| 46 | } | 46 | } |
| 47 | std::string line, column1, column2; | 47 | std::string line, column1, column2; |
| 48 | while (std::getline(in, line)) { | 48 | while (std::getline(in, line)) { |
| @@ -55,8 +55,8 @@ std::vector<std::string> LoadScpFile(const std::string &wav_scp_path) { | @@ -55,8 +55,8 @@ std::vector<std::string> LoadScpFile(const std::string &wav_scp_path) { | ||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | 57 | void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, |
| 58 | - sherpa_onnx::OfflineRecognizer* recognizer, | ||
| 59 | - float* total_length, float* total_time) { | 58 | + sherpa_onnx::OfflineRecognizer *recognizer, |
| 59 | + float *total_length, float *total_time) { | ||
| 60 | std::vector<std::unique_ptr<sherpa_onnx::OfflineStream>> ss; | 60 | std::vector<std::unique_ptr<sherpa_onnx::OfflineStream>> ss; |
| 61 | std::vector<sherpa_onnx::OfflineStream *> ss_pointers; | 61 | std::vector<sherpa_onnx::OfflineStream *> ss_pointers; |
| 62 | float duration = 0.0f; | 62 | float duration = 0.0f; |
| @@ -70,7 +70,7 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | @@ -70,7 +70,7 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | ||
| 70 | sherpa_onnx::ReadWave(wav_filename, &sampling_rate, &is_ok); | 70 | sherpa_onnx::ReadWave(wav_filename, &sampling_rate, &is_ok); |
| 71 | if (!is_ok) { | 71 | if (!is_ok) { |
| 72 | fprintf(stderr, "Failed to read %s\n", wav_filename.c_str()); | 72 | fprintf(stderr, "Failed to read %s\n", wav_filename.c_str()); |
| 73 | - continue; | 73 | + continue; |
| 74 | } | 74 | } |
| 75 | duration += samples.size() / static_cast<float>(sampling_rate); | 75 | duration += samples.size() / static_cast<float>(sampling_rate); |
| 76 | auto s = recognizer->CreateStream(); | 76 | auto s = recognizer->CreateStream(); |
| @@ -97,7 +97,7 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | @@ -97,7 +97,7 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | ||
| 97 | sherpa_onnx::ReadWave(wav_filename, &sampling_rate, &is_ok); | 97 | sherpa_onnx::ReadWave(wav_filename, &sampling_rate, &is_ok); |
| 98 | if (!is_ok) { | 98 | if (!is_ok) { |
| 99 | fprintf(stderr, "Failed to read %s\n", wav_filename.c_str()); | 99 | fprintf(stderr, "Failed to read %s\n", wav_filename.c_str()); |
| 100 | - continue; | 100 | + continue; |
| 101 | } | 101 | } |
| 102 | duration += samples.size() / static_cast<float>(sampling_rate); | 102 | duration += samples.size() / static_cast<float>(sampling_rate); |
| 103 | auto s = recognizer->CreateStream(); | 103 | auto s = recognizer->CreateStream(); |
| @@ -109,9 +109,9 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | @@ -109,9 +109,9 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | ||
| 109 | recognizer->DecodeStreams(ss_pointers.data(), ss_pointers.size()); | 109 | recognizer->DecodeStreams(ss_pointers.data(), ss_pointers.size()); |
| 110 | const auto end = std::chrono::steady_clock::now(); | 110 | const auto end = std::chrono::steady_clock::now(); |
| 111 | float elapsed_seconds = | 111 | float elapsed_seconds = |
| 112 | - std::chrono::duration_cast<std::chrono::milliseconds>(end - begin) | ||
| 113 | - .count() / | ||
| 114 | - 1000.; | 112 | + std::chrono::duration_cast<std::chrono::milliseconds>(end - begin) |
| 113 | + .count() / | ||
| 114 | + 1000.; | ||
| 115 | elapsed_seconds_batch += elapsed_seconds; | 115 | elapsed_seconds_batch += elapsed_seconds; |
| 116 | int i = 0; | 116 | int i = 0; |
| 117 | for (const auto &wav_filename : wav_paths) { | 117 | for (const auto &wav_filename : wav_paths) { |
| @@ -122,7 +122,7 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | @@ -122,7 +122,7 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | ||
| 122 | ss_pointers.clear(); | 122 | ss_pointers.clear(); |
| 123 | ss.clear(); | 123 | ss.clear(); |
| 124 | } | 124 | } |
| 125 | - fprintf(stderr, "thread %lu.\n", std::this_thread::get_id()); | 125 | + |
| 126 | { | 126 | { |
| 127 | std::lock_guard<std::mutex> guard(mtx); | 127 | std::lock_guard<std::mutex> guard(mtx); |
| 128 | *total_length += duration; | 128 | *total_length += duration; |
| @@ -132,7 +132,6 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | @@ -132,7 +132,6 @@ void AsrInference(const std::vector<std::vector<std::string>> &chunk_wav_paths, | ||
| 132 | } | 132 | } |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | - | ||
| 136 | int main(int32_t argc, char *argv[]) { | 135 | int main(int32_t argc, char *argv[]) { |
| 137 | const char *kUsageMessage = R"usage( | 136 | const char *kUsageMessage = R"usage( |
| 138 | Speech recognition using non-streaming models with sherpa-onnx. | 137 | Speech recognition using non-streaming models with sherpa-onnx. |
| @@ -223,17 +222,17 @@ https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html | @@ -223,17 +222,17 @@ https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html | ||
| 223 | for a list of pre-trained models to download. | 222 | for a list of pre-trained models to download. |
| 224 | )usage"; | 223 | )usage"; |
| 225 | std::string wav_scp = ""; // file path, kaldi style wav list. | 224 | std::string wav_scp = ""; // file path, kaldi style wav list. |
| 226 | - int32_t nj = 1; // thread number | ||
| 227 | - int32_t batch_size = 1; // number of wav files processed at once. | 225 | + int32_t nj = 1; // thread number |
| 226 | + int32_t batch_size = 1; // number of wav files processed at once. | ||
| 228 | sherpa_onnx::ParseOptions po(kUsageMessage); | 227 | sherpa_onnx::ParseOptions po(kUsageMessage); |
| 229 | sherpa_onnx::OfflineRecognizerConfig config; | 228 | sherpa_onnx::OfflineRecognizerConfig config; |
| 230 | config.Register(&po); | 229 | config.Register(&po); |
| 231 | po.Register("wav-scp", &wav_scp, | 230 | po.Register("wav-scp", &wav_scp, |
| 232 | "a file including wav-id and wav-path, kaldi style wav list." | 231 | "a file including wav-id and wav-path, kaldi style wav list." |
| 233 | - "default="". when it is not empty, wav files which positional " | 232 | + "default=" |
| 233 | + ". when it is not empty, wav files which positional " | ||
| 234 | "parameters provide are invalid."); | 234 | "parameters provide are invalid."); |
| 235 | - po.Register("nj", &nj, | ||
| 236 | - "multi-thread num for decoding, default=1"); | 235 | + po.Register("nj", &nj, "multi-thread num for decoding, default=1"); |
| 237 | po.Register("batch-size", &batch_size, | 236 | po.Register("batch-size", &batch_size, |
| 238 | "number of wav files processed at once during the decoding" | 237 | "number of wav files processed at once during the decoding" |
| 239 | "process. default=1"); | 238 | "process. default=1"); |
| @@ -262,7 +261,8 @@ for a list of pre-trained models to download. | @@ -262,7 +261,8 @@ for a list of pre-trained models to download. | ||
| 262 | 1000.; | 261 | 1000.; |
| 263 | fprintf(stderr, | 262 | fprintf(stderr, |
| 264 | "Started nj: %d, batch_size: %d, wav_path: %s. recognizer init time: " | 263 | "Started nj: %d, batch_size: %d, wav_path: %s. recognizer init time: " |
| 265 | - "%.6f\n", nj, batch_size, wav_scp.c_str(), elapsed_seconds); | 264 | + "%.6f\n", |
| 265 | + nj, batch_size, wav_scp.c_str(), elapsed_seconds); | ||
| 266 | std::this_thread::sleep_for(std::chrono::seconds(10)); // sleep 10s | 266 | std::this_thread::sleep_for(std::chrono::seconds(10)); // sleep 10s |
| 267 | std::vector<std::string> wav_paths; | 267 | std::vector<std::string> wav_paths; |
| 268 | if (!wav_scp.empty()) { | 268 | if (!wav_scp.empty()) { |
| @@ -282,12 +282,12 @@ for a list of pre-trained models to download. | @@ -282,12 +282,12 @@ for a list of pre-trained models to download. | ||
| 282 | float total_length = 0.0f; | 282 | float total_length = 0.0f; |
| 283 | float total_time = 0.0f; | 283 | float total_time = 0.0f; |
| 284 | for (int i = 0; i < nj; i++) { | 284 | for (int i = 0; i < nj; i++) { |
| 285 | - threads.emplace_back(std::thread(AsrInference, batch_wav_paths, | ||
| 286 | - &recognizer, &total_length, &total_time)); | 285 | + threads.emplace_back(std::thread(AsrInference, batch_wav_paths, &recognizer, |
| 286 | + &total_length, &total_time)); | ||
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | - for (auto& thread : threads) { | ||
| 290 | - thread.join(); | 289 | + for (auto &thread : threads) { |
| 290 | + thread.join(); | ||
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | fprintf(stderr, "num threads: %d\n", config.model_config.num_threads); | 293 | fprintf(stderr, "num threads: %d\n", config.model_config.num_threads); |
| @@ -297,8 +297,8 @@ for a list of pre-trained models to download. | @@ -297,8 +297,8 @@ for a list of pre-trained models to download. | ||
| 297 | } | 297 | } |
| 298 | fprintf(stderr, "Elapsed seconds: %.3f s\n", total_time); | 298 | fprintf(stderr, "Elapsed seconds: %.3f s\n", total_time); |
| 299 | float rtf = total_time / total_length; | 299 | float rtf = total_time / total_length; |
| 300 | - fprintf(stderr, "Real time factor (RTF): %.6f / %.6f = %.4f\n", | ||
| 301 | - total_time, total_length, rtf); | 300 | + fprintf(stderr, "Real time factor (RTF): %.6f / %.6f = %.4f\n", total_time, |
| 301 | + total_length, rtf); | ||
| 302 | fprintf(stderr, "SPEEDUP: %.4f\n", 1.0 / rtf); | 302 | fprintf(stderr, "SPEEDUP: %.4f\n", 1.0 / rtf); |
| 303 | 303 | ||
| 304 | return 0; | 304 | return 0; |
| @@ -37,6 +37,29 @@ class SileroVadModel::Impl { | @@ -37,6 +37,29 @@ class SileroVadModel::Impl { | ||
| 37 | min_speech_samples_ = sample_rate_ * config_.silero_vad.min_speech_duration; | 37 | min_speech_samples_ = sample_rate_ * config_.silero_vad.min_speech_duration; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | +#if __ANDROID_API__ >= 9 | ||
| 41 | + Impl(AAssetManager *mgr, const VadModelConfig &config) | ||
| 42 | + : config_(config), | ||
| 43 | + env_(ORT_LOGGING_LEVEL_ERROR), | ||
| 44 | + sess_opts_(GetSessionOptions(config)), | ||
| 45 | + allocator_{} { | ||
| 46 | + auto buf = ReadFile(mgr, config.silero_vad.model); | ||
| 47 | + Init(buf.data(), buf.size()); | ||
| 48 | + | ||
| 49 | + sample_rate_ = config.sample_rate; | ||
| 50 | + if (sample_rate_ != 16000) { | ||
| 51 | + SHERPA_ONNX_LOGE("Expected sample rate 16000. Given: %d", | ||
| 52 | + config.sample_rate); | ||
| 53 | + exit(-1); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + min_silence_samples_ = | ||
| 57 | + sample_rate_ * config_.silero_vad.min_silence_duration; | ||
| 58 | + | ||
| 59 | + min_speech_samples_ = sample_rate_ * config_.silero_vad.min_speech_duration; | ||
| 60 | + } | ||
| 61 | +#endif | ||
| 62 | + | ||
| 40 | void Reset() { | 63 | void Reset() { |
| 41 | // 2 - number of LSTM layer | 64 | // 2 - number of LSTM layer |
| 42 | // 1 - batch size | 65 | // 1 - batch size |
| @@ -260,6 +283,11 @@ class SileroVadModel::Impl { | @@ -260,6 +283,11 @@ class SileroVadModel::Impl { | ||
| 260 | SileroVadModel::SileroVadModel(const VadModelConfig &config) | 283 | SileroVadModel::SileroVadModel(const VadModelConfig &config) |
| 261 | : impl_(std::make_unique<Impl>(config)) {} | 284 | : impl_(std::make_unique<Impl>(config)) {} |
| 262 | 285 | ||
| 286 | +#if __ANDROID_API__ >= 9 | ||
| 287 | +SileroVadModel::SileroVadModel(AAssetManager *mgr, const VadModelConfig &config) | ||
| 288 | + : impl_(std::make_unique<Impl>(mgr, config)) {} | ||
| 289 | +#endif | ||
| 290 | + | ||
| 263 | SileroVadModel::~SileroVadModel() = default; | 291 | SileroVadModel::~SileroVadModel() = default; |
| 264 | 292 | ||
| 265 | void SileroVadModel::Reset() { return impl_->Reset(); } | 293 | void SileroVadModel::Reset() { return impl_->Reset(); } |
| @@ -6,6 +6,11 @@ | @@ -6,6 +6,11 @@ | ||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 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/vad-model.h" | 14 | #include "sherpa-onnx/csrc/vad-model.h" |
| 10 | 15 | ||
| 11 | namespace sherpa_onnx { | 16 | namespace sherpa_onnx { |
| @@ -13,6 +18,11 @@ namespace sherpa_onnx { | @@ -13,6 +18,11 @@ namespace sherpa_onnx { | ||
| 13 | class SileroVadModel : public VadModel { | 18 | class SileroVadModel : public VadModel { |
| 14 | public: | 19 | public: |
| 15 | explicit SileroVadModel(const VadModelConfig &config); | 20 | explicit SileroVadModel(const VadModelConfig &config); |
| 21 | + | ||
| 22 | +#if __ANDROID_API__ >= 9 | ||
| 23 | + SileroVadModel(AAssetManager *mgr, const VadModelConfig &config); | ||
| 24 | +#endif | ||
| 25 | + | ||
| 16 | ~SileroVadModel() override; | 26 | ~SileroVadModel() override; |
| 17 | 27 | ||
| 18 | // reset the internal model states | 28 | // reset the internal model states |
| @@ -13,4 +13,12 @@ std::unique_ptr<VadModel> VadModel::Create(const VadModelConfig &config) { | @@ -13,4 +13,12 @@ std::unique_ptr<VadModel> VadModel::Create(const VadModelConfig &config) { | ||
| 13 | return std::make_unique<SileroVadModel>(config); | 13 | return std::make_unique<SileroVadModel>(config); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | +#if __ANDROID_API__ >= 9 | ||
| 17 | +std::unique_ptr<VadModel> VadModel::Create(AAssetManager *mgr, | ||
| 18 | + const VadModelConfig &config) { | ||
| 19 | + // TODO(fangjun): Support other VAD models. | ||
| 20 | + return std::make_unique<SileroVadModel>(mgr, config); | ||
| 21 | +} | ||
| 22 | +#endif | ||
| 23 | + | ||
| 16 | } // namespace sherpa_onnx | 24 | } // namespace sherpa_onnx |
| @@ -6,6 +6,11 @@ | @@ -6,6 +6,11 @@ | ||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 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/vad-model-config.h" | 14 | #include "sherpa-onnx/csrc/vad-model-config.h" |
| 10 | 15 | ||
| 11 | namespace sherpa_onnx { | 16 | namespace sherpa_onnx { |
| @@ -16,6 +21,11 @@ class VadModel { | @@ -16,6 +21,11 @@ class VadModel { | ||
| 16 | 21 | ||
| 17 | static std::unique_ptr<VadModel> Create(const VadModelConfig &config); | 22 | static std::unique_ptr<VadModel> Create(const VadModelConfig &config); |
| 18 | 23 | ||
| 24 | +#if __ANDROID_API__ >= 9 | ||
| 25 | + static std::unique_ptr<VadModel> Create(AAssetManager *mgr, | ||
| 26 | + const VadModelConfig &config); | ||
| 27 | +#endif | ||
| 28 | + | ||
| 19 | // reset the internal model states | 29 | // reset the internal model states |
| 20 | virtual void Reset() = 0; | 30 | virtual void Reset() = 0; |
| 21 | 31 |
| @@ -19,10 +19,32 @@ class VoiceActivityDetector::Impl { | @@ -19,10 +19,32 @@ class VoiceActivityDetector::Impl { | ||
| 19 | config_(config), | 19 | config_(config), |
| 20 | buffer_(buffer_size_in_seconds * config.sample_rate) {} | 20 | buffer_(buffer_size_in_seconds * config.sample_rate) {} |
| 21 | 21 | ||
| 22 | +#if __ANDROID_API__ >= 9 | ||
| 23 | + Impl(AAssetManager *mgr, const VadModelConfig &config, | ||
| 24 | + float buffer_size_in_seconds = 60) | ||
| 25 | + : model_(VadModel::Create(mgr, config)), | ||
| 26 | + config_(config), | ||
| 27 | + buffer_(buffer_size_in_seconds * config.sample_rate) {} | ||
| 28 | +#endif | ||
| 29 | + | ||
| 22 | void AcceptWaveform(const float *samples, int32_t n) { | 30 | void AcceptWaveform(const float *samples, int32_t n) { |
| 23 | - buffer_.Push(samples, n); | 31 | + int32_t window_size = model_->WindowSize(); |
| 32 | + | ||
| 33 | + // note n is usally window_size and there is no need to use | ||
| 34 | + // an extra buffer here | ||
| 35 | + last_.insert(last_.end(), samples, samples + n); | ||
| 36 | + int32_t k = static_cast<int32_t>(last_.size()) / window_size; | ||
| 37 | + const float *p = last_.data(); | ||
| 38 | + bool is_speech = false; | ||
| 39 | + | ||
| 40 | + for (int32_t i = 0; i != k; ++i, p += window_size) { | ||
| 41 | + buffer_.Push(p, window_size); | ||
| 42 | + is_speech = model_->IsSpeech(p, window_size); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + last_ = std::vector<float>( | ||
| 46 | + p, static_cast<const float *>(last_.data()) + last_.size()); | ||
| 24 | 47 | ||
| 25 | - bool is_speech = model_->IsSpeech(samples, n); | ||
| 26 | if (is_speech) { | 48 | if (is_speech) { |
| 27 | if (start_ == -1) { | 49 | if (start_ == -1) { |
| 28 | // beginning of speech | 50 | // beginning of speech |
| @@ -31,15 +53,15 @@ class VoiceActivityDetector::Impl { | @@ -31,15 +53,15 @@ class VoiceActivityDetector::Impl { | ||
| 31 | } | 53 | } |
| 32 | } else { | 54 | } else { |
| 33 | // non-speech | 55 | // non-speech |
| 34 | - if (start_ != -1) { | 56 | + if (start_ != -1 && buffer_.Size()) { |
| 35 | // end of speech, save the speech segment | 57 | // end of speech, save the speech segment |
| 36 | int32_t end = buffer_.Tail() - model_->MinSilenceDurationSamples(); | 58 | int32_t end = buffer_.Tail() - model_->MinSilenceDurationSamples(); |
| 37 | 59 | ||
| 38 | - std::vector<float> samples = buffer_.Get(start_, end - start_); | 60 | + std::vector<float> s = buffer_.Get(start_, end - start_); |
| 39 | SpeechSegment segment; | 61 | SpeechSegment segment; |
| 40 | 62 | ||
| 41 | segment.start = start_; | 63 | segment.start = start_; |
| 42 | - segment.samples = std::move(samples); | 64 | + segment.samples = std::move(s); |
| 43 | 65 | ||
| 44 | segments_.push(std::move(segment)); | 66 | segments_.push(std::move(segment)); |
| 45 | 67 | ||
| @@ -73,6 +95,7 @@ class VoiceActivityDetector::Impl { | @@ -73,6 +95,7 @@ class VoiceActivityDetector::Impl { | ||
| 73 | std::unique_ptr<VadModel> model_; | 95 | std::unique_ptr<VadModel> model_; |
| 74 | VadModelConfig config_; | 96 | VadModelConfig config_; |
| 75 | CircularBuffer buffer_; | 97 | CircularBuffer buffer_; |
| 98 | + std::vector<float> last_; | ||
| 76 | 99 | ||
| 77 | int32_t start_ = -1; | 100 | int32_t start_ = -1; |
| 78 | }; | 101 | }; |
| @@ -81,6 +104,13 @@ VoiceActivityDetector::VoiceActivityDetector( | @@ -81,6 +104,13 @@ VoiceActivityDetector::VoiceActivityDetector( | ||
| 81 | const VadModelConfig &config, float buffer_size_in_seconds /*= 60*/) | 104 | const VadModelConfig &config, float buffer_size_in_seconds /*= 60*/) |
| 82 | : impl_(std::make_unique<Impl>(config, buffer_size_in_seconds)) {} | 105 | : impl_(std::make_unique<Impl>(config, buffer_size_in_seconds)) {} |
| 83 | 106 | ||
| 107 | +#if __ANDROID_API__ >= 9 | ||
| 108 | +VoiceActivityDetector::VoiceActivityDetector( | ||
| 109 | + AAssetManager *mgr, const VadModelConfig &config, | ||
| 110 | + float buffer_size_in_seconds /*= 60*/) | ||
| 111 | + : impl_(std::make_unique<Impl>(mgr, config, buffer_size_in_seconds)) {} | ||
| 112 | +#endif | ||
| 113 | + | ||
| 84 | VoiceActivityDetector::~VoiceActivityDetector() = default; | 114 | VoiceActivityDetector::~VoiceActivityDetector() = default; |
| 85 | 115 | ||
| 86 | void VoiceActivityDetector::AcceptWaveform(const float *samples, int32_t n) { | 116 | void VoiceActivityDetector::AcceptWaveform(const float *samples, int32_t n) { |
| @@ -7,6 +7,11 @@ | @@ -7,6 +7,11 @@ | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <vector> | 8 | #include <vector> |
| 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/vad-model-config.h" | 15 | #include "sherpa-onnx/csrc/vad-model-config.h" |
| 11 | 16 | ||
| 12 | namespace sherpa_onnx { | 17 | namespace sherpa_onnx { |
| @@ -20,6 +25,12 @@ class VoiceActivityDetector { | @@ -20,6 +25,12 @@ class VoiceActivityDetector { | ||
| 20 | public: | 25 | public: |
| 21 | explicit VoiceActivityDetector(const VadModelConfig &config, | 26 | explicit VoiceActivityDetector(const VadModelConfig &config, |
| 22 | float buffer_size_in_seconds = 60); | 27 | float buffer_size_in_seconds = 60); |
| 28 | + | ||
| 29 | +#if __ANDROID_API__ >= 9 | ||
| 30 | + VoiceActivityDetector(AAssetManager *mgr, const VadModelConfig &config, | ||
| 31 | + float buffer_size_in_seconds = 60); | ||
| 32 | +#endif | ||
| 33 | + | ||
| 23 | ~VoiceActivityDetector(); | 34 | ~VoiceActivityDetector(); |
| 24 | 35 | ||
| 25 | void AcceptWaveform(const float *samples, int32_t n); | 36 | void AcceptWaveform(const float *samples, int32_t n); |
| @@ -23,6 +23,7 @@ | @@ -23,6 +23,7 @@ | ||
| 23 | #include "sherpa-onnx/csrc/offline-recognizer.h" | 23 | #include "sherpa-onnx/csrc/offline-recognizer.h" |
| 24 | #include "sherpa-onnx/csrc/online-recognizer.h" | 24 | #include "sherpa-onnx/csrc/online-recognizer.h" |
| 25 | #include "sherpa-onnx/csrc/onnx-utils.h" | 25 | #include "sherpa-onnx/csrc/onnx-utils.h" |
| 26 | +#include "sherpa-onnx/csrc/voice-activity-detector.h" | ||
| 26 | #include "sherpa-onnx/csrc/wave-reader.h" | 27 | #include "sherpa-onnx/csrc/wave-reader.h" |
| 27 | 28 | ||
| 28 | #define SHERPA_ONNX_EXTERN_C extern "C" | 29 | #define SHERPA_ONNX_EXTERN_C extern "C" |
| @@ -106,6 +107,33 @@ class SherpaOnnxOffline { | @@ -106,6 +107,33 @@ class SherpaOnnxOffline { | ||
| 106 | OfflineRecognizer recognizer_; | 107 | OfflineRecognizer recognizer_; |
| 107 | }; | 108 | }; |
| 108 | 109 | ||
| 110 | +class SherpaOnnxVad { | ||
| 111 | + public: | ||
| 112 | +#if __ANDROID_API__ >= 9 | ||
| 113 | + SherpaOnnxVad(AAssetManager *mgr, const VadModelConfig &config) | ||
| 114 | + : vad_(mgr, config) {} | ||
| 115 | +#endif | ||
| 116 | + | ||
| 117 | + explicit SherpaOnnxVad(const VadModelConfig &config) : vad_(config) {} | ||
| 118 | + | ||
| 119 | + void AcceptWaveform(const float *samples, int32_t n) { | ||
| 120 | + vad_.AcceptWaveform(samples, n); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + bool Empty() const { return vad_.Empty(); } | ||
| 124 | + | ||
| 125 | + void Pop() { vad_.Pop(); } | ||
| 126 | + | ||
| 127 | + const SpeechSegment &Front() const { return vad_.Front(); } | ||
| 128 | + | ||
| 129 | + bool IsSpeechDetected() const { return vad_.IsSpeechDetected(); } | ||
| 130 | + | ||
| 131 | + void Reset() { vad_.Reset(); } | ||
| 132 | + | ||
| 133 | + private: | ||
| 134 | + VoiceActivityDetector vad_; | ||
| 135 | +}; | ||
| 136 | + | ||
| 109 | static OnlineRecognizerConfig GetConfig(JNIEnv *env, jobject config) { | 137 | static OnlineRecognizerConfig GetConfig(JNIEnv *env, jobject config) { |
| 110 | OnlineRecognizerConfig ans; | 138 | OnlineRecognizerConfig ans; |
| 111 | 139 | ||
| @@ -411,9 +439,166 @@ static OfflineRecognizerConfig GetOfflineConfig(JNIEnv *env, jobject config) { | @@ -411,9 +439,166 @@ static OfflineRecognizerConfig GetOfflineConfig(JNIEnv *env, jobject config) { | ||
| 411 | return ans; | 439 | return ans; |
| 412 | } | 440 | } |
| 413 | 441 | ||
| 442 | +static VadModelConfig GetVadModelConfig(JNIEnv *env, jobject config) { | ||
| 443 | + VadModelConfig ans; | ||
| 444 | + | ||
| 445 | + jclass cls = env->GetObjectClass(config); | ||
| 446 | + jfieldID fid; | ||
| 447 | + | ||
| 448 | + // silero_vad | ||
| 449 | + fid = env->GetFieldID(cls, "sileroVadModelConfig", | ||
| 450 | + "Lcom/k2fsa/sherpa/onnx/SileroVadModelConfig;"); | ||
| 451 | + jobject silero_vad_config = env->GetObjectField(config, fid); | ||
| 452 | + jclass silero_vad_config_cls = env->GetObjectClass(silero_vad_config); | ||
| 453 | + | ||
| 454 | + fid = env->GetFieldID(silero_vad_config_cls, "model", "Ljava/lang/String;"); | ||
| 455 | + auto s = (jstring)env->GetObjectField(silero_vad_config, fid); | ||
| 456 | + auto p = env->GetStringUTFChars(s, nullptr); | ||
| 457 | + ans.silero_vad.model = p; | ||
| 458 | + env->ReleaseStringUTFChars(s, p); | ||
| 459 | + | ||
| 460 | + fid = env->GetFieldID(silero_vad_config_cls, "threshold", "F"); | ||
| 461 | + ans.silero_vad.threshold = env->GetFloatField(silero_vad_config, fid); | ||
| 462 | + | ||
| 463 | + fid = env->GetFieldID(silero_vad_config_cls, "minSilenceDuration", "F"); | ||
| 464 | + ans.silero_vad.min_silence_duration = | ||
| 465 | + env->GetFloatField(silero_vad_config, fid); | ||
| 466 | + | ||
| 467 | + fid = env->GetFieldID(silero_vad_config_cls, "minSpeechDuration", "F"); | ||
| 468 | + ans.silero_vad.min_speech_duration = | ||
| 469 | + env->GetFloatField(silero_vad_config, fid); | ||
| 470 | + | ||
| 471 | + fid = env->GetFieldID(silero_vad_config_cls, "windowSize", "I"); | ||
| 472 | + ans.silero_vad.window_size = env->GetIntField(silero_vad_config, fid); | ||
| 473 | + | ||
| 474 | + fid = env->GetFieldID(cls, "sampleRate", "I"); | ||
| 475 | + ans.sample_rate = env->GetIntField(config, fid); | ||
| 476 | + | ||
| 477 | + fid = env->GetFieldID(cls, "numThreads", "I"); | ||
| 478 | + ans.num_threads = env->GetIntField(config, fid); | ||
| 479 | + | ||
| 480 | + fid = env->GetFieldID(cls, "provider", "Ljava/lang/String;"); | ||
| 481 | + s = (jstring)env->GetObjectField(config, fid); | ||
| 482 | + p = env->GetStringUTFChars(s, nullptr); | ||
| 483 | + ans.provider = p; | ||
| 484 | + env->ReleaseStringUTFChars(s, p); | ||
| 485 | + | ||
| 486 | + fid = env->GetFieldID(cls, "debug", "Z"); | ||
| 487 | + ans.debug = env->GetBooleanField(config, fid); | ||
| 488 | + | ||
| 489 | + return ans; | ||
| 490 | +} | ||
| 491 | + | ||
| 414 | } // namespace sherpa_onnx | 492 | } // namespace sherpa_onnx |
| 415 | 493 | ||
| 416 | SHERPA_ONNX_EXTERN_C | 494 | SHERPA_ONNX_EXTERN_C |
| 495 | +JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_Vad_new( | ||
| 496 | + JNIEnv *env, jobject /*obj*/, jobject asset_manager, jobject _config) { | ||
| 497 | +#if __ANDROID_API__ >= 9 | ||
| 498 | + AAssetManager *mgr = AAssetManager_fromJava(env, asset_manager); | ||
| 499 | + if (!mgr) { | ||
| 500 | + SHERPA_ONNX_LOGE("Failed to get asset manager: %p", mgr); | ||
| 501 | + } | ||
| 502 | +#endif | ||
| 503 | + auto config = sherpa_onnx::GetVadModelConfig(env, _config); | ||
| 504 | + SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); | ||
| 505 | + auto model = new sherpa_onnx::SherpaOnnxVad( | ||
| 506 | +#if __ANDROID_API__ >= 9 | ||
| 507 | + mgr, | ||
| 508 | +#endif | ||
| 509 | + config); | ||
| 510 | + | ||
| 511 | + return (jlong)model; | ||
| 512 | +} | ||
| 513 | + | ||
| 514 | +JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_Vad_newFromFile( | ||
| 515 | + JNIEnv *env, jobject /*obj*/, jobject _config) { | ||
| 516 | + auto config = sherpa_onnx::GetVadModelConfig(env, _config); | ||
| 517 | + SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); | ||
| 518 | + auto model = new sherpa_onnx::SherpaOnnxVad(config); | ||
| 519 | + | ||
| 520 | + return (jlong)model; | ||
| 521 | +} | ||
| 522 | + | ||
| 523 | +SHERPA_ONNX_EXTERN_C | ||
| 524 | +JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_delete(JNIEnv *env, | ||
| 525 | + jobject /*obj*/, | ||
| 526 | + jlong ptr) { | ||
| 527 | + delete reinterpret_cast<sherpa_onnx::SherpaOnnxVad *>(ptr); | ||
| 528 | +} | ||
| 529 | + | ||
| 530 | +SHERPA_ONNX_EXTERN_C | ||
| 531 | +JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_acceptWaveform( | ||
| 532 | + JNIEnv *env, jobject /*obj*/, jlong ptr, jfloatArray samples) { | ||
| 533 | + auto model = reinterpret_cast<sherpa_onnx::SherpaOnnxVad *>(ptr); | ||
| 534 | + | ||
| 535 | + jfloat *p = env->GetFloatArrayElements(samples, nullptr); | ||
| 536 | + jsize n = env->GetArrayLength(samples); | ||
| 537 | + | ||
| 538 | + model->AcceptWaveform(p, n); | ||
| 539 | + | ||
| 540 | + env->ReleaseFloatArrayElements(samples, p, JNI_ABORT); | ||
| 541 | +} | ||
| 542 | + | ||
| 543 | +SHERPA_ONNX_EXTERN_C | ||
| 544 | +JNIEXPORT bool JNICALL Java_com_k2fsa_sherpa_onnx_Vad_empty(JNIEnv *env, | ||
| 545 | + jobject /*obj*/, | ||
| 546 | + jlong ptr) { | ||
| 547 | + auto model = reinterpret_cast<sherpa_onnx::SherpaOnnxVad *>(ptr); | ||
| 548 | + return model->Empty(); | ||
| 549 | +} | ||
| 550 | + | ||
| 551 | +SHERPA_ONNX_EXTERN_C | ||
| 552 | +JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_pop(JNIEnv *env, | ||
| 553 | + jobject /*obj*/, | ||
| 554 | + jlong ptr) { | ||
| 555 | + auto model = reinterpret_cast<sherpa_onnx::SherpaOnnxVad *>(ptr); | ||
| 556 | + model->Pop(); | ||
| 557 | +} | ||
| 558 | + | ||
| 559 | +// see | ||
| 560 | +// https://stackoverflow.com/questions/29043872/android-jni-return-multiple-variables | ||
| 561 | +static jobject NewInteger(JNIEnv *env, int32_t value) { | ||
| 562 | + jclass cls = env->FindClass("java/lang/Integer"); | ||
| 563 | + jmethodID constructor = env->GetMethodID(cls, "<init>", "(I)V"); | ||
| 564 | + return env->NewObject(cls, constructor, value); | ||
| 565 | +} | ||
| 566 | + | ||
| 567 | +SHERPA_ONNX_EXTERN_C | ||
| 568 | +JNIEXPORT jobjectArray JNICALL | ||
| 569 | +Java_com_k2fsa_sherpa_onnx_Vad_front(JNIEnv *env, jobject /*obj*/, jlong ptr) { | ||
| 570 | + const auto &front = | ||
| 571 | + reinterpret_cast<sherpa_onnx::SherpaOnnxVad *>(ptr)->Front(); | ||
| 572 | + | ||
| 573 | + jfloatArray samples_arr = env->NewFloatArray(front.samples.size()); | ||
| 574 | + env->SetFloatArrayRegion(samples_arr, 0, front.samples.size(), | ||
| 575 | + front.samples.data()); | ||
| 576 | + | ||
| 577 | + jobjectArray obj_arr = (jobjectArray)env->NewObjectArray( | ||
| 578 | + 2, env->FindClass("java/lang/Object"), nullptr); | ||
| 579 | + | ||
| 580 | + env->SetObjectArrayElement(obj_arr, 0, NewInteger(env, front.start)); | ||
| 581 | + env->SetObjectArrayElement(obj_arr, 1, samples_arr); | ||
| 582 | + | ||
| 583 | + return obj_arr; | ||
| 584 | +} | ||
| 585 | + | ||
| 586 | +SHERPA_ONNX_EXTERN_C | ||
| 587 | +JNIEXPORT bool JNICALL Java_com_k2fsa_sherpa_onnx_Vad_isSpeechDetected( | ||
| 588 | + JNIEnv *env, jobject /*obj*/, jlong ptr) { | ||
| 589 | + auto model = reinterpret_cast<sherpa_onnx::SherpaOnnxVad *>(ptr); | ||
| 590 | + return model->IsSpeechDetected(); | ||
| 591 | +} | ||
| 592 | + | ||
| 593 | +SHERPA_ONNX_EXTERN_C | ||
| 594 | +JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_reset(JNIEnv *env, | ||
| 595 | + jobject /*obj*/, | ||
| 596 | + jlong ptr) { | ||
| 597 | + auto model = reinterpret_cast<sherpa_onnx::SherpaOnnxVad *>(ptr); | ||
| 598 | + model->Reset(); | ||
| 599 | +} | ||
| 600 | + | ||
| 601 | +SHERPA_ONNX_EXTERN_C | ||
| 417 | JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_SherpaOnnx_new( | 602 | JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_SherpaOnnx_new( |
| 418 | JNIEnv *env, jobject /*obj*/, jobject asset_manager, jobject _config) { | 603 | JNIEnv *env, jobject /*obj*/, jobject asset_manager, jobject _config) { |
| 419 | #if __ANDROID_API__ >= 9 | 604 | #if __ANDROID_API__ >= 9 |
| @@ -564,12 +749,12 @@ SHERPA_ONNX_EXTERN_C | @@ -564,12 +749,12 @@ SHERPA_ONNX_EXTERN_C | ||
| 564 | JNIEXPORT jobjectArray JNICALL Java_com_k2fsa_sherpa_onnx_SherpaOnnx_getTokens( | 749 | JNIEXPORT jobjectArray JNICALL Java_com_k2fsa_sherpa_onnx_SherpaOnnx_getTokens( |
| 565 | JNIEnv *env, jobject /*obj*/, jlong ptr) { | 750 | JNIEnv *env, jobject /*obj*/, jlong ptr) { |
| 566 | auto tokens = reinterpret_cast<sherpa_onnx::SherpaOnnx *>(ptr)->GetTokens(); | 751 | auto tokens = reinterpret_cast<sherpa_onnx::SherpaOnnx *>(ptr)->GetTokens(); |
| 567 | - int size = tokens.size(); | 752 | + int32_t size = tokens.size(); |
| 568 | jclass stringClass = env->FindClass("java/lang/String"); | 753 | jclass stringClass = env->FindClass("java/lang/String"); |
| 569 | 754 | ||
| 570 | // convert C++ list into jni string array | 755 | // convert C++ list into jni string array |
| 571 | jobjectArray result = env->NewObjectArray(size, stringClass, NULL); | 756 | jobjectArray result = env->NewObjectArray(size, stringClass, NULL); |
| 572 | - for (int i = 0; i < size; i++) { | 757 | + for (int32_t i = 0; i < size; i++) { |
| 573 | // Convert the C++ string to a C string | 758 | // Convert the C++ string to a C string |
| 574 | const char *cstr = tokens[i].c_str(); | 759 | const char *cstr = tokens[i].c_str(); |
| 575 | 760 | ||
| @@ -583,14 +768,6 @@ JNIEXPORT jobjectArray JNICALL Java_com_k2fsa_sherpa_onnx_SherpaOnnx_getTokens( | @@ -583,14 +768,6 @@ JNIEXPORT jobjectArray JNICALL Java_com_k2fsa_sherpa_onnx_SherpaOnnx_getTokens( | ||
| 583 | return result; | 768 | return result; |
| 584 | } | 769 | } |
| 585 | 770 | ||
| 586 | -// see | ||
| 587 | -// https://stackoverflow.com/questions/29043872/android-jni-return-multiple-variables | ||
| 588 | -static jobject NewInteger(JNIEnv *env, int32_t value) { | ||
| 589 | - jclass cls = env->FindClass("java/lang/Integer"); | ||
| 590 | - jmethodID constructor = env->GetMethodID(cls, "<init>", "(I)V"); | ||
| 591 | - return env->NewObject(cls, constructor, value); | ||
| 592 | -} | ||
| 593 | - | ||
| 594 | static jobjectArray ReadWaveImpl(JNIEnv *env, std::istream &is, | 771 | static jobjectArray ReadWaveImpl(JNIEnv *env, std::istream &is, |
| 595 | const char *p_filename) { | 772 | const char *p_filename) { |
| 596 | bool is_ok = false; | 773 | bool is_ok = false; |
-
请 注册 或 登录 后发表评论