本文介绍了如何使用阿里云智能语音服务提供的iOS NUI SDK,包括SDK下载安装、关键接口及代码示例。
前提条件
下载安装
- 说明
下载后请在样例初始化代码中替换您的阿里云账号信息、Appkey和Token才可运行。为方便集成,2.5.14版本后iOS接口使用纯Object-C接口,不再使用C++混合接口。
类别
兼容范围
系统
最低支持iOS9
架构
arm64,x86_64
此SDK还包含如下功能,若未支持您想要的功能,请去对应文档获取SDK。
功能
是否支持
一句话识别
是
实时语音识别
是
语音合成
是
实时长文本语音合成
是
流式文本语音合成
是
离线语音合成
否
录音文件识别极速版
是
唤醒及命令词
否
听悟实时推流
是
解压ZIP包,将ZIP包中的nuisdk.framework添加到您的工程中,并在工程Build Phases的Link Binary With Libraries中添加nuisdk.framework。请确保在编译配置的General > Frameworks, Libraries, and Embedded Content中配置nuisdk.framework为Embed & Sign。
使用Xcode打开此工程,工程中提供了参考代码以及一些直接可使用的工具类,例如音频播放录制和文件操作,您可以直接复制源码到您的实际工程进行使用。其中语音合成示例代码在StreamInputTtsViewController类中。
SDK关键接口
startStreamInputTts:开始运行流式TTS
/** * 与服务端完成建链,并开始流式语音合成任务 * @param ticket:json string形式的初始化参数,参见下方说明。 * @param parameters:json string形式的初始化参数,参见下方说明。 * @param session_id:当前会话的id,若客户端请求时传入则原样返回,否则由服务端自动生成32位唯一ID。 * @param level:log打印级别,值越小打印越多。 * @param save_log:是否保存log为文件,存储目录为ticket中的debug_path字段值。注意,log文件无上限,请注意持续存储导致磁盘存满。 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ - (int) startStreamInputTts:(const char *)ticket parameters:(const char *)parameters sessionId:(const char *)sessionId logLevel:(NuiSdkLogLevel)logLevel saveLog:(BOOL)saveLog;
stopStreamInputTts: 停止语音合成
/** * 结束合成任务,通知服务端流入文本数据发送完毕,阻塞等待服务端处理完成,并返回所有合成音频。阻塞超时可以通过start接口中的complete_waiting_ms设置 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ - (int) stopStreamInputTts;
sendStreamInputTts:以流式的方式发送文本
/** * 以流式的方式发送文本 * @param text:从大模型当中生成的流式文本 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ - (int) sendStreamInputTts:(const char *)text;
StreamInputTtsDelegate 事件代理
onStreamInputTtsEventCallback:SDK事件回调
/** * 事件回调 * @param event:回调事件,参见如下事件列表。 * @param taskid:整个实时语音合成会话的任务ID,整个请求中需要保持一致,32位唯一ID。 * @param sessionId:当前会话的id,若客户端请求时传入则原样返回,否则由服务端自动生成32位唯一ID。 * @param ret_code:参见错误码,出现STREAM_INPUT_TTS_EVENT_TASK_FAILED事件时有效,可查阅https://help.aliyun.com/document_detail/459864.html。 * @param error_msg:详细错误信息,出现STREAM_INPUT_TTS_EVENT_TASK_FAILED事件时有效。 * @param timestamp:合成结果中时间戳相关信息。 * @param all_response:完整的json string格式返回消息,可从中解析需要的信息。 */ - (void)onStreamInputTtsEventCallback:(StreamInputTtsCallbackEvent)event taskId:(char*)taskid sessionId:(char*)sessionId ret_code:(int)ret_code error_msg:(char*)error_msg timestamp:(char*)timestamp all_response:(char*)all_response;
StreamInputTtsCallbackEvent事件列表:
名称
说明
TTS_EVENT_SYNTHESIS_STARTED
语音合成开始,准备播放。
TTS_EVENT_SENTENCE_BEGIN
服务端检测到了一句话的开始。
TTS_EVENT_SENTENCE_SYNTHESIS
增量返回语音合成的结果,包含最新的音频和时间戳,句内全量,句间增量。
TTS_EVENT_SENTENCE_END
服务端检测到了一句话的结束,返回该句的全量时间戳。
TTS_EVENT_SYNTHESIS_COMPLETE
服务端检测到了一句话的结束,返回该句的全量时间戳。
TTS_EVENT_TASK_FAILED
语音合成发生错误,详见ret_code和error_msg。
onStreamInputTtsDataCallback:合成数据回调。
/** * 合成数据回调。 * @param data:合成的音频数据,写入播放器。 */ void onStreamInputTtsDataCallback(byte[] data);
调用步骤
初始化SDK和播放组件。
根据业务需求设置参数。
调用startStreamInputTts开始进行流式文本语音合成。
调用sendStreamInputTts持续发送待合成文本。在合成数据回调中,将数据写入播放器进行播放,建议使用流式播放。
调用stopStreamInputTts表示文本发送完成,等待合成完毕。
收到语音合成结束的回调。
代码示例
开始语音合成
NSMutableDictionary *ticketJsonDict = [NSMutableDictionary dictionary]; [ticketJsonDict setObject:@"wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1" forKey:@"url"]; // 必填 [ticketJsonDict setObject:@"10000" forKey:@"complete_waiting_ms"]; //获取账号访问凭证: [_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."); } NSError *error; NSData *ticketJsonData = [NSJSONSerialization dataWithJSONObject:ticketJsonDict options:0 error:&error]; NSString *ticket = [[NSString alloc] initWithData:ticketJsonData encoding:NSUTF8StringEncoding]; NSDictionary *paramsJsonDict = @{ @"voice": @"zhixiaoxia", @"format": @"pcm", @"sample_rate": @(16000), @"volume": @(50), @"speech_rate": @(0), @"pitch_rate": @(0), @"enable_subtitle": @(YES) }; NSData *paramsJsonData = [NSJSONSerialization dataWithJSONObject:paramsJsonDict options:0 error:&error]; NSString *parameters = [[NSString alloc] initWithData:paramsJsonData encoding:NSUTF8StringEncoding]; _streamInputTtsSdk = [StreamInputTts get_instance]; _streamInputTtsSdk.delegate = self; int ret = [_streamInputTtsSdk startStreamInputTts:[ticket UTF8String] parameters:[parameters UTF8String] sessionId:nil logLevel:NUI_LOG_LEVEL_VERBOSE saveLog:NO];
流式发送合成文本
NSString * sentence = @"番茄炒蛋怎么做?"; [_streamInputTtsSdk sendStreamInputTts:[sentence UTF8String]]
结束语音合成
[_streamInputTtsSdk stopStreamInputTts];
回调处理
onStreamInputTtsEventCallback:流式文本语音合成事件回调,根据语音合成状态控制播放器。
- (void)onStreamInputTtsEventCallback:(StreamInputTtsCallbackEvent)event taskId:(char*)taskid sessionId:(char*)sessionId ret_code:(int)ret_code error_msg:(char*)error_msg timestamp:(char*)timestamp all_response:(char*)all_response { NSString *log = [NSString stringWithFormat:@"\n事件回调(%d):%s", event, all_response]; TLog(@"%@", log); if (event == TTS_EVENT_SYNTHESIS_STARTED) { TLog(@"onStreamInputTtsEventCallback TTS_EVENT_SYNTHESIS_STARTED"); // 合成启动,启动播放器 [_audioController startPlayer]; } else if (event == TTS_EVENT_SENTENCE_BEGIN) { TLog(@"onStreamInputTtsEventCallback TTS_EVENT_SENTENCE_BEGIN"); } else if (event == TTS_EVENT_SENTENCE_SYNTHESIS) { TLog(@"onStreamInputTtsEventCallback TTS_EVENT_SENTENCE_SYNTHESIS"); } else if (event == TTS_EVENT_SENTENCE_END) { TLog(@"onStreamInputTtsEventCallback TTS_EVENT_SENTENCE_END"); } else if (event == TTS_EVENT_SYNTHESIS_COMPLETE) { TLog(@"onStreamInputTtsEventCallback TTS_EVENT_SYNTHESIS_COMPLETE"); // 注意这里的event事件是指语音合成完成,而非播放完成,播放完成需要由voicePlayer对象来进行通知 [_audioController drain]; } else if (event == TTS_EVENT_TASK_FAILED) { TLog(@"onStreamInputTtsEventCallback TTS_EVENT_TASK_FAILED:%s", error_msg); // 注意这里的event事件是指语音合成完成,而非播放完成,播放完成需要由voicePlayer对象来进行通知 [_audioController drain]; } }
onStreamInputTtsDataCallback:语音合成数据回调,将回调中的合成数据写入播放器进行播放。
- (void)onStreamInputTtsDataCallback:(char*)buffer len:(int)len { NSString *log = [NSString stringWithFormat:@"\n音频回调 %d bytes", len]; TLog(log); dispatch_async(dispatch_get_main_queue(), ^{ self.logTextView.text = [self.logTextView.text stringByAppendingString:log]; NSRange bottom = NSMakeRange(self.logTextView.text.length -1, 1); [self.logTextView scrollRangeToVisible:bottom]; }); if (buffer != NULL && len > 0) { [_audioController write:(char*)buffer Length:(unsigned int)len]; } }