JAVA接入讯飞离线合成语音

本文介绍了在无法联网的项目环境中,如何通过JNA技术接入讯飞语音合成SDK,实现离线语音合成。首先下载讯飞SDK,然后引入JNA依赖,接着展示具体的JAVA代码实现,包括登录、会话开始、文本输入、音频获取和会话结束等步骤。最后,将合成的语音保存为WAV文件。需要注意的是,需要将代码中的appid替换为自己的,并确保SDK与appid匹配。
摘要由CSDN通过智能技术生成

由于公司接到的项目环境不允许联网,这边通过不停的探索,终于找到通过JNA的方式接入,废话不多说,下面展示!

1、讯飞开放平台SDK下载

 2、JNA包依赖引入

        <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.5.0</version>

        </dependency> 

3、 JAVA代码(将此代码直接贴上去)

注:需要修改appid = 577xxxxx中的参数,改成你自己的appid(因为讯飞平台会识别你下载的sdk和你的appid是否一致)

public class XunFeiSpeech {

    public interface MscLibrary extends Library {

        // DLL文件默认路径为项目根目录,若DLL文件存放在项目外,请使用绝对路径
        MscLibrary INSTANCE = Native.load("lib\\msc_x64.dll", MscLibrary.class);

        int MSPLogin(String username, String password, String param);

        int MSPLogout();

        String QTTSSessionBegin(String params, IntByReference errorCode);

        int QTTSTextPut(String sessionID, String textString, int textLen, String params);

        Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);

        int QTTSSessionEnd(String sessionID, String hints);
    }

    public static void main(String[] args) {

		//登录参数,appid与msc库绑定,请勿随意改动
        String login_params = "appid = 577xxxxx, work_dir = .";
        //合成参数:tts_res_path我这里用了绝对路径
        String session_begin_params = "engine_type = local, voice_name = xiaoyan, text_encoding = UTF-8, tts_res_path = fo|C:\\Users\\96248\\Desktop\\demo\\XunFeiSpeech\\lib\\xiaoyan.jet;fo|C:\\Users\\96248\\Desktop\\demo\\XunFeiSpeech\\lib\\common.jet, sample_rate = 16000, speed = 50, volume = 50, pitch = 50, rdn = 2";
        //合成的语音文件名称
        String filename = "tts_sample.wav"; 
         //合成文本
        String text = "《出师表》是三国时期蜀汉丞相诸葛亮在决定北上伐魏、克复中原 [2]  之前给后主刘禅上书的表文。 [1]  [3]  [20]  这篇表文以议论为主,兼用记叙和抒情。全文以恳切委婉的言辞劝勉后主要广开言路、严明赏罚、亲贤远佞,以此兴复汉室 还于旧都(洛阳) [2]  [4]  ;同时也表达自己以身许国,忠贞不二的思想。文章既不借助于华丽的辞藻,又不引用古老的典故,多以四字句行文。 [5]";

        String sessionId = null;
        RandomAccessFile raf = null;
        try {
            //登录
            int loginCode = MscLibrary.INSTANCE.MSPLogin(null, null, login_params);

            if (loginCode != 0) {
                //登录失败
                return;
            }

            //初始session
            IntByReference errCode = new IntByReference();
            sessionId = MscLibrary.INSTANCE.QTTSSessionBegin(session_begin_params, errCode);

            if (errCode.getValue() != 0) {
                //会话失败
                return;
            }

            //放入文本
            int textPutCode = MscLibrary.INSTANCE.QTTSTextPut(sessionId, text, text.getBytes().length, null);

            if (textPutCode != 0) {
                //放入文本失败
                return;
            }

            //写入空的头格式
            raf = new RandomAccessFile(filename, "rw");
            raf.write(new byte[44]);
            int dataSize = 0;
            IntByReference audioLen = new IntByReference();
            IntByReference synthStatus = new IntByReference();
            while (true) {
                Pointer pointer = MscLibrary.INSTANCE.QTTSAudioGet(sessionId, audioLen, synthStatus, errCode);
                if (pointer != null && audioLen.getValue() > 0) {
                    //写入合成内容
                    raf.write(pointer.getByteArray(0, audioLen.getValue()));
                    //记录数据长度
                    dataSize += audioLen.getValue();
                }
                //转换异常或转换结束跳出循环
                if (errCode.getValue() != 0 || synthStatus.getValue() == 2) {
                    break;
                }
            }
            if (textPutCode != 0) {
                //获取转换数据失败
                return;
            }
            //定位到文件起始位置
            raf.seek(0);
            //写入真实头格式
            raf.write(getWavHeader(dataSize, 16000, 32000, 1, 16));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sessionId != null) {
                MscLibrary.INSTANCE.QTTSSessionEnd(sessionId, "Normal");
            }
            MscLibrary.INSTANCE.MSPLogout();
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * @param totalAudioLen 音频数据总大小
     * @param sampleRate    采样率
     * @param byteRate      位元(组)率(每秒的数据量 单位 字节/秒)   采样率(44100之类的) * 通道数(1,或者2)*每次采样得到的样本位数(16或者8) / 8;
     * @param nChannels     声道数量
     * @param weikuan       位宽
     */
    private static byte[] getWavHeader(int totalAudioLen, int sampleRate, int byteRate, int nChannels, int weikuan) {
        long totalDataLen = totalAudioLen + 36;
        byte[] header = new byte[44];
        header[0] = 'R'; // RIFF/WAVE header
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f'; // 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16; // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1; // format = 1
        header[21] = 0;
        header[22] = (byte) (nChannels & 0xff);
        header[23] = (byte) ((nChannels >> 8) & 0xff);

        header[24] = (byte) (sampleRate & 0xff);//采样率
        header[25] = (byte) ((sampleRate >> 8) & 0xff);
        header[26] = (byte) ((sampleRate >> 16) & 0xff);
        header[27] = (byte) ((sampleRate >> 24) & 0xff);

        header[28] = (byte) (byteRate & 0xff);//取八位
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);

        int b = weikuan * nChannels / 8;//每次采样的大小
        header[32] = (byte) (b & 0xff); // block align
        header[33] = (byte) ((b >> 8) & 0xff);

        header[34] = (byte) (weikuan & 0xff);//位宽
        header[35] = (byte) ((weikuan >> 8) & 0xff);

        header[36] = 'd';//data
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
        return header;
    }
}

