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.UtteranceProgressDispatcher;
     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     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