/* * Copyright 2015, RagingGoblin <http://raginggoblin.wordpress.com> * * This file is part of SpeechLess. * * SpeechLess 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. * * SpeechLess 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 SpeechLess. If not, see <http://www.gnu.org/licenses/>. */ package raging.goblin.speechless.speech; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.prefs.Preferences; import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; import raging.goblin.speechless.Messages; @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Voice { @AllArgsConstructor public enum Gender { MALE(Messages.getInstance().get("male")), FEMALE(Messages.getInstance().get("female")); private String displayName; @Override public String toString() { return displayName; } }; private static final Logger LOG = Logger.getLogger(Voice.class); private static final List<Voice> VOICES = new ArrayList<>(); private static final Preferences PREFERENCES = Preferences.userNodeForPackage(Voice.class); private static final String XML_DIR = "/voices/"; private static final List<String> XML_FILES = Arrays.asList("voice-cmu-bdl-hsmm-5.1-component.xml", "voice-cmu-rms-hsmm-5.1-component.xml", "voice-cmu-slt-hsmm-5.1.2-component.xml", "voice-dfki-obadiah-hsmm-5.1-component.xml", "voice-dfki-poppy-hsmm-5.1-component.xml"); private static final String DEFAULT_VOICE = "dfki-obadiah-hsmm"; @Getter private String name; @Getter private String description; private Gender gender; private String language; private String country; public static List<Voice> getAllVoices() { if (VOICES.isEmpty()) { VOICES.addAll(readAllVoices()); } return VOICES; } public static Voice getSelectedVoice() { String name = PREFERENCES.get("voice", DEFAULT_VOICE); return getVoice(name); } public static void setSelectedVoice(String voice) { PREFERENCES.put("voice", voice); } public static Voice getDefaultVoice() { return getVoice(DEFAULT_VOICE); } @Override public String toString() { return Character.toUpperCase(name.charAt(0)) + name.substring(1) + " (" + gender + ", " + language + " - " + country + ")"; } private static List<Voice> readAllVoices() { return XML_FILES.stream().map(x -> readFromXml(Voice.class.getResourceAsStream(XML_DIR + x), x)) .filter(v -> v != null).collect(Collectors.toList()); } private static Voice readFromXml(InputStream xmlStream, String fileName) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlStream); document.getDocumentElement().normalize(); Node voice = document.getElementsByTagName("voice").item(0); Node description = document.getElementsByTagName("description").item(0); if (voice != null && voice.getNodeType() == Node.ELEMENT_NODE && description != null && description.getNodeType() == Node.ELEMENT_NODE) { String name = ((Element) voice).getAttribute("name"); String descriptionText = ((Element) description).getTextContent(); String genderText = ((Element) voice).getAttribute("gender"); String localeText = ((Element) voice).getAttribute("locale"); Locale locale = new Locale(localeText.split("-")[0], localeText.split("-")[1]); return new Voice(name, descriptionText, Gender.valueOf(genderText.toUpperCase()), locale.getDisplayLanguage(), locale.getDisplayCountry()); } else { LOG.error("No <voice> or <description> element in xml file: " + fileName); } } catch (SAXException | IOException | ParserConfigurationException e) { LOG.error("Not able to parse xml to voice, file: " + fileName, e); } return null; } private static Voice getVoice(String name) { try { return getAllVoices().stream().filter(v -> v.getName().equals(name)).collect(Collectors.toList()).get(0); } catch (IndexOutOfBoundsException e) { LOG.error("No voices found"); } return null; } }