Java后端整合科大讯飞语音合成api————pcm格式和wav、mp3格式的转换

本文分享了在没有现成音频资源的情况下,如何利用科大讯飞语音合成API实现小语种文字转语音的过程,包括参数配置、常见坑点及解决方法,并提供了PCM转WAV和MP3的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

踩坑

说实话本来对接api就是一个踩坑的过程,毕竟每个官方文档也好博客也好,都可能出现坑。所以这里也只能将我踩过的一些坑告诉大家,主要分享一下小语种语音合成的流程。如果只是想中英文的语音合成直接用百度开放的接口就好,流程很简单这里就不多说了,可以参考一下这篇博客

之前找好词库之后因为没有找到既有音频又有词库的统一资料,所以我也不可能再去单独去找音频然后在和单词进行人工的绑定,只有考虑对接语音合成的api将单词进行语音合成,结果貌似大多厂商都只是支持了汉语和英语两种类型的合成,小语种找了半天好像也就讯飞科技和Google两个平台提供。然而Google云开发平台从去年就停止对中国提供开放了。无奈只有微笑。

最后选择了讯飞语音。

正文篇

讯飞语音开放平台基础环境搭建

1.用户注册

传送门

2.注册好后先创建一个自己的应用

创建完成后进入应用可以看到我们开发需要的三个参数:APPID,APISecret,APIKey


3.因为平台提供的SDK中只支持了简单的中英两种语言语音合成,所以这里我们不会用提供的Java的SDK包。

这里是直接使用WebAPI的方式进行整合。到这里你可以直接下载他提供的demo代码,然后把上面我们提到的三个参数换成你自己的就可以完成简单的中文语音的合成了,实际上还是挺简单的。当然后面我也会提供给大家我整合好的demo。

4.后续如果大家需要把demo中的jar使用到项目中可以将jar包安装到自己的maven仓库,安装方法的话可以参考这篇博客

官方demo踩坑

我们主要使用到是WebTTSWS这个类。

1.替换三个参数,以及你需要合成的文本内容。

2.其实已经提示的很明显了,小语种需要和对应的小语种发音人进行配合使用,所以我们还需要在应用中添加对应的小语种发音人。

进入我们的控制台选流式版的语音合成,添加对应的小语种发音人。然后小语种发音人的参数就是我们需要在代码进行配置的。

3.修改几个小语种合成的参数。

参数名类型必传描述示例
entstring引擎类型,可选值:
aisound(普通效果)
intp65(中文)
intp65_en(英文)
mtts(小语种,需配合小语种发音人使用)
xtts(优化效果)
默认为intp65
"intp65"
vcnstring发音人,可选值:请到控制台添加试用或购买发音人,添加后即显示发音人参数值"xiaoyan"
ttestring文本编码格式
GB2312
GBK
BIG5
UNICODE(小语种必须使用UNICODE编码,合成的文本需使用utf16小端的编码方式,详见java示例demo)
GB18030
UTF8
"UTF8"

4.现在可以运行一下demo。运行成功,在对应的路径下就是我们合成的音频文件。如果你还是踩到其他坑了,那么后面就需要你自己去根据返回的错误代码去官方文档一步一步排坑了。

格式转换

因为官方demo中合成的是pcm的格式,而一般我们使用的可播放格式是wav和mp3的格式。所以在使用api合成后我们还不能直接播放,所以我们需要在进行格式的转换。

转换工具类:

package com.clf.word2audio;

import ws.schild.jave.AudioAttributes;
import ws.schild.jave.Encoder;
import ws.schild.jave.EncodingAttributes;
import ws.schild.jave.MultimediaObject;

import java.io.*;

/**
 * @Author: clf
 * @Date: 2020-03-08
 * @Description: 语音合成工具类
 */
public class ConvertUtils {

