summaryrefslogtreecommitdiffstats
path: root/core/java/android/hardware/soundtrigger
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2014-07-30 08:57:39 -0700
committerEric Laurent <elaurent@google.com>2014-08-04 16:35:27 -0700
commitd3b8223377b8046280e4c09e728edc600171f941 (patch)
tree88d3e6bb021548e90b415791c3dbddc8870be60c /core/java/android/hardware/soundtrigger
parent2265c8f70088b88b34ee9e315f7ae5519de1cdba (diff)
downloadframeworks_base-d3b8223377b8046280e4c09e728edc600171f941.zip
frameworks_base-d3b8223377b8046280e4c09e728edc600171f941.tar.gz
frameworks_base-d3b8223377b8046280e4c09e728edc600171f941.tar.bz2
SoundTrigger API update.
Add sound model update callback. Add native service state change callback. Add vendor UUID in sound model description. Add coarse confidence level in recognition event. Add capture format in recognition event. Bug: 12378680. Change-Id: Id63437819ec7b9a4a69e1ff6185b747e20cad95e
Diffstat (limited to 'core/java/android/hardware/soundtrigger')
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java275
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTriggerModule.java19
2 files changed, 261 insertions, 33 deletions
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index adee740..7a49eb5 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,6 +16,7 @@
package android.hardware.soundtrigger;
+import android.media.AudioFormat;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
@@ -86,11 +87,15 @@ public class SoundTrigger {
/** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
public final int powerConsumptionMw;
+ /** Returns the trigger (key phrase) capture in the binary data of the
+ * recognition callback event */
+ public final boolean returnsTriggerInEvent;
+
ModuleProperties(int id, String implementor, String description,
String uuid, int version, int maxSoundModels, int maxKeyphrases,
int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
int maxBufferMs, boolean supportsConcurrentCapture,
- int powerConsumptionMw) {
+ int powerConsumptionMw, boolean returnsTriggerInEvent) {
this.id = id;
this.implementor = implementor;
this.description = description;
@@ -104,6 +109,7 @@ public class SoundTrigger {
this.maxBufferMs = maxBufferMs;
this.supportsConcurrentCapture = supportsConcurrentCapture;
this.powerConsumptionMw = powerConsumptionMw;
+ this.returnsTriggerInEvent = returnsTriggerInEvent;
}
public static final Parcelable.Creator<ModuleProperties> CREATOR
@@ -131,10 +137,11 @@ public class SoundTrigger {
int maxBufferMs = in.readInt();
boolean supportsConcurrentCapture = in.readByte() == 1;
int powerConsumptionMw = in.readInt();
+ boolean returnsTriggerInEvent = in.readByte() == 1;
return new ModuleProperties(id, implementor, description, uuid, version,
maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
- powerConsumptionMw);
+ powerConsumptionMw, returnsTriggerInEvent);
}
@Override
@@ -152,6 +159,7 @@ public class SoundTrigger {
dest.writeInt(maxBufferMs);
dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0));
dest.writeInt(powerConsumptionMw);
+ dest.writeByte((byte) (returnsTriggerInEvent ? 1 : 0));
}
@Override
@@ -167,7 +175,8 @@ public class SoundTrigger {
+ maxUsers + ", recognitionModes=" + recognitionModes
+ ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
+ maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture
- + ", powerConsumptionMw=" + powerConsumptionMw + "]";
+ + ", powerConsumptionMw=" + powerConsumptionMw
+ + ", returnsTriggerInEvent=" + returnsTriggerInEvent + "]";
}
}
@@ -190,11 +199,15 @@ public class SoundTrigger {
/** Sound model type (e.g. TYPE_KEYPHRASE); */
public final int type;
+ /** Unique sound model vendor identifier */
+ public final UUID vendorUuid;
+
/** Opaque data. For use by vendor implementation and enrollment application */
public final byte[] data;
- public SoundModel(UUID uuid, int type, byte[] data) {
+ public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) {
this.uuid = uuid;
+ this.vendorUuid = vendorUuid;
this.type = type;
this.data = data;
}
@@ -329,8 +342,9 @@ public class SoundTrigger {
/** Key phrases in this sound model */
public final Keyphrase[] keyphrases; // keyword phrases in model
- public KeyphraseSoundModel(UUID id, byte[] data, Keyphrase[] keyphrases) {
- super(id, TYPE_KEYPHRASE, data);
+ public KeyphraseSoundModel(
+ UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) {
+ super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
this.keyphrases = keyphrases;
}
@@ -347,9 +361,14 @@ public class SoundTrigger {
private static KeyphraseSoundModel fromParcel(Parcel in) {
UUID uuid = UUID.fromString(in.readString());
+ UUID vendorUuid = null;
+ int length = in.readInt();
+ if (length >= 0) {
+ vendorUuid = UUID.fromString(in.readString());
+ }
byte[] data = in.readBlob();
Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
- return new KeyphraseSoundModel(uuid, data, keyphrases);
+ return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases);
}
@Override
@@ -360,14 +379,21 @@ public class SoundTrigger {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
+ if (vendorUuid == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(vendorUuid.toString().length());
+ dest.writeString(vendorUuid.toString());
+ }
dest.writeBlob(data);
dest.writeTypedArray(keyphrases, flags);
}
@Override
public String toString() {
- return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases) + ", uuid="
- + uuid + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
+ return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases)
+ + ", uuid=" + uuid + ", vendorUuid=" + vendorUuid
+ + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
}
}
@@ -411,18 +437,26 @@ public class SoundTrigger {
public final int captureDelayMs;
/** Duration in ms of audio captured before the start of the trigger. 0 if none. */
public final int capturePreambleMs;
+ /** True if the trigger (key phrase capture is present in binary data */
+ public final boolean triggerInData;
+ /** Audio format of either the trigger in event data or to use for capture of the
+ * rest of the utterance */
+ public AudioFormat captureFormat;
/** Opaque data for use by system applications who know about voice engine internals,
* typically during enrollment. */
public final byte[] data;
public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
- int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data) {
+ int captureSession, int captureDelayMs, int capturePreambleMs,
+ boolean triggerInData, AudioFormat captureFormat, byte[] data) {
this.status = status;
this.soundModelHandle = soundModelHandle;
this.captureAvailable = captureAvailable;
this.captureSession = captureSession;
this.captureDelayMs = captureDelayMs;
this.capturePreambleMs = capturePreambleMs;
+ this.triggerInData = triggerInData;
+ this.captureFormat = captureFormat;
this.data = data;
}
@@ -444,9 +478,21 @@ public class SoundTrigger {
int captureSession = in.readInt();
int captureDelayMs = in.readInt();
int capturePreambleMs = in.readInt();
+ boolean triggerInData = in.readByte() == 1;
+ AudioFormat captureFormat = null;
+ if (triggerInData) {
+ int sampleRate = in.readInt();
+ int encoding = in.readInt();
+ int channelMask = in.readInt();
+ captureFormat = (new AudioFormat.Builder())
+ .setChannelMask(channelMask)
+ .setEncoding(encoding)
+ .setSampleRate(sampleRate)
+ .build();
+ }
byte[] data = in.readBlob();
return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
- captureDelayMs, capturePreambleMs, data);
+ captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
}
@Override
@@ -462,6 +508,14 @@ public class SoundTrigger {
dest.writeInt(captureSession);
dest.writeInt(captureDelayMs);
dest.writeInt(capturePreambleMs);
+ if (triggerInData && (captureFormat != null)) {
+ dest.writeByte((byte)1);
+ dest.writeInt(captureFormat.getSampleRate());
+ dest.writeInt(captureFormat.getEncoding());
+ dest.writeInt(captureFormat.getChannelMask());
+ } else {
+ dest.writeByte((byte)0);
+ }
dest.writeBlob(data);
}
@@ -473,6 +527,12 @@ public class SoundTrigger {
result = prime * result + captureDelayMs;
result = prime * result + capturePreambleMs;
result = prime * result + captureSession;
+ result = prime * result + (triggerInData ? 1231 : 1237);
+ if (captureFormat != null) {
+ result = prime * result + captureFormat.getSampleRate();
+ result = prime * result + captureFormat.getEncoding();
+ result = prime * result + captureFormat.getChannelMask();
+ }
result = prime * result + Arrays.hashCode(data);
result = prime * result + soundModelHandle;
result = prime * result + status;
@@ -502,6 +562,14 @@ public class SoundTrigger {
return false;
if (status != other.status)
return false;
+ if (triggerInData != other.triggerInData)
+ return false;
+ if (captureFormat.getSampleRate() != other.captureFormat.getSampleRate())
+ return false;
+ if (captureFormat.getEncoding() != other.captureFormat.getEncoding())
+ return false;
+ if (captureFormat.getChannelMask() != other.captureFormat.getChannelMask())
+ return false;
return true;
}
@@ -511,6 +579,13 @@ public class SoundTrigger {
+ ", captureAvailable=" + captureAvailable + ", captureSession="
+ captureSession + ", captureDelayMs=" + captureDelayMs
+ ", capturePreambleMs=" + capturePreambleMs
+ + ", triggerInData=" + triggerInData
+ + ((captureFormat == null) ? "" :
+ (", sampleRate=" + captureFormat.getSampleRate()))
+ + ((captureFormat == null) ? "" :
+ (", encoding=" + captureFormat.getEncoding()))
+ + ((captureFormat == null) ? "" :
+ (", channelMask=" + captureFormat.getChannelMask()))
+ ", data=" + (data == null ? 0 : data.length) + "]";
}
}
@@ -673,14 +748,19 @@ public class SoundTrigger {
/** Recognition modes matched for this event */
public final int recognitionModes;
+ /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
+ * is not performed */
+ public final int coarseConfidenceLevel;
+
/** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
* be recognized (RecognitionConfig) */
public final ConfidenceLevel[] confidenceLevels;
- public KeyphraseRecognitionExtra(int id, int recognitionModes,
- ConfidenceLevel[] confidenceLevels) {
+ public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
+ ConfidenceLevel[] confidenceLevels) {
this.id = id;
this.recognitionModes = recognitionModes;
+ this.coarseConfidenceLevel = coarseConfidenceLevel;
this.confidenceLevels = confidenceLevels;
}
@@ -698,14 +778,17 @@ public class SoundTrigger {
private static KeyphraseRecognitionExtra fromParcel(Parcel in) {
int id = in.readInt();
int recognitionModes = in.readInt();
+ int coarseConfidenceLevel = in.readInt();
ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR);
- return new KeyphraseRecognitionExtra(id, recognitionModes, confidenceLevels);
+ return new KeyphraseRecognitionExtra(id, recognitionModes, coarseConfidenceLevel,
+ confidenceLevels);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(recognitionModes);
+ dest.writeInt(coarseConfidenceLevel);
dest.writeTypedArray(confidenceLevels, flags);
}
@@ -721,6 +804,7 @@ public class SoundTrigger {
result = prime * result + Arrays.hashCode(confidenceLevels);
result = prime * result + id;
result = prime * result + recognitionModes;
+ result = prime * result + coarseConfidenceLevel;
return result;
}
@@ -739,12 +823,15 @@ public class SoundTrigger {
return false;
if (recognitionModes != other.recognitionModes)
return false;
+ if (coarseConfidenceLevel != other.coarseConfidenceLevel)
+ return false;
return true;
}
@Override
public String toString() {
return "KeyphraseRecognitionExtra [id=" + id + ", recognitionModes=" + recognitionModes
+ + ", coarseConfidenceLevel=" + coarseConfidenceLevel
+ ", confidenceLevels=" + Arrays.toString(confidenceLevels) + "]";
}
}
@@ -756,15 +843,12 @@ public class SoundTrigger {
/** Indicates if the key phrase is present in the buffered audio available for capture */
public final KeyphraseRecognitionExtra[] keyphraseExtras;
- /** Additional data available for each recognized key phrases in the model */
- public final boolean keyphraseInCapture;
-
public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
- int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data,
- boolean keyphraseInCapture, KeyphraseRecognitionExtra[] keyphraseExtras) {
+ int captureSession, int captureDelayMs, int capturePreambleMs,
+ boolean triggerInData, AudioFormat captureFormat, byte[] data,
+ KeyphraseRecognitionExtra[] keyphraseExtras) {
super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
- capturePreambleMs, data);
- this.keyphraseInCapture = keyphraseInCapture;
+ capturePreambleMs, triggerInData, captureFormat, data);
this.keyphraseExtras = keyphraseExtras;
}
@@ -786,13 +870,24 @@ public class SoundTrigger {
int captureSession = in.readInt();
int captureDelayMs = in.readInt();
int capturePreambleMs = in.readInt();
+ boolean triggerInData = in.readByte() == 1;
+ AudioFormat captureFormat = null;
+ if (triggerInData) {
+ int sampleRate = in.readInt();
+ int encoding = in.readInt();
+ int channelMask = in.readInt();
+ captureFormat = (new AudioFormat.Builder())
+ .setChannelMask(channelMask)
+ .setEncoding(encoding)
+ .setSampleRate(sampleRate)
+ .build();
+ }
byte[] data = in.readBlob();
- boolean keyphraseInCapture = in.readByte() == 1;
KeyphraseRecognitionExtra[] keyphraseExtras =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
- captureSession, captureDelayMs, capturePreambleMs, data, keyphraseInCapture,
- keyphraseExtras);
+ captureSession, captureDelayMs, capturePreambleMs, triggerInData,
+ captureFormat, data, keyphraseExtras);
}
@Override
@@ -803,8 +898,15 @@ public class SoundTrigger {
dest.writeInt(captureSession);
dest.writeInt(captureDelayMs);
dest.writeInt(capturePreambleMs);
+ if (triggerInData && (captureFormat != null)) {
+ dest.writeByte((byte)1);
+ dest.writeInt(captureFormat.getSampleRate());
+ dest.writeInt(captureFormat.getEncoding());
+ dest.writeInt(captureFormat.getChannelMask());
+ } else {
+ dest.writeByte((byte)0);
+ }
dest.writeBlob(data);
- dest.writeByte((byte) (keyphraseInCapture ? 1 : 0));
dest.writeTypedArray(keyphraseExtras, flags);
}
@@ -818,7 +920,6 @@ public class SoundTrigger {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Arrays.hashCode(keyphraseExtras);
- result = prime * result + (keyphraseInCapture ? 1231 : 1237);
return result;
}
@@ -833,23 +934,127 @@ public class SoundTrigger {
KeyphraseRecognitionEvent other = (KeyphraseRecognitionEvent) obj;
if (!Arrays.equals(keyphraseExtras, other.keyphraseExtras))
return false;
- if (keyphraseInCapture != other.keyphraseInCapture)
- return false;
return true;
}
@Override
public String toString() {
return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras)
- + ", keyphraseInCapture=" + keyphraseInCapture + ", status=" + status
+ + ", status=" + status
+ ", soundModelHandle=" + soundModelHandle + ", captureAvailable="
+ captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs="
+ captureDelayMs + ", capturePreambleMs=" + capturePreambleMs
+ + ", triggerInData=" + triggerInData
+ + ((captureFormat == null) ? "" :
+ (", sampleRate=" + captureFormat.getSampleRate()))
+ + ((captureFormat == null) ? "" :
+ (", encoding=" + captureFormat.getEncoding()))
+ + ((captureFormat == null) ? "" :
+ (", channelMask=" + captureFormat.getChannelMask()))
+ ", data=" + (data == null ? 0 : data.length) + "]";
}
}
/**
+ * Status codes for {@link SoundModelEvent}
+ */
+ /** Sound Model was updated */
+ public static final int SOUNDMODEL_STATUS_UPDATED = 0;
+
+ /**
+ * A SoundModelEvent is provided by the
+ * {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
+ * callback when a sound model has been updated by the implementation
+ */
+ public static class SoundModelEvent implements Parcelable {
+ /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
+ public final int status;
+ /** The updated sound model handle */
+ public final int soundModelHandle;
+ /** New sound model data */
+ public final byte[] data;
+
+ SoundModelEvent(int status, int soundModelHandle, byte[] data) {
+ this.status = status;
+ this.soundModelHandle = soundModelHandle;
+ this.data = data;
+ }
+
+ public static final Parcelable.Creator<SoundModelEvent> CREATOR
+ = new Parcelable.Creator<SoundModelEvent>() {
+ public SoundModelEvent createFromParcel(Parcel in) {
+ return SoundModelEvent.fromParcel(in);
+ }
+
+ public SoundModelEvent[] newArray(int size) {
+ return new SoundModelEvent[size];
+ }
+ };
+
+ private static SoundModelEvent fromParcel(Parcel in) {
+ int status = in.readInt();
+ int soundModelHandle = in.readInt();
+ byte[] data = in.readBlob();
+ return new SoundModelEvent(status, soundModelHandle, data);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(status);
+ dest.writeInt(soundModelHandle);
+ dest.writeBlob(data);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(data);
+ result = prime * result + soundModelHandle;
+ result = prime * result + status;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SoundModelEvent other = (SoundModelEvent) obj;
+ if (!Arrays.equals(data, other.data))
+ return false;
+ if (soundModelHandle != other.soundModelHandle)
+ return false;
+ if (status != other.status)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
+ + ", data=" + (data == null ? 0 : data.length) + "]";
+ }
+ }
+
+ /**
+ * Native service state. {@link StatusListener#onServiceStateChange(int)}
+ */
+ // Keep in sync with system/core/include/system/sound_trigger.h
+ /** Sound trigger service is enabled */
+ public static final int SERVICE_STATE_ENABLED = 0;
+ /** Sound trigger service is disabled */
+ public static final int SERVICE_STATE_DISABLED = 1;
+
+ /**
* Returns a list of descriptors for all harware modules loaded.
* @param modules A ModuleProperties array where the list will be returned.
* @return - {@link #STATUS_OK} in case of success
@@ -891,6 +1096,18 @@ public class SoundTrigger {
public abstract void onRecognition(RecognitionEvent event);
/**
+ * Called when a sound model has been updated
+ */
+ public abstract void onSoundModelUpdate(SoundModelEvent event);
+
+ /**
+ * Called when the sound trigger native service state changes.
+ * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
+ * {@link SoundTrigger#SERVICE_STATE_DISABLED}
+ */
+ public abstract void onServiceStateChange(int state);
+
+ /**
* Called when the sound trigger native service dies
*/
public abstract void onServiceDied();
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 4a54fd8..1a8723d 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -36,8 +36,11 @@ public class SoundTriggerModule {
private int mId;
private NativeEventHandlerDelegate mEventHandlerDelegate;
+ // to be kept in sync with core/jni/android_hardware_SoundTrigger.cpp
private static final int EVENT_RECOGNITION = 1;
private static final int EVENT_SERVICE_DIED = 2;
+ private static final int EVENT_SOUNDMODEL = 3;
+ private static final int EVENT_SERVICE_STATE_CHANGE = 4;
SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) {
mId = moduleId;
@@ -133,10 +136,7 @@ public class SoundTriggerModule {
if (handler != null) {
looper = handler.getLooper();
} else {
- looper = Looper.myLooper();
- if (looper == null) {
- looper = Looper.getMainLooper();
- }
+ looper = Looper.getMainLooper();
}
// construct the event handler with this looper
@@ -152,6 +152,17 @@ public class SoundTriggerModule {
(SoundTrigger.RecognitionEvent)msg.obj);
}
break;
+ case EVENT_SOUNDMODEL:
+ if (listener != null) {
+ listener.onSoundModelUpdate(
+ (SoundTrigger.SoundModelEvent)msg.obj);
+ }
+ break;
+ case EVENT_SERVICE_STATE_CHANGE:
+ if (listener != null) {
+ listener.onServiceStateChange(msg.arg1);
+ }
+ break;
case EVENT_SERVICE_DIED:
if (listener != null) {
listener.onServiceDied();