]> git.basschouten.com Git - openhab-addons.git/blob
da384f443c373e1d16466469f2de5034fd3b29bd
[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.common.Disposable;
28 import org.openhab.core.voice.Voice;
29
30 /**
31  * Implementation of {@link AudioStream} for {@link PicoTTSService}
32  *
33  * @author Florian Schmidt - Initial Contribution
34  */
35 @NonNullByDefault
36 class PicoTTSAudioStream extends FixedLengthAudioStream implements Disposable {
37
38     private final Voice voice;
39     private final String text;
40     private final AudioFormat audioFormat;
41     private final InputStream inputStream;
42
43     private long length;
44     private @Nullable File file;
45
46     public PicoTTSAudioStream(String text, Voice voice, AudioFormat audioFormat) throws AudioException {
47         this.text = text;
48         this.voice = voice;
49         this.audioFormat = audioFormat;
50         this.inputStream = createInputStream();
51     }
52
53     @Override
54     public AudioFormat getFormat() {
55         return audioFormat;
56     }
57
58     private InputStream createInputStream() throws AudioException {
59         String outputFile = generateOutputFilename();
60         String[] command = getCommand(outputFile);
61
62         try {
63             Process process = Runtime.getRuntime().exec(command);
64             process.waitFor();
65             File file = new File(outputFile);
66             this.file = file;
67             this.length = file.length();
68             return getFileInputStream(file);
69         } catch (IOException e) {
70             throw new AudioException("Error while executing '" + command + "'", e);
71         } catch (InterruptedException e) {
72             throw new AudioException("The '" + command + "' has been interrupted", e);
73         }
74     }
75
76     private InputStream getFileInputStream(File file) throws AudioException {
77         if (file.exists()) {
78             try {
79                 return new FileInputStream(file);
80             } catch (FileNotFoundException e) {
81                 throw new AudioException("Cannot open temporary audio file '" + file.getName() + ".");
82             }
83         } else {
84             throw new AudioException("Temporary file '" + file.getName() + "' not found!");
85         }
86     }
87
88     /**
89      * Generates a unique, absolute output filename
90      *
91      * @return Unique, absolute output filename
92      */
93     private String generateOutputFilename() throws AudioException {
94         try {
95             File tempFile = File.createTempFile(Integer.toString(text.hashCode()), ".wav");
96             tempFile.deleteOnExit();
97             return tempFile.getAbsolutePath();
98         } catch (IOException e) {
99             throw new AudioException("Unable to create temp file.", e);
100         }
101     }
102
103     /**
104      * Gets the command used to generate an audio file {@code outputFile}
105      *
106      * @param outputFile The absolute filename of the command's output
107      * @return The command used to generate the audio file {@code outputFile}
108      */
109     private String[] getCommand(String outputFile) {
110         return new String[] { "pico2wave", "-l=" + this.voice.getLabel(), "-w=" + outputFile, this.text };
111     }
112
113     @Override
114     public int read() throws IOException {
115         return inputStream.read();
116     }
117
118     @Override
119     public long length() {
120         return length;
121     }
122
123     @Override
124     public InputStream getClonedStream() throws AudioException {
125         File file = this.file;
126         if (file != null) {
127             return getFileInputStream(file);
128         } else {
129             throw new AudioException("No temporary audio file available.");
130         }
131     }
132
133     @Override
134     public void dispose() throws IOException {
135         File localFile = file;
136         if (localFile != null && localFile.exists()) {
137             try {
138                 if (!localFile.delete()) {
139                     throw new IOException("Failed to delete the file " + localFile.getAbsolutePath());
140                 }
141             } catch (SecurityException e) {
142                 throw new IOException("Failed to delete the file " + localFile.getAbsolutePath(), e);
143             }
144         }
145     }
146 }