Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40b8ef99 authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "SoundTrigger API update." into lmp-dev

parents af8a8f3a d3b82233
Loading
Loading
Loading
Loading
+246 −29
Original line number Diff line number Diff line
@@ -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,
        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,22 +934,126 @@ 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.
@@ -890,6 +1095,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
         */
+15 −4
Original line number Diff line number Diff line
@@ -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,11 +136,8 @@ public class SoundTriggerModule {
            if (handler != null) {
                looper = handler.getLooper();
            } else {
                looper = Looper.myLooper();
                if (looper == null) {
                looper = Looper.getMainLooper();
            }
            }

            // construct the event handler with this looper
            if (looper != null) {
@@ -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();
+1 −1
Original line number Diff line number Diff line
@@ -426,7 +426,7 @@ public class AlwaysOnHotwordDetector {
        KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1];
        // TODO: Do we need to do something about the confidence level here?
        recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.id,
                mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]);
                mKeyphraseMetadata.recognitionModeFlags, 0, new ConfidenceLevel[0]);
        boolean captureTriggerAudio =
                (recognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0;
        boolean allowMultipleTriggers =
+118 −18

File changed.

Preview size limit exceeded, changes collapsed.

+14 −0
Original line number Diff line number Diff line
@@ -249,6 +249,20 @@ public class AudioFormat {
    private AudioFormat(int ignoredArgument) {
    }

    /**
     * Constructor used by the JNI
     */
    // Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this
    // constructor
    private AudioFormat(int encoding, int sampleRate, int channelMask) {
        mEncoding = encoding;
        mSampleRate = sampleRate;
        mChannelMask = channelMask;
        mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING |
                AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE |
                AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK;
    }

    /** @hide */
    public final static int AUDIO_FORMAT_HAS_PROPERTY_NONE = 0x0;
    /** @hide */
Loading