实时语音识别库 Wrp 的使用方法
Wrp
库可以使用与 AmiVoice SDK 相似的接口,通过 AmiVoice API 的 WebSocket 接口开发实时应用程序。可以发送流式音频并逐步接收结果。该库可用于 Java、C#、C++、Python、PHP 等语言。
客户端程序概述
使用Wrp
的程序流程如下:
方法
客户端程序按顺序执行以下处理。()中列出了对应的Wrp
方法。
- 连接 (
connect
) - 发送语音识别请求 (
feedDataResume
) - 发送音频数据 (
feedData
) - 结束音频数据发送 (
feedDataPause
) - 断开连接 (
disconnect
)
事件
来自服务器的语音检测和语音识别通知事件都通过监听器类的方法获得。有以下5种事件。()中列出了Wrp
需要实现的方法名称。
- 检测到语音开始时通知的事件
utteranceStarted(startTime)
- 检测到语音结束时通知的事件
utteranceEnded(endTime)
- 语音识别处理开始时通知的事件
resultCreated()
- 识别中间结果的通知事件
resultUpdated(result)
- 识别结果的通知事件
resultFinalized(result)
必须实现识别结果通知事件 resultFinalized(result)
。根据需要实现其他处理服务器通知的方法。
实现指南
我们将逐步说明如何使用Wrp
,并给出每种语言的示例。
以下代码示例均摘自 GitHub 存储库 advanced-media-inc/amivoice-api-client-library 中公开的 WrpSimpleTester。完整代码请参考以下源文件:
有关执行方法和文件结构等说明,请参阅客户端库的示例程序WrpSimpleTester。
1. 初始化
创建Wrp
类的实例。
- Java
- C#
- C++
- PHP
- Python
// 初始化 WebSocket 语音识别服务器
com.amivoice.wrp.Wrp wrp = com.amivoice.wrp.Wrp.construct();
// 初始化 WebSocket 语音识别服务器
com.amivoice.wrp.Wrp wrp = com.amivoice.wrp.Wrp.construct();
// 初始化 WebSocket 语音识别服务器
Pointer<com::amivoice::wrp::Wrp> wrp = com::amivoice::wrp::Wrp::construct();
// 初始化 WebSocket 语音识别服务器
$wrp = com\amivoice\wrp\Wrp::construct();
# 初始化 WebSocket 语音识别服务器
wrp = com.amivoice.wrp.Wrp.construct()
2. 实现监听器类
继承com.amivoice.wrp.WrpListener
类并实现事件处理程序。
语音识别结果通过resultFinalized
的参数result
获得。详细信息请参阅语音识别结果格式的WebSocket 接口。另外,识别结果文本使用 UTF-8 编码并进行 Unicode 转义。另请参阅关于结果文本。
以下代码在utteranceStarted
、utteranceEnded
、resultCreated
、resultUpdated
、resultFinalized
各方法中实现了将日志输出到标准输出。通过wrp.setListener(listener)
将实现了这些方法的监听器实例设置到wrp
实例。使用text_
方法解码结果文本的 Unicode 转义。text_
方法的完整代码在 GitHub 上公开。
- Java
- C#
- C++
- PHP
- Python
public class WrpTester implements com.amivoice.wrp.WrpListener {
public static void main(String[] args) {
// 创建 WebSocket 语音识别服务器事件监听器
com.amivoice.wrp.WrpListener listener = new WrpTester(verbose);
wrp.setListener(listener);
}
@Override
public void utteranceStarted(int startTime) {
System.out.println("S " + startTime);
}
@Override
public void utteranceEnded(int endTime) {
System.out.println("E " + endTime);
}
@Override
public void resultCreated() {
System.out.println("C");
}
@Override
public void resultUpdated(String result) {
System.out.println("U " + result);
String text = text_(result);
if (text != null) {
System.out.println(" -> " + text);
}
}
@Override
public void resultFinalized(String result) {
System.out.println("F " + result);
String text = text_(result);
if (text != null) {
System.out.println(" -> " + text);
}
}
public class WrpTester : com.amivoice.wrp.WrpListener {
public static void Main(string[] args) {
// 创建 WebSocket 语音识别服务器事件监听器
com.amivoice.wrp.WrpListener listener = new WrpTester(verbose);
wrp.setListener(listener);
}
public void utteranceStarted(int startTime) {
Console.WriteLine("S " + startTime);
}
public void utteranceEnded(int endTime) {
Console.WriteLine("E " + endTime);
}
public void resultCreated() {
Console.WriteLine("C");
}
public void resultUpdated(string result) {
Console.WriteLine("U " + result);
string text = text_(result);
if (text != null) {
Console.WriteLine(" -> " + text);
}
}
public void resultFinalized(string result) {
Console.WriteLine("F " + result);
string text = text_(result);
if (text != null) {
Console.WriteLine(" -> " + text);
}
}
}
class WrpTester : private com::amivoice::wrp::WrpListener {
public: static void main(const StringList& args) {
// 创建 WebSocket 语音识别服务器事件监听器
Pointer<com::amivoice::wrp::WrpListener> listener = new WrpTester(verbose);
wrp->setListener(listener);
}
public: void utteranceStarted(int startTime) override {
print("S %d", startTime);
}
public: void utteranceEnded(int endTime) override {
print("E %d", endTime);
}
public: void resultCreated() override {
print("C");
}
public: void resultUpdated(const char* result) override {
print("U %s", String().fromUTF8(result).to());
unsigned short* text = text_(result);
if (text != NULL) {
print(" -> %s", String().fromUTF16(text).to());
delete[] text;
}
}
public: void resultFinalized(const char* result) override {
print("F %s", String().fromUTF8(result).to());
unsigned short* text = text_(result);
if (text != NULL) {
print(" -> %s", String().fromUTF16(text).to());
delete[] text;
}
}
}
String().fromUTF8(result).to()
的实现请参考 GitHub。
class WrpTester implements com\amivoice\wrp\WrpListener {
public static function main($args) {
// 创建 WebSocket 语音识别服务器事件监听器
$listener = new WrpTester($verbose);
$wrp->setListener($listener);
}
public function utteranceStarted($startTime) {
p("S " . $startTime);
}
public function utteranceEnded($endTime) {
p("E " . $endTime);
}
public function resultCreated() {
p("C");
}
public function resultUpdated($result) {
p("U " . $result);
$text = $this->text_($result);
if ($text !== null) {
p(" -> " . $text);
}
}
public function resultFinalized($result) {
p("F " . $result);
$text = $this->text_($result);
if ($text !== null) {
p(" -> " . $text);
}
}
}
'p'的实现如下:
function p($s = "") {
print s($s) . "\n";
}
class WrpTester(com.amivoice.wrp.WrpListener):
@staticmethod
def main(args):
# 创建 WebSocket 语音识别服务器事件监听器
listener = WrpTester(verbose)
# 设置 wrp 对象的监听器
wrp.setListener(listener)
def utteranceStarted(self, startTime):
print("S %d" % startTime)
def utteranceEnded(self, endTime):
print("E %d" % endTime)
def resultCreated(self):
print("C")
def resultUpdated(self, result):
print("U %s" % result)
text = self.text_(result)
if text != None:
print(" -> %s" % text)
def resultFinalized(self, result):
print("F %s" % result)
text = self.text_(result)
if text != None:
print(" -> %s" % text)
3. 连接 (connect
)
连接到语音识别服务器。
在调用此方法之前,必须设置以下参数:
serverURL
... WebSocket 接口的 endpoint
请指定以下 URL:
wss://acp-api.amivoice.com/v1/ (保存日志)
wss://acp-api.amivoice.com/v1/nolog/ (不保存日志)
可以通过设置以下参数来调整行为:
proxyServerName
... 当客户端程序通过代理服务器连接时指定connectTimeout
... 与服务器的连接超时。单位为毫秒。receiveTimeout
... 从服务器接收数据的超时。单位为毫秒。
有关服务器端超时,请参阅限制事项。
以下代码连接到语音识别服务器。如果verbose
为false
,则不显示错误消息。
- Java
- C#
- C++
- PHP
- Python
wrp.setServerURL(serverURL);
wrp.setProxyServerName(proxyServerName);
wrp.setConnectTimeout(connectTimeout);
wrp.setReceiveTimeout(receiveTimeout);
// 连接到 WebSocket 语音识别服务器
if (!wrp.connect()) {
if (!verbose) {
System.out.println(wrp.getLastMessage());
}
System.out.println("WebSocket 音声認識サーバ " + serverURL + " への接続に失敗しました。");
return;
}
wrp.setServerURL(serverURL);
wrp.setProxyServerName(proxyServerName);
wrp.setConnectTimeout(connectTimeout);
wrp.setReceiveTimeout(receiveTimeout);
// 连接到 WebSocket 语音识别服务器
if (!wrp.connect()) {
if (!verbose) {
Console.WriteLine(wrp.getLastMessage());
}
Console.WriteLine("WebSocket 音声認識サーバ " + serverURL + " への接続に失敗しました。");
return;
}
wrp->setServerURL(serverURL);
wrp->setProxyServerName(proxyServerName);
wrp->setConnectTimeout(connectTimeout);
wrp->setReceiveTimeout(receiveTimeout);
// WebSocket 音声認識サーバへの接続
if (!wrp->connect()) {
if (!verbose) {
print("%s", wrp->getLastMessage());
}
print("WebSocket 音声認識サーバ %s への接続に失敗しました。", serverURL);
return;
}
$wrp->setServerURL($serverURL);
$wrp->setProxyServerName($proxyServerName);
$wrp->setConnectTimeout($connectTimeout);
$wrp->setReceiveTimeout($receiveTimeout);
// 连接到 WebSocket 语音识别服务器
if (!$wrp->connect()) {
if (!$verbose) {
p($wrp->getLastMessage());
}
p("WebSocket 音声認識サーバ " . $serverURL . " への接続に失敗しました。");
return;
}
wrp.setServerURL(serverURL)
wrp.setProxyServerName(proxyServerName)
wrp.setConnectTimeout(connectTimeout)
wrp.setReceiveTimeout(receiveTimeout)
# 连接到 WebSocket 语音识别服务器
if not wrp.connect():
if not verbose:
print(wrp.getLastMessage())
print(u"WebSocket 音声認識サーバ %s への接続に失敗しました。" % serverURL)
return
4. 语音识别请求 (feedDataResume
)
发送语音识别请求。该方法会阻塞,直到连接到请求参数中指定的适当的语音识别服务器,或准备好单词注册。如果失败,则返回错误。详情请参阅 s
命令响应数据包的错误消息。
在调用此方法之前,必须设置以下参数:
codec
... 指定音频格式。支持的音频格式请参阅语音识别结果格式。grammarFileNames
... 连接引擎名称。支持的引擎请参阅语音识别引擎。authorization
... 认 证信息。请指定在 My Page 上显示的 APPKEY。或者指定一次性 APPKEY。
可以通过设置以下参数来调整行为:
profileId
profileWords
keepFillerToken
segmenterProperties
resultUpdatedInterval
详情请参阅参数详细信息。
为避免死锁,请不要从用于接收事件通知的监听器类的方法(如 resultFinalized
等)中调用 feedDataResume
方法。
以下代码基于上述参数向服务器发送语音识别请求,并在出错时将消息显示在标准输出上。如果 verbose
为 false
,则不显示错误消息。
- Java
- C#
- C++
- PHP
- Python
wrp.setCodec(codec);
wrp.setGrammarFileNames(grammarFileNames);
wrp.setAuthorization(authorization);
// 开始向 WebSocket 语音识别服务器发送音频数据
if (!wrp.feedDataResume()) {
if (!verbose) {
System.out.println(wrp.getLastMessage());
}
System.out.println("WebSocket 音声認識サーバへの音声データの送信の開始に失敗しました。");
break;
}
wrp.setCodec(codec);
wrp.setAuthorization(authorization);
wrp.setGrammarFileNames(grammarFileNames);
// 开始向 WebSocket 语音识别服务器发送音频数据
if (!wrp.feedDataResume()) {
if (!verbose) {
Console.WriteLine(wrp.getLastMessage());
}
Console.WriteLine("WebSocket 音声認識サーバへの音声データの送信の開始に失敗しました。");
break;
}
wrp->setCodec(codec);
wrp->setAuthorization(authorization);
wrp->setGrammarFileNames(grammarFileNames);
// 开始向 WebSocket 语音识别服务器发送音频数据
if (!wrp->feedDataResume()) {
if (!verbose) {
print("%s", wrp->getLastMessage());
}
print("WebSocket 音声認識サーバへの音声データの送信の開始に失敗しました。");
break;
}
$wrp->setCodec($codec);
$wrp->setAuthorization($authorization);
$wrp->setGrammarFileNames($grammarFileNames);
// 开始向 WebSocket 语音识别服务器发送音频数据
if (!$wrp->feedDataResume()) {
if (!$verbose) {
p($wrp->getLastMessage());
}
p("WebSocket 音声認識サーバへの音声データの送信の開始に失敗しました。");
break;
}
wrp.setCodec(codec)
wrp.setAuthorization(authorization)
wrp.setGrammarFileNames(grammarFileNames)
# 开始向 WebSocket 语音识别服务器发送音频数据
if not wrp.feedDataResume():
if not verbose:
print(wrp.getLastMessage())
print(u"WebSocket 音声認識サーバへの音声データの送信の開始に失敗しました。")
break
5. 发送音频数据 (feedData
)
接下来发送音频数据。此方法不会阻塞。如果服务器端发生任何错误,将在下一次方法调用时返回错误。有关错误内容,请参阅 p
命令响应数据包的错误消息。开始发送音频数据后,监听器类的方法将根据服务器端的处理被调用。
- 请发送与
feedDataResume
中指定的codec
相匹配的音频数据。即使格式不同,也不会出错,但响应时间可能会非常长,或无法正确获得识别结果。 - 如果从发送的音频数据中无法检测到语音,则不会调用监听器类的方法。可能有以下原因,请检查:
- 完全不包含音频。或音量非常小。请检查录音系统是否未静音,音量设置是否适当等。
- 音频格式与音频数据不匹配。请检查音频格式。
- 一次
feedData
方法调用可以发送的最大音频数据大小为 16MB。如果数据大小超过此限制,请将其分割。 - 音频数据可以在任意位置分割,无需考虑 wav 块或 mp3、flac、opus 等的帧边界。
- 无法在中途更改要发送的数据格式。如需更改音频格式,请使用
e
命令结束,然后从s
重新发起语音识别请求。对于带 header 的音频文件,同样需要对每个文件使用e
命令结束,然后从s
重新发起语音识别请求。
以下代码从 audioFileName
指定的音频数据文件中读取音频数据,并将其发送到 WebSocket 语音识别服务器。当 sleepTime
为 -1
时,程序会一直休眠直到等待识别结果的数量不超过 1。当 verbose
为 false
时,不会显示错误消息。
- Java
- C#
- C++
- PHP
- Python
try (FileInputStream audioStream = new FileInputStream(audioFileName)) {
// 从音频数据文件读取音频数据
byte[] audioData = new byte[4096];
int audioDataReadBytes = audioStream.read(audioData, 0, audioData.length);
while (audioDataReadBytes > 0) {
// 检查是否已计算休眠时间
if (sleepTime >= 0) {
// 如果已计算休眠时间...
// 短暂休眠
wrp.sleep(sleepTime);
} else {
// 如果未计算休眠时间...
// 短暂休眠
wrp.sleep(1);
// 休眠直到等待识别结果的数量不超过 1
int maxSleepTime = 50000;
while (wrp.getWaitingResults() > 1 && maxSleepTime > 0) {
wrp.sleep(100);
maxSleepTime -= 100;
}
}
// 向 WebSocket 语音识别服务器发送音频数据
if (!wrp.feedData(audioData, 0, audioDataReadBytes)) {
if (!verbose) {
System.out.println(wrp.getLastMessage());
}
System.out.println("WebSocket 音声認識サーバへの音声データの送信に失敗しました。");
break;
}
// 从音频数据文件读取音频数据
audioDataReadBytes = audioStream.read(audioData, 0, audioData.length);
}
} catch (IOException e) {
System.out.println("音声データファイル " + audioFileName + " の読み込みに失敗しました。");
}
try {
using (FileStream audioStream = new FileStream(audioFileName, FileMode.Open, FileAccess.Read)) {
// 从音频数据文件读取音频数据
byte[] audioData = new byte[4096];
int audioDataReadBytes = audioStream.Read(audioData, 0, audioData.Length);
while (audioDataReadBytes > 0) {
// 检查是否已计算休眠时间
if (sleepTime >= 0) {
// 如果已计算休眠时间...
// 短暂休眠
wrp.sleep(sleepTime);
} else {
// 如果未计算休眠时间...
// 短暂休眠
wrp.sleep(1);
// 休眠直到等待识别结果的数量不超过 1
int maxSleepTime = 50000;
while (wrp.getWaitingResults() > 1 && maxSleepTime > 0) {
wrp.sleep(100);
maxSleepTime -= 100;
}
}
// 向 WebSocket 语音识别服务器发送音频数据
if (!wrp.feedData(audioData, 0, audioDataReadBytes)) {
if (!verbose) {
Console.WriteLine(wrp.getLastMessage());
}
Console.WriteLine("WebSocket 音声認識サーバへの音声データの送信に失敗しました。");
break;
}
// 从音频数据文件读取音频数据
audioDataReadBytes = audioStream.Read(audioData, 0, audioData.Length);
}
}
} catch (IOException) {
Console.WriteLine("音声データファイル " + audioFileName + " の読み込みに失敗しました。");
}
// 打开音频数据文件
FILE* audioStream;
if (fopen_s(&audioStream, audioFileName->to(), "rb") == 0) {
// 从音频数据文件读取音频数据
char audioData[4096];
int audioDataReadBytes = (int)fread(audioData, 1, 4096, audioStream);
while (audioDataReadBytes > 0) {
// 检查是否已计算休眠时间
if (sleepTime >= 0) {
// 如果已计算休眠时间...
// 短暂休眠
wrp->sleep(sleepTime);
} else {
// 如果未计算休眠时间...
// 短暂休眠
wrp->sleep(1);
// 休眠直到等待识别结果的数量不超过 1
int maxSleepTime = 50000;
while (wrp->getWaitingResults() > 1 && maxSleepTime > 0) {
wrp->sleep(100);
maxSleepTime -= 100;
}
}
// 向 WebSocket 语音识别服务器发送音频数据
if (!wrp->feedData(audioData, 0, audioDataReadBytes)) {
if (!verbose) {
print("%s", wrp->getLastMessage());
}
print("WebSocket 音声認識サーバへの音声データの送信に失敗しました。");
break;
}
// 从音频数据文件读取音频数据
audioDataReadBytes = (int)fread(audioData, 1, 4096, audioStream);
}
// 关闭音频数据文件
fclose(audioStream);
} else {
print("音声データファイル %s の読み込みに失敗しました。", audioFileName->to());
}
$audioStream = false;
try {
// 打开音频数据文件
$audioStream = @fopen($audioFileName, "rb");
if ($audioStream === false) {
throw new \Exception();
}
// 从音频数据文件读取音频数据
$audioData = @fread($audioStream, 4096);
while ($audioData !== false && strlen($audioData) > 0) {
// 检查是否已计算休眠时间
if ($sleepTime >= 0) {
// 如果已计算休眠时间...
// 短暂休眠
$wrp->sleep($sleepTime);
} else {
// 如果未计算休眠时间...
// 短暂休眠
$wrp->sleep(1);
// 休眠直到等待识别结果的数量不超过 1
$maxSleepTime = 50000;
while ($wrp->getWaitingResults() > 1 && $maxSleepTime > 0) {
$wrp->sleep(100);
$maxSleepTime -= 100;
}
}
// 向 WebSocket 语音识别服务器发送音频数据
if (!$wrp->feedData($audioData, 0, strlen($audioData))) {
if (!$verbose) {
p($wrp->getLastMessage());
}
p("WebSocket 音声認識サーバへの音声データの送信に失敗しました。");
break;
}
// 从音频数据文件读取音频数据
$audioData = @fread($audioStream, 4096);
}
} catch (\Exception $e) {
p("音声データファイル " . $audioFileName . " の読み込みに失敗しました。");
} finally {
// 关闭音频数据文件
if ($audioStream !== false) {
@fclose($audioStream);
$audioStream = false;
}
}
try:
with open(audioFileName, "rb") as audioStream:
# 从音频数据文件读取音频数据
audioData = audioStream.read(4096)
while len(audioData) > 0:
# 检查是否已计算休眠时间
if sleepTime >= 0:
# 如果已计算休眠时间...
# 短暂休眠
wrp.sleep(sleepTime)
else:
# 如果未计算休眠时间...
# 短暂休眠
wrp.sleep(1)
# 休眠直到等待识别结果的数量不超过 1
maxSleepTime = 50000
while wrp.getWaitingResults() > 1 and maxSleepTime > 0:
wrp.sleep(100)
maxSleepTime -= 100
# 向 WebSocket 语音识别服务器发送音频数据
if not wrp.feedData(audioData, 0, len(audioData)):
if not verbose:
print(wrp.getLastMessage())
print(u"WebSocket 音声認識サーバへの音声データの送信に失敗しました。")
break
# 从音频数据文件读取音频数据
audioData = audioStream.read(4096)
except:
print(u"音声データファイル %s の読み込みに失敗しました。" % audioFileName)
6. 结束发送语音数据 (feedDataPause
)
当语音数据发送完成时调用此方法。此方法会阻塞直到语音识别处理完成。请求可能因某些原因失败。详情请参阅e
命令响应数据包中的错误消息。
请不要从用于接收事件通知的监听器类的方法(如resultFinalized
)中调用feedDataPause
方法,否则可能会导致死锁。
如果不是流式传输而是一次性发送所有语音数据,语音识别处理会需要一些时间,因此需要等待一段时间才能得到结果。预计等待时间约为发送的语音时长的0.5到1.5倍。
以下代码向服务器表明所有语音数据已发送完毕,并阻塞等待结果返回。如果verbose
为false
,则不显示错误消息。
- Java
- C#
- C++
- PHP
- Python
// 完成向WebSocket语音识别服务器发送语音数据
if (!wrp.feedDataPause()) {
if (!verbose) {
System.out.println(wrp.getLastMessage());
}
System.out.println("WebSocket 音声認識サーバへの音声データの送信の完了に失敗しました。");
break;
}
// 完成向WebSocket语音识别服务器发送语音数据
if (!wrp.feedDataPause()) {
if (!verbose) {
Console.WriteLine(wrp.getLastMessage());
}
Console.WriteLine("WebSocket 音声認識サーバへの音声データの送信の完了に失敗しました。");
break;
}
// 完成向WebSocket语音识别服务器发送语音数据
if (!wrp->feedDataPause()) {
if (!verbose) {
print("%s", wrp->getLastMessage());
}
print("WebSocket 音声認識サーバへの音声データの送信の完了に失敗しました。");
break;
}
// 完成向WebSocket语音识别服务器发送语音数据
if (!$wrp->feedDataPause()) {
if (!$verbose) {
p($wrp->getLastMessage());
}
p("WebSocket 音声認識サーバへの音声データの送信の完了に失敗しました。");
break;
}
# 完成向WebSocket语音识别服务器发送语音数据
if not wrp.feedDataPause():
if not verbose:
print(wrp.getLastMessage())
print(u"WebSocket 音声認識サーバへの音声データの送信の完了に失敗しました。")
break
7. 断开连接 (disconnect
)
最后断开与语音识别服务器的连接。
- Java
- C#
- C++
- PHP
- Python
// 断开与WebSocket语音识别服务器的连接
wrp.disconnect();
// 断开与WebSocket语音 识别服务器的连接
wrp.disconnect();
// 断开与WebSocket语音识别服务器的连接
wrp->disconnect();
// 断开与WebSocket语音识别服务器的连接
$wrp->disconnect();
# 断开与WebSocket语音识别服务器的连接
wrp.disconnect()
客户端程序的状态转换
客户端程序会进行以下状态转换。
其他文档
- 关于Wrp客户端库源代码和示例程序的获取方法,请参阅获取方法。也可以参考示例程序的运行方法和目录和文件结构。
Wrp
类库的参考文档请参阅Wrp。Wrp
类库使用AmiVoice API的WebSocket接口。也请参阅WebSocket接口概述。