| @@ -107,6 +107,23 @@ public class SpeechSynthesis { | |||
| return (BUFFER_SIZE_IN_MILLIS * mSampleRate) / 1000; | |||
| } | |||
| private Locale getLocaleFromLanguageName(String name) { | |||
| if (mLocaleFixes.containsKey(name)) { | |||
| return mLocaleFixes.get(name); | |||
| } | |||
| String[] parts = name.split("-"); | |||
| switch (parts.length) { | |||
| case 1: // language | |||
| return new Locale(parts[0]); | |||
| case 2: // language-country | |||
| return new Locale(parts[0], parts[1]); | |||
| case 3: // language-country-variant | |||
| return new Locale(parts[0], parts[1], parts[2]); | |||
| default: | |||
| return null; | |||
| } | |||
| } | |||
| public List<Voice> getAvailableVoices() { | |||
| final List<Voice> voices = new LinkedList<Voice>(); | |||
| final String[] results = nativeGetAvailableVoices(); | |||
| @@ -117,72 +134,38 @@ public class SpeechSynthesis { | |||
| final String identifier = results[i + 1]; | |||
| final int gender = Integer.parseInt(results[i + 2]); | |||
| final int age = Integer.parseInt(results[i + 3]); | |||
| final Locale locale; | |||
| if (name.equals("fa-pin")) { | |||
| // Android locales do not support scripts, so fa-Latn is not possible for Farsi Pinglish: | |||
| locale = null; | |||
| } else if (identifier.equals("asia/fa-en-us")) { | |||
| locale = null; | |||
| } else if (name.equals("om")) { | |||
| // This is an experimental voice that is not currently well tested to be used. | |||
| if (identifier.equals("asia/fa-en-us")) { | |||
| locale = null; | |||
| } else if (name.equals("en-sc")) { | |||
| // 'SC' is not a country code. | |||
| locale = new Locale("en", "GB", "scotland"); | |||
| } else if (name.equals("en-wi")) { | |||
| // 'WI' is not a country code. Also, territory code 029 (Caribbean) | |||
| // is not supported by Android. | |||
| locale = new Locale("en", "JM"); | |||
| } else if (name.equals("es-la")) { | |||
| // 'LA' is the country code for Laos, not Latin America. Also, territory code 419 | |||
| // (Latin America) is not supported by Android. | |||
| locale = new Locale("es", "MX"); | |||
| } else if (name.equals("hy-west")) { | |||
| // 'west' is not a country code. | |||
| locale = null; | |||
| } else if (name.equals("vi-hue")) { | |||
| // 'hue' is for the Hue Province accent/dialect (Central Vietnamese). | |||
| locale = new Locale("vi", "VN", "hue"); | |||
| } else if (name.equals("vi-sgn")) { | |||
| // 'sgn' is for the Saigon accent/dialect (South Vietnamese). | |||
| locale = new Locale("vi", "VN", "saigon"); | |||
| } else if (name.equals("zh-yue")) { | |||
| // Android/Java does not support macrolanguages. | |||
| locale = new Locale("zh", "HK"); | |||
| } else { | |||
| String[] parts = name.split("-"); | |||
| switch (parts.length) { | |||
| case 1: // language | |||
| locale = new Locale(parts[0]); | |||
| break; | |||
| case 2: // language-country | |||
| if (parts[1].equals("uk")) { | |||
| // 'uk' is the language code for Ukranian, not Great Britain. | |||
| parts[1] = "GB"; | |||
| } | |||
| locale = new Locale(parts[0], parts[1]); | |||
| break; | |||
| case 3: // language-country-variant | |||
| if (parts[1].equals("uk")) { | |||
| // 'uk' is the language code for Ukranian, not Great Britain. | |||
| parts[1] = "GB"; | |||
| } | |||
| locale = new Locale(parts[0], parts[1], parts[2]); | |||
| break; | |||
| default: | |||
| locale = null; | |||
| } | |||
| locale = getLocaleFromLanguageName(name); | |||
| } | |||
| if (locale == null) { | |||
| Log.d(TAG, "getAvailableResources: skipping " + name + " => locale not supported"); | |||
| continue; | |||
| } | |||
| try { | |||
| if (locale != null && !locale.getISO3Language().equals("")) { | |||
| final Voice voice = new Voice(name, identifier, gender, age, locale); | |||
| voices.add(voice); | |||
| String language = locale.getISO3Language(); | |||
| if (language.equals("")) { | |||
| throw new IllegalArgumentException("Language '" + locale.getLanguage() + "' not supported."); | |||
| } | |||
| String country = locale.getISO3Country(); | |||
| if (country.equals("") && !locale.getCountry().equals("")) { | |||
| throw new IllegalArgumentException("Country '" + locale.getCountry() + "' not supported."); | |||
| } | |||
| final Voice voice = new Voice(name, identifier, gender, age, locale); | |||
| voices.add(voice); | |||
| } catch (MissingResourceException e) { | |||
| // Android 4.3 throws this exception if the 3-letter language | |||
| // (e.g. nci) or country (e.g. 021) code is missing for a locale. | |||
| // Earlier versions of Android return an empty string. | |||
| // Earlier versions return an empty string (handled above). | |||
| Log.d(TAG, "getAvailableResources: skipping " + name + " => " + e.getMessage()); | |||
| } catch (IllegalArgumentException e) { | |||
| Log.d(TAG, "getAvailableResources: skipping " + name + " => " + e.getMessage()); | |||
| } | |||
| } | |||
| @@ -374,6 +357,7 @@ public class SpeechSynthesis { | |||
| private static final Map<String, String> mJavaToIanaLanguageCode = new HashMap<String, String>(); | |||
| private static final Map<String, String> mJavaToIanaCountryCode = new HashMap<String, String>(); | |||
| private static final HashMap<String, Locale> mLocaleFixes = new HashMap<String, Locale>(); | |||
| static { | |||
| mJavaToIanaLanguageCode.put("afr", "af"); | |||
| mJavaToIanaLanguageCode.put("amh", "am"); | |||
| @@ -453,5 +437,19 @@ public class SpeechSynthesis { | |||
| mJavaToIanaCountryCode.put("PRT", "PT"); | |||
| mJavaToIanaCountryCode.put("USA", "US"); | |||
| mJavaToIanaCountryCode.put("VNM", "VN"); | |||
| // Map eSpeak locales to their correct BCP47 locales supported by Android: | |||
| mLocaleFixes.put("en-sc", new Locale("en", "GB", "scotland")); | |||
| mLocaleFixes.put("en-uk-north", new Locale("en", "GB", "north")); | |||
| mLocaleFixes.put("en-uk-rp", new Locale("en", "GB", "rp")); | |||
| mLocaleFixes.put("en-uk-wmids", new Locale("en", "GB", "wmids")); | |||
| mLocaleFixes.put("en-wi", new Locale("en", "JM")); | |||
| mLocaleFixes.put("es-la", new Locale("es", "MX")); | |||
| mLocaleFixes.put("fa-pin", null); // Script tags not supported. | |||
| mLocaleFixes.put("hy-west", null); // language-variant locales not supported. | |||
| mLocaleFixes.put("om", null); // This is an experimental voice that is not currently well tested to be used. | |||
| mLocaleFixes.put("vi-hue", new Locale("vi", "VN", "hue")); | |||
| mLocaleFixes.put("vi-sgn", new Locale("vi", "VN", "saigon")); | |||
| mLocaleFixes.put("zh-yue", new Locale("zh", "HK")); | |||
| } | |||
| } | |||