4、将在刚刚下载的SDK中的包拉到项目上

 注:xiaofeng.jet、xiaoyan.jet、common.jet 在这个路径下\bin\msc\res\tts;

 注:msc_x64.dll在这个路径下:\bin

 5、运行main方法,可以看到主目录下对应的文件(tts_sample.wav),直接打开文件播放即可

 本人小白,如有错误,请各位大佬指正哈~

在Android Studio中集成语音识别功能,通常需要遵循以下步骤: 1. **添加依赖**:首先,在你的项目中引入云服务的SDK。可以前往官网(www.xfyun.cn)下载Android SDK,并将其添加到项目的build.gradle文件中,比如通过Gradle插件的方式。 ```groovy dependencies { implementation 'com.iflytek.cloud:recognition:latest_version' } ``` 记得替换`latest_version`为实际版本号。 2. **注册应用**:登录开放平台(open.iflytek.com),创建一个新的应用并获取AppID和Key。 3. **初始化服务**:在应用程序启动时,初始化语音识别引擎,提供你的AppID和Key。 ```java IflYTEKCloudConfig config = new IflyYTEKCloudConfig(appId, apiKey); SpeechRecognizer speechRecognizer = SpeechRecognizer.create(config); ``` 4. **设置监听器**:为了接收识别结果,你需要设置一个SpeechListener。 ```java speechRecognizer.setListener(new MyRecognitionListener()); ``` 5. **开始识别**:调用`startListening()`来开始录音并识别语音。 ```java speechRecognizer.startListening(context, recognitionIntentFilter); ``` 这里`context`是当前活动的上下文,`recognitionIntentFilter`是意图过滤器,用于指定识别目标。 6. **处理结果**:在`MyRecognitionListener`类中处理`onResult()`方法中的语音识别结果。 ```java @Override public void onResult(int resultCode, String rawResults) { if (resultCode == IFlySpeechConstant.RESULT_SUCCESS) { // 解析并处理语音识别结果 } } ```
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值