Skip to content

Commit

Permalink
Add avp and fix proguard (#551)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErisMik committed Jul 20, 2023
1 parent 7a7e694 commit 074fd07
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 144 deletions.
5 changes: 2 additions & 3 deletions binding/android/Rhino/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ buildscript {
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.3"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'ai.picovoice:android-voice-processor:1.0.2'
}
}

Expand Down
9 changes: 6 additions & 3 deletions binding/android/Rhino/rhino/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apply plugin: 'com.android.library'

ext {
PUBLISH_GROUP_ID = 'ai.picovoice'
PUBLISH_VERSION = '2.2.0'
PUBLISH_VERSION = '2.2.2'
PUBLISH_ARTIFACT_ID = 'rhino-android'
}

Expand All @@ -15,7 +15,6 @@ android {
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}

Expand All @@ -25,14 +24,18 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

if (file("${rootDir}/publish-mavencentral.gradle").exists()) {
apply from: "${rootDir}/publish-mavencentral.gradle"
}

dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "ai.picovoice:android-voice-processor:1.0.2"
}

task copyLibs(type: Copy) {
Expand Down
2 changes: 1 addition & 1 deletion binding/android/Rhino/rhino/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
-keep class ai.picovoice.rhino.*Exception { <init>(...); }
-keep class ai.picovoice.rhino.RhinoInference
-keep class ai.picovoice.rhino.RhinoInference { <init>(...); }
2 changes: 1 addition & 1 deletion binding/android/Rhino/rhino/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
-keep class ai.picovoice.rhino.*Exception { <init>(...); }
-keep class ai.picovoice.rhino.RhinoInference
-keep class ai.picovoice.rhino.RhinoInference { <init>(...); }
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2018-2022 Picovoice Inc.
Copyright 2018-2023 Picovoice Inc.
You may not use this file except in compliance with the license. A copy of the license is
located in the "LICENSE" file accompanying this source.
Unless required by applicable law or agreed to in writing, software distributed under the
Expand All @@ -22,16 +22,24 @@
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;

import ai.picovoice.android.voiceprocessor.VoiceProcessor;
import ai.picovoice.android.voiceprocessor.VoiceProcessorErrorListener;
import ai.picovoice.android.voiceprocessor.VoiceProcessorException;
import ai.picovoice.android.voiceprocessor.VoiceProcessorFrameListener;

/**
* High-level Android binding for Rhino Speech-to-Intent engine. It handles recording audio from
* microphone, processes it in real-time using Rhino, and notifies the client when an intent is
* inferred from the spoken command. For detailed information about Rhino refer to ${@link Rhino}.
*/
public class RhinoManager {
private final Rhino rhino;
private final RhinoManagerCallback callback;
private final RhinoManagerErrorCallback errorCallback;
private final Handler callbackHandler = new Handler(Looper.getMainLooper());
private final VoiceProcessor voiceProcessor;
private final VoiceProcessorFrameListener vpFrameListener;
private final VoiceProcessorErrorListener vpErrorListener;

private boolean isFinalized;
private boolean isListening;

/**
* Private constructor.
Expand All @@ -41,93 +49,78 @@ public class RhinoManager {
* @param errorCallback A callback that reports errors encountered while processing audio.
*/
private RhinoManager(
Rhino rhino,
RhinoManagerCallback callback,
RhinoManagerErrorCallback errorCallback) {
final Rhino rhino,
final RhinoManagerCallback callback,
final RhinoManagerErrorCallback errorCallback) {

this.rhino = rhino;
this.callback = callback;
this.errorCallback = errorCallback;
}

/**
* Start recording audio from the microphone and infers the user's intent from the spoken
* command. Once the inference is finalized it will invoke the user provided callback and
* terminates recording audio.
*/
public void process() {
Executors.newSingleThreadExecutor().submit(new Callable<Void>() {
this.voiceProcessor = VoiceProcessor.getInstance();
this.vpFrameListener = new VoiceProcessorFrameListener() {
@Override
public Void call() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
final int minBufferSize = AudioRecord.getMinBufferSize(
rhino.getSampleRate(),
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
final int bufferSize = Math.max(rhino.getSampleRate() / 2, minBufferSize);

AudioRecord audioRecord = null;

short[] buffer = new short[rhino.getFrameLength()];

boolean isFinalized = false;
public void onFrame(short[] frame) {
try {
audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
rhino.getSampleRate(),
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize);
audioRecord.startRecording();

while (!isFinalized) {
if (audioRecord.read(buffer, 0, buffer.length) == buffer.length) {
isFinalized = rhino.process(buffer);
}
isFinalized = rhino.process(frame);
if (isFinalized) {
final RhinoInference inference = rhino.getInference();
callback.invoke(inference);
stop();
}
audioRecord.stop();
} catch (final Exception e) {
} catch (RhinoException e) {
if (errorCallback != null) {
callbackHandler.post(new Runnable() {
@Override
public void run() {
errorCallback.invoke(new RhinoException(e));
}
});
errorCallback.invoke(e);
} else {
Log.e("RhinoManager", e.toString());
}
} finally {
if (audioRecord != null) {
audioRecord.release();
}

if (isFinalized) {
try {
final RhinoInference inference = rhino.getInference();
callbackHandler.post(new Runnable() {
@Override
public void run() {
callback.invoke(inference);
}
});
} catch (final RhinoException e) {
if (errorCallback != null) {
callbackHandler.post(new Runnable() {
@Override
public void run() {
errorCallback.invoke(new RhinoException(e));
}
});
} else {
Log.e("RhinoManager", e.toString());
}
}
}
}
return null;
}
});
};
this.vpErrorListener = new VoiceProcessorErrorListener() {
@Override
public void onError(VoiceProcessorException error) {
if (errorCallback != null) {
errorCallback.invoke(new RhinoException(error));
} else {
Log.e("RhinoManager", error.toString());
}
}
};
}

/**
* Stops recording audio.
*/
private void stop() throws RhinoException {
voiceProcessor.removeErrorListener(vpErrorListener);
voiceProcessor.removeFrameListener(vpFrameListener);
if (voiceProcessor.getNumFrameListeners() == 0) {
try {
voiceProcessor.stop();
} catch (VoiceProcessorException e) {
throw new RhinoException(e);
}
}
isListening = false;
}

/**
* Start recording audio from the microphone and infers the user's intent from the spoken
* command. Once the inference is finalized it will invoke the user provided callback and
* terminates recording audio.
*/
public void process() throws RhinoException {
if (isListening) {
return;
}

this.voiceProcessor.addFrameListener(vpFrameListener);
this.voiceProcessor.addErrorListener(vpErrorListener);

try {
voiceProcessor.start(rhino.getFrameLength(), rhino.getSampleRate());
} catch (VoiceProcessorException e) {
throw new RhinoException(e);
}
isListening = true;
}

/**
Expand Down Expand Up @@ -211,7 +204,9 @@ public RhinoManager.Builder setErrorCallback(RhinoManagerErrorCallback errorCall
* @return A RhinoManager instance
* @throws RhinoException if there is an error while initializing Rhino.
*/
public RhinoManager build(Context context, RhinoManagerCallback callback) throws RhinoException {
public RhinoManager build(
Context context,
RhinoManagerCallback callback) throws RhinoException {
Rhino rhino = new Rhino.Builder()
.setAccessKey(accessKey)
.setModelPath(modelPath)
Expand Down
2 changes: 1 addition & 1 deletion demo/android/Activity/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.android.tools.build:gradle:7.2.2'
}
}

Expand Down
2 changes: 1 addition & 1 deletion demo/android/Activity/rhino-activity-demo-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.code.gson:gson:2.10'
implementation 'ai.picovoice:rhino-android:2.2.0'
implementation 'ai.picovoice:rhino-android:2.2.2'

// Espresso UI Testing
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
Expand Down
Loading

0 comments on commit 074fd07

Please sign in to comment.