Skip to content

Commit 4db0f03

Browse files
committed
Update dependencies
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
1 parent 688be46 commit 4db0f03

3 files changed

Lines changed: 258 additions & 11 deletions

File tree

bundles/org.openhab.voice.marytts/pom.xml

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
<name>openHAB Add-ons :: Bundles :: Voice :: Mary Text-to-Speech</name>
1616

1717
<properties>
18-
<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;sun.nio.ch.*</bnd.importpackage>
18+
<bnd.importpackage>com.twmacinta.util;resolution:=optional;gnu.trove;resolution:=optional;Jampack;resolution:=optional;com.sun.*;resolution:=optional;jdk.javadoc.doclet;resolution:=optional;org.apache.http*;resolution:=optional;org.hsqldb;resolution:=optional;org.jdesktop.layout*;resolution:=optional;org.apache.xerces.impl*;resolution:=optional;sun.nio.ch.*;resolution:=optional</bnd.importpackage>
19+
<dfkimarytts.version>5.2.1</dfkimarytts.version>
1920
</properties>
2021

2122
<dependencies>
2223
<dependency>
2324
<groupId>com.ibm.icu</groupId>
2425
<artifactId>icu4j</artifactId>
25-
<version>54.1.1</version>
26+
<version>78.3</version>
2627
<scope>compile</scope>
2728
</dependency>
2829
<dependency>
@@ -46,7 +47,7 @@
4647
<dependency>
4748
<groupId>de.dfki.lt.jtok</groupId>
4849
<artifactId>jtok-core</artifactId>
49-
<version>1.9.3</version>
50+
<version>1.9.4</version>
5051
<scope>compile</scope>
5152
<exclusions>
5253
<exclusion>
@@ -62,13 +63,13 @@
6263
<dependency>
6364
<groupId>de.dfki.mary</groupId>
6465
<artifactId>emotionml-checker-java</artifactId>
65-
<version>1.1</version>
66+
<version>1.2.3</version>
6667
<scope>compile</scope>
6768
</dependency>
6869
<dependency>
6970
<groupId>de.dfki.mary</groupId>
7071
<artifactId>marytts-common</artifactId>
71-
<version>5.2.1</version>
72+
<version>${dfkimarytts.version}</version>
7273
<scope>compile</scope>
7374
<exclusions>
7475
<exclusion>
@@ -84,25 +85,25 @@
8485
<dependency>
8586
<groupId>de.dfki.mary</groupId>
8687
<artifactId>marytts-lang-de</artifactId>
87-
<version>5.2.1</version>
88+
<version>${dfkimarytts.version}</version>
8889
<scope>compile</scope>
8990
</dependency>
9091
<dependency>
9192
<groupId>de.dfki.mary</groupId>
9293
<artifactId>marytts-lang-en</artifactId>
93-
<version>5.2.1</version>
94+
<version>${dfkimarytts.version}</version>
9495
<scope>compile</scope>
9596
</dependency>
9697
<dependency>
9798
<groupId>de.dfki.mary</groupId>
9899
<artifactId>marytts-runtime</artifactId>
99-
<version>5.2.1</version>
100+
<version>${dfkimarytts.version}</version>
100101
<scope>compile</scope>
101102
</dependency>
102103
<dependency>
103104
<groupId>de.dfki.mary</groupId>
104105
<artifactId>marytts-signalproc</artifactId>
105-
<version>5.2.1</version>
106+
<version>${dfkimarytts.version}</version>
106107
<scope>compile</scope>
107108
</dependency>
108109
<dependency>
@@ -122,7 +123,7 @@
122123
<dependency>
123124
<groupId>de.dfki.mary</groupId>
124125
<artifactId>voice-cmu-slt-hsmm</artifactId>
125-
<version>5.2.1</version>
126+
<version>${dfkimarytts.version}</version>
126127
<scope>compile</scope>
127128
</dependency>
128129
<dependency>
@@ -131,6 +132,18 @@
131132
<version>1.0.3</version>
132133
<scope>compile</scope>
133134
</dependency>
135+
<dependency>
136+
<groupId>org.apache.logging.log4j</groupId>
137+
<artifactId>log4j-1.2-api</artifactId>
138+
<version>2.25.2</version>
139+
<scope>test</scope>
140+
</dependency>
141+
<dependency>
142+
<groupId>org.apache.logging.log4j</groupId>
143+
<artifactId>log4j-core</artifactId>
144+
<version>2.25.2</version>
145+
<scope>test</scope>
146+
</dependency>
134147
<dependency>
135148
<groupId>org.apache.opennlp</groupId>
136149
<artifactId>opennlp-maxent</artifactId>
@@ -140,7 +153,7 @@
140153
<dependency>
141154
<groupId>org.apache.opennlp</groupId>
142155
<artifactId>opennlp-tools</artifactId>
143-
<version>1.5.3</version>
156+
<version>2.5.11</version>
144157
<scope>compile</scope>
145158
</dependency>
146159
<dependency>
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Copyright (c) 2010-2026 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 marytts.modules;
14+
15+
import static java.nio.charset.StandardCharsets.UTF_8;
16+
17+
import java.io.BufferedReader;
18+
import java.io.InputStream;
19+
import java.io.InputStreamReader;
20+
import java.util.ArrayList;
21+
import java.util.HashMap;
22+
import java.util.List;
23+
import java.util.Map;
24+
import java.util.StringTokenizer;
25+
26+
import org.eclipse.jdt.annotation.NonNullByDefault;
27+
import org.eclipse.jdt.annotation.Nullable;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
30+
import org.w3c.dom.Document;
31+
import org.w3c.dom.Element;
32+
import org.w3c.dom.traversal.NodeIterator;
33+
import org.w3c.dom.traversal.TreeWalker;
34+
35+
import marytts.datatypes.MaryData;
36+
import marytts.datatypes.MaryDataType;
37+
import marytts.server.MaryProperties;
38+
import marytts.util.MaryUtils;
39+
import marytts.util.dom.MaryDomUtils;
40+
import opennlp.tools.postag.POSModel;
41+
import opennlp.tools.postag.POSTagFormat;
42+
import opennlp.tools.postag.POSTaggerME;
43+
44+
/**
45+
* OpenNLP POS tagger compatible with the model and tag set expected by MaryTTS 5.2.1.
46+
*
47+
* <p>
48+
* MaryTTS 5.2.1 uses OpenNLP's removed {@code tag(List)} API and the legacy one-argument constructor. This replacement
49+
* uses the current array API and explicitly retains Penn Treebank output, on which MaryTTS's English prosody rules
50+
* depend.
51+
*
52+
* @author Leo Siepel - Initial contribution
53+
*/
54+
@NonNullByDefault
55+
public class OpenNLPPosTagger extends InternalModule {
56+
57+
private static final Logger LOGGER = LoggerFactory.getLogger(OpenNLPPosTagger.class);
58+
59+
private final String propertyPrefix;
60+
61+
private @Nullable POSTaggerME tagger;
62+
private @Nullable Map<String, String> posMapper;
63+
64+
public OpenNLPPosTagger(String locale, String propertyPrefix) throws Exception {
65+
super("OpenNLPPosTagger", MaryDataType.WORDS, MaryDataType.PARTSOFSPEECH, MaryUtils.string2locale(locale));
66+
this.propertyPrefix = propertyPrefix.endsWith(".") ? propertyPrefix : propertyPrefix + ".";
67+
}
68+
69+
@Override
70+
public void startup() throws Exception {
71+
super.startup();
72+
73+
try (InputStream modelStream = MaryProperties.needStream(propertyPrefix + "model")) {
74+
tagger = new POSTaggerME(new POSModel(modelStream), POSTagFormat.PENN);
75+
}
76+
77+
InputStream posMapStream = MaryProperties.getStream(propertyPrefix + "posMap");
78+
if (posMapStream != null) {
79+
posMapper = readPosMapper(posMapStream);
80+
}
81+
}
82+
83+
private Map<String, String> readPosMapper(InputStream posMapStream) throws Exception {
84+
Map<String, String> mapper = new HashMap<>();
85+
try (posMapStream; BufferedReader reader = new BufferedReader(new InputStreamReader(posMapStream, UTF_8))) {
86+
String line;
87+
while ((line = reader.readLine()) != null) {
88+
if (!line.startsWith("#") && !line.isBlank()) {
89+
StringTokenizer tokenizer = new StringTokenizer(line);
90+
mapper.put(tokenizer.nextToken(), tokenizer.nextToken());
91+
}
92+
}
93+
}
94+
return mapper;
95+
}
96+
97+
@Override
98+
public MaryData process(@NonNullByDefault({}) MaryData input) throws Exception {
99+
Document document = input.getDocument();
100+
NodeIterator sentenceIterator = MaryDomUtils.createNodeIterator(document, document, "s");
101+
102+
Element sentence;
103+
while ((sentence = (Element) sentenceIterator.nextNode()) != null) {
104+
tagSentence(sentence);
105+
}
106+
107+
MaryData output = new MaryData(getOutputType(), input.getLocale());
108+
output.setDocument(document);
109+
return output;
110+
}
111+
112+
private void tagSentence(Element sentence) {
113+
TreeWalker tokenWalker = MaryDomUtils.createTreeWalker(sentence, "t");
114+
List<Element> tokens = new ArrayList<>();
115+
List<String> words = new ArrayList<>();
116+
117+
Element token;
118+
while ((token = (Element) tokenWalker.nextNode()) != null) {
119+
tokens.add(token);
120+
words.add(MaryDomUtils.tokenText(token));
121+
}
122+
123+
// OpenNLP cannot determine useful context from a one-token sentence.
124+
if (words.size() == 1) {
125+
words.add(".");
126+
}
127+
128+
String[] tags;
129+
synchronized (this) {
130+
POSTaggerME tagger = this.tagger;
131+
if (tagger != null) {
132+
tags = tagger.tag(words.toArray(String[]::new));
133+
} else {
134+
tags = new String[words.size()];
135+
}
136+
}
137+
138+
for (int i = 0; i < tokens.size(); i++) {
139+
Element currentToken = tokens.get(i);
140+
if (!currentToken.hasAttribute("pos")) {
141+
currentToken.setAttribute("pos", mapPosTag(tags[i]));
142+
}
143+
}
144+
}
145+
146+
private String mapPosTag(String posTag) {
147+
Map<String, String> posMapper = this.posMapper;
148+
if (posMapper == null) {
149+
return posTag;
150+
}
151+
152+
String mappedTag = posMapper.get(posTag);
153+
if (mappedTag == null) {
154+
LOGGER.warn("POS map file incomplete: do not know how to map '{}'", posTag);
155+
return posTag;
156+
}
157+
return mappedTag;
158+
}
159+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2010-2026 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.marytts.internal;
14+
15+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
16+
import static org.junit.jupiter.api.Assertions.assertTrue;
17+
18+
import java.util.Locale;
19+
import java.util.stream.IntStream;
20+
21+
import javax.sound.sampled.AudioInputStream;
22+
23+
import org.junit.jupiter.api.BeforeAll;
24+
import org.junit.jupiter.api.Test;
25+
import org.w3c.dom.Document;
26+
import org.w3c.dom.Element;
27+
import org.w3c.dom.NodeList;
28+
29+
import marytts.LocalMaryInterface;
30+
import marytts.MaryInterface;
31+
import marytts.util.dom.MaryDomUtils;
32+
33+
/**
34+
* Runtime compatibility tests for MaryTTS and its upgraded dependencies.
35+
*
36+
* @author Leo Siepel - Initial contribution
37+
*/
38+
public class MaryTTSRuntimeTest {
39+
40+
private static MaryInterface marytts;
41+
42+
@BeforeAll
43+
public static void setUp() throws Exception {
44+
System.setProperty("mary.base", "target");
45+
marytts = new LocalMaryInterface();
46+
marytts.setLocale(Locale.US);
47+
marytts.setInputType("TEXT");
48+
}
49+
50+
@Test
51+
public void posTaggerRetainsPennTreebankTags() throws Exception {
52+
marytts.setOutputType("PARTSOFSPEECH");
53+
54+
Document document = marytts.generateXML("I want to go to the store.");
55+
NodeList tokens = document.getElementsByTagName("t");
56+
String[] words = IntStream.range(0, tokens.getLength())
57+
.mapToObj(i -> MaryDomUtils.tokenText((Element) tokens.item(i))).toArray(String[]::new);
58+
String[] tags = IntStream.range(0, tokens.getLength())
59+
.mapToObj(i -> ((Element) tokens.item(i)).getAttribute("pos")).toArray(String[]::new);
60+
61+
assertArrayEquals(new String[] { "I", "want", "to", "go", "to", "the", "store", "." }, words);
62+
assertArrayEquals(new String[] { "PRP", "VBP", "TO", "VB", "TO", "DT", "NN", "." }, tags);
63+
}
64+
65+
@Test
66+
public void englishSynthesisProducesAudio() throws Exception {
67+
marytts.setOutputType("AUDIO");
68+
69+
try (AudioInputStream audio = marytts.generateAudio("This is a synthesis test.")) {
70+
int frameSize = Math.max(1, audio.getFormat().getFrameSize());
71+
byte[] audioData = new byte[frameSize * 256];
72+
assertTrue(audio.read(audioData) > 0);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)