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打开此工程,工程中提供了参考代码以及一些直接可使用的工具类,例如音频播放录制和文件操作,您可以直接复制源码到您的实际工程进行使用。其中语音合成示例代码在TTSViewController类中。

SDK关键接口

  • tts_initialize:初始化SDK

    /**
     * 初始化SDK,SDK为单例,请先释放后再次进行初始化。请勿在UI线程调用,意外下可能引起阻塞。
     * @param parameters: 初始化参数,参见下方说明或接口说明:https://help.aliyun.com/document_detail/173642.html
     * @param level: log打印级别,值越小打印越多
     * @param save_log: 是否保存log为文件,存储目录为parameter中的debug_path字段值。注意,log文件无上限,请注意持续存储导致磁盘存满。
     * @return 参考错误码:https://help.aliyun.com/document_detail/459864.html
     */
    -(int) nui_tts_initialize:(const char *)parameters
                     logLevel:(NuiSdkLogLevel)level
                      saveLog:(BOOL)save_log;
  • nui_tts_play: 开始播放

    /**
     * 开始播放
     * @param priority: 任务优先级,请使用"1"
     * @param taskid: 任务id,可传入32个字节的uuid或者传入空内容由SDK自动生成
     * @param text: 要播放的文本内容
     * @return 参考错误码:https://help.aliyun.com/document_detail/459864.html
     */
    -(int) nui_tts_play:(const char *)priority
                 taskId:(const char *)taskid
                  text:(const char *)text;
  • nui_tts_cancel:取消播放

    /**
     * 取消合成任务
     * @param taskid: 传入想要停止的任务id,如果为空则取消所有任务
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    -(int) nui_tts_cancel:(const char *)taskid;
  • nui_tts_pause:暂停播放

    /**
     * 暂停
     * @return 参考错误码
     */
    -(int) nui_tts_pause;
  • nui_tts_resume:恢复播放

    /**
     * 恢复暂停的任务
     * @return 参考错误码
     */
    -(int) nui_tts_resume;
  • nui_tts_set_param:设置语音合成参数

    /**
     * 以键值对形式设置参数
     * @param param: 参数名,参考接口说明
     * @param value: 参数值,参考接口说明
     * @return 参考错误码
     */
    -(int) nui_tts_set_param:(const char *)param
                       value:(const char *)value;
  • nui_tts_get_param:获取参数

    /**
     * 获取参数值
     * @param param: 参数名,参考接口说明
     * @return 参数值
     */
    -(const char *) nui_tts_get_param:(const char *)param;
  • nui_tts_release: 释放SDK资源

    /**
     * 释放SDK
     * @return 参考错误码
     */
    -(int) nui_tts_release;
  • NeoNuiTtsDelegate 事件代理

    • onNuiTtsUserdataCallback:在回调中提供音频数据。

      /**
       * 当开始识别时,此回调被连续调用,App需要在回调中进行语音数据填充,语音数据来自App的录音
       * @param info: 在使用时间戳功能时返回时间戳结果,json格式
       * @param info_len: info字段的数据长度
       * @param buffer: 合成的语音数据
       * @param len: 合成的语音长度
       * @param taskid: 本次合成的任务id
       */
      - (void)onNuiTtsUserdataCallback:(char*)info infoLen:(int)info_len buffer:(char*)buffer len:(int)len taskId:(char*)task_id;
  • onNuiTtsEventCallback: 事件回调。

    /**
     * SDK主要事件回调
     * @param event: 回调事件,参考接口说明:https://help.aliyun.com/zh/isi/developer-reference/overview-5
     * @param taskid: 本次合成的任务id
     * @param code: 参考错误码,TTS_EVENT_ERROR时有效
     */
    - (void)onNuiTtsEventCallback:(NuiSdkTtsEvent)event taskId:(char*)taskid code:(int)code;
  • NuiSdkTtsEvent事件列表:

    名称

    说明

    TTS_EVENT_START

    语音合成开始,准备播放。

    TTS_EVENT_END

    语音合成结束,合成数据已全部抛出,但并不表示播放结束。

    TTS_EVENT_CANCEL

    取消语音合成。

    TTS_EVENT_PAUSE

    语音合成暂停。

    TTS_EVENT_RESUME

    语音合成恢复。

    TTS_EVENT_ERROR

    语音合成发生错误。

调用步骤

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

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

  3. 调用nui_tts_play进行播放。

  4. 在合成数据回调中,将数据写入播放器进行播放,建议使用流式播放。

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

代码示例

说明

接口默认采用get_instance方式获得单例,您如果有多例需求,也可以直接alloc对象进行使用。

语音合成初始化

NSString * initParam = [self genInitParams];
[_nui nui_tts_initialize:[initParam UTF8String] logLevel:LOG_LEVEL_VERBOSE saveLog:true];

