淘先锋技术网

首页 1 2 3 4 5 6 7

1、离线语音合成调用主函数(离线语音合成调用属于简单的,无回调函数)

package com.iflytek;
import com.iflytek.util.Step2_tts_thread;
import com.iflytek.util.Step3_audioFormat;
import java.util.Scanner;
import javax.sound.sampled.*;

/**
 * 请注意!!!
 * 1.首选到控制台https://console.xfyun.cn/services/aisound下载普通离线语音合成的Windows MSC。
 * 2.普通离线语音合成Windows MSC解压后,把bin目录下msc文件夹与dll文件拷贝到res目录下。
 * 3.最后请替换Step2_tts_thread中的appid值,appid值在下载页面控制台可以看到。
 */

public class OfflineTtsMain {
	//录音相关参数
	public static AudioFormat audioFormat;
	public static SourceDataLine sourceDataLine;
	public static void main(String[] args) throws Exception {
		System.out.println("输入y开始体验普通离线语音合成...");
		Scanner myScanner = new Scanner(System.in);
		String userCommand = myScanner.next();
		long startTime = System.currentTimeMillis();
		if(userCommand.equals("y")){
			audioFormat = Step3_audioFormat.getAudioFormat(audioFormat);//构造具有线性 PCM 编码和给定参数的 AudioFormat。
			DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class,audioFormat, AudioSystem.NOT_SPECIFIED);
			sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
			Step2_tts_thread myThread=new Step2_tts_thread();
			myThread.start();
		}
	}
}

2、根据MSC文档用JAVA重写方法

package com.iflytek.service;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

public interface Step1_tts_dll extends Library {
    /**
     * 重点:
     * 1.char *   对应  String
     * 2.int *    对应  IntByReference
     * 3.void *   对应  byte[]/Pointer,回调函数里此类型需用String来对应。
     * 4.int      对应  int
     * 5.无参     对应  void
     * 6.回调函数  对应  根据文档自定义回调函数,实现接口Callback,离线语音合成无回调
     */
    //加载dll动态库并实例化,从而使用其内部的方法
    Step1_tts_dll INSTANCE = (Step1_tts_dll) Native.loadLibrary("res/msc_x64.dll", Step1_tts_dll.class);

    //定义登录方法
    public int MSPLogin(String usr, String pwd, String params);

    //开始一次普通离线语音合成
    public String QTTSSessionBegin(String params, IntByReference errorCode);

    //写入需要合成的文本
    public int QTTSTextPut(String sessionID,String textString,int textLen,String params);

    //获取离线合成的音频
    public Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);

    //结束本次普通离线语音合成
    public int QTTSSessionEnd(String sessionID, String hints);

    //定义退出方法
    public int MSPLogout();
}

3、把调用步骤依次写进线程类 

package com.iflytek.util;
import com.iflytek.OfflineTtsMain;
import com.iflytek.service.Step1_tts_dll;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

import java.io.ByteArrayOutputStream;
import java.util.Scanner;

