PQSpeechTranscriberUtil.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. //
  2. // PQSpeechTranscriberUtil.m
  3. // BFFramework
  4. //
  5. // Created by ak on 2021/12/8.
  6. // 说明文档:https://help.aliyun.com/document_detail/173528.html
  7. #import <Foundation/Foundation.h>
  8. #import "NeoNui.h"
  9. #import "PQSpeechTranscriberUtil.h"
  10. #import <AdSupport/ASIdentifierManager.h>
  11. #import "NLSVoiceRecorder.h"
  12. @interface PQSpeechTranscriberUtil ()<NeoNuiSdkDelegate,NlsVoiceRecorderDelegate> {
  13. }
  14. @property(nonatomic,strong) NeoNui* nui;
  15. @property(nonatomic,strong) NlsVoiceRecorder *voiceRecorder;
  16. @property(nonatomic,strong) NSMutableData *recordedVoiceData;
  17. //
  18. @end
  19. @implementation PQSpeechTranscriberUtil
  20. - (id)init {
  21. self = [super init];
  22. _voiceRecorder = [[NlsVoiceRecorder alloc] init];
  23. _voiceRecorder.delegate = self;
  24. [self initNui];
  25. NSString *version = [NSString stringWithUTF8String:[_nui nui_get_version]];
  26. NSLog(@"nui_get_version is %@",version);
  27. return self;
  28. }
  29. /// 开始识别
  30. - (void)startTranscriber{
  31. if (_nui != nil) {
  32. [_nui nui_dialog_start:MODE_P2T dialogParam:NULL];
  33. } else {
  34. NSLog(@"in StartButHandler no nui alloc");
  35. }
  36. }
  37. /// 结束识别
  38. - (void)endTranscriber{
  39. self.recordedVoiceData = nil;
  40. if (_nui != nil) {
  41. [_nui nui_dialog_cancel:NO];
  42. [_voiceRecorder stop:YES];
  43. dispatch_async(dispatch_get_main_queue(), ^{
  44. });
  45. } else {
  46. NSLog(@"in StopButHandler no nui alloc");
  47. }
  48. }
  49. //初始化SDK
  50. - (void) initNui {
  51. if (_nui == NULL) {
  52. _nui = [NeoNui get_instance];
  53. _nui.delegate = self;
  54. }
  55. //请注意此处的参数配置,其中账号相关需要在Utils.m getTicket 方法中填入后才可访问服务
  56. NSString * initParam = [self genInitParams];
  57. int initcode = [_nui nui_initialize:[initParam UTF8String] logLevel:LOG_LEVEL_VERBOSE saveLog:false];
  58. NSLog(@"初始化结果%d",initcode);
  59. NSString * parameters = [self genParams];
  60. int setparamscode = [_nui nui_set_params:[parameters UTF8String]];
  61. NSLog(@"设置参数结果%d",setparamscode);
  62. }
  63. //析构SDK
  64. - (void)terminateNui {
  65. [_nui nui_release];
  66. }
  67. //Get Document Dir
  68. -(NSString *)dirDoc {
  69. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  70. NSString *documentsDirectory = [paths objectAtIndex:0];
  71. NSLog(@"app_home_doc: %@",documentsDirectory);
  72. return documentsDirectory;
  73. }
  74. //create dir for saving files
  75. -(NSString *)createDir {
  76. NSString *documentsPath = [self dirDoc];
  77. NSFileManager *fileManager = [NSFileManager defaultManager];
  78. NSString *testDirectory = [documentsPath stringByAppendingPathComponent:@"voices"];
  79. // 创建目录
  80. BOOL res=[fileManager createDirectoryAtPath:testDirectory withIntermediateDirectories:YES attributes:nil error:nil];
  81. if (res) {
  82. NSLog(@"文件夹创建成功");
  83. }else
  84. NSLog(@"文件夹创建失败");
  85. return testDirectory;
  86. }
  87. -(NSString*) genInitParams {
  88. NSString *strResourcesBundle = [[NSBundle mainBundle] pathForResource:@"Resources" ofType:@"bundle"];
  89. NSString *bundlePath = [[NSBundle bundleWithPath:strResourcesBundle] resourcePath];
  90. NSString *id_string = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
  91. NSString *debug_path = [self createDir];
  92. //
  93. NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
  94. [dictM setObject:bundlePath forKey:@"workspace"];
  95. [dictM setObject:debug_path forKey:@"debug_path"];
  96. [dictM setObject:id_string forKey:@"device_id"];
  97. [dictM setObject:@"false" forKey:@"save_wav"];
  98. //从阿里云获取appkey和token进行语音服务访问
  99. [dictM setObject:@"oTOh8zDVK6iswF9o" forKey:@"app_key"];
  100. [dictM setObject:@"7968a3cc7e8d4b87936bacd85211887b" forKey:@"token"];
  101. //由于token 24小时过期,可以参考getTicket实现从阿里云服务动态获取
  102. // [_utils getTicket:dictM];
  103. [dictM setObject:@"wss://nls-gateway.cn-shanghai.aliyuncs.com:443/ws/v1" forKey:@"url"];
  104. NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];
  105. NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
  106. return jsonStr;
  107. }
  108. -(NSString*) genParams {
  109. //https://help.aliyun.com/document_detail/173528.html 参数说明
  110. NSMutableDictionary *nls_config = [NSMutableDictionary dictionary];
  111. //是否返回中间识别结果,默认值:False。
  112. [nls_config setValue:@YES forKey:@"enable_intermediate_result"];
  113. //是否在后处理中添加标点,默认值:False
  114. [nls_config setValue:@YES forKey:@"enable_punctuation_prediction"];
  115. //是否在后处理中执行ITN。设置为true时,中文数字将转为阿拉伯数字输出,默认值:False。
  116. [nls_config setValue:@YES forKey:@"enable_inverse_text_normalization"];
  117. [nls_config setValue:@YES forKey:@"enable_voice_detection"];
  118. [nls_config setValue:@10000 forKey:@"max_start_silence"];
  119. [nls_config setValue:@800 forKey:@"max_end_silence"];
  120. //语音断句检测阈值,静音时长超过该阈值被认为断句。取值范围:200ms~2000ms,默认值:800ms。
  121. [nls_config setValue:@800 forKey:@"max_sentence_silence"];
  122. //是否开启返回词信息。默认值:False。
  123. [nls_config setValue:@NO forKey:@"enable_words"];
  124. [nls_config setValue:@16000 forKey:@"sample_rate"];
  125. //音频编码格式,支持OPUS编码和PCM原始音频。默认值:OPUS。
  126. [nls_config setValue:@"opus" forKey:@"sr_format"];
  127. //ADD BY AK
  128. [nls_config setValue:@YES forKey:@"enable_sentence_detection"];
  129. NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
  130. [dictM setObject:nls_config forKey:@"nls_config"];
  131. [dictM setValue:@(SERVICE_TYPE_SPEECH_TRANSCRIBER) forKey:@"service_type"];
  132. // 如果有HttpDns则可进行设置
  133. // [dictM setObject:[_utils getDirectIp] forKey:@"direct_ip"];
  134. NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];
  135. NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
  136. return jsonStr;
  137. }
  138. #pragma mark - Voice Recorder Delegate
  139. -(void) recorderDidStart{
  140. NSLog(@"recorderDidStart");
  141. }
  142. -(void) recorderDidStop{
  143. [self.recordedVoiceData setLength:0];
  144. NSLog(@"recorderDidStop");
  145. }
  146. -(void) voiceRecorded:(NSData*) frame{
  147. @synchronized(_recordedVoiceData){
  148. [_recordedVoiceData appendData:frame];
  149. }
  150. }
  151. -(void) voiceDidFail:(NSError*)error{
  152. NSLog(@"recorder error ");
  153. }
  154. #pragma mark - Nui Listener
  155. -(void)onNuiEventCallback:(NuiCallbackEvent)nuiEvent
  156. dialog:(long)dialog
  157. kwsResult:(const char *)wuw
  158. asrResult:(const char *)asr_result
  159. ifFinish:(BOOL)finish
  160. retCode:(int)code {
  161. NSLog(@"onNuiEventCallback event %d finish %d", nuiEvent, finish);
  162. if (nuiEvent == EVENT_ASR_PARTIAL_RESULT || nuiEvent == EVENT_ASR_RESULT || nuiEvent == EVENT_SENTENCE_END) {
  163. NSString *result = [NSString stringWithUTF8String:asr_result];
  164. NSLog(@"识别结果: %@ finish %d", result, finish);
  165. if (self.delegate && [self.delegate respondsToSelector:@selector(eventCallback:asrResult:)])
  166. {
  167. [self.delegate eventCallback:self asrResult:result];
  168. }
  169. } else if (nuiEvent == EVENT_ASR_ERROR) {
  170. NSLog(@"EVENT_ASR_ERROR error[%d]", code);
  171. } else if (nuiEvent == EVENT_MIC_ERROR) {
  172. NSLog(@"MIC ERROR");
  173. [_voiceRecorder stop:YES];
  174. [_voiceRecorder start];
  175. }
  176. //finish 为真(可能是发生错误,也可能是完成识别)表示一次任务生命周期结束,可以开始新的识别
  177. if (finish) {
  178. dispatch_async(dispatch_get_main_queue(), ^{
  179. });
  180. }
  181. return;
  182. }
  183. //录音数据回调,在该回调中填充录音数据。
  184. -(int)onNuiNeedAudioData:(char *)audioData length:(int)len {
  185. static int emptyCount = 0;
  186. @autoreleasepool {
  187. @synchronized(_recordedVoiceData){
  188. if (_recordedVoiceData.length > 0) {
  189. int recorder_len = 0;
  190. if (_recordedVoiceData.length > len)
  191. recorder_len = len;
  192. else
  193. recorder_len = _recordedVoiceData.length;
  194. NSData *tempData = [_recordedVoiceData subdataWithRange:NSMakeRange(0, recorder_len)];
  195. [tempData getBytes:audioData length:recorder_len];
  196. tempData = nil;
  197. NSInteger remainLength = _recordedVoiceData.length - recorder_len;
  198. NSRange range = NSMakeRange(recorder_len, remainLength);
  199. [_recordedVoiceData setData:[_recordedVoiceData subdataWithRange:range]];
  200. emptyCount = 0;
  201. return recorder_len;
  202. } else {
  203. if (emptyCount++ >= 50) {
  204. NSLog(@"_recordedVoiceData length = %lu! empty 50times.", (unsigned long)_recordedVoiceData.length);
  205. emptyCount = 0;
  206. }
  207. return 0;
  208. }
  209. }
  210. }
  211. return 0;
  212. }
  213. -(void)onNuiAudioStateChanged:(NuiAudioState)state{
  214. NSLog(@"onNuiAudioStateChanged state=%u", state);
  215. if (state == STATE_CLOSE || state == STATE_PAUSE) {
  216. [_voiceRecorder stop:YES];
  217. } else if (state == STATE_OPEN){
  218. self.recordedVoiceData = [NSMutableData data];
  219. [_voiceRecorder start];
  220. }
  221. }
  222. -(void)onNuiRmsChanged:(float)rms {
  223. NSLog(@"onNuiRmsChanged rms=%f", rms);
  224. }
  225. @end