帖子

Java通过JNA调用离线语音合成dll

[复制链接]

该用户从未签到

2130 159****1928 发表于 2020-7-27 16:23:00 1#
放置资源(dll、jet、日志配置);
通过JNA加载dll资源;
登录MSPLogin;
开始会话QTTSSessionBegin;
写入文本QTTSTextPut;
while(true){
     获取合成音频QTTSAudioGet;
     if(synthStatus==2)跳出;
}
终止会话QTTSSessionEnd;
退出MSPLogout;

1.整体代码
  1. package com.day.iFlyInterface.commonUtil.dll.tts;
  2. import java.io.BufferedOutputStream;
  3. import java.io.ByteArrayInputStream;
  4. import java.io.ByteArrayOutputStream;
  5. import java.io.File;
  6. import java.io.FileInputStream;
  7. import java.io.FileOutputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. import java.util.Arrays;
  11. import java.util.Base64;
  12. import javax.sound.sampled.AudioFormat;
  13. import javax.sound.sampled.AudioInputStream;
  14. import javax.sound.sampled.AudioSystem;
  15. import javax.sound.sampled.DataLine;
  16. import javax.sound.sampled.LineUnavailableException;
  17. import javax.sound.sampled.SourceDataLine;
  18. import com.sun.jna.Library;
  19. import com.sun.jna.Memory;
  20. import com.sun.jna.Native;
  21. import com.sun.jna.Pointer;
  22. import com.sun.jna.ptr.IntByReference;
  23. public class OfflineWindowsTts {
  24.         /*const char *MSPAPI        QTTSSessionBegin(const char *params, int *errorCode)
  25.         开始一次语音合成,分配语音合成资源。
  26.         int MSPAPI        QTTSTextPut(const char *sessionID, const char *textString, unsigned int textLen, const char *params)
  27.         写入要合成的文本。
  28.         const void *MSPAPI        QTTSAudioGet(const char *sessionID, unsigned int *audioLen, int *synthStatus, int *errorCode)
  29.         获取合成音频。
  30.         int MSPAPI        QTTSSessionEnd(const char *sessionID, const char *hints)
  31.         结束本次语音合成。
  32.         int MSPAPI        QTTSGetParam(const char *sessionID, const char *paramName, char *paramValue, unsigned int *valueLen)
  33.         获取当前语音合成信息,如当前合成音频对应文本结束位置、上行流量、下行流量等。*/
  34.         public interface MyDllInterface extends Library {
  35.                 MyDllInterface INSTANCE = (MyDllInterface)Native.loadLibrary("Tts_x64", MyDllInterface.class);
  36.                 public int MSPLogin(String usr,String pwd,String params);
  37.                 //文档方法写错了,耽搁一段时间   net.java.dev.jna需要这个包
  38.                 //char * 对应String ;;;int *对应IntByReference
  39.                 String QTTSSessionBegin(String params, IntByReference errorCode);
  40.                 //char * 对应String ;;;int 对应int;;;
  41.                 public int QTTSTextPut(String sessionID,String textString,int textLen,String params);
  42.                 //void * 对应Pointer;;;char *对应String ;;;int * 对应IntByReference;;;
  43.                 Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);
  44.                 //char *对应String;;;
  45.                 public int QTTSSessionEnd(String sessionID, String hints);
  46.                 public int MSPLogout();
  47.         }
  48.         public static void main(String[] args) throws Exception {
  49.                 doTts("[p500]浊酒一杯家万里,燕[=yan4]然未勒归无计");
  50.         }
  51.         public static void doTts(String text) throws Exception{
  52.                 String login_params = "appid = 填写你自己的appid, work_dir = ./dllLib/tts";//注意资源存放路径
  53.                 String session_begin_params = "engine_type = local, voice_name = xiaoyan, text_encoding = UTF8, tts_res_path = fo|res\\tts\\xiaoyan.jet;fo|res\\tts\\common.jet, sample_rate = 16000, speed = 50, volume = 50, pitch = 50, rdn = 2";
  54.                 String filename = "tts_sample.wav"; //合成的语音文件名称
  55.                 //String text = "[p500]浊酒一杯家万里,燕[=yan4]然未勒归无计"; //合成文本
  56.                 int ret = MyDllInterface.INSTANCE.MSPLogin(null, null, login_params);
  57.                 if (0!= ret)
  58.                 {
  59.                         System.out.println("MSPLogin failed, error code: %d.\n"+ret);
  60.                 }
  61.                 System.out.println("## 语音合成(Text To Speech,TTS)技术能够自动将任意文字实时转换为连续的 ##");
  62.                 System.out.println("## 自然语音,是一种能够在任何时间、任何地点,向任何人提供语音信息服务的  ##");
  63.                 System.out.println("## 高效便捷手段,非常符合信息时代海量数据、动态更新和个性化查询的需求。  ##");
  64.                 System.out.println("开始合成 ...");
  65.                 IntByReference retOneIntByReference = new IntByReference(-1);
  66.                 String sessionID = MyDllInterface.INSTANCE.QTTSSessionBegin(session_begin_params,retOneIntByReference);
  67.                 System.out.println("sessionID:"+sessionID+";;;C/C++中的文本长度"+text.getBytes().length);
  68.                 //text.length()*3
  69.                 ret= MyDllInterface.INSTANCE.QTTSTextPut(sessionID, text, text.getBytes().length, null);
  70.                 System.out.println("正在合成 ...");
  71.                 int audio_len=1280;
  72.                 int synth_status=1;
  73.                 File file=new File(filename);
  74.                 IntByReference audioLenIntByReference = new IntByReference(0);
  75.                 IntByReference synthStatusIntByReference = new IntByReference(1);
  76.                 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(960000000);
  77.                 int totalAudioLength = 0;
  78.                 int indexFlag=0;
  79.                
  80.                 //设置音频实时播放参数,
  81.                 AudioFormat audioFormat =new AudioFormat(16000F, 16, 1,true,false);
  82.                 // 设置数据输入,开启播放监听
  83.                 DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class,audioFormat, AudioSystem.NOT_SPECIFIED);
  84.                 SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
  85.                 sourceDataLine.open(audioFormat);
  86.                 sourceDataLine.start();
  87.                
  88.                 while (true) {
  89.                         /* 获取合成音频 */
  90.                         //System.err.println("即将发生错误");
  91.                         //const void *不知道怎么转换
  92.                         //System.err.println(ret);
  93.                         Pointer dataPoint = MyDllInterface.INSTANCE.QTTSAudioGet(sessionID,audioLenIntByReference,synthStatusIntByReference,retOneIntByReference);
  94.                         if (0!= ret){
  95.                                 System.err.println(ret);
  96.                                 break;
  97.                         }
  98.                         //System.err.println(retOneIntByReference.getValue());
  99.                         byte[] dataByteAudio;
  100.                         if (dataPoint!=null ){
  101.                                 dataByteAudio = dataPoint.getByteArray(0, audioLenIntByReference.getValue());
  102.                                 try{
  103.                                         //System.out.println("本次获得音频长度"+audioLenIntByReference.getValue());
  104.                                         //System.out.println(dataByteAudio.toString());
  105.                                         byteArrayOutputStream.write(dataByteAudio,0,audioLenIntByReference.getValue());
  106.                                         //每次合成都写入到实时播放的线程中...
  107.                                         sourceDataLine.write(dataByteAudio, 0, audioLenIntByReference.getValue());        
  108.                                 }catch(Exception e){
  109.                                         e.printStackTrace();
  110.                                 }
  111.                                 indexFlag=indexFlag+audioLenIntByReference.getValue();
  112.                                 totalAudioLength=totalAudioLength+audioLenIntByReference.getValue();
  113.                         }
  114.                         //System.err.println(synthStatusIntByReference.getValue());
  115.                         if (synthStatusIntByReference.getValue()==2){
  116.                                 break;
  117.                         }
  118.                         //Thread.sleep(50);
  119.                 }
  120.                 // 清空数据缓冲,并关闭输入。实时播放完毕,关闭播放流
  121.                 sourceDataLine.drain();
  122.                 sourceDataLine.close();
  123.                
  124.                 byteArrayOutputStream.flush();
  125.                 byteArrayOutputStream.close();
  126.                 //可以把字节数组进行Base64加密打印
  127.                 //然后再解密写进去
  128.                 //System.err.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
  129.                 //System.err.println(byteArrayOutputStream.toByteArray());
  130.                 //直接通过FileOutputStream写字节数字到文件也是可以的~
  131.                 /*File f = new File("./zMusic/pcm/Tts/在线语音合成.pcm");
  132.                 FileOutputStream os = new FileOutputStream(f);
  133.                 os.write(byteArrayOutputStream.toByteArray());
  134.                 os.flush();*/

  135.                 WaveHeader.DataSize = WaveHeader.revers(WaveHeader.intToBytes(totalAudioLength));
  136.                 WaveHeader.RIFF_SIZE = WaveHeader.revers(WaveHeader.intToBytes(totalAudioLength + 36 - 8));
  137.                 File wavfile = new File("./tts_sample.wav");
  138.                 FileOutputStream fileOutputStream = null;
  139.                 try {
  140.                         fileOutputStream = new FileOutputStream(wavfile);
  141.                         BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
  142.                         WaveHeader.init();

  143.                         bufferedOutputStream.write(WaveHeader.RIFF);
  144.                         bufferedOutputStream.write(WaveHeader.RIFF_SIZE);
  145.                         bufferedOutputStream.write(WaveHeader.RIFF_TYPE);
  146.                         bufferedOutputStream.write(WaveHeader.FORMAT);
  147.                         bufferedOutputStream.write(WaveHeader.FORMAT_SIZE);
  148.                         bufferedOutputStream.write(WaveHeader.FORMAT_TAG);
  149.                         bufferedOutputStream.write(WaveHeader.CHANNELS);
  150.                         bufferedOutputStream.write(WaveHeader.SamplesPerSec);
  151.                         bufferedOutputStream.write(WaveHeader.AvgBytesPerSec);
  152.                         bufferedOutputStream.write(WaveHeader.BlockAlign);
  153.                         bufferedOutputStream.write(WaveHeader.BitsPerSample);

  154.                         bufferedOutputStream.write(WaveHeader.Data);
  155.                         bufferedOutputStream.write(WaveHeader.DataSize);
  156.                         bufferedOutputStream.write(byteArrayOutputStream.toByteArray());
  157.                         bufferedOutputStream.flush();
  158.                         bufferedOutputStream.close();
  159.                 } catch (IOException e) {
  160.                         // TODO Auto-generated catch block
  161.                         e.printStackTrace();
  162.                 }
  163.                 // 合成完毕
  164.                 retOneIntByReference.setValue(MyDllInterface.INSTANCE.QTTSSessionEnd(sessionID, "正常退出"));
  165.                 if (0!= retOneIntByReference.getValue()) {
  166.                         System.out.println("QTTSSessionEnd failed, error code:" + retOneIntByReference.getValue());
  167.                 }
  168.                 System.out.println("合成完毕");
  169.                 //退出
  170.                 MyDllInterface.INSTANCE.MSPLogout();
  171.                 //无需使用下面的方法,实时播放
  172.                 //PlayWav playWav=new PlayWav("./tts_sample.wav");
  173.                 //playWav.play();
  174.         }
  175. }
