whisper.rn 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +82 -0
- package/android/build.gradle +85 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/rnwhisper/RNWhisperModule.java +175 -0
- package/android/src/main/java/com/rnwhisper/RNWhisperPackage.java +28 -0
- package/android/src/main/java/com/rnwhisper/WhisperContext.java +186 -0
- package/android/src/main/jni/whisper/Android.mk +26 -0
- package/android/src/main/jni/whisper/Application.mk +1 -0
- package/android/src/main/jni/whisper/Whisper.mk +18 -0
- package/android/src/main/jni/whisper/jni.c +159 -0
- package/ios/RNWhisper.h +9 -0
- package/ios/RNWhisper.mm +199 -0
- package/ios/RNWhisper.xcodeproj/project.pbxproj +278 -0
- package/ios/RNWhisper.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
- package/ios/RNWhisper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/RNWhisper.xcodeproj/project.xcworkspace/xcuserdata/jhen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/RNWhisper.xcodeproj/xcuserdata/jhen.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/jest/mock.js +12 -0
- package/lib/commonjs/index.js +42 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/index.js +35 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/index.d.ts +28 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/package.json +132 -0
- package/src/index.tsx +63 -0
- package/whisper-rn.podspec +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Jhen <developer@jhen.me>
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# whisper.rn
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mybigday/whisper.rn/actions)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.npmjs.com/package/whisper.rn/)
|
|
6
|
+
|
|
7
|
+
React Native binding of [whisper.cpp](https://github.com/ggerganov/whisper.cpp).
|
|
8
|
+
|
|
9
|
+
[whisper.cpp](https://github.com/ggerganov/whisper.cpp): High-performance inference of [OpenAI's Whisper](https://github.com/openai/whisper) automatic speech recognition (ASR) model
|
|
10
|
+
|
|
11
|
+
<img src="https://user-images.githubusercontent.com/3001525/225511664-8b2ba3ec-864d-4f55-bcb0-447aef168a32.jpeg" width="500" />
|
|
12
|
+
|
|
13
|
+
> Run example with release mode on iPhone 13 Pro Max
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install whisper.rn
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then re-run `npx pod-install` again for iOS.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import { initWhisper } from 'whisper.rn'
|
|
27
|
+
|
|
28
|
+
const filePath = 'file://.../ggml.base.en.bin'
|
|
29
|
+
const sampleFilePath = 'file://.../sample.wav'
|
|
30
|
+
|
|
31
|
+
const whisperContext = await initWhisper({ filePath })
|
|
32
|
+
|
|
33
|
+
const { result } = await whisperContext.transcribe(sampleFilePath, {
|
|
34
|
+
language: 'en',
|
|
35
|
+
// More options
|
|
36
|
+
})
|
|
37
|
+
// result: (The inference text result from audio file)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Run with example
|
|
41
|
+
|
|
42
|
+
The example app is using [react-native-fs](https://github.com/itinance/react-native-fs) to download the model file and audio file.
|
|
43
|
+
|
|
44
|
+
Model: `base.en` in https://huggingface.co/datasets/ggerganov/whisper.cpp
|
|
45
|
+
Sample file: `jfk.wav` in https://github.com/ggerganov/whisper.cpp/tree/master/samples
|
|
46
|
+
|
|
47
|
+
For test better performance on transcribe, you can run the app in Release mode.
|
|
48
|
+
- iOS: `yarn example ios --configuration Release`
|
|
49
|
+
- Android: `yarn example android --mode release`
|
|
50
|
+
|
|
51
|
+
Please follow [CONTIBUTING.md](./CONTRIBUTING.md#development-workflow) to run the example app.
|
|
52
|
+
|
|
53
|
+
## Mock `whisper.rn`
|
|
54
|
+
|
|
55
|
+
We have provided a mock version of `whisper.rn` for testing purpose you can use on Jest:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
jest.mock('whisper.rn', () => require('whisper.rn/jest/mock'))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Contributing
|
|
62
|
+
|
|
63
|
+
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
<p align="center">
|
|
76
|
+
<a href="https://bricks.tools">
|
|
77
|
+
<img width="90px" src="https://avatars.githubusercontent.com/u/17320237?s=200&v=4">
|
|
78
|
+
</a>
|
|
79
|
+
<p align="center">
|
|
80
|
+
Built and maintained by <a href="https://bricks.tools">BRICKS</a>.
|
|
81
|
+
</p>
|
|
82
|
+
</p>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
classpath "com.android.tools.build:gradle:7.2.1"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def isNewArchitectureEnabled() {
|
|
13
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
apply plugin: "com.android.library"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
|
|
20
|
+
|
|
21
|
+
if (isNewArchitectureEnabled()) {
|
|
22
|
+
apply plugin: "com.facebook.react"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
def getExtOrDefault(name) {
|
|
26
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["RNWhisper_" + name]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def getExtOrIntegerDefault(name) {
|
|
30
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RNWhisper_" + name]).toInteger()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
android {
|
|
34
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
35
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
36
|
+
|
|
37
|
+
defaultConfig {
|
|
38
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
39
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
40
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
41
|
+
}
|
|
42
|
+
externalNativeBuild {
|
|
43
|
+
externalNativeBuild {
|
|
44
|
+
ndkBuild {
|
|
45
|
+
path 'src/main/jni/whisper/Android.mk'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
buildTypes {
|
|
50
|
+
release {
|
|
51
|
+
minifyEnabled false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
lintOptions {
|
|
56
|
+
disable "GradleCompatible"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
compileOptions {
|
|
60
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
61
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
repositories {
|
|
67
|
+
mavenCentral()
|
|
68
|
+
google()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
dependencies {
|
|
73
|
+
// For < 0.71, this will be from the local maven repo
|
|
74
|
+
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
75
|
+
//noinspection GradleDynamicVersion
|
|
76
|
+
implementation "com.facebook.react:react-native:+"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (isNewArchitectureEnabled()) {
|
|
80
|
+
react {
|
|
81
|
+
jsRootDir = file("../src/")
|
|
82
|
+
libraryName = "RNWhisper"
|
|
83
|
+
codegenJavaPackageName = "com.rnwhisper"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
package com.rnwhisper;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import android.util.Log;
|
|
5
|
+
import android.os.Build;
|
|
6
|
+
import android.os.Handler;
|
|
7
|
+
import android.os.AsyncTask;
|
|
8
|
+
|
|
9
|
+
import com.facebook.react.bridge.Promise;
|
|
10
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
11
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
12
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
13
|
+
import com.facebook.react.bridge.LifecycleEventListener;
|
|
14
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
15
|
+
import com.facebook.react.module.annotations.ReactModule;
|
|
16
|
+
|
|
17
|
+
import java.util.HashMap;
|
|
18
|
+
import java.util.Random;
|
|
19
|
+
|
|
20
|
+
@ReactModule(name = RNWhisperModule.NAME)
|
|
21
|
+
public class RNWhisperModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
|
|
22
|
+
public static final String NAME = "RNWhisper";
|
|
23
|
+
|
|
24
|
+
private ReactApplicationContext reactContext;
|
|
25
|
+
|
|
26
|
+
public RNWhisperModule(ReactApplicationContext reactContext) {
|
|
27
|
+
super(reactContext);
|
|
28
|
+
reactContext.addLifecycleEventListener(this);
|
|
29
|
+
this.reactContext = reactContext;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Override
|
|
33
|
+
@NonNull
|
|
34
|
+
public String getName() {
|
|
35
|
+
return NAME;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private HashMap<Integer, WhisperContext> contexts = new HashMap<>();
|
|
39
|
+
|
|
40
|
+
@ReactMethod
|
|
41
|
+
public void initContext(final String modelPath, final Promise promise) {
|
|
42
|
+
new AsyncTask<Void, Void, Integer>() {
|
|
43
|
+
private Exception exception;
|
|
44
|
+
|
|
45
|
+
@Override
|
|
46
|
+
protected Integer doInBackground(Void... voids) {
|
|
47
|
+
try {
|
|
48
|
+
long context = WhisperContext.initContext(modelPath);
|
|
49
|
+
if (context == 0) {
|
|
50
|
+
throw new Exception("Failed to initialize context");
|
|
51
|
+
}
|
|
52
|
+
int id = Math.abs(new Random().nextInt());
|
|
53
|
+
WhisperContext whisperContext = new WhisperContext(context);
|
|
54
|
+
contexts.put(id, whisperContext);
|
|
55
|
+
return id;
|
|
56
|
+
} catch (Exception e) {
|
|
57
|
+
exception = e;
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@Override
|
|
63
|
+
protected void onPostExecute(Integer id) {
|
|
64
|
+
if (exception != null) {
|
|
65
|
+
promise.reject(exception);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
promise.resolve(id);
|
|
69
|
+
}
|
|
70
|
+
}.execute();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@ReactMethod
|
|
74
|
+
public void transcribe(int id, String filePath, ReadableMap options, Promise promise) {
|
|
75
|
+
new AsyncTask<Void, Void, String>() {
|
|
76
|
+
private Exception exception;
|
|
77
|
+
|
|
78
|
+
@Override
|
|
79
|
+
protected String doInBackground(Void... voids) {
|
|
80
|
+
try {
|
|
81
|
+
WhisperContext context = contexts.get(id);
|
|
82
|
+
if (context == null) {
|
|
83
|
+
throw new Exception("Context " + id + " not found");
|
|
84
|
+
}
|
|
85
|
+
return context.transcribe(filePath, options);
|
|
86
|
+
} catch (Exception e) {
|
|
87
|
+
exception = e;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Override
|
|
93
|
+
protected void onPostExecute(String result) {
|
|
94
|
+
if (exception != null) {
|
|
95
|
+
promise.reject(exception);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
promise.resolve(result);
|
|
99
|
+
}
|
|
100
|
+
}.execute();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@ReactMethod
|
|
104
|
+
public void releaseContext(int id, Promise promise) {
|
|
105
|
+
new AsyncTask<Void, Void, Void>() {
|
|
106
|
+
private Exception exception;
|
|
107
|
+
|
|
108
|
+
@Override
|
|
109
|
+
protected Void doInBackground(Void... voids) {
|
|
110
|
+
try {
|
|
111
|
+
WhisperContext context = contexts.get(id);
|
|
112
|
+
if (context == null) {
|
|
113
|
+
throw new Exception("Context " + id + " not found");
|
|
114
|
+
}
|
|
115
|
+
context.release();
|
|
116
|
+
contexts.remove(id);
|
|
117
|
+
} catch (Exception e) {
|
|
118
|
+
exception = e;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@Override
|
|
124
|
+
protected void onPostExecute(Void result) {
|
|
125
|
+
if (exception != null) {
|
|
126
|
+
promise.reject(exception);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
promise.resolve(null);
|
|
130
|
+
}
|
|
131
|
+
}.execute();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@ReactMethod
|
|
135
|
+
public void releaseAllContexts(Promise promise) {
|
|
136
|
+
new AsyncTask<Void, Void, Void>() {
|
|
137
|
+
private Exception exception;
|
|
138
|
+
|
|
139
|
+
@Override
|
|
140
|
+
protected Void doInBackground(Void... voids) {
|
|
141
|
+
try {
|
|
142
|
+
onHostDestroy();
|
|
143
|
+
} catch (Exception e) {
|
|
144
|
+
exception = e;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@Override
|
|
150
|
+
protected void onPostExecute(Void result) {
|
|
151
|
+
if (exception != null) {
|
|
152
|
+
promise.reject(exception);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
promise.resolve(null);
|
|
156
|
+
}
|
|
157
|
+
}.execute();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@Override
|
|
161
|
+
public void onHostResume() {
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@Override
|
|
165
|
+
public void onHostPause() {
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@Override
|
|
169
|
+
public void onHostDestroy() {
|
|
170
|
+
for (WhisperContext context : contexts.values()) {
|
|
171
|
+
context.release();
|
|
172
|
+
}
|
|
173
|
+
contexts.clear();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package com.rnwhisper;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.ReactPackage;
|
|
6
|
+
import com.facebook.react.bridge.NativeModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
9
|
+
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.Collections;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
|
|
14
|
+
public class RNWhisperPackage implements ReactPackage {
|
|
15
|
+
@NonNull
|
|
16
|
+
@Override
|
|
17
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
18
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
19
|
+
modules.add(new RNWhisperModule(reactContext));
|
|
20
|
+
return modules;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@NonNull
|
|
24
|
+
@Override
|
|
25
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
26
|
+
return Collections.emptyList();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
package com.rnwhisper;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
4
|
+
|
|
5
|
+
import android.util.Log;
|
|
6
|
+
import android.os.Build;
|
|
7
|
+
import android.content.res.AssetManager;
|
|
8
|
+
|
|
9
|
+
import java.util.Random;
|
|
10
|
+
import java.lang.StringBuilder;
|
|
11
|
+
import java.io.File;
|
|
12
|
+
import java.io.BufferedReader;
|
|
13
|
+
import java.io.IOException;
|
|
14
|
+
import java.io.FileReader;
|
|
15
|
+
import java.io.ByteArrayOutputStream;
|
|
16
|
+
import java.io.File;
|
|
17
|
+
import java.io.FileInputStream;
|
|
18
|
+
import java.io.IOException;
|
|
19
|
+
import java.io.InputStream;
|
|
20
|
+
import java.nio.ByteBuffer;
|
|
21
|
+
import java.nio.ByteOrder;
|
|
22
|
+
import java.nio.ShortBuffer;
|
|
23
|
+
|
|
24
|
+
public class WhisperContext {
|
|
25
|
+
public static final String NAME = "RNWhisperContext";
|
|
26
|
+
private long context;
|
|
27
|
+
|
|
28
|
+
public WhisperContext(long context) {
|
|
29
|
+
this.context = context;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public String transcribe(final String filePath, final ReadableMap options) throws IOException, Exception {
|
|
33
|
+
int code = fullTranscribe(
|
|
34
|
+
context,
|
|
35
|
+
decodeWaveFile(new File(filePath)),
|
|
36
|
+
// jint n_threads,
|
|
37
|
+
options.hasKey("maxThreads") ? options.getInt("maxThreads") : -1,
|
|
38
|
+
// jint max_context,
|
|
39
|
+
options.hasKey("maxContext") ? options.getInt("maxContext") : -1,
|
|
40
|
+
// jint max_len,
|
|
41
|
+
options.hasKey("maxLen") ? options.getInt("maxLen") : -1,
|
|
42
|
+
// jint offset,
|
|
43
|
+
options.hasKey("offset") ? options.getInt("offset") : -1,
|
|
44
|
+
// jint duration,
|
|
45
|
+
options.hasKey("duration") ? options.getInt("duration") : -1,
|
|
46
|
+
// jint word_thold,
|
|
47
|
+
options.hasKey("wordThold") ? options.getInt("wordThold") : -1,
|
|
48
|
+
// jfloat temperature,
|
|
49
|
+
options.hasKey("temperature") ? (float) options.getDouble("temperature") : -1.0f,
|
|
50
|
+
// jfloat temperature_inc,
|
|
51
|
+
options.hasKey("temperatureInc") ? (float) options.getDouble("temperatureInc") : -1.0f,
|
|
52
|
+
// jint beam_size,
|
|
53
|
+
options.hasKey("beamSize") ? options.getInt("beamSize") : -1,
|
|
54
|
+
// jint best_of,
|
|
55
|
+
options.hasKey("bestOf") ? options.getInt("bestOf") : -1,
|
|
56
|
+
// jboolean speed_up,
|
|
57
|
+
options.hasKey("speedUp") ? options.getBoolean("speedUp") : false,
|
|
58
|
+
// jboolean translate,
|
|
59
|
+
options.hasKey("translate") ? options.getBoolean("translate") : false,
|
|
60
|
+
// jstring language,
|
|
61
|
+
options.hasKey("language") ? options.getString("language") : "auto"
|
|
62
|
+
);
|
|
63
|
+
if (code != 0) {
|
|
64
|
+
throw new Exception("Transcription failed with code " + code);
|
|
65
|
+
}
|
|
66
|
+
Integer count = getTextSegmentCount(context);
|
|
67
|
+
StringBuilder builder = new StringBuilder();
|
|
68
|
+
for (int i = 0; i < count; i++) {
|
|
69
|
+
builder.append(getTextSegment(context, i));
|
|
70
|
+
}
|
|
71
|
+
return builder.toString();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public void release() {
|
|
75
|
+
freeContext(context);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public static float[] decodeWaveFile(File file) throws IOException {
|
|
79
|
+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
80
|
+
try (InputStream inputStream = new FileInputStream(file)) {
|
|
81
|
+
byte[] buffer = new byte[1024];
|
|
82
|
+
int bytesRead;
|
|
83
|
+
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
84
|
+
baos.write(buffer, 0, bytesRead);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
ByteBuffer byteBuffer = ByteBuffer.wrap(baos.toByteArray());
|
|
88
|
+
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
89
|
+
byteBuffer.position(44);
|
|
90
|
+
ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
|
|
91
|
+
short[] shortArray = new short[shortBuffer.limit()];
|
|
92
|
+
shortBuffer.get(shortArray);
|
|
93
|
+
float[] floatArray = new float[shortArray.length];
|
|
94
|
+
for (int i = 0; i < shortArray.length; i++) {
|
|
95
|
+
floatArray[i] = ((float) shortArray[i]) / 32767.0f;
|
|
96
|
+
floatArray[i] = Math.max(floatArray[i], -1f);
|
|
97
|
+
floatArray[i] = Math.min(floatArray[i], 1f);
|
|
98
|
+
}
|
|
99
|
+
return floatArray;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static {
|
|
103
|
+
Log.d(NAME, "Primary ABI: " + Build.SUPPORTED_ABIS[0]);
|
|
104
|
+
boolean loadVfpv4 = false;
|
|
105
|
+
boolean loadV8fp16 = false;
|
|
106
|
+
if (isArmeabiV7a()) {
|
|
107
|
+
// armeabi-v7a needs runtime detection support
|
|
108
|
+
String cpuInfo = cpuInfo();
|
|
109
|
+
if (cpuInfo != null) {
|
|
110
|
+
Log.d(NAME, "CPU info: " + cpuInfo);
|
|
111
|
+
if (cpuInfo.contains("vfpv4")) {
|
|
112
|
+
Log.d(NAME, "CPU supports vfpv4");
|
|
113
|
+
loadVfpv4 = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} else if (isArmeabiV8a()) {
|
|
117
|
+
// ARMv8.2a needs runtime detection support
|
|
118
|
+
String cpuInfo = cpuInfo();
|
|
119
|
+
if (cpuInfo != null) {
|
|
120
|
+
Log.d(NAME, "CPU info: " + cpuInfo);
|
|
121
|
+
if (cpuInfo.contains("fphp")) {
|
|
122
|
+
Log.d(NAME, "CPU supports fp16 arithmetic");
|
|
123
|
+
loadV8fp16 = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (loadVfpv4) {
|
|
129
|
+
Log.d(NAME, "Loading libwhisper_vfpv4.so");
|
|
130
|
+
System.loadLibrary("whisper_vfpv4");
|
|
131
|
+
} else if (loadV8fp16) {
|
|
132
|
+
Log.d(NAME, "Loading libwhisper_v8fp16_va.so");
|
|
133
|
+
System.loadLibrary("whisper_v8fp16_va");
|
|
134
|
+
} else {
|
|
135
|
+
Log.d(NAME, "Loading libwhisper.so");
|
|
136
|
+
System.loadLibrary("whisper");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private static boolean isArmeabiV7a() {
|
|
141
|
+
return Build.SUPPORTED_ABIS[0].equals("armeabi-v7a");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private static boolean isArmeabiV8a() {
|
|
145
|
+
return Build.SUPPORTED_ABIS[0].equals("arm64-v8a");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private static String cpuInfo() {
|
|
149
|
+
File file = new File("/proc/cpuinfo");
|
|
150
|
+
StringBuilder stringBuilder = new StringBuilder();
|
|
151
|
+
try {
|
|
152
|
+
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
|
|
153
|
+
String line;
|
|
154
|
+
while ((line = bufferedReader.readLine()) != null) {
|
|
155
|
+
stringBuilder.append(line);
|
|
156
|
+
}
|
|
157
|
+
bufferedReader.close();
|
|
158
|
+
return stringBuilder.toString();
|
|
159
|
+
} catch (IOException e) {
|
|
160
|
+
Log.w(NAME, "Couldn't read /proc/cpuinfo", e);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
protected static native long initContext(String modelPath);
|
|
166
|
+
protected static native int fullTranscribe(
|
|
167
|
+
long context,
|
|
168
|
+
float[] audio_data,
|
|
169
|
+
int n_threads,
|
|
170
|
+
int max_context,
|
|
171
|
+
int max_len,
|
|
172
|
+
int offset,
|
|
173
|
+
int duration,
|
|
174
|
+
int word_thold,
|
|
175
|
+
float temperature,
|
|
176
|
+
float temperature_inc,
|
|
177
|
+
int beam_size,
|
|
178
|
+
int best_of,
|
|
179
|
+
boolean speed_up,
|
|
180
|
+
boolean translate,
|
|
181
|
+
String language
|
|
182
|
+
);
|
|
183
|
+
protected static native int getTextSegmentCount(long context);
|
|
184
|
+
protected static native String getTextSegment(long context, int index);
|
|
185
|
+
protected static native void freeContext(long contextPtr);
|
|
186
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
LOCAL_PATH := $(call my-dir)
|
|
2
|
+
include $(CLEAR_VARS)
|
|
3
|
+
LOCAL_MODULE := libwhisper
|
|
4
|
+
include $(LOCAL_PATH)/Whisper.mk
|
|
5
|
+
include $(BUILD_SHARED_LIBRARY)
|
|
6
|
+
|
|
7
|
+
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
|
|
8
|
+
include $(CLEAR_VARS)
|
|
9
|
+
LOCAL_MODULE := libwhisper_vfpv4
|
|
10
|
+
include $(LOCAL_PATH)/Whisper.mk
|
|
11
|
+
# Allow building NEON FMA code.
|
|
12
|
+
# https://android.googlesource.com/platform/ndk/+/master/sources/android/cpufeatures/cpu-features.h
|
|
13
|
+
LOCAL_CFLAGS += -mfpu=neon-vfpv4
|
|
14
|
+
include $(BUILD_SHARED_LIBRARY)
|
|
15
|
+
endif
|
|
16
|
+
|
|
17
|
+
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
|
|
18
|
+
include $(CLEAR_VARS)
|
|
19
|
+
LOCAL_MODULE := libwhisper_v8fp16_va
|
|
20
|
+
include $(LOCAL_PATH)/Whisper.mk
|
|
21
|
+
# Allow building NEON FMA code.
|
|
22
|
+
# https://android.googlesource.com/platform/ndk/+/master/sources/android/cpufeatures/cpu-features.h
|
|
23
|
+
LOCAL_CFLAGS += -march=armv8.2-a+fp16
|
|
24
|
+
include $(BUILD_SHARED_LIBRARY)
|
|
25
|
+
endif
|
|
26
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
APP_STL := c++_static
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
WHISPER_LIB_DIR := $(LOCAL_PATH)/../../../../../cpp
|
|
2
|
+
LOCAL_LDLIBS := -landroid -llog
|
|
3
|
+
|
|
4
|
+
# Make the final output library smaller by only keeping the symbols referenced from the app.
|
|
5
|
+
ifneq ($(APP_OPTIM),debug)
|
|
6
|
+
LOCAL_CFLAGS += -O3
|
|
7
|
+
LOCAL_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
|
|
8
|
+
LOCAL_CFLAGS += -ffunction-sections -fdata-sections
|
|
9
|
+
LOCAL_LDFLAGS += -Wl,--gc-sections
|
|
10
|
+
LOCAL_LDFLAGS += -Wl,--exclude-libs,ALL
|
|
11
|
+
LOCAL_LDFLAGS += -flto
|
|
12
|
+
endif
|
|
13
|
+
|
|
14
|
+
LOCAL_CFLAGS += -DSTDC_HEADERS -std=c11 -I $(WHISPER_LIB_DIR)
|
|
15
|
+
LOCAL_CPPFLAGS += -std=c++11
|
|
16
|
+
LOCAL_SRC_FILES := $(WHISPER_LIB_DIR)/ggml.c \
|
|
17
|
+
$(WHISPER_LIB_DIR)/whisper.cpp \
|
|
18
|
+
$(LOCAL_PATH)/jni.c
|