本文介紹 Qwen-Audio-ASR-Streaming 實時語音識別 HarmonyOS SDK 的集成方法、請求參數、接口、回調和示例代碼。
快速開始
-
獲取與配置 API Key。端側應用請勿硬編碼長期有效的API Key。建議由自建服務端獲取臨時API Key,再下發到端側。
-
下載最新SDK整合包,解壓後將
entry/libs/neonui.har複製到應用工程的entry/libs目錄,並在entry/oh-package.json5中添加依賴:{ "dependencies": { "neonui": "file:libs/neonui.har" } }如需通過HarmonyOS C++接口接入,可使用整合包
native/libs目錄中的動態庫和native/include目錄中的頭文件。 -
在應用的
module.json5中聲明網絡和麥克風權限,並在運行時申請麥克風權限。reason_internet和reason_microphone為示例資源名,請在應用資源中定義對應說明。"requestPermissions": [ { "name": "ohos.permission.INTERNET", "reason": "$string:reason_internet", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } }, { "name": "ohos.permission.MICROPHONE", "reason": "$string:reason_microphone", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } } ] -
使用DevEco Studio打開整合包中的示例工程。示例頁面位於
entry/src/main/ets/pages/dashscope/DashFunAsrSpeechTranscriberPage.ets。配置API Key後即可運行。
調用步驟
- 創建
NativeNui(Constants.ModeType.MODE_DIALOG)實例。 - 調用
initialize初始化SDK,並設置連接與控制參數。 - 調用
setParams設置模型及識別效果參數。 - 調用
startDialog啓動識別。 - 在
onNuiAudioStateChanged中根據音頻狀態啓動、暫停或關閉錄音設備。 - 在
onNuiNeedAudioData中持續提供錄音數據;如果啓用了主動推送模式,則調用updateAudio推送數據。 - 在
onNuiEventCallback中獲取識別結果和任務狀態。 - 調用
stopDialog停止識別,並等待EVENT_TRANSCRIBER_COMPLETE事件。 - 不再使用識別功能時,調用
release釋放資源。
請求參數
連接與控制參數
通過 initialize 的 parameters 參數傳入JSON字符串。
{
"url": "wss://dashscope.aliyuncs.com/api-ws/v1/inference",
"device_id": "my_device_id",
"service_mode": "1",
"audio_update_manually": "false"
}
| 參數 | 類型 | 是否必須 | 說明 |
|---|---|---|---|
url | string | 是 | 服務地址:
{WorkspaceId} 替換為真實的業務空間ID。 |
service_mode | string | 是 | 運行模式。實時語音識別固定為 "1",即 Constants.ModeFullCloud。 |
device_id | string | 是 | 終端用戶的唯一標識,可使用應用內用戶ID或客戶端生成的設備標識,主要用於日誌追蹤和問題排查。 |
apikey | string | 否 | API Key。可以在初始化時傳入;更推薦通過 startDialog 的 dialog_params 傳入臨時API Key。 |
audio_update_manually | string | 否 | 是否啓用主動推送音頻數據模式,默認值為 "false"。設為 "true" 時通過 updateAudio 主動推送音頻數據;設為 "false" 時由SDK通過 onNuiNeedAudioData 拉取音頻數據。設為 "true" 且SDK支持端側AEC、VAD等音頻能力時,默認開啓相應端側音頻能力。 |
workspace | string | 否 | 端側資源文件的存儲路徑。audio_update_manually 為 "true" 且啓用AEC或VAD等端側音頻能力時必須設置。 |
debug_path | string | 否 | 日誌文件目錄。僅當 save_log 為 true 時生效,此時必須設置。SDK最多保留兩個日誌文件。 |
save_wav | string | 否 | 是否保存調試音頻,默認值為 "false"。音頻文件保存在 debug_path 下。設為 "true" 時還需設置 debug_path,並在調用 initialize 時將 save_log 設為 true。 |
save_wav_by_id | string | 否 | 是否在開啓 save_wav 後使用 task_id 命名音頻文件,以便檢索,默認值為 "false"。 |
max_log_file_size | number | 否 | 單個日誌文件的最大字節數。默認值為 104857600(100 MiB),僅當 save_log 為 true 時生效。 |
log_track_level | number | 否 | 通過 onNuiLogTrackCallback 返回日誌的過濾級別,默認值為 2。取值:0(VERBOSE)、1(DEBUG)、2(INFO)、3(WARNING)、4(ERROR)、5(NONE)。日誌級別必須同時大於或等於 log_track_level 和 initialize 的 level,才會通過回調返回。例如,前者為2、後者為3時,只返回WARNING及以上級別的日誌。 |
enable_reconnection | string | 否 | 是否開啓斷網續傳,默認值為 "false"。 |
aec_params | object | 否 | 端側AEC配置。僅在 audio_update_manually 為 "true" 時使用。 |
aec_params.enable_aec | boolean | 否 | 是否啓用端側AEC。SDK版本支持端側AEC時默認開啓。 |
aec_params.save_audio | boolean | 否 | 是否保存AEC處理過程中的音頻。開啓 save_wav 並設置 debug_path 後默認開啓。 |
aec_params.enable_aec_data_callback | boolean | 否 | 是否通過 onNuiAssistEventCallback 的 EVENT_AEC_DATA 事件返回AEC處理後的數據,默認值為 false。 |
vad_params | object | 否 | 端側VAD配置。僅在 audio_update_manually 為 "true" 時使用。 |
vad_params.enable_vad | boolean | 否 | 是否啓用端側VAD。SDK版本支持端側VAD時默認開啓。 |
vad_params.save_audio | boolean | 否 | 是否保存VAD處理過程中的音頻。開啓 save_wav 並設置 debug_path 後默認開啓。 |
audio_config | object | 否 | SDK拉取音頻時的採集配置,僅在 audio_update_manually 為 "false" 時使用。 |
audio_config.mic.enable_volume_calculation | boolean | 否 | 是否計算並上報音量,默認值為 true。無需音量回調時可關閉。 |
audio_config.mic.volume_mode | string | 否 | 音量計算模式。設為 "dbfs" 時,按 20*log10(rms/32768) 計算標準dBFS值,滿量程為0 dB。 |
語音識別效果參數
通過 setParams 的 params 參數傳入JSON字符串。
{
"service_type": 4,
"nls_config": {
"model": "qwen-audio-3.0-asr-flash-streaming",
"sr_format": "opus",
"sample_rate": 16000
}
}
| 參數 | 類型 | 是否必須 | 說明 |
|---|---|---|---|
service_type | number | 是 | 語音服務類型。實時語音識別固定為 4,即 Constants.kServiceTypeSpeechTranscriber。 |
nls_config | object | 是 | 識別配置對象。 |
nls_config.model | string | 是 | 模型名稱。 |
nls_config.sr_format | string | 是 | 音頻格式,取值為 pcm 或 opus。設為 opus 時,客戶端仍傳入PCM數據,由SDK編碼為Opus。 |
nls_config.sample_rate | number | 是 | 採樣率(Hz)。支持任意採樣率。啓用端側AEC或VAD時不支持8000 Hz。 |
nls_config.semantic_punctuation_enabled | boolean | 否 | 是否啓用語義斷句,默認值為 false。true 表示啓用語義斷句並關閉VAD斷句,適合對斷句準確性要求較高的會議轉寫場景;false 表示啓用VAD斷句並關閉語義斷句,適合對延遲要求較高的交互場景。 |
nls_config.max_sentence_silence | number | 否 | VAD斷句靜音閾值(毫秒)。一段語音後的靜音時長超過該閾值時,系統判定句子結束。默認值為 1300,取值範圍為 [200, 6000]。啓用語義斷句時,該參數不作為 sentence_end 的返回依據,但設置過小仍可能影響識別效果。 |
nls_config.multi_threshold_mode_enabled | boolean | 否 | 是否啓用多閾值模式,默認值為 false。啓用後可避免VAD斷句切割過長。僅在 semantic_punctuation_enabled 為 false 時生效。 |
nls_config.heartbeat | boolean | 否 | 是否啓用心跳包,默認值為 false。啓用後,在持續發送靜音音頻時可保持連接;未啓用時,連接會在一定時間後因超時而斷開。靜音音頻是音頻文件或數據流中不包含聲音信號的內容。 |
nls_config.vocabulary_id | string | 否 | 預編譯熱詞列表ID。需提前創建熱詞列表,適用於詞彙已知且相對穩定、需要跨請求復用同一詞表的場景,參見預編譯熱詞。 |
nls_config.instant_vocabulary | object | 否 | 即時熱詞,鍵為熱詞文本,值為整數權重,無需提前創建熱詞列表,適用於臨時性、會話級熱詞優化。權重取值為 [1, 5] 或 50;取 [1, 5] 時值越大,模型越傾向輸出該詞;權重為50的超級熱詞最多50個。與預編譯熱詞同時配置時,兩類熱詞會合併;超過2000個時隨機選擇2000個使用。參見即時熱詞。 |
nls_config.language_hints | string[] | 否 | 待識別音頻的語種,無默認值;不設置時由模型自動識別。最多支持設置 4 個值,超出時僅前 4 個生效。 |
nls_config.speech_noise_threshold | number | 否 | VAD語音與噪聲判定閾值,取值範圍為 [-1.0, 1.0]。值越接近-1,噪聲越容易被判定為語音,可能轉寫更多噪聲;值越接近1,語音越容易被判定為噪聲,可能過濾部分語音。該參數可能顯著影響識別效果,建議充分測試後以0.1為步長小幅調整。 |
nls_config.special_word_filter | object | 否 | 敏感詞過濾配置,參見敏感詞過濾。 |
nls_config.enable_connection_fast_check | boolean | 否 | 是否啓用快速網絡檢測,默認關閉。 |
關鍵接口
NativeNui
導入SDK:
import { AsrResult, Constants, INativeNuiCallback, KwsResult, NativeNui } from 'neonui';
創建實例
constructor(mode_type: Constants.ModeType, flag?: string)
| 參數 | 類型 | 說明 |
|---|---|---|
mode_type | Constants.ModeType | 工作模式。取值為 MODE_DIALOG(對話或識別)、MODE_TTS(語音合成)和 MODE_STREAM_INPUT_TTS(流式文本語音合成)。實時語音識別固定為 MODE_DIALOG。 |
flag | string | 可選的實例標記,用於區分實例日誌。 |
initialize
initialize(
callback: INativeNuiCallback,
parameters: string,
level: number,
save_log: boolean = false
): number
初始化SDK。該接口可能阻塞,請勿在UI線程調用。
| 參數 | 類型 | 說明 |
|---|---|---|
callback | INativeNuiCallback | 事件和數據回調接口。 |
parameters | string | 連接與控制參數的JSON字符串。 |
level | number | SDK日誌級別。取值為 LOG_LEVEL_VERBOSE(0)、LOG_LEVEL_DEBUG(1)、LOG_LEVEL_INFO(2)、LOG_LEVEL_WARNING(3)、LOG_LEVEL_ERROR(4)和 LOG_LEVEL_NONE(5)。 |
save_log | boolean | 是否保存本地日誌,默認值為 false。設為 true 時必須在 parameters 中設置 debug_path,並可通過 max_log_file_size 設置文件大小。 |
setParams
setParams(params: string): number
在 startDialog 前設置語音識別效果參數。params 為語音識別效果參數的JSON字符串。
startDialog
startDialog(vad_mode: Constants.VadMode, dialog_params: string): number
開始識別。
| 參數 | 類型 | 說明 |
|---|---|---|
vad_mode | Constants.VadMode | VAD模式。實時語音識別固定為 Constants.VadMode.TYPE_P2T。 |
dialog_params | string | JSON字符串。可更新已過期的臨時API Key,也可通過 input_context 傳入上下文增強信息。 |
示例:
{
"apikey": "st-****",
"input_context": [
{ "role": "user", "content": [{ "type": "input_text", "text": "示例上下文" }] }
]
}
stopDialog
stopDialog(): number
通知服務端結束識別並返回最終結果。收到 EVENT_TRANSCRIBER_COMPLETE 後,任務結束。
cancelDialog
cancelDialog(): number
立即結束識別,不等待服務端返回最終結果。
dialogAction
dialogAction(action_params: string): number
發送運行時動作,用於更新識別上下文、通知AEC播放狀態等運行時行為。
| 參數 | 類型 | 說明 |
|---|---|---|
action_params | string | JSON字符串。 |
action_params.type | string | 固定為 "action"。 |
action_params.command | string | 動作指令。支持 "context(更新上下文)、play_start(通知AEC開始播放參考音)和 play_over"(通知AEC參考音播放結束)。 |
action_params.context | object[] | 當 command 為 "context" 時傳入的上下文增強內容。 |
更新上下文示例:
{
"type": "action",
"command": "context",
"context": [
{
"role": "user",
"content": [
{ "text": "示例上下文", "type": "input_text" }
]
}
]
}
updateAudio
updateAudio(data: ArrayBuffer, first_pack: boolean): number
audio_update_manually 為 "true" 時,通過該接口主動推送錄音數據,不再通過 onNuiNeedAudioData 填充。
| 參數 | 類型 | 說明 |
|---|---|---|
data | ArrayBuffer | 待識別的音頻數據。 |
first_pack | boolean | 是否為首個音頻包。首包設為 true,後續設為 false。 |
pushReferenceData
pushReferenceData(data: ArrayBuffer, first_pack: boolean): number
audio_update_manually 為 "true" 且啓用端側AEC時,通過該接口推送播放器播放的參考音頻。
| 參數 | 類型 | 說明 |
|---|---|---|
data | ArrayBuffer | 參考音頻數據。 |
first_pack | boolean | 是否為首個音頻包。首包設為 true,後續設為 false。 |
release
release(): number
釋放SDK的全部內部資源。調用後實例不可用;如需再次使用,必須重新調用 initialize。
GetVersion
GetVersion(): string
返回當前SDK版本信息。
refreshApikey
refreshApikey(apikey: string, url: string = ''): string
刷新API Key並返回臨時鑒權Token。該接口會進行同步網絡調用,請勿在UI線程調用。
| 參數 | 類型 | 說明 |
|---|---|---|
apikey | string | 已有的API Key。 |
url | string | 可選的鑒權服務地址,默認為空;為空時使用默認地址。 |
INativeNuiCallback
onNuiEventCallback
onNuiEventCallback: (
event: Constants.NuiEvent,
resultCode: number,
arg2: number,
kwsResult: KwsResult,
asrResult: AsrResult
) => void;
接收識別事件和結果。
| 參數 | 類型 | 說明 |
|---|---|---|
event | Constants.NuiEvent | 回調事件。 |
resultCode | number | 僅在出現 EVENT_ASR_ERROR 事件時有效。 |
arg2 | number | 保留參數。 |
kwsResult | KwsResult | 語音喚醒結果,實時語音識別場景無需關注。 |
asrResult | AsrResult | 語音識別結果。allResponse 是服務端返回的完整JSON,可從 header.task_id 獲取任務ID,從 payload.output.sentence.text 獲取句子文本。 |
事件類型:
| 事件 | 說明 |
|---|---|
EVENT_TRANSCRIBER_STARTED | 任務啓動成功。asrResult.allResponse 的 header.task_id 包含任務ID,建議記錄以便排查問題。 |
EVENT_VAD_START | 任務啓動後觸發,不表示檢測到人聲起點。 |
EVENT_VAD_END | 檢測到人聲終點。 |
EVENT_SENTENCE_START | 檢測到一句話開始。 |
EVENT_ASR_PARTIAL_RESULT | 返回語音識別中間結果。 |
EVENT_SENTENCE_END | 檢測到一句話結束,並返回一句完整的識別結果。 |
EVENT_ASR_WARN | 識別過程中出現不影響運行的警告,例如啓用斷網續傳後的斷網事件。 |
EVENT_ASR_ERROR | 識別過程中出現錯誤,錯誤碼通過 resultCode 返回。 |
EVENT_MIC_ERROR | 連續2秒未收到任何音頻數據。請檢查錄音代碼、權限或錄音模塊是否被其他應用佔用。 |
EVENT_TRANSCRIBER_COMPLETE | 語音識別結束。 |
EVENT_AEC_DATA | AEC處理後的音頻數據,通過 onNuiAssistEventCallback 返回。 |
onNuiAudioStateChanged
onNuiAudioStateChanged: (state: Constants.AudioState) => void;
SDK通過該回調通知應用何時啓動或停止錄音。
| 狀態 | 說明 |
|---|---|
STATE_OPEN | 交互啓動,可以打開錄音設備。 |
STATE_PAUSE | 交互停止,可以停止錄音。 |
STATE_CLOSE | SDK實例已釋放,可以徹底關閉錄音設備。 |
HarmonyOS的 AudioCapturer 採用異步方式創建,建議在初始化階段提前創建錄音器實例。收到 STATE_CLOSE 時只停止錄音並保留實例,以便復用;在統一的 release 流程中釋放,避免下次收到 STATE_OPEN 後重建錄音器並立即調用 start 時未生效。
onNuiNeedAudioData
onNuiNeedAudioData: (buffer: ArrayBuffer) => number;
SDK拉取音頻時連續觸發。按 buffer.byteLength 填充音頻數據,通常為20毫秒的單通道16 bit PCM,並返回實際寫入的字節數。返回小於或等於0表示出錯或當前無數據。
onNuiAudioRMSChanged
onNuiAudioRMSChanged: (val: number) => number;
返回當前音頻音量,可用於更新界面。audio_config.mic.volume_mode 為 "dbfs" 時,val 的範圍為 [-160, 0]。回調實現返回 0 即可。
onNuiAssistEventCallback
onNuiAssistEventCallback?: (
event: Constants.NuiEvent,
info: string,
infoLen: number,
data: ArrayBuffer
) => void;
可選的輔助事件回調,用於接收SDK內部的輔助事件和相關數據。不關注時可以不實現。
| 參數 | 類型 | 說明 |
|---|---|---|
event | Constants.NuiEvent | 輔助事件。 |
info | string | 附加信息,通常為JSON字符串。 |
infoLen | number | 附加信息的長度。 |
data | ArrayBuffer | 輔助數據,例如AEC處理後的音頻數據。 |
onNuiLogTrackCallback
onNuiLogTrackCallback: (level: Constants.LogLevel, log: string) => void;
接收SDK追蹤日誌。實際返回級別由 log_track_level 和 initialize 的 level 共同決定。
結果對象
AsrResult
| 屬性 | 類型 | 說明 |
|---|---|---|
finish | boolean | 當前結果是否結束。 |
resultCode | number | 結果狀態碼。 |
asrResult | string | 識別結果文本;EVENT_ASR_ERROR 事件中為錯誤信息。 |
allResponse | string | 服務端返回的完整JSON字符串,包含任務ID、句子文本等完整信息。 |
KwsResult
| 屬性 | 類型 | 說明 |
|---|---|---|
type | Constants.WuwType | 喚醒詞類型,實時語音識別場景無需關注。 |
kws | string | 喚醒詞,實時語音識別場景無需關注。 |
常量與枚舉
| 名稱 | 說明 |
|---|---|
Constants.ModeType | SDK工作模式:MODE_DIALOG、MODE_TTS 和 MODE_STREAM_INPUT_TTS。 |
Constants.VadMode | VAD模式。實時語音識別固定使用 TYPE_P2T,由用戶調用 stopDialog 結束識別。 |
Constants.AudioState | 音頻狀態:STATE_OPEN、STATE_PAUSE 和 STATE_CLOSE。 |
Constants.LogLevel | 日誌級別:LOG_LEVEL_VERBOSE(0)到 LOG_LEVEL_NONE(5)。 |
Constants.NuiResultCode | SDK錯誤碼,例如 SUCCESS(0)、ILLEGAL_PARAM(240002)、NECESSARY_PARAM_LACK(240004)和 SDK_NOT_INIT(240011)。 |
Constants.kServiceTypeSpeechTranscriber | 實時語音識別的 service_type,固定為4。 |
Constants.ModeFullCloud | 純雲端運行模式,service_mode 固定為 "1"。 |
示例代碼
以下代碼展示SDK調用的核心流程。錄音隊列、權限申請和結果JSON解析等完整實現,請參考整合包中的 DashFunAsrSpeechTranscriberPage.ets。
import { AsrResult, Constants, INativeNuiCallback, KwsResult, NativeNui } from 'neonui';
const callback: INativeNuiCallback = {
onNuiEventCallback: (event: Constants.NuiEvent, resultCode: number, arg2: number,
kwsResult: KwsResult, asrResult: AsrResult): void => {
if (event == Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT
|| event == Constants.NuiEvent.EVENT_SENTENCE_END) {
// 從asrResult.allResponse中解析payload.output.sentence.text。
} else if (event == Constants.NuiEvent.EVENT_TRANSCRIBER_COMPLETE) {
// 識別結束。
} else if (event == Constants.NuiEvent.EVENT_ASR_ERROR) {
// resultCode為錯誤碼。
}
},
onNuiAudioStateChanged: (state: Constants.AudioState): void => {
// 根據STATE_OPEN、STATE_PAUSE和STATE_CLOSE控制AudioCapturer。
},
onNuiNeedAudioData: (buffer: ArrayBuffer): number => {
// 從錄音隊列讀取數據並填入buffer,返回實際字節數。
return 0;
},
onNuiAudioRMSChanged: (val: number): number => 0,
onNuiLogTrackCallback: (level: Constants.LogLevel, log: string): void => {}
};
const nuiInstance = new NativeNui(Constants.ModeType.MODE_DIALOG);
const initParams: Record<string, Object> = {};
initParams['url'] = 'wss://dashscope.aliyuncs.com/api-ws/v1/inference';
initParams['device_id'] = 'my_device_id';
initParams['service_mode'] = Constants.ModeFullCloud;
initParams['audio_update_manually'] = 'false';
const initResult = nuiInstance.initialize(
callback,
JSON.stringify(initParams),
Constants.LogLevel.LOG_LEVEL_DEBUG,
false
);
if (initResult == Constants.NuiResultCode.SUCCESS) {
const nlsConfig: Record<string, Object> = {
'model': 'qwen-audio-3.0-asr-flash-streaming',
'sr_format': 'opus',
'sample_rate': 16000
};
const params: Record<string, Object> = {
'service_type': Constants.kServiceTypeSpeechTranscriber,
'nls_config': nlsConfig
};
nuiInstance.setParams(JSON.stringify(params));
const dialogParams: Record<string, Object> = { 'apikey': 'st-****' };
nuiInstance.startDialog(Constants.VadMode.TYPE_P2T, JSON.stringify(dialogParams));
}
// 用戶結束錄音時停止識別,並等待EVENT_TRANSCRIBER_COMPLETE。
function stopRecognition(): void {
nuiInstance.stopDialog();
}
// 在EVENT_TRANSCRIBER_COMPLETE的處理邏輯中調用nuiInstance.release()。
錄音設備可使用 @kit.AudioKit 的 AudioCapturer。音頻應為單通道、16 bit PCM,並使用與模型匹配的採樣率。
import { audio } from '@kit.AudioKit';
const options: audio.AudioCapturerOptions = {
streamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_16000,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
},
capturerInfo: {
source: audio.SourceType.SOURCE_TYPE_MIC,
capturerFlags: 0
}
};
const capturer = await audio.createAudioCapturer(options);
capturer.on('readData', (buffer: ArrayBuffer): void => {
// 回調模式:將數據寫入隊列,供onNuiNeedAudioData讀取。
// 主動模式:調用nuiInstance.updateAudio(buffer, firstPack)。
});
await capturer.start();