• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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