复制代码


2.wav头类
  1. package com.day.iFlyInterface.commonUtil.dll.tts;
  2. public class WaveHeader {

  3.     public static byte[] RIFF = "RIFF".getBytes();
  4.     public static byte[] RIFF_SIZE = new byte[8];
  5.     public static byte[] RIFF_TYPE = "WAVE".getBytes();
  6.     public static byte[] FORMAT = "fmt ".getBytes();
  7.     public static byte[] FORMAT_SIZE = new byte[4];

  8.     public static byte[] FORMAT_TAG = new byte[2];
  9.     public static byte[] CHANNELS = new byte[2];
  10.     public static byte[] SamplesPerSec = new byte[4];
  11.     public static byte[] AvgBytesPerSec = new byte[4];
  12.     public static byte[] BlockAlign = new byte[2];
  13.     public static byte[] BitsPerSample = new byte[2];
  14.     public static byte[] Data = "data".getBytes();
  15.     public static byte[] DataSize = new byte[4];

  16.     public static void init() {
  17.         FORMAT_SIZE = new byte[]{(byte) 16, (byte) 0, (byte) 0, (byte) 0};
  18.         byte[] tmp = revers(intToBytes(1));
  19.         FORMAT_TAG = new byte[]{tmp[0], tmp[1]};
  20.         CHANNELS = new byte[]{tmp[0], tmp[1]};
  21.         SamplesPerSec = revers(intToBytes(16000));
  22.         AvgBytesPerSec = revers(intToBytes(32000));
  23.         tmp = revers(intToBytes(2));
  24.         BlockAlign = new byte[]{tmp[0], tmp[1]};
  25.         tmp = revers(intToBytes(16));
  26.         BitsPerSample = new byte[]{tmp[0], tmp[1]};
  27.     }

  28.     public static byte[] revers(byte[] tmp) {
  29.         byte[] reversed = new byte[tmp.length];
  30.         for (int i = 0; i < tmp.length; i++) {
  31.             reversed[i] = tmp[tmp.length - i - 1];
  32.         }
  33.         return reversed;
  34.     }

  35.     public static byte[] intToBytes(int num) {
  36.         byte[] bytes = new byte[4];
  37.         bytes[0] = (byte) (num >> 24);
  38.         bytes[1] = (byte) ((num >> 16) & 0x000000FF);
  39.         bytes[2] = (byte) ((num >> 8) & 0x000000FF);
  40.         bytes[3] = (byte) (num & 0x000000FF);
  41.         return bytes;
  42.     }
  43. }
复制代码




评分

参与人数 2语点 +80 收起 理由
18356973290 + 30 赞一个!
打豆豆 + 50 很给力!

查看全部评分