科大訊飛TTS接口調用保存爲mp3格式

不廢話,直接上code

package com.iflytek.voicecloud.webapi.demo;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import okhttp3.*;
import okio.ByteString;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.io.ByteArrayOutputStream;
import java.io.IOException;


public class WebTTSWS {
    private static final String hostUrl = "https://tts-api.xfyun.cn/v2/tts"; //http url 不支持解析 ws/wss schema
    private static final String appid = "";//到控制檯-語音合成頁面獲取
    private static final String apiSecret = "";//到控制檯-語音合成頁面獲取
    private static final String apiKey = "";//到控制檯-語音合成頁面獲取
    private static final String text = "在全系5款車型中,上市專享版的性價比最爲突出,在有限的價格下提供了大多數實用配置,而且是唯一標配車內氛圍燈的車型,不失爲價值之選。時尚致雅型和時尚動感型居於中配角色,配置方面較低配車型更加全面,面子和裏子都比較到位,符合人們對豪華品牌的期待,同樣值得考慮。";
    public static final Gson json = new Gson();
    
    

public static class WaveHeader {
 
	public final char fileID[] = { 'R', 'I', 'F', 'F' };
	public int fileLength;
	public char wavTag[] = { 'W', 'A', 'V', 'E' };;
	public char FmtHdrID[] = { 'f', 'm', 't', ' ' };
	public int FmtHdrLeth;
	public short FormatTag;
	public short Channels;
	public int SamplesPerSec;
	public int AvgBytesPerSec;
	public short BlockAlign;
	public short BitsPerSample;
	public char DataHdrID[] = { 'd', 'a', 't', 'a' };
	public int DataHdrLeth;
 
	public byte[] getHeader() throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		writeChar(bos, fileID);
		writeInt(bos, fileLength);
		writeChar(bos, wavTag);
		writeChar(bos, FmtHdrID);
		writeInt(bos, FmtHdrLeth);
		writeShort(bos, FormatTag);
		writeShort(bos, Channels);
		writeInt(bos, SamplesPerSec);
		writeInt(bos, AvgBytesPerSec);
		writeShort(bos, BlockAlign);
		writeShort(bos, BitsPerSample);
		writeChar(bos, DataHdrID);
		writeInt(bos, DataHdrLeth);
		bos.flush();
		byte[] r = bos.toByteArray();
		bos.close();
		return r;
	}
 
	private void writeShort(ByteArrayOutputStream bos, int s) throws IOException {
		byte[] mybyte = new byte[2];
		mybyte[1] = (byte) ((s << 16) >> 24);
		mybyte[0] = (byte) ((s << 24) >> 24);
		bos.write(mybyte);
	}
 
	private void writeInt(ByteArrayOutputStream bos, int n) throws IOException {
		byte[] buf = new byte[4];
		buf[3] = (byte) (n >> 24);
		buf[2] = (byte) ((n << 8) >> 24);
		buf[1] = (byte) ((n << 16) >> 24);
		buf[0] = (byte) ((n << 24) >> 24);
		bos.write(buf);
	}
 
