微信小程式使用訊飛介面語音識別
之前看過網上其他幾位使用訊飛的介面來做微信小程式的。在自己實際跟著別人的部落格做的時候,卻又會遇到一些問題。所以在此對使用訊飛介面做一個總結。這裡我是用WebAPI來做。
1. 申請科大訊飛的介面
進入官網之後,登陸賬號(如果沒有,可以註冊使用)。
登陸之後,點選右上角的控制檯。進入控制中心,建立新應用
填寫好資訊之後便可建立一個新的應用。之後進入這個應用,並且會在左上角看到三個資訊:
- APPID
- APISecret
- APIKey
這三個資訊是我們的程式呼叫訊飛的介面時用來驗證身份的。這點通過它們的名稱就可以猜出來。
2. 建立微信小程式
微信小程式的開發工具的安裝和開發賬號的申請我就不用多說了;對於微信小程式的一些檔案的解釋在官方文件中也有講解。這裡對於微信小程式開發的一些基礎的內容不在多說。
(1)展示頁面
新建立一個微信小程式後,先來寫前端展示的頁面:
app.wxss檔案
page {
height: 100%;
background-color: #ffffff;
}
.container {
height: 100%;
display: flex;
flex-direction: column;
}
index.wxml檔案
<view class="container"> <view class="showContent"> <view>{{searchKey}}</view> </view> <view class="content"> <button class="btn" bindtouchstart='start' bindtouchend="stop">點選按鈕說話</button> </view> </view>
在這裡的 bindtouchstart 是當按下這個按鈕不鬆開會執行指定的方法,相同 bindtouchend 則是鬆開後執行指定的方法。
index.wxss檔案
.showContent { flex: 3 0 auto; text-align: center; padding: 100rpx; font-size: 40rpx; color: black; } .content { flex: 1 1 auto; display: flex; align-items: flex-end; justify-content: center; margin-bottom: 60rpx; width: 100%; } .content .btn { border-radius: 40rpx; width: 80%; letter-spacing: 20rpx; }
因為只是簡單的模板,所以介面不是特別好看。
(2)處理鑑權字串
WebAPI在官方文件中有很多的內容解釋。這裡也不會多的說明。
使用訊飛的API請求的話需要進行介面鑑權。因為個人對Java比較熟悉,所以使用Java來處理生成鑑權請求的字串。
使用Spring Boot來做後端
建立一個Spring Boot的應用後,使用通常的MVC模式來處理。
Controller
import com.example.template.service.UrlService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping(path = "/url")
public class UrlController {
private final UrlService urlService;
@GetMapping
public String getUrl() {
return urlService.getUrl();
}
}
這裡service的程式碼中需要新增okhttp的依賴
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
Service
import okhttp3.HttpUrl;
import org.springframework.stereotype.Service;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
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;
@Service
public class UrlService {
private static final String hostUrl = "https://iat-api.xfyun.cn/v2/iat";
private static final String apiSecret = ""; //在控制檯-我的應用-語音聽寫(流式版)獲取
private static final String apiKey = ""; //在控制檯-我的應用-語音聽寫(流式版)獲取
public String getUrl() {
try {
String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
return authUrl.replace("http://", "ws://").replace("https://", "wss://");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 這個 getAuthUrl() 方法是官網上給出來的,可以在官方文件中找到。
private 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("api_key=\"%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();
}
}
在小程式的js檔案中傳送請求獲得鑑權連結
首先在使用小程式向本地的伺服器傳送請求的時候要先對微信小程式開發工具進行配置的修改。
如果不進行該配置,則無法請求到本地的伺服器進行字串的處理。
在這裡我寫了一個單獨的函式來進行請求並獲取處理後的結果。
getUrl() {
wx.request({
url: 'http://localhost:8080/url',
method: 'GET',
header: {
'content-type': 'application/json' // 預設值
},
success(result) {
apiUrl = result.data;
console.log(apiUrl);
}
})
},
這裡的apiUrl是定義的一個全域性變數。微信的處理請求方法為wx.request。這個方法還有其他的一些引數可以設定。但現在用這做個請求獲得返回的結果就夠用了。
在js檔案中獲取錄音
在進行錄音前我們需要確定訊飛能夠接收的語音的格式。經過和微信文件的對比,在這裡可以使用pcm的格式。
這裡現在js檔案中定義錄音檔案的引數
const options = {
duration: 60000, // 指定錄音的時常,單位ms
sampleRate: 8000, // 取樣率
numberOfChannels: 1, // 錄音通道數
encodeBitRate: 48000, // 編碼位元速率
format: 'PCM', // 音訊格式
frameSize: 5, // 指定幀大小,單位KB
}
這裡我們先寫一個只是錄音的功能。
在微信小程式的官方文件中,錄音這個功能可以使用RecorderManager()來整體管理。所以我們首先在全域性定義一個recorderManager。
const recorderManager = wx.getRecorderManager();
接著我們需要寫兩個方法,分別是開始錄音和結束錄音的方法。
/* 開始錄音 */
start: function () {
recorderManager.start(options); // 開始錄音
recorderManager.onStart(() => { // 開始錄音的監聽事件
console.log('開始錄音');
});
},
/* 結束錄音 */
stop: function() {
recorderManager.stop(); // 停止錄音
recorderManager.onStop((result) => {
console.log('錄音結束' + result.tempFilePath); // tempFilePath是錄音的檔案暫時的存放路徑
});
},
這個時候就可以先簡單的測試一下是否能夠正常的錄音。(如果是首次的話可能會需要開錄音的許可權,關於許可權的問題之後再說明。現在主要是將訊飛的介面和小程式整合)
呼叫訊飛介面
訊飛的WebAPI介面是需要進行Websocket的連結的。所以小程式要建立Websocket連結。
當我們開始錄音的時候就建立連結,所以我們將該功能寫在 start 方法中。並且當連結建立成功之後就可以開始錄音。
start: function () {
wxst = wx.connectSocket({ // 開啟websocket連線
url: apiUrl,
method: 'GET',
success: function (res) {
recorderManager.start(options);//開始錄音
}
});
},
另外的一些Websocket監聽就解除安裝onLoad中,一些錄音的監聽寫在onShow中。這裡是借鑑了網上的其他大佬。這裡是他部落格的連結。
微信小程式前臺呼叫訊飛語音識別介面
這些處理完之後就已經是完成這個功能了。
但這個時候還有一個坑:在電腦虛擬機器測試的時候,訊飛介面返回來的值一直為空。但是錄音是有的。這個解決方法是使用真機調式。用真機除錯才會有結果。當然這裡的鑑權的url可以先通過後臺獲得到後直接放在socket請求的url上方便測試。