mirror of
https://github.com/andreytkachenko/LocalSTT.git
synced 2024-11-22 01:16:23 +04:00
Update to latest Vosk version
This commit is contained in:
parent
a0fe247c46
commit
e90a92e366
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ build
|
||||
.classpath
|
||||
|
||||
.vscode
|
||||
local.properties
|
@ -2,8 +2,9 @@ apply plugin: 'com.android.application'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "https://dl.bintray.com/alphacep/vosk"
|
||||
url 'https://alphacephei.com/maven/'
|
||||
}
|
||||
maven {
|
||||
url "https://jitpack.io"
|
||||
@ -31,9 +32,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.alphacep:vosk-android:0.3.15'
|
||||
implementation 'com.alphacephei:vosk-android:0.3.30'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'net.java.dev.jna:jna:5.8.0@aar'
|
||||
implementation 'com.google.code.gson:gson:2.8.7'
|
||||
implementation 'org.mozilla.deepspeech:libdeepspeech:0.8.2'
|
||||
implementation 'com.github.gkonovalov:android-vad:1.0.0'
|
||||
}
|
||||
|
261
app/src/main/java/cat/oreilly/localstt/Assets.java
Normal file
261
app/src/main/java/cat/oreilly/localstt/Assets.java
Normal file
@ -0,0 +1,261 @@
|
||||
// Copyright 2019 Alpha Cephei Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cat.oreilly.localstt;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
|
||||
/**
|
||||
* Provides utility methods to keep asset files to external storage to allow
|
||||
* further JNI code access assets from a filesystem.
|
||||
*
|
||||
* There must be special file {@value #ASSET_LIST_NAME} among the application
|
||||
* assets containing relative paths of assets to synchronize. If the
|
||||
* corresponding path does not exist on the external storage it is copied. If
|
||||
* the path exists checksums are compared and the asset is copied only if there
|
||||
* is a mismatch. Checksum is stored in a separate asset with the name that
|
||||
* consists of the original name and a suffix that depends on the checksum
|
||||
* algorithm (e.g. MD5). Checksum files are copied along with the corresponding
|
||||
* asset files.
|
||||
*
|
||||
* @author Alexander Solovets
|
||||
*/
|
||||
public class Assets {
|
||||
|
||||
protected static final String TAG = Assets.class.getSimpleName();
|
||||
|
||||
public static final String ASSET_LIST_NAME = "assets.lst";
|
||||
public static final String SYNC_DIR = "sync";
|
||||
public static final String HASH_EXT = ".md5";
|
||||
|
||||
private final AssetManager assetManager;
|
||||
private final File externalDir;
|
||||
|
||||
/**
|
||||
* Creates new instance for asset synchronization
|
||||
*
|
||||
* @param context
|
||||
* application context
|
||||
*
|
||||
* @throws IOException
|
||||
* if the directory does not exist
|
||||
*
|
||||
* @see android.content.Context#getExternalFilesDir
|
||||
* @see android.os.Environment#getExternalStorageState
|
||||
*/
|
||||
public Assets(Context context) throws IOException {
|
||||
File appDir = context.getExternalFilesDir(null);
|
||||
if (null == appDir)
|
||||
throw new IOException("cannot get external files dir, "
|
||||
+ "external storage state is " + Environment.getExternalStorageState());
|
||||
externalDir = new File(appDir, SYNC_DIR);
|
||||
assetManager = context.getAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new instance with specified destination for assets
|
||||
*
|
||||
* @param context
|
||||
* application context to retrieve the assets
|
||||
* @param path
|
||||
* path to sync the files
|
||||
*/
|
||||
public Assets(Context context, String dest) {
|
||||
externalDir = new File(dest);
|
||||
assetManager = context.getAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns destination path on external storage where assets are copied.
|
||||
*
|
||||
* @return path to application directory or null if it does not exists
|
||||
*/
|
||||
public File getExternalDir() {
|
||||
return externalDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of asset paths to the files checksums.
|
||||
*
|
||||
* @return path to the root of resources directory on external storage
|
||||
* @throws IOException
|
||||
* if an I/O error occurs or "assets.lst" is missing
|
||||
*/
|
||||
public Map<String, String> getItems() throws IOException {
|
||||
Map<String, String> items = new HashMap<String, String>();
|
||||
for (String path : readLines(openAsset(ASSET_LIST_NAME))) {
|
||||
Reader reader = new InputStreamReader(openAsset(path + HASH_EXT));
|
||||
items.put(path, new BufferedReader(reader).readLine());
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to hash mappings for the previously copied files. This
|
||||
* method can be used to find out assets which must be updated.
|
||||
*/
|
||||
public Map<String, String> getExternalItems() {
|
||||
try {
|
||||
Map<String, String> items = new HashMap<String, String>();
|
||||
File assetFile = new File(externalDir, ASSET_LIST_NAME);
|
||||
for (String line : readLines(new FileInputStream(assetFile))) {
|
||||
String[] fields = line.split(" ");
|
||||
items.put(fields[0], fields[1]);
|
||||
}
|
||||
return items;
|
||||
} catch (IOException e) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In case you want to create more smart sync implementation, this method
|
||||
* returns the list of items which must be synchronized.
|
||||
*/
|
||||
public Collection<String> getItemsToCopy(String path) throws IOException {
|
||||
Collection<String> items = new ArrayList<String>();
|
||||
Queue<String> queue = new ArrayDeque<String>();
|
||||
queue.offer(path);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
path = queue.poll();
|
||||
String[] list = assetManager.list(path);
|
||||
for (String nested : list)
|
||||
queue.offer(nested);
|
||||
|
||||
if (list.length == 0)
|
||||
items.add(path);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<String> readLines(InputStream source) throws IOException {
|
||||
List<String> lines = new ArrayList<String>();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(source));
|
||||
String line;
|
||||
while (null != (line = br.readLine()))
|
||||
lines.add(line);
|
||||
return lines;
|
||||
}
|
||||
|
||||
private InputStream openAsset(String asset) throws IOException {
|
||||
return assetManager.open(new File(SYNC_DIR, asset).getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the list of synchronized items. The list is stored as a two-column
|
||||
* space-separated list of items in a text file. The file is located at the
|
||||
* root of synchronization directory in the external storage.
|
||||
*
|
||||
* @param items
|
||||
* the items
|
||||
* @throws IOException
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
public void updateItemList(Map<String, String> items) throws IOException {
|
||||
File assetListFile = new File(externalDir, ASSET_LIST_NAME);
|
||||
PrintWriter pw = new PrintWriter(new FileOutputStream(assetListFile));
|
||||
for (Map.Entry<String, String> entry : items.entrySet())
|
||||
pw.format("%s %s\n", entry.getKey(), entry.getValue());
|
||||
pw.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies raw asset resource to external storage of the device.
|
||||
*
|
||||
* @param path
|
||||
* path of the asset to copy
|
||||
* @throws IOException
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
public File copy(String asset) throws IOException {
|
||||
InputStream source = openAsset(asset);
|
||||
File destinationFile = new File(externalDir, asset);
|
||||
destinationFile.getParentFile().mkdirs();
|
||||
OutputStream destination = new FileOutputStream(destinationFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
int nread;
|
||||
|
||||
while ((nread = source.read(buffer)) != -1) {
|
||||
if (nread == 0) {
|
||||
nread = source.read();
|
||||
if (nread < 0)
|
||||
break;
|
||||
destination.write(nread);
|
||||
continue;
|
||||
}
|
||||
destination.write(buffer, 0, nread);
|
||||
}
|
||||
destination.close();
|
||||
return destinationFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the sync of assets in the application and on the external
|
||||
* storage
|
||||
*
|
||||
* @return The folder on external storage with data
|
||||
* @throws IOException
|
||||
*/
|
||||
public File syncAssets() throws IOException {
|
||||
Collection<String> newItems = new ArrayList<String>();
|
||||
Collection<String> unusedItems = new ArrayList<String>();
|
||||
Map<String, String> items = getItems();
|
||||
Map<String, String> externalItems = getExternalItems();
|
||||
|
||||
for (String path : items.keySet()) {
|
||||
if (!items.get(path).equals(externalItems.get(path))
|
||||
|| !(new File(externalDir, path).exists()))
|
||||
newItems.add(path);
|
||||
}
|
||||
|
||||
unusedItems.addAll(externalItems.keySet());
|
||||
unusedItems.removeAll(items.keySet());
|
||||
|
||||
for (String path : newItems) {
|
||||
File file = copy(path);
|
||||
}
|
||||
|
||||
for (String path : unusedItems) {
|
||||
File file = new File(externalDir, path);
|
||||
file.delete();
|
||||
}
|
||||
|
||||
updateItemList(items);
|
||||
return externalDir;
|
||||
}
|
||||
|
||||
}
|
@ -15,23 +15,17 @@
|
||||
package cat.oreilly.localstt;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.speech.RecognitionService;
|
||||
import android.util.Log;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
|
||||
import org.kaldi.Assets;
|
||||
import org.kaldi.RecognitionListener;
|
||||
import org.vosk.android.RecognitionListener;
|
||||
import org.mozilla.deepspeech.libdeepspeech.DeepSpeechModel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -170,6 +164,14 @@ public class DeepSpeechRecognitionService extends RecognitionService implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinalResult(String hypothesis) {
|
||||
if (hypothesis != null) {
|
||||
Log.i(TAG, hypothesis);
|
||||
results(createResultsBundle(hypothesis), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartialResult(String hypothesis) {
|
||||
if (hypothesis != null) {
|
||||
|
@ -16,9 +16,6 @@
|
||||
|
||||
package cat.oreilly.localstt;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
@ -30,7 +27,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kaldi.RecognitionListener;
|
||||
import org.vosk.android.RecognitionListener;
|
||||
import org.mozilla.deepspeech.libdeepspeech.DeepSpeechModel;
|
||||
import org.mozilla.deepspeech.libdeepspeech.DeepSpeechStreamingState;
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
package cat.oreilly.localstt;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@ -26,12 +25,12 @@ import android.speech.RecognitionService;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.kaldi.Assets;
|
||||
import org.kaldi.KaldiRecognizer;
|
||||
import org.kaldi.Model;
|
||||
import org.kaldi.RecognitionListener;
|
||||
import org.kaldi.SpeechService;
|
||||
import org.kaldi.Vosk;
|
||||
import org.vosk.Recognizer;
|
||||
import org.vosk.Model;
|
||||
import org.vosk.android.RecognitionListener;
|
||||
import org.vosk.android.SpeechService;
|
||||
import org.vosk.LibVosk;
|
||||
import org.vosk.LogLevel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
@ -44,7 +43,7 @@ public class VoskRecognitionService extends RecognitionService implements Recogn
|
||||
private final static String TAG = VoskRecognitionService.class.getSimpleName();
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
private KaldiRecognizer recognizer;
|
||||
private Recognizer recognizer;
|
||||
private SpeechService speechService;
|
||||
private Model model;
|
||||
|
||||
@ -77,7 +76,7 @@ public class VoskRecognitionService extends RecognitionService implements Recogn
|
||||
if (model == null) {
|
||||
Assets assets = new Assets(VoskRecognitionService.this);
|
||||
File assetDir = assets.syncAssets();
|
||||
Vosk.SetLogLevel(0);
|
||||
LibVosk.setLogLevel(LogLevel.INFO);
|
||||
|
||||
Log.i(TAG, "Loading model");
|
||||
model = new Model(assetDir.toString() + "/vosk-catala");
|
||||
@ -111,23 +110,22 @@ public class VoskRecognitionService extends RecognitionService implements Recogn
|
||||
}
|
||||
}
|
||||
|
||||
private void setupRecognizer() throws IOException {
|
||||
private void setupRecognizer() {
|
||||
try {
|
||||
if (recognizer == null) {
|
||||
Log.i(TAG, "Creating recognizer");
|
||||
|
||||
recognizer = new KaldiRecognizer(model, 16000.0f);
|
||||
recognizer = new Recognizer(model, 16000.0f);
|
||||
}
|
||||
|
||||
if (speechService == null) {
|
||||
Log.i(TAG, "Creating speechService");
|
||||
|
||||
speechService = new SpeechService(recognizer, 16000.0f);
|
||||
speechService.addListener(this);
|
||||
} else {
|
||||
speechService.cancel();
|
||||
}
|
||||
speechService.startListening();
|
||||
speechService.startListening(this);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
@ -171,7 +169,9 @@ public class VoskRecognitionService extends RecognitionService implements Recogn
|
||||
}
|
||||
|
||||
private void error(int errorCode) {
|
||||
if (speechService != null) {
|
||||
speechService.cancel();
|
||||
}
|
||||
try {
|
||||
mCallback.error(errorCode);
|
||||
} catch (RemoteException e) {
|
||||
@ -190,6 +190,17 @@ public class VoskRecognitionService extends RecognitionService implements Recogn
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinalResult(String hypothesis) {
|
||||
if (hypothesis != null) {
|
||||
Log.i(TAG, hypothesis);
|
||||
Gson gson = new Gson();
|
||||
Map<String, String> map = gson.fromJson(hypothesis, Map.class);
|
||||
String text = map.get("text");
|
||||
results(createResultsBundle(text), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartialResult(String hypothesis) {
|
||||
if (hypothesis != null) {
|
||||
@ -210,6 +221,6 @@ public class VoskRecognitionService extends RecognitionService implements Recogn
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
speechService.cancel();
|
||||
speechService.startListening();
|
||||
speechService.startListening(this);
|
||||
}
|
||||
}
|
||||
|
@ -2,17 +2,17 @@ buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||
classpath 'com.android.tools.build:gradle:4.2.0'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
android.enableD8=true
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
|
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,6 @@
|
||||
#Tue Aug 03 00:12:16 CEST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
Loading…
Reference in New Issue
Block a user