iOS SDK

本文介绍了如何使用阿里云智能语音服务提供的iOS NUI SDK,包括SDK下载安装、关键接口及代码示例。

前提条件

下载安装

  1. 下载SDK和示例代码

    说明

    下载后请在样例初始化代码中替换您的阿里云账号信息、Appkey和Token才可运行。为方便集成,2.5.14版本后iOS接口使用纯Object-C接口,不再使用C++混合接口。

    类别

    兼容范围

    系统

    最低支持iOS9

    架构

    arm64,x86_64

    此SDK还包含如下功能,若未支持您想要的功能,请去对应文档获取SDK。

    功能

    是否支持

    一句话识别

    实时语音识别

    语音合成

    实时长文本语音合成

    流式文本语音合成

    离线语音合成

    录音文件识别极速版

    唤醒及命令词

    听悟实时推流

  2. 解压ZIP包,将ZIP包中的nuisdk.framework添加到您的工程中,并在工程Build Phases的Link Binary With Libraries中添加nuisdk.framework。请确保在编译配置的General > Frameworks, Libraries, and Embedded Content中配置nuisdk.framework为Embed & Sign。

  3. 使用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);

调用步骤

  1. 初始化SDK和播放组件。

  2. 根据业务需求设置参数。

  3. 调用startStreamInputTts开始进行流式文本语音合成。

  4. 调用sendStreamInputTts持续发送待合成文本。在合成数据回调中,将数据写入播放器进行播放,建议使用流式播放。

  5. 调用stopStreamInputTts表示文本发送完成,等待合成完毕。

  6. 收到语音合成结束的回调。

代码示例

  • 开始语音合成

    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];
          }
      }