大模型交互-超拟人合成

1、超拟人合成:将文字转化为自然流畅的人声,在实时语音合成的基础上,精准模拟人类的副语言现象,如呼吸、叹气、语速变化等,使得语音不仅流畅自然,更富有情感和生命力。

2、唤醒的持久运行--->合成能力加持(唤醒成功后语音答复:主人 我在)--->调用在线或离线听写能力(建议用讯飞在线效果好)--->识别用户说的语音成文字后发给大模型--->建议调用讯飞星火认知大模型--->获取大模型答案后调用语音合成(合成在线离线均可)进行答案输出。这样就顺利实现了用纯语音与大模型进行交互!

3、在获取大模型答案后调用语音合成(合成在线离线均可)进行答案输出环节,讯飞推出超拟人合成,交互更像真人。

4、通过对大模型返回及时性与合成及时性的结合,逻辑编排使得模拟真人交互成为可能。

5、超拟人合成Java调用示例给大家!

java 复制代码
package com.iflytek;

import com.google.gson.Gson;
import okhttp3.HttpUrl;
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 语音合成流式 WebAPI 接口调用示例 接口文档(必看):https://www.xfyun.cn/doc/tts/online_tts/API.html
 * 发音人使用方式:登陆开放平台https://www.xfyun.cn/后,到控制台-我的应用-语音合成-添加试用或购买发音人,添加后即显示该发音人参数值
 * 错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
 * 小语种需要传输小语种文本、使用小语种发音人vcn、tte=unicode以及修改文本编码方式
 *
 */
public class WebTtsWs {
    // 地址与鉴权信息
    // public static final String hostUrl = "https://tts-api.xfyun.cn/v2/tts"; // 普通合成
    public static final String hostUrl = "https://cbm01.cn-huabei-1.xf-yun.com/v1/private/medd90fec"; // 超拟人合成
    // 均到控制台-语音合成页面获取
    public static final String appid = "";
    public static final String apiSecret = "";
    public static final String apiKey = "";
    // 合成文本
    public static final String TEXT = "商鞅(约公元前390年---前338年),卫国人,公孙氏,姬姓,名鞅。他是一位重要的战国时期政治家、改革家、思想家和军事家。作为法家派别的代表人物,商鞅积极实行变法。在秦孝公的请求下,他提出并实施了一系列深远影响的改革措施,这被称为"商鞅变法"。这些措施包括废除井田制、重视农业、奖励军功、统一度量衡和建立县制等。虽然在他的领导下,秦国强大并繁荣,但他也因其严苛的法律而饱受争议。总的来说,商鞅是一位影响力深远的历史人物。";
    // 合成文本编码格式
    public static final String TTE = "UTF8"; // 小语种必须使用UNICODE编码作为值
    // 发音人参数。到控制台-我的应用-语音合成-添加试用或购买发音人,添加后即显示该发音人参数值,若试用未添加的发音人会报错11200
    public static final String VCN = "xiaoyan";
    // 合成文件名称
    public static final String OUTPUT_FILE_PATH = "src/main/resources/tts/" + System.currentTimeMillis() + ".pcm";
    // json
    public static final Gson gson = new Gson();
    public static boolean wsCloseFlag = false;
    public static long startTime = System.currentTimeMillis();
    public static long endTime = System.currentTimeMillis();

    public static boolean ttsWorkingFlag = false;

    public static void main(String[] args) throws Exception {
        String wsUrl = getAuthUrl(hostUrl, apiKey, apiSecret).replace("https://", "wss://");
        OutputStream outputStream = new FileOutputStream(OUTPUT_FILE_PATH);
        websocketWork(wsUrl, outputStream);
    }

