创建声音复刻 SDK 引擎实例前调用,完成网络环境等相关依赖配置。本方法每个App进程生命周期内仅需调用一次。
[SpeechEngine prepareEnvironment];
声音复刻 SDK 如下方式获取相关实例。
//创建实例 self.engine = [[SpeechEngine alloc] init]; //添加引擎代理,需要实现回调方法 [self.engine createEngineWithDelegate:self];
对于声音复刻 SDK 所有流程,需配置如下通用参数:
// 声音复刻引擎 [self.engine setStringParam:SE_VOICECLONE_ENGINE forKey:SE_PARAMS_KEY_ENGINE_NAME_STRING];
为便于开发者集成调试,有如下建议:
日志级别,开发时设置为 TRACE
,线上设置WARN
;
调试路径,声音复刻 SDK 会在该路径下生成名为 speech_sdk.log
的日志文件,开发时设置,线上关闭。
// 日志级别 [self.engine setStringParam:SE_LOG_LEVEL_TRACE forKey:SE_PARAMS_KEY_LOG_LEVEL_STRING]; // 调试路径 [self.engine setStringParam:@"{DEBUG PATH}" forKey:SE_PARAMS_KEY_DEBUG_PATH_STRING];
如果需要一个用户持有多个音色,需要业务方自行组织用户子ID格式,例如通过“用户ID + 序号/时间戳”来生成用户子ID,从而确保音色的唯一性。
[self.engine setStringParam:@"{YOUR UID}" forKey:SE_PARAMS_KEY_UID_STRING];
需要申请 Appid
和 Token
,配置时 Token
需要添加固定前缀 Bearer;
。
[self.engine setStringParam:@"{APPID}" forKey:SE_PARAMS_KEY_APP_ID_STRING]; [self.engine setStringParam:@"Bearer;{TOKEN}" forKey:SE_PARAMS_KEY_APP_TOKEN_STRING];
发起声音复刻请求,需要配置请求地址
参数。
[self.engine setStringParam:@"https://openspeech.bytedance.com" forKey:SE_PARAMS_KEY_VOICECLONE_ADDRESS_STRING]; [self.engine setStringParam:@"wss://openspeech.bytedance.com" forKey:SE_PARAMS_KEY_VOICECLONE_STREAM_ADDRESS_STRING];
SDK 还支持调整建连、接收超时,单位:毫秒。
// 如无特殊需要,建议使用默认值 [self.engine setIntParam:12000 forKey:SE_PARAMS_KEY_VOICECLONE_CONN_TIMEOUT_INT]; [self.engine setIntParam:8000 forKey:SE_PARAMS_KEY_VOICECLONE_RECV_TIMEOUT_INT];
语音识别 SDK 支持以录音机、原始音频流或音频文件作为输入,配置值分别为:
[self.engine setStringParam:SE_RECORDER_TYPE_RECORDER forKey:SE_PARAMS_KEY_RECORDER_TYPE_STRING];
[self.engine setStringParam:SE_RECORDER_TYPE_FILE forKey:SE_PARAMS_KEY_RECORDER_TYPE_STRING]; [self.engine setStringParam:@"../data/voiceclone_rec_file.pcm" forKey:SE_PARAMS_KEY_RECORDER_FILE_STRING];
[self.engine setStringParam:SE_RECORDER_TYPE_STREAM forKey:SE_PARAMS_KEY_RECORDER_TYPE_STRING]; // ... // 启动某复刻流程 // ... // 流式输入音频数据 while (data_size > 0) { SEEngineErrorCode ret = [self.engine feedAudio:data length:data_size]; if (ret != SENoError) { NSLog(@"Fail to feed data to engine: %d", ret); } } // 音频输入完成 SEEngineErrorCode ret = [self.engine sendDirective:SEDirectiveFinishTalking]; if (ret != SENoError) { NSLog(@"Fail to finish talking: %d", ret); }
对于复刻流程中可能出现的录音操作,是否将录到的音频以wav格式写录音文件。
// 是否开启写录音文件 [self.engine setBoolParam:TRUE forKey:SE_PARAMS_KEY_VOICECLONE_ENABLE_DUMP_BOOL]; // 写录音文件路径 [self.engine setStringParam:@"{REC FILE PATH}" forKey:SE_PARAMS_KEY_VOICECLONE_REC_PATH_STRING];
参数配置完成后,调用 初始化接口
,完成引擎实例的初始化。
SEEngineErrorCode ret = [self.engine initEngine]; if (ret != SENoError) { NSLog(@"Init Engine failed: %d", ret); }
// 设置用户性别 [self.engine setBoolParam:FALSE forKey:SE_PARAMS_KEY_VOICECLONE_GENDER_BOOL];
SEEngineErrorCode ret = [self.engine sendDirective:SEDirectiveVoiceCloneGetTask]; if (ret != SpeechEngineDefines.ERR_NO_ERROR) { NSLog(@"Fail to get task: %d", ret); }
- (void)onMessageWithType:(SEMessageType)type andData:(NSData *)data { NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; switch (type) { case SEVoiceCloneGetTaskResult: NSLog(@"Get task: %@.", dataStr); break; } }
其中data为任务信息JSON字符串,JSON格式如下:
[ { "task_id":10, // int32, 音色复刻任务ID,任务类型标识 "progress":1, // int32, 当前用户已完成进度 "total_text":20, // int32, 任务文本数 "texts":["文本","文本","文本"] // array(string), 任务文本数组,每项为所需朗读的文本字符串 }, { "task_id":11, "progress":1, "total_text":20, "texts":["文本","文本","文本"] } ]
// 设置当前执行的任务ID [self.engine setIntParam:10 forKey:SE_PARAMS_KEY_VOICECLONE_TASKID_INT];
SEEngineErrorCode ret = [self.engine sendDirective:SEDirectiveVoiceCloneCheckEnv]; if (ret != SpeechEngineDefines.ERR_NO_ERROR) { NSLog(@"Fail to check env: %d", ret); }
- (void)onMessageWithType:(SEMessageType)type andData:(NSData *)data { NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; switch (type) { case SEVoiceCloneCheckEnvResult: NSLog(@"Check env: %@.", dataStr); break; } }
其中data为检测结果JSON字符串,JSON格式如下:
{ "status":0, // int32, 环境检测状态 "noise":1, // double(float64), 噪音分贝 }
其中status:(等于0 -- 通过;小于0 -- 未通过;大于0 -- 检测中)
2:检测中,当前时间区间噪音较大
1:检测中,当前时间区间信噪比检测正常
0:检测通过
-1:检测失败,未知错误
-2:检测未通过,噪音较大
复刻任务ID: 设置执行的复刻任务ID。
文本序号: 当前朗读的文本在整个任务中的序号,0为第一句。
文本内容: 当前朗读的任务文本内容。
// 设置当前执行的复刻任务ID [self.engine setIntParam:10 forKey:SE_PARAMS_KEY_VOICECLONE_TASKID_INT]; // 设置任务文本序号 [self.engine setIntParam:0 forKey:SE_PARAMS_KEY_VOICECLONE_TEXT_SEQ_INT]; // 设置任务文本内容 [self.engine setStringParam:@"文本" forKey:SE_PARAMS_KEY_VOICECLONE_TEXT_STRING];
SEEngineErrorCode ret = [self.engine sendDirective:SEDirectiveVoiceCloneRecordVoice]; if (ret != SpeechEngineDefines.ERR_NO_ERROR) { NSLog(@"Fail to record voice: %d", ret); }
// 音频输入完成,等待最终处理结果 [self.engine sendDirective:SEDirectiveFinishTalking];
- (void)onMessageWithType:(SEMessageType)type andData:(NSData *)data { NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; switch (type) { case SEVoiceCloneRecordVoiceResult: NSLog(@"Record voice: %@.", dataStr); break; } }
其中data为检测结果JSON字符串,JSON格式如下:
{ "status":0, // int32, 录音检测结果 "wrong_index": [ // array(int32), 检测未通过的word索引,假设有文本 "一二三四五六" 而wrong_index:[2] 的意思是 "三"字没有通过检测 2, 3 ] }
其中status: (等于0 -- 通过;小于0 -- 未通过;大于0 -- 检测中)
0: 检测通过
-1: 检测失败,未知错误
-2: 噪音太大
-3: 声音太小
-4: 声音太大
-5: 信噪比过低
-6: 识别结果中有错字
-7: 识别结果中有多读
-8: 识别结果中有漏读
-9: 录音进度冲突,如重复录制当前位置之前的文本
注:多读、漏读时wrong_index为空,且问题优先级:多读 > 漏读 > 错字,即有多读时会忽略漏读错字等问题。
// 设置当前执行的任务ID [self.engine setIntParam:10 forKey:SE_PARAMS_KEY_VOICECLONE_TASKID_INT];
SEEngineErrorCode ret = [self.engine sendDirective:SEDirectiveVoiceCloneSubmitTask]; if (ret != SpeechEngineDefines.ERR_NO_ERROR) { NSLog(@"Fail to submit: %d", ret); }
- (void)onMessageWithType:(SEMessageType)type andData:(NSData *)data { NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; switch (type) { case SEVoiceCloneSubmitTaskResult: NSLog(@"Submit task: %@.", dataStr); break; } }
其中data 为JSON字符串, JSON格式如下:
{ "voice_type":"abc", // string, 音色名,用于训练完成后语音合成时做传入参数 }
// 设置需要查询的用户子ID [self.engine setStringParam:@"uid1;uid2;uid3" forKey:SE_PARAMS_KEY_VOICECLONE_QUERY_UIDS_STRING];
SEEngineErrorCode ret = [self.engine sendDirective:SEDirectiveVoiceCloneQueryStatus]; if (ret != SpeechEngineDefines.ERR_NO_ERROR) { NSLog(@"Fail to query status: %d", ret); }
- (void)onMessageWithType:(SEMessageType)type andData:(NSData *)data { NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; switch (type) { case SEVoiceCloneQueryStatusResult: NSLog(@"Query status: %@.", dataStr); break; } }
其中data为查询结果JSON字符串,JSON格式如下:
[ { "status":0, // int32, 状态码 "uid":"388808087185088", // string, 用户子ID "task_id":1, // int32, 任务ID "appid":"test", "extra":{ "voice_type": "xxx", // string, 音色名,用于训练完成后语音合成时做传入参数 "vc_styles":[ {"name":"通用中文","value":"default"}, {"name":"通用美式英文","value":"en"} ], // 音色支持的风格名和风格值,其中风格值可以传入TTS引擎合成使用。 "record_url":["",""] // 录音音频地址 } }, { "status":1, "uid":"388808087185088", "appid":"test", "task_id":1, "extra":{} }, { "uid":"388808087185088", "appid":"test", "task_id":1, "status":2, "extra":{ "queueing_count":10, // 前面排队中的人 "queueing_cost":50, // 排队耗时,单位为分钟 } }, { "uid":"388808087185088", "appid":"test", "task_id":1, "status":3, "extra":{ "training_cost":50, // 训练平均耗时,单位为分钟 } }, { "uid":"388808087185088", "appid":"test", "task_id":1, "status":4, "extra":{ "loading_cost":2, // 加载平均耗时,单位为分钟 } } ]
其中status: (等于0 -- 通过;小于0 -- 训练失败;大于0 -- 训练中)
-2: 任务不存在
-1: 训练失败
0: 音色可用
1: 录制中
2: 排队中
3: 训练中
4: 音色加载中
// 设置当前执行的任务ID [self.engine setIntParam:10 forKey:SE_PARAMS_KEY_VOICECLONE_TASKID_INT];
SEEngineErrorCode ret = [self.engine sendDirective:SEDirectiveVoiceCloneDeleteData]; if (ret != SpeechEngineDefines.ERR_NO_ERROR) { NSLog(@"Fail to delete data: %d", ret); }
- (void)onMessageWithType:(SEMessageType)type andData:(NSData *)data { switch (type) { case SEVoiceCloneDeleteDataResult: NSLog(@"Delete data."); break; } }
接收到该回调即为删除成功,其中data为空字符串。
一个引擎实例在一时间只能执行某一个流程,可以通过两种方式立刻停止正在执行的流程而不需要处理结果。
需要在收到 SEEngineStop 消息后引擎才算真正停止。
[self.engine sendDirective:SEDirectiveStopEngine];
执行后会在当前位置等待引擎结束,再执行下一行代码。请勿直接在回调线程中执行该方法,否则可能导致死锁等待。
[self.engine sendDirective:SEDirectiveSyncStopEngine];
当不再需要声音复刻功能后,建议对引擎实例进行销毁,释放内存资源。
[self.engine destroyEngine]; self.engine = nil;