annotate java/src/uk/ac/qmul/eecs/ccmi/speech/NativeNarrator.java @ 1:e3935c01cde2 tip

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