1. 接入流程
?
?
?2. 接入准备
2.1 SDK 获取
2.2 接入须知
开发者在调用前请先查看实时语音识别的 接口说明,了解接口的使用要求和使用步骤。
该接口需要手机能够连接网络(3G、4G、5G 或 Wi-Fi 等),且系统为 Android 5.0 及其以上版本。
运行 Demo 必须设置 AppID、SecretID、SecretKey,可在 API 密钥管理 中获取。
2.3 开发环境
添加实时语音识别 SDK aar
将 asr-realtime-release.aar 放在 libs 目录下,在 App 的 build.gradle 文件中添加以下代码。
implementation(name: 'asr-realtime-release', ext: 'aar')
添加其他依赖,在 App 的 build.gradle 文件中添加以下代码。
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
在 AndroidManifest.xml 添加如下权限:
<uses-permission android:name="android.permission.RECORD_AUDIO"/><uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
2.4 混淆规则
-keepclasseswithmembernames class * { # 保持 native 方法不被混淆native <methods>;}-keep public class com.tencent.aai.*-keep public class com.qq.wx.voice.*
3. 快速接入
3.1 启动实时语音识别
int appid = XXX;int projectid = 0; //此参数固定为0;String secretId = "XXX";?final AAIClient aaiClient;try {/**直接鉴权**/// 1. 签名鉴权类,sdk中给出了一个本地的鉴权类,您也可以自行实现CredentialProvider接口,在您的服务器上实现鉴权签名aaiClient = new AAIClient(MainActivity.this, appid, projectId, secretId ,new LocalCredentialProvider(secretKey));?/** 使用临时密钥鉴权* (1).通过sts 获取到临时证书 (secretId secretKey token) ,此步骤应在您的服务器端实现,见/document/product/598/33416* (2).通过临时密钥调用接口*/// aaiClient = new AAIClient(MainActivity.this, appid, projectId,"临时secretId", "临时secretKey","对应的token");?// 2、初始化语音识别请求。AudioRecognizeRequest.Builder?builder?=?new?AudioRecognizeRequest.Builder();final AudioRecognizeRequest audioRecognizeRequest = builder//设置数据源,数据源要求实现PcmAudioDataSource接口,您可以自己实现此接口来定制您的自定义数据源,例如从第三方推流中获.pcmAudioDataSource(new AudioRecordDataSource(false)) // 使用SDK内置录音器作为数据源,false:不保存音频.setEngineModelType("16k_zh") // 设置引擎参数("16k_zh" 通用引擎,支持中文普通话+英文).setFilterDirty(0) // 0 :默认状态 不过滤脏话 1:过滤脏话.setFilterModal(0) // 0 :默认状态 不过滤语气词 1:过滤部分语气词 2:严格过滤.setFilterPunc(0) // 0 :默认状态 不过滤句末的句号 1:滤句末的句号.setConvert_num_mode(1) //1:默认状态 根据场景智能转换为阿拉伯数字;0:全部转为中文数字。.setNeedvad(1) //0:关闭 vad,1:默认状态 开启 vad。语音时长超过一分钟需要开启,如果对实时性要求较高,并且时间较短的输入,建议关闭// .setHotWordId("")//热词 id。用于调用对应的热词表,如果在调用语音识别服务时,不进行单独的热词 id 设置,自动生效默认热词;如果进行了单独的热词 id 设置,那么将生效单独设置的热词 id。//.setCustomizationId("")//自学习模型 id。如果设置了该参数,那么将生效对应的自学习模型.build();?// 3、初始化语音识别结果监听器。final AudioRecognizeResultListener audioRecognizeResultlistener = new AudioRecognizeResultListener() {?@Overridepublic void onSliceSuccess(AudioRecognizeRequest request, AudioRecognizeResult result, int seq) {//返回分片的识别结果,此为中间态结果,会被持续修正}?@Overridepublic void onSegmentSuccess(AudioRecognizeRequest request, AudioRecognizeResult result, int seq) {//返回语音流的识别结果,此为稳定态结果,可做为识别结果用与业务}?@Overridepublic void onSuccess(AudioRecognizeRequest request, String result) {//识别结束回调,返回所有的识别结果}?@Overridepublic void onFailure(AudioRecognizeRequest request, final ClientException clientException, final ServerException serverException,String response) {// 识别失败}};?// 4、自定义识别配置final AudioRecognizeConfiguration audioRecognizeConfiguration = new AudioRecognizeConfiguration.Builder()//分片默认40ms,可设置40-5000,如果您不了解此参数不建议更改//.sliceTime(40)// 是否使能静音检测,.setSilentDetectTimeOut(false)// 静音检测超时停止录音可设置>2000ms,setSilentDetectTimeOut为true有效,超过指定时间没有说话将关闭识别;需要大于等于sliceTime,实际时间为sliceTime的倍数,如果小于sliceTime,则按sliceTime的时间为准.audioFlowSilenceTimeOut(5000)// 音量回调时间,需要大于等于sliceTime,实际时间为sliceTime的倍数,如果小于sliceTime,则按sliceTime的时间为准.minVolumeCallbackTime(80).build();?// 5、启动语音识别new Thread(new Runnable() {@Overridepublic void run() {if (aaiClient!=null) {aaiClient.startAudioRecognize(audioRecognizeRequest,audioRecognizeResultlistener,audioRecognizeStateListener,audioRecognizeConfiguration);}}}).start();?} catch (ClientException e) {e.printStackTrace();}
3.2 停止实时语音识别
new Thread(new Runnable() {@Overridepublic void run() {if (aaiClient!=null){//停止语音识别,等待最终识别结果aaiClient.stopAudioRecognize();}}}).start();
3.3 取消实时语音识别
new Thread(new Runnable() {@Overridepublic void run() {if (aaiClient!=null){//取消语音识别,丢弃当前任务,丢弃最终结果aaiClient.cancelAudioRecognize();}}}).start();
4. 主要接口类和方法说明
4.1 初始化 AAIClient
AAIClient 是语音服务的核心类,用户可以调用该类来开始、停止以及取消语音识别。
public AAIClient(Context context, int appid, int projectId, String secreteId, AbsCredentialProvider credentialProvider) throws ClientException
参数名称 | 类型 | 是否必填 | 参数描述 |
context | Context | 是 | 上下文 |
appid | Int | 是 | 便宜云服务器租用注册的 AppID |
projectId | Int | 否 | 此参数固定为0 |
secreteId | String | 是 | 用户的 SecreteId |
credentialProvider | AbsCredentialProvider | 是 | 鉴权类 |
示例:
try {AAIClient aaiClient = new AAIClient(context, appid, projectId, secretId, credentialProvider);} catch (ClientException e) {e.printStackTrace();}
如果 aaiClient 不再需要使用,请调用 release() 方法释放资源:
aaiClient.release();
4.2 配置全局参数
用户调用 ClientConfiguration 类的静态方法来修改全局配置。
方法 | 方法描述 | 默认值 | 有效范围 |
setAudioRecognizeSliceTimeout | HTTP 读超时时间 | 5000ms | 500 - 10000ms |
setAudioRecognizeConnectTimeout | HTTP 连接超时时间 | 5000ms | 500 - 10000ms |
setAudioRecognizeWriteTimeout | HTTP 写超时时间 | 5000ms | 500 - 10000ms |
示例:
ClientConfiguration.setAudioRecognizeSliceTimeout(2000)ClientConfiguration.setAudioRecognizeConnectTimeout(2000)ClientConfiguration.setAudioRecognizeWriteTimeout(2000)
4.3 设置结果监听器
AudioRecognizeResultListener 可以用来监听语音识别的结果,共有如下四个接口:
语音分片的语音识别结果回调接口
void onSliceSuccess(AudioRecognizeRequest request, AudioRecognizeResult result, int order);
参数 | 参数类型 | 参数描述 |
request | AudioRecognizeRequest | 语音识别请求 |
result | AudioRecognizeResult | 语音分片的语音识别结果 |
order | Int | 该语音分片所在语音流的次序 |
?
语音流的语音识别结果回调接口
void onSegmentSuccess(AudioRecognizeRequest request, AudioRecognizeResult result, int seq);
参数 | 参数类型 | 参数描述 |
request | AudioRecognizeRequest | 语音识别请求 |
result | AudioRecognizeResult | 语音分片的语音识别结果 |
seq | Int | 该语音流的次序 |
返回所有的识别结果
void onSuccess(AudioRecognizeRequest request, String result);
参数 | 参数类型 | 参数描述 |
request | AudioRecognizeRequest | 语音识别请求 |
result | String | 所有的识别结果 |
语音识别请求失败回调函数
void onFailure(AudioRecognizeRequest request, final ClientException clientException, final ServerException serverException,String response);
参数 | 参数类型 | 参数描述 |
request | AudioRecognizeRequest | 语音识别请求 |
clientException | ClientException | 客户端异常 |
serverException | ServerException | 服务端异常 |
response | String | 服务端返回的 json 字符串 |
4.4 设置语音识别参数
通过构建 AudioRecognizeConfiguration 类,可以设置语音识别时的配置:
参数名称 | 类型 | 是否必填 | 参数描述 | 默认值 |
setSilentDetectTimeOut | Boolean | 否 | 是否开启静音检测,开启后检测到超时不说话将停止识别 | false |
audioFlowSilenceTimeOut | Int | 否 | 配置 setSilentDetectTimeOut 时间超时时间 | 5000ms |
minVolumeCallbackTime | Int | 否 | 音量检测回调时间 | 80ms |
示例:
AudioRecognizeConfiguration audioRecognizeConfiguration = new AudioRecognizeConfiguration.Builder().setSilentDetectTimeOut(true)// 是否开启静音检测,开启后检测到超时不说话将停止识别.audioFlowSilenceTimeOut(5000) // 静音检测超时停止录音.minVolumeCallbackTime(80) // 音量回调时间.build();
4.5 设置状态监听器
AudioRecognizeStateListener 可以用来监听语音识别的状态:
方法 | 方法描述 |
onStartRecord | 开始录音 |
onStopRecord | 结束录音 |
onVoiceDb | 音量分贝(取值范围:0~100,集中分布在40~80) |
onNextAudioData | 返回音频流,用于返回宿主层做录音缓存业务。new AudioRecordDataSource(true) 传递 true 时生效 |
onSilentDetectTimeOut | 静音检测超时回调,此时任务还未中止,仍会等待最终识别结果 |
示例:
AudioRecognizeStateListener audioRecognizeStateListener = new AudioRecognizeStateListener() {@Overridepublic void onStartRecord(AudioRecognizeRequest audioRecognizeRequest) {// 开始录音}@Overridepublic void onStopRecord(AudioRecognizeRequest audioRecognizeRequest) {// 结束录音}@Overridepublic void onVoiceVolume(AudioRecognizeRequest audioRecognizeRequest, int i) {// 音量回调}/*** 返回音频流,* 用于返回宿主层做录音缓存业务。* 由于方法跑在sdk线程上,这里多用于文件操作,宿主需要新开一条线程专门用于实现业务逻辑* new AudioRecordDataSource(true) 有效,否则不会回调该函数* @param audioDatas*/@Overridepublic void onNextAudioData(final short[] audioDatas, final int readBufferLength){}?/*** 静音检测超时回调* 注意:此时任务还未中止,仍然会等待最终识别结果*/@Overridevoid onSilentDetectTimeOut(){//触发了静音检测事件}};
4.6 其他重要类说明
4.6.1 AudioRecognizeRequest
参数名称 | 类型 | 是否必填 | 参数描述 | 默认值 |
pcmAudioDataSource | PcmAudioDataSource | 是 | 音频数据源 | 无 |
setEngineModelType | String | 否 | 设置引擎参数 | "16k_zh" |
setFilterDirty | int | 否 | 0 :不过滤脏话 1:过滤脏话 | 0 |
setFilterModal | int | 否 | 0 :不过滤语气词 1:过滤部分语气词 2:严格过滤 | 0 |
setFilterPunc | int | 否 | 0 :不过滤句末的句号 1:滤句末的句号 | 0 |
setConvert_num_mode | int | 否 | 1: 根据场景智能转换为阿拉伯数字;0:全部转为中文数字。 | 1 |
setVadSilenceTime | int | 否 | 语音断句检测阈值,静音时长超过该阈值会被认为断句(需配合 needvad = 1 使用) 默认不传递该参数,不建议更改 | 无 |
setNeedvad | int | 否 | 0:关闭 vad,1: 开启 vad。语音时长超过一分钟需要开启,如果对实时性要求较高。 | 1 |
setHotWordId | String | 否 | 热词 id。用于调用对应的热词表,如果在调用语音识别服务时,不进行单独的热词 id 设置,自动生效默认热词;如果进行了单独的热词 id 设置,那么将生效单独设置的热词 id。 | 无 |
setWordInfo | int | 否 | 是否显示词级别时间戳。0:不显示;1:显示,不包含标点时间戳,2:显示,包含标点时间戳。时间戳信息需要自行解析 AudioRecognizeResult.resultJson 获取 | 0 |
setCustomizationId | String | 否 | 自学习模型 id。如不设置该参数,自动生效最后一次上线的自学习模型;如果设置了该参数,那么将生效对应的自学习模型。 | 无 |
setNoiseThreshold | float | 否 | 噪音参数阈值,默认为0,取值范围:[-1,1],详情见API文档 | 无 |
setMaxSpeakTime | int | 否 | 强制断句功能,取值范围 5000-90000(单位:毫秒),在连续说话不间断情况下,该参数将实现强制断句。 | 默认值0(不开启) |
setApiParam | Object | 否 | 自定义请求参数,用于在请求中添加SDK尚未支持的参数 | 无 |
4.6.2 AudioRecognizeResult
语音识别结果对象,和 AudioRecognizeRequest 对象相对应,用于返回语音识别的结果。
参数名称 | 类型 | 参数描述 |
sliceType | Int | 0表示一小段话开始,1表示在小段话的进行中,2表示小段话的结束 |
message | String | 识别提示信息 |
text | String | 识别结果 |
seq | Int | 当前一段话结果在整个音频流中的序号,从0开始逐句递增 |
voiceId | String | 该语音分片所在语音流的 ID |
startTime | int | 当前一段话结果在整个音频流中的起始时间 |
endTime | int | 当前一段话结果在整个音频流中的结束时间 |
resultJson | String | 后端返回的 json 原文本,可解析出上面列出的参数内容,如有需求,您可以自行解析获取更多信息 |
4.6.3 PcmAudioDataSource
用户可以实现这个接口来识别单通道、采样率16k的 PCM 音频数据。主要包括如下几个接口:
向语音识别器添加数据,将长度为 length 的数据从下标0开始复制到 audioPcmData 数组中,并返回实际的复制的数据量的长度。
int read(short[] audioPcmData, int length);
启动识别时回调函数,用户可以在这里做些初始化的工作。
void start() throws AudioRecognizerException;
结束识别时回调函数,用户可以在这里进行一些清理工作。
void stop();
是否保存语音源文件的开关,打开后,音频数据将通过 onNextAudioData 回调返回给调用层。
boolean isSetSaveAudioRecordFiles();
4.6.4 AudioRecordDataSource
PcmAudioDataSource 接口的实现类,可以直接读取麦克风输入的音频数据,用于实时识别,其中 demo 也提供了一份录音器源码作为数据源的示例,源码与 SDK 内置录音器 AudioRecordDataSource 一致,您可以参考此源代码自由定制修改,详情查阅 SDK 包内 DemoAudioRecordDataSource.java 内注释。
4.6.5 AAILogger
用户可以利用 AAILogger 来控制日志的输出,可以选择性的输出 debug、info、warn 以及 error 级别的日志信息。
public static void disableDebug();public static void disableInfo();public static void disableWarn();public static void disableError();public static void enableDebug();public static void enableInfo();public static void enableWarn();public static void enableError();
5. 错误码
后端错误码,详情请参见 API 文档。
客户端错误码如下:
错误码 | 名称 | 描述 |
-100 | AUDIO_RECORD_INIT_FAILED | 录音器初始化失败 |
-101 | AUDIO_RECORD_START_FAILED | 录音器启动失败 |
-102 | AUDIO_RECORD_MULTIPLE_START | 录音器重复启动 |
-103 | AUDIO_RECOGNIZE_THREAD_START_FAILED | 创建线程失败,录音线程无法启动 |
-104 | AUDIO_SOURCE_DATA_NULL | 数据源为空 |
-105 | AUDIO_RECOGNIZE_REQUEST_NULL | 请求参数为空 |
-106 | WEBSOCKET_NETWORK_FAILED | websocket 网络连接失败 |
-1 | UNKNOWN_ERROR | 未知异常,详见 message 信息 |
?
6. 常见问题指引
6.1 音频数据本地缓存指引
宿主层可根据自身业务需求选择将音频保存到本地或者不保存。若需要保存到本地可按照如下步骤进行操作:
1.
new AudioRecordDataSource(isSaveAudioRecordFiles)
初始化时,isSaveAudioRecordFiles
设置为 true。2.
AudioRecognizeStateListener.onStartRecord
回调函数内添加创建本次录音的文件逻辑。路径、文件名可支持自定义。3.
AudioRecognizeStateListener.onStopRecord
回调函数内添加关流逻辑。(可选)将 PCM 文件转存为 WAV 文件。4.
AudioRecognizeStateListener.onNextAudioData
回调函数内添加将音频流写入本地文件的逻辑。5. 由于回调函数均跑在 sdk 线程中。为了避免写入业务耗时问题影响 sdk 内部运行流畅度,建议将上述步骤放在单独线程池里完成,详情见 Demo 工程中的
MainActivity
类中的示例代码。6.2 回音消除指引
本小节主要介绍如何通过Android原生API实现回音消除,下面将分章节详细展开(详情可参见Demo工程中的DemoAudioRecordDataSource类里的start方法)。
6.2.1 设置音源的方式
/*** 注: 部分android机型可以通过该方式解决回音消除失效的问题* https://blog.csdn.net/wyw0000/article/details/125195997*/// 1. 设置音频模式为AudioManager.MODE_IN_COMMUNICATION可以起到回音消除的作用AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);?// 2. 音频源使用MediaRecorder.AudioSource.VOICE_COMMUNICATION可以起到回音消除的作用int audioSource = MediaRecorder.AudioSource.VOICE_COMMUNICATION;AudioRecord audioRecord = new AudioRecord(audioSource, sampleRate, channel, audioFormat, bufferSize);
6.2.2 尝试开启AEC和噪音抑制
/*** 注: 以下两个能力(AcousticEchoCanceler和NoiseSuppressor)和手机硬件能力相关,有些机型(比如小米11)即使isAvailable()==true,回音消除也不生效*/// AcousticEchoCanceler回音消除if (AcousticEchoCanceler.isAvailable()) {Log.d(TAG, "AcousticEchoCanceler isAvailable.");AcousticEchoCanceler acousticEchoCanceler = AcousticEchoCanceler.create(audioRecord.getAudioSessionId());int resultCode = acousticEchoCanceler.setEnabled(true);if (AudioEffect.SUCCESS == resultCode) {Log.d(TAG, "AcousticEchoCanceler AudioEffect SUCCESS");}}?// NoiseSuppressor噪音抑制if (NoiseSuppressor.isAvailable()) {Log.d(TAG, "NoiseSuppressor isAvailable.");NoiseSuppressor noiseSuppressor = NoiseSuppressor.create(audioRecord.getAudioSessionId());int resultCode = noiseSuppressor.setEnabled(true);if (AudioEffect.SUCCESS == resultCode) {Log.d(TAG, "NoiseSuppressor AudioEffect SUCCESS");}}