KSAccompanyWebViewController.m 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. //
  2. // KSAccompanyWebViewController.m
  3. // KulexiuForTeacher
  4. //
  5. // Created by Kyle on 2022/3/20.
  6. //
  7. #import "KSAccompanyWebViewController.h"
  8. #import "RecordCheckManager.h"
  9. #import "KSAQRecordManager.h"
  10. #import "KSWebSocketManager.h"
  11. #import "KSVideoRecordManager.h"
  12. #import "KSWebNavView.h"
  13. #import "KSAudioSessionManager.h"
  14. #import "KSCloudBeatView.h"
  15. #import "MidiPlayerEngine.h"
  16. #import "AccompanyLoadingView.h"
  17. #define KSMidiSongFileKey (@"KSDownloadMidiSong")
  18. #import "KulexiuForTeacher-swift.h"
  19. @interface KSAccompanyWebViewController ()<KSAQRecordManagerDelegate,KSAudioSessionManagerDelegate,PlayerEngineDelegate,TunerDelegate>
  20. @property (nonatomic, strong) KSAudioSessionManager *audioSessionManager;
  21. @property (nonatomic, assign) MetronomeType beatType;
  22. @property (nonatomic, strong) MidiPlayerEngine *playerEngine; // player
  23. @property (nonatomic, assign) BOOL initEngineSuccess; // 加载引擎是否成功
  24. @property (nonatomic, assign) float songOriginalSpeed;
  25. @property (nonatomic, assign) float currentSpeed; // 当前播放的速度
  26. @property (nonatomic, assign) BOOL isPlaying; // 是否正在播放的状态
  27. @property (nonatomic, strong) NSString *currentSongId; // song id
  28. @property (nonatomic, strong) NSDictionary *configEngineParm; // 初始化mid播放引擎的参数
  29. @property (nonatomic, strong) NSMutableDictionary *metronomeParm; // 节拍器播放的参数
  30. // 是否发送了musicXML信息
  31. @property (nonatomic, assign) BOOL hasSendStartMessage;
  32. @property (nonatomic, strong) KSAQRecordManager *AQManager;
  33. @property (nonatomic, strong) KSWebSocketManager *socketManager;
  34. @property (nonatomic, strong) NSMutableDictionary *evaluatParm;
  35. // 录制的message
  36. @property (nonatomic, strong) NSMutableDictionary *recordParm;
  37. // 评测开始时发送recordStart消息的标识
  38. @property (nonatomic, assign) BOOL isCompareStart;
  39. // 校音开始时发送 checkStart消息标识
  40. @property (nonatomic, assign) BOOL isSoundCheckStart;
  41. // 自定义UI控件容器
  42. @property (nonatomic, strong) UIView *viewContainer;
  43. @property (nonatomic, strong) KSVideoRecordManager *videoRecordManager;
  44. @property (nonatomic, strong) NSDictionary *endRecordParm;
  45. @property (nonatomic, assign) BOOL isCameraOpen;
  46. @property (nonatomic, strong) AccompanyLoadingView *loadingView;
  47. @property (nonatomic, assign) BOOL isTunerRuning;
  48. @property (nonatomic, strong) Tuner *tuner;
  49. @end
  50. @implementation KSAccompanyWebViewController
  51. - (void)handerAudioInterruption {
  52. if (_playerEngine) {
  53. [self stopPlayAction];
  54. }
  55. }
  56. - (void)resumeAudioSession {
  57. // 恢复
  58. if (_playerEngine) {
  59. [self.playerEngine resumeAUGraph];
  60. }
  61. }
  62. // 打断处理
  63. - (void)audioInterruption {
  64. if (_videoRecordManager) {
  65. [self.videoRecordManager resetSession];
  66. }
  67. [self handerAudioInterruption];
  68. }
  69. - (void)ignorRecordVideo {
  70. if (_videoRecordManager) {
  71. self.videoRecordManager.skipSaveRecord = YES;
  72. }
  73. }
  74. - (void)stopSession {
  75. if (_videoRecordManager) {
  76. [self.videoRecordManager stopSession];
  77. }
  78. }
  79. - (void)viewDidLoad {
  80. [super viewDidLoad];
  81. // Do any additional setup after loading the view.
  82. [self configAudioSession];
  83. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterBackground) name:@"appEnterBackground" object:nil];
  84. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterForeground) name:@"appBecomeActive" object:nil];
  85. }
  86. - (void)connectSocketService {
  87. MJWeakSelf;
  88. self.socketManager.didReceiveMessage = ^(id _Nonnull message) {
  89. dispatch_async(dispatch_get_main_queue(), ^{
  90. // api
  91. NSMutableDictionary *sendMessage = [NSMutableDictionary dictionary];
  92. [sendMessage setValue:@"sendResult" forKey:@"api"];
  93. [sendMessage setValue:message forKey:@"content"];
  94. // 服务返回数据传给H5
  95. [weakSelf postMessage:sendMessage];
  96. });
  97. };
  98. self.socketManager.connectionStatus = ^(BOOL isSuccess) {
  99. dispatch_async(dispatch_get_main_queue(), ^{
  100. if (!isSuccess) {
  101. NSLog(@"-----连接失败");
  102. if (weakSelf.hasSendStartMessage) {
  103. if (weakSelf.AQManager.isRunning) {
  104. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  105. @"content" : @{@"reson":@"服务异常,请重试"}
  106. };
  107. [weakSelf postMessage:postParm];
  108. [weakSelf stopRecordService];
  109. weakSelf.isCompareStart = NO;
  110. weakSelf.isSoundCheckStart = NO;
  111. }
  112. }
  113. else if (weakSelf.evaluatParm) {
  114. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:weakSelf.evaluatParm];
  115. NSLog(@"%@",sendParm);
  116. NSDictionary *valueDic = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
  117. [valueDic setValue:@"服务异常,请重试" forKey:@"reson"];
  118. NSLog(@"%@",valueDic);
  119. [sendParm setValue:valueDic forKey:@"content"];
  120. [weakSelf postMessage:sendParm];
  121. weakSelf.hasSendStartMessage = YES;
  122. weakSelf.isCompareStart = NO;
  123. weakSelf.isSoundCheckStart = NO;
  124. }
  125. else { // 其他断开
  126. if (weakSelf.AQManager.isRunning) {
  127. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  128. @"content" : @{@"reson":@"服务异常,请重试"}
  129. };
  130. [weakSelf postMessage:postParm];
  131. [weakSelf stopRecordService];
  132. weakSelf.isCompareStart = NO;
  133. weakSelf.isSoundCheckStart = NO;
  134. }
  135. }
  136. }
  137. else {
  138. NSLog(@"-----连接成功");
  139. if (weakSelf.hasSendStartMessage == NO && weakSelf.evaluatParm) {
  140. NSDictionary *content = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
  141. NSString *sendData = [weakSelf configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
  142. [weakSelf sendDataToSocketService:sendData];
  143. [weakSelf postMessage:weakSelf.evaluatParm];
  144. weakSelf.hasSendStartMessage = YES;
  145. NSLog(@"---- send musicXML message");
  146. }
  147. }
  148. });
  149. };
  150. [self.socketManager SRWebSocketOpen];
  151. }
  152. - (void)appEnterBackground {
  153. NSDictionary *parm = @{@"api":@"suspendPlay"};
  154. [self postMessage:parm];
  155. [self handerAudioInterruption];
  156. }
  157. - (void)appEnterForeground {
  158. if (self.isCameraOpen) {
  159. if ([self.videoRecordManager getSessionStatusisActive] == NO) {
  160. [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
  161. }
  162. }
  163. }
  164. - (void)initWebView {
  165. [self.scrollView removeFromSuperview];
  166. [self.view addSubview:self.viewContainer];
  167. [self.viewContainer mas_makeConstraints:^(MASConstraintMaker *make) {
  168. make.left.right.top.bottom.mas_equalTo(self.view);
  169. }];
  170. if (self.myWebView == nil) {
  171. WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
  172. config.selectionGranularity = WKSelectionGranularityDynamic;
  173. config.allowsInlineMediaPlayback = YES;
  174. if (@available(iOS 10.0, *)) {
  175. config.mediaTypesRequiringUserActionForPlayback = NO;
  176. } else {
  177. // Fallback on earlier versions
  178. config.requiresUserActionForMediaPlayback = NO;
  179. }
  180. config.processPool = [KSBaseWKWebViewController singleWkProcessPool];
  181. config.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
  182. [self configUserAgent:config];
  183. //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
  184. WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
  185. //这个类主要用来做native与JavaScript的交互管理
  186. WKUserContentController * wkUController = [[WKUserContentController alloc] init];
  187. [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"COLEXIU"];
  188. config.userContentController = wkUController;
  189. WKPreferences *preferences = [WKPreferences new];
  190. // 是否支出javaScript
  191. preferences.javaScriptEnabled = YES;
  192. //不通过用户交互,是否可以打开窗口
  193. preferences.javaScriptCanOpenWindowsAutomatically = YES;
  194. config.preferences = preferences;
  195. self.myWebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
  196. self.myWebView.opaque = NO;
  197. self.myWebView.UIDelegate = self;
  198. self.myWebView.navigationDelegate = self;
  199. self.myWebView.scrollView.bounces = NO;
  200. self.myWebView.backgroundColor = [UIColor clearColor];
  201. self.myWebView.scrollView.backgroundColor = [UIColor clearColor];
  202. // 加载进度条和title
  203. [self.myWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
  204. [self.myWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
  205. [self.view addSubview:self.myWebView];
  206. [self.myWebView mas_makeConstraints:^(MASConstraintMaker *make) {
  207. make.left.right.mas_equalTo(self.view);
  208. make.top.mas_equalTo(self.view.mas_top);
  209. make.bottom.mas_equalTo(self.view.mas_bottom);
  210. }];
  211. if (@available(iOS 11.0, *)) {
  212. self.myWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  213. } else {
  214. // Fallback on earlier versions
  215. }
  216. [self setupProgress];
  217. // 修改userAgent
  218. [self loadRequest];
  219. }
  220. else {
  221. [self.myWebView reload];
  222. }
  223. }
  224. - (void)configUserAgent:(WKWebViewConfiguration *)config {
  225. NSString *oldUserAgent = config.applicationNameForUserAgent;
  226. NSString *newAgent = [NSString stringWithFormat:@"%@ %@ %@",oldUserAgent,@"COLEXIUAPPI",@"COLEXIUTEACHER"];
  227. config.applicationNameForUserAgent = newAgent;
  228. }
  229. - (void)configRecordManager {
  230. self.AQManager = [[KSAQRecordManager alloc] init];
  231. self.AQManager.delegate = self;
  232. }
  233. - (void)viewWillAppear:(BOOL)animated {
  234. [super viewWillAppear:animated];
  235. [self connectSocketService];
  236. if (self.isCameraOpen) {
  237. [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
  238. }
  239. }
  240. - (void)viewWillDisappear:(BOOL)animated {
  241. [super viewWillDisappear:animated];
  242. if (_AQManager) {
  243. [_AQManager freeAudioQueue];
  244. _AQManager = nil;
  245. }
  246. if (_socketManager) {
  247. [self.socketManager SRWebSocketClose];
  248. _socketManager = nil;
  249. }
  250. // 返回不保存视频
  251. [self ignorRecordVideo];
  252. [self stopSession];
  253. // 如果退出评测页面 清除 playerEngine
  254. if (self.playerEngine) {
  255. [self.playerEngine cleanup];
  256. self.playerEngine = nil;
  257. }
  258. [self stopTuner];
  259. }
  260. - (void)sendDataToSocketService:(id)data {
  261. if (_socketManager) {
  262. [self.socketManager sendData:data];
  263. }
  264. }
  265. #pragma mark --- WKScriptMessageHandler
  266. - (void)userContentController:(WKUserContentController *)userContentController
  267. didReceiveScriptMessage:(WKScriptMessage *)message {
  268. if ([message.name isEqualToString:@"COLEXIU"]) {
  269. NSDictionary *parm = [self convertJsonStringToNSDictionary:message.body];
  270. // 回到主线程
  271. dispatch_async(dispatch_get_main_queue(), ^{
  272. if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startEvaluating"]) { // 开始评测
  273. [self configRecordManager];
  274. self.hasSendStartMessage = NO;
  275. PREMISSIONTYPE isOk = [RecordCheckManager checkPermissionShowAlert:NO showInView:nil];
  276. if (isOk == PREMISSIONTYPE_YES) {
  277. self.evaluatParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  278. // 如果socket 连上了
  279. if (self.socketManager.socketReadyState == SR_OPEN) {
  280. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  281. NSString *sendData = [self configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
  282. [self sendDataToSocketService:sendData];
  283. [self postMessage:parm];
  284. self.hasSendStartMessage = YES;
  285. }
  286. else {
  287. [self connectSocketService];
  288. }
  289. }
  290. else {
  291. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  292. @"content" : @{@"reson":@"没有麦克风权限"}
  293. };
  294. [self postMessage:postParm];
  295. }
  296. }
  297. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endEvaluating"]) {// 停止评测
  298. self.evaluatParm = nil;
  299. [self stopRecordService];
  300. [self postMessage:parm];
  301. [self sendEndMessage];
  302. }
  303. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cancelEvaluating"]) { // 取消评测
  304. self.evaluatParm = nil;
  305. [self stopRecordService];
  306. [self postMessage:parm];
  307. [self sendEndMessage];
  308. }
  309. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startRecording"]) { // 开始录制
  310. // self.recordParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  311. [self postMessage:parm];
  312. self.isCompareStart = YES;
  313. [self startRecordService];
  314. }
  315. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endRecording"]) { // 停止录音
  316. self.recordParm = nil;
  317. [self stopRecordService];
  318. [self postMessage:parm];
  319. }
  320. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"pauseRecording"]) {
  321. [self puaseRecordService];
  322. [self postMessage:parm];
  323. }
  324. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"resumeRecording"]) {
  325. [self resumeRecordService];
  326. [self postMessage:parm];
  327. }
  328. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"isWiredHeadsetOn"]) {
  329. [self configAudioDeviceType:parm];
  330. }
  331. // 发送消息给socket service
  332. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyMessage"]) {
  333. [self sendMessageToSocket:parm];
  334. }
  335. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyServiceMessage"]) {
  336. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  337. NSString *sendData = [content mj_JSONString];
  338. NSLog(@"proxyServiceMessage ------- %@",sendData);
  339. [self sendDataToSocketService:sendData];
  340. }
  341. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openCamera"]) { // 开启摄像头
  342. PREMISSIONTYPE canOpenCamera = [RecordCheckManager checkCameraPremissionAvaiable:YES showInView:self.view];
  343. if (canOpenCamera == PREMISSIONTYPE_YES) {
  344. self.isCameraOpen = YES;
  345. [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
  346. [self postMessage:parm];
  347. }
  348. }
  349. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"closeCamera"]) { // 关闭摄像头
  350. self.isCameraOpen = NO;
  351. if (self->_videoRecordManager) {
  352. [self.videoRecordManager removeDisplay];
  353. }
  354. [self postMessage:parm];
  355. }
  356. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startCapture"]) { // 开始录制
  357. // 需要合成音频
  358. [self.videoRecordManager setIgnoreAudio:YES];
  359. [RecordCheckManager checkPhotoLibraryPremissionAvaiable:YES showInView:self.view];
  360. self.videoRecordManager.audioUrl = self.AQManager.audioUrl;
  361. [self.videoRecordManager startRecord];
  362. [self postMessage:parm];
  363. }
  364. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endCapture"]) { // 结束录制
  365. if (self->_videoRecordManager) {
  366. self.endRecordParm = parm;
  367. [self.videoRecordManager stopRecord];
  368. }
  369. else {
  370. [self postMessage:parm];
  371. }
  372. }
  373. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"setCaptureMode"]) {
  374. NSString *modeString = [[parm ks_dictionaryValueForKey:@"content"] ks_stringValueForKey:@"mode"];
  375. BOOL isIgnoreAudio = [modeString isEqualToString:@"evaluating"];
  376. [self postMessage:parm];
  377. [self.videoRecordManager setIgnoreAudio:isIgnoreAudio];
  378. }
  379. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startSoundCheck"]) { // 开始校音
  380. [self configRecordManager];
  381. [self postMessage:parm];
  382. self.isCompareStart = NO;
  383. self.isSoundCheckStart = YES;
  384. [self startRecordService];
  385. }
  386. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endSoundCheck"]) {
  387. // 结束校音
  388. self.isSoundCheckStart = NO;
  389. [self stopRecordService];
  390. [self postMessage:parm];
  391. }
  392. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"videoUpdate"]) { // 上传
  393. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  394. NSMutableDictionary *contentParm = [NSMutableDictionary dictionaryWithDictionary:[sendParm ks_dictionaryValueForKey:@"content"]];
  395. if (self.videoRecordManager) {
  396. MJWeakSelf;
  397. [self.videoRecordManager uploadRecordVideoSuccess:^(NSString * _Nonnull videoUrl) {
  398. [contentParm setValue:@"success" forKey:@"type"];
  399. [contentParm setValue:videoUrl forKey:@"filePath"];
  400. [contentParm setValue:@"上传成功" forKey:@"message"];
  401. [sendParm setValue:contentParm forKey:@"content"];
  402. [weakSelf postMessage:sendParm];
  403. } failure:^(NSString * _Nonnull desc) {
  404. [contentParm setValue:@"error" forKey:@"type"];
  405. [contentParm setValue:desc forKey:@"message"];
  406. [sendParm setValue:contentParm forKey:@"content"];
  407. [weakSelf postMessage:sendParm];
  408. }];
  409. }
  410. }
  411. #pragma mark -------- 云教练原生播放对接
  412. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDetail"]) { // 初始化方法
  413. /**
  414. api: 'cloudDetail',
  415. content: {
  416. midi: '',
  417. denominator: 4,
  418. numerator: 4,
  419. // xml整体原始速度
  420. originalSpeed: 90,
  421. // 间隔(ms)
  422. interval: 50
  423. }
  424. */
  425. [self configAudioSession];
  426. // 重置track num array
  427. self.configEngineParm = [parm copy];
  428. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  429. NSString *midiUrl = [content ks_stringValueForKey:@"midi"];
  430. NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
  431. NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
  432. NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
  433. self.beatType = [self getBeatTypeFromString:beatString];
  434. float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
  435. self.songOriginalSpeed = originalSpeed;
  436. self.currentSpeed = originalSpeed;
  437. NSInteger reportInterval = [content ks_integerValueForKey:@"interval"];
  438. // 下载midi文件
  439. BOOL hasSaveSong = [self checkSongHasSaveWithSongUrl:midiUrl];
  440. NSString *fileName = [[midiUrl componentsSeparatedByString:@"/"] lastObject];
  441. NSString *filePath = [self getFilePathWithName:fileName];
  442. if (hasSaveSong) {
  443. [self configPlayerEngineWithSong:filePath reportTime:reportInterval];
  444. }
  445. else {
  446. MJWeakSelf;
  447. [self downloadMidiFile:midiUrl success:^{
  448. [weakSelf configPlayerEngineWithSong:filePath reportTime:reportInterval];
  449. } faliure:^{
  450. }];
  451. }
  452. }
  453. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudGetMediaStatus"]) { // 获取播放状态
  454. /**
  455. api: 'cloudGetMediaStatus',
  456. content: {
  457. status: 'init' | 'play' | 'suspend'
  458. }
  459. */
  460. NSString *engineStatus = @"init";
  461. if (self.initEngineSuccess) {
  462. if (self.isPlaying) {
  463. engineStatus = @"play";
  464. }
  465. else {
  466. engineStatus = @"suspend";
  467. }
  468. }
  469. NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
  470. [valueDic setValue:engineStatus forKey:@"status"];
  471. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  472. [sendParm setValue:valueDic forKey:@"content"];
  473. [self postMessage:sendParm];
  474. }
  475. // 播放、暂停、进度、播放结束、跳转指定位置
  476. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudPlay"]) { // 播放
  477. /**
  478. api: 'cloudPlay',
  479. content: {
  480. // 当前曲目id
  481. songID: 0,
  482. // xml整体原始速度
  483. originalSpeed: 90,
  484. // 当前选择速度
  485. speed: 90,
  486. // 开始时间(ms)
  487. startTime: 0
  488. // 播放频率
  489. hertz:440
  490. }
  491. */
  492. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  493. self.currentSongId = [content ks_stringValueForKey:@"songID"];
  494. float speed = [content ks_floatValueForKey:@"speed"];
  495. float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
  496. double rate = speed / originalSpeed;
  497. self.currentSpeed = speed;
  498. float hertz = [content ks_floatValueForKey:@"hertz"];
  499. // 播放的hertz
  500. [self.playerEngine adjustPitchByHZ:hertz];
  501. [self.playerEngine setMusicPlayerSpeed:rate];
  502. Float64 startTime = [content ks_doubleValueForKey:@"startTime"];
  503. NSLog(@"------%@", [content ks_stringValueForKey:@"startTime"]);
  504. [self.playerEngine setProgressTime:(startTime/1000)];
  505. // 播放
  506. [self playAction];
  507. [self postMessage:parm];
  508. }
  509. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSuspend"]) { // 暂停
  510. /**
  511. api: 'cloudSuspend',
  512. content: {
  513. // 当前曲目id
  514. songID: 0,
  515. }
  516. */
  517. [self stopPlayAction];
  518. [self postMessage:parm];
  519. }
  520. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSetCurrentTime"]) { // // 跳转指定位置
  521. /**
  522. api: 'cloudSetCurrentTime',
  523. content: {
  524. // 当前曲目id
  525. songID: 0,
  526. // 指定位置时间(ms)
  527. currentTime: 0
  528. }
  529. */
  530. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  531. Float64 currentTime = [content ks_doubleValueForKey:@"currentTime"];
  532. if (self.playerEngine) {
  533. [self.playerEngine setProgressTime:(currentTime/1000)];
  534. }
  535. [self postMessage:parm];
  536. }
  537. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudChangeSpeed"]) { // 调速 范围(45-270整数)
  538. /**
  539. api: 'cloudChangeSpeed',
  540. content: {
  541. // 当前曲目id
  542. songID: 0,
  543. // 调整速度
  544. speed: 90
  545. // xml整体原始速度
  546. originalSpeed: 90,
  547. }
  548. */
  549. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  550. float speed = [content ks_floatValueForKey:@"speed"];
  551. float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
  552. double rate = speed / originalSpeed;
  553. self.currentSpeed = speed;
  554. [self.playerEngine setMusicPlayerSpeed:rate];
  555. // 回报信息
  556. [self postMessage:parm];
  557. }
  558. // 设置每个轨道音量
  559. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudVolume"]) {
  560. /**
  561. api: 'cloudVolume',
  562. content: {
  563. parts: [
  564. {
  565. name: '',
  566. volume: 90,//0-100
  567. }
  568. ]
  569. }
  570. */
  571. NSLog(@"-cloudVolume -----%@",parm);
  572. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  573. int instrumentId = [content ks_intValueForKey:@"activeMidiId"];
  574. float volume = [content ks_floatValueForKey:@"activeMidiVolume"];
  575. if (instrumentId < 0) {
  576. [self postMessage:parm];
  577. [self.playerEngine getAllTrackVolume];
  578. return;
  579. }
  580. [self.playerEngine volumeTrackVolumeWithInstrumentId:instrumentId volume:volume/100];
  581. [self postMessage:parm];
  582. [self.playerEngine getAllTrackVolume];
  583. }
  584. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudMetronome"]) { // 节拍器播放
  585. self.metronomeParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  586. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  587. NSInteger repeatCount = [content ks_integerValueForKey:@"repeat"];
  588. NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
  589. NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
  590. NSInteger supplement = [content ks_integerValueForKey:@"supplement"];
  591. NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
  592. self.beatType = [self getBeatTypeFromString:beatString];
  593. [self showBeatViewRepeatCount:repeatCount supplement:supplement];
  594. }
  595. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDestroy"]) { // 销毁播放器
  596. self.initEngineSuccess = NO;
  597. // 重置track num array
  598. self.isPlaying = NO;
  599. if (self.playerEngine) {
  600. [self.playerEngine cleanup];
  601. self.playerEngine = nil;
  602. }
  603. }
  604. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudLoading"]) { // loading
  605. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  606. BOOL showLoading = [content ks_boolValueForKey:@"show"];
  607. if (showLoading) {
  608. [self showCustomLoading];
  609. }
  610. else {
  611. [self removeCustomLoadingView];
  612. }
  613. }
  614. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudToggleFollow"]) { // 跟音
  615. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  616. NSString *status = [content ks_stringValueForKey:@"state"];
  617. if ([status isEqualToString:@"start"]) { // 开始
  618. [self startTuner];
  619. }
  620. else if ([status isEqualToString:@"end"]) { // 结束
  621. [self stopTuner];
  622. }
  623. }
  624. else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudAccompanyMessage"]) { // 获取伴奏
  625. NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
  626. NSString *accompanyUrl = [content ks_stringValueForKey:@"accompanyUrl"];
  627. if (![NSString isEmptyString:accompanyUrl]) {
  628. NSString *fileName = [[accompanyUrl componentsSeparatedByString:@"/"] lastObject];
  629. NSString *filePath = [self getAccompanyFilePathWithName:fileName];
  630. NSURL *fileUrl = [[NSURL alloc] initFileURLWithPath:filePath];
  631. if ([self checkSongHasSaveAccompanyWithSongUrl:accompanyUrl]) {
  632. [self configVideoRecord:fileUrl];
  633. }
  634. else {
  635. MJWeakSelf;
  636. [self downloadUrl:accompanyUrl success:^{
  637. [weakSelf configVideoRecord:fileUrl];
  638. } faliure:nil];
  639. }
  640. }
  641. else {
  642. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"当前曲目无mp3伴奏"];
  643. [self configVideoRecord:nil];
  644. }
  645. }
  646. else {
  647. [super handleScriptMessageSource:parm];
  648. }
  649. });
  650. }
  651. }
  652. - (void)downloadUrl:(NSString *)url success:(void(^)(void))success faliure:(void(^)(void))faliure {
  653. // [LOADING_MANAGER showHUD];
  654. [KSNetworkingManager downloadFileRequestWithFileUrl:url progress:^(int64_t bytesRead, int64_t totalBytes) {
  655. } success:^(NSURL * _Nonnull fileUrl) {
  656. // [LOADING_MANAGER removeHUD];
  657. if ([self saveAccompanyFileWithUrl:fileUrl accompanyUrl:url]) {
  658. success();
  659. }
  660. } faliure:^(NSError * _Nonnull error) {
  661. }];
  662. }
  663. - (void)configVideoRecord:(NSURL *)path {
  664. self.videoRecordManager.bgAudioUrl = path;
  665. }
  666. - (void)showBeatViewRepeatCount:(NSInteger)repeatCount supplement:(NSInteger)supplement {
  667. KSCloudBeatView *beatView = [KSCloudBeatView shareInstanceWithBeatType:self.beatType speed:self.currentSpeed repeatCount:repeatCount supplement:supplement];
  668. MJWeakSelf;
  669. [beatView startPlayWithEndCallback:^(BOOL isCancle) {
  670. if (isCancle) { // 取消
  671. [weakSelf sendEndMetronomeMessage:YES];
  672. }
  673. else { // 播放完成
  674. [weakSelf sendEndMetronomeMessage:NO];
  675. }
  676. }];
  677. [self.view addSubview:beatView];
  678. }
  679. - (void)sendEndMetronomeMessage:(BOOL)isCancel {
  680. NSString *status = isCancel ? @"cancel" : @"finish";
  681. NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[self.metronomeParm ks_dictionaryValueForKey:@"content"]];
  682. [valueDic setValue:status forKey:@"status"];
  683. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:self.metronomeParm];
  684. [sendParm setValue:valueDic forKey:@"content"];
  685. [self postMessage:sendParm];
  686. }
  687. - (void)downloadMidiFile:(NSString *)midiUrl success:(void(^)(void))success faliure:(void(^)(void))faliure {
  688. [LOADING_MANAGER showHUD];
  689. [KSNetworkingManager downloadFileRequestWithFileUrl:midiUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
  690. } success:^(NSURL * _Nonnull fileUrl) {
  691. [LOADING_MANAGER removeHUD];
  692. if ([self saveMidiFileWithUrl:fileUrl midiUrl:midiUrl]) {
  693. success();
  694. }
  695. } faliure:^(NSError * _Nonnull error) {
  696. faliure();
  697. }];
  698. }
  699. - (BOOL)saveMidiFileWithUrl:(NSURL *)fileUrl midiUrl:(NSString *)midiUrl {
  700. NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
  701. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  702. NSString *filePath= [cachePath stringByAppendingPathComponent:@"MidiSong"];
  703. // 先创建子目录
  704. NSFileManager *fileManager = [NSFileManager defaultManager];
  705. if (![fileManager fileExistsAtPath:filePath]) {
  706. [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
  707. }else{
  708. NSLog(@"已创建文件夹");
  709. }
  710. NSString *fileName = [[midiUrl componentsSeparatedByString:@"/"] lastObject];
  711. NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
  712. BOOL success = [sourceData writeToFile:tempPath atomically:NO];
  713. return success;
  714. }
  715. - (NSString *)getFilePathWithName:(NSString *)fileName {
  716. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  717. NSString *filePath= [cachePath stringByAppendingPathComponent:@"MidiSong"];
  718. return [filePath stringByAppendingPathComponent:fileName];;
  719. }
  720. - (BOOL)checkSongHasSaveWithSongUrl:(NSString *)songUrl {
  721. BOOL hasSaveFile = NO;
  722. NSString *fileName = [[songUrl componentsSeparatedByString:@"/"] lastObject];
  723. NSString *filePath = [self getFilePathWithName:fileName];
  724. NSFileManager *fileManager = [NSFileManager defaultManager];
  725. if ([fileManager fileExistsAtPath:filePath]) {
  726. hasSaveFile = YES;
  727. }
  728. return hasSaveFile;
  729. }
  730. - (void)sendMessageToSocket:(NSDictionary *)parm {
  731. NSString *messageHeader = @"proxyMessage";
  732. NSString *sendMessage = [self configDataCommond:messageHeader body:[parm ks_dictionaryValueForKey:@"content"]];
  733. [self sendDataToSocketService:sendMessage];
  734. }
  735. - (void)configAudioDeviceType:(NSDictionary *)parm {
  736. AUDIODEVICE_TYPE type = [KSAQRecordManager queryAudioOutputDeviceType];
  737. NSString *valueStr = @"";
  738. BOOL checkIsWired = NO;
  739. switch (type) {
  740. case AUDIODEVICE_TYPE_HEADPHONE:
  741. {
  742. valueStr = @"有线耳机";
  743. checkIsWired = YES;
  744. }
  745. break;
  746. case AUDIODEVICE_TYPE_BLUETOOTH:
  747. {
  748. valueStr = @"蓝牙耳机";
  749. checkIsWired = YES;
  750. }
  751. break;
  752. case AUDIODEVICE_TYPE_NONE:
  753. {
  754. valueStr = @"";
  755. checkIsWired = NO;
  756. }
  757. break;
  758. default:
  759. break;
  760. }
  761. NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
  762. [valueDic setValue:valueStr forKey:@"type"];
  763. [valueDic setValue:[NSNumber numberWithBool:checkIsWired] forKey:@"checkIsWired"];
  764. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
  765. [sendParm setValue:valueDic forKey:@"content"];
  766. [self postMessage:sendParm];
  767. }
  768. - (void)startRecordService {
  769. if (self.AQManager.isRunning) {
  770. [self.AQManager stopRecord];
  771. }
  772. [self.AQManager startRecord];
  773. }
  774. - (void)stopRecordService {
  775. if (self.AQManager.isRunning) {
  776. [self.AQManager stopRecord];
  777. }
  778. }
  779. - (void)puaseRecordService {
  780. if (self.AQManager.isRunning) {
  781. [self.AQManager pauserRecord];
  782. }
  783. }
  784. - (void)resumeRecordService {
  785. [self.AQManager resumeRecord];
  786. }
  787. - (void)sendEndMessage {
  788. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  789. // 上传停止的信息 发送给服务端
  790. NSString *endMessage = @"recordEnd";
  791. NSString *endData = [self configDataCommond:endMessage body:nil type:@"SOUND_COMPARE"];
  792. [self sendDataToSocketService:endData];
  793. self.isCompareStart = NO;
  794. NSLog(@"---- send end message");
  795. });
  796. }
  797. #pragma mark-------- KSAQRecordManagerDelegate
  798. - (void)recordInterruption {
  799. NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
  800. @"content" : @{@"reson":@"录制错误,请重试"}
  801. };
  802. [self postMessage:postParm];
  803. }
  804. - (void)audioRouteChange:(AUDIODEVICE_TYPE)type {
  805. NSString *valueStr = @"";
  806. BOOL checkIsWired = NO;
  807. switch (type) {
  808. case AUDIODEVICE_TYPE_HEADPHONE:
  809. {
  810. valueStr = @"有线耳机";
  811. checkIsWired = YES;
  812. }
  813. break;
  814. case AUDIODEVICE_TYPE_BLUETOOTH:
  815. {
  816. valueStr = @"蓝牙耳机";
  817. checkIsWired = YES;
  818. }
  819. break;
  820. case AUDIODEVICE_TYPE_NONE:
  821. {
  822. valueStr = @"";
  823. checkIsWired = NO;
  824. }
  825. break;
  826. default:
  827. break;
  828. }
  829. NSDictionary *postParm = @{@"api" : @"listenerWiredStatus",
  830. @"content" : @{@"type":valueStr,
  831. @"checkIsWired":[NSNumber numberWithBool:checkIsWired]
  832. }
  833. };
  834. [self postMessage:postParm];
  835. }
  836. - (void)audioRecord:(KSAQRecordManager *)audioRecord didRecordAudioData:(void *)data length:(UInt32)length {
  837. if (self.socketManager.socketReadyState != SR_OPEN) {
  838. return;
  839. }
  840. NSData *pushData = [[NSData alloc] initWithBytes:data length:length];
  841. if (self.isCompareStart) { // 发送评测开始消息
  842. dispatch_async(dispatch_get_main_queue(), ^{
  843. NSDate *date = [NSDate date];
  844. NSTimeInterval inteveral = [date timeIntervalSince1970];
  845. double beginTime = inteveral - audioRecord.sampleTime;
  846. NSDictionary *parm = @{
  847. @"api" : @"recordStartTime",
  848. @"content" : @{@"inteveral" : [NSNumber numberWithDouble:beginTime]}
  849. };
  850. [self postMessage:parm];
  851. });
  852. NSLog(@"--------- send start message");
  853. _isCompareStart = NO;
  854. NSString *startMessage = @"recordStart";
  855. NSString *startString = [self configDataCommond:startMessage body:nil type:@"SOUND_COMPARE"];
  856. [self sendDataToSocketService:startString];
  857. // // 发送开始录制的消息给H5
  858. // if (self.recordParm) {
  859. // [self postMessage:self.recordParm];
  860. // }
  861. }
  862. else if (self.isSoundCheckStart) { // 校音开始
  863. NSLog(@"--------- send check start message");
  864. _isSoundCheckStart = NO;
  865. NSString *checkStartMessage = @"start";
  866. NSString *startString = [self configDataCommond:checkStartMessage body:nil type:@"PITCH_DETECTION"];
  867. [self sendDataToSocketService:startString];
  868. }
  869. NSLog(@"--------- send audio data length %d", length);
  870. [self sendDataToSocketService:pushData];
  871. }
  872. - (NSString *)configDataCommond:(NSString *)commond body:(id)bodyMessage type:(NSString *)dataType {
  873. NSMutableDictionary *parm = [NSMutableDictionary dictionary];
  874. if (bodyMessage) {
  875. [parm setValue:bodyMessage forKey:@"body"];
  876. }
  877. NSMutableDictionary *headerParm = [NSMutableDictionary dictionary];
  878. if ([NSString isEmptyString:commond]) {
  879. [headerParm setValue:@"" forKey:@"commond"];
  880. }
  881. else {
  882. [headerParm setValue:commond forKey:@"commond"];
  883. }
  884. if (![NSString isEmptyString:dataType]) {
  885. [headerParm setValue:dataType forKey:@"type"];
  886. }
  887. [headerParm setValue:@(200) forKey:@"status"];
  888. [parm setValue:headerParm forKey:@"header"];
  889. return [parm mj_JSONString];
  890. }
  891. - (NSString *)configDataCommond:(NSString *)commond body:(id)bodyMessage {
  892. NSMutableDictionary *parm = [NSMutableDictionary dictionary];
  893. if (bodyMessage) {
  894. [parm setValue:bodyMessage forKey:@"body"];
  895. }
  896. if ([NSString isEmptyString:commond]) {
  897. [parm setValue:@{@"commond":@""} forKey:@"header"];
  898. }
  899. else {
  900. [parm setValue:@{@"commond":commond} forKey:@"header"];
  901. }
  902. return [parm mj_JSONString];
  903. }
  904. - (KSWebSocketManager *)socketManager {
  905. if (!_socketManager) {
  906. _socketManager = [[KSWebSocketManager alloc] init];
  907. }
  908. return _socketManager;
  909. }
  910. #pragma mark --- lazying
  911. - (UIView *)viewContainer {
  912. if (!_viewContainer) {
  913. _viewContainer = [[UIView alloc] init];
  914. }
  915. return _viewContainer;
  916. }
  917. - (KSVideoRecordManager *)videoRecordManager {
  918. if (!_videoRecordManager) {
  919. MJWeakSelf;
  920. _videoRecordManager = [[KSVideoRecordManager alloc] initSessionRecordCallback:^(BOOL isSuccess, NSString * _Nullable message) {
  921. if (isSuccess) {
  922. [weakSelf showSuccessMessage:message];
  923. }
  924. else {
  925. if (![NSString isEmptyString:message]) {
  926. [LOADING_MANAGER MBShowAUTOHidingInWindow:message];
  927. }
  928. }
  929. }];
  930. }
  931. return _videoRecordManager;
  932. }
  933. - (void)showSuccessMessage:(NSString *)message {
  934. [LOADING_MANAGER MBShowAUTOHidingInWindow:message];
  935. // 成功
  936. if (self.endRecordParm) {
  937. [self postMessage:self.endRecordParm];
  938. self.endRecordParm = nil;
  939. }
  940. }
  941. - (MetronomeType)getBeatTypeFromString:(NSString *)typeString {
  942. if ([typeString isEqualToString:@"1/4"]) {
  943. return MetronomeType1V4;
  944. }
  945. else if ([typeString isEqualToString:@"2/4"]) {
  946. return MetronomeType2V4;
  947. }
  948. else if ([typeString isEqualToString:@"3/4"]) {
  949. return MetronomeType3V4;
  950. }
  951. else if ([typeString isEqualToString:@"4/4"]) {
  952. return MetronomeType4V4;
  953. }
  954. else if ([typeString isEqualToString:@"3/8"]) {
  955. return MetronomeType3V8;
  956. }
  957. else if ([typeString isEqualToString:@"6/8"]) {
  958. return MetronomeType6V8;
  959. }
  960. else {
  961. return MetronomeType4V4;
  962. }
  963. }
  964. #pragma mark ------- midi 播放相关
  965. - (void)configPlayerEngineWithSong:(NSString *)songPath reportTime:(NSInteger)reportTime {
  966. self.initEngineSuccess = NO;
  967. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  968. self.playerEngine = [[MidiPlayerEngine alloc] init];
  969. self.playerEngine.reportTime = reportTime;
  970. self.playerEngine.delegate = self;
  971. [self.playerEngine configSoundFilePath:[[NSBundle mainBundle]
  972. pathForResource:@"synthgms" ofType:@"sf2"]];
  973. [self.playerEngine loadMIDIFileWithString:songPath];
  974. });
  975. }
  976. - (void)configAudioSession {
  977. self.audioSessionManager = [[KSAudioSessionManager alloc] init];
  978. self.audioSessionManager.delegate = self;
  979. [self.audioSessionManager configAudioSession:AUDIOCONFIG_PLAYANDRECORD];
  980. }
  981. #pragma mark ---- PlayerEngineDelegate
  982. /** 进度更新
  983. api: 'cloudTimeUpdae',
  984. content: {
  985. // 当前曲目id
  986. songID: 0,
  987. // 当前位置时间(ms)
  988. currentTime: 0
  989. }
  990. */
  991. /** 播放结束事件
  992. api: 'cloudplayed',
  993. content: {
  994. // 当前曲目id
  995. songID: 0,
  996. }
  997. */
  998. - (void)ProgressUpdated:(float)progress currentTime:(MusicTimeStamp)currentTime {
  999. if (self.isPlaying == NO) {
  1000. return;
  1001. }
  1002. // 回调
  1003. NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
  1004. [sendParm setValue:@"cloudTimeUpdae" forKey:@"api"];
  1005. NSMutableDictionary *content = [NSMutableDictionary dictionary];
  1006. [content setValue:self.currentSongId forKey:@"songID"];
  1007. [content setValue:@(currentTime*1000) forKey:@"currentTime"];
  1008. [sendParm setValue:content forKey:@"content"];
  1009. [self postMessage:sendParm];
  1010. // NSLog(@"------time source %@", content);
  1011. }
  1012. - (void)playEnd {
  1013. [self stopPlayAction];
  1014. NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
  1015. [sendParm setValue:@"cloudplayed" forKey:@"api"];
  1016. NSMutableDictionary *content = [NSMutableDictionary dictionary];
  1017. [content setValue:self.currentSongId forKey:@"songID"];
  1018. [sendParm setValue:content forKey:@"content"];
  1019. [self postMessage:sendParm];
  1020. }
  1021. - (void)initPlayerEngineSuccess:(float)totalTime {
  1022. self.initEngineSuccess = YES;
  1023. if (self.configEngineParm) {
  1024. NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:self.configEngineParm];
  1025. NSMutableDictionary *content = [NSMutableDictionary dictionaryWithDictionary:[sendParm ks_dictionaryValueForKey:@"content"]];
  1026. [content setValue:@(totalTime*1000) forKey:@"midiDuration"];
  1027. [sendParm setValue:content forKey:@"content"];
  1028. [self postMessage:sendParm];
  1029. self.configEngineParm = nil;
  1030. }
  1031. }
  1032. // 总时长
  1033. - (void)GetMusicTotalTime:(float)time {
  1034. }
  1035. #pragma mark ----- 播放控制
  1036. - (void)playAction {
  1037. self.isPlaying = YES;
  1038. [self.playerEngine playMIDIFile];
  1039. }
  1040. - (void)stopPlayAction {
  1041. self.isPlaying = NO;
  1042. if ([self.playerEngine isPlayingFile]) {
  1043. [self.playerEngine stopPlayingMIDIFile];
  1044. }
  1045. }
  1046. #pragma mark ----- 小酷AI loading
  1047. - (AccompanyLoadingView *)loadingView {
  1048. if (!_loadingView) {
  1049. _loadingView = [AccompanyLoadingView shareInstance];
  1050. MJWeakSelf;
  1051. [_loadingView loadingCallback:^{
  1052. [weakSelf backAction];
  1053. }];
  1054. }
  1055. return _loadingView;
  1056. }
  1057. - (void)showCustomLoading {
  1058. if ([self.view.subviews containsObject:self.loadingView]) {
  1059. return;
  1060. }
  1061. [self.view addSubview:self.loadingView];
  1062. [self.loadingView mas_makeConstraints:^(MASConstraintMaker *make) {
  1063. make.left.top.right.bottom.mas_equalTo(self.view);
  1064. }];
  1065. [self.view bringSubviewToFront:self.loadingView];
  1066. [self.loadingView showLoading];
  1067. }
  1068. - (void)removeCustomLoadingView {
  1069. [self.loadingView stopLoading];
  1070. }
  1071. #pragma mark ----- 跟音模块
  1072. - (void)startTuner {
  1073. if (self.isTunerRuning == NO) {
  1074. self.isTunerRuning = YES;
  1075. [self.tuner start];
  1076. }
  1077. }
  1078. - (void)stopTuner {
  1079. if (self.isTunerRuning) {
  1080. self.isTunerRuning = NO;
  1081. [self.tuner stop];
  1082. }
  1083. }
  1084. - (Tuner *)tuner {
  1085. if (!_tuner) {
  1086. _tuner = [[Tuner alloc] initWithThreshold:0 smoothing:0.25];
  1087. _tuner.delegate = self;
  1088. }
  1089. return _tuner;
  1090. }
  1091. - (void)tunerDidUpdate:(Tuner *)tuner output:(TunerOutput *)output {
  1092. if (output.amplitude < 0.01) {
  1093. }
  1094. else {
  1095. // 回调频率
  1096. NSDictionary *parm = @{
  1097. @"api" : @"cloudFollowTime",
  1098. @"content" : @{@"frequency" : [NSNumber numberWithDouble:output.frequency]}
  1099. };
  1100. [self postMessage:parm];
  1101. }
  1102. NSLog(@"-------- %@%zd --- distance :%f frequence : %f" , output.pitch, output.octave, output.distance, output.frequency);
  1103. }
  1104. #pragma mark ---- 保存伴奏
  1105. - (BOOL)saveAccompanyFileWithUrl:(NSURL *)fileUrl accompanyUrl:(NSString *)accompanyUrl {
  1106. NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
  1107. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  1108. NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
  1109. // 先创建子目录
  1110. NSFileManager *fileManager = [NSFileManager defaultManager];
  1111. if (![fileManager fileExistsAtPath:filePath]) {
  1112. [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
  1113. }else{
  1114. NSLog(@"已创建文件夹");
  1115. }
  1116. NSString *fileName = [[accompanyUrl componentsSeparatedByString:@"/"] lastObject];
  1117. NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
  1118. BOOL success = [sourceData writeToFile:tempPath atomically:NO];
  1119. return success;
  1120. }
  1121. - (NSString *)getAccompanyFilePathWithName:(NSString *)fileName {
  1122. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  1123. NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
  1124. return [filePath stringByAppendingPathComponent:fileName];;
  1125. }
  1126. - (BOOL)checkSongHasSaveAccompanyWithSongUrl:(NSString *)songUrl {
  1127. BOOL hasSaveFile = NO;
  1128. NSString *fileName = [[songUrl componentsSeparatedByString:@"/"] lastObject];
  1129. NSString *filePath = [self getAccompanyFilePathWithName:fileName];
  1130. NSFileManager *fileManager = [NSFileManager defaultManager];
  1131. if ([fileManager fileExistsAtPath:filePath]) {
  1132. hasSaveFile = YES;
  1133. }
  1134. return hasSaveFile;
  1135. }
  1136. /*
  1137. #pragma mark - Navigation
  1138. // In a storyboard-based application, you will often want to do a little preparation before navigation
  1139. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  1140. // Get the new view controller using [segue destinationViewController].
  1141. // Pass the selected object to the new view controller.
  1142. }
  1143. */
  1144. @end