Mercurial > hg > accesspd
view 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 |
line wrap: on
line source
/* CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.qmul.eecs.ccmi.speech; import java.net.URL; import java.util.ResourceBundle; import java.util.concurrent.LinkedBlockingQueue; import uk.ac.qmul.eecs.ccmi.utils.ResourceFileWriter; import uk.ac.qmul.eecs.ccmi.utils.OsDetector; import uk.ac.qmul.eecs.ccmi.utils.PreferencesService; import com.sun.speech.freetts.Voice; import com.sun.speech.freetts.VoiceManager; /* * Implementation of the Narrator interface using the Windows system text to speech * synthesizer. */ class NativeNarrator implements Narrator { static { nativeLibraryNotFound = true; if(OsDetector.isWindows()){ String res = OsDetector.has64BitJVM() ? "WinNarrator64.dll" : "WinNarrator.dll" ; URL url = NativeNarrator.class.getResource(res); if(url != null){ ResourceFileWriter fileWriter = new ResourceFileWriter(url); fileWriter.writeOnDisk( PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir")), OsDetector.has64BitJVM() ? "CCmIWinNarrator64.dll" : "CCmIWinNarrator.dll"); String path = fileWriter.getFilePath(); if(path != null) try{ System.load( path ); nativeLibraryNotFound = false; }catch(UnsatisfiedLinkError e){ e.printStackTrace(); /* do nothing: nativeLibraryNotFound won't be set to false */ /* which will trigger a NarratorException */ } } } } public NativeNarrator(){ resources = ResourceBundle.getBundle(Narrator.class.getName()); VoiceManager voiceManager = VoiceManager.getInstance(); secondaryVoice = voiceManager.getVoice(VOICE_NAME); if(secondaryVoice == null) System.out.println("Could not create voice for the second speaker"); else{ secondaryVoice.setAudioPlayer(new BeadsAudioPlayer(1.0f,1.0f)); secondaryVoice.setRate(250f); secondaryVoice.allocate(); } } @Override public void init() throws NarratorException { if(nativeLibraryNotFound) throw new NarratorException(); firstVoiceMuted = false; secondVoiceMuted = false; queue = new LinkedBlockingQueue<QueueEntry>(); executor = new Executor(); boolean success = _init(); if(!success) throw new NarratorException(); rate = Integer.parseInt(PreferencesService.getInstance().get("speech_rate", DEFAULT_RATE_VALUE)); _setRate(rate); executor.start(); } @Override public void setMuted(boolean muted, int voice) { if(voice == SECOND_VOICE) secondVoiceMuted = muted; else firstVoiceMuted = muted; } @Override public boolean isMuted(int voice){ if(voice == SECOND_VOICE) return secondVoiceMuted; else return firstVoiceMuted; } @Override public void setRate(int rate){ if(rate < MIN_RATE || rate > MAX_RATE) throw new IllegalArgumentException("Rate value must be between 0 and 20"); _setRate(rate); this.rate = rate; PreferencesService.getInstance().put("speech_rate", Integer.toString(rate)); } @Override public int getRate(){ return rate; } @Override public void shutUp() { _shutUp(); } @Override public void speak(String text, int voice) { if(firstVoiceMuted || secondVoiceMuted && voice == Narrator.SECOND_VOICE) return; if(" ".equals(text)) text = resources.getString("char.space"); else if("\n".equals(text)) text = resources.getString("char.new_line"); else if(text.contains(".ccmi")) text = text.replaceAll(".ccmi", " "+resources.getString("ccmi_spell")); queue.add(new QueueEntry(text,false,voice)); } public void speak(String text){ speak(text,Narrator.FIRST_VOICE); } @Override public void speakWholeText(String text, int voice) { if(firstVoiceMuted || secondVoiceMuted && voice == Narrator.SECOND_VOICE) return; if(" ".equals(text)) text = resources.getString("char.space"); else if("\n".equals(text)) text = resources.getString("char.new_line"); else if(text.contains(".ccmi")) text = text.replaceAll(".ccmi", " "+resources.getString("ccmi_spell")); queue.add(new QueueEntry(text,true,voice)); } @Override public void speakWholeText(String text) { speakWholeText(text, Narrator.FIRST_VOICE); } @Override public void dispose() { executor.mustSayGoodbye = true; executor.interrupt(); } /* native routines used by the Executor thread */ private native boolean _init(); private native void _dispose(); private native void _speak(String text); private native void _speakWholeText(String text); private native void _setRate(int rate); private native void _shutUp(); private volatile boolean firstVoiceMuted; private volatile boolean secondVoiceMuted; private int rate; private LinkedBlockingQueue<QueueEntry> queue; private Executor executor; private ResourceBundle resources; private Voice secondaryVoice; private static String DEFAULT_RATE_VALUE = "13"; private static final String VOICE_NAME = "kevin16"; private static boolean nativeLibraryNotFound; private class Executor extends Thread{ private Executor(){ super("Narrator Thread"); mustSayGoodbye = false; } @Override public void run(){ QueueEntry entry; while(!mustSayGoodbye){ try { entry = queue.take(); } catch (InterruptedException e) { continue; /* start over the while cycle */ } if(!entry.speakToEnd && queue.peek() != null) continue;/* the user submitted another text to be spoken out and this can be overwritten */ if(entry.speakToEnd && entry.voice == Narrator.FIRST_VOICE){ _speakWholeText(entry.text); }else if(entry.voice == Narrator.FIRST_VOICE){ _speak(entry.text); }else if(secondaryVoice != null){ secondaryVoice.speak(entry.text); } } if(secondaryVoice != null) secondaryVoice.deallocate(); _dispose(); } private volatile boolean mustSayGoodbye; } private static class QueueEntry { QueueEntry(String text, boolean speakToEnd, int voice){ this.text = text; this.speakToEnd = speakToEnd; this.voice = voice; } String text; boolean speakToEnd; int voice; } }