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 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /*
  2. * Copyright (C) 2012 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.io.File;
  18. import java.util.HashSet;
  19. import java.util.List;
  20. import java.util.Locale;
  21. import java.util.Set;
  22. import com.reecedunn.espeak.SpeechSynthesis;
  23. import com.reecedunn.espeak.SpeechSynthesis.Voice;
  24. import android.content.Intent;
  25. import android.content.pm.ActivityInfo;
  26. import android.content.pm.PackageManager;
  27. import android.content.pm.ResolveInfo;
  28. import android.media.AudioFormat;
  29. import android.speech.tts.TextToSpeech;
  30. import android.test.AndroidTestCase;
  31. import android.util.Log;
  32. import static org.hamcrest.MatcherAssert.assertThat;
  33. import static org.hamcrest.Matchers.*;
  34. public class SpeechSynthesisTest extends AndroidTestCase
  35. {
  36. public static final Locale af = new Locale("af"); // Afrikaans
  37. public static final Locale afr = new Locale("afr"); // Afrikaans
  38. public static final Locale de = new Locale("de"); // German
  39. public static final Locale de_DE = new Locale("de", "DE"); // German (Germany)
  40. public static final Locale de_1996 = new Locale("de", "", "1996"); // German (1996 Orthography)
  41. public static final Locale de_CH_1901 = new Locale("de", "CH", "1901"); // German (Traditional Orthography,Switzerland)
  42. public static final Locale deu = new Locale("deu"); // German
  43. public static final Locale deu_DEU = new Locale("deu", "DEU"); // German (Germany)
  44. public static final Locale deu_1996 = new Locale("deu", "", "1996"); // German (1996 Orthography)
  45. public static final Locale deu_CHE_1901 = new Locale("deu", "CHE", "1901"); // German (Traditional Orthography,Switzerland)
  46. public static final Locale fr = new Locale("fr"); // French
  47. public static final Locale fr_FR = new Locale("fr", "FR"); // French (France)
  48. public static final Locale fr_BE = new Locale("fr", "BE"); // French (Belgium)
  49. public static final Locale fr_1694acad = new Locale("fr", "", "1694acad"); // French (Early Modern French)
  50. public static final Locale fr_FR_1694acad = new Locale("fr", "FR", "1694acad"); // French (Early Modern French,France)
  51. public static final Locale fr_BE_1694acad = new Locale("fr", "BE", "1694acad"); // French (Early Modern French,Belgium)
  52. public static final Locale fra = new Locale("fra"); // French
  53. public static final Locale fra_FRA = new Locale("fra", "FRA"); // French (France)
  54. public static final Locale fra_BEL = new Locale("fra", "BEL"); // French (Belgium)
  55. public static final Locale fra_1694acad = new Locale("fra", "", "1694acad"); // French (Early Modern French)
  56. public static final Locale fra_FRA_1694acad = new Locale("fra", "FRA", "1694acad"); // French (Early Modern French,France)
  57. public static final Locale fra_BEL_1694acad = new Locale("fra", "BEL", "1694acad"); // French (Early Modern French,Belgium)
  58. public static final Locale hy = new Locale("hy"); // Armenian
  59. public static final Locale hy_AM = new Locale("hy", "AM"); // Armenian (Armenia)
  60. public static final Locale hy_arevela = new Locale("hy", "", "arevela"); // Armenian (Eastern)
  61. public static final Locale hy_arevmda = new Locale("hy", "", "arevmda"); // Armenian (Western)
  62. public static final Locale hy_AM_arevela = new Locale("hy", "AM", "arevela"); // Armenian (Eastern,Armenia)
  63. public static final Locale hy_AM_arevmda = new Locale("hy", "AM", "arevmda"); // Armenian (Western,Armenia)
  64. public static final Locale hye = new Locale("hye"); // Armenian
  65. public static final Locale hye_ARM = new Locale("hye", "ARM"); // Armenian (Armenia)
  66. public static final Locale hye_arevela = new Locale("hye", "", "arevela"); // Armenian (Eastern)
  67. public static final Locale hye_arevmda = new Locale("hye", "", "arevmda"); // Armenian (Western)
  68. public static final Locale hye_ARM_arevela = new Locale("hye", "ARM", "arevela"); // Armenian (Eastern,Armenia)
  69. public static final Locale hye_ARM_arevmda = new Locale("hye", "ARM", "arevmda"); // Armenian (Western,Armenia)
  70. public static final Locale en = new Locale("en"); // English
  71. public static final Locale en_GB = new Locale("en", "GB"); // English (Great Britain)
  72. public static final Locale en_US = new Locale("en", "US"); // English (USA)
  73. public static final Locale en_scotland = new Locale("en", "", "scotland"); // English (Scottish)
  74. public static final Locale en_GB_scotland = new Locale("en", "GB", "scotland"); // English (Scottish,Great Britain)
  75. public static final Locale en_GB_north = new Locale("en", "GB", "north"); // English (North,Great Britain)
  76. public static final Locale eng = new Locale("en"); // English
  77. public static final Locale eng_GBR = new Locale("en", "GBR"); // English (Great Britain)
  78. public static final Locale eng_USA = new Locale("en", "USA"); // English (USA)
  79. public static final Locale eng_scotland = new Locale("en", "", "scotland"); // English (Scottish)
  80. public static final Locale eng_GBR_scotland = new Locale("en", "GBR", "scotland"); // English (Scottish,Great Britain)
  81. public static final Locale eng_GBR_north = new Locale("en", "GBR", "north"); // English (North,Great Britain)
  82. private SpeechSynthesis.SynthReadyCallback mCallback = new SpeechSynthesis.SynthReadyCallback()
  83. {
  84. @Override
  85. public void onSynthDataReady(byte[] audioData)
  86. {
  87. }
  88. @Override
  89. public void onSynthDataComplete()
  90. {
  91. }
  92. };
  93. private List<Voice> mVoices = null;
  94. private Set<String> mAdded = new HashSet<String>();
  95. private Set<String> mRemoved = new HashSet<String>();
  96. public List<Voice> getVoices()
  97. {
  98. if (mVoices == null)
  99. {
  100. final SpeechSynthesis synth = new SpeechSynthesis(getContext(), mCallback);
  101. mVoices = synth.getAvailableVoices();
  102. assertThat(mVoices, is(notNullValue()));
  103. Set<String> voices = new HashSet<String>();
  104. for (Voice data : mVoices)
  105. {
  106. voices.add(data.name);
  107. }
  108. Set<String> expected = new HashSet<String>();
  109. for (VoiceData.Voice data : VoiceData.voices)
  110. {
  111. expected.add(data.name);
  112. }
  113. for (String voice : voices)
  114. {
  115. if (!expected.contains(voice))
  116. {
  117. mAdded.add(voice);
  118. }
  119. }
  120. for (String voice : expected)
  121. {
  122. if (!voices.contains(voice))
  123. {
  124. mRemoved.add(voice);
  125. }
  126. }
  127. }
  128. return mVoices;
  129. }
  130. public Voice getVoice(String name)
  131. {
  132. for (Voice voice : getVoices())
  133. {
  134. if (voice.name.equals(name))
  135. {
  136. return voice;
  137. }
  138. }
  139. return null;
  140. }
  141. /**
  142. * This tests that the location of the espeak TTS shared object matches
  143. * the location that Android 2.2 looks for it in.
  144. */
  145. public void testSharedObjectLocation()
  146. {
  147. Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
  148. intent.setPackage("com.reecedunn.espeak");
  149. PackageManager pm = getContext().getPackageManager();
  150. List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
  151. assertThat(resolveInfos, is(notNullValue()));
  152. assertThat(resolveInfos.isEmpty(), is(false));
  153. ResolveInfo[] enginesArray = resolveInfos.toArray(new ResolveInfo[0]);
  154. ActivityInfo aInfo = enginesArray[0].activityInfo;
  155. String soFilename = aInfo.name.replace(aInfo.packageName + ".", "") + ".so";
  156. soFilename = soFilename.toLowerCase();
  157. assertThat(soFilename, is("espeak.so"));
  158. soFilename = "/data/data/" + aInfo.packageName + "/lib/libtts" + soFilename;
  159. assertThat(soFilename, is("/data/data/com.reecedunn.espeak/lib/libttsespeak.so"));
  160. File f = new File(soFilename);
  161. assertThat(f.exists(), is(true));
  162. }
  163. public void testConstruction()
  164. {
  165. final SpeechSynthesis synth = new SpeechSynthesis(getContext(), mCallback);
  166. assertThat(synth.getSampleRate(), is(22050));
  167. assertThat(synth.getChannelCount(), is(1));
  168. assertThat(synth.getAudioFormat(), is(AudioFormat.ENCODING_PCM_16BIT));
  169. assertThat(synth.getBufferSizeInBytes(), is(22050));
  170. }
  171. public void testAddedVoices()
  172. {
  173. getVoices(); // Ensure that the voice data has been populated.
  174. assertThat(mAdded.toString(), is("[]"));
  175. }
  176. public void testRemovedVoices()
  177. {
  178. getVoices(); // Ensure that the voice data has been populated.
  179. assertThat(mRemoved.toString(), is("[]"));
  180. }
  181. public void testVoiceData()
  182. {
  183. for (VoiceData.Voice data : VoiceData.voices)
  184. {
  185. if (mRemoved.contains(data.name))
  186. {
  187. Log.i("SpeechSynthesisTest", "Skipping the missing voice '" + data.name + "'");
  188. continue;
  189. }
  190. try
  191. {
  192. final Voice voice = getVoice(data.name);
  193. assertThat(voice, is(notNullValue()));
  194. assertThat(voice.name, is(data.name));
  195. assertThat(voice.identifier, is(data.identifier));
  196. assertThat(voice.age, is(0));
  197. assertThat(voice.gender, is(data.gender));
  198. assertThat(voice.locale.getLanguage(), is(data.ianaLanguage));
  199. assertThat(voice.locale.getISO3Language(), is(data.javaLanguage));
  200. assertThat(voice.locale.getCountry(), is(data.ianaCountry));
  201. assertThat(voice.locale.getISO3Country(), is(data.javaCountry));
  202. assertThat(voice.locale.getVariant(), is(data.variant));
  203. assertThat(voice.toString(), is(data.locale));
  204. }
  205. catch (AssertionError e)
  206. {
  207. throw new VoiceData.Exception(data, e);
  208. }
  209. }
  210. }
  211. public void testMatchVoiceWithLanguage()
  212. {
  213. final Voice voice = getVoice("de"); // language="de" country="" variant=""
  214. assertThat(voice, is(notNullValue()));
  215. assertThat(voice.match(fr), is(TextToSpeech.LANG_NOT_SUPPORTED));
  216. assertThat(voice.match(fr_BE), is(TextToSpeech.LANG_NOT_SUPPORTED));
  217. assertThat(voice.match(fr_1694acad), is(TextToSpeech.LANG_NOT_SUPPORTED));
  218. assertThat(voice.match(fr_FR_1694acad), is(TextToSpeech.LANG_NOT_SUPPORTED));
  219. assertThat(voice.match(de), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  220. assertThat(voice.match(de_1996), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  221. assertThat(voice.match(de_DE), is(TextToSpeech.LANG_AVAILABLE));
  222. assertThat(voice.match(de_CH_1901), is(TextToSpeech.LANG_AVAILABLE));
  223. assertThat(voice.match(deu), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  224. assertThat(voice.match(deu_1996), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  225. assertThat(voice.match(deu_DEU), is(TextToSpeech.LANG_AVAILABLE));
  226. assertThat(voice.match(deu_CHE_1901), is(TextToSpeech.LANG_AVAILABLE));
  227. }
  228. public void testMatchVoiceWithLanguageAndCountry()
  229. {
  230. final Voice voice = getVoice("fr-fr"); // language="fr" country="fr" variant=""
  231. assertThat(voice, is(notNullValue()));
  232. assertThat(voice.match(de), is(TextToSpeech.LANG_NOT_SUPPORTED));
  233. assertThat(voice.match(de_1996), is(TextToSpeech.LANG_NOT_SUPPORTED));
  234. assertThat(voice.match(de_DE), is(TextToSpeech.LANG_NOT_SUPPORTED));
  235. assertThat(voice.match(de_CH_1901), is(TextToSpeech.LANG_NOT_SUPPORTED));
  236. assertThat(voice.match(fr), is(TextToSpeech.LANG_AVAILABLE));
  237. assertThat(voice.match(fr_FR), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  238. assertThat(voice.match(fr_BE), is(TextToSpeech.LANG_AVAILABLE));
  239. assertThat(voice.match(fr_1694acad), is(TextToSpeech.LANG_AVAILABLE));
  240. assertThat(voice.match(fr_FR_1694acad), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  241. assertThat(voice.match(fr_BE_1694acad), is(TextToSpeech.LANG_AVAILABLE));
  242. assertThat(voice.match(fra), is(TextToSpeech.LANG_AVAILABLE));
  243. assertThat(voice.match(fra_FRA), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  244. assertThat(voice.match(fra_BEL), is(TextToSpeech.LANG_AVAILABLE));
  245. assertThat(voice.match(fra_1694acad), is(TextToSpeech.LANG_AVAILABLE));
  246. assertThat(voice.match(fra_FRA_1694acad), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  247. assertThat(voice.match(fra_BEL_1694acad), is(TextToSpeech.LANG_AVAILABLE));
  248. }
  249. public void testMatchVoiceWithLanguageAndVariant()
  250. {
  251. final Voice voice = getVoice("hy-west"); // language="hy" country="" variant="arevmda"
  252. assertThat(voice, is(notNullValue()));
  253. assertThat(voice.match(fr), is(TextToSpeech.LANG_NOT_SUPPORTED));
  254. assertThat(voice.match(fr_BE), is(TextToSpeech.LANG_NOT_SUPPORTED));
  255. assertThat(voice.match(fr_1694acad), is(TextToSpeech.LANG_NOT_SUPPORTED));
  256. assertThat(voice.match(fr_FR_1694acad), is(TextToSpeech.LANG_NOT_SUPPORTED));
  257. assertThat(voice.match(hy), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  258. assertThat(voice.match(hy_AM), is(TextToSpeech.LANG_AVAILABLE));
  259. assertThat(voice.match(hy_arevela), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  260. assertThat(voice.match(hy_arevmda), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  261. assertThat(voice.match(hy_AM_arevela), is(TextToSpeech.LANG_AVAILABLE));
  262. assertThat(voice.match(hy_AM_arevmda), is(TextToSpeech.LANG_AVAILABLE)); // NOTE: Android does not support LANG_VAR_AVAILABLE.
  263. assertThat(voice.match(hye), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  264. assertThat(voice.match(hye_ARM), is(TextToSpeech.LANG_AVAILABLE));
  265. assertThat(voice.match(hye_arevela), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  266. assertThat(voice.match(hye_arevmda), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  267. assertThat(voice.match(hye_ARM_arevela), is(TextToSpeech.LANG_AVAILABLE));
  268. assertThat(voice.match(hye_ARM_arevmda), is(TextToSpeech.LANG_AVAILABLE)); // NOTE: Android does not support LANG_VAR_AVAILABLE.
  269. }
  270. public void testMatchVoiceWithLanguageCountryAndVariant()
  271. {
  272. final Voice voice = getVoice("en-sc"); // language="en" country="GB" variant="scotland"
  273. assertThat(voice, is(notNullValue()));
  274. assertThat(voice.match(de), is(TextToSpeech.LANG_NOT_SUPPORTED));
  275. assertThat(voice.match(de_1996), is(TextToSpeech.LANG_NOT_SUPPORTED));
  276. assertThat(voice.match(de_DE), is(TextToSpeech.LANG_NOT_SUPPORTED));
  277. assertThat(voice.match(de_CH_1901), is(TextToSpeech.LANG_NOT_SUPPORTED));
  278. assertThat(voice.match(en), is(TextToSpeech.LANG_AVAILABLE));
  279. assertThat(voice.match(en_GB), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  280. assertThat(voice.match(en_US), is(TextToSpeech.LANG_AVAILABLE));
  281. assertThat(voice.match(en_scotland), is(TextToSpeech.LANG_AVAILABLE)); // NOTE: Android does not support LANG_VAR_AVAILABLE.
  282. assertThat(voice.match(en_GB_scotland), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  283. assertThat(voice.match(en_GB_north), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  284. assertThat(voice.match(eng), is(TextToSpeech.LANG_AVAILABLE));
  285. assertThat(voice.match(eng_GBR), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  286. assertThat(voice.match(eng_USA), is(TextToSpeech.LANG_AVAILABLE));
  287. assertThat(voice.match(eng_scotland), is(TextToSpeech.LANG_AVAILABLE)); // NOTE: Android does not support LANG_VAR_AVAILABLE.
  288. assertThat(voice.match(eng_GBR_scotland), is(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE));
  289. assertThat(voice.match(eng_GBR_north), is(TextToSpeech.LANG_COUNTRY_AVAILABLE));
  290. }
  291. public void testGetSampleText()
  292. {
  293. for (VoiceData.Voice data : VoiceData.voices)
  294. {
  295. if (mRemoved.contains(data.name))
  296. {
  297. Log.i("SpeechSynthesisTest", "Skipping the missing voice '" + data.name + "'");
  298. continue;
  299. }
  300. try
  301. {
  302. final Locale ianaLocale = new Locale(data.ianaLanguage, data.ianaCountry, data.variant);
  303. assertThat(SpeechSynthesis.getSampleText(getContext(), ianaLocale), is(data.sampleText));
  304. if (!data.javaLanguage.equals(""))
  305. {
  306. final Locale javaLocale = new Locale(data.javaLanguage, data.javaCountry, data.variant);
  307. assertThat(SpeechSynthesis.getSampleText(getContext(), javaLocale), is(data.sampleText));
  308. }
  309. }
  310. catch (AssertionError e)
  311. {
  312. throw new VoiceData.Exception(data, e);
  313. }
  314. }
  315. }
  316. }