public class Step2_tts_thread extends Thread{
	public static int total_audio_length=0;
	public static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
	public void run() {
		while (true) {
			//登录参数
			String login_params = "appid = 替换你的appid, work_dir = ./res";
			//第一个参数为用户名,第二个参数为密码,传null即可
			int ret = Step1_tts_dll.INSTANCE.MSPLogin(null, null, login_params);
			if (ret != 0) {//登录成功标志ret为0
				System.out.println("登录失败...请检查");
				System.exit(1);
			} else {
				//System.out.println("登录成功...");
			}
			//请让一让,A02-02-03
			//开始一次普通离线语音合成会话
			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";
			IntByReference errorCode = new IntByReference(-100);
			String session_id = Step1_tts_dll.INSTANCE.QTTSSessionBegin(session_begin_params,errorCode);
			if(errorCode.getValue()==0){
				//System.out.println("开启普通离线语音合成成功,session_id是:"+session_id);
			}else{
				System.out.println("开启普通离线语音合成失败:"+errorCode.getValue());
			}

			//写入需要合成的文本
			System.out.println("\033[43;34;4m"+"请输入你要合成的文本并回车"+"\033[0m");
			Scanner myScanner = new Scanner(System.in);
			String tts_text =myScanner.nextLine();
			//System.out.println(tts_text);//注意获取的文本是否完整
			ret= Step1_tts_dll.INSTANCE.QTTSTextPut(session_id, tts_text, tts_text.getBytes().length,null);
			if(ret==0){
				//System.out.println("写入合成文本成功...");
			}else{
				System.out.println("写入合成文本失败:"+ret);
			}

			//循环获取离线合成的音频,并实时进行播放
			IntByReference audio_len=new IntByReference(-100);
			IntByReference synth_status=new IntByReference(-100);
			errorCode=new IntByReference(-100);
			try{
				//实时播放
				OfflineTtsMain.sourceDataLine.open(OfflineTtsMain.audioFormat);
				OfflineTtsMain.sourceDataLine.start();
			}catch (Exception e){
				e.printStackTrace();
			}
			//int i=0;
			while (true) {
				Pointer audioPointer = Step1_tts_dll.INSTANCE.QTTSAudioGet(session_id,audio_len,synth_status,errorCode);
				byte[] audioDataByteArray=null;
				if(audioPointer!=null){
					audioDataByteArray=audioPointer.getByteArray(0,audio_len.getValue());
				}
				if (errorCode.getValue()==0){
					//System.out.println("正常获取音频中...");
				}else{
					System.out.println("获取音频发生错误:"+errorCode);
					break;
				}
				if (audioDataByteArray!=null ){
					try{
						//实时播放
						OfflineTtsMain.sourceDataLine.write(audioDataByteArray, 0, audio_len.getValue());
						/*i=i+1;//实现暂停的效果
						System.out.println(i);
						if(i==5){
							OfflineTtsMain.sourceDataLine.stop();
							Thread.sleep(5000);
						}
						if(i==6){
							OfflineTtsMain.sourceDataLine.start();
						}*/
						//将数据写入字节数组的输出流,用来生成音频文件
						byteArrayOutputStream.write(audioDataByteArray, 0, audio_len.getValue());
					}catch(Exception e){
						e.printStackTrace();
					}
					//计算总音频长度,用来生成音频文件
					total_audio_length=total_audio_length+audio_len.getValue();
				}
				if (synth_status.getValue()==2){
					//说明音频已经取完,退出本次循环
					try{
						OfflineTtsMain.sourceDataLine.drain();
						OfflineTtsMain.sourceDataLine.close();
						byteArrayOutputStream.flush();
						byteArrayOutputStream.close();
					}catch (Exception e){
						e.printStackTrace();
					}
					break;
				}
			}

			//把合成的音频存放为wav格式
			String dateAsFileName= String.valueOf(System.currentTimeMillis());//用当前时间标记文件名
			Boolean waveProductFlag=Step4_wave_product.waveProduct(total_audio_length,byteArrayOutputStream,"./res/"+dateAsFileName+".wav");
			if(waveProductFlag){
				System.out.println("生成音频文件成功:"+dateAsFileName+".wav");
			}else{
				System.out.println("生成音频文件失败...");
			}

			//结束本次普通离线语音合成
			ret=Step1_tts_dll.INSTANCE.QTTSSessionEnd(session_id, "正常退出");
			if(ret==0){
				//System.out.println("离线语音合成正常退出...");
			}else{
				System.out.println("离线语音合成退出异常:"+ret);
			}

			//执行最终退出
			ret = Step1_tts_dll.INSTANCE.MSPLogout();
			if (ret == 0) {
				//System.out.println("正常退出...");
			} else {
				System.out.println("异常退出:"+ret);
			}

			//是否退出Java程序
			System.out.println("\33[43;31;4m"+"结束合成:输入n,继续合成:输入其他内容"+"\33[0m");
			Scanner myScanner2 = new Scanner(System.in);
			String userCommand = myScanner2.nextLine();
			if(userCommand.equals("n")){
				OfflineTtsMain.sourceDataLine.stop();
				OfflineTtsMain.sourceDataLine.close();
				System.exit(0);
			}
		}
	}
}

4、生成16K、16BIT单声道音频流

package com.iflytek.util;
import javax.sound.sampled.AudioFormat;

public class Step3_audioFormat {
    //构造线程参数
    //16k采样率的16bit音频,一帧的大小为640B, 时长20ms
    /**
     sampleRate - 每秒样品数
     sampleSizeInBits - 每个样本中的位数
     channels - 通道数(1为mono,2为立体声等)
     signed - 表示数据是签名还是无符号
     bigEndian - 指示单个样本的数据是否以大字节顺序存储( false表示小端)
     */
    public static AudioFormat getAudioFormat(AudioFormat audioFormat) {
        audioFormat=new AudioFormat(16000F, 16, 1,true,false);
        // true,false 指示是以 big-endian 顺序还是以 little-endian 顺序存储音频数据。
        return audioFormat;//构造具有线性 PCM 编码和给定参数的 AudioFormat。
    }
}

5、生成WAV音频所需的文件头

package com.iflytek.util;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;