    /**
     * 转换音频文件
     * @param src 需要转换的pcm音频路径
     * @param target 保存转换后wav格式的音频路径
     * @throws Exception
     */
    public static void convertPcm2Wav(String src, String target) throws Exception {
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(target);

        //计算长度
        byte[] buf = new byte[1024 * 4];
        int size = fis.read(buf);
        int PCMSize = 0;
        while (size != -1) {
            PCMSize += size;
            size = fis.read(buf);
        }
        fis.close();

        //填入参数,比特率等等。这里用的是16位单声道 8000 hz
        WaveHeader header = new WaveHeader();
        //长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
        header.fileLength = PCMSize + (44 - 8);
        header.FmtHdrLeth = 16;
        header.BitsPerSample = 16;
        header.Channels = 2;
        header.FormatTag = 0x0001;
        header.SamplesPerSec = 8000;
        header.BlockAlign = (short)(header.Channels * header.BitsPerSample / 8);
        header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
        header.DataHdrLeth = PCMSize;

        byte[] h = header.getHeader();

        assert h.length == 44; //WAV标准,头部应该是44字节
        //write header
        fos.write(h, 0, h.length);
        //write data stream
        fis = new FileInputStream(src);
        size = fis.read(buf);
        while (size != -1) {
            fos.write(buf, 0, size);
            size = fis.read(buf);
        }
        fis.close();
        fos.close();
        System.out.println("Convert OK!");
    }


    /**
     * wav格式转换成mp3格式
     * @param source  源文件
     * @param target 目标文件
     * @return
     */
    public static boolean convertWav2Mp3(File source, File target) {
        boolean succeeded = true;
        try {
            AudioAttributes audio = new AudioAttributes();
            audio.setCodec("libmp3lame");
            audio.setBitRate(128000);
            audio.setChannels(2);
            audio.setSamplingRate(44100);
            audio.setVolume(new Integer(256));

            EncodingAttributes attrs = new EncodingAttributes();
            attrs.setFormat("mp3");
            attrs.setAudioAttributes(audio);
            Encoder encoder = new Encoder();
            encoder.encode(new MultimediaObject(source), target, attrs);
        } catch (Exception ex) {
            ex.printStackTrace();
            succeeded = false;
        }
        return succeeded;
    }

}

pcm文件转换wav需要的header:

package com.clf.word2audio;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * @Author: clf
 * @Date: 2020-03-07
 * @Description: wav转换mp3的header
 */
public class WaveHeader {

    public final char fileID[] = {'R', 'I', 'F', 'F'};
    public int fileLength;
    public char wavTag[] = {'W', 'A', 'V', 'E'};;
    public char FmtHdrID[] = {'f', 'm', 't', ' '};
    public int FmtHdrLeth;
    public short FormatTag;
    public short Channels;
    public int SamplesPerSec;
    public int AvgBytesPerSec;
    public short BlockAlign;
    public short BitsPerSample;
    public char DataHdrID[] = {'d','a','t','a'};
    public int DataHdrLeth;

    public byte[] getHeader() throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        WriteChar(bos, fileID);
        WriteInt(bos, fileLength);
        WriteChar(bos, wavTag);
        WriteChar(bos, FmtHdrID);
        WriteInt(bos,FmtHdrLeth);
        WriteShort(bos,FormatTag);
        WriteShort(bos,Channels);
        WriteInt(bos,SamplesPerSec);
        WriteInt(bos,AvgBytesPerSec);
        WriteShort(bos,BlockAlign);
        WriteShort(bos,BitsPerSample);
        WriteChar(bos,DataHdrID);
        WriteInt(bos,DataHdrLeth);
        bos.flush();
        byte[] r = bos.toByteArray();
        bos.close();
        return r;
    }

    private void WriteShort(ByteArrayOutputStream bos, int s) throws IOException {
        byte[] mybyte = new byte[2];
        mybyte[1] =(byte)( (s << 16) >> 24 );
        mybyte[0] =(byte)( (s << 24) >> 24 );
        bos.write(mybyte);
    }


    private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {
        byte[] buf = new byte[4];
        buf[3] =(byte)( n >> 24 );
        buf[2] =(byte)( (n << 8) >> 24 );
        buf[1] =(byte)( (n << 16) >> 24 );
        buf[0] =(byte)( (n << 24) >> 24 );
        bos.write(buf);
    }

    private void WriteChar(ByteArrayOutputStream bos, char[] id) {
        for (int i=0; i<id.length; i++) {
            char c = id[i];
            bos.write(c);
        }
    }

}

需要使用到的相关pom依赖:

        <!--utils-->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>2.4.4</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-native-osx64</artifactId>
            <version>2.4.6</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode.soundlibs</groupId>
            <artifactId>mp3spi</artifactId>
            <version>1.9.5.4</version>
        </dependency>

最后

当然如果还是需要源码的话,可以从这里下载。

顺便做一波推广,对jdk源码有兴趣的朋友可以关注一下: JDK-Source,也欢迎大家PR。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值