| /* | /* | ||||
| * Copyright (C) 2011 Google Inc. | * Copyright (C) 2011 Google Inc. | ||||
| * Copyright (C) 2012 Reece H. Dunn | |||||
| * | * | ||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| * you may not use this file except in compliance with the License. | * you may not use this file except in compliance with the License. | ||||
| import android.speech.tts.SynthesisRequest; | import android.speech.tts.SynthesisRequest; | ||||
| import android.speech.tts.TextToSpeech; | import android.speech.tts.TextToSpeech; | ||||
| import android.speech.tts.TextToSpeechService; | import android.speech.tts.TextToSpeechService; | ||||
| import android.text.TextUtils; | |||||
| import android.util.Log; | import android.util.Log; | ||||
| import com.googlecode.eyesfree.espeak.SpeechSynthesis.SynthReadyCallback; | import com.googlecode.eyesfree.espeak.SpeechSynthesis.SynthReadyCallback; | ||||
| private SynthesisCallback mCallback; | private SynthesisCallback mCallback; | ||||
| private List<Voice> mAvailableVoices; | private List<Voice> mAvailableVoices; | ||||
| private Voice mMatchingVoice = null; | |||||
| private String mLanguage = DEFAULT_LANGUAGE; | private String mLanguage = DEFAULT_LANGUAGE; | ||||
| private String mCountry = DEFAULT_COUNTRY; | private String mCountry = DEFAULT_COUNTRY; | ||||
| final Locale query = new Locale(language, country, variant); | final Locale query = new Locale(language, country, variant); | ||||
| boolean hasLanguage = false; | |||||
| boolean hasCountry = false; | |||||
| Voice languageVoice = null; | |||||
| Voice countryVoice = null; | |||||
| synchronized (mAvailableVoices) { | synchronized (mAvailableVoices) { | ||||
| for (Voice voice : mAvailableVoices) { | for (Voice voice : mAvailableVoices) { | ||||
| switch (voice.match(query)) { | switch (voice.match(query)) { | ||||
| case TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE: | case TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE: | ||||
| mMatchingVoice = voice; | |||||
| return TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE; | return TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE; | ||||
| case TextToSpeech.LANG_COUNTRY_AVAILABLE: | case TextToSpeech.LANG_COUNTRY_AVAILABLE: | ||||
| hasCountry = true; | |||||
| countryVoice = voice; | |||||
| case TextToSpeech.LANG_AVAILABLE: | case TextToSpeech.LANG_AVAILABLE: | ||||
| hasLanguage = true; | |||||
| languageVoice = voice; | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (!hasLanguage) { | |||||
| if (languageVoice == null) { | |||||
| mMatchingVoice = null; | |||||
| return TextToSpeech.LANG_NOT_SUPPORTED; | return TextToSpeech.LANG_NOT_SUPPORTED; | ||||
| } else if (!hasCountry) { | |||||
| } else if (countryVoice == null) { | |||||
| mMatchingVoice = languageVoice; | |||||
| return TextToSpeech.LANG_AVAILABLE; | return TextToSpeech.LANG_AVAILABLE; | ||||
| } else { | } else { | ||||
| mMatchingVoice = countryVoice; | |||||
| return TextToSpeech.LANG_COUNTRY_AVAILABLE; | return TextToSpeech.LANG_COUNTRY_AVAILABLE; | ||||
| } | } | ||||
| } | } | ||||
| @Override | @Override | ||||
| final int result = onIsLanguageAvailable(language, country, variant); | final int result = onIsLanguageAvailable(language, country, variant); | ||||
| // Return immediately if the language is not available. | // Return immediately if the language is not available. | ||||
| if (result != TextToSpeech.LANG_AVAILABLE && result != TextToSpeech.LANG_COUNTRY_AVAILABLE | |||||
| && result != TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) { | |||||
| if (result == TextToSpeech.LANG_NOT_SUPPORTED) { | |||||
| Log.e(TAG, "Failed to load language {language='" + language + "', country='" + country | Log.e(TAG, "Failed to load language {language='" + language + "', country='" + country | ||||
| + "', variant='" + variant + "'"); | + "', variant='" + variant + "'"); | ||||
| return result; | return result; | ||||
| @Override | @Override | ||||
| protected synchronized void onSynthesizeText( | protected synchronized void onSynthesizeText( | ||||
| SynthesisRequest request, SynthesisCallback callback) { | SynthesisRequest request, SynthesisCallback callback) { | ||||
| final int result = onLoadLanguage(request.getLanguage(), request.getCountry(), request.getVariant()); | |||||
| // Return immediately if the language is not available. | |||||
| if (result == TextToSpeech.LANG_NOT_SUPPORTED) { | |||||
| return; | |||||
| } | |||||
| final String text = request.getText(); | final String text = request.getText(); | ||||
| final String language = getRequestLanguage(request); | |||||
| final int gender = getDefaultGender(); | final int gender = getDefaultGender(); | ||||
| final int rate = scaleRate(request.getSpeechRate()); | final int rate = scaleRate(request.getSpeechRate()); | ||||
| final int pitch = scalePitch(request.getPitch()); | final int pitch = scalePitch(request.getPitch()); | ||||
| final Bundle params = request.getParams(); | final Bundle params = request.getParams(); | ||||
| mLanguage = request.getLanguage(); | |||||
| mCountry = request.getCountry(); | |||||
| mVariant = request.getVariant(); | |||||
| if (DEBUG) { | if (DEBUG) { | ||||
| Log.i(TAG, "Received synthesis request: {language=\"" + language + "\"}"); | |||||
| Log.i(TAG, "Received synthesis request: {language=\"" + mMatchingVoice.name + "\"}"); | |||||
| for (String key : params.keySet()) { | for (String key : params.keySet()) { | ||||
| Log.v(TAG, | Log.v(TAG, | ||||
| mCallback.start(mEngine.getSampleRate(), mEngine.getAudioFormat(), | mCallback.start(mEngine.getSampleRate(), mEngine.getAudioFormat(), | ||||
| mEngine.getChannelCount()); | mEngine.getChannelCount()); | ||||
| mEngine.setVoiceByProperties(null, language, gender, 0, 0); | |||||
| mEngine.setVoiceByProperties(null, mMatchingVoice.name, gender, 0, 0); | |||||
| mEngine.setRate(rate); | mEngine.setRate(rate); | ||||
| mEngine.setPitch(pitch); | mEngine.setPitch(pitch); | ||||
| mEngine.synthesize(text); | mEngine.synthesize(text); | ||||
| return (rate * defaultRate / 100); | return (rate * defaultRate / 100); | ||||
| } | } | ||||
| /** | |||||
| * Retrieves the language code from a synthesis request. | |||||
| * | |||||
| * @param request The synthesis request. | |||||
| * @return A language code in the format "en-uk-n". | |||||
| */ | |||||
| private static String getRequestLanguage(SynthesisRequest request) { | |||||
| final StringBuffer result = new StringBuffer(request.getLanguage()); | |||||
| final String country = request.getCountry(); | |||||
| final String variant = request.getVariant(); | |||||
| if (!TextUtils.isEmpty(country)) { | |||||
| result.append('-'); | |||||
| result.append(country); | |||||
| } | |||||
| if (!TextUtils.isEmpty(variant)) { | |||||
| result.append('-'); | |||||
| result.append(variant); | |||||
| } | |||||
| return result.toString(); | |||||
| } | |||||
| /** | /** | ||||
| * Pipes synthesizer output from native eSpeak to an {@link AudioTrack}. | * Pipes synthesizer output from native eSpeak to an {@link AudioTrack}. | ||||
| */ | */ |