diff --git a/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/InputStreamAudioStream.java b/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/InputStreamAudioStream.java index 27d93a0efd..2c8135111b 100644 --- a/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/InputStreamAudioStream.java +++ b/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/InputStreamAudioStream.java @@ -12,6 +12,10 @@ */ package org.openhab.voice.mimic.internal; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -33,12 +37,66 @@ public class InputStreamAudioStream extends FixedLengthAudioStream { public InputStream innerInputStream; public AudioFormat audioFormat; public long length; + public @Nullable File file; + private final String text; + private static final int READ_BUFFER_SIZE = 4096; - public InputStreamAudioStream(InputStream innerInputStream, AudioFormat audioFormat, long length) { + public InputStreamAudioStream(InputStream innerInputStream, AudioFormat audioFormat, long length, String text) { super(); this.innerInputStream = innerInputStream; this.audioFormat = audioFormat; this.length = length; + this.text = text; + try { + String outputFile = generateOutputFilename(); + File file = new File(outputFile); + try (InputStream is = this.innerInputStream; FileOutputStream fos = new FileOutputStream(file)) { + copyStream(is, fos); + } catch (FileNotFoundException e) { + throw new AudioException("Cannot open temporary audio file '" + file.getName() + "."); + } catch (IOException e) { + throw new AudioException("Unable to create temp file.", e); + } + this.file = file; + } catch (AudioException e) { + // throw new AudioException("Error while executing ", e); + } + } + + private void copyStream(InputStream inputStream, OutputStream outputStream) throws IOException { + byte[] bytes = new byte[READ_BUFFER_SIZE]; + int read = inputStream.read(bytes, 0, READ_BUFFER_SIZE); + while (read > 0) { + outputStream.write(bytes, 0, read); + read = inputStream.read(bytes, 0, READ_BUFFER_SIZE); + } + } + + private InputStream getFileInputStream(File file) throws AudioException { + if (file.exists()) { + try { + return new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new AudioException("Cannot open temporary audio file '" + file.getName() + "."); + } + } else { + throw new AudioException("Temporary file '" + file.getName() + "' not found!"); + } + } + + /** + * Generates a unique, absolute output filename + * + * @return Unique, absolute output filename + */ + private String generateOutputFilename() throws AudioException { + try { + File tempFile = File.createTempFile(Integer.toString(text.hashCode()), ".wav"); + tempFile.deleteOnExit(); + return tempFile.getAbsolutePath(); + } catch (IOException e) { + throw new AudioException("Unable to create temp file.", e); + } } @Override @@ -118,6 +176,11 @@ public class InputStreamAudioStream extends FixedLengthAudioStream { @Override public InputStream getClonedStream() throws AudioException { - throw new AudioException("Operation not supported"); + File file = this.file; + if (file != null) { + return getFileInputStream(file); + } else { + throw new AudioException("No temporary audio file available."); + } } } diff --git a/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/MimicTTSService.java b/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/MimicTTSService.java index 108b74bf03..9293b651a7 100644 --- a/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/MimicTTSService.java +++ b/bundles/org.openhab.voice.mimictts/src/main/java/org/openhab/voice/mimic/internal/MimicTTSService.java @@ -13,6 +13,8 @@ package org.openhab.voice.mimic.internal; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; @@ -234,20 +236,33 @@ public class MimicTTSService implements TTSService { String ssml = ""; if (text.startsWith("")) { - ssml = "&ssml=true"; + ssml = "&ssml=1"; + } + + String encodedVoice; + try { + encodedVoice = URLEncoder.encode(((MimicVoice) voice).getTechnicalName(), + StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Cannot encode voice in URL " + ((MimicVoice) voice).getTechnicalName()); } // create the url for given locale, format - String urlTTS = config.url + SYNTHETIZE_URL + "?voice=" + ((MimicVoice) voice).getTechnicalName() + ssml - + "&noiseScale=" + config.audioVolatility + "&noiseW=" + config.phonemeVolatility + "&lengthScale=" - + config.speakingRate + "&audioTarget=client"; + String urlTTS = config.url + SYNTHETIZE_URL + "?voice=" + encodedVoice + ssml + "&noiseScale=" + + config.audioVolatility + "&noiseW=" + config.phonemeVolatility + "&lengthScale=" + config.speakingRate + + "&audioTarget=client"; logger.debug("Querying mimic with URL {}", urlTTS); // prepare the response as an inputstream InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); // we will use a POST method for the text StringContentProvider textContentProvider = new StringContentProvider(text, StandardCharsets.UTF_8); - httpClient.POST(urlTTS).content(textContentProvider).accept("audio/wav").send(inputStreamResponseListener); + if (text.startsWith("")) { + httpClient.POST(urlTTS).header("Content-Type", "application/ssml+xml").content(textContentProvider) + .accept("audio/wav").send(inputStreamResponseListener); + } else { + httpClient.POST(urlTTS).content(textContentProvider).accept("audio/wav").send(inputStreamResponseListener); + } // compute the estimated timeout using a "stupid" method based on text length, as the response time depends on // the requested text. Average speaker speed estimated to 10/second. @@ -269,7 +284,8 @@ public class MimicTTSService implements TTSService { "Cannot get Content-Length header from mimic response. Are you sure to query a mimic TTS server at " + urlTTS + " ?"); } - return new InputStreamAudioStream(inputStreamResponseListener.getInputStream(), AUDIO_FORMAT, length); + return new InputStreamAudioStream(inputStreamResponseListener.getInputStream(), AUDIO_FORMAT, length, + text); } else { String errorMessage = "Cannot get wav from mimic url " + urlTTS + " with HTTP response code " + response.getStatus() + " for reason " + response.getReason();