Mercurial > hg > accesspd
comparison java/src/uk/ac/qmul/eecs/ccmi/speech/NativeNarrator.java @ 0:78b7fc5391a2
first import, outcome of NIME 2014 hackaton
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Tue, 08 Jul 2014 16:28:59 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78b7fc5391a2 |
---|---|
1 /* | |
2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool | |
3 | |
4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) | |
5 | |
6 This program is free software: you can redistribute it and/or modify | |
7 it under the terms of the GNU General Public License as published by | |
8 the Free Software Foundation, either version 3 of the License, or | |
9 (at your option) any later version. | |
10 | |
11 This program is distributed in the hope that it will be useful, | |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 GNU General Public License for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 */ | |
19 | |
20 package uk.ac.qmul.eecs.ccmi.speech; | |
21 | |
22 import java.net.URL; | |
23 import java.util.ResourceBundle; | |
24 import java.util.concurrent.LinkedBlockingQueue; | |
25 | |
26 import uk.ac.qmul.eecs.ccmi.utils.ResourceFileWriter; | |
27 import uk.ac.qmul.eecs.ccmi.utils.OsDetector; | |
28 import uk.ac.qmul.eecs.ccmi.utils.PreferencesService; | |
29 | |
30 import com.sun.speech.freetts.Voice; | |
31 import com.sun.speech.freetts.VoiceManager; | |
32 /* | |
33 * Implementation of the Narrator interface using the Windows system text to speech | |
34 * synthesizer. | |
35 */ | |
36 class NativeNarrator implements Narrator { | |
37 static { | |
38 nativeLibraryNotFound = true; | |
39 if(OsDetector.isWindows()){ | |
40 String res = OsDetector.has64BitJVM() ? "WinNarrator64.dll" : "WinNarrator.dll" ; | |
41 URL url = NativeNarrator.class.getResource(res); | |
42 if(url != null){ | |
43 ResourceFileWriter fileWriter = new ResourceFileWriter(url); | |
44 fileWriter.writeOnDisk( | |
45 PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir")), | |
46 OsDetector.has64BitJVM() ? "CCmIWinNarrator64.dll" : "CCmIWinNarrator.dll"); | |
47 String path = fileWriter.getFilePath(); | |
48 if(path != null) | |
49 try{ | |
50 System.load( path ); | |
51 nativeLibraryNotFound = false; | |
52 }catch(UnsatisfiedLinkError e){ | |
53 e.printStackTrace(); | |
54 /* do nothing: nativeLibraryNotFound won't be set to false */ | |
55 /* which will trigger a NarratorException */ | |
56 } | |
57 } | |
58 } | |
59 } | |
60 | |
61 public NativeNarrator(){ | |
62 resources = ResourceBundle.getBundle(Narrator.class.getName()); | |
63 VoiceManager voiceManager = VoiceManager.getInstance(); | |
64 secondaryVoice = voiceManager.getVoice(VOICE_NAME); | |
65 if(secondaryVoice == null) | |
66 System.out.println("Could not create voice for the second speaker"); | |
67 else{ | |
68 secondaryVoice.setAudioPlayer(new BeadsAudioPlayer(1.0f,1.0f)); | |
69 secondaryVoice.setRate(250f); | |
70 secondaryVoice.allocate(); | |
71 } | |
72 } | |
73 | |
74 @Override | |
75 public void init() throws NarratorException { | |
76 if(nativeLibraryNotFound) | |
77 throw new NarratorException(); | |
78 | |
79 firstVoiceMuted = false; | |
80 secondVoiceMuted = false; | |
81 queue = new LinkedBlockingQueue<QueueEntry>(); | |
82 executor = new Executor(); | |
83 boolean success = _init(); | |
84 if(!success) | |
85 throw new NarratorException(); | |
86 rate = Integer.parseInt(PreferencesService.getInstance().get("speech_rate", DEFAULT_RATE_VALUE)); | |
87 _setRate(rate); | |
88 executor.start(); | |
89 } | |
90 | |
91 @Override | |
92 public void setMuted(boolean muted, int voice) { | |
93 if(voice == SECOND_VOICE) | |
94 secondVoiceMuted = muted; | |
95 else | |
96 firstVoiceMuted = muted; | |
97 } | |
98 | |
99 @Override | |
100 public boolean isMuted(int voice){ | |
101 if(voice == SECOND_VOICE) | |
102 return secondVoiceMuted; | |
103 else | |
104 return firstVoiceMuted; | |
105 } | |
106 | |
107 @Override | |
108 public void setRate(int rate){ | |
109 if(rate < MIN_RATE || rate > MAX_RATE) | |
110 throw new IllegalArgumentException("Rate value must be between 0 and 20"); | |
111 _setRate(rate); | |
112 this.rate = rate; | |
113 PreferencesService.getInstance().put("speech_rate", Integer.toString(rate)); | |
114 } | |
115 | |
116 @Override | |
117 public int getRate(){ | |
118 return rate; | |
119 } | |
120 | |
121 @Override | |
122 public void shutUp() { | |
123 _shutUp(); | |
124 } | |
125 | |
126 @Override | |
127 public void speak(String text, int voice) { | |
128 if(firstVoiceMuted || secondVoiceMuted && voice == Narrator.SECOND_VOICE) | |
129 return; | |
130 if(" ".equals(text)) | |
131 text = resources.getString("char.space"); | |
132 else if("\n".equals(text)) | |
133 text = resources.getString("char.new_line"); | |
134 else if(text.contains(".ccmi")) | |
135 text = text.replaceAll(".ccmi", " "+resources.getString("ccmi_spell")); | |
136 queue.add(new QueueEntry(text,false,voice)); | |
137 } | |
138 | |
139 public void speak(String text){ | |
140 speak(text,Narrator.FIRST_VOICE); | |
141 } | |
142 | |
143 @Override | |
144 public void speakWholeText(String text, int voice) { | |
145 if(firstVoiceMuted || secondVoiceMuted && voice == Narrator.SECOND_VOICE) | |
146 return; | |
147 if(" ".equals(text)) | |
148 text = resources.getString("char.space"); | |
149 else if("\n".equals(text)) | |
150 text = resources.getString("char.new_line"); | |
151 else if(text.contains(".ccmi")) | |
152 text = text.replaceAll(".ccmi", " "+resources.getString("ccmi_spell")); | |
153 queue.add(new QueueEntry(text,true,voice)); | |
154 } | |
155 | |
156 @Override | |
157 public void speakWholeText(String text) { | |
158 speakWholeText(text, Narrator.FIRST_VOICE); | |
159 } | |
160 | |
161 @Override | |
162 public void dispose() { | |
163 executor.mustSayGoodbye = true; | |
164 executor.interrupt(); | |
165 } | |
166 | |
167 /* native routines used by the Executor thread */ | |
168 private native boolean _init(); | |
169 | |
170 private native void _dispose(); | |
171 | |
172 private native void _speak(String text); | |
173 | |
174 private native void _speakWholeText(String text); | |
175 | |
176 private native void _setRate(int rate); | |
177 | |
178 private native void _shutUp(); | |
179 | |
180 private volatile boolean firstVoiceMuted; | |
181 private volatile boolean secondVoiceMuted; | |
182 private int rate; | |
183 private LinkedBlockingQueue<QueueEntry> queue; | |
184 private Executor executor; | |
185 private ResourceBundle resources; | |
186 private Voice secondaryVoice; | |
187 private static String DEFAULT_RATE_VALUE = "13"; | |
188 private static final String VOICE_NAME = "kevin16"; | |
189 private static boolean nativeLibraryNotFound; | |
190 | |
191 private class Executor extends Thread{ | |
192 private Executor(){ | |
193 super("Narrator Thread"); | |
194 mustSayGoodbye = false; | |
195 } | |
196 | |
197 @Override | |
198 public void run(){ | |
199 QueueEntry entry; | |
200 while(!mustSayGoodbye){ | |
201 try { | |
202 entry = queue.take(); | |
203 } catch (InterruptedException e) { | |
204 continue; /* start over the while cycle */ | |
205 } | |
206 if(!entry.speakToEnd && queue.peek() != null) | |
207 continue;/* the user submitted another text to be spoken out and this can be overwritten */ | |
208 if(entry.speakToEnd && entry.voice == Narrator.FIRST_VOICE){ | |
209 _speakWholeText(entry.text); | |
210 }else if(entry.voice == Narrator.FIRST_VOICE){ | |
211 _speak(entry.text); | |
212 }else if(secondaryVoice != null){ | |
213 secondaryVoice.speak(entry.text); | |
214 } | |
215 } | |
216 if(secondaryVoice != null) | |
217 secondaryVoice.deallocate(); | |
218 _dispose(); | |
219 } | |
220 | |
221 private volatile boolean mustSayGoodbye; | |
222 } | |
223 | |
224 private static class QueueEntry { | |
225 QueueEntry(String text, boolean speakToEnd, int voice){ | |
226 this.text = text; | |
227 this.speakToEnd = speakToEnd; | |
228 this.voice = voice; | |
229 } | |
230 String text; | |
231 boolean speakToEnd; | |
232 int voice; | |
233 } | |
234 | |
235 } |