/androidsays/src/com/google/marvin/androidsays/SfxController.java

http://eyes-free.googlecode.com/ · Java · 281 lines · 224 code · 24 blank · 33 comment · 55 complexity · 68e4d197b0d786e5d50aca3f6bda2e0a MD5 · raw file

  1. /*
  2. * Copyright (C) 2008 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * 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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.google.marvin.androidsays;
  17. import android.content.Context;
  18. import android.media.MediaPlayer;
  19. import android.media.MediaPlayer.OnCompletionListener;
  20. import android.net.Uri;
  21. import java.util.ArrayList;
  22. import java.util.HashMap;
  23. import java.util.concurrent.locks.ReentrantLock;
  24. /**
  25. * Controls the playback of sound effects.
  26. *
  27. * @author clchen@google.com (Charles L. Chen)
  28. */
  29. public class SfxController implements OnCompletionListener {
  30. private ArrayList<String> soundQueue;
  31. private MediaPlayer player;
  32. private HashMap<String, SoundResource> sounds;
  33. private boolean isPlaying;
  34. private Context ctx;
  35. private final ReentrantLock soundQueueLock = new ReentrantLock();
  36. public SfxController(Context contextObject) {
  37. ctx = contextObject;
  38. soundQueue = new ArrayList<String>();
  39. sounds = new HashMap<String, SoundResource>();
  40. player = null;
  41. isPlaying = false;
  42. sounds.put("[right]", new SoundResource(R.raw.right_snd));
  43. sounds.put("[wrong]", new SoundResource(R.raw.wrong_snd));
  44. sounds.put("Game over. Your score is:", new SoundResource(R.raw.game_over));
  45. sounds.put("Android says:", new SoundResource(R.raw.android_says));
  46. sounds.put("0", new SoundResource(R.raw.num0_robot_));
  47. sounds.put("1", new SoundResource(R.raw.num1_robot_));
  48. sounds.put("2", new SoundResource(R.raw.num2_robot_));
  49. sounds.put("3", new SoundResource(R.raw.num3_robot_));
  50. sounds.put("4", new SoundResource(R.raw.num4_robot_));
  51. sounds.put("5", new SoundResource(R.raw.num5_robot_));
  52. sounds.put("6", new SoundResource(R.raw.num6_robot_));
  53. sounds.put("7", new SoundResource(R.raw.num7_robot_));
  54. sounds.put("8", new SoundResource(R.raw.num8_robot_));
  55. sounds.put("9", new SoundResource(R.raw.num9_robot_));
  56. sounds.put("10", new SoundResource(R.raw.num10_robot_));
  57. sounds.put("11", new SoundResource(R.raw.num11_robot_));
  58. sounds.put("12", new SoundResource(R.raw.num12_robot_));
  59. sounds.put("13", new SoundResource(R.raw.num13_robot_));
  60. sounds.put("14", new SoundResource(R.raw.num14_robot_));
  61. sounds.put("15", new SoundResource(R.raw.num15_robot_));
  62. sounds.put("16", new SoundResource(R.raw.num16_robot_));
  63. sounds.put("17", new SoundResource(R.raw.num17_robot_));
  64. sounds.put("18", new SoundResource(R.raw.num18_robot_));
  65. sounds.put("19", new SoundResource(R.raw.num19_robot_));
  66. sounds.put("20", new SoundResource(R.raw.num20_robot_));
  67. sounds.put("30", new SoundResource(R.raw.num30_robot_));
  68. sounds.put("40", new SoundResource(R.raw.num40_robot_));
  69. sounds.put("50", new SoundResource(R.raw.num50_robot_));
  70. sounds.put("60", new SoundResource(R.raw.num60_robot_));
  71. sounds.put("70", new SoundResource(R.raw.num70_robot_));
  72. sounds.put("80", new SoundResource(R.raw.num80_robot_));
  73. sounds.put("90", new SoundResource(R.raw.num90_robot_));
  74. sounds.put("hundred", new SoundResource(R.raw.hundred_robot_));
  75. sounds.put("[slnc]", new SoundResource(R.raw.slnc_snd));
  76. sounds.put("[red]", new SoundResource(R.raw.red_snd));
  77. sounds.put("[green]", new SoundResource(R.raw.green_snd));
  78. sounds.put("[yellow]", new SoundResource(R.raw.yellow_snd));
  79. sounds.put("[blue]", new SoundResource(R.raw.blue_snd));
  80. }
  81. public void play(String soundName, int queueMode) {
  82. if (!sounds.containsKey(soundName)) {
  83. if (soundName.length() > 1) {
  84. // Flush the queue first if needed
  85. if (queueMode == 0) {
  86. play("", 0);
  87. }
  88. // Decompose this into a number if possible.
  89. // Remove this once full-fledged TTS is available.
  90. if (spokenAsNumber(soundName)) {
  91. return;
  92. }
  93. for (int i = 0; i < soundName.length(); i++) {
  94. String currentCharacter = soundName.substring(i, i + 1);
  95. if (currentCharacter.length() == 1) {
  96. play(currentCharacter, 1);
  97. }
  98. }
  99. }
  100. return;
  101. }
  102. if (isPlaying && (queueMode == 0)) {
  103. stop();
  104. }
  105. soundQueue.add(soundName);
  106. if (!isPlaying) {
  107. processSoundQueue();
  108. }
  109. }
  110. // Special algorithm to decompose numbers into speakable parts.
  111. // This will handle positive numbers up to 999.
  112. private boolean spokenAsNumber(String text) {
  113. try {
  114. int number = Integer.parseInt(text);
  115. // Handle cases that are between 100 and 999, inclusive
  116. if ((number > 99) && (number < 1000)) {
  117. int remainder = number % 100;
  118. number = number / 100;
  119. play(Integer.toString(number), 1);
  120. play("[slnc]", 1);
  121. play("[slnc]", 1);
  122. play("hundred", 1);
  123. play("[slnc]", 1);
  124. play("[slnc]", 1);
  125. if (remainder > 0) {
  126. play(Integer.toString(remainder), 1);
  127. }
  128. return true;
  129. }
  130. // Handle cases that are less than 100
  131. int digit = 0;
  132. if ((number > 20) && (number < 100)) {
  133. if ((number > 20) && (number < 30)) {
  134. play(Integer.toString(20), 1);
  135. play("[slnc]", 1);
  136. play("[slnc]", 1);
  137. digit = number - 20;
  138. } else if ((number > 30) && (number < 40)) {
  139. play(Integer.toString(30), 1);
  140. play("[slnc]", 1);
  141. play("[slnc]", 1);
  142. digit = number - 30;
  143. } else if ((number > 40) && (number < 50)) {
  144. play(Integer.toString(40), 1);
  145. play("[slnc]", 1);
  146. play("[slnc]", 1);
  147. digit = number - 40;
  148. } else if ((number > 50) && (number < 60)) {
  149. play(Integer.toString(50), 1);
  150. play("[slnc]", 1);
  151. play("[slnc]", 1);
  152. digit = number - 50;
  153. } else if ((number > 60) && (number < 70)) {
  154. play(Integer.toString(60), 1);
  155. play("[slnc]", 1);
  156. play("[slnc]", 1);
  157. digit = number - 60;
  158. } else if ((number > 70) && (number < 80)) {
  159. play(Integer.toString(70), 1);
  160. play("[slnc]", 1);
  161. play("[slnc]", 1);
  162. digit = number - 70;
  163. } else if ((number > 80) && (number < 90)) {
  164. play(Integer.toString(80), 1);
  165. play("[slnc]", 1);
  166. play("[slnc]", 1);
  167. digit = number - 80;
  168. } else if ((number > 90) && (number < 100)) {
  169. play(Integer.toString(90), 1);
  170. play("[slnc]", 1);
  171. play("[slnc]", 1);
  172. digit = number - 90;
  173. }
  174. if (digit > 0) {
  175. play(Integer.toString(digit), 1);
  176. return true;
  177. }
  178. }
  179. // Any other cases are either too large to handle
  180. // or have an utterance that is directly mapped.
  181. return false;
  182. } catch (NumberFormatException nfe) {
  183. return false;
  184. }
  185. }
  186. public void stop() {
  187. soundQueue.clear();
  188. isPlaying = false;
  189. if (player != null) {
  190. try {
  191. player.stop();
  192. } catch (IllegalStateException e) {
  193. // Do nothing, the player is already stopped.
  194. }
  195. }
  196. }
  197. public void loadSoundFile(String soundName, String fileName) {
  198. sounds.put(soundName, new SoundResource(fileName));
  199. }
  200. public void loadSoundResource(String soundName, int resId) {
  201. sounds.put(soundName, new SoundResource(resId));
  202. }
  203. public void onCompletion(MediaPlayer arg0) {
  204. if (soundQueue.size() > 0) {
  205. processSoundQueue();
  206. } else {
  207. isPlaying = false;
  208. }
  209. }
  210. private void processSoundQueue() {
  211. boolean speechQueueAvailable = soundQueueLock.tryLock();
  212. if (!speechQueueAvailable) {
  213. return;
  214. }
  215. String soundName = soundQueue.get(0);
  216. isPlaying = true;
  217. if (sounds.containsKey(soundName)) {
  218. SoundResource sr = sounds.get(soundName);
  219. cleanUpPlayer();
  220. if (sr.resId != -1) {
  221. player = MediaPlayer.create(ctx, sr.resId);
  222. } else {
  223. player = MediaPlayer.create(ctx, Uri.parse(sr.filename));
  224. }
  225. // Check for if Media Server is dead;
  226. // if it is, clear the queue and give
  227. // up for now - hopefully, it will recover itself.
  228. if (player == null) {
  229. soundQueue.clear();
  230. isPlaying = false;
  231. soundQueueLock.unlock();
  232. return;
  233. }
  234. player.setOnCompletionListener(this);
  235. try {
  236. player.start();
  237. } catch (IllegalStateException e) {
  238. soundQueue.clear();
  239. isPlaying = false;
  240. cleanUpPlayer();
  241. soundQueueLock.unlock();
  242. return;
  243. }
  244. isPlaying = true;
  245. }
  246. if (soundQueue.size() > 0) {
  247. soundQueue.remove(0);
  248. }
  249. soundQueueLock.unlock();
  250. }
  251. private void cleanUpPlayer() {
  252. if (player != null) {
  253. player.release();
  254. player = null;
  255. }
  256. }
  257. }