SLNuiSpeechRecognizer.m 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. //
  2. // SLNuiSpeechRecognizer.m
  3. // SLAiELTS
  4. //
  5. // Created by Gusont on 2023/3/6.
  6. //
  7. #import "SLNuiSpeechRecognizer.h"
  8. #import <nuisdk/NeoNuiTts.h>
  9. #import "NLSPlayAudio.h"
  10. #import "NuiSdkUtils.h"
  11. #import <nuisdk/NeoNui.h>
  12. #import "NLSVoiceRecorder.h"
  13. #import <AdSupport/ASIdentifierManager.h>
  14. static BOOL save_log = YES;//不加无法保存
  15. static BOOL save_wav = YES;
  16. @interface SLNuiSpeechRecognizer()<NeoNuiSdkDelegate, NlsVoiceRecorderDelegate, NlsPlayerDelegate>
  17. @property(nonatomic,strong) NeoNui* nui;
  18. @property(nonatomic,strong) NlsVoiceRecorder *voiceRecorder;
  19. @property(nonatomic,strong) NSMutableData *recordedVoiceData;
  20. @property(nonatomic,strong) NuiSdkUtils *utils;
  21. @property(nonatomic,strong) NLSPlayAudio *voicePlayer;
  22. @property (nonatomic, strong) NSString *recorderStartTime;
  23. @property (nonatomic, assign) BOOL isSaveVoice;
  24. @end
  25. @implementation SLNuiSpeechRecognizer
  26. - (instancetype)init {
  27. self = [super init];
  28. if (self) {
  29. [self confignSpeechRecognizer];
  30. }
  31. return self;
  32. }
  33. -(void)dealloc{
  34. TLog(@"%s",__FUNCTION__);
  35. [_nui nui_release];
  36. }
  37. - (void)confignSpeechRecognizer {
  38. _voicePlayer = [[NLSPlayAudio alloc] init];
  39. _voicePlayer.delegate = self;
  40. _voiceRecorder = [[NlsVoiceRecorder alloc] init];
  41. _voiceRecorder.delegate = self;
  42. [SLAuthorizationManager requestAuthorization:YMAuthorizationTypeMicrophone grantedBlock:^(BOOL granted) {
  43. }];
  44. _utils = [[NuiSdkUtils alloc] init];
  45. [self initNui];
  46. }
  47. - (void)initNui {
  48. if (_nui == NULL) {
  49. _nui = [NeoNui get_instance];
  50. _nui.delegate = self;
  51. }
  52. //请注意此处的参数配置,其中账号相关需要在Utils.m getTicket 方法中填入后才可访问服务
  53. NSString * initParam = [self genInitParams];
  54. [_nui nui_initialize:[initParam UTF8String] logLevel:LOG_LEVEL_ERROR saveLog:save_log];
  55. NSString * parameters = [self genParams];
  56. [_nui nui_set_params:[parameters UTF8String]];
  57. }
  58. #pragma mark - 清除文件缓存
  59. - (void)clearCacheWithFilePath:(NSString *)path {
  60. //拿到path路径的下一级目录的子文件夹
  61. NSArray *subPathArr = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
  62. NSString *filePath = nil;
  63. NSError *error = nil;
  64. for (NSString *subPath in subPathArr)
  65. {
  66. filePath = [path stringByAppendingPathComponent:subPath];
  67. NSArray *subFilePathArr = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:filePath error:nil];
  68. if (subFilePathArr.count) {
  69. [self clearCacheWithFilePath:filePath];
  70. }else {
  71. if (![filePath containsString:@"origin_0"]) {
  72. //删除子文件夹
  73. [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
  74. }
  75. }
  76. }
  77. }
  78. -(NSString*) genInitParams {
  79. NSString *strResourcesBundle = [[NSBundle mainBundle] pathForResource:@"Resources" ofType:@"bundle"];
  80. NSString *bundlePath = [[NSBundle bundleWithPath:strResourcesBundle] resourcePath];
  81. NSString *id_string = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
  82. NSString *debug_path = [_utils createDir];
  83. dispatch_async(dispatch_queue_create("sl.nuispeech.voices.file", DISPATCH_QUEUE_CONCURRENT), ^{
  84. [self clearCacheWithFilePath:debug_path];
  85. });
  86. NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
  87. [dictM setObject:bundlePath forKey:@"workspace"];
  88. [dictM setObject:debug_path forKey:@"debug_path"];
  89. [dictM setObject:id_string forKey:@"device_id"];
  90. [dictM setObject:save_wav ? @"true" : @"false" forKey:@"save_wav"];
  91. //从阿里云获取appkey和token进行语音服务访问
  92. [dictM setObject:SLNuisdkAppKey forKey:@"app_key"];
  93. // [dictM setObject:SLNuisdkToken forKey:@"token"];
  94. //由于token 24小时过期,可以参考getTicket实现从阿里云服务动态获取
  95. [_utils getTicket:dictM];
  96. [dictM setObject:@"wss://nls-gateway.cn-shanghai.aliyuncs.com:443/ws/v1" forKey:@"url"];
  97. NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];
  98. NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
  99. return jsonStr;
  100. }
  101. -(NSString*) genParams {
  102. NSMutableDictionary *nls_config = [NSMutableDictionary dictionary];
  103. [nls_config setValue:@YES forKey:@"enable_intermediate_result"];
  104. // 参数可根据实际业务进行配置
  105. // [nls_config setValue:@true forKey:@"enable_punctuation_prediction"];
  106. // [nls_config setValue:@true forKey:@"enable_inverse_text_normalization"];
  107. // [nls_config setValue:@true forKey:@"enable_voice_detection"];
  108. // [nls_config setValue:@10000 forKey:@"max_start_silence"];
  109. // [nls_config setValue:@800 forKey:@"max_end_silence"];
  110. // [nls_config setValue:@16000 forKey:@"sample_rate"];
  111. // [nls_config setValue:@"opus" forKey:@"sr_format"];
  112. NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
  113. [dictM setObject:nls_config forKey:@"nls_config"];
  114. [dictM setValue:@(SERVICE_TYPE_ASR) forKey:@"service_type"];
  115. // 如果有HttpDns则可进行设置
  116. // [dictM setObject:[_utils getDirectIp] forKey:@"direct_ip"];
  117. NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];
  118. NSString * jsonStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
  119. return jsonStr;
  120. }
  121. #pragma mark - ------语音识别------
  122. - (void)startSpeechRecognizer {
  123. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  124. [formatter setDateFormat:@"yyyyMMdd_HHmmss"];
  125. NSDate *datenow = [NSDate date];
  126. NSString *currentTimeString = [formatter stringFromDate:datenow];
  127. self.recorderStartTime = currentTimeString;
  128. NSLog(@"currentTimeString----%@",currentTimeString);
  129. self.recordedVoiceData = nil;
  130. dispatch_async(dispatch_get_main_queue(), ^{
  131. if (_nui != nil) {
  132. [_nui nui_dialog_start:MODE_P2T dialogParam:NULL];
  133. } else {
  134. TLog(@"in StartButHandler no nui alloc");
  135. }
  136. });
  137. }
  138. - (void)stopSpeechRecognizer:(BOOL)isSaveVoice {
  139. self.isSaveVoice = isSaveVoice;
  140. if (_nui != nil) {
  141. [_nui nui_dialog_cancel:NO];
  142. [_voiceRecorder stop:YES];
  143. } else {
  144. TLog(@"in StopButHandler no nui alloc");
  145. }
  146. }
  147. #pragma mark - Nui Listener
  148. -(void)onNuiEventCallback:(NuiCallbackEvent)nuiEvent
  149. dialog:(long)dialog
  150. kwsResult:(const char *)wuw
  151. asrResult:(const char *)asr_result
  152. ifFinish:(bool)finish
  153. retCode:(int)code {
  154. // TLog(@"onNuiEventCallback event %d finish %d", nuiEvent, finish);
  155. if (nuiEvent == EVENT_ASR_PARTIAL_RESULT || nuiEvent == EVENT_ASR_RESULT) {
  156. // TLog(@"ASR RESULT %s finish %d", asr_result, finish);
  157. NSString *result = [NSString stringWithUTF8String:asr_result];
  158. NSDictionary *dict = [result mj_JSONObject];
  159. NSDictionary *payloadDict = [dict objectForKey:@"payload"];
  160. if (finish && self.recognizerResultBlock && self.isSaveVoice) {
  161. NSString *voicePath = [_utils createDir];
  162. voicePath = [voicePath stringByAppendingFormat:@"/%@/origin_0.wav", self.recorderStartTime];
  163. self.recognizerResultBlock([payloadDict objectForKey:@"result"] ?: @"", voicePath);
  164. }
  165. }
  166. else if (nuiEvent == EVENT_ASR_ERROR) {
  167. // TLog(@"EVENT_ASR_ERROR error[%d]", code);
  168. } else if (nuiEvent == EVENT_MIC_ERROR) {
  169. // TLog(@"MIC ERROR");
  170. [_voiceRecorder stop:true];
  171. [_voiceRecorder start];
  172. }
  173. //
  174. //finish 为真(可能是发生错误,也可能是完成识别)表示一次任务生命周期结束,可以开始新的识别
  175. if (finish) {
  176. }
  177. return;
  178. }
  179. -(int)onNuiNeedAudioData:(char *)audioData length:(int)len {
  180. // TLog(@"onNuiNeedAudioData");
  181. static int emptyCount = 0;
  182. @autoreleasepool {
  183. @synchronized(_recordedVoiceData){
  184. if (_recordedVoiceData.length > 0) {
  185. int recorder_len = 0;
  186. if (_recordedVoiceData.length > len)
  187. recorder_len = len;
  188. else
  189. recorder_len = _recordedVoiceData.length;
  190. NSData *tempData = [_recordedVoiceData subdataWithRange:NSMakeRange(0, recorder_len)];
  191. [tempData getBytes:audioData length:recorder_len];
  192. tempData = nil;
  193. NSInteger remainLength = _recordedVoiceData.length - recorder_len;
  194. NSRange range = NSMakeRange(recorder_len, remainLength);
  195. [_recordedVoiceData setData:[_recordedVoiceData subdataWithRange:range]];
  196. emptyCount = 0;
  197. return recorder_len;
  198. } else {
  199. if (emptyCount++ >= 50) {
  200. TLog(@"_recordedVoiceData length = %lu! empty 50times.", (unsigned long)_recordedVoiceData.length);
  201. emptyCount = 0;
  202. }
  203. return 0;
  204. }
  205. }
  206. }
  207. return 0;
  208. }
  209. -(void)onNuiAudioStateChanged:(NuiAudioState)state {
  210. // TLog(@"onNuiAudioStateChanged state=%u", state);
  211. if (state == STATE_CLOSE || state == STATE_PAUSE) {
  212. [_voiceRecorder stop:YES];
  213. } else if (state == STATE_OPEN){
  214. self.recordedVoiceData = [NSMutableData data];
  215. [_voiceRecorder start];
  216. }
  217. }
  218. -(void)onNuiRmsChanged:(float)rms {
  219. // TLog(@"onNuiRmsChanged rms=%f", rms);
  220. if (self.onVolumeChangedBlock) {
  221. self.onVolumeChangedBlock(rms);
  222. }
  223. }
  224. - (void)recorderDidStart {
  225. TLog(@"recorderDidStart");
  226. }
  227. - (void)recorderDidStop {
  228. [self.recordedVoiceData setLength:0];
  229. }
  230. - (void)voiceDidFail:(NSError *)error {
  231. TLog(@"recorder error ");
  232. }
  233. - (void)voiceRecorded:(NSData *)frame {
  234. @synchronized(_recordedVoiceData){
  235. [_recordedVoiceData appendData:frame];
  236. }
  237. }
  238. - (void)playerDidFinish {
  239. NSLog(@"----->>>playerDidFinish");
  240. }
  241. @end