eSpeak NG is an open source speech synthesizer that supports more than hundred languages and accents.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SpeechSynthesisTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /*
  2. * Copyright (C) 2012-2015 Reece H. Dunn
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.reecedunn.espeak.test;
  17. import java.util.HashMap;
  18. import java.util.HashSet;
  19. import java.util.List;
  20. import java.util.Locale;
  21. import java.util.Map;
  22. import java.util.Set;
  23. import com.reecedunn.espeak.SpeechSynthesis;
  24. import com.reecedunn.espeak.Voice;
  25. import android.media.AudioFormat;
  26. import android.speech.tts.TextToSpeech;
  27. import android.util.Log;
  28. import static com.reecedunn.espeak.test.TtsMatcher.isTtsLangCode;
  29. import static org.hamcrest.MatcherAssert.assertThat;
  30. import static org.hamcrest.Matchers.*;
  31. import static org.hamcrest.core.AnyOf.anyOf;
  32. public class SpeechSynthesisTest extends TextToSpeechTestCase
  33. {
  34. public static final Locale af = new Locale("af"); // Afrikaans
  35. public static final Locale afr = new Locale("afr"); // Afrikaans
  36. public static final Locale de = new Locale("de"); // German
  37. public static final Locale de_DE = new Locale("de", "DE"); // German (Germany)
  38. public static final Locale de_1996 = new Locale("de", "", "1996"); // German (1996 Orthography)
  39. public static final Locale de_CH_1901 = new Locale("de", "CH", "1901"); // German (Traditional Orthography,Switzerland)
  40. public static final Locale deu = new Locale("deu"); // German
  41. public static final Locale deu_DEU = new Locale("deu", "DEU"); // German (Germany)
  42. public static final Locale deu_1996 = new Locale("deu", "", "1996"); // German (1996 Orthography)
  43. public static final Locale deu_CHE_1901 = new Locale("deu", "CHE", "1901"); // German (Traditional Orthography,Switzerland)
  44. public static final Locale fr = new Locale("fr"); // French
  45. public static final Locale fr_FR = new Locale("fr", "FR"); // French (France)
  46. public static final Locale fr_BE = new Locale("fr", "BE"); // French (Belgium)
  47. public static final Locale fr_1694acad = new Locale("fr", "", "1694acad"); // French (Early Modern French)
  48. public static final Locale fr_FR_1694acad = new Locale("fr", "FR", "1694acad"); // French (Early Modern French,France)
  49. public static final Locale fr_BE_1694acad = new Locale("fr", "BE", "1694acad"); // French (Early Modern French,Belgium)
  50. public static final Locale fra = new Locale("fra"); // French
  51. public static final Locale fra_FRA = new Locale("fra", "FRA"); // French (France)
  52. public static final Locale fra_BEL = new Locale("fra", "BEL"); // French (Belgium)
  53. public static final Locale fra_1694acad = new Locale("fra", "", "1694acad"); // French (Early Modern French)
  54. public static final Locale fra_FRA_1694acad = new Locale("fra", "FRA", "1694acad"); // French (Early Modern French,France)
  55. public static final Locale fra_BEL_1694acad = new Locale("fra", "BEL", "1694acad"); // French (Early Modern French,Belgium)
  56. public static final Locale hy = new Locale("hy"); // Armenian
  57. public static final Locale hy_AM = new Locale("hy", "AM"); // Armenian (Armenia)
  58. public static final Locale hy_arevela = new Locale("hy", "", "arevela"); // Armenian (Eastern)
  59. public static final Locale hy_arevmda = new Locale("hy", "", "arevmda"); // Armenian (Western)
  60. public static final Locale hy_AM_arevela = new Locale("hy", "AM", "arevela"); // Armenian (Eastern,Armenia)
  61. public static final Locale hy_AM_arevmda = new Locale("hy", "AM", "arevmda"); // Armenian (Western,Armenia)
  62. public static final Locale hye = new Locale("hye"); // Armenian
  63. public static final Locale hye_ARM = new Locale("hye", "ARM"); // Armenian (Armenia)
  64. public static final Locale hye_arevela = new Locale("hye", "", "arevela"); // Armenian (Eastern)
  65. public static final Locale hye_arevmda = new Locale("hye", "", "arevmda"); // Armenian (Western)
  66. public static final Locale hye_ARM_arevela = new Locale("hye", "ARM", "arevela"); // Armenian (Eastern,Armenia)
  67. public static final Locale hye_ARM_arevmda = new Locale("hye", "ARM", "arevmda"); // Armenian (Western,Armenia)
  68. public static final Locale en = new Locale("en"); // English
  69. public static final Locale en_GB = new Locale("en", "GB"); // English (Great Britain)
  70. public static final Locale en_US = new Locale("en", "US"); // English (USA)
  71. public static final Locale en_scotland = new Locale("en", "", "scotland"); // English (Scottish)
  72. public static final Locale en_GB_scotland = new Locale("en", "GB", "scotland"); // English (Scottish,Great Britain)
  73. public static final Locale en_GB_north = new Locale("en", "GB", "north"); // English (North,Great Britain)
  74. public static final Locale eng = new Locale("en"); // English
  75. public static final Locale eng_GBR = new Locale("en", "GBR"); // English (Great Britain)
  76. public static final Locale eng_USA = new Locale("en", "USA"); // English (USA)
  77. public static final Locale eng_scotland = new Locale("en", "", "scotland"); // English (Scottish)
  78. public static final Locale eng_GBR_scotland = new Locale("en", "GBR", "scotland"); // English (Scottish,Great Britain)
  79. public static final Locale eng_GBR_north = new Locale("en", "GBR", "north"); // English (North,Great Britain)
  80. private SpeechSynthesis.SynthReadyCallback mCallback = new SpeechSynthesis.SynthReadyCallback()
  81. {
  82. @Override
  83. public void onSynthDataReady(byte[] audioData)
  84. {
  85. }
  86. @Override
  87. public void onSynthDataComplete()
  88. {
  89. }
  90. };
  91. private Map<String, Voice> mVoices = null;
  92. private Set<String> mAdded = new HashSet<String>();
  93. private Set<String> mRemoved = new HashSet<String>();
  94. public Map<String, Voice> getVoices()
  95. {
  96. if (mVoices == null)
  97. {
  98. final SpeechSynthesis synth = new SpeechSynthesis(getContext(), mCallback);
  99. mVoices = new HashMap<String, Voice>();
  100. for (Voice voice : synth.getAvailableVoices()) {
  101. assertThat(mVoices.get(voice.name), is(nullValue()));
  102. mVoices.put(voice.name, voice);
  103. }
  104. assertThat(mVoices, is(notNullValue()));
  105. Set<String> voices = new HashSet<String>();
  106. for (Voice data : mVoices.values())
  107. {
  108. voices.add(data.name);
  109. }
  110. Set<String> expected = new HashSet<String>();
  111. for (VoiceData.Voice data : VoiceData.voices)
  112. {
  113. expected.add(data.name);
  114. }
  115. for (String voice : voices)
  116. {
  117. if (!expected.contains(voice))
  118. {
  119. mAdded.add(voice);
  120. }
  121. }
  122. for (String voice : expected)
  123. {
  124. if (!voices.contains(voice))
  125. {
  126. mRemoved.add(voice);
  127. }
  128. }
  129. }
  130. return mVoices;
  131. }
  132. public Voice getVoice(String name)
  133. {
  134. return getVoices().get(name);
  135. }
  136. public void testConstruction()
  137. {
  138. final SpeechSynthesis synth = new SpeechSynthesis(getContext(), mCallback);
  139. assertThat(synth.getSampleRate(), is(22050));
  140. assertThat(synth.getChannelCount(), is(1));
  141. assertThat(synth.getAudioFormat(), is(AudioFormat.ENCODING_PCM_16BIT));
  142. }
  143. public void testJavaToIanaLanguageCode()
  144. {
  145. for (VoiceData.Voice data : VoiceData.voices)
  146. {
  147. assertThat(SpeechSynthesis.getIanaLanguageCode(data.javaLanguage), is(data.ianaLanguage));
  148. }
  149. }
  150. public void testJavaToIanaCountryCode()
  151. {
  152. for (VoiceData.Voice data : VoiceData.voices)
  153. {
  154. assertThat(SpeechSynthesis.getIanaCountryCode(data.javaCountry), is(data.ianaCountry));
  155. }
  156. }
  157. public void testAddedVoices()
  158. {
  159. getVoices(); // Ensure that the voice data has been populated.
  160. assertThat(mAdded.toString(), is("[]"));
  161. }
  162. public void testRemovedVoices()
  163. {
  164. getVoices(); // Ensure that the voice data has been populated.
  165. assertThat(mRemoved.toString(), is("[]"));
  166. }
  167. public void testVoiceData()
  168. {
  169. for (VoiceData.Voice data : VoiceData.voices)
  170. {
  171. if (mRemoved.contains(data.name))
  172. {
  173. Log.i("SpeechSynthesisTest", "Skipping the missing voice '" + data.name + "'");
  174. continue;
  175. }
  176. String context = "[voice]";
  177. try
  178. {
  179. final Voice voice = getVoice(data.name);
  180. assertThat(voice, is(notNullValue()));
  181. context = "[name]";
  182. assertThat(voice.name, is(data.name));
  183. context = "[identifier]";
  184. assertThat(voice.identifier, is(data.identifier));
  185. context = "[age]";
  186. assertThat(voice.age, is(0));
  187. context = "[gender]";
  188. assertThat(voice.gender, is(data.gender));
  189. context = "[locale:language]";
  190. assertThat(voice.locale.getLanguage(), is(data.ianaLanguage));
  191. context = "[locale:iso3language]";
  192. assertThat(voice.locale.getISO3Language(), is(data.javaLanguage));
  193. context = "[locale:country]";
  194. assertThat(voice.locale.getCountry(), is(data.ianaCountry));
  195. context = "[locale:iso3country]";
  196. assertThat(voice.locale.getISO3Country(), is(data.javaCountry));
  197. context = "[locale:variant]";
  198. assertThat(voice.locale.getVariant(), is(data.variant));
  199. context = "[toString]";
  200. assertThat(voice.toString(), is(data.locale));
  201. }
  202. catch (AssertionError e)
  203. {
  204. throw new VoiceData.Exception(data, context, e);
  205. }
  206. }
  207. }
  208. public void testMatchVoiceWithLanguage()
  209. {
  210. final Voice voice = getVoice("de"); // language="de" country="" variant=""
  211. assertThat(voice, is(notNullValue()));
  212. assertThat(voice.match(fr), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  213. assertThat(voice.match(fr_BE), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  214. assertThat(voice.match(fr_1694acad), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  215. assertThat(voice.match(fr_FR_1694acad), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  216. assertThat(voice.match(de), isTtsLangCode(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  217. assertThat(voice.match(de_1996), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  218. assertThat(voice.match(de_DE), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  219. assertThat(voice.match(de_CH_1901), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  220. assertThat(voice.match(deu), isTtsLangCode(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  221. assertThat(voice.match(deu_1996), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  222. assertThat(voice.match(deu_DEU), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  223. assertThat(voice.match(deu_CHE_1901), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  224. }
  225. public void testMatchVoiceWithLanguageAndCountry()
  226. {
  227. final Voice voice = getVoice("fr-be"); // language="fr" country="BE" variant=""
  228. assertThat(voice, is(notNullValue()));
  229. assertThat(voice.match(de), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  230. assertThat(voice.match(de_1996), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  231. assertThat(voice.match(de_DE), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  232. assertThat(voice.match(de_CH_1901), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  233. assertThat(voice.match(fr), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  234. assertThat(voice.match(fr_FR), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  235. assertThat(voice.match(fr_BE), isTtsLangCode(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  236. assertThat(voice.match(fr_1694acad), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  237. assertThat(voice.match(fr_FR_1694acad), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  238. assertThat(voice.match(fr_BE_1694acad), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  239. assertThat(voice.match(fra), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  240. assertThat(voice.match(fra_FRA), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  241. assertThat(voice.match(fra_BEL), isTtsLangCode(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  242. assertThat(voice.match(fra_1694acad), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  243. assertThat(voice.match(fra_FRA_1694acad), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  244. assertThat(voice.match(fra_BEL_1694acad), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  245. }
  246. public void testMatchVoiceWithLanguageCountryAndVariant()
  247. {
  248. final Voice voice = getVoice("en-gb-scotland"); // language="en" country="GB" variant="scotland"
  249. assertThat(voice, is(notNullValue()));
  250. assertThat(voice.match(de), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  251. assertThat(voice.match(de_1996), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  252. assertThat(voice.match(de_DE), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  253. assertThat(voice.match(de_CH_1901), isTtsLangCode(TextToSpeech.LANG_NOT_SUPPORTED));
  254. assertThat(voice.match(en), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  255. assertThat(voice.match(en_GB), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  256. assertThat(voice.match(en_US), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  257. assertThat(voice.match(en_scotland), isTtsLangCode(TextToSpeech.LANG_AVAILABLE)); // NOTE: Android does not support LANG_VAR_AVAILABLE.
  258. assertThat(voice.match(en_GB_scotland), isTtsLangCode(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  259. assertThat(voice.match(en_GB_north), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  260. assertThat(voice.match(eng), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  261. assertThat(voice.match(eng_GBR), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  262. assertThat(voice.match(eng_USA), isTtsLangCode(TextToSpeech.LANG_AVAILABLE));
  263. assertThat(voice.match(eng_scotland), isTtsLangCode(TextToSpeech.LANG_AVAILABLE)); // NOTE: Android does not support LANG_VAR_AVAILABLE.
  264. assertThat(voice.match(eng_GBR_scotland), isTtsLangCode(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  265. assertThat(voice.match(eng_GBR_north), isTtsLangCode(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  266. }
  267. public void testGetSampleText()
  268. {
  269. final String[] currentLocales = getContext().getResources().getAssets().getLocales();
  270. for (VoiceData.Voice data : VoiceData.voices)
  271. {
  272. if (mRemoved.contains(data.name))
  273. {
  274. Log.i("SpeechSynthesisTest", "Skipping the missing voice '" + data.name + "'");
  275. continue;
  276. }
  277. String context = null;
  278. try
  279. {
  280. final Locale ianaLocale = new Locale(data.ianaLanguage, data.ianaCountry, data.variant);
  281. context = "[iana:sample-text]";
  282. assertThat(SpeechSynthesis.getSampleText(getContext(), ianaLocale), isIn(data.sampleText));
  283. context = "[iana:resource-locale]";
  284. assertThat(getContext().getResources().getAssets().getLocales(), is(currentLocales));
  285. if (!data.javaLanguage.equals(""))
  286. {
  287. final Locale javaLocale = new Locale(data.javaLanguage, data.javaCountry, data.variant);
  288. context = "[java:sample-text]";
  289. assertThat(SpeechSynthesis.getSampleText(getContext(), javaLocale), isIn(data.sampleText));
  290. context = "[java:resource-locale]";
  291. assertThat(getContext().getResources().getAssets().getLocales(), is(currentLocales));
  292. }
  293. }
  294. catch (AssertionError e)
  295. {
  296. throw new VoiceData.Exception(data, context, e);
  297. }
  298. }
  299. }
  300. }