Loading core/java/android/speech/tts/AudioPlaybackHandler.java +3 −13 Original line number Diff line number Diff line Loading @@ -390,10 +390,10 @@ class AudioPlaybackHandler { audioTrack.play(); } int count = 0; while (count < bufferCopy.mLength) { while (count < bufferCopy.mBytes.length) { // Note that we don't take bufferCopy.mOffset into account because // it is guaranteed to be 0. int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength); int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mBytes.length); if (written <= 0) { break; } Loading Loading @@ -453,7 +453,7 @@ class AudioPlaybackHandler { } final AudioTrack audioTrack = params.mAudioTrack; final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat); final int bytesPerFrame = params.mBytesPerFrame; final int lengthInBytes = params.mBytesWritten; final int lengthInFrames = lengthInBytes / bytesPerFrame; Loading Loading @@ -511,16 +511,6 @@ class AudioPlaybackHandler { return 0; } static int getBytesPerFrame(int audioFormat) { if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { return 1; } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { return 2; } return -1; } private static void setupVolume(AudioTrack audioTrack, float volume, float pan) { float vol = clip(volume, 0.0f, 1.0f); float panning = clip(pan, -1.0f, 1.0f); Loading core/java/android/speech/tts/PlaybackSynthesisCallback.java +27 −8 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // Note that mLogger.mError might be true too at this point. mLogger.onStopped(); SynthesisMessageParams token = null; synchronized (mStateLock) { if (mStopped) { Log.w(TAG, "stop() called twice"); Loading @@ -97,9 +98,19 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // In all other cases, mAudioTrackHandler.stop() will // result in onComplete being called. mLogger.onWriteData(); } else { token = mToken; } mStopped = true; } if (token != null) { // This might result in the synthesis thread being woken up, at which // point it will write an additional buffer to the token - but we // won't worry about that because the audio playback queue will be cleared // soon after (see SynthHandler#stop(String). token.clearBuffers(); } } @Override Loading Loading @@ -155,17 +166,21 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { + length + " bytes)"); } SynthesisMessageParams token = null; synchronized (mStateLock) { if (mToken == null || mStopped) { return TextToSpeech.ERROR; } token = mToken; } // Sigh, another copy. final byte[] bufferCopy = new byte[length]; System.arraycopy(buffer, offset, bufferCopy, 0, length); mToken.addBuffer(bufferCopy); mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken); } // Might block on mToken.this, if there are too many buffers waiting to // be consumed. token.addBuffer(bufferCopy); mAudioTrackHandler.enqueueSynthesisDataAvailable(token); mLogger.onEngineDataReceived(); Loading @@ -176,6 +191,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { public int done() { if (DBG) Log.d(TAG, "done()"); SynthesisMessageParams token = null; synchronized (mStateLock) { if (mDone) { Log.w(TAG, "Duplicate call to done()"); Loading @@ -188,9 +204,12 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { return TextToSpeech.ERROR; } mAudioTrackHandler.enqueueSynthesisDone(mToken); mLogger.onEngineComplete(); token = mToken; } mAudioTrackHandler.enqueueSynthesisDone(token); mLogger.onEngineComplete(); return TextToSpeech.SUCCESS; } Loading core/java/android/speech/tts/SynthesisMessageParams.java +56 −13 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package android.speech.tts; import android.media.AudioFormat; import android.media.AudioTrack; import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; Loading @@ -24,6 +25,8 @@ import java.util.LinkedList; * Params required to play back a synthesis request. */ final class SynthesisMessageParams extends MessageParams { private static final long MAX_UNCONSUMED_AUDIO_MS = 500; final int mStreamType; final int mSampleRateInHz; final int mAudioFormat; Loading @@ -32,10 +35,16 @@ final class SynthesisMessageParams extends MessageParams { final float mPan; final EventLogger mLogger; final int mBytesPerFrame; volatile AudioTrack mAudioTrack; // Not volatile, accessed only from the synthesis thread. int mBytesWritten; // Written by the synthesis thread, but read on the audio playback // thread. volatile int mBytesWritten; // Not volatile, accessed only from the audio playback thread. int mAudioBufferSize; // Always synchronized on "this". int mUnconsumedBytes; private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); Loading @@ -53,6 +62,8 @@ final class SynthesisMessageParams extends MessageParams { mPan = pan; mLogger = logger; mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount; // initially null. mAudioTrack = null; mBytesWritten = 0; Loading @@ -64,18 +75,36 @@ final class SynthesisMessageParams extends MessageParams { return TYPE_SYNTHESIS; } synchronized void addBuffer(byte[] buffer, int offset, int length) { mDataBufferList.add(new ListEntry(buffer, offset, length)); synchronized void addBuffer(byte[] buffer) { long unconsumedAudioMs = 0; while ((unconsumedAudioMs = getUnconsumedAudioLengthMs()) > MAX_UNCONSUMED_AUDIO_MS) { try { wait(); } catch (InterruptedException ie) { return; } } synchronized void addBuffer(byte[] buffer) { mDataBufferList.add(new ListEntry(buffer, 0, buffer.length)); mDataBufferList.add(new ListEntry(buffer)); mUnconsumedBytes += buffer.length; } synchronized void clearBuffers() { mDataBufferList.clear(); mUnconsumedBytes = 0; notifyAll(); } synchronized ListEntry getNextBuffer() { return mDataBufferList.poll(); ListEntry entry = mDataBufferList.poll(); if (entry != null) { mUnconsumedBytes -= entry.mBytes.length; notifyAll(); } return entry; } void setAudioTrack(AudioTrack audioTrack) { mAudioTrack = audioTrack; Loading @@ -85,15 +114,29 @@ final class SynthesisMessageParams extends MessageParams { return mAudioTrack; } // Must be called synchronized on this. private long getUnconsumedAudioLengthMs() { final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame; final long estimatedTimeMs = unconsumedFrames * 1000 / mSampleRateInHz; return estimatedTimeMs; } private static int getBytesPerFrame(int audioFormat) { if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { return 1; } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { return 2; } return -1; } static final class ListEntry { final byte[] mBytes; final int mOffset; final int mLength; ListEntry(byte[] bytes, int offset, int length) { ListEntry(byte[] bytes) { mBytes = bytes; mOffset = offset; mLength = length; } } } Loading Loading
core/java/android/speech/tts/AudioPlaybackHandler.java +3 −13 Original line number Diff line number Diff line Loading @@ -390,10 +390,10 @@ class AudioPlaybackHandler { audioTrack.play(); } int count = 0; while (count < bufferCopy.mLength) { while (count < bufferCopy.mBytes.length) { // Note that we don't take bufferCopy.mOffset into account because // it is guaranteed to be 0. int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength); int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mBytes.length); if (written <= 0) { break; } Loading Loading @@ -453,7 +453,7 @@ class AudioPlaybackHandler { } final AudioTrack audioTrack = params.mAudioTrack; final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat); final int bytesPerFrame = params.mBytesPerFrame; final int lengthInBytes = params.mBytesWritten; final int lengthInFrames = lengthInBytes / bytesPerFrame; Loading Loading @@ -511,16 +511,6 @@ class AudioPlaybackHandler { return 0; } static int getBytesPerFrame(int audioFormat) { if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { return 1; } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { return 2; } return -1; } private static void setupVolume(AudioTrack audioTrack, float volume, float pan) { float vol = clip(volume, 0.0f, 1.0f); float panning = clip(pan, -1.0f, 1.0f); Loading
core/java/android/speech/tts/PlaybackSynthesisCallback.java +27 −8 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // Note that mLogger.mError might be true too at this point. mLogger.onStopped(); SynthesisMessageParams token = null; synchronized (mStateLock) { if (mStopped) { Log.w(TAG, "stop() called twice"); Loading @@ -97,9 +98,19 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // In all other cases, mAudioTrackHandler.stop() will // result in onComplete being called. mLogger.onWriteData(); } else { token = mToken; } mStopped = true; } if (token != null) { // This might result in the synthesis thread being woken up, at which // point it will write an additional buffer to the token - but we // won't worry about that because the audio playback queue will be cleared // soon after (see SynthHandler#stop(String). token.clearBuffers(); } } @Override Loading Loading @@ -155,17 +166,21 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { + length + " bytes)"); } SynthesisMessageParams token = null; synchronized (mStateLock) { if (mToken == null || mStopped) { return TextToSpeech.ERROR; } token = mToken; } // Sigh, another copy. final byte[] bufferCopy = new byte[length]; System.arraycopy(buffer, offset, bufferCopy, 0, length); mToken.addBuffer(bufferCopy); mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken); } // Might block on mToken.this, if there are too many buffers waiting to // be consumed. token.addBuffer(bufferCopy); mAudioTrackHandler.enqueueSynthesisDataAvailable(token); mLogger.onEngineDataReceived(); Loading @@ -176,6 +191,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { public int done() { if (DBG) Log.d(TAG, "done()"); SynthesisMessageParams token = null; synchronized (mStateLock) { if (mDone) { Log.w(TAG, "Duplicate call to done()"); Loading @@ -188,9 +204,12 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { return TextToSpeech.ERROR; } mAudioTrackHandler.enqueueSynthesisDone(mToken); mLogger.onEngineComplete(); token = mToken; } mAudioTrackHandler.enqueueSynthesisDone(token); mLogger.onEngineComplete(); return TextToSpeech.SUCCESS; } Loading
core/java/android/speech/tts/SynthesisMessageParams.java +56 −13 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package android.speech.tts; import android.media.AudioFormat; import android.media.AudioTrack; import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; Loading @@ -24,6 +25,8 @@ import java.util.LinkedList; * Params required to play back a synthesis request. */ final class SynthesisMessageParams extends MessageParams { private static final long MAX_UNCONSUMED_AUDIO_MS = 500; final int mStreamType; final int mSampleRateInHz; final int mAudioFormat; Loading @@ -32,10 +35,16 @@ final class SynthesisMessageParams extends MessageParams { final float mPan; final EventLogger mLogger; final int mBytesPerFrame; volatile AudioTrack mAudioTrack; // Not volatile, accessed only from the synthesis thread. int mBytesWritten; // Written by the synthesis thread, but read on the audio playback // thread. volatile int mBytesWritten; // Not volatile, accessed only from the audio playback thread. int mAudioBufferSize; // Always synchronized on "this". int mUnconsumedBytes; private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); Loading @@ -53,6 +62,8 @@ final class SynthesisMessageParams extends MessageParams { mPan = pan; mLogger = logger; mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount; // initially null. mAudioTrack = null; mBytesWritten = 0; Loading @@ -64,18 +75,36 @@ final class SynthesisMessageParams extends MessageParams { return TYPE_SYNTHESIS; } synchronized void addBuffer(byte[] buffer, int offset, int length) { mDataBufferList.add(new ListEntry(buffer, offset, length)); synchronized void addBuffer(byte[] buffer) { long unconsumedAudioMs = 0; while ((unconsumedAudioMs = getUnconsumedAudioLengthMs()) > MAX_UNCONSUMED_AUDIO_MS) { try { wait(); } catch (InterruptedException ie) { return; } } synchronized void addBuffer(byte[] buffer) { mDataBufferList.add(new ListEntry(buffer, 0, buffer.length)); mDataBufferList.add(new ListEntry(buffer)); mUnconsumedBytes += buffer.length; } synchronized void clearBuffers() { mDataBufferList.clear(); mUnconsumedBytes = 0; notifyAll(); } synchronized ListEntry getNextBuffer() { return mDataBufferList.poll(); ListEntry entry = mDataBufferList.poll(); if (entry != null) { mUnconsumedBytes -= entry.mBytes.length; notifyAll(); } return entry; } void setAudioTrack(AudioTrack audioTrack) { mAudioTrack = audioTrack; Loading @@ -85,15 +114,29 @@ final class SynthesisMessageParams extends MessageParams { return mAudioTrack; } // Must be called synchronized on this. private long getUnconsumedAudioLengthMs() { final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame; final long estimatedTimeMs = unconsumedFrames * 1000 / mSampleRateInHz; return estimatedTimeMs; } private static int getBytesPerFrame(int audioFormat) { if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { return 1; } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { return 2; } return -1; } static final class ListEntry { final byte[] mBytes; final int mOffset; final int mLength; ListEntry(byte[] bytes, int offset, int length) { ListEntry(byte[] bytes) { mBytes = bytes; mOffset = offset; mLength = length; } } } Loading