| return (BUFFER_SIZE_IN_MILLIS * mSampleRate) / 1000; | 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() { | public List<Voice> getAvailableVoices() { | ||||
| final List<Voice> voices = new LinkedList<Voice>(); | final List<Voice> voices = new LinkedList<Voice>(); | ||||
| final String[] results = nativeGetAvailableVoices(); | final String[] results = nativeGetAvailableVoices(); | ||||
| final String identifier = results[i + 1]; | final String identifier = results[i + 1]; | ||||
| final int gender = Integer.parseInt(results[i + 2]); | final int gender = Integer.parseInt(results[i + 2]); | ||||
| final int age = Integer.parseInt(results[i + 3]); | final int age = Integer.parseInt(results[i + 3]); | ||||
| final Locale locale; | 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; | 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 { | } 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 { | 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) { | } catch (MissingResourceException e) { | ||||
| // Android 4.3 throws this exception if the 3-letter language | // 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. | // (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()); | |||||
| } | } | ||||
| } | } | ||||
| private static final Map<String, String> mJavaToIanaLanguageCode = new HashMap<String, String>(); | 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 Map<String, String> mJavaToIanaCountryCode = new HashMap<String, String>(); | ||||
| private static final HashMap<String, Locale> mLocaleFixes = new HashMap<String, Locale>(); | |||||
| static { | static { | ||||
| mJavaToIanaLanguageCode.put("afr", "af"); | mJavaToIanaLanguageCode.put("afr", "af"); | ||||
| mJavaToIanaLanguageCode.put("amh", "am"); | mJavaToIanaLanguageCode.put("amh", "am"); | ||||
| mJavaToIanaCountryCode.put("PRT", "PT"); | mJavaToIanaCountryCode.put("PRT", "PT"); | ||||
| mJavaToIanaCountryCode.put("USA", "US"); | mJavaToIanaCountryCode.put("USA", "US"); | ||||
| mJavaToIanaCountryCode.put("VNM", "VN"); | 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")); | |||||
| } | } | ||||
| } | } |