    // Websocket方法
    public static void websocketWork(String wsUrl, OutputStream outputStream) {
        try {
            URI uri = new URI(wsUrl);
            WebSocketClient webSocketClient = new WebSocketClient(uri) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    try {    //实时播放
                        Constants.TTS_SOURCE_DATA_LINE.open(Constants.TTS_AUDIO_FORMAT);
                        Constants.TTS_SOURCE_DATA_LINE.start();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    ttsWorkingFlag = true;
                    startTime = System.currentTimeMillis();
                    System.out.println("ws建立连接成功...");
                }

                @Override
                public void onMessage(String text) {
                    System.out.println("返回结果打印:" + text);
                    JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
                    if (myJsonParse.header.code != 0) {
                        System.out.println("发生错误,错误码为:" + myJsonParse.header.code);
                        System.out.println("本次请求的sid为:" + myJsonParse.header.sid);
                    }
               /*     if (myJsonParse.header.status == 2) {
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        endTime = System.currentTimeMillis();
                        System.out.println("本次合成耗时:" + (endTime - startTime) + "ms");
                        System.out.println("本次请求的sid==>" + myJsonParse.header.sid);
                        System.out.println("合成成功,文件保存路径为==>" + OUTPUT_FILE_PATH);
                        // 可以关闭连接,释放资源
                        ttsWorkingFlag = false;
                        wsCloseFlag = true;
                        Constants.TTS_SOURCE_DATA_LINE.stop();
                        Constants.TTS_SOURCE_DATA_LINE.close();
                    }*/
                    if (myJsonParse.payload.audio.audio != null) {
                        try {
                            byte[] textBase64Decode = Base64.getDecoder().decode(myJsonParse.payload.audio.audio);
                            outputStream.write(textBase64Decode);
                            outputStream.flush();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        // 实时播报
                        try {
                            byte[] textBase64Decode = Base64.getDecoder().decode(myJsonParse.payload.audio.audio);
                           /* outputStream.write(textBase64Decode);
                            outputStream.flush();*/
                            if (ttsWorkingFlag) {
                                Constants.TTS_SOURCE_DATA_LINE.write(textBase64Decode, 0, textBase64Decode.length); //实时写音频流
                                // System.err.println("写入");
                            } else {
                                Constants.TTS_SOURCE_DATA_LINE.stop();
                                Constants.TTS_SOURCE_DATA_LINE.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onClose(int i, String s, boolean b) {
                    System.out.println("ws链接已关闭,本次请求完成...");
                }

                @Override
                public void onError(Exception e) {
                    if (e.getMessage() != null) {
                        System.out.println("发生错误 " + e.getMessage());
                    }
                }
            };
            // 建立连接
            webSocketClient.connect();
            while (!webSocketClient.getReadyState().equals(WebSocket.READYSTATE.OPEN)) {
                //System.out.println("正在连接...");
                Thread.sleep(100);
            }
            MyThread webSocketThread = new MyThread(webSocketClient);
            webSocketThread.start();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    // 线程来发送音频与参数
    static class MyThread extends Thread {
        WebSocketClient webSocketClient;

        public MyThread(WebSocketClient webSocketClient) {
            this.webSocketClient = webSocketClient;
        }

        public void run() {
            String requestJson;//请求参数json串
            try {
                requestJson = "{\n" + "  \"common\": {\n" + "    \"app_id\": \"" + appid + "\"\n" + "  },\n" + "  \"business\": {\n" + "    \"aue\": \"raw\",\n" + "    \"tte\": \"" + TTE + "\",\n" + "    \"ent\": \"intp65\",\n" + "    \"vcn\": \"" + VCN + "\",\n" + "    \"pitch\": 50,\n" + "    \"speed\": 50\n" + "  },\n" + "  \"data\": {\n" + "    \"status\": 2,\n" + "    \"text\": \"" + Base64.getEncoder().encodeToString(TEXT.getBytes(StandardCharsets.UTF_8)) + "\"\n" +
                        //"    \"text\": \"" + Base64.getEncoder().encodeToString(TEXT.getBytes("UTF-16LE")) + "\"\n" +
                        "  }\n" + "}";
                requestJson = "{\n" + "  \"header\": {\n" + "    \"app_id\": \"" + appid + "\",\n" + "    \"status\": 0\n" + "  },\n" + "  \"parameter\": {\n" + "    \"oral\": {\n" + "      \"spark_assist\": 1,\n" + "      \"oral_level\": \"mid\"\n" + "    },\n" + "    \"tts\": {\n" + "      \"vcn\": \"x4_lingxiaoxuan_oral\",\n" + "      \"speed\": 50,\n" + "      \"volume\": 50,\n" + "      \"pitch\": 50,\n" + "      \"bgs\": 0,\n" + "      \"reg\": 0,\n" + "      \"rdn\": 0,\n" + "      \"rhy\": 0,\n" + "      \"scn\": 0,\n" + "      \"version\": 0,\n" + "      \"L5SilLen\": 1000,\n" + "      \"ParagraphSilLen\": 0,\n" + "      \"audio\": {\n" + "        \"encoding\": \"raw\",\n" + "        \"sample_rate\": 16000,\n" + "        \"channels\": 1,\n" + "        \"bit_depth\": 16,\n" + "        \"frame_size\": 0\n" + "      },\n" + "      \"pybuf\": {\n" + "        \"encoding\": \"utf8\",\n" + "        \"compress\": \"raw\",\n" + "        \"format\": \"plain\"\n" + "      }\n" + "    }\n" + "  },\n" + "  \"payload\": {\n" + "    \"text\": {\n" + "      \"encoding\": \"utf8\",\n" + "      \"compress\": \"raw\",\n" + "      \"format\": \"json\",\n" + "      \"status\": 0,\n" + "      \"seq\": 0,\n" + "      \"text\": \"" + Base64.getEncoder().encodeToString(TEXT.getBytes(StandardCharsets.UTF_8)) + "\"\n" + "    },\n" + "    \"user_text\": {\n" + "      \"encoding\": \"utf8\",\n" + "      \"compress\": \"raw\",\n" + "      \"format\": \"json\",\n" + "      \"status\": 0,\n" + "      \"seq\": 0,\n" + "      \"text\": \"" + Base64.getEncoder().encodeToString(TEXT.getBytes(StandardCharsets.UTF_8)) + "\"\n" + "    }\n" + "  }\n" + "}";
                System.err.println(requestJson);
                webSocketClient.send(requestJson);
                // 等待服务端返回完毕后关闭
                while (!wsCloseFlag) {
                    Thread.sleep(200);
                }
                webSocketClient.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 鉴权方法
    public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        // 时间
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        // 拼接
        String preStr = "host: " + url.getHost() + "\n" + "date: " + date + "\n" + "GET " + url.getPath() + " HTTP/1.1";
        //System.out.println(preStr);
        // SHA256加密
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        // Base64加密
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // 拼接
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        // 拼接地址
        HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().//
                addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).//
                addQueryParameter("date", date).//
                addQueryParameter("host", url.getHost()).//
                build();

        return httpUrl.toString();
    }

    //返回的json结果拆解
    class JsonParse {
        Header header;
        Payload payload;
    }

    class Header {
        int code;
        String sid;
        int status;
    }

    class Payload {
        Audio audio;
    }

    class Audio {
        String audio;
        int seq;
    }
}
相关推荐
m0_5164846717 分钟前
C#winform多选框代码
开发语言·c#
啾啾Fun38 分钟前
Java反射操作百倍性能优化
java·性能优化·反射·缓存思想
20岁30年经验的码农1 小时前
若依微服务Openfeign接口调用超时问题
java·微服务·架构
曲莫终1 小时前
SpEl表达式之强大的集合选择(Collection Selection)和集合投影(Collection Projection)
java·spring boot·spring
ajassi20001 小时前
开源 java android app 开发(十二)封库.aar
android·java·linux·开源
q567315231 小时前
Java使用Selenium反爬虫优化方案
java·开发语言·分布式·爬虫·selenium
kaikaile19951 小时前
解密Spring Boot:深入理解条件装配与条件注解
java·spring boot·spring
守护者1702 小时前
JAVA学习-练习试用Java实现“一个词频统计工具 :读取文本文件,统计并输出每个单词的频率”
java·学习
bing_1582 小时前
Spring Boot 中ConditionalOnClass、ConditionalOnMissingBean 注解详解
java·spring boot·后端
ergdfhgerty2 小时前
斐讯N1部署Armbian与CasaOS实现远程存储管理
java·docker