/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Speech module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ package org.qtproject.qt5.android.speech; import android.content.ContentResolver; import android.content.Context; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.Engine; import android.speech.tts.TextToSpeech.OnInitListener; import android.speech.tts.UtteranceProgressListener; import android.os.Build; import android.os.Bundle; import android.util.Log; import java.lang.Float; import java.util.HashMap; import java.util.Locale; import java.util.List; import java.util.ArrayList; import java.util.Set; public class QtTextToSpeech { // Native callback functions native public void notifyError(long id); native public void notifyReady(long id); native public void notifySpeaking(long id); private TextToSpeech mTts; private final long mId; private boolean mInitialized = false; private float mPitch = 1.0f; private float mRate = 1.0f; private float mVolume = 1.0f; // OnInitListener private final OnInitListener mTtsChangeListener = new OnInitListener() { @Override public void onInit(int status) { Log.w("QtTextToSpeech", "tts initialized"); if (status == TextToSpeech.SUCCESS) { mInitialized = true; notifyReady(mId); } else { mInitialized = false; notifyError(mId); } } }; // UtteranceProgressListener private final UtteranceProgressListener mTtsUtteranceProgressListener = new UtteranceProgressListener() { @Override public void onDone(String utteranceId) { Log.w("UtteranceProgressListener", "onDone"); if (utteranceId.equals("UtteranceId")) { notifyReady(mId); } } @Override public void onError(String utteranceId) { Log.w("UtteranceProgressListener", "onError"); if (utteranceId.equals("UtteranceId")) { notifyReady(mId); } } @Override public void onStart(String utteranceId) { Log.w("UtteranceProgressListener", "onStart"); if (utteranceId.equals("UtteranceId")) { notifySpeaking(mId); } } }; public static QtTextToSpeech open(final Context context, final long id) { return new QtTextToSpeech(context, id); } QtTextToSpeech(final Context context, final long id) { mId = id; mTts = new TextToSpeech(context, mTtsChangeListener); mTts.setOnUtteranceProgressListener(mTtsUtteranceProgressListener); // Read pitch from settings ContentResolver resolver = context.getContentResolver(); try { float pitch = Settings.Secure.getFloat(resolver, android.provider.Settings.Secure.TTS_DEFAULT_PITCH); mPitch = pitch / 100.0f; } catch (SettingNotFoundException e) { mPitch = 1.0f; } // Read rate from settings try { float rate = Settings.Secure.getFloat(resolver, android.provider.Settings.Secure.TTS_DEFAULT_RATE); mRate = rate / 100.0f; } catch (SettingNotFoundException e) { mRate = 1.0f; } } public void say(String text) { Log.w("QtTextToSpeech", text); int result = -1; HashMap map = new HashMap(); map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "UtteranceId"); map.put(TextToSpeech.Engine.KEY_PARAM_VOLUME, Float.toString(mVolume)); result = mTts.speak(text, TextToSpeech.QUEUE_FLUSH, map); Log.w("QtTextToSpeech", "RESULT: " + Integer.toString(result)); } public void stop() { Log.w("QtTextToSpeech", "STOP"); mTts.stop(); } public float pitch() { return mPitch; } public int setPitch(float pitch) { if (Float.compare(pitch, mPitch) == 0) return TextToSpeech.ERROR; int success = mTts.setPitch(pitch); if (success == TextToSpeech.SUCCESS) mPitch = pitch; return success; } public float rate() { return mRate; } public int setRate(float rate) { if (Float.compare(rate, mRate) == 0) return TextToSpeech.ERROR; int success = mTts.setSpeechRate(rate); if (success == TextToSpeech.SUCCESS) mRate = rate; return success; } public void shutdown() { mTts.shutdown(); } public float volume() { return mVolume; } public int setVolume(float volume) { if (Float.compare(volume, mVolume) == 0) return TextToSpeech.ERROR; mVolume = volume; return TextToSpeech.SUCCESS; } public boolean setLocale(Locale locale) { int result = mTts.setLanguage(locale); return (result != TextToSpeech.LANG_NOT_SUPPORTED) && (result != TextToSpeech.LANG_MISSING_DATA); } public List getAvailableVoices() { if (mInitialized && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { //Log.d("QtTextToSpeech", "Voices: " + mTts.getVoices()); return new ArrayList(mTts.getVoices()); } return new ArrayList(); } public List getAvailableLocales() { if (mInitialized && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { //Log.d("QtTextToSpeech", "Locales: " + mTts.getAvailableLanguages()); final Set languages = mTts.getAvailableLanguages(); ArrayList locales = new ArrayList(); for (Locale language : languages) { String languageCode = language.getLanguage(); String countryCode = language.getCountry(); if (languageCode.equals(language.getISO3Language())) languageCode = convertLanguageCodeThreeDigitToTwoDigit(languageCode); if (countryCode.equals(language.getISO3Country())) countryCode = convertCountryCodeThreeDigitToTwoDigit(countryCode); locales.add(new Locale(languageCode, countryCode)); } return locales; } return new ArrayList(); } public Locale getLocale() { //Log.d("QtTextToSpeech", "getLocale: " + mLocale); final Locale language = mTts.getLanguage(); String languageCode = language.getLanguage(); String countryCode = language.getCountry(); if (languageCode.equals(language.getISO3Language())) languageCode = convertLanguageCodeThreeDigitToTwoDigit(languageCode); if (countryCode.equals(language.getISO3Country())) countryCode = convertCountryCodeThreeDigitToTwoDigit(countryCode); return new Locale(languageCode, countryCode); } public Object getVoice() { if (mInitialized && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { return mTts.getVoice(); } return null; } public boolean setVoice(String voiceName) { if (!mInitialized) return false; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { for (android.speech.tts.Voice voice : mTts.getVoices()) { if (voice.getName().equals(voiceName)) { int result = mTts.setVoice(voice); if (result == TextToSpeech.SUCCESS) { //Log.d("QtTextToSpeech", "setVoice: " + voice); return true; } break; } } } return false; } private String convertLanguageCodeThreeDigitToTwoDigit(String iso3Language) { final String[] isoLanguages = Locale.getISOLanguages(); for (String isoLanguage : isoLanguages) { if (iso3Language.equals(new Locale(isoLanguage).getISO3Language())) { return isoLanguage; } } return iso3Language; } private String convertCountryCodeThreeDigitToTwoDigit(String iso3Country) { final String[] isoCountries = Locale.getISOCountries(); for (String isoCountry : isoCountries) { if (iso3Country.equals(new Locale("en", isoCountry).getISO3Country())) { return isoCountry; } } return iso3Country; } }