IFlyUtils.java 7.77 KB
package com.bsth.util;

import com.bsth.entity.speech.SpeechRequest;
import com.bsth.entity.speech.SpeechResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

public class IFlyUtils {

    private final static ObjectMapper mapper = new ObjectMapper();

    private final static Logger log = LoggerFactory.getLogger(IFlyUtils.class);

    private final static String apiKey = "46780e6779b6b1ba93503f24f097b771";

    private final static String apiSecret = "ZWFjNzkzMTkzNzI3YmMzMTgwMWUzMWE0";

    private final static String appId = "b4b21ad4";

    /**
     * 生成普通话语音
     * text以","进行分割
     * @param text
     */
    public static void textToSpeechCn(String text, String outputPath) throws Exception {
        File file = new File(outputPath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        String wsUrl = getAuthUrl("https://tts-api.xfyun.cn/v2/tts", apiKey, apiSecret).replace("https://", "wss://");
        SpeechRequest request = new SpeechRequest();
        request.getCommon().put("app_id", appId);
        request.getBusiness().put("aue", "lame");
        request.getBusiness().put("sfl", 1);
        request.getBusiness().put("vcn", "x4_lingxiaoshan_profnews");
        request.getData().put("text", Base64.getEncoder().encodeToString(text.replace(",", "[p1000]").getBytes("GBK")));
        request.getData().put("status", 2);
        websocketWork(wsUrl, request, new FileOutputStream(file));
        while (!request.isCompleted()) {
            Thread.sleep(500);
        }
    }

    /**
     * 生成上海话语音
     * text以","进行分割
     * @param text
     */
    public static void textToSpeechSh(String text, String outputPath) throws Exception {
        String wsUrl = getAuthUrl("https://tts-api.xfyun.cn/v2/tts", apiKey, apiSecret).replace("https://", "wss://");
        SpeechRequest request = new SpeechRequest();
        request.getCommon().put("app_id", appId);
        request.getBusiness().put("aue", "lame");
        request.getBusiness().put("sfl", 1);
        request.getBusiness().put("vcn", "x3_ziling");
        request.getData().put("text", Base64.getEncoder().encodeToString(text.replace(",", "[p1000]").getBytes("GBK")));
        request.getData().put("status", 2);
        websocketWork(wsUrl, request, new FileOutputStream(outputPath));
        while (!request.isCompleted()) {
            Thread.sleep(500);
        }
    }

    /**
     * 生成英语语音
     * text以","进行分割
     * @param text
     */
    public static void textToSpeechEn(String text, String outputPath) throws Exception {
        String wsUrl = getAuthUrl("https://tts-api.xfyun.cn/v2/tts", apiKey, apiSecret).replace("https://", "wss://");
        SpeechRequest request = new SpeechRequest();
        request.getCommon().put("app_id", appId);
        request.getBusiness().put("aue", "lame");
        request.getBusiness().put("sfl", 1);
        request.getBusiness().put("vcn", "x4_enus_luna_assist");
        request.getData().put("text", Base64.getEncoder().encodeToString(text.replace(",", "[p1000]").getBytes(StandardCharsets.UTF_8)));
        request.getData().put("status", 2);
        websocketWork(wsUrl, request, new FileOutputStream(outputPath));
        while (!request.isCompleted()) {
            Thread.sleep(500);
        }
    }

    public static void websocketWork(String wsUrl, SpeechRequest request, OutputStream out) {
        try {
            URI uri = new URI(wsUrl);
            WebSocketClient webSocketClient = new WebSocketClient(uri) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    try {
                        this.send(mapper.writeValueAsString(request));
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }

                @Override
                public void onMessage(String text) {
                    try {
                        SpeechResponse response = mapper.readValue(text, SpeechResponse.class);
                        log.info("response: {}", response);
                        if (response.getCode() != 0) {
                            log.error("在线语音合成发生错误");
                        }
                        if (response.getData() != null) {
                            byte[] bytes = Base64.getDecoder().decode(response.getData().getAudio());
                            out.write(bytes);
                            out.flush();
                            if (response.getData().getStatus() == 2) {
                                request.setCompleted(true);
                                out.close();
                            }
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }

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

                @Override
                public void onError(Exception e) {
                    log.error("发生错误", e);
                }
            };
            webSocketClient.connect();
        } catch (Exception e) {
            log.error("", e);
        }
    }

    /**
     * 讯飞语音合成鉴权
     * @param hostUrl
     * @param apiKey
     * @param apiSecret
     * @return
     * @throws Exception
     */
    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";
        // 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);
        StringBuilder sb = new StringBuilder(hostUrl);
        sb.append("?authorization=").append(Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8)))
        .append("&date=").append(URLEncoder.encode(date))
        .append("&host=").append(URLEncoder.encode(url.getHost()));
//        // 拼接地址
//        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 sb.toString();
    }
}