]> git.basschouten.com Git - openhab-addons.git/blob
af4e24c6ec647e2c850a4fa82adf4bc420ed5f94
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.voice.picotts.internal;
14
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.FileNotFoundException;
18 import java.io.IOException;
19 import java.io.InputStream;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.core.audio.AudioException;
24 import org.openhab.core.audio.AudioFormat;
25 import org.openhab.core.audio.AudioStream;
26 import org.openhab.core.audio.FixedLengthAudioStream;
27 import org.openhab.core.voice.Voice;
28
29 /**
30  * Implementation of {@link AudioStream} for {@link PicoTTSService}
31  *
32  * @author Florian Schmidt - Initial Contribution
33  */
34 @NonNullByDefault
35 class PicoTTSAudioStream extends FixedLengthAudioStream {
36     private final Voice voice;
37     private final String text;
38     private final AudioFormat audioFormat;
39     private final InputStream inputStream;
40
41     private long length;
42     private @Nullable File file;
43
44     public PicoTTSAudioStream(String text, Voice voice, AudioFormat audioFormat) throws AudioException {
45         this.text = text;
46         this.voice = voice;
47         this.audioFormat = audioFormat;
48         this.inputStream = createInputStream();
49     }
50
51     @Override
52     public AudioFormat getFormat() {
53         return audioFormat;
54     }
55
56     private InputStream createInputStream() throws AudioException {
57         String outputFile = generateOutputFilename();
58         String[] command = getCommand(outputFile);
59
60         try {
61             Process process = Runtime.getRuntime().exec(command);
62             process.waitFor();
63             File file = new File(outputFile);
64             this.file = file;
65             this.length = file.length();
66             return getFileInputStream(file);
67         } catch (IOException e) {
68             throw new AudioException("Error while executing '" + command + "'", e);
69         } catch (InterruptedException e) {
70             throw new AudioException("The '" + command + "' has been interrupted", e);
71         }
72     }
73
74     private InputStream getFileInputStream(File file) throws AudioException {
75         if (file.exists()) {
76             try {
77                 return new FileInputStream(file);
78             } catch (FileNotFoundException e) {
79                 throw new AudioException("Cannot open temporary audio file '" + file.getName() + ".");
80             }
81         } else {
82             throw new AudioException("Temporary file '" + file.getName() + "' not found!");
83         }
84     }
85
86     /**
87      * Generates a unique, absolute output filename
88      *
89      * @return Unique, absolute output filename
90      */
91     private String generateOutputFilename() throws AudioException {
92         try {
93             File tempFile = File.createTempFile(Integer.toString(text.hashCode()), ".wav");
94             tempFile.deleteOnExit();
95             return tempFile.getAbsolutePath();
96         } catch (IOException e) {
97             throw new AudioException("Unable to create temp file.", e);
98         }
99     }
100
101     /**
102      * Gets the command used to generate an audio file {@code outputFile}
103      *
104      * @param outputFile The absolute filename of the command's output
105      * @return The command used to generate the audio file {@code outputFile}
106      */
107     private String[] getCommand(String outputFile) {
108         return new String[] { "pico2wave", "-l=" + this.voice.getLabel(), "-w=" + outputFile, this.text };
109     }
110
111     @Override
112     public int read() throws IOException {
113         return inputStream.read();
114     }
115
116     @Override
117     public long length() {
118         return length;
119     }
120
121     @Override
122     public InputStream getClonedStream() throws AudioException {
123         File file = this.file;
124         if (file != null) {
125             return getFileInputStream(file);
126         } else {
127             throw new AudioException("No temporary audio file available.");
128         }
129     }
130 }