Skip to content

Commit e736b13

Browse files
edgchen1MaanavD
andauthored
[Android] Speech recognition example (#236)
Add speech recognition example for Android using Whisper model. Thanks @MaanavD for help with the UI. --------- Co-authored-by: MaanavD <[email protected]> Co-authored-by: maanavd <[email protected]>
1 parent cc2f5ef commit e736b13

File tree

72 files changed

+1662
-83
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1662
-83
lines changed

ci_build/azure_pipelines/mobile-examples-pipeline.yml

+50-82
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,27 @@ jobs:
8282
scheme: 'OrtBasicUsage'
8383
args: $(XcodeArgs)
8484

85+
# mobile/examples/speech_recognition/android
86+
- job: SpeechRecognitionAndroid
87+
pool:
88+
vmImage: "macOS-12"
89+
90+
steps:
91+
- template: templates/use-python-step.yml
92+
93+
- template: templates/use-jdk-step.yml
94+
parameters:
95+
jdkVersion: "17"
96+
97+
- template: templates/run-with-android-emulator-steps.yml
98+
parameters:
99+
steps:
100+
- bash: |
101+
set -e
102+
./gradlew connectedDebugAndroidTest --no-daemon
103+
workingDirectory: mobile/examples/speech_recognition/android
104+
displayName: "Build and run tests"
105+
85106
# mobile/examples/speech_recognition/ios
86107
- job: SpeechRecognitionIos
87108
pool:
@@ -185,12 +206,7 @@ jobs:
185206
steps:
186207
- template: templates/use-python-step.yml
187208

188-
- task: JavaToolInstaller@0
189-
displayName: Use jdk 11
190-
inputs:
191-
versionSpec: "11"
192-
jdkArchitectureOption: "x64"
193-
jdkSourceOption: "PreInstalled"
209+
- template: templates/use-jdk-step.yml
194210

195211
- bash: |
196212
set -e
@@ -208,25 +224,12 @@ jobs:
208224
cat ${GRADLE_FILE}
209225
displayName: "Update build.gradle"
210226
211-
- script: |
212-
python3 ./ci_build/python/run_android_emulator.py \
213-
--android-sdk-root ${ANDROID_SDK_ROOT} \
214-
--create-avd --system-image "system-images;android-30;default;x86_64" \
215-
--start --emulator-extra-args="-partition-size 4096" \
216-
--emulator-pid-file $(Build.BinariesDirectory)/emulator.pid
217-
displayName: "Start Android emulator"
218-
219-
- bash: ./gradlew testDebugUnitTest connectedDebugAndroidTest
220-
workingDirectory: mobile/examples/image_classification/android
221-
displayName: "Build and run tests"
222-
223-
- script: |
224-
python3 ./ci_build/python/run_android_emulator.py \
225-
--android-sdk-root ${ANDROID_SDK_ROOT} \
226-
--stop \
227-
--emulator-pid-file $(Build.BinariesDirectory)/emulator.pid
228-
displayName: "Stop Android emulator"
229-
condition: always()
227+
- template: templates/run-with-android-emulator-steps.yml
228+
parameters:
229+
steps:
230+
- bash: ./gradlew connectedDebugAndroidTest --no-daemon
231+
workingDirectory: mobile/examples/image_classification/android
232+
displayName: "Build and run tests"
230233

231234
# Note: start with testing with the included aar package,
232235
# can update with testing with Full/Mobile packages as the other samples later.
@@ -238,36 +241,17 @@ jobs:
238241

239242
steps:
240243
- template: templates/use-python-step.yml
241-
242-
- script: |
243-
python3 ./ci_build/python/run_android_emulator.py \
244-
--android-sdk-root ${ANDROID_SDK_ROOT} \
245-
--create-avd --system-image "system-images;android-30;default;x86_64" \
246-
--start --emulator-extra-args="-partition-size 4096" \
247-
--emulator-pid-file $(Build.BinariesDirectory)/emulator.pid
248-
displayName: "Start Android emulator"
249244

250-
- task: JavaToolInstaller@0
251-
displayName: Use jdk 11
252-
inputs:
253-
versionSpec: "11"
254-
jdkArchitectureOption: "x64"
255-
jdkSourceOption: "PreInstalled"
245+
- template: templates/use-jdk-step.yml
256246

257-
- bash: |
258-
set -e
259-
chmod +x ./gradlew
260-
./gradlew connectedDebugAndroidTest --no-daemon
261-
workingDirectory: mobile/examples/super_resolution/android
262-
displayName: "Build and run tests"
263-
264-
- script: |
265-
python ./ci_build/python/run_android_emulator.py \
266-
--android-sdk-root ${ANDROID_SDK_ROOT} \
267-
--stop \
268-
--emulator-pid-file $(Build.BinariesDirectory)/emulator.pid
269-
displayName: "Stop Android emulator"
270-
condition: always()
247+
- template: templates/run-with-android-emulator-steps.yml
248+
parameters:
249+
steps:
250+
- bash: |
251+
set -e
252+
./gradlew connectedDebugAndroidTest --no-daemon
253+
workingDirectory: mobile/examples/super_resolution/android
254+
displayName: "Build and run tests"
271255
272256
# Note: start with testing with the pre-release version pods.
273257
# can update with testing with Full/Mobile pods as the other samples later.
@@ -288,6 +272,7 @@ jobs:
288272
xcWorkspacePath: 'mobile/examples/super_resolution/ios/ORTSuperResolution/ORTSuperResolution.xcworkspace'
289273
scheme: 'ORTSuperResolution'
290274

275+
# mobile/examples/question_answering/android
291276
- job: QuestionAnsweringAndroid
292277
pool:
293278
vmImage: "macOS-12"
@@ -299,36 +284,19 @@ jobs:
299284
bash ./prepare_model.sh
300285
workingDirectory: 'mobile/examples/question_answering/android'
301286
displayName: "Generate model"
302-
303-
- script: |
304-
python3 ./ci_build/python/run_android_emulator.py \
305-
--android-sdk-root ${ANDROID_SDK_ROOT} \
306-
--create-avd --system-image "system-images;android-30;default;x86_64" \
307-
--start --emulator-extra-args="-partition-size 4096" \
308-
--emulator-pid-file $(Build.BinariesDirectory)/emulator.pid
309-
displayName: "Start Android emulator"
310287
311-
- task: JavaToolInstaller@0
312-
displayName: Use jdk 11
313-
inputs:
314-
versionSpec: "11"
315-
jdkArchitectureOption: "x64"
316-
jdkSourceOption: "PreInstalled"
317-
318-
- bash: |
319-
set -e
320-
./gradlew connectedDebugAndroidTest --no-daemon
321-
workingDirectory: mobile/examples/question_answering/android
322-
displayName: "Build and run tests"
323-
324-
- script: |
325-
python ./ci_build/python/run_android_emulator.py \
326-
--android-sdk-root ${ANDROID_SDK_ROOT} \
327-
--stop \
328-
--emulator-pid-file $(Build.BinariesDirectory)/emulator.pid
329-
displayName: "Stop Android emulator"
330-
condition: always()
288+
- template: templates/use-jdk-step.yml
331289

290+
- template: templates/run-with-android-emulator-steps.yml
291+
parameters:
292+
steps:
293+
- bash: |
294+
set -e
295+
./gradlew connectedDebugAndroidTest --no-daemon
296+
workingDirectory: mobile/examples/question_answering/android
297+
displayName: "Build and run tests"
298+
299+
# mobile/examples/question_answering/ios
332300
- job: QuestionAnsweringIos
333301
pool:
334302
vmImage: "macOS-12"
@@ -341,4 +309,4 @@ jobs:
341309
- template: templates/xcode-build-and-test-step.yml
342310
parameters:
343311
xcWorkspacePath: 'mobile/examples/question_answering/ios/ORTQuestionAnswering/ORTQuestionAnswering.xcworkspace'
344-
scheme: 'ORTQuestionAnswering'
312+
scheme: 'ORTQuestionAnswering'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
parameters:
2+
- name: steps
3+
type: stepList
4+
5+
steps:
6+
- bash: |
7+
set -e -x
8+
9+
ORT_EXAMPLES_BUILD_ANDROID_EMULATOR_PID_FILE="$(Build.BinariesDirectory)/android_emulator.pid"
10+
11+
python ./ci_build/python/run_android_emulator.py \
12+
--android-sdk-root "${ANDROID_SDK_ROOT}" \
13+
--create-avd --system-image "system-images;android-30;default;x86_64" \
14+
--start --emulator-extra-args="-partition-size 4096" \
15+
--emulator-pid-file "${ORT_EXAMPLES_BUILD_ANDROID_EMULATOR_PID_FILE}"
16+
17+
set +x # do not output next line or it may be parsed again with a trailing quote
18+
echo "##vso[task.setvariable variable=ORT_EXAMPLES_BUILD_ANDROID_EMULATOR_PID_FILE]${ORT_EXAMPLES_BUILD_ANDROID_EMULATOR_PID_FILE}"
19+
displayName: "Create and start Android emulator"
20+
21+
- ${{ parameters.steps }}
22+
23+
- bash: |
24+
set -e -x
25+
26+
if [[ -n "${ORT_EXAMPLES_BUILD_ANDROID_EMULATOR_PID_FILE-}" ]]; then
27+
python ./ci_build/python/run_android_emulator.py \
28+
--android-sdk-root "${ANDROID_SDK_ROOT}" \
29+
--stop \
30+
--emulator-pid-file "${ORT_EXAMPLES_BUILD_ANDROID_EMULATOR_PID_FILE}"
31+
32+
rm "${ORT_EXAMPLES_BUILD_ANDROID_EMULATOR_PID_FILE}"
33+
fi
34+
displayName: "Stop Android emulator"
35+
condition: always()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
parameters:
2+
- name: jdkVersion
3+
type: string
4+
default: "11"
5+
6+
steps:
7+
- task: JavaToolInstaller@0
8+
displayName: Use jdk ${{ parameters.jdkVersion }}
9+
inputs:
10+
versionSpec: "${{ parameters.jdkVersion }}"
11+
jdkArchitectureOption: "x64"
12+
jdkSourceOption: "PreInstalled"

mobile/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ The example app uses image classification which is able to continuously classify
4040

4141
The example app uses speech recognition to transcribe speech from audio recorded by the device.
4242

43+
- [Android Speech Recognition](examples/speech_recognition/android)
4344
- [iOS Speech Recognition](examples/speech_recognition/ios)
4445

4546
### Object Detection
@@ -65,4 +66,4 @@ The example application accomplishes the task of recovering a high resolution (H
6566
The example app gives a demo of introducing question answering models with pre/post processing into mobile scenario. Currently supports on platform Android and iOS.
6667

6768
- [Android Question answering](examples/question_answering/android)
68-
- [iOS Question answering](examples/question_answering/ios)
69+
- [iOS Question answering](examples/question_answering/ios)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
plugins {
2+
id 'com.android.application'
3+
id 'org.jetbrains.kotlin.android'
4+
}
5+
6+
android {
7+
namespace 'ai.onnxruntime.example.speechrecognition'
8+
compileSdk 33
9+
10+
defaultConfig {
11+
applicationId "ai.onnxruntime.example.speechrecognition"
12+
minSdk 24
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+
implementation 'androidx.core:core-ktx:1.10.0'
37+
implementation 'androidx.appcompat:appcompat:1.6.1'
38+
implementation 'com.google.android.material:material:1.8.0'
39+
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
40+
41+
// TODO use released onnxruntime and onnxruntime-extensions packages
42+
//implementation 'com.microsoft.onnxruntime:onnxruntime-android:latest.release'
43+
//implementation 'com.microsoft.onnxruntime:onnxruntime-extensions-android:latest.release'
44+
implementation(name: "onnxruntime-android-1.15.0", ext: "aar")
45+
implementation(name: "onnxruntime-extensions-android-0.8.0", ext: "aar")
46+
47+
testImplementation 'junit:junit:4.13.2'
48+
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
49+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This directory contains pre-release versions of the onnxruntime and onnxruntime-extensions Android packages (AARs).
2+
When onnxruntime 1.15.0 and onnxruntime-extensions 0.8.0 are released, the released packages should be used directly.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package ai.onnxruntime.example.speechrecognition
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 SpeechRecognitionInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("ai.onnxruntime.example.speechrecognition", appContext.packageName)
23+
}
24+
25+
@Test
26+
fun runModelWithPrerecordedAudio() {
27+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
28+
29+
val modelBytes: ByteArray =
30+
appContext.resources.openRawResource(R.raw.whisper_cpu_int8_model).use {
31+
it.readBytes()
32+
}
33+
34+
SpeechRecognizer(modelBytes).use { speechRecognizer ->
35+
val audioTensor =
36+
appContext.resources.openRawResource(R.raw.audio_mono_16khz_f32le).use {
37+
AudioTensorSource.fromRawPcmBytes(it.readBytes())
38+
}
39+
40+
val result = audioTensor.use { speechRecognizer.run(audioTensor) }
41+
assertTrue(
42+
result.text.contains(
43+
"welcome to the speech recognition example application",
44+
ignoreCase = true
45+
)
46+
)
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)