diff options
author | Przemyslaw Szczepaniak <pszczepaniak@google.com> | 2014-06-18 11:35:52 +0100 |
---|---|---|
committer | Przemyslaw Szczepaniak <pszczepaniak@google.com> | 2014-07-02 11:28:24 +0100 |
commit | 5cbf17ca053b09beadd0b031a46ce193ab27a0f8 (patch) | |
tree | df3f6c0530799b1e8af3ffabcce8d953ef1b36c0 /core/java/android/speech | |
parent | 160a6e5b99de15ce755e2e5521dce32d81ab180a (diff) | |
download | frameworks_base-5cbf17ca053b09beadd0b031a46ce193ab27a0f8.zip frameworks_base-5cbf17ca053b09beadd0b031a46ce193ab27a0f8.tar.gz frameworks_base-5cbf17ca053b09beadd0b031a46ce193ab27a0f8.tar.bz2 |
Add support for audio session id in the TTS
+ #playEarcon & #queueAudio respects request Volume/Pan settings.
Bug:15432115
Change-Id: I136afef77afbc56c34810c64123f7be4b431d378
Diffstat (limited to 'core/java/android/speech')
6 files changed, 139 insertions, 68 deletions
diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java index 03e53ec..c13b47f 100644 --- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java +++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java @@ -16,9 +16,12 @@ package android.speech.tts; import android.content.Context; +import android.media.AudioSystem; +import android.media.AudioTrack; import android.media.MediaPlayer; import android.net.Uri; import android.os.ConditionVariable; +import android.speech.tts.TextToSpeechService.AudioOutputParams; import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; import android.util.Log; @@ -27,7 +30,7 @@ class AudioPlaybackQueueItem extends PlaybackQueueItem { private final Context mContext; private final Uri mUri; - private final int mStreamType; + private final AudioOutputParams mAudioParams; private final ConditionVariable mDone; private MediaPlayer mPlayer; @@ -35,12 +38,12 @@ class AudioPlaybackQueueItem extends PlaybackQueueItem { AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher, Object callerIdentity, - Context context, Uri uri, int streamType) { + Context context, Uri uri, AudioOutputParams audioParams) { super(dispatcher, callerIdentity); mContext = context; mUri = uri; - mStreamType = streamType; + mAudioParams = audioParams; mDone = new ConditionVariable(); mPlayer = null; @@ -73,7 +76,11 @@ class AudioPlaybackQueueItem extends PlaybackQueueItem { mDone.open(); } }); - mPlayer.setAudioStreamType(mStreamType); + mPlayer.setAudioStreamType(mAudioParams.mStreamType); + setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan); + if (mAudioParams.mSessionId != AudioSystem.AUDIO_SESSION_ALLOCATE) { + mPlayer.setAudioSessionId(mAudioParams.mSessionId); + } mPlayer.start(); mDone.block(); finish(); @@ -89,6 +96,23 @@ class AudioPlaybackQueueItem extends PlaybackQueueItem { } } + private static void setupVolume(MediaPlayer player, float volume, float pan) { + final float vol = clip(volume, 0.0f, 1.0f); + final float panning = clip(pan, -1.0f, 1.0f); + + float volLeft = vol, volRight = vol; + if (panning > 0.0f) { + volLeft *= (1.0f - panning); + } else if (panning < 0.0f) { + volRight *= (1.0f + panning); + } + player.setVolume(volLeft, volRight); + } + + private static final float clip(float value, float min, float max) { + return value < min ? min : (value < max ? value : max); + } + private void finish() { try { mPlayer.stop(); diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java index 92bb0ac..b405de0 100644 --- a/core/java/android/speech/tts/BlockingAudioTrack.java +++ b/core/java/android/speech/tts/BlockingAudioTrack.java @@ -4,6 +4,7 @@ package android.speech.tts; import android.media.AudioFormat; import android.media.AudioTrack; +import android.speech.tts.TextToSpeechService.AudioOutputParams; import android.util.Log; /** @@ -44,12 +45,11 @@ class BlockingAudioTrack { private static final int MIN_AUDIO_BUFFER_SIZE = 8192; - private final int mStreamType; + private final AudioOutputParams mAudioParams; private final int mSampleRateInHz; private final int mAudioFormat; private final int mChannelCount; - private final float mVolume; - private final float mPan; + private final int mBytesPerFrame; /** @@ -73,15 +73,14 @@ class BlockingAudioTrack { private AudioTrack mAudioTrack; private volatile boolean mStopped; - BlockingAudioTrack(int streamType, int sampleRate, - int audioFormat, int channelCount, - float volume, float pan) { - mStreamType = streamType; + private int mSessionId; + + BlockingAudioTrack(AudioOutputParams audioParams, int sampleRate, + int audioFormat, int channelCount) { + mAudioParams = audioParams; mSampleRateInHz = sampleRate; mAudioFormat = audioFormat; mChannelCount = channelCount; - mVolume = volume; - mPan = pan; mBytesPerFrame = AudioFormat.getBytesPerSample(mAudioFormat) * mChannelCount; mIsShortUtterance = false; @@ -215,8 +214,9 @@ class BlockingAudioTrack { = AudioTrack.getMinBufferSize(mSampleRateInHz, channelConfig, mAudioFormat); int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes); - AudioTrack audioTrack = new AudioTrack(mStreamType, mSampleRateInHz, channelConfig, - mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM); + AudioTrack audioTrack = new AudioTrack(mAudioParams.mStreamType, mSampleRateInHz, + channelConfig, mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM, + mAudioParams.mSessionId); if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { Log.w(TAG, "Unable to create audio track."); audioTrack.release(); @@ -225,7 +225,7 @@ class BlockingAudioTrack { mAudioBufferSize = bufferSizeInBytes; - setupVolume(audioTrack, mVolume, mPan); + setupVolume(audioTrack, mAudioParams.mVolume, mAudioParams.mPan); return audioTrack; } @@ -328,19 +328,11 @@ class BlockingAudioTrack { } private static final long clip(long value, long min, long max) { - if (value < min) { - return min; - } - - if (value > max) { - return max; - } - - return value; + return value < min ? min : (value < max ? value : max); } - private static float clip(float value, float min, float max) { - return value > max ? max : (value < min ? min : value); + private static final float clip(float value, float min, float max) { + return value < min ? min : (value < max ? value : max); } } diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index 7e68d27..f850f10 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -15,6 +15,7 @@ */ package android.speech.tts; +import android.speech.tts.TextToSpeechService.AudioOutputParams; import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; import android.util.Log; @@ -28,23 +29,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { private static final int MIN_AUDIO_BUFFER_SIZE = 8192; - /** - * Audio stream type. Must be one of the STREAM_ contants defined in - * {@link android.media.AudioManager}. - */ - private final int mStreamType; - - /** - * Volume, in the range [0.0f, 1.0f]. The default value is - * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f). - */ - private final float mVolume; - - /** - * Left/right position of the audio, in the range [-1.0f, 1.0f]. - * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f). - */ - private final float mPan; + private final AudioOutputParams mAudioParams; /** * Guards {@link #mAudioTrackHandler}, {@link #mItem} and {@link #mStopped}. @@ -65,13 +50,11 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { private final Object mCallerIdentity; private final AbstractEventLogger mLogger; - PlaybackSynthesisCallback(int streamType, float volume, float pan, - AudioPlaybackHandler audioTrackHandler, UtteranceProgressDispatcher dispatcher, - Object callerIdentity, AbstractEventLogger logger, boolean clientIsUsingV2) { + PlaybackSynthesisCallback(AudioOutputParams audioParams, AudioPlaybackHandler audioTrackHandler, + UtteranceProgressDispatcher dispatcher, Object callerIdentity, + AbstractEventLogger logger, boolean clientIsUsingV2) { super(clientIsUsingV2); - mStreamType = streamType; - mVolume = volume; - mPan = pan; + mAudioParams = audioParams; mAudioTrackHandler = audioTrackHandler; mDispatcher = dispatcher; mCallerIdentity = callerIdentity; @@ -161,7 +144,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { return TextToSpeech.ERROR; } SynthesisPlaybackQueueItem item = new SynthesisPlaybackQueueItem( - mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, + mAudioParams, sampleRateInHz, audioFormat, channelCount, mDispatcher, mCallerIdentity, mLogger); mAudioTrackHandler.enqueue(item); mItem = item; diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java index 104f486..7423933 100644 --- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java +++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java @@ -15,6 +15,7 @@ */ package android.speech.tts; +import android.speech.tts.TextToSpeechService.AudioOutputParams; import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; import android.util.Log; @@ -62,9 +63,8 @@ final class SynthesisPlaybackQueueItem extends PlaybackQueueItem { private final BlockingAudioTrack mAudioTrack; private final AbstractEventLogger mLogger; - SynthesisPlaybackQueueItem(int streamType, int sampleRate, - int audioFormat, int channelCount, - float volume, float pan, UtteranceProgressDispatcher dispatcher, + SynthesisPlaybackQueueItem(AudioOutputParams audioParams, int sampleRate, + int audioFormat, int channelCount, UtteranceProgressDispatcher dispatcher, Object callerIdentity, AbstractEventLogger logger) { super(dispatcher, callerIdentity); @@ -74,8 +74,7 @@ final class SynthesisPlaybackQueueItem extends PlaybackQueueItem { mDone = false; mStatusCode = TextToSpeech.SUCCESS; - mAudioTrack = new BlockingAudioTrack(streamType, sampleRate, audioFormat, - channelCount, volume, pan); + mAudioTrack = new BlockingAudioTrack(audioParams, sampleRate, audioFormat, channelCount); mLogger = logger; } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index a338c19..0d2b69b 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -564,6 +564,17 @@ public class TextToSpeech { * @see TextToSpeech#getFeatures(java.util.Locale) */ public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts"; + + /** + * Parameter key to specify an audio session identifier (obtained from + * {@link AudioManager#allocateAudioSessionId()}) that will be used by the request audio + * output. It can be used to associate one of the {@link android.media.audiofx.AudioEffect} + * objects with the synthesis (or earcon) output. + * + * @see TextToSpeech#speak(String, int, HashMap) + * @see TextToSpeech#playEarcon(String, int, HashMap) + */ + public static final String KEY_PARAM_SESSION_ID = "sessionId"; } private final Context mContext; @@ -1336,6 +1347,7 @@ public class TextToSpeech { if (params != null && !params.isEmpty()) { Bundle bundle = new Bundle(mParams); copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM); + copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID); copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID); copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME); copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN); diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index a53518f..5a3c5f7 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -17,6 +17,8 @@ package android.speech.tts; import android.app.Service; import android.content.Intent; +import android.media.AudioManager; +import android.media.AudioSystem; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -419,6 +421,73 @@ public abstract class TextToSpeechService extends Service { public void dispatchOnError(int errorCode); } + + /** Set of parameters affecting audio output. */ + static class AudioOutputParams { + /** + * Audio session identifier. May be used to associate audio playback with one of the + * {@link android.media.audiofx.AudioEffect} objects. If not specified by client, + * it should be equal to {@link AudioSystem#AUDIO_SESSION_ALLOCATE}. + */ + public final int mSessionId; + + /** + * Audio stream type. Must be one of the STREAM_ contants defined in + * {@link android.media.AudioManager}. + */ + public final int mStreamType; + + /** + * Volume, in the range [0.0f, 1.0f]. The default value is + * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f). + */ + public final float mVolume; + + /** + * Left/right position of the audio, in the range [-1.0f, 1.0f]. + * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f). + */ + public final float mPan; + + /** Create AudioOutputParams with default values */ + AudioOutputParams() { + mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; + mStreamType = Engine.DEFAULT_STREAM; + mVolume = Engine.DEFAULT_VOLUME; + mPan = Engine.DEFAULT_PAN; + } + + AudioOutputParams(int sessionId, int streamType, float volume, float pan) { + mSessionId = sessionId; + mStreamType = streamType; + mVolume = volume; + mPan = pan; + } + + /** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */ + static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle) { + if (paramsBundle == null) { + return new AudioOutputParams(); + } + + return new AudioOutputParams( + paramsBundle.getInt( + Engine.KEY_PARAM_SESSION_ID, + AudioSystem.AUDIO_SESSION_ALLOCATE), + paramsBundle.getInt( + Engine.KEY_PARAM_STREAM, + Engine.DEFAULT_STREAM), + paramsBundle.getFloat( + Engine.KEY_PARAM_VOLUME, + Engine.DEFAULT_VOLUME), + paramsBundle.getFloat( + Engine.KEY_PARAM_PAN, + Engine.DEFAULT_PAN)); + } + + } + + /** * An item in the synth thread queue. */ @@ -592,16 +661,8 @@ public abstract class TextToSpeechService extends Service { return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null); } - int getStreamType() { - return getIntParam(mParams, Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM); - } - - float getVolume() { - return getFloatParam(mParams, Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME); - } - - float getPan() { - return getFloatParam(mParams, Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN); + AudioOutputParams getAudioParams() { + return AudioOutputParams.createFromV1ParamsBundle(mParams); } } @@ -668,7 +729,7 @@ public abstract class TextToSpeechService extends Service { } protected AbstractSynthesisCallback createSynthesisCallback() { - return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(), + return new PlaybackSynthesisCallback(getAudioParams(), mAudioPlaybackHandler, this, getCallerIdentity(), mEventLogger, false); } @@ -743,7 +804,7 @@ public abstract class TextToSpeechService extends Service { Bundle params, Uri uri) { super(callerIdentity, callerUid, callerPid, params); mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(), - TextToSpeechService.this, uri, getStreamType()); + TextToSpeechService.this, uri, getAudioParams()); } @Override |