Add support for audio session id in the TTS

+ #playEarcon & #queueAudio respects request Volume/Pan settings.

Bug:15432115
Change-Id: I136afef77afbc56c34810c64123f7be4b431d378
This commit is contained in:
Przemyslaw Szczepaniak
2014-06-18 11:35:52 +01:00
parent 160a6e5b99
commit 5cbf17ca05
7 changed files with 140 additions and 68 deletions

View File

@@ -26660,6 +26660,7 @@ package android.speech.tts {
field public static final java.lang.String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
field public static final java.lang.String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
field public static final java.lang.String KEY_PARAM_PAN = "pan";
field public static final java.lang.String KEY_PARAM_SESSION_ID = "sessionId";
field public static final java.lang.String KEY_PARAM_STREAM = "streamType";
field public static final java.lang.String KEY_PARAM_UTTERANCE_ID = "utteranceId";
field public static final java.lang.String KEY_PARAM_VOLUME = "volume";

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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