All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.daisy.pipeline.tts.osx.impl.OSXSpeechEngine Maven / Gradle / Ivy

The newest version!
package org.daisy.pipeline.tts.osx.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IllformedLocaleException;
import java.util.Locale;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmNode;

import org.daisy.common.file.URLs;
import org.daisy.common.shell.CommandRunner;
import org.daisy.pipeline.tts.TTSEngine;
import org.daisy.pipeline.tts.TTSRegistry.TTSResource;
import org.daisy.pipeline.tts.TTSService.SynthesisException;
import org.daisy.pipeline.tts.Voice;
import org.daisy.pipeline.tts.VoiceInfo;
import org.daisy.pipeline.tts.VoiceInfo.Gender;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OSXSpeechEngine extends TTSEngine {

	private final String mSayPath;
	private final int mPriority;

	private final static URL ssmlTransformer = URLs.getResourceFromJAR("/transform-ssml.xsl", OSXSpeechEngine.class);
	private final static Logger mLogger = LoggerFactory.getLogger(OSXSpeechEngine.class);

	public OSXSpeechEngine(OSXSpeechService service, String osxPath, int priority) {
		super(service);
		mPriority = priority;
		mSayPath = osxPath;
	}

	@Override
	public SynthesisResult synthesize(XdmNode ssml, Voice voice, TTSResource threadResources)
			throws SynthesisException, InterruptedException {
		
		String sentence; {
			try {
				sentence = transformSsmlNodeToString(ssml, ssmlTransformer, null);
			} catch (IOException | SaxonApiException e) {
				throw new SynthesisException(e);
			}
		}
		File waveOut = null;
		try {
			waveOut = File.createTempFile("pipeline", ".wav");
			new CommandRunner(mSayPath, "--data-format=LEI16@22050", "-o",
			                  waveOut.getAbsolutePath(), "-v", voice.getName())
				.feedInput(sentence.getBytes("utf-8"))
				.consumeError(mLogger)
				.run();
			
			// read the wave on the standard output
			return new SynthesisResult(
				createAudioStream(
					new FileInputStream(waveOut)));
		} catch (InterruptedException e) {
			throw e;
		} catch (Throwable e) {
			StringWriter sw = new StringWriter();
			e.printStackTrace(new PrintWriter(sw));
			throw new SynthesisException(e);
		} finally {
			if (waveOut != null)
				waveOut.delete();
		}
	}

		@Override
	public Collection getAvailableVoices() throws SynthesisException,
	        InterruptedException {

		Collection result = new ArrayList();
		try {
			new CommandRunner(mSayPath, "-v", "?")
				.consumeOutput(stream -> {
						Matcher mr = Pattern.compile("(?.*?)\\s+(?\\w{2}_\\w{2})\\s*(#.*)?").matcher("");
						try (Scanner scanner = new Scanner(stream)) {
							while (scanner.hasNextLine()) {
								String line = scanner.nextLine();
								mr.reset(line);
								if (mr.find()) {
									String name = mr.group("name").trim();
									try {
										Locale locale = (new Locale.Builder()).setLanguageTag(mr.group("locale").replace("_", "-")).build();
										// Note that we could also maintain (hard-code) a mapping from voice to gender
										Gender unknownGender = Gender.ANY;
										result.add(new Voice(getProvider().getName(), name, locale, unknownGender));
									} catch (IllformedLocaleException e) {
										mLogger.debug("Could not parse line from `say -v ?' output: " + line);
										mLogger.debug("Reason: could not parse locale: " + mr.group("locale"));
										result.add(new Voice(getProvider().getName(), name));
									}
								} else {
									mLogger.debug("Could not parse line from `say -v ?' output: " + line);
								}
							}
						}
					}
				)
				.consumeError(mLogger)
				.run();
		} catch (InterruptedException e) {
			throw e;
		} catch (Throwable e) {
			throw new SynthesisException(e);
		}

		return result;
	}

	@Override
	public int getOverallPriority() {
		return mPriority;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy