Digipom commited on
Commit
f4bcbaa
·
unverified ·
1 Parent(s): 40eeba4

Add Android sample (#277)

Browse files

* Add Android sample

* Use main project C files

* Stop existing playback before starting new playback

* Make text scrollable

* Stop playback when starting to record

* Remove extra var

Files changed (42) hide show
  1. examples/whisper.android/.gitignore +15 -0
  2. examples/whisper.android/.idea/.gitignore +3 -0
  3. examples/whisper.android/.idea/.name +1 -0
  4. examples/whisper.android/.idea/compiler.xml +6 -0
  5. examples/whisper.android/.idea/gradle.xml +18 -0
  6. examples/whisper.android/.idea/misc.xml +10 -0
  7. examples/whisper.android/.idea/vcs.xml +6 -0
  8. examples/whisper.android/README.md +10 -0
  9. examples/whisper.android/app/.gitignore +1 -0
  10. examples/whisper.android/app/build.gradle +76 -0
  11. examples/whisper.android/app/proguard-rules.pro +21 -0
  12. examples/whisper.android/app/src/androidTest/java/com/whispercppdemo/ExampleInstrumentedTest.kt +24 -0
  13. examples/whisper.android/app/src/main/AndroidManifest.xml +32 -0
  14. examples/whisper.android/app/src/main/java/com/whispercppdemo/MainActivity.kt +22 -0
  15. examples/whisper.android/app/src/main/java/com/whispercppdemo/media/RiffWaveHelper.kt +76 -0
  16. examples/whisper.android/app/src/main/java/com/whispercppdemo/recorder/Recorder.kt +88 -0
  17. examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreen.kt +99 -0
  18. examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreenViewModel.kt +193 -0
  19. examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/theme/Color.kt +11 -0
  20. examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/theme/Theme.kt +68 -0
  21. examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/theme/Type.kt +34 -0
  22. examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt +61 -0
  23. examples/whisper.android/app/src/main/jni/whisper/Android.mk +22 -0
  24. examples/whisper.android/app/src/main/jni/whisper/Application.mk +1 -0
  25. examples/whisper.android/app/src/main/jni/whisper/jni.c +93 -0
  26. examples/whisper.android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  27. examples/whisper.android/app/src/main/res/drawable/ic_launcher_foreground.xml +30 -0
  28. examples/whisper.android/app/src/main/res/mipmap-anydpi/ic_launcher.xml +5 -0
  29. examples/whisper.android/app/src/main/res/values/colors.xml +10 -0
  30. examples/whisper.android/app/src/main/res/values/strings.xml +3 -0
  31. examples/whisper.android/app/src/main/res/values/themes.xml +5 -0
  32. examples/whisper.android/app/src/main/res/xml/backup_rules.xml +13 -0
  33. examples/whisper.android/app/src/main/res/xml/data_extraction_rules.xml +19 -0
  34. examples/whisper.android/app/src/test/java/com/whispercppdemo/ExampleUnitTest.kt +17 -0
  35. examples/whisper.android/build.gradle +6 -0
  36. examples/whisper.android/gradle.properties +23 -0
  37. examples/whisper.android/gradle/wrapper/gradle-wrapper.jar +0 -0
  38. examples/whisper.android/gradle/wrapper/gradle-wrapper.properties +6 -0
  39. examples/whisper.android/gradlew +185 -0
  40. examples/whisper.android/gradlew.bat +89 -0
  41. examples/whisper.android/local.properties +10 -0
  42. examples/whisper.android/settings.gradle +16 -0
examples/whisper.android/.gitignore ADDED
@@ -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
examples/whisper.android/.idea/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
examples/whisper.android/.idea/.name ADDED
@@ -0,0 +1 @@
 
 
1
+ WhisperCppDemo
examples/whisper.android/.idea/compiler.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="CompilerConfiguration">
4
+ <bytecodeTargetLevel target="11" />
5
+ </component>
6
+ </project>
examples/whisper.android/.idea/gradle.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="GradleSettings">
4
+ <option name="linkedExternalProjectsSettings">
5
+ <GradleProjectSettings>
6
+ <option name="testRunner" value="GRADLE" />
7
+ <option name="distributionType" value="DEFAULT_WRAPPED" />
8
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
9
+ <option name="modules">
10
+ <set>
11
+ <option value="$PROJECT_DIR$" />
12
+ <option value="$PROJECT_DIR$/app" />
13
+ </set>
14
+ </option>
15
+ </GradleProjectSettings>
16
+ </option>
17
+ </component>
18
+ </project>
examples/whisper.android/.idea/misc.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
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>
examples/whisper.android/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
5
+ </component>
6
+ </project>
examples/whisper.android/README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ A sample Android app using [whisper.cpp](https://github.com/ggerganov/whisper.cpp/) to do voice-to-text transcriptions.
2
+
3
+ To use:
4
+
5
+ 1. Select a model from the [whisper.cpp repository](https://github.com/ggerganov/whisper.cpp/tree/master/models).[^1]
6
+ 2. Copy the model to the "app/src/main/assets/models" folder.
7
+ 3. Select a sample audio file (for example, [jfk.wav](https://github.com/ggerganov/whisper.cpp/raw/master/samples/jfk.wav)).
8
+ 4. Copy the sample to the "app/src/main/assets/samples" folder.
9
+ 5. Select the "release" active build variant, and use Android Studio to run and deploy to your device.
10
+ [^1]: I recommend the tiny or base models for running on an Android device.
examples/whisper.android/app/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ /build
examples/whisper.android/app/build.gradle ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ plugins {
2
+ id 'com.android.application'
3
+ id 'org.jetbrains.kotlin.android'
4
+ }
5
+
6
+ android {
7
+ namespace 'com.whispercppdemo'
8
+ compileSdk 33
9
+
10
+ defaultConfig {
11
+ applicationId "com.whispercppdemo"
12
+ minSdk 26
13
+ targetSdk 32
14
+ versionCode 1
15
+ versionName "1.0"
16
+
17
+ ndk {
18
+ abiFilters 'arm64-v8a', 'x86_64'
19
+ }
20
+
21
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
22
+ vectorDrawables {
23
+ useSupportLibrary true
24
+ }
25
+ }
26
+
27
+ buildTypes {
28
+ release {
29
+ signingConfig signingConfigs.debug
30
+ minifyEnabled true
31
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
32
+ }
33
+ }
34
+ compileOptions {
35
+ sourceCompatibility JavaVersion.VERSION_1_8
36
+ targetCompatibility JavaVersion.VERSION_1_8
37
+ }
38
+ kotlinOptions {
39
+ jvmTarget = '1.8'
40
+ }
41
+ buildFeatures {
42
+ compose true
43
+ }
44
+ composeOptions {
45
+ kotlinCompilerExtensionVersion '1.3.1'
46
+ }
47
+ ndkVersion "25.0.8528842"
48
+ externalNativeBuild {
49
+ ndkBuild {
50
+ path 'src/main/jni/whisper/Android.mk'
51
+ }
52
+ }
53
+ packagingOptions {
54
+ resources {
55
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
56
+ }
57
+ }
58
+ }
59
+
60
+ dependencies {
61
+ implementation 'androidx.activity:activity-compose:1.6.1'
62
+ implementation 'androidx.compose.material:material-icons-core:1.3.1'
63
+ implementation 'androidx.compose.material3:material3:1.0.1'
64
+ implementation "androidx.compose.ui:ui:1.3.2"
65
+ implementation "androidx.compose.ui:ui-tooling-preview:1.3.2"
66
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
67
+ implementation "com.google.accompanist:accompanist-permissions:0.28.0"
68
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
69
+
70
+ testImplementation 'junit:junit:4.13.2'
71
+ androidTestImplementation 'androidx.test.ext:junit:1.1.4'
72
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
73
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.3.2"
74
+ debugImplementation "androidx.compose.ui:ui-tooling:1.3.2"
75
+ debugImplementation "androidx.compose.ui:ui-test-manifest:1.3.2"
76
+ }
examples/whisper.android/app/proguard-rules.pro ADDED
@@ -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
examples/whisper.android/app/src/androidTest/java/com/whispercppdemo/ExampleInstrumentedTest.kt ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo
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.whispercppdemo", appContext.packageName)
23
+ }
24
+ }
examples/whisper.android/app/src/main/AndroidManifest.xml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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:supportsRtl="true"
14
+ android:theme="@style/Theme.WhisperCppDemo"
15
+ tools:targetApi="31">
16
+ <activity
17
+ android:name=".MainActivity"
18
+ android:exported="true"
19
+ android:theme="@style/Theme.WhisperCppDemo">
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>
examples/whisper.android/app/src/main/java/com/whispercppdemo/MainActivity.kt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo
2
+
3
+ import android.os.Bundle
4
+ import androidx.activity.ComponentActivity
5
+ import androidx.activity.compose.setContent
6
+ import androidx.activity.viewModels
7
+ import com.whispercppdemo.ui.main.MainScreen
8
+ import com.whispercppdemo.ui.main.MainScreenViewModel
9
+ import com.whispercppdemo.ui.theme.WhisperCppDemoTheme
10
+
11
+ class MainActivity : ComponentActivity() {
12
+ private val viewModel: MainScreenViewModel by viewModels { MainScreenViewModel.factory() }
13
+
14
+ override fun onCreate(savedInstanceState: Bundle?) {
15
+ super.onCreate(savedInstanceState)
16
+ setContent {
17
+ WhisperCppDemoTheme {
18
+ MainScreen(viewModel)
19
+ }
20
+ }
21
+ }
22
+ }
examples/whisper.android/app/src/main/java/com/whispercppdemo/media/RiffWaveHelper.kt ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.media
2
+
3
+ import java.io.ByteArrayOutputStream
4
+ import java.io.File
5
+ import java.nio.ByteBuffer
6
+ import java.nio.ByteOrder
7
+
8
+ fun decodeWaveFile(file: File): FloatArray {
9
+ val baos = ByteArrayOutputStream()
10
+ file.inputStream().use { it.copyTo(baos) }
11
+ val buffer = ByteBuffer.wrap(baos.toByteArray())
12
+ buffer.order(ByteOrder.LITTLE_ENDIAN)
13
+ buffer.position(44)
14
+ val shortBuffer = buffer.asShortBuffer()
15
+ val shortArray = ShortArray(shortBuffer.limit())
16
+ shortBuffer.get(shortArray)
17
+ return FloatArray(shortArray.size) { index ->
18
+ (shortArray[index] / 32767.0f).coerceIn(-1f..1f)
19
+ }
20
+ }
21
+
22
+ fun encodeWaveFile(file: File, data: ShortArray) {
23
+ file.outputStream().use {
24
+ it.write(headerBytes(data.size * 2))
25
+ val buffer = ByteBuffer.allocate(data.size * 2)
26
+ buffer.order(ByteOrder.LITTLE_ENDIAN)
27
+ buffer.asShortBuffer().put(data)
28
+ val bytes = ByteArray(buffer.limit())
29
+ buffer.get(bytes)
30
+ it.write(bytes)
31
+ }
32
+ }
33
+
34
+ private fun headerBytes(totalLength: Int): ByteArray {
35
+ require(totalLength >= 44)
36
+ ByteBuffer.allocate(44).apply {
37
+ order(ByteOrder.LITTLE_ENDIAN)
38
+
39
+ put('R'.code.toByte())
40
+ put('I'.code.toByte())
41
+ put('F'.code.toByte())
42
+ put('F'.code.toByte())
43
+
44
+ putInt(totalLength - 8)
45
+
46
+ put('W'.code.toByte())
47
+ put('A'.code.toByte())
48
+ put('V'.code.toByte())
49
+ put('E'.code.toByte())
50
+
51
+ put('f'.code.toByte())
52
+ put('m'.code.toByte())
53
+ put('t'.code.toByte())
54
+ put(' '.code.toByte())
55
+
56
+ putInt(16)
57
+ putShort(1.toShort())
58
+ putShort(1.toShort())
59
+ putInt(16000)
60
+ putInt(32000)
61
+ putShort(2.toShort())
62
+ putShort(16.toShort())
63
+
64
+ put('d'.code.toByte())
65
+ put('a'.code.toByte())
66
+ put('t'.code.toByte())
67
+ put('a'.code.toByte())
68
+
69
+ putInt(totalLength - 44)
70
+ position(0)
71
+ }.also {
72
+ val bytes = ByteArray(it.limit())
73
+ it.get(bytes)
74
+ return bytes
75
+ }
76
+ }
examples/whisper.android/app/src/main/java/com/whispercppdemo/recorder/Recorder.kt ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.recorder
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.media.AudioFormat
5
+ import android.media.AudioRecord
6
+ import android.media.MediaRecorder
7
+ import com.whispercppdemo.media.encodeWaveFile
8
+ import kotlinx.coroutines.CoroutineScope
9
+ import kotlinx.coroutines.asCoroutineDispatcher
10
+ import kotlinx.coroutines.withContext
11
+ import java.io.File
12
+ import java.util.concurrent.Executors
13
+ import java.util.concurrent.atomic.AtomicBoolean
14
+
15
+ class Recorder {
16
+ private val scope: CoroutineScope = CoroutineScope(
17
+ Executors.newSingleThreadExecutor().asCoroutineDispatcher()
18
+ )
19
+ private var recorder: AudioRecordThread? = null
20
+
21
+ suspend fun startRecording(outputFile: File, onError: (Exception) -> Unit) = withContext(scope.coroutineContext) {
22
+ recorder = AudioRecordThread(outputFile, onError)
23
+ recorder?.start()
24
+ }
25
+
26
+ suspend fun stopRecording() = withContext(scope.coroutineContext) {
27
+ recorder?.stopRecording()
28
+ @Suppress("BlockingMethodInNonBlockingContext")
29
+ recorder?.join()
30
+ recorder = null
31
+ }
32
+ }
33
+
34
+ private class AudioRecordThread(
35
+ private val outputFile: File,
36
+ private val onError: (Exception) -> Unit
37
+ ) :
38
+ Thread("AudioRecorder") {
39
+ private var quit = AtomicBoolean(false)
40
+
41
+ @SuppressLint("MissingPermission")
42
+ override fun run() {
43
+ try {
44
+ val bufferSize = AudioRecord.getMinBufferSize(
45
+ 16000,
46
+ AudioFormat.CHANNEL_IN_MONO,
47
+ AudioFormat.ENCODING_PCM_16BIT
48
+ ) * 4
49
+ val buffer = ShortArray(bufferSize / 2)
50
+
51
+ val audioRecord = AudioRecord(
52
+ MediaRecorder.AudioSource.MIC,
53
+ 16000,
54
+ AudioFormat.CHANNEL_IN_MONO,
55
+ AudioFormat.ENCODING_PCM_16BIT,
56
+ bufferSize
57
+ )
58
+
59
+ try {
60
+ audioRecord.startRecording()
61
+
62
+ val allData = mutableListOf<Short>()
63
+
64
+ while (!quit.get()) {
65
+ val read = audioRecord.read(buffer, 0, buffer.size)
66
+ if (read > 0) {
67
+ for (i in 0 until read) {
68
+ allData.add(buffer[i])
69
+ }
70
+ } else {
71
+ throw java.lang.RuntimeException("audioRecord.read returned $read")
72
+ }
73
+ }
74
+
75
+ audioRecord.stop()
76
+ encodeWaveFile(outputFile, allData.toShortArray())
77
+ } finally {
78
+ audioRecord.release()
79
+ }
80
+ } catch (e: Exception) {
81
+ onError(e)
82
+ }
83
+ }
84
+
85
+ fun stopRecording() {
86
+ quit.set(true)
87
+ }
88
+ }
examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreen.kt ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.ui.main
2
+
3
+ import androidx.compose.foundation.layout.*
4
+ import androidx.compose.foundation.rememberScrollState
5
+ import androidx.compose.foundation.verticalScroll
6
+ import androidx.compose.material3.*
7
+ import androidx.compose.runtime.Composable
8
+ import androidx.compose.ui.Modifier
9
+ import androidx.compose.ui.res.stringResource
10
+ import androidx.compose.ui.unit.dp
11
+ import com.google.accompanist.permissions.ExperimentalPermissionsApi
12
+ import com.google.accompanist.permissions.isGranted
13
+ import com.google.accompanist.permissions.rememberPermissionState
14
+ import com.whispercppdemo.R
15
+
16
+ @Composable
17
+ fun MainScreen(viewModel: MainScreenViewModel) {
18
+ MainScreen(
19
+ canTranscribe = viewModel.canTranscribe,
20
+ isRecording = viewModel.isRecording,
21
+ messageLog = viewModel.dataLog,
22
+ onTranscribeSampleTapped = viewModel::transcribeSample,
23
+ onRecordTapped = viewModel::toggleRecord
24
+ )
25
+ }
26
+
27
+ @OptIn(ExperimentalMaterial3Api::class)
28
+ @Composable
29
+ private fun MainScreen(
30
+ canTranscribe: Boolean,
31
+ isRecording: Boolean,
32
+ messageLog: String,
33
+ onTranscribeSampleTapped: () -> Unit,
34
+ onRecordTapped: () -> Unit
35
+ ) {
36
+ Scaffold(
37
+ topBar = {
38
+ TopAppBar(
39
+ title = { Text(stringResource(R.string.app_name)) }
40
+ )
41
+ },
42
+ ) { innerPadding ->
43
+ Column(
44
+ modifier = Modifier
45
+ .padding(innerPadding)
46
+ .padding(16.dp)
47
+ ) {
48
+ Row(horizontalArrangement = Arrangement.SpaceBetween) {
49
+ TranscribeSampleButton(enabled = canTranscribe, onClick = onTranscribeSampleTapped)
50
+ RecordButton(
51
+ enabled = canTranscribe,
52
+ isRecording = isRecording,
53
+ onClick = onRecordTapped
54
+ )
55
+ }
56
+ MessageLog(messageLog)
57
+ }
58
+ }
59
+ }
60
+
61
+ @Composable
62
+ private fun MessageLog(log: String) {
63
+ Text(modifier = Modifier.verticalScroll(rememberScrollState()), text = log)
64
+ }
65
+
66
+ @Composable
67
+ private fun TranscribeSampleButton(enabled: Boolean, onClick: () -> Unit) {
68
+ Button(onClick = onClick, enabled = enabled) {
69
+ Text("Transcribe sample")
70
+ }
71
+ }
72
+
73
+ @OptIn(ExperimentalPermissionsApi::class)
74
+ @Composable
75
+ private fun RecordButton(enabled: Boolean, isRecording: Boolean, onClick: () -> Unit) {
76
+ val micPermissionState = rememberPermissionState(
77
+ permission = android.Manifest.permission.RECORD_AUDIO,
78
+ onPermissionResult = { granted ->
79
+ if (granted) {
80
+ onClick()
81
+ }
82
+ }
83
+ )
84
+ Button(onClick = {
85
+ if (micPermissionState.status.isGranted) {
86
+ onClick()
87
+ } else {
88
+ micPermissionState.launchPermissionRequest()
89
+ }
90
+ }, enabled = enabled) {
91
+ Text(
92
+ if (isRecording) {
93
+ "Stop recording"
94
+ } else {
95
+ "Start recording"
96
+ }
97
+ )
98
+ }
99
+ }
examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreenViewModel.kt ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.ui.main
2
+
3
+ import android.app.Application
4
+ import android.content.Context
5
+ import android.media.MediaPlayer
6
+ import android.util.Log
7
+ import androidx.compose.runtime.getValue
8
+ import androidx.compose.runtime.mutableStateOf
9
+ import androidx.compose.runtime.setValue
10
+ import androidx.core.net.toUri
11
+ import androidx.lifecycle.ViewModel
12
+ import androidx.lifecycle.ViewModelProvider
13
+ import androidx.lifecycle.viewModelScope
14
+ import androidx.lifecycle.viewmodel.initializer
15
+ import androidx.lifecycle.viewmodel.viewModelFactory
16
+ import com.whispercppdemo.media.decodeWaveFile
17
+ import com.whispercppdemo.recorder.Recorder
18
+ import com.whispercppdemo.whisper.WhisperContext
19
+ import kotlinx.coroutines.Dispatchers
20
+ import kotlinx.coroutines.launch
21
+ import kotlinx.coroutines.runBlocking
22
+ import kotlinx.coroutines.withContext
23
+ import java.io.File
24
+
25
+ private const val LOG_TAG = "MainScreenViewModel"
26
+
27
+ class MainScreenViewModel(private val application: Application) : ViewModel() {
28
+ var canTranscribe by mutableStateOf(false)
29
+ private set
30
+ var dataLog by mutableStateOf("")
31
+ private set
32
+ var isRecording by mutableStateOf(false)
33
+ private set
34
+
35
+ private val modelsPath = File(application.filesDir, "models")
36
+ private val samplesPath = File(application.filesDir, "samples")
37
+ private var recorder: Recorder = Recorder()
38
+ private var whisperContext: WhisperContext? = null
39
+ private var mediaPlayer: MediaPlayer? = null
40
+ private var recordedFile: File? = null
41
+
42
+ init {
43
+ viewModelScope.launch {
44
+ loadData()
45
+ }
46
+ }
47
+
48
+ private suspend fun loadData() {
49
+ printMessage("Loading data...\n")
50
+ try {
51
+ copyAssets()
52
+ loadBaseModel()
53
+ canTranscribe = true
54
+ } catch (e: Exception) {
55
+ Log.w(LOG_TAG, e)
56
+ printMessage("${e.localizedMessage}\n")
57
+ }
58
+ }
59
+
60
+ private suspend fun printMessage(msg: String) = withContext(Dispatchers.Main) {
61
+ dataLog += msg
62
+ }
63
+
64
+ private suspend fun copyAssets() = withContext(Dispatchers.IO) {
65
+ modelsPath.mkdirs()
66
+ samplesPath.mkdirs()
67
+ application.copyData("models", modelsPath, ::printMessage)
68
+ application.copyData("samples", samplesPath, ::printMessage)
69
+ printMessage("All data copied to working directory.\n")
70
+ }
71
+
72
+ private suspend fun loadBaseModel() = withContext(Dispatchers.IO) {
73
+ printMessage("Loading model...\n")
74
+ val firstModel = modelsPath.listFiles()!!.first()
75
+ whisperContext = WhisperContext.createContext(firstModel.absolutePath)
76
+ printMessage("Loaded model ${firstModel.name}.\n")
77
+ }
78
+
79
+ fun transcribeSample() = viewModelScope.launch {
80
+ transcribeAudio(getFirstSample())
81
+ }
82
+
83
+ private suspend fun getFirstSample(): File = withContext(Dispatchers.IO) {
84
+ samplesPath.listFiles()!!.first()
85
+ }
86
+
87
+ private suspend fun readAudioSamples(file: File): FloatArray = withContext(Dispatchers.IO) {
88
+ stopPlayback()
89
+ startPlayback(file)
90
+ return@withContext decodeWaveFile(file)
91
+ }
92
+
93
+ private suspend fun stopPlayback() = withContext(Dispatchers.Main) {
94
+ mediaPlayer?.stop()
95
+ mediaPlayer?.release()
96
+ mediaPlayer = null
97
+ }
98
+
99
+ private suspend fun startPlayback(file: File) = withContext(Dispatchers.Main) {
100
+ mediaPlayer = MediaPlayer.create(application, file.absolutePath.toUri())
101
+ mediaPlayer?.start()
102
+ }
103
+
104
+ private suspend fun transcribeAudio(file: File) {
105
+ if (!canTranscribe) {
106
+ return
107
+ }
108
+
109
+ canTranscribe = false
110
+
111
+ try {
112
+ printMessage("Reading wave samples...\n")
113
+ val data = readAudioSamples(file)
114
+ printMessage("Transcribing data...\n")
115
+ val text = whisperContext?.transcribeData(data)
116
+ printMessage("Done: $text\n")
117
+ } catch (e: Exception) {
118
+ Log.w(LOG_TAG, e)
119
+ printMessage("${e.localizedMessage}\n")
120
+ }
121
+
122
+ canTranscribe = true
123
+ }
124
+
125
+ fun toggleRecord() = viewModelScope.launch {
126
+ try {
127
+ if (isRecording) {
128
+ recorder.stopRecording()
129
+ isRecording = false
130
+ recordedFile?.let { transcribeAudio(it) }
131
+ } else {
132
+ stopPlayback()
133
+ val file = getTempFileForRecording()
134
+ recorder.startRecording(file) { e ->
135
+ viewModelScope.launch {
136
+ withContext(Dispatchers.Main) {
137
+ printMessage("${e.localizedMessage}\n")
138
+ isRecording = false
139
+ }
140
+ }
141
+ }
142
+ isRecording = true
143
+ recordedFile = file
144
+ }
145
+ } catch (e: Exception) {
146
+ Log.w(LOG_TAG, e)
147
+ printMessage("${e.localizedMessage}\n")
148
+ isRecording = false
149
+ }
150
+ }
151
+
152
+ private suspend fun getTempFileForRecording() = withContext(Dispatchers.IO) {
153
+ File.createTempFile("recording", "wav")
154
+ }
155
+
156
+ override fun onCleared() {
157
+ runBlocking {
158
+ whisperContext?.release()
159
+ whisperContext = null
160
+ stopPlayback()
161
+ }
162
+ }
163
+
164
+ companion object {
165
+ fun factory() = viewModelFactory {
166
+ initializer {
167
+ val application =
168
+ this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
169
+ MainScreenViewModel(application)
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ private suspend fun Context.copyData(
176
+ assetDirName: String,
177
+ destDir: File,
178
+ printMessage: suspend (String) -> Unit
179
+ ) = withContext(Dispatchers.IO) {
180
+ assets.list(assetDirName)?.forEach { name ->
181
+ val assetPath = "$assetDirName/$name"
182
+ Log.v(LOG_TAG, "Processing $assetPath...")
183
+ val destination = File(destDir, name)
184
+ Log.v(LOG_TAG, "Copying $assetPath to $destination...")
185
+ printMessage("Copying $name...\n")
186
+ assets.open(assetPath).use { input ->
187
+ destination.outputStream().use { output ->
188
+ input.copyTo(output)
189
+ }
190
+ }
191
+ Log.v(LOG_TAG, "Copied $assetPath to $destination")
192
+ }
193
+ }
examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/theme/Color.kt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.ui.theme
2
+
3
+ import androidx.compose.ui.graphics.Color
4
+
5
+ val Purple80 = Color(0xFFD0BCFF)
6
+ val PurpleGrey80 = Color(0xFFCCC2DC)
7
+ val Pink80 = Color(0xFFEFB8C8)
8
+
9
+ val Purple40 = Color(0xFF6650a4)
10
+ val PurpleGrey40 = Color(0xFF625b71)
11
+ val Pink40 = Color(0xFF7D5260)
examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/theme/Theme.kt ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.ui.theme
2
+
3
+ import android.app.Activity
4
+ import android.os.Build
5
+ import androidx.compose.foundation.isSystemInDarkTheme
6
+ import androidx.compose.material3.MaterialTheme
7
+ import androidx.compose.material3.darkColorScheme
8
+ import androidx.compose.material3.dynamicDarkColorScheme
9
+ import androidx.compose.material3.dynamicLightColorScheme
10
+ import androidx.compose.material3.lightColorScheme
11
+ import androidx.compose.runtime.Composable
12
+ import androidx.compose.runtime.SideEffect
13
+ import androidx.compose.ui.graphics.toArgb
14
+ import androidx.compose.ui.platform.LocalContext
15
+ import androidx.compose.ui.platform.LocalView
16
+ import androidx.core.view.ViewCompat
17
+
18
+ private val DarkColorScheme = darkColorScheme(
19
+ primary = Purple80,
20
+ secondary = PurpleGrey80,
21
+ tertiary = Pink80
22
+ )
23
+
24
+ private val LightColorScheme = lightColorScheme(
25
+ primary = Purple40,
26
+ secondary = PurpleGrey40,
27
+ tertiary = Pink40
28
+
29
+ /* Other default colors to override
30
+ background = Color(0xFFFFFBFE),
31
+ surface = Color(0xFFFFFBFE),
32
+ onPrimary = Color.White,
33
+ onSecondary = Color.White,
34
+ onTertiary = Color.White,
35
+ onBackground = Color(0xFF1C1B1F),
36
+ onSurface = Color(0xFF1C1B1F),
37
+ */
38
+ )
39
+
40
+ @Composable
41
+ fun WhisperCppDemoTheme(
42
+ darkTheme: Boolean = isSystemInDarkTheme(),
43
+ // Dynamic color is available on Android 12+
44
+ dynamicColor: Boolean = true,
45
+ content: @Composable () -> Unit
46
+ ) {
47
+ val colorScheme = when {
48
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49
+ val context = LocalContext.current
50
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51
+ }
52
+ darkTheme -> DarkColorScheme
53
+ else -> LightColorScheme
54
+ }
55
+ val view = LocalView.current
56
+ if (!view.isInEditMode) {
57
+ SideEffect {
58
+ (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb()
59
+ ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme
60
+ }
61
+ }
62
+
63
+ MaterialTheme(
64
+ colorScheme = colorScheme,
65
+ typography = Typography,
66
+ content = content
67
+ )
68
+ }
examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/theme/Type.kt ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.ui.theme
2
+
3
+ import androidx.compose.material3.Typography
4
+ import androidx.compose.ui.text.TextStyle
5
+ import androidx.compose.ui.text.font.FontFamily
6
+ import androidx.compose.ui.text.font.FontWeight
7
+ import androidx.compose.ui.unit.sp
8
+
9
+ // Set of Material typography styles to start with
10
+ val Typography = Typography(
11
+ bodyLarge = TextStyle(
12
+ fontFamily = FontFamily.Default,
13
+ fontWeight = FontWeight.Normal,
14
+ fontSize = 16.sp,
15
+ lineHeight = 24.sp,
16
+ letterSpacing = 0.5.sp
17
+ )
18
+ /* Other default text styles to override
19
+ titleLarge = TextStyle(
20
+ fontFamily = FontFamily.Default,
21
+ fontWeight = FontWeight.Normal,
22
+ fontSize = 22.sp,
23
+ lineHeight = 28.sp,
24
+ letterSpacing = 0.sp
25
+ ),
26
+ labelSmall = TextStyle(
27
+ fontFamily = FontFamily.Default,
28
+ fontWeight = FontWeight.Medium,
29
+ fontSize = 11.sp,
30
+ lineHeight = 16.sp,
31
+ letterSpacing = 0.5.sp
32
+ )
33
+ */
34
+ )
examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo.whisper
2
+
3
+ import kotlinx.coroutines.*
4
+ import java.util.concurrent.Executors
5
+
6
+ class WhisperContext private constructor(private var ptr: Long) {
7
+ // Meet Whisper C++ constraint: Don't access from more than one thread at a time.
8
+ private val scope: CoroutineScope = CoroutineScope(
9
+ Executors.newSingleThreadExecutor().asCoroutineDispatcher()
10
+ )
11
+
12
+ suspend fun transcribeData(data: FloatArray): String = withContext(scope.coroutineContext) {
13
+ require(ptr != 0L)
14
+ WhisperLib.fullTranscribe(ptr, data)
15
+ val textCount = WhisperLib.getTextSegmentCount(ptr)
16
+ return@withContext buildString {
17
+ for (i in 0 until textCount) {
18
+ append(WhisperLib.getTextSegment(ptr, i))
19
+ }
20
+ }
21
+ }
22
+
23
+ suspend fun release() = withContext(scope.coroutineContext) {
24
+ if (ptr != 0L) {
25
+ WhisperLib.freeContext(ptr)
26
+ ptr = 0
27
+ }
28
+ }
29
+
30
+ protected fun finalize() {
31
+ runBlocking {
32
+ release()
33
+ }
34
+ }
35
+
36
+ companion object {
37
+ fun createContext(filePath: String): WhisperContext {
38
+ val ptr = WhisperLib.initContext(filePath)
39
+ if (ptr == 0L) {
40
+ throw java.lang.RuntimeException("Couldn't create context with path $filePath")
41
+ }
42
+ return WhisperContext(ptr)
43
+ }
44
+ }
45
+ }
46
+
47
+ private class WhisperLib {
48
+ companion object {
49
+ init {
50
+ System.loadLibrary("whisper")
51
+ }
52
+
53
+ // JNI methods
54
+ external fun initContext(modelPath: String): Long
55
+ external fun freeContext(contextPtr: Long)
56
+ external fun fullTranscribe(contextPtr: Long, audioData: FloatArray)
57
+ external fun getTextSegmentCount(contextPtr: Long): Int
58
+ external fun getTextSegment(contextPtr: Long, index: Int): String
59
+ }
60
+ }
61
+
examples/whisper.android/app/src/main/jni/whisper/Android.mk ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ LOCAL_PATH := $(call my-dir)
2
+ include $(CLEAR_VARS)
3
+ WHISPER_LIB_DIR := $(LOCAL_PATH)/../../../../../../../
4
+ LOCAL_LDLIBS := -llog
5
+ LOCAL_MODULE := libwhisper
6
+
7
+ # Make the final output library smaller by only keeping the symbols referenced from the app.
8
+ ifneq ($(APP_OPTIM),debug)
9
+ LOCAL_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
10
+ LOCAL_CFLAGS += -ffunction-sections -fdata-sections
11
+ LOCAL_LDFLAGS += -Wl,--gc-sections
12
+ LOCAL_LDFLAGS += -Wl,--exclude-libs,ALL
13
+ LOCAL_LDFLAGS += -flto
14
+ endif
15
+
16
+ LOCAL_CFLAGS += -DSTDC_HEADERS -std=c11 -I $(WHISPER_LIB_DIR)
17
+ LOCAL_CPPFLAGS += -std=c++11
18
+ LOCAL_SRC_FILES := $(WHISPER_LIB_DIR)/ggml.c \
19
+ $(WHISPER_LIB_DIR)/whisper.cpp \
20
+ $(LOCAL_PATH)/jni.c
21
+
22
+ include $(BUILD_SHARED_LIBRARY)
examples/whisper.android/app/src/main/jni/whisper/Application.mk ADDED
@@ -0,0 +1 @@
 
 
1
+ APP_STL := c++_static
examples/whisper.android/app/src/main/jni/whisper/jni.c ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <jni.h>
2
+ #include <android/log.h>
3
+ #include <stdlib.h>
4
+ #include <sys/sysinfo.h>
5
+ #include "whisper.h"
6
+
7
+ #define UNUSED(x) (void)(x)
8
+ #define TAG "JNI"
9
+
10
+ #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
11
+
12
+ static inline int min(int a, int b) {
13
+ return (a < b) ? a : b;
14
+ }
15
+
16
+ static inline int max(int a, int b) {
17
+ return (a > b) ? a : b;
18
+ }
19
+
20
+ JNIEXPORT jlong JNICALL
21
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_initContext(
22
+ JNIEnv *env, jobject thiz, jstring model_path_str) {
23
+ UNUSED(thiz);
24
+ struct whisper_context *context = NULL;
25
+ const char *model_path_chars = (*env)->GetStringUTFChars(env, model_path_str, NULL);
26
+ context = whisper_init(model_path_chars);
27
+ (*env)->ReleaseStringUTFChars(env, model_path_str, model_path_chars);
28
+ return (jlong) context;
29
+ }
30
+
31
+ JNIEXPORT void JNICALL
32
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_freeContext(
33
+ JNIEnv *env, jobject thiz, jlong context_ptr) {
34
+ UNUSED(env);
35
+ UNUSED(thiz);
36
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
37
+ whisper_free(context);
38
+ }
39
+
40
+ JNIEXPORT void JNICALL
41
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_fullTranscribe(
42
+ JNIEnv *env, jobject thiz, jlong context_ptr, jfloatArray audio_data) {
43
+ UNUSED(thiz);
44
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
45
+ jfloat *audio_data_arr = (*env)->GetFloatArrayElements(env, audio_data, NULL);
46
+ const jsize audio_data_length = (*env)->GetArrayLength(env, audio_data);
47
+
48
+ // Leave 2 processors free (i.e. the high-efficiency cores).
49
+ int max_threads = max(1, min(8, get_nprocs() - 2));
50
+ LOGI("Selecting %d threads", max_threads);
51
+
52
+ // The below adapted from the Objective-C iOS sample
53
+ struct whisper_full_params params = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
54
+ params.print_realtime = true;
55
+ params.print_progress = false;
56
+ params.print_timestamps = true;
57
+ params.print_special = false;
58
+ params.translate = false;
59
+ params.language = "en";
60
+ params.n_threads = max_threads;
61
+ params.offset_ms = 0;
62
+ params.no_context = true;
63
+ params.single_segment = false;
64
+
65
+ whisper_reset_timings(context);
66
+
67
+ LOGI("About to run whisper_full");
68
+ if (whisper_full(context, params, audio_data_arr, audio_data_length) != 0) {
69
+ LOGI("Failed to run the model");
70
+ } else {
71
+ whisper_print_timings(context);
72
+ }
73
+ (*env)->ReleaseFloatArrayElements(env, audio_data, audio_data_arr, JNI_ABORT);
74
+ }
75
+
76
+ JNIEXPORT jint JNICALL
77
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_getTextSegmentCount(
78
+ JNIEnv *env, jobject thiz, jlong context_ptr) {
79
+ UNUSED(env);
80
+ UNUSED(thiz);
81
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
82
+ return whisper_full_n_segments(context);
83
+ }
84
+
85
+ JNIEXPORT jstring JNICALL
86
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_getTextSegment(
87
+ JNIEnv *env, jobject thiz, jlong context_ptr, jint index) {
88
+ UNUSED(thiz);
89
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
90
+ const char *text = whisper_full_get_segment_text(context, index);
91
+ jstring string = (*env)->NewStringUTF(env, text);
92
+ return string;
93
+ }
examples/whisper.android/app/src/main/res/drawable/ic_launcher_background.xml ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
examples/whisper.android/app/src/main/res/drawable/ic_launcher_foreground.xml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
examples/whisper.android/app/src/main/res/mipmap-anydpi/ic_launcher.xml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <background android:drawable="@drawable/ic_launcher_background" />
4
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
5
+ </adaptive-icon>
examples/whisper.android/app/src/main/res/values/colors.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
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>
examples/whisper.android/app/src/main/res/values/strings.xml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ <resources>
2
+ <string name="app_name">WhisperCppDemo</string>
3
+ </resources>
examples/whisper.android/app/src/main/res/values/themes.xml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+
4
+ <style name="Theme.WhisperCppDemo" parent="android:Theme.Material.Light.NoActionBar" />
5
+ </resources>
examples/whisper.android/app/src/main/res/xml/backup_rules.xml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
examples/whisper.android/app/src/main/res/xml/data_extraction_rules.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
examples/whisper.android/app/src/test/java/com/whispercppdemo/ExampleUnitTest.kt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercppdemo
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
+ }
examples/whisper.android/build.gradle ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
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.10' apply false
6
+ }
examples/whisper.android/gradle.properties ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
examples/whisper.android/gradle/wrapper/gradle-wrapper.jar ADDED
Binary file (59.2 kB). View file
 
examples/whisper.android/gradle/wrapper/gradle-wrapper.properties ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #Wed Dec 14 10:37:24 EST 2022
2
+ distributionBase=GRADLE_USER_HOME
3
+ distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4
+ distributionPath=wrapper/dists
5
+ zipStorePath=wrapper/dists
6
+ zipStoreBase=GRADLE_USER_HOME
examples/whisper.android/gradlew ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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" "$@"
examples/whisper.android/gradlew.bat ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
examples/whisper.android/local.properties ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ## This file is automatically generated by Android Studio.
2
+ # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3
+ #
4
+ # This file should *NOT* be checked into Version Control Systems,
5
+ # as it contains information specific to your local configuration.
6
+ #
7
+ # Location of the SDK. This is only used by Gradle.
8
+ # For customization when using a Version Control System, please read the
9
+ # header note.
10
+ sdk.dir=/Users/kevin/Library/Android/sdk
examples/whisper.android/settings.gradle ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 = "WhisperCppDemo"
16
+ include ':app'