基于java语言,使用科大讯飞Java sdk实时语音合成开发案例

使用背景:

  最近在工作项目中,有一个智能机器人闲聊的模块需要开发,通过用户的语音或者手动输入文字,机器人需要给出对应的文字输出和语音输出,这时候就需要用到科大讯飞的实时语音合成技术了.

准备工作:

  首先需要在科大讯飞开发平台(https://console.xfyun.cn/services)注册一个账号,没有付费的账号接口调用服务是有数量限制的.如图

下载java MSC

下载之后,会得到一个zip包,解压缩之后里面有一些jar包和几个demo,以及两个so和两个dll文件

 如果你是在本地开发环境,则只需要将这四个文件放到resources目录下面,但是注意,如果项目发布到linux服务器的话,需要将四个文件放到对应的位置: /usr/java/packages/lib/amd64 ,如果没有这个目录则新建.

然后里面有两个jar包需要用到,将两个jar包放到resources下的lib目录里面,没有则新建,然后再pom中引入本地lib下的文件

  
<dependency>
<groupId>json-jena</groupId>
<artifactId>任意</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/json-jena-1.0.jar</systemPath>
</dependency>
<dependency>
<groupId>msc</groupId>
<artifactId>任意</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/Msc.jar</systemPath>
</dependency>

做完上面的两步,就可以开始撸代码了.

如果使用代理服务器,则需要开通一些防火墙:

  • 代理服务器不能对80端口做限制,不能对如下域名做拦截: hdns.openspeech.cn scs.openspeech.cn open.xf-yun.com dev.voicecloud.cn

代码开发:

  1.启动配置-当springboot项目启动后,需要首先实例化科大讯飞的初始化类,如果有代理的话,则代理信息也是在这里配置

@Configuration
public class VoiceConfig implements ApplicationRunner {

    @Value("${appid}")
    private String appId;
    @Value("${proxy.ip}")
    private String proxyIp;
    @Value("${proxy.port}")
    private String port;

    @Override
    public void run(ApplicationArguments args) {
        SpeechUtility.createUtility(SpeechConstant.APPID + "=" + appId + "," + String.format("net_type=custom,proxy_ip=%s,proxy_port=%s", proxyIp, port));
    }
}

 其中的appId在科大讯飞开放平台就能看到

然后就是文字输入,音频输入的工具类,很简单,不过科大讯飞默认的音频格式是pcm,如果想要mp3,则还需要进行转换

public static void textToMp3Url(String contentStr, String sourceFileName, String targetFileName) {
        SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer();
        mTts.setParameter(SpeechConstant.VOICE_NAME, "XXXXXX"); // 设置发音人,这里根据自己控制台拥有的发语音名称配置
        mTts.setParameter(SpeechConstant.SPEED, "50"); // 设置语速,范围0~100 默认50
        mTts.setParameter(SpeechConstant.PITCH, "50"); // 设置语调,范围0~100
        mTts.setParameter(SpeechConstant.VOLUME, "50"); // 设置音量,范围0~100
        SynthesizeToUriListener synthesizeToUriListener = new SynthesizeToUriListener() {
            // progress为合成进度0~100
            @Override
            public void onBufferProgress(int progress) {
                if (progress == 100) {
                    try {
//将pcm转换成mp3文件 pcmToMp3(sourceFileName, targetFileName); }
catch (Exception e) { log.error("合成语音异常:", e); } } } // 会话合成完成回调接口 // uri为合成保存地址,error为错误信息,为null时表示合成会话成功 @Override public void onSynthesizeCompleted(String uri, SpeechError error) { log.info("语音合成开始 uri==>" + uri + "," + error);
          //这里可以写一些业务逻辑
} @Override public void onEvent(int i, int i1, int i2, int i3, Object o, Object o1) { } }; mTts.synthesizeToUri(contentStr, sourceFileName, synthesizeToUriListener); }
//pcm格式转换成mp3格式代码
public
static void pcmToMp3(String src, String target) throws IOException { 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 = 1; header.FormatTag = 0x0001; header.SamplesPerSec = 16000; 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(); }

 

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);
        }
    }
}

至此,就能实现文字转mp3格式的音频文件了,将对应的文件路径给到前端,就能实现播放了.

 

posted @ 2024-04-08 14:57  蜗牛滑板鞋  阅读(388)  评论(0编辑  收藏  举报