惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
The GitHub Blog
The GitHub Blog
博客园_首页
T
The Blog of Author Tim Ferriss
H
Hackread – Cybersecurity News, Data Breaches, AI and More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
腾讯CDC
I
InfoQ
量子位
J
Java Code Geeks
P
Proofpoint News Feed
有赞技术团队
有赞技术团队
Webroot Blog
Webroot Blog
Martin Fowler
Martin Fowler
D
Docker
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
V
Vulnerabilities – Threatpost
罗磊的独立博客
P
Proofpoint News Feed
T
The Exploit Database - CXSecurity.com
Cyberwarzone
Cyberwarzone
P
Privacy & Cybersecurity Law Blog
Last Week in AI
Last Week in AI
爱范儿
爱范儿
The Hacker News
The Hacker News
S
SegmentFault 最新的问题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
V
V2EX
Simon Willison's Weblog
Simon Willison's Weblog
AI
AI
Y
Y Combinator Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
GbyAI
GbyAI
V
Visual Studio Blog
H
Heimdal Security Blog
S
Secure Thoughts
B
Blog RSS Feed
雷峰网
雷峰网
T
Tenable Blog
C
Check Point Blog
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
About on SuperTechFans
Recent Commits to openclaw:main
Recent Commits to openclaw:main

博客园 - 海乐学习

Java Maven 开发的常用命令 C语言开发的常用命令 三汇Linux配置Config说明 Java实现优雅的关闭程序并执行清理流程的写法 C语言开发中优雅的关闭程序并执行清理流程的写法 将C语言开发的程序做成 麒麟系统的服务(Systemd 标准服务,Linux 通用) 实现开机自启动 ZeroMQ的DEALER双帧结构(路由空帧 + 业务帧)(支持异步收发、主动推送事件)C写服务端 java写客户端 ZeroMQ中ZMQ_DEALER单帧数据(支持异步收发、主动推送事件)C写服务端 java写客户端 win10系统中 关闭专注助手 执行 xx.sh 脚本文件时出现: /bin/bash^M:解释器错误: 没有那个文件或目录 运行编译打包好的 xxx.jar 中没有主清单属性 出现这个错误 将Java编译的 .jar文件做成 麒麟系统的服务(Systemd 标准服务,Linux 通用) 实现开机自启动 将Java编译的 .jar文件做成windows服务 实现开机自启动 方法二 RabbitMQ在麒麟系统中离线安装说明 VMware启动虚似机后出现 无法获取快照信息: 锁定文件失败 模块“Snapshot”启动失败。未能启动虚拟机。 C语言在Linux中开发完整Demo包含读配置文件写日志和定时器Timer C语言在Linux中开发读取配置文件app.conf 麒麟V10 Server系统中搭建C语言开发环境 麒麟ServerV10 修改IP4地址 麒麟ServerV10 配置IP4 当系统中有两个版本的Maven时,用IDEA创建Maven有时会出错 麒麟ServerV10安装 espeak-ng 和 ffmpeg 方法 C语言在Linux中开发没有界面纯后台运行的Demo程序(含日志和Timer) C语言在Linux中开发使用定时器Timer在界面上显示时间 C语言在Linux中开发带界面的程序(含每小时日志) C语言在Linux中开发第一个项目Hello Word 在apache-maven项目中使用log4写日志 在apache-maven项目中解决中文乱码问题 在apache-maven项目中读取配置文件 创建apache-maven项目 远程桌面连接时出现身份验证错误 要求的函数不受支持 这可能是由于CredSSP加密数据库修正 win10系统查看电脑从锁屏状态回到使用状态 apache-maven的常用命令 C语言在 Linux 中的常用命令 apache-maven安装配置 麒麟CentOS下安装ZeroMQ开发包 Window上用VS Code + Remote-SSH组件的方式来实现开发编译Linux上的C++程序 win10弹出 无法使用内置管理员账户打开 Microsoft Edge。请使用其他账户登录 在麒麟系统上安装Qwen3-TTS文字转语音 在麒麟系统上安装MaryTTS文字转语音 FTP上传Linux/Unix文件系统权限的修改方法 麒麟系统Kylin Linux Advanced Server 中安装 python3.10 将exe做成windows服务 java实现ftp上传 node.js和Next.js 编译部署说明
java实现TTS文字转语音wav (Jacob + SAPI)
海乐学习 · 2026-01-13 · via 博客园 - 海乐学习

