如何让Mary TTS 5.2库兼容JDK 11?
我需要借助CDI Bean将接收到的土耳其语文本转换为语音,用于网页上点击触发的语音按钮。问题在于Mary TTS仅兼容JDK 8,但我的项目采用Jakarta规范且使用JDK 11。从Mary TTS官网下载后用JDK 8运行效果良好,但无法在项目中正常使用。
CDI Bean代码
protected int bytesRead = 0; public void guvenlikKodSeslendir() { try { // 创建MaryTTS接口 MaryInterface maryTts = new LocalMaryInterface(); // 加载土耳其语区域设置 maryTts.setLocale(new Locale("tr")); // 设置要使用的语音模型 maryTts.setVoice("dfki-pavoque-neutral"); // 设置要朗读的文本 String text = "Merhaba, dünya!"; // 朗读文本并获取音频流数据 AudioInputStream audio = maryTts.generateAudio(str); // 创建用于播放音频流的SourceDataLine try (SourceDataLine line = AudioSystem.getSourceDataLine(audio.getFormat())) { line.open(audio.getFormat()); line.start(); byte[] buffer = new byte[4096]; int bytesRead = 0; while ((bytesRead = audio.read(buffer)) != -1) { line.write(buffer, 0, bytesRead); } line.drain(); line.stop(); } } catch (IOException | LineUnavailableException | MaryConfigurationException | SynthesisException e) { e.printStackTrace(); } }
注:代码中的str会由Facelet页面的内容替换,此处为清晰起见保留占位。
启动Mary TTS服务器时的错误(JDK 11环境)
我下载Mary TTS 5.2版本后,启动bin文件夹中的marytts-server.bat文件(电脑同时安装了JDK 11,未修改路径变量),控制台显示如下警告:
Java version "11.0.18" 2023-01-17 LTS Java(TM) SE Runtime Environment 18.9 (build 11.0.18+9-LTS-195) Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.18+9-LTS-195, mixed mode) MARY server 5.2 starting as a HTTP server...WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass$3$1 (file:/C:/Users/MFC/Documents/marytts-5.2/lib/marytts-client-5.2-jar-with-dependencies.jar) to method java.lang.Object.finalize() WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass$3$1 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release started in 4.966 s on port 59125
使用JDK 8启动时不会出现上述警告。
测试示例类代码
为排查问题,我编写了一个带main方法的测试类:
package tr.com.fatih.beans; import java.io.IOException; import java.util.Locale; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; import marytts.LocalMaryInterface; import marytts.exceptions.MaryConfigurationException; import marytts.exceptions.SynthesisException; public class MaryTTSExample { public static void main(String[] args) { try { LocalMaryInterface maryTts = new LocalMaryInterface(); maryTts.setLocale(new Locale("tr")); maryTts.setVoice("dfki-pavoque-neutral"); String text = "Merhaba, bu bir deneme metnidir."; AudioInputStream audio = maryTts.generateAudio(text); try (SourceDataLine line = AudioSystem.getSourceDataLine(audio.getFormat())) { line.open(audio.getFormat()); line.start(); byte[] buffer = new byte[4096]; int bytesRead = 0; while ((bytesRead = audio.read(buffer)) != -1) { line.write(buffer, 0, bytesRead); } line.drain(); line.stop(); } } catch (IOException | IllegalArgumentException | LineUnavailableException | MaryConfigurationException | SynthesisException e) { e.printStackTrace(); } } }
运行示例类的错误输出
在NetBeans中运行该类时,控制台输出如下错误:
marytts.exceptions.MaryConfigurationException: Cannot start MARY server at marytts.LocalMaryInterface.<init>(LocalMaryInterface.java:66) at tr.com.fatih.beans.MaryTTSExample.main(MaryTTSExample.java:16) Caused by: java.lang.Exception: Problem starting module Synthesis at marytts.server.Mary.startModules(Mary.java:151) at marytts.server.Mary.startup(Mary.java:297) at marytts.server.Mary.startup(Mary.java:204) at marytts.util.MaryRuntimeUtils.ensureMaryStarted(MaryRuntimeUtils.java:72) at marytts.LocalMaryInterface.<init>(LocalMaryInterface.java:64) ... 1 more Caused by: marytts.exceptions.MaryConfigurationException: Cannot build unit selection voice 'dfki-ot' at marytts.unitselection.UnitSelectionVoice.<init>(UnitSelectionVoice.java:218) at marytts.unitselection.UnitSelectionSynthesizer.startup(UnitSelectionSynthesizer.java:94) at marytts.modules.Synthesis.startupSynthesizers(Synthesis.java:73) at marytts.modules.Synthesis.startup(Synthesis.java:65) at marytts.server.Mary.startModules(Mary.java:149) ... 5 more Caused by: marytts.exceptions.NoSuchPropertyException: Cannot read file `voices\dfki-ot\halfphoneFeatures_ac.mry'. Check property `voice.dfki-ot.featureFile' in configuration files at marytts.server.MaryProperties.needFilename(MaryProperties.java:389) at marytts.unitselection.UnitSelectionVoice.<init>(UnitSelectionVoice.java:107) ... 9 more Exception in thread "Thread-1" java.lang.IllegalStateException: MARY system is not running at marytts.server.Mary.shutdown(Mary.java:371) at marytts.server.Mary$2.run(Mary.java:290)
解决方案
1. 服务器模式调用(推荐方案)
Mary TTS 5.2在JDK 11下直接运行本地接口会触发反射限制和配置问题,最稳定的做法是用JDK 8启动独立的Mary TTS服务器,再在JDK 11项目中通过远程客户端调用:
步骤1:用JDK 8启动服务器
修改marytts-server.bat,指定JDK 8的Java路径,例如:"C:\Program Files\Java\jdk1.8.0_381\bin\java" -jar lib/marytts-server-5.2.jar确保服务器正常启动后,会在端口59125监听请求。
步骤2:修改CDI Bean为远程调用
替换LocalMaryInterface为RemoteMaryInterface,通过HTTP连接到服务器:
public void guvenlikKodSeslendir() {
try {
// 连接到本地运行的Mary TTS服务器
MaryInterface maryTts = new RemoteMaryInterface("http://localhost:59125");
maryTts.setLocale(new Locale("tr"));
maryTts.setVoice("dfki-pavoque-neutral");
// 替换为Facelet页面传入的文本
String text = "Merhaba, dünya!";
AudioInputStream audio = maryTts.generateAudio(text);
// 音频播放逻辑保持不变 try (SourceDataLine line = AudioSystem.getSourceDataLine(audio.getFormat())) { line.open(audio.getFormat()); line.start(); byte[] buffer = new byte[4096]; int bytesRead = 0; while ((bytesRead = audio.read(buffer)) != -1) { line.write(buffer, 0, bytesRead); } line.drain(); line.stop(); } } catch (Exception e) { e.printStackTrace(); }
}
这种方式完全隔离了JDK版本差异,稳定性最高。 ### 2. 本地运行的兼容修复(不推荐) 如果必须在JDK 11下本地运行Mary TTS,需要解决两个核心问题: - **解决反射访问限制**:在项目的JVM启动参数中添加:
--add-opens java.base/java.lang=ALL-UNNAMED
允许Groovy框架的反射操作。 - **解决语音文件缺失**:确保Mary TTS的`voices`目录完整,或者在代码启动前设置Mary的基础路径: ```java System.setProperty("mary.base", "C:/Users/MFC/Documents/marytts-5.2");
不过这种方法可能会遇到更多JDK模块化带来的兼容性问题,不如服务器模式可靠。
3. 升级Mary TTS版本
检查Mary TTS官方是否发布了支持JDK 11的更新版本,若有则直接升级依赖包,替换5.2版本,从根源解决兼容性问题。
内容的提问来源于stack exchange,提问作者Mehmet Fatih ÇİN

