KSAccompanyWebViewController.m 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771
  1. //
  2. // KSAccompanyWebViewController.m
  3. // KulexiuForTeacher
  4. //
  5. // Created by Kyle on 2022/3/20.
  6. //
  7. #import "KSAccompanyWebViewController.h"
  8. #import "WebViewBaseConfig.h" // 基础配置
  9. #import "SwiftImportHeader.h" // swift 桥接
  10. #import <KSToolLibrary/KSAudioSessionManager.h> // audio session
  11. #import <KSToolLibrary/KSAQRecordManager.h> // 录音
  12. #import <KSToolLibrary/KSWebSocketManager.h> // web socket
  13. #import <CloudAccompanyLibrary/KSVideoRecordManager.h> // 视频录制
  14. #import <CloudAccompanyLibrary/KSCloudBeatView.h> // 节拍器
  15. #import <KSToolLibrary/MidiPlayerEngine.h> // midi 播放
  16. #import <CloudAccompanyLibrary/kSNewPlayer.h> // mp3 播放器
  17. #import "AccompanyLoadingView.h"
  18. #define KSMidiSongFileKey (@"KSDownloadMidiSong")
  19. // 合成
  20. #import <KSToolLibrary/KSMediaEditor.h>
  21. #import "KSMediaMergeView.h"
  22. #import <KSTunerLibrary/KSTunerLibrary-Swift.h>
  23. @interface KSAccompanyWebViewController ()<KSAQRecordManagerDelegate,KSAudioSessionManagerDelegate,PlayerEngineDelegate,TunerDelegate,kSNewPlayerManagerDelegate>
  24. @property (nonatomic, strong) KSAudioSessionManager *audioSessionManager;
  25. @property (nonatomic, assign) MetronomeType beatType;
  26. @property (nonatomic, strong) MidiPlayerEngine *playerEngine; // player
  27. @property (nonatomic, assign) BOOL initEngineSuccess; // 加载引擎是否成功
  28. @property (nonatomic, assign) float songOriginalSpeed;
  29. @property (nonatomic, assign) float currentSpeed; // 当前播放的速度
  30. @property (nonatomic, assign) BOOL isPlaying; // 是否正在播放的状态
  31. @property (nonatomic, strong) NSString *currentSongId; // song id
  32. @property (nonatomic, strong) NSDictionary *configEngineParm; // 初始化mid播放引擎的参数
  33. @property (nonatomic, strong) NSMutableDictionary *metronomeParm; // 节拍器播放的参数
  34. // 是否发送了musicXML信息
  35. @property (nonatomic, assign) BOOL hasSendStartMessage;
  36. @property (nonatomic, strong) KSAQRecordManager *AQManager;
  37. @property (nonatomic, strong) KSWebSocketManager *socketManager;
  38. @property (nonatomic, strong) NSMutableDictionary *evaluatParm;
  39. // 录制的message
  40. @property (nonatomic, strong) NSMutableDictionary *recordParm;
  41. // 评测开始时发送recordStart消息的标识
  42. @property (nonatomic, assign) BOOL isCompareStart;
  43. // 校音开始时发送 checkStart消息标识
  44. @property (nonatomic, assign) BOOL isSoundCheckStart;
  45. // 自定义UI控件容器
  46. @property (nonatomic, strong) UIView *viewContainer;
  47. @property (nonatomic, strong) KSVideoRecordManager *videoRecordManager;
  48. @property (nonatomic, strong) NSDictionary *endRecordParm;
  49. @property (nonatomic, assign) BOOL isCameraOpen;
  50. @property (nonatomic, strong) AccompanyLoadingView *loadingView;
  51. @property (nonatomic, assign) BOOL isTunerRuning;
  52. @property (nonatomic, strong) Tuner *tuner;
  53. // 延迟校准开始时发送 AdjustStart消息标识
  54. @property (nonatomic, assign) BOOL isDelayCheckStart;
  55. // 检测延迟播放器
  56. @property (nonatomic, strong) kSNewPlayer *dingPlayer;
  57. @property (nonatomic, strong) kSNewPlayer *dongPlayer;
  58. // 音频播放器
  59. @property (nonatomic, strong) kSNewPlayer *musicPlayer;
  60. // 录音开始时间
  61. @property (nonatomic, assign) NSTimeInterval recordStartTime;
  62. // 播放开始时间
  63. @property (nonatomic, assign) NSTimeInterval playerStartTime;
  64. // 播放延迟
  65. @property (nonatomic, assign) NSInteger offsetTime;
  66. @property (nonatomic, assign) BOOL dingPlayerReady;
  67. @property (nonatomic, assign) BOOL dongPlayerReady;
  68. @property (nonatomic, assign) BOOL musicPlayerReady;
  69. @property (nonatomic, strong) NSDictionary *playerParm;
  70. @property (nonatomic, strong) NSMutableArray *delayArray;
  71. @property (nonatomic, assign) NSInteger checkIndex;
  72. @property (nonatomic, assign) NSInteger firstFrequence;
  73. @property (nonatomic, assign) NSInteger secondFrequence;
  74. @property (nonatomic, strong) NSURL *bgAudioUrl;
  75. @property (nonatomic, strong) NSURL *recordUrl;
  76. @property (nonatomic, strong) NSString *accompanyUrl;
  77. @property (nonatomic, assign) BOOL muteAccompany; // 是否静音
  78. @property (nonatomic, assign) NSInteger musicStartTime; // 开始播放的时间
  79. @end
  80. @implementation KSAccompanyWebViewController
  81. - (void)handerAudioInterruption {
  82. if (_playerEngine) {
  83. [self stopPlayAction];
  84. }
  85. if (_musicPlayer) {
  86. [self stopMp3Player];
  87. }
  88. }
  89. - (void)resumeAudioSession {
  90. // 恢复
  91. if (_playerEngine) {
  92. [self.playerEngine resumeAUGraph];
  93. }
  94. }
  95. // 打断处理
  96. - (void)audioInterruption {
  97. if (_videoRecordManager) {
  98. [self.videoRecordManager resetSession];
  99. }
  100. [self handerAudioInterruption];
  101. }
  102. - (void)ignorRecordVideo {
  103. if (_videoRecordManager) {
  104. self.videoRecordManager.skipSaveRecord = YES;
  105. }
  106. }
  107. - (void)stopSession {
  108. if (_videoRecordManager) {
  109. [self.videoRecordManager stopSession];
  110. }
  111. }
  112. - (void)resetPlayerConfig {
  113. [self freeMp3Player]; // 若之前有播放器,剔除
  114. self.firstFrequence = 800;
  115. self.secondFrequence = 800;
  116. self.dingPlayerReady = NO;
  117. self.dongPlayerReady = NO;
  118. self.musicPlayerReady = NO;
  119. self.recordStartTime = 0;
  120. self.playerStartTime = 0;
  121. self.offsetTime = 0;
  122. }
  123. - (void)downloadMp3File:(NSString *)musicUrl {
  124. if (![NSString isEmptyString:musicUrl]) {
  125. NSString *fileName = [musicUrl getUrlFileName];
  126. NSString *filePath = [self getAccompanyFilePathWithName:fileName];
  127. NSURL *fileUrl = [[NSURL alloc] initFileURLWithPath:filePath];
  128. if ([self checkSongHasSaveAccompanyWithSongUrl:musicUrl]) {
  129. [self configVideoRecord:fileUrl];
  130. }
  131. else {
  132. MJWeakSelf;
  133. [self downloadUrl:musicUrl success:^{
  134. [weakSelf configVideoRecord:fileUrl];
  135. } faliure:^{
  136. }];
  137. }
  138. }
  139. else {
  140. [self configVideoRecord:nil];
  141. }
  142. }
  143. - (void)initMp3Player:(NSString *)musicUrl checkUrl:(NSString *)checkUrl {
  144. [self resetPlayerConfig];
  145. self.accompanyUrl = musicUrl;
  146. [self.musicPlayer preparePlaySongWithUrl:musicUrl];
  147. [self.dingPlayer preparePlaySongWithUrl:checkUrl];
  148. [self.dongPlayer preparePlaySongWithUrl:checkUrl];
  149. [self prepareMusic];
  150. }
  151. - (void)prepareMusic {
  152. self.dingPlayer.isMute = YES;
  153. [self.dingPlayer startPlayNoSeek];
  154. self.dongPlayer.isMute = YES;
  155. [self.dongPlayer startPlayNoSeek];
  156. self.musicPlayer.isMute = YES;
  157. [self.musicPlayer startPlayNoSeek];
  158. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  159. [self.musicPlayer puasePlay];
  160. self.musicPlayer.isMute = NO;
  161. [self.dingPlayer puasePlay];
  162. self.dingPlayer.isMute = NO;
  163. [self.dongPlayer puasePlay];
  164. self.dongPlayer.isMute = NO;
  165. [self configPlayerDelegate];
  166. });
  167. }
  168. - (void)freeMp3Player {
  169. if (_dingPlayer) {
  170. [self.dingPlayer freePlayer];
  171. }
  172. if (_dongPlayer) {
  173. [self.dongPlayer freePlayer];
  174. }
  175. if (_musicPlayer) {
  176. [self.musicPlayer freePlayer];
  177. }
  178. }
  179. - (void)stopMp3Player {
  180. if (_dingPlayer) {
  181. [self.dingPlayer puasePlay];
  182. }
  183. if (_dongPlayer) {
  184. [self.dongPlayer puasePlay];
  185. }
  186. if (_musicPlayer) {
  187. [self.musicPlayer puasePlay];
  188. }
  189. }
  190. - (void)viewDidLoad {
  191. [super viewDidLoad];
  192. // Do any additional setup after loading the view.
  193. [self configAudioSession];
  194. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterBackground) name:@"appEnterBackground" object:nil];
  195. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterForeground) name:@"appEnterForeground" object:nil];
  196. }
  197. - (void)checkMessage:(id)message {
  198. NSDictionary *result = [message mj_JSONObject];
  199. NSString *type = [[result ks_dictionaryValueForKey:@"header"] ks_stringValueForKey:@"type"];
  200. NSString *commond = [[result ks_dictionaryValueForKey:@"header"] ks_stringValueForKey:@"commond"];
  201. if ([type isEqualToString:@"DELAY_CHECK"] && [commond isEqualToString:@"recordEnd"]) {
  202. NSDictionary *body = [result ks_dictionaryValueForKey:@"body"];
  203. NSTimeInterval micDelay = [body ks_doubleValueForKey:@"firstNoteDelayDuration"] - self.offsetTime;
  204. [self.delayArray addObject:[NSNumber numberWithDouble:micDelay]];
  205. }
  206. }
  207. - (void)connectSocketService {
  208. MJWeakSelf;
  209. self.socketManager.didReceiveMessage = ^(id _Nonnull message) {
  210. dispatch_async(dispatch_get_main_queue(), ^{
  211. [weakSelf checkMessage:message];
  212. // api
  213. NSMutableDictionary *sendMessage = [NSMutableDictionary dictionary];
  214. [sendMessage setValue:@"sendResult" forKey:@"api"];
  215. [sendMessage setValue:message forKey:@"content"];
  216. // 服务返回数据传给H5
  217. [weakSelf postMessage:sendMessage];
  218. });
  219. };
  220. self.socketManager.connectionStatus = ^(BOOL isSuccess) {
  221. dispatch_async(dispatch_get_main_queue(), ^{
  222. if (!isSuccess) {
  223. // NSLog(@"-----连接失败");
  224. if (weakSelf.hasSendStartMessage) {
  225. if (weakSelf.AQManager.isRunning) {
  226. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  227. @"content" : @{@"reson":@"服务异常,请重试"}
  228. };
  229. [weakSelf postMessage:postParm];
  230. [weakSelf stopRecordService];
  231. weakSelf.isCompareStart = NO;
  232. weakSelf.isSoundCheckStart = NO;
  233. weakSelf.isDelayCheckStart = NO;
  234. }
  235. }
  236. else if (weakSelf.evaluatParm) {
  237. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:weakSelf.evaluatParm];
  238. NSDictionary *valueDic = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
  239. [valueDic setValue:@"服务异常,请重试" forKey:@"reson"];
  240. [sendParm setValue:valueDic forKey:@"content"];
  241. [weakSelf postMessage:sendParm];
  242. weakSelf.hasSendStartMessage = YES;
  243. weakSelf.isCompareStart = NO;
  244. weakSelf.isSoundCheckStart = NO;
  245. weakSelf.isDelayCheckStart = NO;
  246. }
  247. else { // 其他断开
  248. if (weakSelf.AQManager.isRunning) {
  249. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  250. @"content" : @{@"reson":@"服务异常,请重试"}
  251. };
  252. [weakSelf postMessage:postParm];
  253. [weakSelf stopRecordService];
  254. weakSelf.isCompareStart = NO;
  255. weakSelf.isSoundCheckStart = NO;
  256. weakSelf.isDelayCheckStart = NO;
  257. }
  258. }
  259. }
  260. else {
  261. // NSLog(@"-----连接成功");
  262. if (weakSelf.hasSendStartMessage == NO && weakSelf.evaluatParm) {
  263. NSDictionary *content = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
  264. NSString *sendData = [weakSelf configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
  265. [weakSelf sendDataToSocketService:sendData];
  266. [weakSelf postMessage:weakSelf.evaluatParm];
  267. weakSelf.hasSendStartMessage = YES;
  268. }
  269. }
  270. });
  271. };
  272. [self.socketManager SRWebSocketOpen];
  273. }
  274. - (void)appEnterBackground {
  275. NSDictionary *parm = @{@"api":@"suspendPlay"};
  276. [self postMessage:parm];
  277. [self handerAudioInterruption];
  278. }
  279. - (void)appEnterForeground {
  280. if (self.isCameraOpen) {
  281. if ([self.videoRecordManager getSessionStatusisActive] == NO) {
  282. [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
  283. }
  284. }
  285. }
  286. - (void)initWebView {
  287. [self.scrollView removeFromSuperview];
  288. [self.view addSubview:self.viewContainer];
  289. [self.viewContainer mas_makeConstraints:^(MASConstraintMaker *make) {
  290. make.left.right.top.bottom.mas_equalTo(self.view);
  291. }];
  292. if (self.myWebView == nil) {
  293. WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
  294. config.selectionGranularity = WKSelectionGranularityDynamic;
  295. config.allowsInlineMediaPlayback = YES;
  296. config.mediaTypesRequiringUserActionForPlayback = NO;
  297. config.processPool = [KSBaseWKWebViewController singleWkProcessPool];
  298. config.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
  299. [self configUserAgent:config];
  300. //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
  301. WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
  302. //这个类主要用来做native与JavaScript的交互管理
  303. WKUserContentController * wkUController = [[WKUserContentController alloc] init];
  304. [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:SCRIPT_NAME];
  305. config.userContentController = wkUController;
  306. WKPreferences *preferences = [WKPreferences new];
  307. // 是否支出javaScript
  308. preferences.javaScriptEnabled = YES;
  309. //不通过用户交互,是否可以打开窗口
  310. preferences.javaScriptCanOpenWindowsAutomatically = YES;
  311. config.preferences = preferences;
  312. self.myWebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
  313. self.myWebView.opaque = NO;
  314. self.myWebView.UIDelegate = self;
  315. self.myWebView.navigationDelegate = self;
  316. self.myWebView.scrollView.bounces = NO;
  317. self.myWebView.backgroundColor = [UIColor clearColor];
  318. self.myWebView.scrollView.backgroundColor = [UIColor clearColor];
  319. #ifdef DEBUG
  320. if (@available(iOS 16.4, *)) {
  321. self.myWebView.inspectable = YES;
  322. }
  323. #endif
  324. // 加载进度条和title
  325. [self.myWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
  326. [self.myWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
  327. [self.view addSubview:self.myWebView];
  328. [self.myWebView mas_makeConstraints:^(MASConstraintMaker *make) {
  329. make.left.right.mas_equalTo(self.view);
  330. make.top.mas_equalTo(self.view.mas_top);
  331. make.bottom.mas_equalTo(self.view.mas_bottom);
  332. }];
  333. self.myWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  334. [self setupProgress];
  335. [self loadRequest];
  336. }
  337. else {
  338. [self.myWebView reload];
  339. }
  340. }
  341. - (void)configUserAgent:(WKWebViewConfiguration *)config {
  342. NSString *oldUserAgent = config.applicationNameForUserAgent;
  343. NSString *newAgent = [NSString stringWithFormat:@"%@ %@ %@",oldUserAgent,AGENT_NAME,AGENT_DOMAIN];
  344. config.applicationNameForUserAgent = newAgent;
  345. }
  346. - (void)configRecordManager {
  347. if (self.AQManager.isRunning) {
  348. [self.AQManager stopRecord];
  349. }
  350. self.AQManager = [[KSAQRecordManager alloc] init];
  351. self.AQManager.delegate = self;
  352. }
  353. - (void)viewWillAppear:(BOOL)animated {
  354. [super viewWillAppear:animated];
  355. [self connectSocketService];
  356. if (self.isCameraOpen) {
  357. [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
  358. }
  359. }
  360. - (void)viewWillDisappear:(BOOL)animated {
  361. [super viewWillDisappear:animated];
  362. if (_socketManager) {
  363. [self.socketManager SRWebSocketClose];
  364. _socketManager = nil;
  365. }
  366. // 停止播放和录制
  367. [self stopSession];
  368. [self stopPlayAction];
  369. [self stopRecordService];
  370. [self stopTuner];
  371. BOOL isBack = [self isViewPopDismiss];
  372. if (isBack) { // 页面销毁才删除
  373. if (_AQManager) {
  374. [_AQManager freeAudioQueue];
  375. _AQManager = nil;
  376. }
  377. // 如果退出评测页面 清除 playerEngine
  378. if (self.playerEngine) {
  379. [self.playerEngine cleanup];
  380. self.playerEngine = nil;
  381. }
  382. // 返回不保存视频
  383. [self ignorRecordVideo];
  384. [self freeMp3Player];
  385. [self removeTuner];
  386. }
  387. }
  388. - (void)removeTuner {
  389. if (_tuner) {
  390. [_tuner freeTuner];
  391. _tuner = nil;
  392. NSLog(@"--- free tuner ");
  393. }
  394. }
  395. - (void)sendDataToSocketService:(id)data {
  396. if (_socketManager) {
  397. [self.socketManager sendData:data];
  398. }
  399. }
  400. #pragma mark --- WKScriptMessageHandler
  401. - (void)userContentController:(WKUserContentController *)userContentController
  402. didReceiveScriptMessage:(WKScriptMessage *)message {
  403. if ([message.name isEqualToString:SCRIPT_NAME]) {
  404. NSDictionary *parm = [self convertJsonStringToNSDictionary:message.body];
  405. // 回到主线程
  406. dispatch_async(dispatch_get_main_queue(), ^{
  407. if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"createMusicPlayer"]) {
  408. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  409. NSString *musicUrl = [content ks_stringValueForKey:@"musicSrc"];
  410. NSString *checkUrl = [content ks_stringValueForKey:@"tuneSrc"];
  411. self.playerParm = parm;
  412. [self initMp3Player:musicUrl checkUrl:checkUrl];
  413. // 下载文件
  414. [self downloadMp3File:musicUrl];
  415. }
  416. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startEvaluating"]) { // 开始评测
  417. [self configRecordManager];
  418. self.hasSendStartMessage = NO;
  419. PREMISSIONTYPE isOk = [RecordCheckManager checkMicPermissionAvaiable];
  420. if (isOk == PREMISSIONTYPE_YES) {
  421. self.evaluatParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  422. // 如果socket 连上了
  423. if (self.socketManager.socketReadyState == SR_OPEN) {
  424. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  425. NSString *sendData = [self configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
  426. [self sendDataToSocketService:sendData];
  427. [self postMessage:parm];
  428. self.hasSendStartMessage = YES;
  429. }
  430. else {
  431. [self connectSocketService];
  432. }
  433. }
  434. else {
  435. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  436. @"content" : @{@"reson":@"没有麦克风权限"}
  437. };
  438. [self postMessage:postParm];
  439. }
  440. }
  441. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endEvaluating"]) {// 停止评测
  442. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  443. self.musicStartTime = 0;
  444. self.evaluatParm = nil;
  445. [self stopRecordService];
  446. [self postMessage:parm];
  447. [self sendEndMessage];
  448. [self stopMp3Player]; // 停止播放
  449. });
  450. }
  451. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cancelEvaluating"]) { // 取消评测
  452. self.musicStartTime = 0;
  453. self.evaluatParm = nil;
  454. [self stopRecordService];
  455. [self postMessage:parm];
  456. [self stopMp3Player]; // 停止播放
  457. }
  458. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startRecording"]) { // 开始录制
  459. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  460. self.musicStartTime = [content ks_integerValueForKey:@"firstNoteTime"];
  461. if ([[content allKeys] containsObject:@"accompanimentState"]) {
  462. BOOL mute = [content ks_boolValueForKey:@"accompanimentState"] == NO;
  463. self.muteAccompany = mute;
  464. }
  465. else {
  466. self.muteAccompany = NO;
  467. }
  468. if (self->_videoRecordManager) {
  469. [self.videoRecordManager clearVideoFile];
  470. }
  471. [self postMessage:parm];
  472. self.isCompareStart = YES;
  473. [self startRecordService];
  474. }
  475. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endRecording"]) { // 停止录音
  476. self.recordParm = nil;
  477. [self stopRecordService];
  478. [self postMessage:parm];
  479. }
  480. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"pauseRecording"]) {
  481. [self puaseRecordService];
  482. [self postMessage:parm];
  483. }
  484. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"resumeRecording"]) {
  485. [self resumeRecordService];
  486. [self postMessage:parm];
  487. }
  488. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"isWiredHeadsetOn"]) {
  489. [self configAudioDeviceType:parm];
  490. }
  491. // 发送消息给socket service
  492. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyMessage"]) {
  493. [self sendMessageToSocket:parm];
  494. }
  495. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyServiceMessage"]) {
  496. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  497. NSString *sendData = [content mj_JSONString];
  498. NSLog(@"proxyServiceMessage ------- %@",sendData);
  499. [self sendDataToSocketService:sendData];
  500. }
  501. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openCamera"]) { // 开启摄像头
  502. PREMISSIONTYPE canOpenCamera = [RecordCheckManager checkCameraPremissionAvaiable];
  503. PREMISSIONTYPE albumEnable = [RecordCheckManager checkPhotoLibraryPremissionAvaiable];
  504. if (canOpenCamera == PREMISSIONTYPE_YES) {
  505. self.isCameraOpen = YES;
  506. [self.videoRecordManager setIgnoreAudio:YES];
  507. [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
  508. [self postMessage:parm];
  509. }
  510. if (albumEnable == PREMISSIONTYPE_NO) {
  511. [self showAlertWithMessage:@"请开启相册访问权限" type:CHECKDEVICETYPE_CAMREA];
  512. }
  513. }
  514. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"closeCamera"]) { // 关闭摄像头
  515. self.isCameraOpen = NO;
  516. if (self->_videoRecordManager) {
  517. [self.videoRecordManager removeDisplay];
  518. }
  519. [self postMessage:parm];
  520. }
  521. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startCapture"]) { // 开始录制
  522. PREMISSIONTYPE isOk = [RecordCheckManager checkPhotoLibraryPremissionAvaiable];
  523. if (isOk == PREMISSIONTYPE_YES) {
  524. [self.videoRecordManager setIgnoreAudio:YES];
  525. self.videoRecordManager.audioUrl = self.AQManager.audioUrl;
  526. [self.videoRecordManager startRecord];
  527. [self postMessage:parm];
  528. }
  529. }
  530. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endCapture"]) { // 结束录制
  531. if (self->_videoRecordManager) {
  532. self.endRecordParm = parm;
  533. [self.videoRecordManager stopRecord];
  534. }
  535. else {
  536. [self postMessage:parm];
  537. }
  538. }
  539. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"setCaptureMode"]) {
  540. NSString *modeString = [[parm ks_dictionaryValueForKey:@"content"] ks_stringValueForKey:@"mode"];
  541. BOOL isIgnoreAudio = [modeString isEqualToString:@"evaluating"];
  542. [self postMessage:parm];
  543. [self.videoRecordManager setIgnoreAudio:isIgnoreAudio];
  544. }
  545. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startSoundCheck"]) { // 开始校音
  546. [self configRecordManager];
  547. [self postMessage:parm];
  548. self.isCompareStart = NO;
  549. self.isSoundCheckStart = YES;
  550. self.isDelayCheckStart = NO;
  551. [self startRecordService];
  552. }
  553. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endSoundCheck"]) {
  554. // 结束校音
  555. self.isSoundCheckStart = NO;
  556. [self stopRecordService];
  557. [self postMessage:parm];
  558. }
  559. // 音视频合成
  560. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openAdjustRecording"]) {
  561. KSMediaMergeView *mergeView = [[KSMediaMergeView alloc] init];
  562. if (self.bgAudioUrl == nil) {
  563. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"当前曲目无mp3伴奏"];
  564. }
  565. else {
  566. if (self.AQManager && self.AQManager.audioUrl) {
  567. self.recordUrl = self.AQManager.audioUrl;
  568. [self.view addSubview:mergeView];
  569. [mergeView mas_makeConstraints:^(MASConstraintMaker *make) {
  570. make.left.right.top.bottom.mas_equalTo(self.view);
  571. }];
  572. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  573. mergeView.recordId = [content ks_stringValueForKey:@"recordId"];
  574. mergeView.songName = [content ks_stringValueForKey:@"title"];
  575. mergeView.coverImage = [content ks_stringValueForKey:@"coverImg"];
  576. MJWeakSelf;
  577. NSInteger micDelay = [UserDefaultObjectForKey(@"micDelay") integerValue];
  578. NSInteger defaultDelay = self.offsetTime + micDelay;
  579. [mergeView configWithVideoUrl:self.videoRecordManager.videoFileURL bgAudioUrl:self.bgAudioUrl remoteBgUrl:self.accompanyUrl recordUrl:self.recordUrl offsetTime:defaultDelay mergeCallback:^{
  580. [weakSelf musicPublishCallBack:content];
  581. }];
  582. }
  583. else {
  584. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"麦克风被占用"];
  585. }
  586. }
  587. }
  588. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"videoUpdate"]) { // 上传
  589. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  590. NSMutableDictionary *contentParm = [NSMutableDictionary dictionaryWithDictionary:[sendParm ks_dictionaryValueForKey:@"content"]];
  591. if (self.videoRecordManager) {
  592. MJWeakSelf;
  593. [self.videoRecordManager saveVideoCallback:^(BOOL isSuccess, NSString * _Nullable message) {
  594. if (isSuccess) {
  595. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"已保存到相册"];
  596. [weakSelf uploadVideoWithParm:contentParm sendParm:sendParm];
  597. }
  598. }];
  599. }
  600. }
  601. #pragma mark -------- 云教练原生播放对接
  602. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDetail"]) { // 初始化方法
  603. /**
  604. api: 'cloudDetail',
  605. content: {
  606. midi: '',
  607. denominator: 4,
  608. numerator: 4,
  609. // xml整体原始速度
  610. originalSpeed: 90,
  611. // 间隔(ms)
  612. interval: 50
  613. }
  614. */
  615. [self configAudioSession];
  616. // 重置track num array
  617. self.configEngineParm = [parm copy];
  618. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  619. NSString *midiUrl = [content ks_stringValueForKey:@"midi"];
  620. NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
  621. NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
  622. NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
  623. self.beatType = [self getBeatTypeFromString:beatString];
  624. float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
  625. self.songOriginalSpeed = originalSpeed;
  626. self.currentSpeed = originalSpeed;
  627. NSInteger reportInterval = [content ks_integerValueForKey:@"interval"];
  628. // 下载midi文件
  629. BOOL hasSaveSong = [self checkSongHasSaveWithSongUrl:midiUrl];
  630. NSString *fileName = [midiUrl getUrlFileName];
  631. NSString *filePath = [self getFilePathWithName:fileName];
  632. if (hasSaveSong) {
  633. [self configPlayerEngineWithSong:filePath reportTime:reportInterval];
  634. }
  635. else {
  636. MJWeakSelf;
  637. [self downloadMidiFile:midiUrl success:^{
  638. [weakSelf configPlayerEngineWithSong:filePath reportTime:reportInterval];
  639. } faliure:^{
  640. }];
  641. }
  642. }
  643. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudGetMediaStatus"]) { // 获取播放状态
  644. /**
  645. api: 'cloudGetMediaStatus',
  646. content: {
  647. status: 'init' | 'play' | 'suspend'
  648. }
  649. */
  650. NSString *engineStatus = @"init";
  651. if (self.initEngineSuccess) {
  652. if (self.isPlaying) {
  653. engineStatus = @"play";
  654. }
  655. else {
  656. engineStatus = @"suspend";
  657. }
  658. }
  659. NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
  660. [valueDic setValue:engineStatus forKey:@"status"];
  661. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  662. [sendParm setValue:valueDic forKey:@"content"];
  663. [self postMessage:sendParm];
  664. }
  665. // 播放、暂停、进度、播放结束、跳转指定位置
  666. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudPlay"]) { // 播放
  667. /**
  668. api: 'cloudPlay',
  669. content: {
  670. // 当前曲目id
  671. songID: 0,
  672. // xml整体原始速度
  673. originalSpeed: 90,
  674. // 当前选择速度
  675. speed: 90,
  676. // 开始时间(ms)
  677. startTime: 0
  678. // 播放频率
  679. hertz:440
  680. }
  681. */
  682. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  683. self.currentSongId = [content ks_stringValueForKey:@"songID"];
  684. float speed = [content ks_floatValueForKey:@"speed"];
  685. float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
  686. double rate = speed / originalSpeed;
  687. self.currentSpeed = speed;
  688. float hertz = [content ks_floatValueForKey:@"hertz"];
  689. // 播放的hertz
  690. [self.playerEngine adjustPitchByHZ:hertz];
  691. [self.playerEngine setMusicPlayerSpeed:rate];
  692. Float64 startTime = [content ks_doubleValueForKey:@"startTime"];
  693. NSLog(@"------%@", [content ks_stringValueForKey:@"startTime"]);
  694. [self.playerEngine setProgressTime:(startTime/1000)];
  695. // 播放
  696. [self playAction];
  697. [self postMessage:parm];
  698. }
  699. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSuspend"]) { // 暂停
  700. /**
  701. api: 'cloudSuspend',
  702. content: {
  703. // 当前曲目id
  704. songID: 0,
  705. }
  706. */
  707. [self stopPlayAction];
  708. [self postMessage:parm];
  709. }
  710. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSetCurrentTime"]) { // // 跳转指定位置
  711. /**
  712. api: 'cloudSetCurrentTime',
  713. content: {
  714. // 当前曲目id
  715. songID: 0,
  716. // 指定位置时间(ms)
  717. currentTime: 0
  718. }
  719. */
  720. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  721. Float64 currentTime = [content ks_doubleValueForKey:@"currentTime"];
  722. if (self.playerEngine) {
  723. [self.playerEngine setProgressTime:(currentTime/1000)];
  724. }
  725. [self postMessage:parm];
  726. }
  727. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudChangeSpeed"]) { // 调速 范围(45-270整数)
  728. /**
  729. api: 'cloudChangeSpeed',
  730. content: {
  731. // 当前曲目id
  732. songID: 0,
  733. // 调整速度
  734. speed: 90
  735. // xml整体原始速度
  736. originalSpeed: 90,
  737. }
  738. */
  739. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  740. float speed = [content ks_floatValueForKey:@"speed"];
  741. float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
  742. double rate = speed / originalSpeed;
  743. self.currentSpeed = speed;
  744. [self.playerEngine setMusicPlayerSpeed:rate];
  745. // 回报信息
  746. [self postMessage:parm];
  747. }
  748. // 设置每个轨道音量
  749. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudVolume"]) {
  750. /**
  751. api: 'cloudVolume',
  752. content: {
  753. parts: [
  754. {
  755. name: '',
  756. volume: 90,//0-100
  757. }
  758. ]
  759. }
  760. */
  761. NSLog(@"-cloudVolume -----%@",parm);
  762. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  763. int instrumentId = [content ks_intValueForKey:@"activeMidiId"];
  764. float volume = [content ks_floatValueForKey:@"activeMidiVolume"];
  765. if (instrumentId < 0) {
  766. [self postMessage:parm];
  767. [self.playerEngine getAllTrackVolume];
  768. return;
  769. }
  770. [self.playerEngine volumeTrackVolumeWithInstrumentId:instrumentId volume:volume/100];
  771. [self postMessage:parm];
  772. [self.playerEngine getAllTrackVolume];
  773. }
  774. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudMetronome"]) { // 节拍器播放
  775. self.metronomeParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  776. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  777. NSInteger repeatCount = [content ks_integerValueForKey:@"repeat"];
  778. NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
  779. NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
  780. NSInteger supplement = [content ks_integerValueForKey:@"supplement"];
  781. NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
  782. self.beatType = [self getBeatTypeFromString:beatString];
  783. [self showBeatViewRepeatCount:repeatCount supplement:supplement];
  784. }
  785. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDestroy"]) { // 销毁播放器
  786. self.initEngineSuccess = NO;
  787. // 重置track num array
  788. self.isPlaying = NO;
  789. if (self.playerEngine) {
  790. [self.playerEngine cleanup];
  791. self.playerEngine = nil;
  792. }
  793. }
  794. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudLoading"]) { // loading
  795. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  796. BOOL showLoading = [content ks_boolValueForKey:@"show"];
  797. if (showLoading) {
  798. [self showCustomLoading];
  799. }
  800. else {
  801. [self removeCustomLoadingView];
  802. }
  803. }
  804. // 跟音
  805. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudToggleFollow"]) {
  806. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  807. NSString *status = [content ks_stringValueForKey:@"state"];
  808. if ([status isEqualToString:@"start"]) { // 开始
  809. [self startTuner];
  810. }
  811. else if ([status isEqualToString:@"end"]) { // 结束
  812. [self stopTuner];
  813. }
  814. }
  815. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudAccompanyMessage"]) { // 获取伴奏 废弃⚠️
  816. }
  817. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startTune"]) { // 延迟测试开始
  818. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  819. NSInteger count = [content ks_integerValueForKey:@"count"];
  820. if (count == 0) {
  821. [self.delayArray removeAllObjects];
  822. self.checkIndex = 0;
  823. }
  824. self.checkIndex += 1;
  825. [self configRecordManager];
  826. [self postMessage:parm];
  827. self.isCompareStart = NO;
  828. self.isSoundCheckStart = NO;
  829. self.isDelayCheckStart = YES;
  830. [self startRecordService];
  831. }
  832. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endTune"]) { // 延迟测试停止
  833. self.isDelayCheckStart = NO;
  834. [self stopRecordService];
  835. [self postMessage:parm];
  836. [self stopMp3Player];
  837. [self sendAdjustEndMessage]; // 发送结束消息
  838. }
  839. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"finishTune"]) { // 延迟测试结束
  840. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  841. NSInteger errorCount = 0;
  842. NSInteger totalDelay = 0;
  843. for (NSInteger index = 0 ; index < self.delayArray.count; index++) {
  844. NSInteger micDelay = [self.delayArray[index] integerValue];
  845. if (micDelay > 10 && micDelay < 100) {
  846. totalDelay += micDelay;
  847. }
  848. else {
  849. errorCount++;
  850. }
  851. }
  852. NSMutableDictionary *contentParm = [NSMutableDictionary dictionaryWithDictionary:content];
  853. if (errorCount > 1) { // 错误次数过多
  854. [contentParm setValue:@(NO) forKey:@"result"];
  855. }
  856. else {
  857. [contentParm setValue:@(YES) forKey:@"result"];
  858. NSInteger averageDelay = totalDelay / (self.delayArray.count - errorCount);
  859. UserDefaultSet([NSNumber numberWithDouble:averageDelay], @"micDelay");
  860. }
  861. NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
  862. [sendParm setValue:@"finishTune" forKey:@"api"];
  863. [sendParm setValue:contentParm forKey:@"content"];
  864. [self postMessage:sendParm];
  865. [self.delayArray removeAllObjects];
  866. self.checkIndex = 0;
  867. }
  868. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"getDeviceDelay"]) {
  869. NSInteger micDelay = [UserDefaultObjectForKey(@"micDelay") integerValue];
  870. NSMutableDictionary *content = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
  871. [content setValue:[NSNumber numberWithInteger:micDelay] forKey:@"value"];
  872. NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
  873. [sendParm setValue:@"getDeviceDelay" forKey:@"api"];
  874. [sendParm setValue:content forKey:@"content"];
  875. [self postMessage:sendParm];
  876. }
  877. else {
  878. [super handleScriptMessageSource:parm];
  879. }
  880. });
  881. }
  882. }
  883. - (void)musicPublishCallBack:(NSDictionary *)content {
  884. NSMutableDictionary *parm = [NSMutableDictionary dictionary];
  885. [parm setValue:@"hideComplexButton" forKey:@"api"];
  886. [parm setValue:@{} forKey:@"content"];
  887. [self postMessage:parm];
  888. }
  889. - (void)uploadVideoWithParm:(NSMutableDictionary *)contentParm sendParm:(NSMutableDictionary *)sendParm {
  890. MJWeakSelf;
  891. [self.videoRecordManager exportRecordVideoUploadSuccess:^(NSString * _Nonnull videoFileUrl) {
  892. } failure:^(NSString * _Nonnull desc) {
  893. }];
  894. // [self.videoRecordManager uploadRecordVideoSuccess:^(NSString * _Nonnull videoUrl) {
  895. // [contentParm setValue:@"success" forKey:@"type"];
  896. // [contentParm setValue:videoUrl forKey:@"filePath"];
  897. // [contentParm setValue:@"上传成功" forKey:@"message"];
  898. // [sendParm setValue:contentParm forKey:@"content"];
  899. //
  900. // [weakSelf postMessage:sendParm];
  901. // } failure:^(NSString * _Nonnull desc) {
  902. // [contentParm setValue:@"error" forKey:@"type"];
  903. // [contentParm setValue:desc forKey:@"message"];
  904. // [sendParm setValue:contentParm forKey:@"content"];
  905. // [weakSelf postMessage:sendParm];
  906. // }];
  907. }
  908. - (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
  909. [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:self.view cancel:^{
  910. } confirm:^{
  911. [self openSettingView];
  912. }];
  913. }
  914. - (void)openSettingView {
  915. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
  916. }
  917. - (void)downloadUrl:(NSString *)url success:(void(^)(void))success faliure:(void(^)(void))faliure {
  918. [KSNetworkingManager downloadFileRequestWithFileUrl:url progress:^(int64_t bytesRead, int64_t totalBytes) {
  919. } success:^(NSURL * _Nonnull fileUrl) {
  920. if ([self saveAccompanyFileWithUrl:fileUrl accompanyUrl:url]) {
  921. if (success) {
  922. success();
  923. }
  924. }
  925. } faliure:^(NSError * _Nonnull error) {
  926. if (faliure) {
  927. faliure();
  928. }
  929. }];
  930. }
  931. - (void)configVideoRecord:(NSURL *)path {
  932. self.videoRecordManager.bgAudioUrl = path;
  933. self.bgAudioUrl = path;
  934. }
  935. - (void)showBeatViewRepeatCount:(NSInteger)repeatCount supplement:(NSInteger)supplement {
  936. KSCloudBeatView *beatView = [KSCloudBeatView shareInstanceWithBeatType:self.beatType speed:self.currentSpeed repeatCount:repeatCount supplement:supplement];
  937. MJWeakSelf;
  938. [beatView startPlayWithEndCallback:^(BOOL isCancle) {
  939. if (isCancle) { // 取消
  940. [weakSelf sendEndMetronomeMessage:YES];
  941. }
  942. else { // 播放完成
  943. [weakSelf sendEndMetronomeMessage:NO];
  944. }
  945. }];
  946. [self.view addSubview:beatView];
  947. }
  948. - (void)sendEndMetronomeMessage:(BOOL)isCancel {
  949. NSString *status = isCancel ? @"cancel" : @"finish";
  950. NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[self.metronomeParm ks_dictionaryValueForKey:@"content"]];
  951. [valueDic setValue:status forKey:@"status"];
  952. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:self.metronomeParm];
  953. [sendParm setValue:valueDic forKey:@"content"];
  954. [self postMessage:sendParm];
  955. }
  956. - (void)downloadMidiFile:(NSString *)midiUrl success:(void(^)(void))success faliure:(void(^)(void))faliure {
  957. [KSNetworkingManager downloadFileRequestWithFileUrl:midiUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
  958. } success:^(NSURL * _Nonnull fileUrl) {
  959. if ([self saveMidiFileWithUrl:fileUrl midiUrl:midiUrl]) {
  960. if (success) {
  961. success();
  962. }
  963. }
  964. } faliure:^(NSError * _Nonnull error) {
  965. if (faliure) {
  966. faliure();
  967. }
  968. }];
  969. }
  970. - (BOOL)saveMidiFileWithUrl:(NSURL *)fileUrl midiUrl:(NSString *)midiUrl {
  971. NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
  972. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  973. NSString *filePath= [cachePath stringByAppendingPathComponent:@"MidiSong"];
  974. // 先创建子目录
  975. NSFileManager *fileManager = [NSFileManager defaultManager];
  976. if (![fileManager fileExistsAtPath:filePath]) {
  977. [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
  978. }else{
  979. NSLog(@"已创建文件夹");
  980. }
  981. NSString *fileName = [midiUrl getUrlFileName];
  982. NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
  983. BOOL success = [sourceData writeToFile:tempPath atomically:NO];
  984. return success;
  985. }
  986. - (NSString *)getFilePathWithName:(NSString *)fileName {
  987. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  988. NSString *filePath= [cachePath stringByAppendingPathComponent:@"MidiSong"];
  989. return [filePath stringByAppendingPathComponent:fileName];;
  990. }
  991. - (BOOL)checkSongHasSaveWithSongUrl:(NSString *)songUrl {
  992. BOOL hasSaveFile = NO;
  993. NSString *fileName = [songUrl getUrlFileName];
  994. NSString *filePath = [self getFilePathWithName:fileName];
  995. NSFileManager *fileManager = [NSFileManager defaultManager];
  996. if ([fileManager fileExistsAtPath:filePath]) {
  997. hasSaveFile = YES;
  998. }
  999. return hasSaveFile;
  1000. }
  1001. - (void)sendMessageToSocket:(NSDictionary *)parm {
  1002. NSString *messageHeader = @"proxyMessage";
  1003. NSString *sendMessage = [self configDataCommond:messageHeader body:[parm ks_dictionaryValueForKey:@"content"]];
  1004. [self sendDataToSocketService:sendMessage];
  1005. }
  1006. - (void)configAudioDeviceType:(NSDictionary *)parm {
  1007. AUDIODEVICE_TYPE type = [KSAQRecordManager queryAudioOutputDeviceType];
  1008. NSString *valueStr = @"";
  1009. BOOL checkIsWired = NO;
  1010. switch (type) {
  1011. case AUDIODEVICE_TYPE_HEADPHONE:
  1012. {
  1013. valueStr = @"有线耳机";
  1014. checkIsWired = YES;
  1015. }
  1016. break;
  1017. case AUDIODEVICE_TYPE_BLUETOOTH:
  1018. {
  1019. valueStr = @"蓝牙耳机";
  1020. checkIsWired = YES;
  1021. }
  1022. break;
  1023. case AUDIODEVICE_TYPE_NONE:
  1024. {
  1025. valueStr = @"";
  1026. checkIsWired = NO;
  1027. }
  1028. break;
  1029. default:
  1030. break;
  1031. }
  1032. NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
  1033. [valueDic setValue:valueStr forKey:@"type"];
  1034. [valueDic setValue:[NSNumber numberWithBool:checkIsWired] forKey:@"checkIsWired"];
  1035. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  1036. [sendParm setValue:valueDic forKey:@"content"];
  1037. [self postMessage:sendParm];
  1038. }
  1039. - (void)startRecordService {
  1040. if (self.AQManager.isRunning) {
  1041. [self.AQManager stopRecord];
  1042. }
  1043. [self.AQManager startRecord];
  1044. }
  1045. - (void)stopRecordService {
  1046. if (self.AQManager.isRunning) {
  1047. [self.AQManager stopRecord];
  1048. }
  1049. }
  1050. - (void)puaseRecordService {
  1051. if (self.AQManager.isRunning) {
  1052. [self.AQManager pauserRecord];
  1053. }
  1054. }
  1055. - (void)resumeRecordService {
  1056. [self.AQManager resumeRecord];
  1057. }
  1058. - (void)sendEndMessage {
  1059. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1060. // 上传停止的信息 发送给服务端
  1061. NSString *endMessage = @"recordEnd";
  1062. NSString *endData = [self configDataCommond:endMessage body:nil type:@"SOUND_COMPARE"];
  1063. [self sendDataToSocketService:endData];
  1064. self.isCompareStart = NO;
  1065. NSLog(@"---- send end message");
  1066. });
  1067. }
  1068. - (void)sendAdjustEndMessage {
  1069. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1070. // 上传停止的信息 发送给服务端
  1071. NSString *endMessage = @"recordEnd";
  1072. NSString *endData = [self configDataCommond:endMessage body:nil type:@"DELAY_CHECK"];
  1073. [self sendDataToSocketService:endData];
  1074. self.isDelayCheckStart = NO;
  1075. NSLog(@"---- send adjust end message");
  1076. });
  1077. }
  1078. - (void)sendOffsetTimeToService {
  1079. // 上传停止的信息 发送给服务端
  1080. NSString *offsetMessage = @"audioPlayStart";
  1081. NSTimeInterval micDelay = [UserDefault(@"micDelay") doubleValue];
  1082. NSDictionary *dic = @{@"offsetTime" : [NSNumber numberWithInteger:self.offsetTime], @"micDelay": [NSNumber numberWithInteger:micDelay]};
  1083. NSString *endData = [self configDataCommond:offsetMessage body:dic type:@"SOUND_COMPARE"];
  1084. NSLog(@"------ %@", endData);
  1085. [self sendDataToSocketService:endData];
  1086. self.isCompareStart = NO;
  1087. }
  1088. #pragma mark-------- KSAQRecordManagerDelegate
  1089. - (void)recordInterruption {
  1090. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  1091. @"content" : @{@"reson":@"录制错误,请重试"}
  1092. };
  1093. [self postMessage:postParm];
  1094. }
  1095. - (void)audioRouteChange:(AUDIODEVICE_TYPE)type {
  1096. NSString *valueStr = @"";
  1097. BOOL checkIsWired = NO;
  1098. switch (type) {
  1099. case AUDIODEVICE_TYPE_HEADPHONE:
  1100. {
  1101. valueStr = @"有线耳机";
  1102. checkIsWired = YES;
  1103. }
  1104. break;
  1105. case AUDIODEVICE_TYPE_BLUETOOTH:
  1106. {
  1107. valueStr = @"蓝牙耳机";
  1108. checkIsWired = YES;
  1109. }
  1110. break;
  1111. case AUDIODEVICE_TYPE_NONE:
  1112. {
  1113. valueStr = @"";
  1114. checkIsWired = NO;
  1115. }
  1116. break;
  1117. default:
  1118. break;
  1119. }
  1120. NSDictionary *postParm = @{@"api" : @"listenerWiredStatus",
  1121. @"content" : @{@"type":valueStr,
  1122. @"checkIsWired":[NSNumber numberWithBool:checkIsWired]
  1123. }
  1124. };
  1125. [self postMessage:postParm];
  1126. }
  1127. #pragma mark ------- 评测app播放
  1128. - (void)recordDidStart:(NSTimeInterval)time { // ms
  1129. self.recordStartTime = time;
  1130. if (self.isDelayCheckStart) {
  1131. NSLog(@"---- delay - record did start %f", time);
  1132. // 播放音频
  1133. // 播放校音音频
  1134. dispatch_main_sync_safe(^{
  1135. if (self.checkIndex % 2 == 0) {
  1136. [self.dongPlayer startPlay];
  1137. }
  1138. else {
  1139. [self.dingPlayer startPlay];
  1140. }
  1141. });
  1142. }
  1143. else if (self.isCompareStart) {
  1144. NSLog(@"---- compare - record did start %f", time);
  1145. // 播放伴奏
  1146. dispatch_main_sync_safe(^{
  1147. self.musicPlayer.isMute = self.muteAccompany;
  1148. [self.musicPlayer startPlay];
  1149. [self.musicPlayer seekToTimePlay:self.musicStartTime];
  1150. });
  1151. }
  1152. }
  1153. - (void)audioRecord:(KSAQRecordManager *)audioRecord didRecordAudioData:(void *)data length:(UInt32)length {
  1154. if (self.socketManager.socketReadyState != SR_OPEN) {
  1155. return;
  1156. }
  1157. NSData *pushData = [[NSData alloc] initWithBytes:data length:length];
  1158. if (self.isCompareStart) { // 发送评测开始消息
  1159. dispatch_async(dispatch_get_main_queue(), ^{
  1160. NSDate *date = [NSDate date];
  1161. NSTimeInterval inteveral = [date timeIntervalSince1970];
  1162. double beginTime = inteveral - audioRecord.sampleTime;
  1163. NSDictionary *parm = @{
  1164. @"api" : @"recordStartTime",
  1165. @"content" : @{@"inteveral" : [NSNumber numberWithDouble:beginTime]}
  1166. };
  1167. [self postMessage:parm];
  1168. });
  1169. NSLog(@"--------- send start message");
  1170. _isCompareStart = NO;
  1171. NSString *startMessage = @"recordStart";
  1172. NSString *startString = [self configDataCommond:startMessage body:nil type:@"SOUND_COMPARE"];
  1173. [self sendDataToSocketService:startString];
  1174. }
  1175. else if (self.isSoundCheckStart) { // 校音开始
  1176. NSLog(@"--------- send check start message");
  1177. _isSoundCheckStart = NO;
  1178. NSString *checkStartMessage = @"start";
  1179. NSString *startString = [self configDataCommond:checkStartMessage body:nil type:@"PITCH_DETECTION"];
  1180. [self sendDataToSocketService:startString];
  1181. }
  1182. else if (self.isDelayCheckStart) {
  1183. NSLog(@"--------- send delay check start message");
  1184. _isDelayCheckStart = NO;
  1185. NSString *checkStartMessage = @"recordStart";
  1186. NSInteger frequence = 0.0f;
  1187. if (self.checkIndex % 2 == 0) {
  1188. frequence = self.secondFrequence;
  1189. }
  1190. else {
  1191. frequence = self.firstFrequence;
  1192. }
  1193. NSDictionary *parm = @{@"HZ" : @(frequence)};
  1194. NSString *startString = [self configDataCommond:checkStartMessage body:parm type:@"DELAY_CHECK"];
  1195. [self sendDataToSocketService:startString];
  1196. }
  1197. // NSLog(@"--------- send audio data length %d", length);
  1198. [self sendDataToSocketService:pushData];
  1199. }
  1200. - (NSString *)configDataCommond:(NSString *)commond body:(id)bodyMessage type:(NSString *)dataType {
  1201. NSMutableDictionary *parm = [NSMutableDictionary dictionary];
  1202. if (bodyMessage) {
  1203. [parm setValue:bodyMessage forKey:@"body"];
  1204. }
  1205. NSMutableDictionary *headerParm = [NSMutableDictionary dictionary];
  1206. if ([NSString isEmptyString:commond]) {
  1207. [headerParm setValue:@"" forKey:@"commond"];
  1208. }
  1209. else {
  1210. [headerParm setValue:commond forKey:@"commond"];
  1211. }
  1212. if (![NSString isEmptyString:dataType]) {
  1213. [headerParm setValue:dataType forKey:@"type"];
  1214. }
  1215. [headerParm setValue:@(200) forKey:@"status"];
  1216. [parm setValue:headerParm forKey:@"header"];
  1217. return [parm mj_JSONString];
  1218. }
  1219. - (NSString *)configDataCommond:(NSString *)commond body:(id)bodyMessage {
  1220. NSMutableDictionary *parm = [NSMutableDictionary dictionary];
  1221. if (bodyMessage) {
  1222. [parm setValue:bodyMessage forKey:@"body"];
  1223. }
  1224. if ([NSString isEmptyString:commond]) {
  1225. [parm setValue:@{@"commond":@""} forKey:@"header"];
  1226. }
  1227. else {
  1228. [parm setValue:@{@"commond":commond} forKey:@"header"];
  1229. }
  1230. return [parm mj_JSONString];
  1231. }
  1232. - (KSWebSocketManager *)socketManager {
  1233. if (!_socketManager) {
  1234. _socketManager = [[KSWebSocketManager alloc] initWithUrl:SOCKET_URL userId:UserDefault(UIDKey) tokenType:UserDefault(TokenKey) token:UserDefault(TokenKey)];
  1235. }
  1236. return _socketManager;
  1237. }
  1238. #pragma mark --- lazying
  1239. - (UIView *)viewContainer {
  1240. if (!_viewContainer) {
  1241. _viewContainer = [[UIView alloc] init];
  1242. }
  1243. return _viewContainer;
  1244. }
  1245. - (KSVideoRecordManager *)videoRecordManager {
  1246. if (!_videoRecordManager) {
  1247. MJWeakSelf;
  1248. _videoRecordManager = [[KSVideoRecordManager alloc] initSessionRecordCallback:^(BOOL isSuccess, NSString * _Nullable message) {
  1249. if (isSuccess) {
  1250. [weakSelf showSuccessMessage:message];
  1251. }
  1252. else {
  1253. if (![NSString isEmptyString:message]) {
  1254. [LOADING_MANAGER MBShowAUTOHidingInWindow:message];
  1255. }
  1256. }
  1257. }];
  1258. }
  1259. return _videoRecordManager;
  1260. }
  1261. - (void)showSuccessMessage:(NSString *)message {
  1262. if (![NSString isEmptyString:message]) {
  1263. [LOADING_MANAGER MBShowAUTOHidingInWindow:message];
  1264. }
  1265. // 成功
  1266. if (self.endRecordParm) {
  1267. [self postMessage:self.endRecordParm];
  1268. self.endRecordParm = nil;
  1269. }
  1270. }
  1271. - (MetronomeType)getBeatTypeFromString:(NSString *)typeString {
  1272. if ([typeString isEqualToString:@"1/4"]) {
  1273. return MetronomeType1V4;
  1274. }
  1275. else if ([typeString isEqualToString:@"2/4"]) {
  1276. return MetronomeType2V4;
  1277. }
  1278. else if ([typeString isEqualToString:@"3/4"]) {
  1279. return MetronomeType3V4;
  1280. }
  1281. else if ([typeString isEqualToString:@"4/4"]) {
  1282. return MetronomeType4V4;
  1283. }
  1284. else if ([typeString isEqualToString:@"3/8"]) {
  1285. return MetronomeType3V8;
  1286. }
  1287. else if ([typeString isEqualToString:@"6/8"]) {
  1288. return MetronomeType6V8;
  1289. }
  1290. else {
  1291. return MetronomeType4V4;
  1292. }
  1293. }
  1294. #pragma mark ------- midi 播放相关
  1295. - (void)configPlayerEngineWithSong:(NSString *)songPath reportTime:(NSInteger)reportTime {
  1296. self.initEngineSuccess = NO;
  1297. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  1298. self.playerEngine = [[MidiPlayerEngine alloc] init];
  1299. self.playerEngine.reportTime = reportTime;
  1300. self.playerEngine.delegate = self;
  1301. [self.playerEngine configSoundFilePath:[[NSBundle mainBundle]
  1302. pathForResource:@"synthgms" ofType:@"sf2"]];
  1303. [self.playerEngine loadMIDIFileWithString:songPath];
  1304. });
  1305. }
  1306. - (void)configAudioSession {
  1307. self.audioSessionManager = [[KSAudioSessionManager alloc] init];
  1308. self.audioSessionManager.delegate = self;
  1309. [self.audioSessionManager configAudioSession:AUDIOCONFIG_PLAYANDRECORD];
  1310. }
  1311. #pragma mark ---- PlayerEngineDelegate
  1312. /** 进度更新
  1313. api: 'cloudTimeUpdae',
  1314. content: {
  1315. // 当前曲目id
  1316. songID: 0,
  1317. // 当前位置时间(ms)
  1318. currentTime: 0
  1319. }
  1320. */
  1321. /** 播放结束事件
  1322. api: 'cloudplayed',
  1323. content: {
  1324. // 当前曲目id
  1325. songID: 0,
  1326. }
  1327. */
  1328. - (void)ProgressUpdated:(float)progress currentTime:(MusicTimeStamp)currentTime {
  1329. if (self.isPlaying == NO) {
  1330. return;
  1331. }
  1332. // 回调
  1333. NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
  1334. [sendParm setValue:@"cloudTimeUpdae" forKey:@"api"];
  1335. NSMutableDictionary *content = [NSMutableDictionary dictionary];
  1336. [content setValue:self.currentSongId forKey:@"songID"];
  1337. [content setValue:@(currentTime*1000) forKey:@"currentTime"];
  1338. [sendParm setValue:content forKey:@"content"];
  1339. [self postMessage:sendParm];
  1340. }
  1341. - (void)playEnd {
  1342. [self stopPlayAction];
  1343. NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
  1344. [sendParm setValue:@"cloudplayed" forKey:@"api"];
  1345. NSMutableDictionary *content = [NSMutableDictionary dictionary];
  1346. [content setValue:self.currentSongId forKey:@"songID"];
  1347. [sendParm setValue:content forKey:@"content"];
  1348. [self postMessage:sendParm];
  1349. }
  1350. - (void)initPlayerEngineSuccess:(float)totalTime {
  1351. self.initEngineSuccess = YES;
  1352. if (self.configEngineParm) {
  1353. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:self.configEngineParm];
  1354. NSMutableDictionary *content = [NSMutableDictionary dictionaryWithDictionary:[sendParm ks_dictionaryValueForKey:@"content"]];
  1355. [content setValue:@(totalTime*1000) forKey:@"midiDuration"];
  1356. [sendParm setValue:content forKey:@"content"];
  1357. [self postMessage:sendParm];
  1358. self.configEngineParm = nil;
  1359. }
  1360. }
  1361. // 总时长
  1362. - (void)GetMusicTotalTime:(float)time {
  1363. }
  1364. #pragma mark ----- 播放控制
  1365. - (void)playAction {
  1366. if (self.playerEngine) {
  1367. self.isPlaying = YES;
  1368. [self.playerEngine playMIDIFile];
  1369. }
  1370. }
  1371. - (void)stopPlayAction {
  1372. if (self.playerEngine) {
  1373. self.isPlaying = NO;
  1374. if ([self.playerEngine isPlayingFile]) {
  1375. [self.playerEngine stopPlayingMIDIFile];
  1376. }
  1377. }
  1378. }
  1379. #pragma mark ----- 小酷AI loading
  1380. - (AccompanyLoadingView *)loadingView {
  1381. if (!_loadingView) {
  1382. _loadingView = [AccompanyLoadingView shareInstance];
  1383. MJWeakSelf;
  1384. [_loadingView loadingCallback:^{
  1385. [weakSelf backAction];
  1386. }];
  1387. }
  1388. return _loadingView;
  1389. }
  1390. - (void)showCustomLoading {
  1391. if ([self.view.subviews containsObject:self.loadingView]) {
  1392. return;
  1393. }
  1394. [self.view addSubview:self.loadingView];
  1395. [self.loadingView mas_makeConstraints:^(MASConstraintMaker *make) {
  1396. make.left.top.right.bottom.mas_equalTo(self.view);
  1397. }];
  1398. [self.view bringSubviewToFront:self.loadingView];
  1399. [self.loadingView showLoading];
  1400. }
  1401. - (void)removeCustomLoadingView {
  1402. [self.loadingView stopLoading];
  1403. }
  1404. #pragma mark ----- 跟音模块
  1405. - (void)startTuner {
  1406. if (self.isTunerRuning == NO) {
  1407. self.isTunerRuning = YES;
  1408. [self.tuner start];
  1409. }
  1410. }
  1411. - (void)stopTuner {
  1412. if (self.isTunerRuning) {
  1413. self.isTunerRuning = NO;
  1414. [self.tuner stop];
  1415. }
  1416. }
  1417. - (Tuner *)tuner {
  1418. if (!_tuner) {
  1419. _tuner = [[Tuner alloc] initWithThreshold:0 smoothing:0.25];
  1420. _tuner.delegate = self;
  1421. }
  1422. return _tuner;
  1423. }
  1424. - (void)tunerDidUpdate:(Tuner *)tuner output:(TunerOutput *)output {
  1425. if (output.amplitude < 0.01) {
  1426. }
  1427. else {
  1428. // 回调频率
  1429. NSDictionary *parm = @{
  1430. @"api" : @"cloudFollowTime",
  1431. @"content" : @{@"frequency" : [NSNumber numberWithDouble:output.frequency]}
  1432. };
  1433. [self postMessage:parm];
  1434. }
  1435. NSLog(@"-------- %@%zd --- distance :%f frequence : %f" , output.pitch, output.octave, output.distance, output.frequency);
  1436. }
  1437. #pragma mark ---- 保存伴奏
  1438. - (BOOL)saveAccompanyFileWithUrl:(NSURL *)fileUrl accompanyUrl:(NSString *)accompanyUrl {
  1439. NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
  1440. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  1441. NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
  1442. // 先创建子目录
  1443. NSFileManager *fileManager = [NSFileManager defaultManager];
  1444. if (![fileManager fileExistsAtPath:filePath]) {
  1445. [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
  1446. }else{
  1447. NSLog(@"已创建文件夹");
  1448. }
  1449. NSString *fileName = [accompanyUrl getUrlFileName];
  1450. NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
  1451. BOOL success = [sourceData writeToFile:tempPath atomically:NO];
  1452. return success;
  1453. }
  1454. - (NSString *)getAccompanyFilePathWithName:(NSString *)fileName {
  1455. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  1456. NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
  1457. return [filePath stringByAppendingPathComponent:fileName];;
  1458. }
  1459. - (BOOL)checkSongHasSaveAccompanyWithSongUrl:(NSString *)songUrl {
  1460. BOOL hasSaveFile = NO;
  1461. NSString *fileName = [songUrl getUrlFileName];
  1462. NSString *filePath = [self getAccompanyFilePathWithName:fileName];
  1463. NSFileManager *fileManager = [NSFileManager defaultManager];
  1464. if ([fileManager fileExistsAtPath:filePath]) {
  1465. hasSaveFile = YES;
  1466. }
  1467. return hasSaveFile;
  1468. }
  1469. #pragma mark ---- player
  1470. - (void)configPlayerDelegate {
  1471. self.dingPlayer.delegate = self;
  1472. self.dongPlayer.delegate = self;
  1473. self.musicPlayer.delegate = self;
  1474. }
  1475. - (kSNewPlayer *)dingPlayer {
  1476. if (!_dingPlayer) {
  1477. _dingPlayer = [[kSNewPlayer alloc] init];
  1478. }
  1479. return _dingPlayer;
  1480. }
  1481. - (kSNewPlayer *)dongPlayer {
  1482. if (!_dongPlayer) {
  1483. _dongPlayer = [[kSNewPlayer alloc] init];
  1484. }
  1485. return _dongPlayer;
  1486. }
  1487. - (kSNewPlayer *)musicPlayer {
  1488. if (!_musicPlayer) {
  1489. _musicPlayer = [[kSNewPlayer alloc] init];
  1490. }
  1491. return _musicPlayer;
  1492. }
  1493. #pragma mark ---- new player delegate
  1494. - (void)getSongCurrentTime:(NSInteger)currentTime andTotalTime:(NSInteger)totalTime andProgress:(CGFloat)progress currentInterval:(NSTimeInterval)currentInterval playTime:(NSTimeInterval)playTime inPlayer:(kSNewPlayer *)player {
  1495. if (player == self.dingPlayer || player == self.dongPlayer) {
  1496. if (playTime >= 300 && playTime < 310 && self.recordStartTime > 0) {
  1497. NSLog(@" --- check player start play time %f", currentInterval*1000 - playTime);
  1498. self.playerStartTime = currentInterval*1000 - playTime;
  1499. self.offsetTime = self.playerStartTime - self.recordStartTime;
  1500. NSLog(@"--------- check player offset time -- %zd", self.offsetTime);
  1501. }
  1502. }
  1503. if (playTime >= self.musicStartTime + 300 && playTime < self.musicStartTime + 310 && player == self.musicPlayer) {
  1504. if (self.recordStartTime > 0) {
  1505. NSInteger newPlayTime = playTime - self.musicStartTime; // 选段
  1506. NSLog(@" --- music player start play time %f", currentInterval*1000 - newPlayTime);
  1507. self.playerStartTime = currentInterval*1000 - newPlayTime;
  1508. self.offsetTime = self.playerStartTime - self.recordStartTime;
  1509. NSLog(@"--------- music play offset time -- %zd", self.offsetTime);
  1510. [self sendOffsetTimeToService];
  1511. }
  1512. NSLog(@"------- record start time %f", self.recordStartTime);
  1513. }
  1514. // 回调进度
  1515. if (player == self.musicPlayer) {
  1516. // NSLog(@"------ music play progress - %f", progress);
  1517. // 回调进度
  1518. NSDictionary *parm = @{
  1519. @"api" : @"playProgress",
  1520. @"content" : @{@"currentTime" : [NSNumber numberWithInteger:currentTime],
  1521. @"totalDuration" : [NSNumber numberWithInteger:totalTime],
  1522. }
  1523. };
  1524. // NSLog(@" -----music play progress %@---- ", parm);
  1525. [self postMessage:parm];
  1526. }
  1527. }
  1528. - (void)sendPlayerReadyMsg {
  1529. if (self.playerParm) {
  1530. [self postMessage:self.playerParm];
  1531. }
  1532. }
  1533. - (void)playerIsReadyPlay:(kSNewPlayer *)player {
  1534. if (player == self.musicPlayer) {
  1535. self.musicPlayerReady = YES;
  1536. }
  1537. else if (player == self.dingPlayer) {
  1538. self.dingPlayerReady = YES;
  1539. }
  1540. else if (player == self.dongPlayer) {
  1541. self.dongPlayerReady = YES;
  1542. }
  1543. // 如果都准备好
  1544. if (self.musicPlayerReady && self.dingPlayerReady && self.dongPlayerReady) {
  1545. [self sendPlayerReadyMsg];
  1546. }
  1547. }
  1548. - (void)playerDidError:(kSNewPlayer *)player {
  1549. [self stopRecordService];
  1550. [self stopMp3Player]; // 停止播放
  1551. // 播放出现问题
  1552. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  1553. @"content" : @{@"reson":@"播放已停止"}
  1554. };
  1555. [self postMessage:postParm];
  1556. }
  1557. - (NSMutableArray *)delayArray {
  1558. if (!_delayArray) {
  1559. _delayArray = [NSMutableArray array];
  1560. }
  1561. return _delayArray;
  1562. }
  1563. /*
  1564. #pragma mark - Navigation
  1565. // In a storyboard-based application, you will often want to do a little preparation before navigation
  1566. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  1567. // Get the new view controller using [segue destinationViewController].
  1568. // Pass the selected object to the new view controller.
  1569. }
  1570. */
  1571. @end