public class Step4_wave_product {
    public static byte[] RIFF = "RIFF".getBytes();
    public static byte[] RIFF_SIZE = new byte[8];
    public static byte[] RIFF_TYPE = "WAVE".getBytes();
    public static byte[] FORMAT = "fmt ".getBytes();
    public static byte[] FORMAT_SIZE = new byte[4];
    public static byte[] FORMAT_TAG = new byte[2];
    public static byte[] CHANNELS = new byte[2];
    public static byte[] SamplesPerSec = new byte[4];
    public static byte[] AvgBytesPerSec = new byte[4];
    public static byte[] BlockAlign = new byte[2];
    public static byte[] BitsPerSample = new byte[2];
    public static byte[] Data = "data".getBytes();
    public static byte[] DataSize = new byte[4];
    public static boolean waveProduct(int audioLength, ByteArrayOutputStream byteArrayOutputStream, String outputFilePath){
        Step4_wave_product.DataSize = Step4_wave_product.revers(Step4_wave_product.intToBytes(audioLength));
        Step4_wave_product.RIFF_SIZE = Step4_wave_product.revers(Step4_wave_product.intToBytes(audioLength + 36 - 8));
        File waveFile = new File(outputFilePath);
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(waveFile);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            init();
            bufferedOutputStream.write(RIFF);
            bufferedOutputStream.write(RIFF_SIZE);
            bufferedOutputStream.write(RIFF_TYPE);
            bufferedOutputStream.write(FORMAT);
            bufferedOutputStream.write(FORMAT_SIZE);
            bufferedOutputStream.write(FORMAT_TAG);
            bufferedOutputStream.write(CHANNELS);
            bufferedOutputStream.write(SamplesPerSec);
            bufferedOutputStream.write(AvgBytesPerSec);
            bufferedOutputStream.write(BlockAlign);
            bufferedOutputStream.write(BitsPerSample);
            bufferedOutputStream.write(Data);
            bufferedOutputStream.write(DataSize);
            bufferedOutputStream.write(byteArrayOutputStream.toByteArray());
            bufferedOutputStream.flush();
            bufferedOutputStream.close();
            return true;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }
    }
    public static void init() {
        FORMAT_SIZE = new byte[]{(byte) 16, (byte) 0, (byte) 0, (byte) 0};
        byte[] tmp = revers(intToBytes(1));
        FORMAT_TAG = new byte[]{tmp[0], tmp[1]};
        CHANNELS = new byte[]{tmp[0], tmp[1]};
        SamplesPerSec = revers(intToBytes(16000));
        AvgBytesPerSec = revers(intToBytes(32000));
        tmp = revers(intToBytes(2));
        BlockAlign = new byte[]{tmp[0], tmp[1]};
        tmp = revers(intToBytes(16));
        BitsPerSample = new byte[]{tmp[0], tmp[1]};
    }
    public static byte[] revers(byte[] tmp) {
        byte[] reversed = new byte[tmp.length];
        for (int i = 0; i < tmp.length; i++) {
            reversed[i] = tmp[tmp.length - i - 1];
        }
        return reversed;
    }
    public static byte[] intToBytes(int num) {
        byte[] bytes = new byte[4];
        bytes[0] = (byte) (num >> 24);
        bytes[1] = (byte) ((num >> 16) & 0x000000FF);
        bytes[2] = (byte) ((num >> 8) & 0x000000FF);
        bytes[3] = (byte) (num & 0x000000FF);
        return bytes;
    }
}

6、放置讯飞开放平台下载Linux/Windows中所带的资源

7、Linux代码基本类似,离线合成普通版,注意资源放置

package com.iflytek.service;
import com.iflytek.OfflineTtsMain;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

public interface Step1_tts_so extends Library {
    /**
     * 重点:
     * 1.char *   对应  String
     * 2.int *    对应  IntByReference
     * 3.void *   对应  byte[]/Pointer,回调函数里此类型需用String来对应。
     * 4.int      对应  int
     * 5.无参     对应  void
     * 6.回调函数  对应  根据文档自定义回调函数,实现接口Callback,离线语音合成无回调
     */
    //加载dll动态库并实例化,从而使用其内部的方法
    Step1_tts_so INSTANCE = (Step1_tts_so) Native.loadLibrary(OfflineTtsMain.userPath+"res/libmsc.so", Step1_tts_so.class);

    //定义登录方法
    public int MSPLogin(String usr, String pwd, String params);

    //开始一次普通离线语音合成
    public String QTTSSessionBegin(String params, IntByReference errorCode);

    //写入需要合成的文本
    public int QTTSTextPut(String sessionID,String textString,int textLen,String params);

    //获取离线合成的音频
    public Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);

    //结束本次普通离线语音合成
    public int QTTSSessionEnd(String sessionID, String hints);

    //定义退出方法
    public int MSPLogout();
}

8、Linux代码基本类似,离线合成高品质,注意资源放置

package com.iflytek.service;
import com.iflytek.OfflineHighQualityTtsMain;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

public interface Step1_tts_so extends Library {
    /**
     * 重点:
     * 1.char *   对应  String
     * 2.int *    对应  IntByReference
     * 3.void *   对应  byte[]/Pointer,回调函数里此类型需用String来对应。
     * 4.int      对应  int
     * 5.无参     对应  void
     * 6.回调函数  对应  根据文档自定义回调函数,实现接口Callback,离线语音合成无回调
     */
    //加载so动态库并实例化,从而使用其内部的方法
    Step1_tts_so INSTANCE = (Step1_tts_so) Native.loadLibrary(OfflineHighQualityTtsMain.userPath+"res/libmsc.so", Step1_tts_so.class);

    //定义登录方法
    public int MSPLogin(String usr, String pwd, String params);

    //开始一次高品质离线语音合成
    public String QTTSSessionBegin(String params, IntByReference errorCode);

    //写入需要合成的文本
    public int QTTSTextPut(String sessionID,String textString,int textLen,String params);

    //获取离线合成的音频
    public Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);

    //结束本次高品质离线语音合成
    public int QTTSSessionEnd(String sessionID, String hints);

    //定义退出方法
    public int MSPLogout();
}

9、普通版与高品质版对内存的消耗也不同,请根据实际所需进行选择