基于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格式的音频文件了,将对应的文件路径给到前端,就能实现播放了.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步