	private void writeChar(ByteArrayOutputStream bos, char[] id) {
		for (int i = 0; i < id.length; i++) {
			char c = id[i];
			bos.write(c);
		}
	}
}

    
    
    
	public static void convertAudioFiles(String src, String target) throws Exception {
		   FileInputStream fis = new FileInputStream(src);
		   FileOutputStream fos = new FileOutputStream(target);

		   //計算長度
		   byte[] buf = new byte[1024 * 4];
		   int size = fis.read(buf);
		   int PCMSize = 0;
		   while (size != -1) {
		      PCMSize += size;
		      size = fis.read(buf);
		    }
		   fis.close();

		   //填入參數,比特率等等。這裏用的是16位單聲道 8000 hz
		   WaveHeader header = new WaveHeader();
		   //長度字段 = 內容的大小(PCMSize) + 頭部字段的大小(不包括前面4字節的標識符RIFF以及fileLength本身的4字節)
		   header.fileLength = PCMSize + (44 - 8);
		   header.FmtHdrLeth = 16;
		   header.BitsPerSample = 16;
		   header.Channels = 1;
		   header.FormatTag = 0x0001;
		   header.SamplesPerSec = 14500;
		   header.BlockAlign = (short)(header.Channels * header.BitsPerSample / 8);
		   header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
		   header.DataHdrLeth = PCMSize;

		   byte[] h = header.getHeader();

		   assert h.length == 44; //WAV標準,頭部應該是44字節
		   //write header
		   fos.write(h, 0, h.length);
		   //write data stream
		   fis = new FileInputStream(src);
		   size = fis.read(buf);
		   while (size != -1) {
		      fos.write(buf, 0, size);
		      size = fis.read(buf);
		   }
		   fis.close();
		   fos.close();
		   System.out.println("Convert OK!");
		}

    
    
    
    
    
    
    public static void main(String[] args) throws Exception {
        // 構建鑑權url
        String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
        OkHttpClient client = new OkHttpClient.Builder().build();
        //將url中的 schema http://和https://分別替換爲ws:// 和 wss://
        String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(url).build();
        // 存放音頻的文件
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String date = sdf.format(new Date());
        File f = new File("resource/tts/" + date + ".pcm");
        if (!f.exists()) {
            f.createNewFile();
        }
        FileOutputStream os = new FileOutputStream(f);
        WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                super.onOpen(webSocket, response);
                try {
                    System.out.println(response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //發送數據
                JsonObject frame = new JsonObject();
                JsonObject business = new JsonObject();
                JsonObject common = new JsonObject();
                JsonObject data = new JsonObject();
                // 填充common
                common.addProperty("app_id", appid);
                //填充business
                business.addProperty("aue", "raw");
                business.addProperty("tte", "UTF8");
                business.addProperty("ent", "intp65");
                business.addProperty("vcn", "x_yifeng");//到控制檯-我的應用-語音合成-添加試用或購買發音人,添加後即顯示該發音人蔘數值,若試用未添加的發音人會報錯11200
                business.addProperty("pitch", 50);
                business.addProperty("speed", 60);
                //填充data
                data.addProperty("status", 2);//固定位2
                data.addProperty("text", Base64.getEncoder().encodeToString(text.getBytes()));
                data.addProperty("encoding", "");
                //填充frame
                frame.add("common", common);
                frame.add("business", business);
                frame.add("data", data);
                webSocket.send(frame.toString());
            }
            @Override
            public void onMessage(WebSocket webSocket, String text) {
                super.onMessage(webSocket, text);
                //處理返回數據
                System.out.println("receive=>" + text);
                ResponseData resp = null;
                try {
                    resp = json.fromJson(text, ResponseData.class);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (resp != null) {
                    if (resp.getCode() != 0) {
                        System.out.println("error=>" + resp.getMessage() + " sid=" + resp.getSid());
                        return;
                    }
                    if (resp.getData() != null) {
                        String result = resp.getData().audio;
                        byte[] audio = Base64.getDecoder().decode(result);
                        try {
                            os.write(audio);
                            os.flush();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if (resp.getData().status == 2) {
                            // todo  resp.data.status ==2 說明數據全部返回完畢,可以關閉連接,釋放資源
                            System.out.println("session end ");
                            System.out.println("合成的音頻文件保存在:" + f.getPath());
                            webSocket.close(1000, "");
                            
                            try {
								convertAudioFiles(f.getPath(),f.getPath()+".mp3");
							} catch (Exception e1) {
								// TODO Auto-generated catch block
								e1.printStackTrace();
							}
                            
                            
                            try {
                            	
                       
                                os.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            @Override
            public void onMessage(WebSocket webSocket, ByteString bytes) {
                super.onMessage(webSocket, bytes);
            }
            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                super.onClosing(webSocket, code, reason);
                System.out.println("socket closing");
            }
            @Override
            public void onClosed(WebSocket webSocket, int code, String reason) {
                super.onClosed(webSocket, code, reason);
                System.out.println("socket closed");
            }
            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                super.onFailure(webSocket, t, response);
                System.out.println("connection failed");
            }
        });
    }
    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());
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").//
                append("date: ").append(date).append("\n").//
                append("GET ").append(url.getPath()).append(" HTTP/1.1");
        Charset charset = Charset.forName("UTF-8");
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        String authorization = String.format("hmac username=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder().//
                addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(charset))).//
                addQueryParameter("date", date).//
                addQueryParameter("host", url.getHost()).//
                build();
        return httpUrl.toString();
    }
    public static class ResponseData {
        private int code;
        private String message;
        private String sid;
        private Data data;
        public int getCode() {
            return code;
        }
        public String getMessage() {
            return this.message;
        }
        public String getSid() {
            return sid;
        }
        public Data getData() {
            return data;
        }
    }
    public static class Data {
        private int status;  //標誌音頻是否返回結束  status=1,表示後續還有音頻返回,status=2表示所有的音頻已經返回
        private String audio;  //返回的音頻,base64 編碼
        private String ced;  // 合成進度
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章