Tong Li litongmacos bobqianic commited on
Commit
bb48f57
·
unverified ·
1 Parent(s): 730c424

examples : add whisper.android.java for compatibility with older Android versions using Java (#1382)

Browse files

* save the recorded audio to a file

* Alignment -help

* Save the correct audio

* chage to a consistent coding style

* Correct typo

* Update examples/stream/stream.cpp

* Update examples/stream/stream.cpp

* Correct variable misuse

* Update examples/stream/stream.cpp

* Update examples/stream/stream.cpp

* Update examples/stream/stream.cpp

* Update examples/stream/stream.cpp

* add *.bin .cxx/ .gradle/ cmake-build-debug/ to gitignore

* add whisper.android.java

* Added support for older versions of Android of Java

* add examples for android java

* add README.md for android java

* add fullTranscribeWithTime

* 增加 toString()方法和测试

* change return type to void

* update to v1.4.1

* add WhisperService

* chage to whisper_full_get_segment_t1

* add method transcribeDataWithTime

* modified toString
```
return "[" + start + " --> " + end + "]:" + sentence;
```

* Optimize code logic

* update text view on handle

* set max lines

* change Chinese to English

* Update bindings/java/build.gradle

* Update .gitignore

* add android.java to github action

* chage android.java to android_java in build.yml

* remove gradle

* chage jdk to temurin in android_java of CI

* chage jdk to temurin 11 in android_java of CI

* add x to gradlew

* set api-level for android_java of CI

* Update examples/whisper.android.java/app/src/main/jni/whisper/CMakeLists.txt

* add ndk version in build.gradle

* remove local.properties

* add testFullTranscribeWithTime

---------

Co-authored-by: litongmacos <[email protected]>
Co-authored-by: bobqianic <[email protected]>

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .github/workflows/build.yml +26 -0
  2. .gitignore +4 -0
  3. bindings/java/build.gradle +1 -0
  4. bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCpp.java +25 -0
  5. bindings/java/src/main/java/io/github/ggerganov/whispercpp/bean/WhisperSegment.java +47 -0
  6. bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperCppTest.java +44 -1
  7. examples/whisper.android.java/.gitignore +15 -0
  8. examples/whisper.android.java/README.md +20 -0
  9. examples/whisper.android.java/README_files/1.jpg +0 -0
  10. examples/whisper.android.java/app/.gitignore +1 -0
  11. examples/whisper.android.java/app/build.gradle +58 -0
  12. examples/whisper.android.java/app/proguard-rules.pro +21 -0
  13. examples/whisper.android.java/app/src/androidTest/java/com/litongjava/whisper/android/java/ExampleInstrumentedTest.java +26 -0
  14. examples/whisper.android.java/app/src/main/AndroidManifest.xml +22 -0
  15. examples/whisper.android.java/app/src/main/assets/logback.xml +40 -0
  16. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/MainActivity.java +107 -0
  17. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/app/App.java +13 -0
  18. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/bean/WhisperSegment.java +47 -0
  19. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/services/WhisperService.java +101 -0
  20. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/single/LocalWhisper.java +66 -0
  21. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/task/LoadModelTask.java +44 -0
  22. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/task/TranscriptionTask.java +44 -0
  23. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/utils/AssetUtils.java +91 -0
  24. examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/utils/WaveEncoder.java +105 -0
  25. examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/CpuInfo.java +121 -0
  26. examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperContext.java +138 -0
  27. examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperCpuConfig.java +12 -0
  28. examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperLib.java +75 -0
  29. examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperUtils.java +34 -0
  30. examples/whisper.android.java/app/src/main/jni/whisper/CMakeLists.txt +56 -0
  31. examples/whisper.android.java/app/src/main/jni/whisper/jni.c +257 -0
  32. examples/whisper.android.java/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
  33. examples/whisper.android.java/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  34. examples/whisper.android.java/app/src/main/res/layout/activity_main.xml +57 -0
  35. examples/whisper.android.java/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  36. examples/whisper.android.java/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  37. examples/whisper.android.java/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  38. examples/whisper.android.java/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  39. examples/whisper.android.java/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  40. examples/whisper.android.java/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  41. examples/whisper.android.java/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  42. examples/whisper.android.java/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  43. examples/whisper.android.java/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  44. examples/whisper.android.java/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  45. examples/whisper.android.java/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  46. examples/whisper.android.java/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  47. examples/whisper.android.java/app/src/main/res/values-night/themes.xml +16 -0
  48. examples/whisper.android.java/app/src/main/res/values/colors.xml +10 -0
  49. examples/whisper.android.java/app/src/main/res/values/strings.xml +3 -0
  50. examples/whisper.android.java/app/src/main/res/values/themes.xml +16 -0
.github/workflows/build.yml CHANGED
@@ -396,6 +396,32 @@ jobs:
396
  cd examples/whisper.android
397
  ./gradlew assembleRelease --no-daemon
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  java:
400
  needs: [ 'windows' ]
401
  runs-on: windows-latest
 
396
  cd examples/whisper.android
397
  ./gradlew assembleRelease --no-daemon
398
 
399
+ android_java:
400
+ runs-on: ubuntu-latest
401
+
402
+ steps:
403
+ - name: Clone
404
+ uses: actions/checkout@v3
405
+
406
+ - name: set up JDK 11
407
+ uses: actions/setup-java@v3
408
+ with:
409
+ java-version: '11'
410
+ distribution: 'temurin'
411
+ cache: gradle
412
+
413
+ - name: Setup Android SDK
414
+ uses: android-actions/setup-android@v2
415
+ with:
416
+ api-level: 30
417
+ build-tools-version: 30.0.3
418
+
419
+ - name: Build
420
+ run: |
421
+ cd examples/whisper.android.java
422
+ chmod +x ./gradlew
423
+ ./gradlew assembleRelease
424
+
425
  java:
426
  needs: [ 'windows' ]
427
  runs-on: windows-latest
.gitignore CHANGED
@@ -54,3 +54,7 @@ bindings/java/.idea/
54
  .idea/
55
 
56
  benchmark_results.csv
 
 
 
 
 
54
  .idea/
55
 
56
  benchmark_results.csv
57
+ cmake-build-debug/
58
+ .cxx/
59
+ .gradle/
60
+ local.properties
bindings/java/build.gradle CHANGED
@@ -9,6 +9,7 @@ archivesBaseName = 'whispercpp'
9
  group = 'io.github.ggerganov'
10
  version = '1.4.0'
11
 
 
12
  sourceCompatibility = 1.8
13
  targetCompatibility = 1.8
14
 
 
9
  group = 'io.github.ggerganov'
10
  version = '1.4.0'
11
 
12
+
13
  sourceCompatibility = 1.8
14
  targetCompatibility = 1.8
15
 
bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCpp.java CHANGED
@@ -2,6 +2,7 @@ package io.github.ggerganov.whispercpp;
2
 
3
  import com.sun.jna.Native;
4
  import com.sun.jna.Pointer;
 
5
  import io.github.ggerganov.whispercpp.params.WhisperContextParams;
6
  import io.github.ggerganov.whispercpp.params.WhisperFullParams;
7
  import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
@@ -9,6 +10,8 @@ import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
9
  import java.io.File;
10
  import java.io.FileNotFoundException;
11
  import java.io.IOException;
 
 
12
 
13
  /**
14
  * Before calling most methods, you must call `initContext(modelPath)` to initialise the `ctx` Pointer.
@@ -160,6 +163,28 @@ public class WhisperCpp implements AutoCloseable {
160
 
161
  return str.toString().trim();
162
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  // public int getTextSegmentCount(Pointer ctx) {
165
  // return lib.whisper_full_n_segments(ctx);
 
2
 
3
  import com.sun.jna.Native;
4
  import com.sun.jna.Pointer;
5
+ import io.github.ggerganov.whispercpp.bean.WhisperSegment;
6
  import io.github.ggerganov.whispercpp.params.WhisperContextParams;
7
  import io.github.ggerganov.whispercpp.params.WhisperFullParams;
8
  import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
 
10
  import java.io.File;
11
  import java.io.FileNotFoundException;
12
  import java.io.IOException;
13
+ import java.util.ArrayList;
14
+ import java.util.List;
15
 
16
  /**
17
  * Before calling most methods, you must call `initContext(modelPath)` to initialise the `ctx` Pointer.
 
163
 
164
  return str.toString().trim();
165
  }
166
+ public List<WhisperSegment> fullTranscribeWithTime(WhisperFullParams whisperParams, float[] audioData) throws IOException {
167
+ if (ctx == null) {
168
+ throw new IllegalStateException("Model not initialised");
169
+ }
170
+
171
+ if (lib.whisper_full(ctx, whisperParams, audioData, audioData.length) != 0) {
172
+ throw new IOException("Failed to process audio");
173
+ }
174
+
175
+ int nSegments = lib.whisper_full_n_segments(ctx);
176
+ List<WhisperSegment> segments= new ArrayList<>(nSegments);
177
+
178
+
179
+ for (int i = 0; i < nSegments; i++) {
180
+ long t0 = lib.whisper_full_get_segment_t0(ctx, i);
181
+ String text = lib.whisper_full_get_segment_text(ctx, i);
182
+ long t1 = lib.whisper_full_get_segment_t1(ctx, i);
183
+ segments.add(new WhisperSegment(t0,t1,text));
184
+ }
185
+
186
+ return segments;
187
+ }
188
 
189
  // public int getTextSegmentCount(Pointer ctx) {
190
  // return lib.whisper_full_n_segments(ctx);
bindings/java/src/main/java/io/github/ggerganov/whispercpp/bean/WhisperSegment.java ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package io.github.ggerganov.whispercpp.bean;
2
+
3
+ /**
4
+ * Created by [email protected] on 10/21/2023_7:48 AM
5
+ */
6
+ public class WhisperSegment {
7
+ private long start, end;
8
+ private String sentence;
9
+
10
+ public WhisperSegment() {
11
+ }
12
+
13
+ public WhisperSegment(long start, long end, String sentence) {
14
+ this.start = start;
15
+ this.end = end;
16
+ this.sentence = sentence;
17
+ }
18
+
19
+ public long getStart() {
20
+ return start;
21
+ }
22
+
23
+ public long getEnd() {
24
+ return end;
25
+ }
26
+
27
+ public String getSentence() {
28
+ return sentence;
29
+ }
30
+
31
+ public void setStart(long start) {
32
+ this.start = start;
33
+ }
34
+
35
+ public void setEnd(long end) {
36
+ this.end = end;
37
+ }
38
+
39
+ public void setSentence(String sentence) {
40
+ this.sentence = sentence;
41
+ }
42
+
43
+ @Override
44
+ public String toString() {
45
+ return "[" + start + " --> " + end + "]:" + sentence;
46
+ }
47
+ }
bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperCppTest.java CHANGED
@@ -2,6 +2,7 @@ package io.github.ggerganov.whispercpp;
2
 
3
  import static org.junit.jupiter.api.Assertions.*;
4
 
 
5
  import io.github.ggerganov.whispercpp.params.CBool;
6
  import io.github.ggerganov.whispercpp.params.WhisperFullParams;
7
  import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
@@ -11,6 +12,7 @@ import javax.sound.sampled.AudioInputStream;
11
  import javax.sound.sampled.AudioSystem;
12
  import java.io.File;
13
  import java.io.FileNotFoundException;
 
14
 
15
  class WhisperCppTest {
16
  private static WhisperCpp whisper = new WhisperCpp();
@@ -20,7 +22,8 @@ class WhisperCppTest {
20
  static void init() throws FileNotFoundException {
21
  // By default, models are loaded from ~/.cache/whisper/ and are usually named "ggml-${name}.bin"
22
  // or you can provide the absolute path to the model file.
23
- String modelName = "../../models/ggml-tiny.en.bin";
 
24
  try {
25
  whisper.initContext(modelName);
26
  // whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY);
@@ -99,4 +102,44 @@ class WhisperCppTest {
99
  audioInputStream.close();
100
  }
101
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
 
2
 
3
  import static org.junit.jupiter.api.Assertions.*;
4
 
5
+ import io.github.ggerganov.whispercpp.bean.WhisperSegment;
6
  import io.github.ggerganov.whispercpp.params.CBool;
7
  import io.github.ggerganov.whispercpp.params.WhisperFullParams;
8
  import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
 
12
  import javax.sound.sampled.AudioSystem;
13
  import java.io.File;
14
  import java.io.FileNotFoundException;
15
+ import java.util.List;
16
 
17
  class WhisperCppTest {
18
  private static WhisperCpp whisper = new WhisperCpp();
 
22
  static void init() throws FileNotFoundException {
23
  // By default, models are loaded from ~/.cache/whisper/ and are usually named "ggml-${name}.bin"
24
  // or you can provide the absolute path to the model file.
25
+ String modelName = "../../models/ggml-tiny.bin";
26
+ // String modelName = "../../models/ggml-tiny.en.bin";
27
  try {
28
  whisper.initContext(modelName);
29
  // whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY);
 
102
  audioInputStream.close();
103
  }
104
  }
105
+
106
+ @Test
107
+ void testFullTranscribeWithTime() throws Exception {
108
+ if (!modelInitialised) {
109
+ System.out.println("Model not initialised, skipping test");
110
+ return;
111
+ }
112
+
113
+ // Given
114
+ File file = new File(System.getProperty("user.dir"), "../../samples/jfk.wav");
115
+ AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
116
+
117
+ byte[] b = new byte[audioInputStream.available()];
118
+ float[] floats = new float[b.length / 2];
119
+
120
+ // WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY);
121
+ WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH);
122
+ params.setProgressCallback((ctx, state, progress, user_data) -> System.out.println("progress: " + progress));
123
+ params.print_progress = CBool.FALSE;
124
+ // params.initial_prompt = "and so my fellow Americans um, like";
125
+
126
+
127
+ try {
128
+ audioInputStream.read(b);
129
+
130
+ for (int i = 0, j = 0; i < b.length; i += 2, j++) {
131
+ int intSample = (int) (b[i + 1]) << 8 | (int) (b[i]) & 0xFF;
132
+ floats[j] = intSample / 32767.0f;
133
+ }
134
+
135
+ List<WhisperSegment> segments = whisper.fullTranscribeWithTime(params, floats);
136
+ assertTrue(segments.size() > 0, "The size of segments should be greater than 0");
137
+ for (WhisperSegment segment : segments) {
138
+ System.out.println(segment);
139
+ }
140
+ } finally {
141
+ audioInputStream.close();
142
+ }
143
+ }
144
+
145
  }
examples/whisper.android.java/.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.java/README.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ A sample Android app using java code and [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. Modify the modelFilePath in the WhisperService.java
10
+ 6. Modify the sampleFilePath in the WhisperService.java
11
+ 7. Select the "release" active build variant, and use Android Studio to run and deploy to your device.
12
+ [^1]: I recommend the tiny or base models for running on an Android device.
13
+
14
+ PS:
15
+ 1. Do not move this android project folder individually to other folders, because this android project folder depends on the files of the whole project.
16
+ 2. The cpp code is compiled during the build process
17
+ 3. If you want to import a compiled cpp project in your Android project, please refer to the https://github.com/litongjava/whisper.cpp.android.java.demo
18
+
19
+ ![](README_files/1.jpg)
20
+
examples/whisper.android.java/README_files/1.jpg ADDED
examples/whisper.android.java/app/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ /build
examples/whisper.android.java/app/build.gradle ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ plugins {
2
+ id 'com.android.application'
3
+ }
4
+
5
+ android {
6
+ compileSdkVersion 30
7
+ buildToolsVersion '30.0.3'
8
+
9
+ defaultConfig {
10
+ applicationId "com.litongjava.whisper.android.java"
11
+ minSdkVersion 21
12
+ targetSdkVersion 30
13
+ versionCode 1
14
+ versionName "1.0"
15
+
16
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17
+ externalNativeBuild {
18
+ cmake {
19
+ cppFlags ""
20
+ }
21
+ }
22
+ ndk {
23
+ abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
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
+ externalNativeBuild {
35
+ cmake {
36
+ path "src/main/jni/whisper/CMakeLists.txt"
37
+ }
38
+ }
39
+ ndkVersion "25.2.9519653"
40
+ compileOptions {
41
+ sourceCompatibility JavaVersion.VERSION_1_8
42
+ targetCompatibility JavaVersion.VERSION_1_8
43
+ }
44
+ }
45
+
46
+ dependencies {
47
+ implementation 'androidx.appcompat:appcompat:1.1.0'
48
+ implementation 'com.google.android.material:material:1.1.0'
49
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
50
+ testImplementation 'junit:junit:4.+'
51
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
52
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
53
+
54
+ //litongjava
55
+ implementation 'com.litongjava:android-view-inject:1.0'
56
+ implementation 'com.litongjava:jfinal-aop:1.0.1'
57
+ implementation 'com.litongjava:litongjava-android-utils:1.0.0'
58
+ }
examples/whisper.android.java/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.java/app/src/androidTest/java/com/litongjava/whisper/android/java/ExampleInstrumentedTest.java ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java;
2
+
3
+ import android.content.Context;
4
+
5
+ import androidx.test.platform.app.InstrumentationRegistry;
6
+ import androidx.test.ext.junit.runners.AndroidJUnit4;
7
+
8
+ import org.junit.Test;
9
+ import org.junit.runner.RunWith;
10
+
11
+ import static org.junit.Assert.*;
12
+
13
+ /**
14
+ * Instrumented test, which will execute on an Android device.
15
+ *
16
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
17
+ */
18
+ @RunWith(AndroidJUnit4.class)
19
+ public class ExampleInstrumentedTest {
20
+ @Test
21
+ public void useAppContext() {
22
+ // Context of the app under test.
23
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24
+ assertEquals("com.litongjava.whisper.android.java", appContext.getPackageName());
25
+ }
26
+ }
examples/whisper.android.java/app/src/main/AndroidManifest.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+ package="com.litongjava.whisper.android.java">
4
+
5
+ <application
6
+ android:allowBackup="true"
7
+ android:name=".app.App"
8
+ android:icon="@mipmap/ic_launcher"
9
+ android:label="@string/app_name"
10
+ android:roundIcon="@mipmap/ic_launcher_round"
11
+ android:supportsRtl="true"
12
+ android:theme="@style/Theme.Whisperandroidjava">
13
+ <activity android:name=".MainActivity">
14
+ <intent-filter>
15
+ <action android:name="android.intent.action.MAIN" />
16
+
17
+ <category android:name="android.intent.category.LAUNCHER" />
18
+ </intent-filter>
19
+ </activity>
20
+ </application>
21
+
22
+ </manifest>
examples/whisper.android.java/app/src/main/assets/logback.xml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <configuration debug="false" xmlns="http://ch.qos.logback/xml/ns/logback"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd
5
+ http://ch.qos.logback/xml/ns/logback ">
6
+ <!--Define the storage address of the log file Do not use relative paths in the LogBack configuration. -->
7
+ <property name="LOG_HOME" value="logs" />
8
+ <!--Formatted output: %d means the date, %-6level: log level from the left display 6 characters wide, %m: log message, %n is a newline character -->
9
+ <property name="CONSOLE_LOG_PATTERN"
10
+ value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-6level%logger{0}.%M:%L - %m%n" />
11
+
12
+ <!-- console output -->
13
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
14
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
15
+ <pattern>${CONSOLE_LOG_PATTERN}</pattern>
16
+ </encoder>
17
+ </appender>
18
+
19
+ <!-- Generate log files on a daily basis -->
20
+ <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
21
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
22
+ <pattern>${CONSOLE_LOG_PATTERN}</pattern>
23
+ </encoder>
24
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
25
+ <!--File name for log file output -->
26
+ <fileNamePattern>${LOG_HOME}/project-name-%d{yyyy-MM-dd}.log</fileNamePattern>
27
+ <!--Maximum size of log file -->
28
+ <maxHistory>180</maxHistory>
29
+ </rollingPolicy>
30
+ <!--日志文件最大的大小 -->
31
+ <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
32
+ <maxFileSize>10MB</maxFileSize>
33
+ </triggeringPolicy>
34
+ </appender>
35
+ <!-- Log output level and source-->
36
+ <root level="info">
37
+ <appender-ref ref="STDOUT" />
38
+ <appender-ref ref="FILE" />
39
+ </root>
40
+ </configuration>
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/MainActivity.java ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java;
2
+
3
+ import androidx.annotation.RequiresApi;
4
+ import androidx.appcompat.app.AppCompatActivity;
5
+
6
+ import android.content.Context;
7
+ import android.os.Build;
8
+ import android.os.Bundle;
9
+ import android.os.Handler;
10
+ import android.os.Looper;
11
+ import android.view.View;
12
+ import android.widget.TextView;
13
+
14
+ import com.blankj.utilcode.util.ThreadUtils;
15
+ import com.litongjava.android.view.inject.annotation.FindViewById;
16
+ import com.litongjava.android.view.inject.annotation.FindViewByIdLayout;
17
+ import com.litongjava.android.view.inject.annotation.OnClick;
18
+ import com.litongjava.android.view.inject.utils.ViewInjectUtils;
19
+ import com.litongjava.jfinal.aop.Aop;
20
+ import com.litongjava.jfinal.aop.AopManager;
21
+ import com.litongjava.whisper.android.java.services.WhisperService;
22
+ import com.litongjava.whisper.android.java.task.LoadModelTask;
23
+ import com.litongjava.whisper.android.java.task.TranscriptionTask;
24
+ import com.litongjava.whisper.android.java.utils.AssetUtils;
25
+ import com.whispercpp.java.whisper.WhisperLib;
26
+
27
+ import org.slf4j.Logger;
28
+ import org.slf4j.LoggerFactory;
29
+
30
+ import java.io.File;
31
+
32
+
33
+ @FindViewByIdLayout(R.layout.activity_main)
34
+ public class MainActivity extends AppCompatActivity {
35
+
36
+ @FindViewById(R.id.sample_text)
37
+ private TextView tv;
38
+
39
+ Logger log = LoggerFactory.getLogger(this.getClass());
40
+ private WhisperService whisperService = Aop.get(WhisperService.class);
41
+
42
+ @RequiresApi(api = Build.VERSION_CODES.O)
43
+ @Override
44
+ protected void onCreate(Bundle savedInstanceState) {
45
+ super.onCreate(savedInstanceState);
46
+ //setContentView(R.layout.activity_main);
47
+ ViewInjectUtils.injectActivity(this, this);
48
+ initAopBean();
49
+ showSystemInfo();
50
+ }
51
+
52
+ private void initAopBean() {
53
+ Handler mainHandler = new Handler(Looper.getMainLooper());
54
+ AopManager.me().addSingletonObject(mainHandler);
55
+ }
56
+
57
+ @RequiresApi(api = Build.VERSION_CODES.O)
58
+ @OnClick(R.id.loadModelBtn)
59
+ public void loadModelBtn_OnClick(View v) {
60
+ Context context = getBaseContext();
61
+ ThreadUtils.executeByIo(new LoadModelTask(tv));
62
+ }
63
+
64
+ @OnClick(R.id.transcriptSampleBtn)
65
+ public void transcriptSampleBtn_OnClick(View v) {
66
+ Context context = getBaseContext();
67
+
68
+ long start = System.currentTimeMillis();
69
+ String sampleFilePath = "samples/jfk.wav";
70
+ File filesDir = context.getFilesDir();
71
+ File sampleFile = AssetUtils.copyFileIfNotExists(context, filesDir, sampleFilePath);
72
+ long end = System.currentTimeMillis();
73
+ String msg = "copy file:" + (end - start) + "ms";
74
+ outputMsg(tv, msg);
75
+ ThreadUtils.executeByIo(new TranscriptionTask(tv, sampleFile));
76
+ }
77
+
78
+ private void outputMsg(TextView tv, String msg) {
79
+ tv.append(msg + "\n");
80
+ log.info(msg);
81
+ }
82
+
83
+
84
+ @RequiresApi(api = Build.VERSION_CODES.O)
85
+ @OnClick(R.id.systemInfoBtn)
86
+ public void systemInfoBtn_OnClick(View v) {
87
+ showSystemInfo();
88
+ }
89
+
90
+ @RequiresApi(api = Build.VERSION_CODES.O)
91
+ public void showSystemInfo() {
92
+ String systemInfo = WhisperLib.getSystemInfo();
93
+ tv.append(systemInfo + "\n");
94
+ }
95
+
96
+ @OnClick(R.id.clearBtn)
97
+ public void clearBtn_OnClick(View v) {
98
+ tv.setText("");
99
+ }
100
+
101
+ @RequiresApi(api = Build.VERSION_CODES.O)
102
+ @Override
103
+ protected void onDestroy() {
104
+ super.onDestroy();
105
+ whisperService.release();
106
+ }
107
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/app/App.java ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.app;
2
+
3
+ import android.app.Application;
4
+
5
+ import com.blankj.utilcode.util.Utils;
6
+
7
+ public class App extends Application {
8
+ @Override
9
+ public void onCreate() {
10
+ super.onCreate();
11
+ Utils.init(this);
12
+ }
13
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/bean/WhisperSegment.java ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.bean;
2
+
3
+ /**
4
+ * Created by [email protected] on 10/21/2023_7:48 AM
5
+ */
6
+ public class WhisperSegment {
7
+ private long start, end;
8
+ private String sentence;
9
+
10
+ public WhisperSegment() {
11
+ }
12
+
13
+ public WhisperSegment(long start, long end, String sentence) {
14
+ this.start = start;
15
+ this.end = end;
16
+ this.sentence = sentence;
17
+ }
18
+
19
+ public long getStart() {
20
+ return start;
21
+ }
22
+
23
+ public long getEnd() {
24
+ return end;
25
+ }
26
+
27
+ public String getSentence() {
28
+ return sentence;
29
+ }
30
+
31
+ public void setStart(long start) {
32
+ this.start = start;
33
+ }
34
+
35
+ public void setEnd(long end) {
36
+ this.end = end;
37
+ }
38
+
39
+ public void setSentence(String sentence) {
40
+ this.sentence = sentence;
41
+ }
42
+
43
+ @Override
44
+ public String toString() {
45
+ return "["+start+" --> "+end+"]:"+sentence;
46
+ }
47
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/services/WhisperService.java ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.services;
2
+
3
+ import android.content.Context;
4
+ import android.os.Build;
5
+ import android.os.Handler;
6
+ import android.widget.TextView;
7
+ import android.widget.Toast;
8
+
9
+ import androidx.annotation.RequiresApi;
10
+
11
+ import com.blankj.utilcode.util.ToastUtils;
12
+ import com.blankj.utilcode.util.Utils;
13
+ import com.litongjava.android.utils.dialog.AlertDialogUtils;
14
+ import com.litongjava.jfinal.aop.Aop;
15
+ import com.litongjava.whisper.android.java.bean.WhisperSegment;
16
+ import com.litongjava.whisper.android.java.single.LocalWhisper;
17
+ import com.litongjava.whisper.android.java.utils.WaveEncoder;
18
+
19
+ import org.slf4j.Logger;
20
+ import org.slf4j.LoggerFactory;
21
+
22
+ import java.io.File;
23
+ import java.io.IOException;
24
+ import java.util.List;
25
+ import java.util.concurrent.ExecutionException;
26
+
27
+ public class WhisperService {
28
+ private Logger log = LoggerFactory.getLogger(this.getClass());
29
+
30
+ private final Object lock = new Object();
31
+
32
+ @RequiresApi(api = Build.VERSION_CODES.O)
33
+ public void loadModel(TextView tv) {
34
+ String modelFilePath = LocalWhisper.modelFilePath;
35
+ String msg = "load model from :" + modelFilePath + "\n";
36
+ outputMsg(tv, msg);
37
+
38
+ long start = System.currentTimeMillis();
39
+ LocalWhisper.INSTANCE.init();
40
+ long end = System.currentTimeMillis();
41
+ msg = "model load successful:" + (end - start) + "ms";
42
+ outputMsg(tv, msg);
43
+ ToastUtils.showLong(msg);
44
+
45
+ }
46
+
47
+ @RequiresApi(api = Build.VERSION_CODES.O)
48
+ public void transcribeSample(TextView tv, File sampleFile) {
49
+ String msg = "";
50
+ msg = "transcribe file from :" + sampleFile.getAbsolutePath();
51
+ outputMsg(tv, msg);
52
+
53
+ Long start = System.currentTimeMillis();
54
+ float[] audioData = new float[0]; // 读取音频样本
55
+ try {
56
+ audioData = WaveEncoder.decodeWaveFile(sampleFile);
57
+ } catch (IOException e) {
58
+ e.printStackTrace();
59
+ return;
60
+ }
61
+ long end = System.currentTimeMillis();
62
+ msg = "decode wave file:" + (end - start) + "ms";
63
+ outputMsg(tv, msg);
64
+
65
+ start = System.currentTimeMillis();
66
+ List<WhisperSegment> transcription = null;
67
+ try {
68
+ //transcription = LocalWhisper.INSTANCE.transcribeData(audioData);
69
+ transcription = LocalWhisper.INSTANCE.transcribeDataWithTime(audioData);
70
+ } catch (ExecutionException e) {
71
+ e.printStackTrace();
72
+ } catch (InterruptedException e) {
73
+ e.printStackTrace();
74
+ }
75
+ end = System.currentTimeMillis();
76
+ if(transcription!=null){
77
+ ToastUtils.showLong(transcription.toString());
78
+ msg = "Transcript successful:" + (end - start) + "ms";
79
+ outputMsg(tv, msg);
80
+
81
+ outputMsg(tv, transcription.toString());
82
+
83
+ }else{
84
+ msg = "Transcript failed:" + (end - start) + "ms";
85
+ outputMsg(tv, msg);
86
+ }
87
+
88
+ }
89
+
90
+ private void outputMsg(TextView tv, String msg) {
91
+ log.info(msg);
92
+ if(tv!=null){
93
+ Aop.get(Handler.class).post(()->{ tv.append(msg + "\n");});
94
+ }
95
+ }
96
+
97
+ @RequiresApi(api = Build.VERSION_CODES.O)
98
+ public void release() {
99
+ //noting to do
100
+ }
101
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/single/LocalWhisper.java ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.single;
2
+
3
+ import android.app.Application;
4
+ import android.os.Build;
5
+ import android.os.Handler;
6
+
7
+ import androidx.annotation.RequiresApi;
8
+
9
+ import com.blankj.utilcode.util.ToastUtils;
10
+ import com.blankj.utilcode.util.Utils;
11
+ import com.litongjava.jfinal.aop.Aop;
12
+ import com.litongjava.whisper.android.java.bean.WhisperSegment;
13
+ import com.litongjava.whisper.android.java.utils.AssetUtils;
14
+ import com.whispercpp.java.whisper.WhisperContext;
15
+
16
+ import java.io.File;
17
+ import java.util.List;
18
+ import java.util.concurrent.ExecutionException;
19
+
20
+
21
+ @RequiresApi(api = Build.VERSION_CODES.O)
22
+ public enum LocalWhisper {
23
+ INSTANCE;
24
+
25
+ public static final String modelFilePath = "models/ggml-tiny.bin";
26
+ private WhisperContext whisperContext;
27
+
28
+ @RequiresApi(api = Build.VERSION_CODES.O)
29
+ LocalWhisper() {
30
+ Application context = Utils.getApp();
31
+ File filesDir = context.getFilesDir();
32
+ File modelFile = AssetUtils.copyFileIfNotExists(context, filesDir, modelFilePath);
33
+ String realModelFilePath = modelFile.getAbsolutePath();
34
+ whisperContext = WhisperContext.createContextFromFile(realModelFilePath);
35
+ }
36
+
37
+ public synchronized String transcribeData(float[] data) throws ExecutionException, InterruptedException {
38
+ if(whisperContext==null){
39
+ toastModelLoading();
40
+ return null;
41
+ }else{
42
+ return whisperContext.transcribeData(data);
43
+ }
44
+ }
45
+
46
+ private static void toastModelLoading() {
47
+ Aop.get(Handler.class).post(()->{
48
+ ToastUtils.showShort("please wait for model loading");
49
+ });
50
+ }
51
+
52
+ public List<WhisperSegment> transcribeDataWithTime(float[] audioData) throws ExecutionException, InterruptedException {
53
+ if(whisperContext==null){
54
+ toastModelLoading();
55
+ return null;
56
+ }else{
57
+ return whisperContext.transcribeDataWithTime(audioData);
58
+ }
59
+ }
60
+
61
+ public void init() {
62
+ //noting to do.but init
63
+ }
64
+
65
+
66
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/task/LoadModelTask.java ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.task;
2
+
3
+ import android.content.Context;
4
+ import android.os.Build;
5
+ import android.os.Handler;
6
+ import android.widget.TextView;
7
+
8
+ import com.blankj.utilcode.util.ThreadUtils;
9
+ import com.litongjava.jfinal.aop.Aop;
10
+ import com.litongjava.whisper.android.java.services.WhisperService;
11
+
12
+ import java.io.File;
13
+
14
+ public class LoadModelTask extends ThreadUtils.Task<Object> {
15
+ private final TextView tv;
16
+ public LoadModelTask(TextView tv) {
17
+ this.tv = tv;
18
+ }
19
+
20
+ @Override
21
+ public Object doInBackground() {
22
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
23
+ Aop.get(WhisperService.class).loadModel(tv);
24
+ }else{
25
+ Aop.get(Handler.class).post(()->{
26
+ tv.append("not supported android devices");
27
+ });
28
+
29
+ }
30
+ return null;
31
+ }
32
+
33
+ @Override
34
+ public void onSuccess(Object result) {
35
+ }
36
+
37
+ @Override
38
+ public void onCancel() {
39
+ }
40
+
41
+ @Override
42
+ public void onFail(Throwable t) {
43
+ }
44
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/task/TranscriptionTask.java ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.task;
2
+
3
+ import android.content.Context;
4
+ import android.os.Build;
5
+ import android.widget.TextView;
6
+
7
+ import com.blankj.utilcode.util.ThreadUtils;
8
+ import com.litongjava.jfinal.aop.Aop;
9
+ import com.litongjava.whisper.android.java.services.WhisperService;
10
+
11
+ import java.io.File;
12
+
13
+ public class TranscriptionTask extends ThreadUtils.Task<Object> {
14
+ private final TextView tv;
15
+ private final File sampleFile;
16
+
17
+ public TranscriptionTask(TextView tv, File sampleFile) {
18
+ this.tv = tv;
19
+ this.sampleFile = sampleFile;
20
+
21
+ }
22
+
23
+ @Override
24
+ public Object doInBackground() {
25
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
26
+ Aop.get(WhisperService.class).transcribeSample(tv, sampleFile);
27
+ }else{
28
+ tv.append("not supported android devices");
29
+ }
30
+ return null;
31
+ }
32
+
33
+ @Override
34
+ public void onSuccess(Object result) {
35
+ }
36
+
37
+ @Override
38
+ public void onCancel() {
39
+ }
40
+
41
+ @Override
42
+ public void onFail(Throwable t) {
43
+ }
44
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/utils/AssetUtils.java ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.utils;
2
+
3
+ import android.content.Context;
4
+
5
+ import org.slf4j.Logger;
6
+ import org.slf4j.LoggerFactory;
7
+
8
+ import java.io.BufferedInputStream;
9
+ import java.io.BufferedOutputStream;
10
+ import java.io.File;
11
+ import java.io.FileNotFoundException;
12
+ import java.io.FileOutputStream;
13
+ import java.io.IOException;
14
+ import java.io.InputStream;
15
+ import java.io.OutputStream;
16
+
17
+ public class AssetUtils {
18
+ private static Logger log = LoggerFactory.getLogger(AssetUtils.class);
19
+
20
+ public static File copyFileIfNotExists(Context context, File distDir, String filename) {
21
+ File dstFile = new File(distDir, filename);
22
+ if (dstFile.exists()) {
23
+ return dstFile;
24
+ } else {
25
+ File parentFile = dstFile.getParentFile();
26
+ log.info("parentFile:{}", parentFile);
27
+ if (!parentFile.exists()) {
28
+ parentFile.mkdirs();
29
+ }
30
+ AssetUtils.copyFileFromAssets(context, filename, dstFile);
31
+ }
32
+ return dstFile;
33
+ }
34
+
35
+ public static void copyDirectoryFromAssets(Context appCtx, String srcDir, String dstDir) {
36
+ if (srcDir.isEmpty() || dstDir.isEmpty()) {
37
+ return;
38
+ }
39
+ try {
40
+ if (!new File(dstDir).exists()) {
41
+ new File(dstDir).mkdirs();
42
+ }
43
+ for (String fileName : appCtx.getAssets().list(srcDir)) {
44
+ String srcSubPath = srcDir + File.separator + fileName;
45
+ String dstSubPath = dstDir + File.separator + fileName;
46
+ if (new File(srcSubPath).isDirectory()) {
47
+ copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath);
48
+ } else {
49
+ copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
50
+ }
51
+ }
52
+ } catch (Exception e) {
53
+ e.printStackTrace();
54
+ }
55
+ }
56
+
57
+ public static void copyFileFromAssets(Context appCtx, String srcPath, String dstPath) {
58
+ File dstFile = new File(dstPath);
59
+ copyFileFromAssets(appCtx, srcPath, dstFile);
60
+ }
61
+
62
+ public static void copyFileFromAssets(Context appCtx, String srcPath, File dstFile) {
63
+ if (srcPath.isEmpty()) {
64
+ return;
65
+ }
66
+ InputStream is = null;
67
+ OutputStream os = null;
68
+ try {
69
+ is = new BufferedInputStream(appCtx.getAssets().open(srcPath));
70
+
71
+ os = new BufferedOutputStream(new FileOutputStream(dstFile));
72
+ byte[] buffer = new byte[1024];
73
+ int length = 0;
74
+ while ((length = is.read(buffer)) != -1) {
75
+ os.write(buffer, 0, length);
76
+ }
77
+ } catch (FileNotFoundException e) {
78
+ e.printStackTrace();
79
+ } catch (IOException e) {
80
+ e.printStackTrace();
81
+ } finally {
82
+ try {
83
+ os.close();
84
+ is.close();
85
+ } catch (IOException e) {
86
+ e.printStackTrace();
87
+ }
88
+ }
89
+
90
+ }
91
+ }
examples/whisper.android.java/app/src/main/java/com/litongjava/whisper/android/java/utils/WaveEncoder.java ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.litongjava.whisper.android.java.utils;
2
+
3
+ import java.io.ByteArrayOutputStream;
4
+ import java.io.File;
5
+ import java.io.FileInputStream;
6
+ import java.io.FileOutputStream;
7
+ import java.io.IOException;
8
+ import java.nio.ByteBuffer;
9
+ import java.nio.ByteOrder;
10
+ import java.nio.ShortBuffer;
11
+
12
+ public class WaveEncoder {
13
+
14
+ public static float[] decodeWaveFile(File file) throws IOException {
15
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
16
+ try (FileInputStream fis = new FileInputStream(file)) {
17
+ byte[] buffer = new byte[1024];
18
+ int bytesRead;
19
+ while ((bytesRead = fis.read(buffer)) != -1) {
20
+ baos.write(buffer, 0, bytesRead);
21
+ }
22
+ }
23
+ ByteBuffer byteBuffer = ByteBuffer.wrap(baos.toByteArray());
24
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
25
+
26
+ int channel = byteBuffer.getShort(22);
27
+ byteBuffer.position(44);
28
+
29
+ ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
30
+ short[] shortArray = new short[shortBuffer.limit()];
31
+ shortBuffer.get(shortArray);
32
+
33
+ float[] output = new float[shortArray.length / channel];
34
+
35
+ for (int index = 0; index < output.length; index++) {
36
+ if (channel == 1) {
37
+ output[index] = Math.max(-1f, Math.min(1f, shortArray[index] / 32767.0f));
38
+ } else {
39
+ output[index] = Math.max(-1f, Math.min(1f, (shortArray[2 * index] + shortArray[2 * index + 1]) / 32767.0f / 2.0f));
40
+ }
41
+ }
42
+ return output;
43
+ }
44
+
45
+ public static void encodeWaveFile(File file, short[] data) throws IOException {
46
+ try (FileOutputStream fos = new FileOutputStream(file)) {
47
+ fos.write(headerBytes(data.length * 2));
48
+
49
+ ByteBuffer buffer = ByteBuffer.allocate(data.length * 2);
50
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
51
+ buffer.asShortBuffer().put(data);
52
+
53
+ byte[] bytes = new byte[buffer.limit()];
54
+ buffer.get(bytes);
55
+
56
+ fos.write(bytes);
57
+ }
58
+ }
59
+
60
+ private static byte[] headerBytes(int totalLength) {
61
+ if (totalLength < 44)
62
+ throw new IllegalArgumentException("Total length must be at least 44 bytes");
63
+
64
+ ByteBuffer buffer = ByteBuffer.allocate(44);
65
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
66
+
67
+ buffer.put((byte) 'R');
68
+ buffer.put((byte) 'I');
69
+ buffer.put((byte) 'F');
70
+ buffer.put((byte) 'F');
71
+
72
+ buffer.putInt(totalLength - 8);
73
+
74
+ buffer.put((byte) 'W');
75
+ buffer.put((byte) 'A');
76
+ buffer.put((byte) 'V');
77
+ buffer.put((byte) 'E');
78
+
79
+ buffer.put((byte) 'f');
80
+ buffer.put((byte) 'm');
81
+ buffer.put((byte) 't');
82
+ buffer.put((byte) ' ');
83
+
84
+ buffer.putInt(16);
85
+ buffer.putShort((short) 1);
86
+ buffer.putShort((short) 1);
87
+ buffer.putInt(16000);
88
+ buffer.putInt(32000);
89
+ buffer.putShort((short) 2);
90
+ buffer.putShort((short) 16);
91
+
92
+ buffer.put((byte) 'd');
93
+ buffer.put((byte) 'a');
94
+ buffer.put((byte) 't');
95
+ buffer.put((byte) 'a');
96
+
97
+ buffer.putInt(totalLength - 44);
98
+ buffer.position(0);
99
+
100
+ byte[] bytes = new byte[buffer.limit()];
101
+ buffer.get(bytes);
102
+
103
+ return bytes;
104
+ }
105
+ }
examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/CpuInfo.java ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercpp.java.whisper;
2
+
3
+ import android.os.Build;
4
+ import android.util.Log;
5
+
6
+ import androidx.annotation.RequiresApi;
7
+
8
+ import java.io.BufferedReader;
9
+ import java.io.FileReader;
10
+ import java.io.IOException;
11
+ import java.util.ArrayList;
12
+ import java.util.HashMap;
13
+ import java.util.List;
14
+ import java.util.Map;
15
+
16
+ public class CpuInfo {
17
+ private static final String LOG_TAG = "WhisperCpuConfig";
18
+
19
+ private List<String> lines;
20
+
21
+ public CpuInfo(List<String> lines) {
22
+ this.lines = lines;
23
+ }
24
+
25
+ @RequiresApi(api = Build.VERSION_CODES.N)
26
+ public int getHighPerfCpuCount0() {
27
+ try {
28
+ return getHighPerfCpuCountByFrequencies();
29
+ } catch (Exception e) {
30
+ Log.d(LOG_TAG, "Couldn't read CPU frequencies", e);
31
+ return getHighPerfCpuCountByVariant();
32
+ }
33
+ }
34
+
35
+ @RequiresApi(api = Build.VERSION_CODES.N)
36
+ private int getHighPerfCpuCountByFrequencies() {
37
+ List<Integer> frequencies = getCpuValues("processor", line -> {
38
+ try {
39
+ return getMaxCpuFrequency(Integer.parseInt(line.trim()));
40
+ } catch (IOException e) {
41
+ e.printStackTrace();
42
+ }
43
+ return 0;
44
+ }
45
+ );
46
+ Log.d(LOG_TAG, "Binned cpu frequencies (frequency, count): " + binnedValues(frequencies));
47
+ return countDroppingMin(frequencies);
48
+ }
49
+
50
+ @RequiresApi(api = Build.VERSION_CODES.N)
51
+ private int getHighPerfCpuCountByVariant() {
52
+ List<Integer> variants = getCpuValues("CPU variant", line -> Integer.parseInt(line.trim().substring(line.indexOf("0x") + 2), 16));
53
+ Log.d(LOG_TAG, "Binned cpu variants (variant, count): " + binnedValues(variants));
54
+ return countKeepingMin(variants);
55
+ }
56
+
57
+ @RequiresApi(api = Build.VERSION_CODES.N)
58
+ private Map<Integer, Integer> binnedValues(List<Integer> values) {
59
+ Map<Integer, Integer> countMap = new HashMap<>();
60
+ for (int value : values) {
61
+ countMap.put(value, countMap.getOrDefault(value, 0) + 1);
62
+ }
63
+ return countMap;
64
+ }
65
+
66
+ @RequiresApi(api = Build.VERSION_CODES.N)
67
+ private List<Integer> getCpuValues(String property, Mapper mapper) {
68
+ List<Integer> values = new ArrayList<>();
69
+ for (String line : lines) {
70
+ if (line.startsWith(property)) {
71
+ values.add(mapper.map(line.substring(line.indexOf(':') + 1)));
72
+ }
73
+ }
74
+ values.sort(Integer::compareTo);
75
+ return values;
76
+ }
77
+
78
+ @RequiresApi(api = Build.VERSION_CODES.N)
79
+ private int countDroppingMin(List<Integer> values) {
80
+ int min = values.stream().mapToInt(i -> i).min().orElse(Integer.MAX_VALUE);
81
+ return (int) values.stream().filter(value -> value > min).count();
82
+ }
83
+
84
+ @RequiresApi(api = Build.VERSION_CODES.N)
85
+ private int countKeepingMin(List<Integer> values) {
86
+ int min = values.stream().mapToInt(i -> i).min().orElse(Integer.MAX_VALUE);
87
+ return (int) values.stream().filter(value -> value.equals(min)).count();
88
+ }
89
+
90
+ @RequiresApi(api = Build.VERSION_CODES.N)
91
+ public static int getHighPerfCpuCount() {
92
+ try {
93
+ return readCpuInfo().getHighPerfCpuCount0();
94
+ } catch (Exception e) {
95
+ Log.d(LOG_TAG, "Couldn't read CPU info", e);
96
+ return Math.max(Runtime.getRuntime().availableProcessors() - 4, 0);
97
+ }
98
+ }
99
+
100
+ private static CpuInfo readCpuInfo() throws IOException {
101
+ try (BufferedReader reader = new BufferedReader(new FileReader("/proc/cpuinfo"))) {
102
+ List<String> lines = new ArrayList<>();
103
+ String line;
104
+ while ((line = reader.readLine()) != null) {
105
+ lines.add(line);
106
+ }
107
+ return new CpuInfo(lines);
108
+ }
109
+ }
110
+
111
+ private static int getMaxCpuFrequency(int cpuIndex) throws IOException {
112
+ String path = "/sys/devices/system/cpu/cpu" + cpuIndex + "/cpufreq/cpuinfo_max_freq";
113
+ try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
114
+ return Integer.parseInt(reader.readLine());
115
+ }
116
+ }
117
+
118
+ private interface Mapper {
119
+ int map(String line);
120
+ }
121
+ }
examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperContext.java ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercpp.java.whisper;
2
+
3
+ import android.content.res.AssetManager;
4
+ import android.os.Build;
5
+ import android.util.Log;
6
+
7
+ import androidx.annotation.RequiresApi;
8
+
9
+ import com.litongjava.whisper.android.java.bean.WhisperSegment;
10
+
11
+ import java.io.InputStream;
12
+ import java.util.ArrayList;
13
+ import java.util.List;
14
+ import java.util.concurrent.Callable;
15
+ import java.util.concurrent.ExecutionException;
16
+ import java.util.concurrent.ExecutorService;
17
+ import java.util.concurrent.Executors;
18
+
19
+ public class WhisperContext {
20
+
21
+ private static final String LOG_TAG = "LibWhisper";
22
+ private long ptr;
23
+ private final ExecutorService executorService;
24
+
25
+ private WhisperContext(long ptr) {
26
+ this.ptr = ptr;
27
+ this.executorService = Executors.newSingleThreadExecutor();
28
+ }
29
+
30
+ public String transcribeData(float[] data) throws ExecutionException, InterruptedException {
31
+ return executorService.submit(new Callable<String>() {
32
+ @RequiresApi(api = Build.VERSION_CODES.O)
33
+ @Override
34
+ public String call() throws Exception {
35
+ if (ptr == 0L) {
36
+ throw new IllegalStateException();
37
+ }
38
+ int numThreads = WhisperCpuConfig.getPreferredThreadCount();
39
+ Log.d(LOG_TAG, "Selecting " + numThreads + " threads");
40
+
41
+ StringBuilder result = new StringBuilder();
42
+ synchronized (this) {
43
+
44
+ WhisperLib.fullTranscribe(ptr, numThreads, data);
45
+ int textCount = WhisperLib.getTextSegmentCount(ptr);
46
+ for (int i = 0; i < textCount; i++) {
47
+ String sentence = WhisperLib.getTextSegment(ptr, i);
48
+ result.append(sentence);
49
+ }
50
+ }
51
+ return result.toString();
52
+ }
53
+ }).get();
54
+ }
55
+
56
+ public List<WhisperSegment> transcribeDataWithTime(float[] data) throws ExecutionException, InterruptedException {
57
+ return executorService.submit(new Callable<List<WhisperSegment>>() {
58
+ @RequiresApi(api = Build.VERSION_CODES.O)
59
+ @Override
60
+ public List<WhisperSegment> call() throws Exception {
61
+ if (ptr == 0L) {
62
+ throw new IllegalStateException();
63
+ }
64
+ int numThreads = WhisperCpuConfig.getPreferredThreadCount();
65
+ Log.d(LOG_TAG, "Selecting " + numThreads + " threads");
66
+
67
+ List<WhisperSegment> segments = new ArrayList<>();
68
+ synchronized (this) {
69
+ // StringBuilder result = new StringBuilder();
70
+ WhisperLib.fullTranscribe(ptr, numThreads, data);
71
+ int textCount = WhisperLib.getTextSegmentCount(ptr);
72
+ for (int i = 0; i < textCount; i++) {
73
+ long start = WhisperLib.getTextSegmentT0(ptr, i);
74
+ String sentence = WhisperLib.getTextSegment(ptr, i);
75
+ long end = WhisperLib.getTextSegmentT1(ptr, i);
76
+ // result.append();
77
+ segments.add(new WhisperSegment(start, end, sentence));
78
+
79
+ }
80
+ // return result.toString();
81
+ }
82
+ return segments;
83
+ }
84
+ }).get();
85
+ }
86
+
87
+ @RequiresApi(api = Build.VERSION_CODES.O)
88
+ public String benchMemory(int nthreads) throws ExecutionException, InterruptedException {
89
+ return executorService.submit(() -> WhisperLib.benchMemcpy(nthreads)).get();
90
+ }
91
+
92
+ @RequiresApi(api = Build.VERSION_CODES.O)
93
+ public String benchGgmlMulMat(int nthreads) throws ExecutionException, InterruptedException {
94
+ return executorService.submit(() -> WhisperLib.benchGgmlMulMat(nthreads)).get();
95
+ }
96
+
97
+ @RequiresApi(api = Build.VERSION_CODES.O)
98
+ public void release() throws ExecutionException, InterruptedException {
99
+ executorService.submit(() -> {
100
+ if (ptr != 0L) {
101
+ WhisperLib.freeContext(ptr);
102
+ ptr = 0;
103
+ }
104
+ }).get();
105
+ }
106
+
107
+ @RequiresApi(api = Build.VERSION_CODES.O)
108
+ public static WhisperContext createContextFromFile(String filePath) {
109
+ long ptr = WhisperLib.initContext(filePath);
110
+ if (ptr == 0L) {
111
+ throw new RuntimeException("Couldn't create context with path " + filePath);
112
+ }
113
+ return new WhisperContext(ptr);
114
+ }
115
+
116
+ @RequiresApi(api = Build.VERSION_CODES.O)
117
+ public static WhisperContext createContextFromInputStream(InputStream stream) {
118
+ long ptr = WhisperLib.initContextFromInputStream(stream);
119
+ if (ptr == 0L) {
120
+ throw new RuntimeException("Couldn't create context from input stream");
121
+ }
122
+ return new WhisperContext(ptr);
123
+ }
124
+
125
+ @RequiresApi(api = Build.VERSION_CODES.O)
126
+ public static WhisperContext createContextFromAsset(AssetManager assetManager, String assetPath) {
127
+ long ptr = WhisperLib.initContextFromAsset(assetManager, assetPath);
128
+ if (ptr == 0L) {
129
+ throw new RuntimeException("Couldn't create context from asset " + assetPath);
130
+ }
131
+ return new WhisperContext(ptr);
132
+ }
133
+
134
+ @RequiresApi(api = Build.VERSION_CODES.O)
135
+ public static String getSystemInfo() {
136
+ return WhisperLib.getSystemInfo();
137
+ }
138
+ }
examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperCpuConfig.java ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercpp.java.whisper;
2
+
3
+ import android.os.Build;
4
+
5
+ import androidx.annotation.RequiresApi;
6
+
7
+ public class WhisperCpuConfig {
8
+ @RequiresApi(api = Build.VERSION_CODES.N)
9
+ public static int getPreferredThreadCount() {
10
+ return Math.max(CpuInfo.getHighPerfCpuCount(), 2);
11
+ }
12
+ }
examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperLib.java ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercpp.java.whisper;
2
+
3
+ import android.content.res.AssetManager;
4
+ import android.os.Build;
5
+ import android.util.Log;
6
+
7
+ import androidx.annotation.RequiresApi;
8
+
9
+ import java.io.InputStream;
10
+
11
+ @RequiresApi(api = Build.VERSION_CODES.O)
12
+ public class WhisperLib {
13
+ private static final String LOG_TAG = "LibWhisper";
14
+
15
+ static {
16
+
17
+ Log.d(LOG_TAG, "Primary ABI: " + Build.SUPPORTED_ABIS[0]);
18
+ boolean loadVfpv4 = false;
19
+ boolean loadV8fp16 = false;
20
+ if (WhisperUtils.isArmEabiV7a()) {
21
+ String cpuInfo = WhisperUtils.cpuInfo();
22
+ if (cpuInfo != null) {
23
+ Log.d(LOG_TAG, "CPU info: " + cpuInfo);
24
+ if (cpuInfo.contains("vfpv4")) {
25
+ Log.d(LOG_TAG, "CPU supports vfpv4");
26
+ loadVfpv4 = true;
27
+ }
28
+ }
29
+ } else if (WhisperUtils.isArmEabiV8a()) {
30
+ String cpuInfo = WhisperUtils.cpuInfo();
31
+ if (cpuInfo != null) {
32
+ Log.d(LOG_TAG, "CPU info: " + cpuInfo);
33
+ if (cpuInfo.contains("fphp")) {
34
+ Log.d(LOG_TAG, "CPU supports fp16 arithmetic");
35
+ loadV8fp16 = true;
36
+ }
37
+ }
38
+ }
39
+
40
+ if (loadVfpv4) {
41
+ Log.d(LOG_TAG, "Loading libwhisper_vfpv4.so");
42
+ System.loadLibrary("whisper_vfpv4");
43
+ } else if (loadV8fp16) {
44
+ Log.d(LOG_TAG, "Loading libwhisper_v8fp16_va.so");
45
+ System.loadLibrary("whisper_v8fp16_va");
46
+ } else {
47
+ Log.d(LOG_TAG, "Loading libwhisper.so");
48
+ System.loadLibrary("whisper");
49
+ }
50
+ }
51
+
52
+ public static native long initContextFromInputStream(InputStream inputStream);
53
+
54
+ public static native long initContextFromAsset(AssetManager assetManager, String assetPath);
55
+
56
+ public static native long initContext(String modelPath);
57
+
58
+ public static native void freeContext(long contextPtr);
59
+
60
+ public static native void fullTranscribe(long contextPtr, int numThreads, float[] audioData);
61
+
62
+ public static native int getTextSegmentCount(long contextPtr);
63
+
64
+ public static native String getTextSegment(long contextPtr, int index);
65
+
66
+ public static native long getTextSegmentT0(long contextPtr, int index);
67
+
68
+ public static native long getTextSegmentT1(long contextPtr, int index);
69
+
70
+ public static native String getSystemInfo();
71
+
72
+ public static native String benchMemcpy(int nthread);
73
+
74
+ public static native String benchGgmlMulMat(int nthread);
75
+ }
examples/whisper.android.java/app/src/main/java/com/whispercpp/java/whisper/WhisperUtils.java ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.whispercpp.java.whisper;
2
+
3
+ import android.os.Build;
4
+ import android.util.Log;
5
+
6
+ import androidx.annotation.RequiresApi;
7
+
8
+ import java.io.File;
9
+ import java.nio.file.Path;
10
+
11
+ public class WhisperUtils {
12
+ private static final String LOG_TAG = "LibWhisper";
13
+
14
+
15
+ public static boolean isArmEabiV7a() {
16
+ return Build.SUPPORTED_ABIS[0].equals("armeabi-v7a");
17
+ }
18
+
19
+ public static boolean isArmEabiV8a() {
20
+ return Build.SUPPORTED_ABIS[0].equals("arm64-v8a");
21
+ }
22
+
23
+ @RequiresApi(api = Build.VERSION_CODES.O)
24
+ public static String cpuInfo() {
25
+ try {
26
+ Path path = new File("/proc/cpuinfo").toPath();
27
+ return new String(java.nio.file.Files.readAllBytes(path));
28
+ } catch (Exception e) {
29
+ Log.w(LOG_TAG, "Couldn't read /proc/cpuinfo", e);
30
+ return null;
31
+ }
32
+
33
+ }
34
+ }
examples/whisper.android.java/app/src/main/jni/whisper/CMakeLists.txt ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cmake_minimum_required(VERSION 3.10)
2
+
3
+ project(whisper.cpp)
4
+
5
+ set(CMAKE_CXX_STANDARD 11)
6
+ set(WHISPER_LIB_DIR ${CMAKE_SOURCE_DIR}/../../../../../../../)
7
+
8
+ set(
9
+ SOURCE_FILES
10
+ ${WHISPER_LIB_DIR}/ggml.c
11
+ ${WHISPER_LIB_DIR}/ggml-alloc.c
12
+ ${WHISPER_LIB_DIR}/ggml-backend.c
13
+ ${WHISPER_LIB_DIR}/ggml-quants.c
14
+ ${WHISPER_LIB_DIR}/whisper.cpp
15
+ ${CMAKE_SOURCE_DIR}/jni.c
16
+ )
17
+
18
+ find_library(LOG_LIB log)
19
+
20
+ function(build_library target_name)
21
+ add_library(
22
+ ${target_name}
23
+ SHARED
24
+ ${SOURCE_FILES}
25
+ )
26
+
27
+ target_link_libraries(${target_name} ${LOG_LIB} android)
28
+
29
+ if (${target_name} STREQUAL "whisper_v8fp16_va")
30
+ target_compile_options(${target_name} PRIVATE -march=armv8.2-a+fp16)
31
+ elseif (${target_name} STREQUAL "whisper_vfpv4")
32
+ target_compile_options(${target_name} PRIVATE -mfpu=neon-vfpv4)
33
+ endif ()
34
+
35
+ if (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
36
+
37
+ target_compile_options(${target_name} PRIVATE -O3)
38
+ target_compile_options(${target_name} PRIVATE -fvisibility=hidden -fvisibility-inlines-hidden)
39
+ target_compile_options(${target_name} PRIVATE -ffunction-sections -fdata-sections)
40
+
41
+ #target_link_options(${target_name} PRIVATE -Wl,--gc-sections)
42
+ #target_link_options(${target_name} PRIVATE -Wl,--exclude-libs,ALL)
43
+ #target_link_options(${target_name} PRIVATE -flto)
44
+
45
+ endif ()
46
+ endfunction()
47
+
48
+ build_library("whisper") # Default target
49
+
50
+ if (${ANDROID_ABI} STREQUAL "arm64-v8a")
51
+ build_library("whisper_v8fp16_va")
52
+ elseif (${ANDROID_ABI} STREQUAL "armeabi-v7a")
53
+ build_library("whisper_vfpv4")
54
+ endif ()
55
+
56
+ include_directories(${WHISPER_LIB_DIR})
examples/whisper.android.java/app/src/main/jni/whisper/jni.c ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <jni.h>
2
+ #include <android/asset_manager.h>
3
+ #include <android/asset_manager_jni.h>
4
+ #include <android/log.h>
5
+ #include <stdlib.h>
6
+ #include <sys/sysinfo.h>
7
+ #include <string.h>
8
+ #include "whisper.h"
9
+ #include "ggml.h"
10
+
11
+ #define UNUSED(x) (void)(x)
12
+ #define TAG "JNI"
13
+
14
+ #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
15
+ #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
16
+
17
+ static inline int min(int a, int b) {
18
+ return (a < b) ? a : b;
19
+ }
20
+
21
+ static inline int max(int a, int b) {
22
+ return (a > b) ? a : b;
23
+ }
24
+
25
+ struct input_stream_context {
26
+ size_t offset;
27
+ JNIEnv * env;
28
+ jobject thiz;
29
+ jobject input_stream;
30
+
31
+ jmethodID mid_available;
32
+ jmethodID mid_read;
33
+ };
34
+
35
+ size_t inputStreamRead(void * ctx, void * output, size_t read_size) {
36
+ struct input_stream_context* is = (struct input_stream_context*)ctx;
37
+
38
+ jint avail_size = (*is->env)->CallIntMethod(is->env, is->input_stream, is->mid_available);
39
+ jint size_to_copy = read_size < avail_size ? (jint)read_size : avail_size;
40
+
41
+ jbyteArray byte_array = (*is->env)->NewByteArray(is->env, size_to_copy);
42
+
43
+ jint n_read = (*is->env)->CallIntMethod(is->env, is->input_stream, is->mid_read, byte_array, 0, size_to_copy);
44
+
45
+ if (size_to_copy != read_size || size_to_copy != n_read) {
46
+ LOGI("Insufficient Read: Req=%zu, ToCopy=%d, Available=%d", read_size, size_to_copy, n_read);
47
+ }
48
+
49
+ jbyte* byte_array_elements = (*is->env)->GetByteArrayElements(is->env, byte_array, NULL);
50
+ memcpy(output, byte_array_elements, size_to_copy);
51
+ (*is->env)->ReleaseByteArrayElements(is->env, byte_array, byte_array_elements, JNI_ABORT);
52
+
53
+ (*is->env)->DeleteLocalRef(is->env, byte_array);
54
+
55
+ is->offset += size_to_copy;
56
+
57
+ return size_to_copy;
58
+ }
59
+ bool inputStreamEof(void * ctx) {
60
+ struct input_stream_context* is = (struct input_stream_context*)ctx;
61
+
62
+ jint result = (*is->env)->CallIntMethod(is->env, is->input_stream, is->mid_available);
63
+ return result <= 0;
64
+ }
65
+ void inputStreamClose(void * ctx) {
66
+
67
+ }
68
+
69
+ JNIEXPORT jlong JNICALL
70
+ Java_com_whispercpp_java_whisper_WhisperLib_initContextFromInputStream(
71
+ JNIEnv *env, jobject thiz, jobject input_stream) {
72
+ UNUSED(thiz);
73
+
74
+ struct whisper_context *context = NULL;
75
+ struct whisper_model_loader loader = {};
76
+ struct input_stream_context inp_ctx = {};
77
+
78
+ inp_ctx.offset = 0;
79
+ inp_ctx.env = env;
80
+ inp_ctx.thiz = thiz;
81
+ inp_ctx.input_stream = input_stream;
82
+
83
+ jclass cls = (*env)->GetObjectClass(env, input_stream);
84
+ inp_ctx.mid_available = (*env)->GetMethodID(env, cls, "available", "()I");
85
+ inp_ctx.mid_read = (*env)->GetMethodID(env, cls, "read", "([BII)I");
86
+
87
+ loader.context = &inp_ctx;
88
+ loader.read = inputStreamRead;
89
+ loader.eof = inputStreamEof;
90
+ loader.close = inputStreamClose;
91
+
92
+ loader.eof(loader.context);
93
+
94
+ context = whisper_init(&loader);
95
+ return (jlong) context;
96
+ }
97
+
98
+ static size_t asset_read(void *ctx, void *output, size_t read_size) {
99
+ return AAsset_read((AAsset *) ctx, output, read_size);
100
+ }
101
+
102
+ static bool asset_is_eof(void *ctx) {
103
+ return AAsset_getRemainingLength64((AAsset *) ctx) <= 0;
104
+ }
105
+
106
+ static void asset_close(void *ctx) {
107
+ AAsset_close((AAsset *) ctx);
108
+ }
109
+
110
+ static struct whisper_context *whisper_init_from_asset(
111
+ JNIEnv *env,
112
+ jobject assetManager,
113
+ const char *asset_path
114
+ ) {
115
+ LOGI("Loading model from asset '%s'\n", asset_path);
116
+ AAssetManager *asset_manager = AAssetManager_fromJava(env, assetManager);
117
+ AAsset *asset = AAssetManager_open(asset_manager, asset_path, AASSET_MODE_STREAMING);
118
+ if (!asset) {
119
+ LOGW("Failed to open '%s'\n", asset_path);
120
+ return NULL;
121
+ }
122
+
123
+ whisper_model_loader loader = {
124
+ .context = asset,
125
+ .read = &asset_read,
126
+ .eof = &asset_is_eof,
127
+ .close = &asset_close
128
+ };
129
+
130
+ return whisper_init(&loader);
131
+ }
132
+
133
+ JNIEXPORT jlong JNICALL
134
+ Java_com_whispercpp_java_whisper_WhisperLib_initContextFromAsset(
135
+ JNIEnv *env, jobject thiz, jobject assetManager, jstring asset_path_str) {
136
+ UNUSED(thiz);
137
+ struct whisper_context *context = NULL;
138
+ const char *asset_path_chars = (*env)->GetStringUTFChars(env, asset_path_str, NULL);
139
+ context = whisper_init_from_asset(env, assetManager, asset_path_chars);
140
+ (*env)->ReleaseStringUTFChars(env, asset_path_str, asset_path_chars);
141
+ return (jlong) context;
142
+ }
143
+
144
+ JNIEXPORT jlong JNICALL
145
+ Java_com_whispercpp_java_whisper_WhisperLib_initContext(
146
+ JNIEnv *env, jobject thiz, jstring model_path_str) {
147
+ UNUSED(thiz);
148
+ struct whisper_context *context = NULL;
149
+ const char *model_path_chars = (*env)->GetStringUTFChars(env, model_path_str, NULL);
150
+ context = whisper_init_from_file(model_path_chars);
151
+ (*env)->ReleaseStringUTFChars(env, model_path_str, model_path_chars);
152
+ return (jlong) context;
153
+ }
154
+
155
+ JNIEXPORT void JNICALL
156
+ Java_com_whispercpp_java_whisper_WhisperLib_freeContext(
157
+ JNIEnv *env, jobject thiz, jlong context_ptr) {
158
+ UNUSED(env);
159
+ UNUSED(thiz);
160
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
161
+ whisper_free(context);
162
+ }
163
+
164
+ JNIEXPORT void JNICALL
165
+ Java_com_whispercpp_java_whisper_WhisperLib_fullTranscribe(
166
+ JNIEnv *env, jobject thiz, jlong context_ptr, jint num_threads, jfloatArray audio_data) {
167
+ UNUSED(thiz);
168
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
169
+ jfloat *audio_data_arr = (*env)->GetFloatArrayElements(env, audio_data, NULL);
170
+ const jsize audio_data_length = (*env)->GetArrayLength(env, audio_data);
171
+
172
+ // The below adapted from the Objective-C iOS sample
173
+ struct whisper_full_params params = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
174
+ params.print_realtime = true;
175
+ params.print_progress = false;
176
+ params.print_timestamps = true;
177
+ params.print_special = false;
178
+ params.translate = false;
179
+ params.language = "en";
180
+ params.n_threads = num_threads;
181
+ params.offset_ms = 0;
182
+ params.no_context = true;
183
+ params.single_segment = false;
184
+
185
+ whisper_reset_timings(context);
186
+
187
+ LOGI("About to run whisper_full");
188
+ if (whisper_full(context, params, audio_data_arr, audio_data_length) != 0) {
189
+ LOGI("Failed to run the model");
190
+ } else {
191
+ whisper_print_timings(context);
192
+ }
193
+ (*env)->ReleaseFloatArrayElements(env, audio_data, audio_data_arr, JNI_ABORT);
194
+ }
195
+
196
+ JNIEXPORT jint JNICALL
197
+ Java_com_whispercpp_java_whisper_WhisperLib_getTextSegmentCount(
198
+ JNIEnv *env, jobject thiz, jlong context_ptr) {
199
+ UNUSED(env);
200
+ UNUSED(thiz);
201
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
202
+ return whisper_full_n_segments(context);
203
+ }
204
+
205
+
206
+ JNIEXPORT jstring JNICALL
207
+ Java_com_whispercpp_java_whisper_WhisperLib_getTextSegment(
208
+ JNIEnv *env, jobject thiz, jlong context_ptr, jint index) {
209
+ UNUSED(thiz);
210
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
211
+ const char *text = whisper_full_get_segment_text(context, index);
212
+ jstring string = (*env)->NewStringUTF(env, text);
213
+ return string;
214
+ }
215
+
216
+ JNIEXPORT jlong JNICALL
217
+ Java_com_whispercpp_java_whisper_WhisperLib_getTextSegmentT0(JNIEnv *env, jobject thiz,jlong context_ptr, jint index) {
218
+ UNUSED(thiz);
219
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
220
+ const int64_t t0 = whisper_full_get_segment_t0(context, index);
221
+ return (jlong)t0;
222
+ }
223
+
224
+ JNIEXPORT jlong JNICALL
225
+ Java_com_whispercpp_java_whisper_WhisperLib_getTextSegmentT1(JNIEnv *env, jobject thiz,jlong context_ptr, jint index) {
226
+ UNUSED(thiz);
227
+ struct whisper_context *context = (struct whisper_context *) context_ptr;
228
+ const int64_t t1 = whisper_full_get_segment_t1(context, index);
229
+ return (jlong)t1;
230
+ }
231
+
232
+ JNIEXPORT jstring JNICALL
233
+ Java_com_whispercpp_java_whisper_WhisperLib_getSystemInfo(
234
+ JNIEnv *env, jobject thiz
235
+ ) {
236
+ UNUSED(thiz);
237
+ const char *sysinfo = whisper_print_system_info();
238
+ jstring string = (*env)->NewStringUTF(env, sysinfo);
239
+ return string;
240
+ }
241
+
242
+ JNIEXPORT jstring JNICALL
243
+ Java_com_whispercpp_java_whisper_WhisperLib_benchMemcpy(JNIEnv *env, jobject thiz,
244
+ jint n_threads) {
245
+ UNUSED(thiz);
246
+ const char *bench_ggml_memcpy = whisper_bench_memcpy_str(n_threads);
247
+ jstring string = (*env)->NewStringUTF(env, bench_ggml_memcpy);
248
+ }
249
+
250
+ JNIEXPORT jstring JNICALL
251
+ Java_com_whispercpp_java_whisper_WhisperLib_benchGgmlMulMat(JNIEnv *env, jobject thiz,
252
+ jint n_threads) {
253
+ UNUSED(thiz);
254
+ const char *bench_ggml_mul_mat = whisper_bench_ggml_mul_mat_str(n_threads);
255
+ jstring string = (*env)->NewStringUTF(env, bench_ggml_mul_mat);
256
+ }
257
+
examples/whisper.android.java/app/src/main/res/drawable-v24/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.java/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.java/app/src/main/res/layout/activity_main.xml ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <LinearLayout 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
+ android:orientation="vertical"
8
+ tools:context=".MainActivity">
9
+
10
+ <LinearLayout
11
+ android:layout_width="match_parent"
12
+ android:layout_height="wrap_content">
13
+
14
+ <Button
15
+ android:id="@+id/systemInfoBtn"
16
+ android:layout_width="wrap_content"
17
+ android:layout_height="wrap_content"
18
+ android:text="System Info" />
19
+
20
+ <Button
21
+ android:id="@+id/loadModelBtn"
22
+ android:layout_width="wrap_content"
23
+ android:layout_height="wrap_content"
24
+ android:text="Load model" />
25
+
26
+ </LinearLayout>
27
+
28
+ <LinearLayout
29
+ android:layout_width="wrap_content"
30
+ android:layout_height="wrap_content">
31
+
32
+ <Button
33
+ android:id="@+id/transcriptSampleBtn"
34
+ android:layout_width="wrap_content"
35
+ android:layout_height="wrap_content"
36
+ android:text="Transcribe sample" />
37
+
38
+ <Button
39
+ android:id="@+id/clearBtn"
40
+ android:layout_width="wrap_content"
41
+ android:layout_height="wrap_content"
42
+ android:text="Clear" />
43
+ </LinearLayout>
44
+
45
+ <TextView
46
+ android:id="@+id/sample_text"
47
+ android:layout_width="match_parent"
48
+ android:layout_height="wrap_content"
49
+ android:text="Hello World!"
50
+ app:layout_constraintBottom_toBottomOf="parent"
51
+ app:layout_constraintLeft_toLeftOf="parent"
52
+ app:layout_constraintRight_toRightOf="parent"
53
+ app:layout_constraintTop_toTopOf="parent"
54
+ android:scrollbarAlwaysDrawHorizontalTrack="true"
55
+ android:maxLines="999"/>
56
+
57
+ </LinearLayout>
examples/whisper.android.java/app/src/main/res/mipmap-anydpi-v26/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.java/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.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.java/app/src/main/res/mipmap-hdpi/ic_launcher.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-hdpi/ic_launcher_round.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-mdpi/ic_launcher.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-mdpi/ic_launcher_round.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-xhdpi/ic_launcher.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-xxhdpi/ic_launcher.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png ADDED
examples/whisper.android.java/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png ADDED
examples/whisper.android.java/app/src/main/res/values-night/themes.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <resources xmlns:tools="http://schemas.android.com/tools">
2
+ <!-- Base application theme. -->
3
+ <style name="Theme.Whisperandroidjava" 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" tools:targetApi="l">?attr/colorPrimaryVariant</item>
14
+ <!-- Customize your theme here. -->
15
+ </style>
16
+ </resources>
examples/whisper.android.java/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.java/app/src/main/res/values/strings.xml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ <resources>
2
+ <string name="app_name">whisper.android.java</string>
3
+ </resources>
examples/whisper.android.java/app/src/main/res/values/themes.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <resources xmlns:tools="http://schemas.android.com/tools">
2
+ <!-- Base application theme. -->
3
+ <style name="Theme.Whisperandroidjava" 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" tools:targetApi="l">?attr/colorPrimaryVariant</item>
14
+ <!-- Customize your theme here. -->
15
+ </style>
16
+ </resources>