1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package android.speech.tts; 17 18 import android.media.AudioFormat; 19 import android.media.AudioTrack; 20 import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; 21 22 import java.util.LinkedList; 23 24 /** 25 * Params required to play back a synthesis request. 26 */ 27 final class SynthesisMessageParams extends MessageParams { 28 private static final long MAX_UNCONSUMED_AUDIO_MS = 500; 29 30 final int mStreamType; 31 final int mSampleRateInHz; 32 final int mAudioFormat; 33 final int mChannelCount; 34 final float mVolume; 35 final float mPan; 36 final EventLogger mLogger; 37 38 final int mBytesPerFrame; 39 40 volatile AudioTrack mAudioTrack; 41 // Written by the synthesis thread, but read on the audio playback 42 // thread. 43 volatile int mBytesWritten; 44 // A "short utterance" is one that uses less bytes than the audio 45 // track buffer size (mAudioBufferSize). In this case, we need to call 46 // AudioTrack#stop() to send pending buffers to the mixer, and slightly 47 // different logic is required to wait for the track to finish. 48 // 49 // Not volatile, accessed only from the audio playback thread. 50 boolean mIsShortUtterance; 51 int mAudioBufferSize; 52 // Always synchronized on "this". 53 int mUnconsumedBytes; 54 55 private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); 56 SynthesisMessageParams(int streamType, int sampleRate, int audioFormat, int channelCount, float volume, float pan, UtteranceCompletedDispatcher dispatcher, String callingApp, EventLogger logger)57 SynthesisMessageParams(int streamType, int sampleRate, 58 int audioFormat, int channelCount, 59 float volume, float pan, UtteranceCompletedDispatcher dispatcher, 60 String callingApp, EventLogger logger) { 61 super(dispatcher, callingApp); 62 63 mStreamType = streamType; 64 mSampleRateInHz = sampleRate; 65 mAudioFormat = audioFormat; 66 mChannelCount = channelCount; 67 mVolume = volume; 68 mPan = pan; 69 mLogger = logger; 70 71 mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount; 72 73 // initially null. 74 mAudioTrack = null; 75 mBytesWritten = 0; 76 mAudioBufferSize = 0; 77 } 78 79 @Override getType()80 int getType() { 81 return TYPE_SYNTHESIS; 82 } 83 addBuffer(byte[] buffer)84 synchronized void addBuffer(byte[] buffer) { 85 long unconsumedAudioMs = 0; 86 87 while ((unconsumedAudioMs = getUnconsumedAudioLengthMs()) > MAX_UNCONSUMED_AUDIO_MS) { 88 try { 89 wait(); 90 } catch (InterruptedException ie) { 91 return; 92 } 93 } 94 95 mDataBufferList.add(new ListEntry(buffer)); 96 mUnconsumedBytes += buffer.length; 97 } 98 clearBuffers()99 synchronized void clearBuffers() { 100 mDataBufferList.clear(); 101 mUnconsumedBytes = 0; 102 notifyAll(); 103 } 104 getNextBuffer()105 synchronized ListEntry getNextBuffer() { 106 ListEntry entry = mDataBufferList.poll(); 107 if (entry != null) { 108 mUnconsumedBytes -= entry.mBytes.length; 109 notifyAll(); 110 } 111 112 return entry; 113 } 114 setAudioTrack(AudioTrack audioTrack)115 void setAudioTrack(AudioTrack audioTrack) { 116 mAudioTrack = audioTrack; 117 } 118 getAudioTrack()119 AudioTrack getAudioTrack() { 120 return mAudioTrack; 121 } 122 123 // Must be called synchronized on this. getUnconsumedAudioLengthMs()124 private long getUnconsumedAudioLengthMs() { 125 final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame; 126 final long estimatedTimeMs = unconsumedFrames * 1000 / mSampleRateInHz; 127 128 return estimatedTimeMs; 129 } 130 getBytesPerFrame(int audioFormat)131 private static int getBytesPerFrame(int audioFormat) { 132 if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { 133 return 1; 134 } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { 135 return 2; 136 } 137 138 return -1; 139 } 140 141 static final class ListEntry { 142 final byte[] mBytes; 143 ListEntry(byte[] bytes)144 ListEntry(byte[] bytes) { 145 mBytes = bytes; 146 } 147 } 148 } 149 150