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 */ 16package android.speech.tts; 17 18import android.media.AudioFormat; 19import android.media.AudioTrack; 20import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; 21 22import java.util.LinkedList; 23 24/** 25 * Params required to play back a synthesis request. 26 */ 27final 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 volatile boolean mIsError; 55 56 private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); 57 58 SynthesisMessageParams(int streamType, int sampleRate, 59 int audioFormat, int channelCount, 60 float volume, float pan, UtteranceProgressDispatcher dispatcher, 61 String callingApp, EventLogger logger) { 62 super(dispatcher, callingApp); 63 64 mStreamType = streamType; 65 mSampleRateInHz = sampleRate; 66 mAudioFormat = audioFormat; 67 mChannelCount = channelCount; 68 mVolume = volume; 69 mPan = pan; 70 mLogger = logger; 71 72 mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount; 73 74 // initially null. 75 mAudioTrack = null; 76 mBytesWritten = 0; 77 mAudioBufferSize = 0; 78 mIsError = false; 79 } 80 81 @Override 82 int getType() { 83 return TYPE_SYNTHESIS; 84 } 85 86 synchronized void addBuffer(byte[] buffer) { 87 long unconsumedAudioMs = 0; 88 89 while ((unconsumedAudioMs = getUnconsumedAudioLengthMs()) > MAX_UNCONSUMED_AUDIO_MS) { 90 try { 91 wait(); 92 } catch (InterruptedException ie) { 93 return; 94 } 95 } 96 97 mDataBufferList.add(new ListEntry(buffer)); 98 mUnconsumedBytes += buffer.length; 99 } 100 101 synchronized void clearBuffers() { 102 mDataBufferList.clear(); 103 mUnconsumedBytes = 0; 104 notifyAll(); 105 } 106 107 synchronized ListEntry getNextBuffer() { 108 ListEntry entry = mDataBufferList.poll(); 109 if (entry != null) { 110 mUnconsumedBytes -= entry.mBytes.length; 111 notifyAll(); 112 } 113 114 return entry; 115 } 116 117 void setAudioTrack(AudioTrack audioTrack) { 118 mAudioTrack = audioTrack; 119 } 120 121 AudioTrack getAudioTrack() { 122 return mAudioTrack; 123 } 124 125 void setIsError(boolean isError) { 126 mIsError = isError; 127 } 128 129 boolean isError() { 130 return mIsError; 131 } 132 133 // Must be called synchronized on this. 134 private long getUnconsumedAudioLengthMs() { 135 final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame; 136 final long estimatedTimeMs = unconsumedFrames * 1000 / mSampleRateInHz; 137 138 return estimatedTimeMs; 139 } 140 141 private static int getBytesPerFrame(int audioFormat) { 142 if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { 143 return 1; 144 } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { 145 return 2; 146 } 147 148 return -1; 149 } 150 151 static final class ListEntry { 152 final byte[] mBytes; 153 154 ListEntry(byte[] bytes) { 155 mBytes = bytes; 156 } 157 } 158} 159 160