其中,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];
  
    //注意:
    //  语音交互服务需要先准备好账号,并开通相关服务。具体步骤请查看:
    //    https://help.aliyun.com/zh/isi/getting-started/start-here
    //
    //原始账号:
    //  账号(子账号)信息主要包括AccessKey ID(后续简称为ak_id)和AccessKey Secret(后续简称为ak_secret)。
    //  此账号信息一定不可存储在app代码中或移动端侧,以防账号信息泄露造成资费损失。
    //
    //STS临时凭证:
    //  由于账号信息下发给客户端存在泄露的可能,阿里云提供的一种临时访问权限管理服务STS(Security Token Service)。
    //  STS是由账号信息ak_id和ak_secret,通过请求生成临时的sts_ak_id/sts_ak_secret/sts_token
    //  (为了区别原始账号信息和STS临时凭证, 命名前缀sts_表示STS生成的临时凭证信息)
    //什么是STS:https://help.aliyun.com/zh/ram/product-overview/what-is-sts
    //STS SDK概览:https://help.aliyun.com/zh/ram/developer-reference/sts-sdk-overview
    //STS Python SDK调用示例:https://help.aliyun.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
    //  若使用在线功能(语音合成、实时转写、一句话识别、录音文件转写等), 则只需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.");
    }

    //工作目录路径,SDK从该路径读取配置文件
    [ticketJsonDict setObject:bundlePath forKey:@"workspace"]; // 必填, 且需要有读写权限
    TLog(@"workspace:%@", bundlePath);
    [ticketJsonDict setObject:debug_path forKey:@"debug_path"];
    TLog(@"debug_path:%@", debug_path);

    [ticketJsonDict setObject:@"wss://nls-gateway.cn-shanghai.aliyuncs.com:443/ws/v1" forKey:@"url"]; // 默认

    // 设置成在线语音合成模式, 这个设置很重要, 遗漏会导致无法运行
    [ticketJsonDict setObject:@"2" forKey:@"mode_type"]; // 必填
    
    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;
}

根据需求设置参数

//可以设置发音人、语调和语速等参数,参数列表参见接口说明。
[self.nui nui_tts_set_param:"font_name" value:"xiaoyun"];

启动语音合成

//建议同一时间单示例启动一个task进行语音合成,单实例多task易出异常。
[self.nui nui_tts_play:"1" taskId:"" text:[content UTF8String]];

回调处理

  • onNuiTtsEventCallback:语音合成事件回调,根据语音合成状态控制播放器。

    - (void)onNuiTtsEventCallback:(NuiSdkTtsEvent)event taskId:(char*)taskid code:(int)code {
        TLog(@"onNuiTtsEventCallback event[%d]", event);
        if (event == TTS_EVENT_START) {
            TLog(@"onNuiTtsEventCallback TTS_EVENT_START");
            loop_in = TTS_EVENT_START;
            // 旧版本示例工程提供的播放器,仅做参考,可根据自身业务重写播放器。
            // [self->_voicePlayer play];
    
            // 新版本示例工程提供了新的播放器,仅做参考,可根据自身业务重写播放器。
            [_audioController startPlayer];
        } else if (event == TTS_EVENT_END || event == TTS_EVENT_CANCEL || event == TTS_EVENT_ERROR) {
            loop_in = event;
            if (event == TTS_EVENT_END) {
                TLog(@"onNuiTtsEventCallback TTS_EVENT_END");
                // 旧版本示例工程提供的播放器,仅做参考,可根据自身业务重写播放器。
                // 注意这里的event事件是指语音合成完成,而非播放完成,播放完成需要由voicePlayer对象来进行通知
                // [self->_voicePlayer drain];
    
                // 新版本示例工程提供了新的播放器,仅做参考,可根据自身业务重写播放器。
                // 注意这里的event事件是指语音合成完成,而非播放完成,播放完成需要由audioController对象来进行通知
                [_audioController drain];
            } else {
                // 旧版本示例工程提供的播放器,仅做参考,可根据自身业务重写播放器。
                // 取消播报、或者发生异常时终止播放
                // [self->_voicePlayer stop];
    
                // 新版本示例工程提供了新的播放器,仅做参考,可根据自身业务重写播放器。
                // 取消播报、或者发生异常时终止播放
                [_audioController stopPlayer];
            }
            if (event == TTS_EVENT_ERROR) {
                const char *errmsg = [_nui nui_tts_get_param: "error_msg"];
                TLog(@"tts get errmsg:%s", errmsg);
            }
        }
    }
  • onNuiTtsUserdataCallback:语音合成数据回调,将回调中的合成数据写入播放器进行播放。

    - (void)onNuiTtsUserdataCallback:(char*)info infoLen:(int)info_len buffer:(char*)buffer len:(int)len taskId:(char*)task_id {
        TLog(@"onNuiTtsUserdataCallback info ...");
        if (info_len > 0) {
            TLog(@"onNuiTtsUserdataCallback info text %s. index %d.", info, info_len);
        }
        if (len > 0) {
            // 旧版本示例工程提供的播放器,仅做参考,可根据自身业务重写播放器。
            // [_voicePlayer write:(char*)buffer Length:(unsigned int)len];
    
            // 新版本示例工程提供了新的播放器,仅做参考,可根据自身业务重写播放器。
            [_audioController write:(char*)buffer Length:(unsigned int)len];
        }
    }

