本文介紹了如何使用阿里云智能語音服務提供的iOS NUI SDK,包括SDK下載安裝、關鍵接口及代碼示例。
前提條件
SDK關鍵接口
nui_initialize:初始化SDK。
/** * 初始化SDK,SDK為單例,請先釋放后再次進行初始化。請勿在UI線程調用,可能引起阻塞。 * @param parameters: 初始化參數(shù),參考如下說明。 * @param level: log打印級別,值越小打印越多 * @param save_log: 是否保存log為文件,存儲目錄為parameter中的debug_path字段值。注意,log文件無上限,請注意持續(xù)存儲導致磁盤存滿。 * @return 參見錯誤碼 */ -(NuiResultCode) nui_initialize:(const char *)parameters logLevel:(NuiSdkLogLevel)level saveLog:(BOOL)save_log;
parameters詳細說明:
參數(shù)
類型
是否必選
說明
workspace
String
是
工作目錄路徑,SDK從該路徑讀取配置文件。
app_key
String
是
必須填“default”。
token
String
是
必須填“default”。
url
String
是
創(chuàng)建聽悟實時記錄任務時返回的會議MeetingJoinUrl作為音頻流推送地址,在后續(xù)實時音頻流識別時通過該地址進行推流。
service_mode
String
是
必須填“1”,表示啟用在線功能。
device_id
String
是
設備標識,唯一表示一臺設備(如Mac地址/SN/UniquePsuedoID等)。
debug_path
String
否
debug目錄。當初始化SDK時的save_log參數(shù)取值為true時,該目錄用于保存日志文件。
save_wav
String
否
當初始化SDK時的save_log參數(shù)取值為true時,該參數(shù)生效。表示是否保存音頻debug,該數(shù)據(jù)保存在debug目錄中,需要確保debug_path有效可寫。
注意,音頻文件無上限,請注意持續(xù)存儲導致磁盤存滿。
nui_set_params:以JSON格式設置SDK參數(shù)。
/** * 以JSON格式設置參數(shù)。接口需要在nui_initialize之后nui_dialog_start之前調用。 * @param params: 參數(shù)信息請參見如下說明。 * @return 參見錯誤碼 */ -(NuiResultCode) nui_set_params:(const char *)params;
params詳細說明:
參數(shù)
類型
是否必選
說明
service_type
Int
是
必須填“4”。此為需要請求的語音服務類型,聽悟實時推流為“4”。
nls_config
JsonObject
是
訪問語音服務相關的參數(shù)配置,詳見如下。
nls_config.sr_format
String
是
必須填“pcm”。對應的《CreateTask - 創(chuàng)建聽悟任務》中,創(chuàng)建聽悟任務時也請指定音頻流數(shù)據(jù)的編碼格式為pcm。
nls_config.sample_rate
Integer
是
音頻采樣率,默認值:16000Hz。對應的《CreateTask - 創(chuàng)建聽悟任務》中,創(chuàng)建聽悟任務時也請指定音頻流數(shù)據(jù)的采樣率,當前支持 8000 和 16000。
nui_dialog_start:開始識別。
/** * 開始識別 * @param vad_mode: 多種模式,對于識別場景,請使用P2T。 * @param dialog_params: 設置識別參數(shù),可不設置直接傳入空JSON字符串。 * @return 參見錯誤碼 */ -(NuiResultCode) nui_dialog_start:(NuiVadMode)vad_mode dialogParam:(const char *)dialog_params;
nui_dialog_cancel:結束識別。
/** * 結束識別,調用該接口后,服務端將返回最終識別結果并結束任務 * @param force: 是否強制結束而忽略最終結果,false表示停止但是等待完整結果返回 * @return 參見錯誤碼 */ -(NuiResultCode) nui_dialog_cancel:(BOOL)force;
nui_release:釋放SDK。
/** * 釋放SDK資源 * @return 參見錯誤碼 */ -(NuiResultCode) nui_release;
nui_get_version:獲得當前SDK版本信息。
/** * 獲得當前SDK版本信息 * @return 字符串形式的SDK版本信息 */ -(const char*) nui_get_version;
nui_get_all_response:獲得當前事件回調的完整信息。
/** * onNuiEventCallback回調中獲得當前事件回調的完整信息 * @return json字符串形式的完整事件信息 */ -(const char*) nui_get_all_response;
NeoNuiSdkDelegate
onNuiEventCallback:SDK事件回調。
/** * SDK主要事件回調 * @param event: 回調事件,參見如下事件列表 * @param dialog: 會話編號,暫不使用 * @param wuw: 語音喚醒功能使用(暫不支持) * @param asr_result: 語音識別結果和翻譯結果 * @param finish: 本輪識別是否結束標志 * @param resultCode: 參見錯誤碼,在出現(xiàn)EVENT_ASR_ERROR事件時有效 */ -(void) onNuiEventCallback:(NuiCallbackEvent)nuiEvent dialog:(long)dialog kwsResult:(const char *)wuw asrResult:(const char *)asr_result ifFinish:(BOOL)finish retCode:(int)code;
NuiCallbackEvent事件列表:
名稱
說明
EVENT_VAD_START
檢測到人聲起點.
EVENT_VAD_END
檢測到人聲尾點。
EVENT_ASR_PARTIAL_RESULT
語音識別中間結果。
EVENT_ASR_RESULT
語音識別最終結果。
EVENT_ASR_ERROR
根據(jù)錯誤碼信息判斷出錯原因。
EVENT_MIC_EEROR
錄音錯誤,表示SDK連續(xù)2秒未收到任何音頻,可檢查錄音系統(tǒng)是否正常。
EVENT_SENTENCE_START
實時語音識別事件,檢測到一句話開始。
EVENT_SENTENCE_END
實時語音識別事件,檢測一句話結束,返回一句的完整結果。
EVENT_SENTENCE_SEMANTICS
暫不使用。
EVENT_RESULT_TRANSLATED
翻譯結果。
EVENT_TRANSCRIBER_COMPLETE
停止語音識別后上報。
onNuiNeedAudioData:獲取音頻。
/** * 開始識別時,此回調被連續(xù)調用,App需要在回調中進行語音數(shù)據(jù)填充 * @param audioData: 填充語音的存儲區(qū) * @param len: 需要填充語音的字節(jié)數(shù) * @return 實際填充的字節(jié)數(shù) */ -(int) onNuiNeedAudioData:(char *)audioData length:(int)len;
onNuiAudioStateChanged:根據(jù)音頻狀態(tài)進行錄音功能的開關。
/** * 當start/stop/cancel等接口調用時,SDK通過此回調通知App進行錄音的開關操作 * @param state:錄音需要的狀態(tài)(打開/關閉) */ -(void) onNuiAudioStateChanged:(NuiAudioState)state;
onNuiRmsChanged:音頻能量事件。
/** * SDK運行過程中收到音頻的實時音頻能量值。 * @param rms: 音頻能量值,范圍為-160至0 */ -(void) onNuiRmsChanged:(float) rms;
調用步驟
請下載后在聽悟的樣例初始化代碼中將Appkey和Token置為default,url置為您創(chuàng)建聽悟實時記錄返回的會議MeetingJoinUrl。
初始化SDK、錄音實例。
根據(jù)業(yè)務需求配置參數(shù)。
調用nui_dialog_start開始識別。
根據(jù)音頻狀態(tài)回調onNuiAudioStateChanged,打開錄音機。
在onNuiNeedAudioData回調中提供錄音數(shù)據(jù)。
在EVENT_ASR_PARTIAL_RESULT和EVENT_SENTENCE_END事件回調中獲取識別結果,在EVENT_RESULT_TRANSLATED事件回調中獲得翻譯結果。
調用nui_dialog_cancel結束識別。
結束調用,使用nui_release接口釋放SDK資源。
代碼示例
接口默認采用get_instance方式獲得單例,您如果有多例需求,也可以直接alloc對象進行使用。
NUI SDK初始化
BOOL save_log = NO;
NSString * initParam = [self genInitParams];
[_nui nui_initialize:[initParam UTF8String] logLevel:NUI_LOG_LEVEL_VERBOSE saveLog:save_log];
其中,genInitParams生成為String JSON字符串,包含資源目錄和用戶信息。其中用戶信息包含如下字段。
-(NSString*) genInitParams {
NSString *strResourcesBundle = [[NSBundle mainBundle] pathForResource:@"Resources" ofType:@"bundle"];
NSString *bundlePath = [[NSBundle bundleWithPath:strResourcesBundle] resourcePath];
NSString *debug_path = [_utils createDir];
// 1. 接口與實現(xiàn):http://bestwisewords.com/zh/tingwu/interface-and-implementation?spm=a2c4g.11186623.0.0.2b045060jFsVFw
// 按文檔步驟,首先創(chuàng)建AccessKey和創(chuàng)建項目
// 然后需要用戶在自己的服務端調用CreateTask接口創(chuàng)建實時記錄,獲得MeetingJoinUrl
// 此MeetingJoinUrl即為下方url
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
//賬號和項目創(chuàng)建
// ak_id ak_secret 如何獲得,請查看http://bestwisewords.com/document_detail/72138.html
[dictM setObject:@"default" forKey:@"app_key"]; // 必填,不改動
[dictM setObject:@"default" forKey:@"token"]; // 必填,不改動
// url中填入生成的MeetingJoinUrl。
// 由于MeetingJoinUrl生成過程涉及ak/sk,移動端不可存儲賬號信息,故需要在服務端生成,并下發(fā)給移動端。
// 詳細請看: http://bestwisewords.com/zh/tingwu/api-tingwu-2023-09-30-createtask
[dictM setObject:@"wss://tingwu-realtime-cn-hangzhou-pre.aliyuncs.com/api/ws/v1?XXXX" forKey:@"url"]; // 必填
//工作目錄路徑,SDK從該路徑讀取配置文件
[dictM setObject:bundlePath forKey:@"workspace"]; // 必填
//當初始化SDK時的save_log參數(shù)取值為true時,該參數(shù)生效。表示是否保存音頻debug,該數(shù)據(jù)保存在debug目錄中,需要確保debug_path有效可寫
[dictM setObject:save_wav ? @"true" : @"false" forKey:@"save_wav"];
//debug目錄。當初始化SDK時的save_log參數(shù)取值為true時,該目錄用于保存中間音頻文件
[dictM setObject:debug_path forKey:@"debug_path"];
//FullCloud = 1 // 在線實時語音識別可以選這個
[dictM setObject:@"1" forKey:@"service_mode"]; // 必填,不改動
NSString *id_string = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
TLog(@"id: %s", [id_string UTF8String]);
[dictM setObject:id_string forKey:@"device_id"]; // 必填, 推薦填入具有唯一性的id, 方便定位問題
NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];
NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return jsonStr;
}
參數(shù)設置
以JSON字符串形式進行設置。
-(NSString*) genParams {
NSMutableDictionary *nls_config = [NSMutableDictionary dictionary];
[nls_config setValue:@16000 forKey:@"sample_rate"];
[nls_config setValue:@"pcm" forKey:@"sr_format"];
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
[dictM setObject:nls_config forKey:@"nls_config"];
[dictM setValue:@(SERVICE_TYPE_SPEECH_TRANSCRIBER) forKey:@"service_type"]; // 必填
NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];
NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return jsonStr;
}
NSString * parameters = [self genParams];
[_nui nui_set_params:[parameters UTF8String]];
開始識別
通過nui_dialog_start接口開啟監(jiān)聽。
-(NSString*) genDialogParams {
NSMutableDictionary *dialog_params = [NSMutableDictionary dictionary];
NSData *data = [NSJSONSerialization dataWithJSONObject:dialog_params options:NSJSONWritingPrettyPrinted error:nil];
NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return jsonStr;
}
NSString * parameters = [self genDialogParams];
[_nui nui_dialog_start:MODE_P2T dialogParam:[parameters UTF8String]];
回調處理
onNuiAudioStateChanged:錄音狀態(tài)回調,SDK內部維護錄音狀態(tài),調用時根據(jù)該狀態(tài)的回調進行錄音機的開關操作。
-(void)onNuiAudioStateChanged:(NuiAudioState)state{ TLog(@"onNuiAudioStateChanged state=%u", state); if (state == STATE_CLOSE || state == STATE_PAUSE) { // 舊版本示例工程提供的錄音模塊,僅做參考,可根據(jù)自身業(yè)務重寫錄音模塊。 // [_voiceRecorder stop:YES]; // 新版本示例工程提供了新的錄音模塊,僅做參考,可根據(jù)自身業(yè)務重寫錄音模塊。 [_audioController stopRecorder:NO]; } else if (state == STATE_OPEN){ self.recordedVoiceData = [NSMutableData data]; // 舊版本示例工程提供的錄音模塊,僅做參考,可根據(jù)自身業(yè)務重寫錄音模塊。 // [_voiceRecorder start]; // 新版本示例工程提供了新的錄音模塊,僅做參考,可根據(jù)自身業(yè)務重寫錄音模塊。 [_audioController startRecorder]; } }
onNuiNeedAudioData:錄音數(shù)據(jù)回調,在該回調中填充錄音數(shù)據(jù)。
-(int)onNuiNeedAudioData:(char *)audioData length:(int)len { static int emptyCount = 0; @autoreleasepool { @synchronized(_recordedVoiceData){ if (_recordedVoiceData.length > 0) { int recorder_len = 0; if (_recordedVoiceData.length > len) recorder_len = len; else recorder_len = _recordedVoiceData.length; NSData *tempData = [_recordedVoiceData subdataWithRange:NSMakeRange(0, recorder_len)]; [tempData getBytes:audioData length:recorder_len]; tempData = nil; NSInteger remainLength = _recordedVoiceData.length - recorder_len; NSRange range = NSMakeRange(recorder_len, remainLength); [_recordedVoiceData setData:[_recordedVoiceData subdataWithRange:range]]; emptyCount = 0; return recorder_len; } else { if (emptyCount++ >= 50) { TLog(@"_recordedVoiceData length = %lu! empty 50times.", (unsigned long)_recordedVoiceData.length); emptyCount = 0; } return 0; } } } return 0; }
onNuiEventCallback:NUI SDK事件回調,請勿在事件回調中調用SDK的接口,可能引起死鎖。
-(void)onNuiEventCallback:(NuiCallbackEvent)nuiEvent dialog:(long)dialog kwsResult:(const char *)wuw asrResult:(const char *)asr_result ifFinish:(bool)finish retCode:(int)code { TLog(@"onNuiEventCallback event %d finish %d", nuiEvent, finish); if (nuiEvent == EVENT_ASR_PARTIAL_RESULT || nuiEvent == EVENT_SENTENCE_END) { // asr_result在此包含task_id,task_id有助于排查問題,請用戶進行記錄保存。 TLog(@"ASR RESULT %s finish %d", asr_result, finish); NSString *result = [NSString stringWithUTF8String:asr_result]; } else if (nuiEvent == EVENT_RESULT_TRANSLATED) { // 獲得翻譯結果 NSString *result = [NSString stringWithUTF8String:asr_result]; } else if (nuiEvent == EVENT_ASR_ERROR) { // asr_result在EVENT_ASR_ERROR中為錯誤信息,搭配錯誤碼code和其中的task_id更易排查問題,請用戶進行記錄保存。 TLog(@"EVENT_ASR_ERROR error[%d], error mesg[%s]", code, asr_result); // 可通過nui_get_all_response獲得完整的信息 const char *response = [_nui nui_get_all_response]; if (response != NULL) { TLog(@"GET ALL RESPONSE: %s", response); } } else if (nuiEvent == EVENT_MIC_ERROR) { TLog(@"MIC ERROR"); // 舊版本示例工程提供的錄音模塊,僅做參考,可根據(jù)自身業(yè)務重寫錄音模塊。 // [_voiceRecorder stop:YES]; // [_voiceRecorder start]; // 新版本示例工程提供了新的錄音模塊,僅做參考,可根據(jù)自身業(yè)務重寫錄音模塊。 [_audioController stopRecorder:NO]; [_audioController startRecorder]; } //finish 為真(可能是發(fā)生錯誤,也可能是完成識別)表示一次任務生命周期結束,可以開始新的識別 if (finish) { } return; }
結束識別
[_nui nui_dialog_cancel:NO];