18d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath/*
28d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Copyright (C) 2011 The Android Open Source Project
38d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath *
48d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); you may not
58d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * use this file except in compliance with the License. You may obtain a copy of
68d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * the License at
78d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath *
88d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * http://www.apache.org/licenses/LICENSE-2.0
98d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath *
108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Unless required by applicable law or agreed to in writing, software
118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
128d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * License for the specific language governing permissions and limitations under
148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * the License.
158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath */
168d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamathpackage android.speech.tts;
178d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
18af802c6831551323126537cf8edabea97d2fc762Narayan Kamathimport android.content.Context;
195cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniakimport android.media.AudioSystem;
20af802c6831551323126537cf8edabea97d2fc762Narayan Kamathimport android.media.MediaPlayer;
21af802c6831551323126537cf8edabea97d2fc762Narayan Kamathimport android.net.Uri;
22af802c6831551323126537cf8edabea97d2fc762Narayan Kamathimport android.os.ConditionVariable;
235cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniakimport android.speech.tts.TextToSpeechService.AudioOutputParams;
24754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamathimport android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
2567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport android.util.Log;
268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
2767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathclass AudioPlaybackQueueItem extends PlaybackQueueItem {
28af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    private static final String TAG = "TTS.AudioQueueItem";
29af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
30af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    private final Context mContext;
31af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    private final Uri mUri;
325cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    private final AudioOutputParams mAudioParams;
33af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
34af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    private final ConditionVariable mDone;
35af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    private MediaPlayer mPlayer;
36af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    private volatile boolean mFinished;
378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher,
39af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            Object callerIdentity,
405cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak            Context context, Uri uri, AudioOutputParams audioParams) {
41492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath        super(dispatcher, callerIdentity);
42af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
43af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        mContext = context;
44af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        mUri = uri;
455cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        mAudioParams = audioParams;
46af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
47af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        mDone = new ConditionVariable();
48af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        mPlayer = null;
49af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        mFinished = false;
508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
5167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    @Override
5267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    public void run() {
53af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        final UtteranceProgressDispatcher dispatcher = getDispatcher();
54af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
55af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        dispatcher.dispatchOnStart();
56672695e42387a024083d198f3d665f354ef1d27cPrzemyslaw Szczepaniak
57672695e42387a024083d198f3d665f354ef1d27cPrzemyslaw Szczepaniak        int sessionId = mAudioParams.mSessionId;
58672695e42387a024083d198f3d665f354ef1d27cPrzemyslaw Szczepaniak        mPlayer = MediaPlayer.create(
59672695e42387a024083d198f3d665f354ef1d27cPrzemyslaw Szczepaniak                mContext, mUri, null, mAudioParams.mAudioAttributes,
60672695e42387a024083d198f3d665f354ef1d27cPrzemyslaw Szczepaniak                sessionId > 0 ? sessionId : AudioSystem.AUDIO_SESSION_ALLOCATE);
61af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        if (mPlayer == null) {
62fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            dispatcher.dispatchOnError(TextToSpeech.ERROR_OUTPUT);
63af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            return;
64af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        }
65af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
66af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        try {
67af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
68af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                @Override
69af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                public boolean onError(MediaPlayer mp, int what, int extra) {
70af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                    Log.w(TAG, "Audio playback error: " + what + ", " + extra);
71af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                    mDone.open();
72af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                    return true;
73af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                }
74af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            });
75af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
76af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                @Override
77af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                public void onCompletion(MediaPlayer mp) {
78af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                    mFinished = true;
79af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                    mDone.open();
80af802c6831551323126537cf8edabea97d2fc762Narayan Kamath                }
81af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            });
82672695e42387a024083d198f3d665f354ef1d27cPrzemyslaw Szczepaniak
835cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak            setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan);
84af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            mPlayer.start();
85af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            mDone.block();
86af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            finish();
87af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        } catch (IllegalArgumentException ex) {
88af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            Log.w(TAG, "MediaPlayer failed", ex);
89af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            mDone.open();
90af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        }
91af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
92af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        if (mFinished) {
9390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            dispatcher.dispatchOnSuccess();
94af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        } else {
9590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            dispatcher.dispatchOnStop();
96af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        }
97af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    }
98af802c6831551323126537cf8edabea97d2fc762Narayan Kamath
995cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    private static void setupVolume(MediaPlayer player, float volume, float pan) {
1005cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        final float vol = clip(volume, 0.0f, 1.0f);
1015cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        final float panning = clip(pan, -1.0f, 1.0f);
1025cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak
1035cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        float volLeft = vol, volRight = vol;
1045cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        if (panning > 0.0f) {
1055cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak            volLeft *= (1.0f - panning);
1065cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        } else if (panning < 0.0f) {
1075cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak            volRight *= (1.0f + panning);
1085cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        }
1095cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        player.setVolume(volLeft, volRight);
1105cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    }
1115cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak
1125cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    private static final float clip(float value, float min, float max) {
1135cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        return value < min ? min : (value < max ? value : max);
1145cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    }
1155cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak
116af802c6831551323126537cf8edabea97d2fc762Narayan Kamath    private void finish() {
117af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        try {
118af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            mPlayer.stop();
119af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        } catch (IllegalStateException ex) {
120af802c6831551323126537cf8edabea97d2fc762Narayan Kamath            // Do nothing, the player is already stopped
121af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        }
122af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        mPlayer.release();
1238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    @Override
12690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    void stop(int errorCode) {
127af802c6831551323126537cf8edabea97d2fc762Narayan Kamath        mDone.open();
1288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1298d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath}
130