added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.voice.marytts</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,20 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons
== Third-party Content
marytts
* License: LGPL v3.0 License
* Project: http://mary.dfki.de
* Source: https://github.com/marytts/marytts

View File

@@ -0,0 +1,31 @@
# Mary Text-to-Speech
## Overview
The Mary Text-to-Speech (TTS) service is a pure Java implementation of a TTS service, which uses the [MaryTTS](http://mary.dfki.de/) project of DFKI.
While it provides good quality results, it must be noted that it is too heavy-weight for most embedded hardware like a Raspberry Pi. When using this service, you should be running openHAB on some real server instead.
## Configuration
There is no need to configure anything for this service.
## Voices
MaryTTS comes with three packages voices, one for American English, two for German:
```
> voice voices
marytts:cmuslthsmm cmu-slt-hsmm (en_US)
marytts:bits3hsmm bits3-hsmm (de)
marytts:bits1hsmm bits1-hsmm (de)
```
## Supported Audio Formats
The MaryTTS service produces audio streams using WAV containers and PCM (signed) codec with 16bit depth.
The sample frequency depends on the chosen voice and ranges from 16kHz to 48kHz.
## Log files
The log messages of Mary TTS are not bundled with the openHAB log messages in the `openhab.log` file of your log directory but are stored in their own log file at `server.log` of your log directory.

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.voice.marytts</artifactId>
<name>openHAB Add-ons :: Bundles :: Voice :: Mary Text-to-Speech</name>
<properties>
<bnd.importpackage>com.twmacinta.util;resolution:=optional,gnu.trove;resolution:=optional,Jampack;resolution:=optional,net.didion.jwnl*;resolution:=optional,org.apache.http*;resolution:=optional,org.apache.xerces.impl*;resolution:=optional,org.hsqldb;resolution:=optional,org.jdesktop.layout*;resolution:=optional</bnd.importpackage>
</properties>
<dependencies>
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>54.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.lt.jtok</groupId>
<artifactId>jtok-core</artifactId>
<version>1.9.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>emotionml-checker-java</artifactId>
<version>1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>marytts-common</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>marytts-lang-de</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>marytts-lang-en</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>marytts-runtime</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>marytts-signalproc</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>voice-bits1-hsmm</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>voice-bits3-hsmm</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.dfki.mary</groupId>
<artifactId>voice-cmu-slt-hsmm</artifactId>
<version>5.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>gov.nist.math</groupId>
<artifactId>jama</artifactId>
<version>1.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.opennlp</groupId>
<artifactId>opennlp-maxent</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.opennlp</groupId>
<artifactId>opennlp-tools</artifactId>
<version>1.5.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.voice.marytts-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-voice-marytts" description="Mary Text-to-Speech" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle dependency="true">mvn:commons-collections/commons-collections/3.2.2</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.voice.marytts/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,161 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.voice.marytts.internal;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import javax.sound.sampled.AudioInputStream;
import org.apache.commons.io.IOUtils;
import org.openhab.core.audio.AudioException;
import org.openhab.core.audio.AudioFormat;
import org.openhab.core.audio.AudioSource;
import org.openhab.core.audio.FixedLengthAudioStream;
/**
* Implementation of the {@link AudioSource} interface for the {@link MaryTTSService}
*
* @author Kelly Davis - Initial contribution and API
* @author Kai Kreuzer - Refactored to updated APIs and moved to openHAB
*/
class MaryTTSAudioStream extends FixedLengthAudioStream {
/**
* {@link AudioFormat} of this {@link AudioSource}
*/
private final AudioFormat audioFormat;
/**
* {@link InputStream} of this {@link AudioSource}
*/
private InputStream inputStream;
private final byte[] rawAudio;
private final int length;
/**
* Constructs an instance with the passed properties
*
* @param inputStream The InputStream of this instance
* @param audioFormat The AudioFormat of this instance
* @throws IOException
*/
public MaryTTSAudioStream(AudioInputStream inputStream, AudioFormat audioFormat) throws IOException {
rawAudio = IOUtils.toByteArray(inputStream);
this.length = rawAudio.length + 36;
this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
this.audioFormat = audioFormat;
}
@Override
public AudioFormat getFormat() {
return this.audioFormat;
}
@Override
public int read(byte[] b) throws IOException {
return inputStream.read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return inputStream.read(b, off, len);
}
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public long length() {
return length;
}
private InputStream getWavHeaderInputStream(int length) throws IOException {
// WAVE header
// see http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
byte[] header = new byte[44];
byte format = 0x10; // PCM
byte bits = 16;
byte channel = 1;
long srate = (this.audioFormat != null) ? this.audioFormat.getFrequency() : 48000l;
long rawLength = length - 36;
long bitrate = srate * channel * bits;
header[0] = 'R';
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (length & 0xff);
header[5] = (byte) ((length >> 8) & 0xff);
header[6] = (byte) ((length >> 16) & 0xff);
header[7] = (byte) ((length >> 24) & 0xff);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f';
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = format;
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 1;
header[21] = 0;
header[22] = channel;
header[23] = 0;
header[24] = (byte) (srate & 0xff);
header[25] = (byte) ((srate >> 8) & 0xff);
header[26] = (byte) ((srate >> 16) & 0xff);
header[27] = (byte) ((srate >> 24) & 0xff);
header[28] = (byte) ((bitrate / 8) & 0xff);
header[29] = (byte) (((bitrate / 8) >> 8) & 0xff);
header[30] = (byte) (((bitrate / 8) >> 16) & 0xff);
header[31] = (byte) (((bitrate / 8) >> 24) & 0xff);
header[32] = (byte) ((channel * bits) / 8);
header[33] = 0;
header[34] = 16;
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (rawLength & 0xff);
header[41] = (byte) ((rawLength >> 8) & 0xff);
header[42] = (byte) ((rawLength >> 16) & 0xff);
header[43] = (byte) ((rawLength >> 24) & 0xff);
return new ByteArrayInputStream(header);
}
@Override
public synchronized void reset() throws IOException {
IOUtils.closeQuietly(inputStream);
this.inputStream = new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
}
@Override
public InputStream getClonedStream() throws AudioException {
try {
return new SequenceInputStream(getWavHeaderInputStream(length), new ByteArrayInputStream(rawAudio));
} catch (IOException e) {
throw new AudioException(e);
}
}
}

View File

@@ -0,0 +1,192 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.voice.marytts.internal;
import static javax.sound.sampled.AudioSystem.NOT_SPECIFIED;
import java.io.IOException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.openhab.core.audio.AudioFormat;
import org.openhab.core.audio.AudioStream;
import org.openhab.core.voice.TTSException;
import org.openhab.core.voice.TTSService;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import marytts.LocalMaryInterface;
import marytts.MaryInterface;
import marytts.exceptions.MaryConfigurationException;
import marytts.exceptions.SynthesisException;
import marytts.modules.synthesis.Voice;
/**
* This is a TTS service implementation for using MaryTTS.
*
* @author Kelly Davis - Initial contribution and API
* @author Kai Kreuzer - Refactored to updated APIs and moved to openHAB
*/
@Component
public class MaryTTSService implements TTSService {
private final Logger logger = LoggerFactory.getLogger(MaryTTSService.class);
private MaryInterface marytts;
/**
* Set of supported voices
*/
private Set<org.openhab.core.voice.Voice> voices;
/**
* Set of supported audio formats
*/
private Set<AudioFormat> audioFormats;
protected void activate() {
try {
marytts = new LocalMaryInterface();
voices = initVoices();
audioFormats = initAudioFormats();
} catch (MaryConfigurationException e) {
logger.error("Failed to initialize MaryTTS: {}", e.getMessage(), e);
}
}
@Override
public Set<org.openhab.core.voice.Voice> getAvailableVoices() {
return voices;
}
@Override
public Set<AudioFormat> getSupportedFormats() {
return audioFormats;
}
@Override
public AudioStream synthesize(String text, org.openhab.core.voice.Voice voice, AudioFormat requestedFormat)
throws TTSException {
// Validate arguments
if (text == null || text.isEmpty()) {
throw new TTSException("The passed text is null or empty");
}
if (!voices.contains(voice)) {
throw new TTSException("The passed voice is unsupported");
}
if (audioFormats.stream().noneMatch(f -> f.isCompatible(requestedFormat))) {
throw new TTSException("The passed AudioFormat is unsupported");
}
/*
* NOTE: For each MaryTTS voice only a single AudioFormat is supported
* However, the TTSService interface allows the AudioFormat and
* the Voice to vary independently. Thus, an external user does
* not know about the requirement that a given voice is paired
* with a given AudioFormat. The test below enforces this.
*
* However, this leads to a problem. The user has no way to
* know which AudioFormat is apropos for a give Voice. Thus,
* throwing a TTSException for the wrong AudioFormat makes
* the user guess the right AudioFormat, a painful process.
* Alternatively, we can get the right AudioFormat for the
* Voice and ignore what the user requests, also wrong.
*
* TODO: Decide what to do
* Voice maryTTSVoice = Voice.getVoice(voice.getLabel());
* AudioFormat maryTTSVoiceAudioFormat = getAudioFormat(maryTTSVoice.dbAudioFormat());
* if (!maryTTSVoiceAudioFormat.isCompatible(requestedFormat)) {
* throw new TTSException("The passed AudioFormat is incompatable with the voice");
* }
*/
Voice maryTTSVoice = Voice.getVoice(voice.getLabel());
AudioFormat maryTTSVoiceAudioFormat = getAudioFormat(maryTTSVoice.dbAudioFormat());
// Synchronize on marytts
synchronized (marytts) {
// Set voice (Each voice supports only a single AudioFormat)
marytts.setLocale(voice.getLocale());
marytts.setVoice(voice.getLabel());
try {
return new MaryTTSAudioStream(marytts.generateAudio(text), maryTTSVoiceAudioFormat);
} catch (SynthesisException | IOException e) {
throw new TTSException("Error generating an AudioStream", e);
}
}
}
/**
* Initializes voices
*
* @return The voices of this instance
*/
private Set<org.openhab.core.voice.Voice> initVoices() {
Set<org.openhab.core.voice.Voice> voices = new HashSet<>();
for (Locale locale : marytts.getAvailableLocales()) {
for (String voiceLabel : marytts.getAvailableVoices(locale)) {
voices.add(new MaryTTSVoice(locale, voiceLabel));
}
}
return voices;
}
/**
* Initializes audioFormats
*
* @return The audio formats of this instance
*/
private Set<AudioFormat> initAudioFormats() {
Set<AudioFormat> audioFormats = new HashSet<>();
for (String voiceLabel : marytts.getAvailableVoices()) {
audioFormats.add(getAudioFormat(Voice.getVoice(voiceLabel).dbAudioFormat()));
}
return audioFormats;
}
/**
* Obtains an AudioFormat from a javax.sound.sampled.AudioFormat
*
* @param audioFormat The javax.sound.sampled.AudioFormat
* @return The corresponding AudioFormat
*/
private AudioFormat getAudioFormat(javax.sound.sampled.AudioFormat audioFormat) {
String container = AudioFormat.CONTAINER_WAVE;
String codec = audioFormat.getEncoding().toString();
Boolean bigEndian = audioFormat.isBigEndian();
int frameSize = audioFormat.getFrameSize(); // In bytes
int bitsPerFrame = frameSize * 8;
Integer bitDepth = NOT_SPECIFIED == frameSize ? null : bitsPerFrame;
float frameRate = audioFormat.getFrameRate();
Integer bitRate = NOT_SPECIFIED == frameRate ? null : (int) frameRate * bitsPerFrame;
float sampleRate = audioFormat.getSampleRate();
Long frequency = NOT_SPECIFIED == sampleRate ? null : (long) sampleRate;
return new AudioFormat(container, codec, bigEndian, bitDepth, bitRate, frequency);
}
@Override
public String getId() {
return "marytts";
}
@Override
public String getLabel(Locale locale) {
return "MaryTTS";
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.voice.marytts.internal;
import java.util.Locale;
import org.openhab.core.voice.Voice;
/**
* Implementation of the Voice interface for MaryTTS
*
* @author Kelly Davis - Initial contribution and API
* @author Kai Kreuzer - Refactored to updated APIs and moved to openHAB
*/
public class MaryTTSVoice implements Voice {
/**
* Voice locale
*/
private final Locale locale;
/**
* Voice label
*/
private final String label;
/**
* Constructs a MaryTTS Voice for the passed data
*
* @param locale The Locale of the voice
* @param label The label of the voice
*/
public MaryTTSVoice(Locale locale, String label) {
this.locale = locale;
this.label = label;
}
/**
* Globally unique identifier of the voice.
*
* @return A String uniquely identifying the voice globally
*/
@Override
public String getUID() {
return "marytts:" + label.replaceAll("[^a-zA-Z0-9_]", "");
}
/**
* The voice label, used for GUI's or VUI's
*
* @return The voice label, may not be globally unique
*/
@Override
public String getLabel() {
return label;
}
/**
* @inheritDoc
*/
@Override
public Locale getLocale() {
return locale;
}
}

View File

@@ -0,0 +1,6 @@
marytts.config.MainConfig
marytts.language.de.GermanConfig
marytts.language.en.EnglishConfig
marytts.voice.Bits1Hsmm.Config
marytts.voice.Bits3Hsmm.Config
marytts.voice.CmuSltHsmm.Config