接口文档:https://www.xfyun.cn/doc/tts/online_tts/API.html#%E6%8E%A5%E5%8F%A3%E8%A6%81%E6%B1%82
讯飞是ws的对接方式,sdk文档也没有golang。哈哈
首先语音合成:
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/gorilla/websocket"
)
/**
- 语音听写流式 WebAPI 接口调用示例 接口文档(必看):https://doc.xfyun.cn/rest_api/语音听写(流式版).html
- webapi 听写服务参考帖子(必看):http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=38947&extra=
- 语音听写流式WebAPI 服务,热词使用方式:登陆开放平台https://www.xfyun.cn/后,找到控制台--我的应用---语音听写---服务管理--上传热词
- 注意:热词只能在识别的时候会增加热词的识别权重,需要注意的是增加相应词条的识别率,但并不是绝对的,具体效果以您测试为准。
- 错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
- @author iflytek
*/
var (
hostUrl = "wss://tts-api.xfyun.cn/v2/tts"
apiKey = ""
apiSecret = ""
appid = "b34589c1"
)
const (
STATUS_FIRST_FRAME = 0
STATUS_CONTINUE_FRAME = 1
STATUS_LAST_FRAME = 2
)
func main() {
fmt.Println(HmacWithShaTobase64("hmac-sha256", "hello\nhello", "hello"))
st := time.Now()
d := websocket.Dialer{
HandshakeTimeout: 5 * time.Second,
}
var srcText string = "你好天下123ABC"//握手并建立websocket 连接conn, resp, err := d.Dial(assembleAuthUrl(hostUrl, apiKey, apiSecret), nil)if err != nil { panic(readResp(resp) + err.Error()) return} else if resp.StatusCode != 101 { panic(readResp(resp) + err.Error())}defer conn.Close()frameData := map[string]interface{}{ "common": map[string]interface{}{ "app_id": appid, //appid 必须带上,只需第一帧发送 }, "business": map[string]interface{}{ //business 参数,只需一帧发送 "vcn": "x4_lingxiaolu_en", "aue": "raw", "speed": 50, "tte": "UTF8", }, "data": map[string]interface{}{ "status": STATUS_LAST_FRAME, "text": base64.StdEncoding.EncodeToString([]byte(srcText)), },}fmt.Println("send first")conn.WriteJSON(frameData)//获取返回的数据//var decoder DecoderaudioFile, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)if err != nil { panic(err)}for { var resp = RespData{} _, msg, err := conn.ReadMessage() if err != nil { fmt.Println("read message error:", err) break } fmt.Println(string(msg)) json.Unmarshal(msg, &resp) if resp.Code != 0 { fmt.Println(resp.Code, resp.Message, time.Since(st)) return } //decoder.Decode(&resp.Data.Audio) //audiobytes, err := base64.StdEncoding.DecodeString(resp.Data.Audio) //if err != nil { // panic(err) //} //_, err = audioFile.Write(audiobytes) //if err != nil { // panic(err) //} if resp.Data.Status == 2 { //cf() //fmt.Println("final:",decoder.String()) fmt.Println(resp.Data.Audio, 1111111) fmt.Println(resp.Code, resp.Message, time.Since(st)) break //return }}audioFile.Close()time.Sleep(1 * time.Second)
}
type RespData struct {
Sid string json:"sid"
Code int json:"code"
Message string json:"message"
Data Data json:"data"
}
type Data struct {
Audio string json:"audio,omitempty"
Ced int json:"ced,omitempty"
Status int json:"status,omitempty"
}
// 创建鉴权url apikey 即 hmac username
func assembleAuthUrl(hosturl string, apiKey, apiSecret string) string {
ul, err := url.Parse(hosturl)
if err != nil {
fmt.Println(err)
}
//签名时间
date := time.Now().UTC().Format(time.RFC1123)
//date = "Tue, 28 May 2019 09:10:42 MST"
//参与签名的字段 host ,date, request-line
signString := []string{"host: " + ul.Host, "date: " + date, "GET " + ul.Path + " HTTP/1.1"}
//拼接签名字符串
sgin := strings.Join(signString, "\n")
fmt.Println(sgin)
//签名结果
sha := HmacWithShaTobase64("hmac-sha256", sgin, apiSecret)
fmt.Println(sha)
//构建请求参数 此时不需要urlencoding
authUrl := fmt.Sprintf("hmac username=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey,
"hmac-sha256", "host date request-line", sha)
//将请求参数使用base64编码
authorization := base64.StdEncoding.EncodeToString([]byte(authUrl))
v := url.Values{}v.Add("host", ul.Host)v.Add("date", date)v.Add("authorization", authorization)//将编码后的字符串url encode后添加到url后面callurl := hosturl + "?" + v.Encode()return callurl
}
func HmacWithShaTobase64(algorithm, data, key string) string {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(data))
encodeData := mac.Sum(nil)
return base64.StdEncoding.EncodeToString(encodeData)
}
func readResp(resp *http.Response) string {
if resp == nil {
return ""
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
return fmt.Sprintf("code=%d,body=%s", resp.StatusCode, string(b))
}
// 解析返回数据,仅供demo参考,实际场景可能与此不同。
type Decoder struct {
results []*Result
}
func (d Decoder) Decode(result Result) {
if len(d.results) <= result.Sn {
d.results = append(d.results, make([]*Result, result.Sn-len(d.results)+1)...)
}
if result.Pgs == "rpl" {
for i := result.Rg[0]; i <= result.Rg[1]; i++ {
d.results[i] = nil
}
}
d.results[result.Sn] = result
}
func (d *Decoder) String() string {
var r string
for _, v := range d.results {
if v == nil {
continue
}
r += v.String()
}
return r
}
type Result struct {
Ls bool json:"ls"
Rg []int json:"rg"
Sn int json:"sn"
Pgs string json:"pgs"
Ws []Ws json:"ws"
}
func (t *Result) String() string {
var wss string
for _, v := range t.Ws {
wss += v.String()
}
return wss
}
type Ws struct {
Bg int json:"bg"
Cw []Cw json:"cw"
}
func (w *Ws) String() string {
var wss string
for _, v := range w.Cw {
wss += v.W
}
return wss
}
type Cw struct {
Sc int json:"sc"
W string json:"w"
}
评论一下?