iOS SDK
本文介紹了如何使用阿里云智能語音服務(wù)提供的iOS NUI SDK,包括SDK下載安裝、關(guān)鍵接口及代碼示例。
前提條件
使用SDK前,請先閱讀接口說明,詳情請參見接口說明。
已獲取項目Appkey,詳情請參見創(chuàng)建項目。
已獲取Access Token,詳情請參見獲取Token概述。
下載安裝
- 重要
下載后請在樣例初始化代碼中替換您的阿里云賬號信息、Appkey和Token才可運(yùn)行。
為方便集成,2.5.14版本后iOS接口使用純Object-C接口,不再使用C++混合接口。
對象
說明
智能語音交互移動端SDK
智能語音交互移動端SDK
阿里云計算有限公司
阿里云計算有限公司
SDK版本
2.6.3-01B
SDK更新時間
2024-12-19
SDK整合包大小
17.3MB
SDK整合包MD5值
edf1a1a770bf869b738ee89ab43d88aa
隱私政策
SDK整合包下載
類別
兼容范圍
系統(tǒng)
最低支持iOS9
架構(gòu)
arm64,x86_64
此SDK還包含如下功能,若未支持您想要的功能,請前往對應(yīng)文檔獲取SDK。
功能
是否支持
一句話識別
是
實時語音識別
是
語音合成
是
實時長文本語音合成
是
流式文本語音合成
是
離線語音合成
否
錄音文件識別極速版
是
喚醒及命令詞
否
聽悟?qū)崟r推流
是
解壓ZIP包。
將ZIP包中的nuisdk.framework添加到您的工程中,并在工程Build Phases的Link Binary With Libraries中添加nuisdk.framework。
使用Xcode打開此工程,工程中提供了參考代碼以及一些直接可使用的工具類,例如音頻播放錄制和文件操作,您可以直接復(fù)制源碼到您的實際工程進(jìn)行使用。其中實時轉(zhuǎn)寫示例代碼為SpeechTranscriberViewController。替換Appkey和Token后可直接運(yùn)行。
SDK關(guān)鍵接口
nui_initialize:初始化SDK。
/** * 初始化SDK,SDK為單例,請先釋放后再次進(jìn)行初始化。請勿在UI線程調(diào)用,可能引起阻塞。 * @param parameters: 初始化參數(shù),參見接口說明文檔:http://bestwisewords.com/zh/isi/developer-reference/overview-4 * @param level: log打印級別,值越小打印越多 * @param save_log: 是否保存log為文件,存儲目錄為parameter中的debug_path字段值。注意,log文件無上限,請注意持續(xù)存儲導(dǎo)致磁盤存滿。 * @return 參見錯誤碼 */ -(NuiResultCode) nui_initialize:(const char *)parameters logLevel:(NuiSdkLogLevel)level saveLog:(BOOL)save_log;
nui_set_params:以JSON格式設(shè)置SDK參數(shù)。
/** * 以JSON格式設(shè)置參數(shù) * @param params: 參數(shù)信息請參見接口說明文檔:http://bestwisewords.com/zh/isi/developer-reference/overview-4 * @return 參見錯誤碼 */ -(NuiResultCode) nui_set_params:(const char *)params;
nui_dialog_start:開始識別。
/** * 開始識別 * @param vad_mode: 多種模式,對于識別場景,請使用P2T * @param dialog_params: 設(shè)置識別參數(shù),可不設(shè)置,可參考接口文檔:http://bestwisewords.com/zh/isi/developer-reference/overview-4 * @return 參見錯誤碼 */ -(NuiResultCode) nui_dialog_start:(NuiVadMode)vad_mode dialogParam:(const char *)dialog_params;
nui_dialog_cancel:結(jié)束識別。
/** * 結(jié)束識別,調(diào)用該接口后,服務(wù)端將返回最終識別結(jié)果并結(jié)束任務(wù) * @param force: 是否強(qiáng)制結(jié)束而忽略最終結(jié)果,false表示停止但是等待完整結(jié)果返回 * @return 參見錯誤碼 */ -(NuiResultCode) nui_dialog_cancel:(BOOL)force;
nui_release:釋放SDK。
/** * 釋放SDK資源 * @return 參見錯誤碼 */ -(NuiResultCode) nui_release;
nui_get_version:獲得當(dāng)前SDK版本信息。
/** * 獲得當(dāng)前SDK版本信息 * @return 字符串形式的SDK版本信息 */ -(const char*) nui_get_version;
nui_get_all_response:獲得當(dāng)前事件回調(diào)的完整信息。
/** * 獲得當(dāng)前事件回調(diào)的完整信息 * @return json字符串形式的完整事件信息 */ -(const char*) nui_get_all_response;
NeoNuiSdkDelegate
onNuiEventCallback:SDK事件回調(diào)。
/** * SDK主要事件回調(diào) * @param event: 回調(diào)事件,參見如下事件列表 * @param dialog: 會話編號,暫不使用 * @param wuw: 語音喚醒功能使用(暫不支持) * @param asr_result: 語音識別結(jié)果 * @param finish: 本輪識別是否結(jié)束標(biāo)志 * @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
語音識別中間結(jié)果。
EVENT_ASR_RESULT
語音識別最終結(jié)果。
EVENT_ASR_ERROR
根據(jù)錯誤碼信息判斷出錯原因。
EVENT_MIC_EEROR
錄音錯誤,表示SDK連續(xù)2秒未收到任何音頻,可檢查錄音系統(tǒng)是否正常。
EVENT_SENTENCE_START
實時語音識別事件,檢測到一句話開始。
EVENT_SENTENCE_END
實時語音識別事件,檢測一句話結(jié)束,返回一句的完整結(jié)果。
EVENT_SENTENCE_SEMANTICS
暫不使用。
EVENT_TRANSCRIBER_COMPLETE
停止語音識別后上報。
onNuiNeedAudioData:獲取音頻。
/** * 開始識別時,此回調(diào)被連續(xù)調(diào)用,App需要在回調(diào)中進(jìn)行語音數(shù)據(jù)填充 * @param audioData: 填充語音的存儲區(qū) * @param len: 需要填充語音的字節(jié)數(shù) * @return 實際填充的字節(jié)數(shù) */ -(int) onNuiNeedAudioData:(char *)audioData length:(int)len;
onNuiAudioStateChanged:根據(jù)音頻狀態(tài)進(jìn)行錄音功能的開關(guān)。
/** * 當(dāng)start/stop/cancel等接口調(diào)用時,SDK通過此回調(diào)通知App進(jìn)行錄音的開關(guān)操作 * @param state:錄音需要的狀態(tài)(打開/關(guān)閉) */ -(void) onNuiAudioStateChanged:(NuiAudioState)state;
onNuiRmsChanged:音頻能量事件。
/** * SDK主要事件回調(diào) * @param rms: 語音能量值,范圍為-160至0 */ -(void) onNuiRmsChanged:(float) rms;
調(diào)用步驟
初始化SDK、錄音實例。
根據(jù)業(yè)務(wù)需求配置參數(shù)。
調(diào)用nui_dialog_start開始識別。
根據(jù)音頻狀態(tài)回調(diào)onNuiAudioStateChanged,打開錄音機(jī)。
在onNuiNeedAudioData回調(diào)中提供錄音數(shù)據(jù)。
在EVENT_ASR_PARTIAL_RESULT和EVENT_SENTENCE_END事件回調(diào)中獲取識別結(jié)果。
調(diào)用nui_dialog_cancel結(jié)束識別。
結(jié)束調(diào)用,使用nui_release接口釋放SDK資源。
代碼示例
接口默認(rèn)采用get_instance方式獲得單例,您如果有多例需求,也可以直接alloc對象進(jìn)行使用。
NUI SDK初始化
BOOL save_log = NO;
NSString * initParam = [self genInitParams];
[_nui nui_initialize:[initParam UTF8String] logLevel: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];
NSMutableDictionary *ticketJsonDict = [NSMutableDictionary dictionary];
//獲取賬號訪問憑證:
// getTicket為示例工程中提供了多種可能的方式,請選擇適合自身業(yè)務(wù)的安全方式
//
//注意:
// 語音交互服務(wù)需要先準(zhǔn)備好賬號,并開通相關(guān)服務(wù)。具體步驟請查看:
// http://bestwisewords.com/zh/isi/getting-started/start-here
//
//原始賬號:
// 賬號(子賬號)信息主要包括AccessKey ID(后續(xù)簡稱為ak_id)和AccessKey Secret(后續(xù)簡稱為ak_secret)。
// 此賬號信息一定不可存儲在app代碼中或移動端側(cè),以防賬號信息泄露造成資費(fèi)損失。
//
//STS臨時憑證:
// 由于賬號信息下發(fā)給客戶端存在泄露的可能,阿里云提供的一種臨時訪問權(quán)限管理服務(wù)STS(Security Token Service)。
// STS是由賬號信息ak_id和ak_secret,通過請求生成臨時的sts_ak_id/sts_ak_secret/sts_token
// (為了區(qū)別原始賬號信息和STS臨時憑證, 命名前綴sts_表示STS生成的臨時憑證信息)
//什么是STS:http://bestwisewords.com/zh/ram/product-overview/what-is-sts
//STS SDK概覽:http://bestwisewords.com/zh/ram/developer-reference/sts-sdk-overview
//STS Python SDK調(diào)用示例:http://bestwisewords.com/zh/ram/developer-reference/use-the-sts-openapi-example
//
//賬號需求說明:
// 若使用離線功能(離線語音合成、喚醒), 則必須app_key、ak_id和ak_secret,或app_key、sts_ak_id、sts_ak_secret和sts_token
// 若使用在線功能(語音合成、實時轉(zhuǎn)寫、一句話識別、錄音文件轉(zhuǎn)寫等), 則只需app_key和token
[_utils getTicket:ticketJsonDict Type:get_token_from_server_for_online_features];
if ([ticketJsonDict objectForKey:@"token"] != nil) {
NSString *tokenValue = [ticketJsonDict objectForKey:@"token"];
if ([tokenValue length] == 0) {
TLog(@"The 'token' key exists but the value is empty.");
}
} else {
TLog(@"The 'token' key does not exist.");
}
[ticketJsonDict setObject:@"wss://nls-gateway.cn-shanghai.aliyuncs.com:443/ws/v1" forKey:@"url"]; // 默認(rèn)
//工作目錄路徑,SDK從該路徑讀取配置文件
[ticketJsonDict setObject:bundlePath forKey:@"workspace"]; // 必填
//當(dāng)初始化SDK時的save_log參數(shù)取值為true時,該參數(shù)生效。表示是否保存音頻debug,該數(shù)據(jù)保存在debug目錄中,需要確保debug_path有效可寫
[ticketJsonDict setObject:save_wav ? @"true" : @"false" forKey:@"save_wav"];
//debug目錄,當(dāng)初始化SDK時的save_log參數(shù)取值為true時,該目錄用于保存中間音頻文件
[ticketJsonDict setObject:debug_path forKey:@"debug_path"];
//FullCloud = 1 // 在線實時語音識別可以選這個
[ticketJsonDict setObject:@"1" forKey:@"service_mode"]; // 必填
NSString *id_string = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
TLog(@"id: %s", [id_string UTF8String]);
[ticketJsonDict setObject:id_string forKey:@"device_id"]; // 必填, 推薦填入具有唯一性的id, 方便定位問題
NSData *data = [NSJSONSerialization dataWithJSONObject:ticketJsonDict options:NSJSONWritingPrettyPrinted error:nil];
NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return jsonStr;
}
參數(shù)設(shè)置
以JSON字符串形式進(jìn)行設(shè)置。
-(NSString*) genParams {
NSMutableDictionary *nls_config = [NSMutableDictionary dictionary];
[nls_config setValue:@YES forKey:@"enable_intermediate_result"]; // 必填
//參數(shù)可根據(jù)實際業(yè)務(wù)進(jìn)行配置
//接口說明可見: http://bestwisewords.com/document_detail/173528.html
//查看 2.開始識別
//[nls_config setValue:@"<更新token>" forKey:@"token"];
//[nls_config setValue:@YES forKey:@"enable_punctuation_prediction"];
//[nls_config setValue:@YES forKey:@"enable_inverse_text_normalization"];
//[nls_config setValue:@YES forKey:@"enable_voice_detection"];
//[nls_config setValue:@10000 forKey:@"max_start_silence"];
//[nls_config setValue:@800 forKey:@"max_end_silence"];
//[nls_config setValue:@800 forKey:@"max_sentence_silence"];
//[nls_config setValue:@NO forKey:@"enable_words"];
//[nls_config setValue:@16000 forKey:@"sample_rate"];
//[nls_config setValue:@"opus" forKey:@"sr_format"];
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
[dictM setObject:nls_config forKey:@"nls_config"];
[dictM setValue:@(SERVICE_TYPE_SPEECH_TRANSCRIBER) forKey:@"service_type"]; // 必填
//如果有HttpDns則可進(jìn)行設(shè)置
//[dictM setObject:[_utils getDirectIp] forKey:@"direct_ip"];
/*若文檔中不包含某些參數(shù),但是此功能支持這個參數(shù),可以用如下萬能接口設(shè)置參數(shù)*/
//NSMutableDictionary *extend_config = [NSMutableDictionary dictionary];
//[extend_config setValue:@YES forKey:@"custom_test"];
//[dictM setObject:extend_config forKey:@"extend_config"];
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];
// 運(yùn)行過程中可以在nui_dialog_start時更新臨時參數(shù),尤其是更新過期token
// 注意: 若下一輪對話不再設(shè)置參數(shù),則繼續(xù)使用初始化時傳入的參數(shù)
//[dialog_params setValue:@"" forKey:@"app_key"];
//[dialog_params setValue:@"" forKey:@"token"];
NSData *data = [NSJSONSerialization dataWithJSONObject:dialog_params options:NSJSONWritingPrettyPrinted error:nil];
NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return jsonStr;
}
//若需要修改token等參數(shù), 可詳見genDialogParams()
NSString * parameters = [self genDialogParams];
//若要使用VAD模式,則需要設(shè)置nls_config參數(shù)啟動在線VAD模式(見genParams())
[_nui nui_dialog_start:MODE_P2T dialogParam:[parameters UTF8String]];
回調(diào)處理
onNuiAudioStateChanged:錄音狀態(tài)回調(diào),SDK內(nèi)部維護(hù)錄音狀態(tài),調(diào)用時根據(jù)該狀態(tài)的回調(diào)進(jìn)行錄音機(jī)的開關(guān)操作。
-(void)onNuiAudioStateChanged:(NuiAudioState)state{ TLog(@"onNuiAudioStateChanged state=%u", state); if (state == STATE_CLOSE || state == STATE_PAUSE) { // 舊版本示例工程提供的錄音模塊,僅做參考,可根據(jù)自身業(yè)務(wù)重寫錄音模塊。 // [_voiceRecorder stop:YES]; // 新版本示例工程提供了新的錄音模塊,僅做參考,可根據(jù)自身業(yè)務(wù)重寫錄音模塊。 [_audioController stopRecorder:NO]; } else if (state == STATE_OPEN){ self.recordedVoiceData = [NSMutableData data]; // 舊版本示例工程提供的錄音模塊,僅做參考,可根據(jù)自身業(yè)務(wù)重寫錄音模塊。 // [_voiceRecorder start]; // 新版本示例工程提供了新的錄音模塊,僅做參考,可根據(jù)自身業(yè)務(wù)重寫錄音模塊。 [_audioController startRecorder]; } }
onNuiNeedAudioData:錄音數(shù)據(jù)回調(diào),在該回調(diào)中填充錄音數(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事件回調(diào),請勿在事件回調(diào)中調(diào)用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有助于排查問題,請用戶進(jìn)行記錄保存。 TLog(@"ASR RESULT %s finish %d", asr_result, finish); NSString *result = [NSString stringWithUTF8String:asr_result]; } else if (nuiEvent == EVENT_ASR_ERROR) { // asr_result在EVENT_ASR_ERROR中為錯誤信息,搭配錯誤碼code和其中的task_id更易排查問題,請用戶進(jìn)行記錄保存。 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è)務(wù)重寫錄音模塊。 // [_voiceRecorder stop:YES]; // [_voiceRecorder start]; // 新版本示例工程提供了新的錄音模塊,僅做參考,可根據(jù)自身業(yè)務(wù)重寫錄音模塊。 [_audioController stopRecorder:NO]; [_audioController startRecorder]; } //finish 為真(可能是發(fā)生錯誤,也可能是完成識別)表示一次任務(wù)生命周期結(jié)束,可以開始新的識別 if (finish) { } return; }
結(jié)束識別
[_nui nui_dialog_cancel:NO];
常見問題
是否有Android和iOS的SDK,能否用在專有云環(huán)境下?
有SDK,在專有云安裝包里默認(rèn)不提供,可以通過阿里云幫助中心對應(yīng)的服務(wù)文檔中下載,如實時語音識別的Android SDK和iOS SDK。移動端SDK可以調(diào)用公共云ASR、TTS服務(wù),也可以用在專有云環(huán)境下。
iOS是否支持后臺處理?
SDK本身不限制前后臺,iOS SDK的樣例工程默認(rèn)僅支持前臺處理,如果您需要支持后臺處理,可以做如下修改:
在工程Info.list中添加Required background modes配置,并在該配置下添加Item,Value設(shè)置為App plays audio or streams audio/video using AirPlay。
在錄音模塊中進(jìn)入后臺時,不停止錄音。亦即NLSVoiceRecorder.m中_appResignActive接口中不做停止錄音調(diào)用。
下載語音交互iOS SDK至本地靜態(tài)庫,運(yùn)行Demo程序測試代碼時,模擬器可以正常運(yùn)行,真機(jī)無法運(yùn)行,報錯“Reason: no suitable image found. Did find:xxx”如何解決?
建議您刪除手機(jī)上對應(yīng)的APP后,執(zhí)行xcode clean
,并重新嘗試運(yùn)行。除此以外,還需檢查簽名的正確性,如果簽名不正確,需撤銷原來的inHouse證書,重新制作新的證書和provisioning profile,并將代碼重新簽名,再次打包。
iOS端集成nuisdk運(yùn)行報mic錯誤如何處理?
請檢查當(dāng)前錄音設(shè)備是否被占用。
使用智能語音服務(wù)集成iOS SDK,接入nuisdk.framework后,導(dǎo)入頭文件#import "nuisdk.framework/Headers/NeoNui.h"后項目報錯如何解決?
一般情況下是SDK導(dǎo)入有問題導(dǎo)致,請您確認(rèn)下圖參數(shù)是否已勾選,如果已勾選,建議您將頭文件導(dǎo)入方式換為#import <nuisdk/NeoNui.h>
。
按照文檔使用SDK接入后報錯“/Users/admin/FlashTranscription_iOS/Fc_ASR.xcodeproj Building for iOS, but the linked and embedded framework 'nuisdk.framework' was built for iOS + iOS Simulator."”如何解決?
可能因為版本過高導(dǎo)致,建議您修改項目配置Validate Workspace為Yes后,重新編譯。
使用集成語音服務(wù)iOS SDK,集成flutter_plugin時報錯“Undefined symbols for architecture arm64: "std::__1::mutex::~mutex()", referenced from: ___cxx_global_var_init in libflutter_tts.a(ringBuf.o)”如何解決?
您可以打開iOS工程下的Podfile文件,修改post_install do |installer|部分的代碼,再次執(zhí)行構(gòu)建即可成功。
TRTC實時音視頻和語音識別結(jié)合,當(dāng)同時調(diào)用麥克風(fēng)時可能會發(fā)生沖突,導(dǎo)致有一方?jīng)]有聲音如何解決?
建議嘗試TRTC的音視頻流,然后使用localStream.getAudioTrack
獲取MediaStreamTrack
對象,并轉(zhuǎn)換為符合ASR標(biāo)準(zhǔn)的音頻流,然后通過語音識別SDK發(fā)起請求。
使用集成語音服務(wù)iOS SDK,接入nuisdk.framework后報錯,要修改Legacy Build system才可以運(yùn)行,如何解決?
建議您修改項目配置Validate Workspace為Yes后,重新編譯。
使用App集成iOS SDK,提交到App store失敗,提示“Unsupported Architectures. The executable for AliYunSmart.app/Frameworks/nuisdk.framework contains unsupported architectures '[x86_ _64, i386]'. With error code”如何解決?
可能是模擬器架構(gòu)影響,您可以參考如下方法查看framework版本并移除framework模擬器架構(gòu)。
進(jìn)入到framework目錄。
輸入命令
lipo -info xxxFramework
,查看framework的架構(gòu)版本,如果含有模擬器打包需要把模擬器架構(gòu)移除。