Home | History | Annotate | Download | only in tts
      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 
     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
     80     int getType() {
     81         return TYPE_SYNTHESIS;
     82     }
     83 
     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 
     99     synchronized void clearBuffers() {
    100         mDataBufferList.clear();
    101         mUnconsumedBytes = 0;
    102         notifyAll();
    103     }
    104 
    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 
    115     void setAudioTrack(AudioTrack audioTrack) {
    116         mAudioTrack = audioTrack;
    117     }
    118 
    119     AudioTrack getAudioTrack() {
    120         return mAudioTrack;
    121     }
    122 
    123     // Must be called synchronized on this.
    124     private long getUnconsumedAudioLengthMs() {
    125         final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame;
    126         final long estimatedTimeMs = unconsumedFrames * 1000 / mSampleRateInHz;
    127 
    128         return estimatedTimeMs;
    129     }
    130 
    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 
    144         ListEntry(byte[] bytes) {
    145             mBytes = bytes;
    146         }
    147     }
    148 }
    149 
    150