2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.voice.marytts.internal;
15 import java.io.ByteArrayInputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.SequenceInputStream;
20 import javax.sound.sampled.AudioInputStream;
22 import org.apache.commons.io.IOUtils;
23 import org.openhab.core.audio.AudioException;
24 import org.openhab.core.audio.AudioFormat;
25 import org.openhab.core.audio.AudioSource;
26 import org.openhab.core.audio.FixedLengthAudioStream;
29 * Implementation of the {@link AudioSource} interface for the {@link MaryTTSService}
31 * @author Kelly Davis - Initial contribution and API
32 * @author Kai Kreuzer - Refactored to updated APIs and moved to openHAB
34 class MaryTTSAudioStream extends FixedLengthAudioStream {
37 * {@link AudioFormat} of this {@link AudioSource}
39 private final AudioFormat audioFormat;
42 * {@link InputStream} of this {@link AudioSource}
44 private InputStream inputStream;
46 private final byte[] rawAudio;
47 private final int length;
50 * Constructs an instance with the passed properties
52 * @param inputStream The InputStream of this instance
53 * @param audioFormat The AudioFormat of this instance
56 public MaryTTSAudioStream(AudioInputStream inputStream, AudioFormat audioFormat) throws IOException {
57 rawAudio = IOUtils.toByteArray(inputStream);
58 this.length = rawAudio.length + 36;
59 this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
60 this.audioFormat = audioFormat;
64 public AudioFormat getFormat() {
65 return this.audioFormat;
69 public int read(byte[] b) throws IOException {
70 return inputStream.read(b, 0, b.length);
74 public int read(byte[] b, int off, int len) throws IOException {
75 return inputStream.read(b, off, len);
79 public int read() throws IOException {
80 return inputStream.read();
84 public long length() {
88 private InputStream getWavHeaderInputStream(int length) throws IOException {
90 // see http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
91 byte[] header = new byte[44];
93 byte format = 0x10; // PCM
96 long srate = (this.audioFormat != null) ? this.audioFormat.getFrequency() : 48000l;
97 long rawLength = length - 36;
98 long bitrate = srate * channel * bits;
104 header[4] = (byte) (length & 0xff);
105 header[5] = (byte) ((length >> 8) & 0xff);
106 header[6] = (byte) ((length >> 16) & 0xff);
107 header[7] = (byte) ((length >> 24) & 0xff);
122 header[22] = channel;
124 header[24] = (byte) (srate & 0xff);
125 header[25] = (byte) ((srate >> 8) & 0xff);
126 header[26] = (byte) ((srate >> 16) & 0xff);
127 header[27] = (byte) ((srate >> 24) & 0xff);
128 header[28] = (byte) ((bitrate / 8) & 0xff);
129 header[29] = (byte) (((bitrate / 8) >> 8) & 0xff);
130 header[30] = (byte) (((bitrate / 8) >> 16) & 0xff);
131 header[31] = (byte) (((bitrate / 8) >> 24) & 0xff);
132 header[32] = (byte) ((channel * bits) / 8);
140 header[40] = (byte) (rawLength & 0xff);
141 header[41] = (byte) ((rawLength >> 8) & 0xff);
142 header[42] = (byte) ((rawLength >> 16) & 0xff);
143 header[43] = (byte) ((rawLength >> 24) & 0xff);
144 return new ByteArrayInputStream(header);
148 public synchronized void reset() throws IOException {
149 IOUtils.closeQuietly(inputStream);
150 this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
154 public InputStream getClonedStream() throws AudioException {
156 return new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
157 } catch (IOException e) {
158 throw new AudioException(e);