Jacob + SAPI 的优缺点

Windows集成 SAPI  Jacob 调用系统语音,音质较好,无需安装额外引擎。 但仅限Windows平台,依赖COM组件。

注:此代码生成的wav文件,非单声道,不能直接应该在电话系统中,需要转换格式才行(单声道 8k  6bit)

第一步 下载 jacob-1.19-x64.dll

将 jacob-1.19-x64.dll 存放到工程根目录下

image

 第二步 下载 jacob.jar 引用到工程中

image

 第三步 直接上代码

WindowsTtsHelper.java

package com.JavaTTS_Jacob;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;

public class WindowsTtsHelper {
    private ActiveXComponent voice;
    private Dispatch sapVoice;

    /**
     * 初始化 TTS 引擎
     */
    public WindowsTtsHelper() {
        try {
            this.voice = new ActiveXComponent("SAPI.SpVoice");
            this.sapVoice = voice.getObject();
            System.out.println("TTS引擎初始化成功。");
        } catch (Exception e) {
            System.err.println("初始化TTS引擎失败,请确认系统支持 SAPI。");
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置语音参数
     *
     * @param rate   语速,范围 -10(慢) 到 10(快),0为正常
     * @param volume 音量,范围 0 到 100
     */
    public void setParams(int rate, int volume) {
        try {
            if (rate < -10 || rate > 10) rate = 0;
            if (volume < 0 || volume > 100) volume = 80;

            voice.setProperty("Rate", new Variant(rate));
            voice.setProperty("Volume", new Variant(volume));
        } catch (Exception e) {
            System.err.println("设置参数失败: "+e.getMessage());
        }
    }

    /**
     * 选择并设置指定的发音人
     *
     * @param voiceNameKeyword 发音人名称关键词,如 "Huihui", "David", "Zira"
     * @return 是否设置成功
     */
    public boolean selectVoice(String voiceNameKeyword) {
        try {
            Dispatch voiceItems = Dispatch.call(sapVoice, "GetVoices").toDispatch();
            int count = Dispatch.call(voiceItems, "Count").changeType(Variant.VariantInt).getInt();

            for (int i = 0; i < count; i++) {
                Dispatch voiceItem = Dispatch.call(voiceItems, "Item", new Variant(i)).toDispatch();
                String desc = Dispatch.call(voiceItem, "GetDescription").toString();
                System.out.println("索引 " + i + ": " + desc);//显示所有语音包

                if (desc.toLowerCase().contains(voiceNameKeyword.toLowerCase())) {
                    // 【核心修复行】使用 put 设置属性
                    Dispatch.put(sapVoice, "Voice", voiceItem);
                    System.out.println("发音人设置成功: "+desc);
                    return true;
                }
            }
            System.out.println("未找到包含 ‘" + voiceNameKeyword + "‘的发音人,使用默认。");
            return false;
        } catch (Exception e) {
            System.err.println("选择发音人时出错: "+e.getMessage());
            return false;
        }
    }

    /**
     * 朗读文本(同步,会阻塞直到读完)
     *
     * @param text 要朗读的文本
     */
    public void speak(String text) {
        try {
            Dispatch.call(sapVoice, "Speak", new Variant(text));
        } catch (Exception e) {
            System.err.println("朗读失败: "+e.getMessage());
        }
    }

    /**
     * 将文本合成并保存为 WAV 文件 (修复版)
     * @param text 要合成的文本
     * @param filePath 输出文件完整路径,如 "C:\\output.wav"
     */
    public void saveToFile(String text, String filePath) {
        ActiveXComponent audioStream = null;
        try {
            // 1. 创建文件流对象
            audioStream = new ActiveXComponent("SAPI.SpFileStream");
            Dispatch stream = audioStream.getObject();

            // 2. 打开文件。模式 3 表示:创建用于写入(SSFMCreateForWrite)
            Dispatch.call(stream, "Open", new Variant(filePath), new Variant(3), new Variant(true));

            // 3. 【核心修复】使用 putRef 将文件流设置为语音对象的输出
            Dispatch.putRef(sapVoice, "AudioOutputStream", stream);

            // 4. 此时合成语音,音频数据将自动写入文件
            Dispatch.call(sapVoice, "Speak", new Variant(text));

            System.out.println("文件保存成功: " + filePath);

        } catch (Exception e) {
            System.err.println("保存文件失败: " + e.getMessage());
            e.printStackTrace(); // 打印完整堆栈有助于进一步调试
        } finally {
            // 5. 至关重要的清理工作:关闭流并重置输出
            try {
                if (audioStream != null) {
                    Dispatch stream = audioStream.getObject();
                    Dispatch.call(stream, "Close");
                    // 将音频输出重置回默认的扬声器,避免后续语音无法播放
                    Dispatch.putRef(sapVoice, "AudioOutputStream", null);
                    audioStream.safeRelease();
                }
            } catch (Exception e) {
                // 忽略清理时的次要错误
            }
        }
    }


    /**
     * 释放 COM 资源(重要!使用完毕后必须调用)
     */
    public void release() {
        if (voice != null) {
            voice.safeRelease();
            voice = null;
            sapVoice = null;
            System.out.println("TTS资源已释放。");
        }
    }

}

调用测试代码

App.java

package com.JavaTTS_Jacob;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;

/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        WindowsTtsHelper tts = new WindowsTtsHelper();
        try {
            // 1. 设置参数
            tts.setParams(1, 90); // -2稍慢,音量90%

            // 2. 选择中文发音人(如果系统有安装) 未找到包含 ‘David‘的发音人,使用默认。
            //boolean success = tts.selectVoice("David");

            // 3. 朗读
            //tts.speak("你好,语音导航测试。这是一个修复后的稳定版本。");
            //Thread.sleep(500); // 简单等待一下

            // 4. 保存为文件
            tts.saveToFile("福佳2楼管道漏水,暂时停供,正在抢修,给您造成不便,敬请谅解!", "d:\\joincc_output.wav");
 
            System.out.println("\n所有功能测试完成!");

        } catch (Exception e) {
            System.err.println("WindowsTTS转Wav出错: " + e.getMessage());
            //e.printStackTrace();
        }finally {
            // 确保释放资源
            tts.release();
        }

    }


    //不需要选择特定发音人
    private void TTSRead(){
        ActiveXComponent voice = new ActiveXComponent("SAPI.SpVoice");

        try {
            // 只设置基础参数,不选择特定发音人
            voice.setProperty("Rate", new Variant(0));   // 正常语速
            voice.setProperty("Volume", new Variant(100)); // 最大音量

            // 直接朗读
            String[] texts = {
                    "你好,这是默认语音测试。",
                    "欢迎使用语音导航系统。",
                    "操作完成,感谢使用。"
            };

            for (String text : texts) {
                System.out.println("朗读: " + text);
                Dispatch.call(voice.getObject(), "Speak", new Variant(text));
                Thread.sleep(800); // 稍微停顿
            }

        } catch (Exception e) {
            System.err.println("错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            voice.safeRelease();
        }

    }

}

 pom.xml 

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>5.11.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>com.hynnet</groupId>
        <artifactId>jacob</artifactId>
        <version>1.19</version>
      </dependency>
      
    </dependencies>
  </dependencyManagement>