结束语音合成

[self.nui nui_tts_cancel:NULL];

常见问题

使用在线合成语音iOS SDK,写入文件播放声音是杂音,是什情况?

首先需要确认合成音频格式(PCM、WAV、MP3),如存储的音频流是MP3格式,但播放器不支持该格式音频就会出现杂音的状况,建议更换一下播放软件重试。同时也有用户出现音频只有尾部出现杂音的情况,可以用BeyondCompare查看音频流,是否有日志写入音。

使用语音合成iOS SDK,连续点击播放按钮,高频率触发播放出现页面终止情况,该如何解决?

由于在线合成是需要连接网络,网络状况会直接影响接口响应时间,如果您的业务需要快速停止任务并开始下一条,可以根据业务需求调整网络超时时间。

使用语音合成iOS SDK,如何保存为文件,保存格式是什么?

可以在onNuiTtsUserdataCallback接口参数中将合成的数据保存成文件,合成的格式以传出参数为主,例如[nls_config setObject:@"mp3" forKey:@"encode_type"]

目前支持格式为PCM、WAV、mp3,需要注意是,语音合成的文档案例中播放器不支持mp3格式音频,直接使用可能产生噪音,但存储的mp3格式文件可以用支持mp3格式的播放软件试听。如果个别音频文件出现少字的现象,可能是因为该发音人合成速度过快(如xiaoyun),部分数据没有写入文件被清除,您可以在fwrite后调用fflush保证数据完全写入文件。code

使用语音合成iOS SDK,onNuiTtsUserdataCallback不返回时间戳信息,如何解决?

默认情况SDK不返回时间戳,如果您需要获取时间戳信息,可以通过接口setparamTts设置enable_subtitle,详情请参见接口说明

是否有Android和iOS的SDK,能否用在专有云下?

有SDK,在专有云安装包里默认不提供,可以通过阿里云帮助中心对应的服务文档中下载,如实时语音识别的Android SDKiOS SDK。移动端SDK可以调用公共云ASR、TTS服务,也可以用在专有云环境下。

是否支持后台处理?

SDK本身不限制前后台,iOS SDK的样例工程默认仅支持前台处理,如果您需要支持后台处理,可以做如下修改:

  1. 在工程Info.list中添加Required background modes配置,并在该配置下添加item,Value设置为App plays audio or streams audio/video using AirPlay配置1

  2. 在录音模块中进入后台时,不停止录音。亦即NLSVoiceRecorder.m中_appResignActive接口中不做停止录音调用。配置2

下载语音交互iOS SDK至本地库,测试代码时,在模拟器可以正常运行,真机却无法运行,报错提示“Reason: no suitable image found. Did find:xxx”。

建议您删除手机上对应的APP后,执行xcode clean,并重新尝试运行。除此以外,还需检查签名的正确性,如果签名不正确,需撤销原来的inHouse证书,重新制作新的证书和provisioning profile,并将代码重新签名,再次打包。

使用智能语音服务集成iOS SDK,接入NuiSdk运行报错MIC。

建议您检查下当前录音设备是否有被占用。

使用智能语音服务集成iOS SDK,接入nuisdk.framework后,导入头文件项目失败。

一般情况下是SDK导入有问题导致,请您确认下图参数是否已勾选,如果已勾选,建议您将头文件导入方式换为#import <nuisdk/NeoNui.h>nui

使用iOS SDK接入nuisdk.framework后,报错提示“/Users/admin/FlashTranscription_iOS/Fc_ASR.xcodeproj Building for iOS, but the linked and embedded framework 'nuisdk.framework' was built for iOS + iOS Simulator”。

可能因为版本过高导致,建议您修改项目配置Validate Workspace为Yes后,重新编译。Validate Workspace

使用集成语音服务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|部分的代码,再次执行构建即可成功。code

TRTC实时音视频和语音识别结合,当同时调用麦克风时可能会发生冲突,导致有一方没有声音,如何解决?

建议尝试TRTC的音视频流,然后使用localStream.getAudioTrack获取MediaStreamTrack对象,并转换为符合ASR标准的音频流,之后通过语音识别SDK发起请求。

使用集成语音服务iOS SDK,在接入nuisdk.framework后报错,需要修改Legacy Build system,才可以运行,是什么原因?

建议您修改项目配置Validate Workspace为Yes后,重新编译。

Validate Workspace

使用App集成iOS SDK,提交到App store失败,提示“Unsupported Architectures. The executable for AliYunSmart.app/Frameworks/nuisdk.framework contains unsupported architectures '[x86_ _64, i386]'. With error code”。

可能是模拟器架构影响,您可以参考如下方法查看framework版本并移除framework模拟器架构。

  1. 进入到framework目录。

  2. 输入命令lipo -info xxxFramework,查看framework的架构版本,如果含有模拟器打包需要把模拟器架构移除。