|
@@ -0,0 +1,2215 @@
|
|
|
+//
|
|
|
+// KSCloudViewController.m
|
|
|
+// StudentDaya
|
|
|
+//
|
|
|
+// Created by Kyle on 2021/12/8.
|
|
|
+// Copyright © 2021 DayaMusic. All rights reserved.
|
|
|
+//
|
|
|
+
|
|
|
+#import "KSCloudViewController.h"
|
|
|
+#import "UIDevice+TFDevice.h"
|
|
|
+#import "AppDelegate.h"
|
|
|
+#import "StaffImageDisplayView.h"
|
|
|
+#import "KYSourceParseManager.h"
|
|
|
+#import "MidiPlayerEngine.h"
|
|
|
+#import "CloudControlButton.h"
|
|
|
+#import "KSCloudSettingView.h"
|
|
|
+#import "KSXMLInfoParse.h"
|
|
|
+#import "ScoreAnimationView.h"
|
|
|
+#import "EvaluateResultAlert.h"
|
|
|
+#import "NoWiredTipsAlert.h"
|
|
|
+#import "LFPopupMenu.h"
|
|
|
+#import "KSSliderView.h"
|
|
|
+#import "SoundCheckView.h"
|
|
|
+#import "KSBaseWKWebViewController.h"
|
|
|
+#import <SSZipArchive/SSZipArchive.h>
|
|
|
+#import "CloudHelpView.h"
|
|
|
+#import "CloudFeedbackView.h"
|
|
|
+#import "KSAudioSessionManager.h"
|
|
|
+#import "KSCloudBeatView.h"
|
|
|
+
|
|
|
+#import "RecordCheckManager.h"
|
|
|
+#import "KSAQRecordManager.h"
|
|
|
+#import "KSWebSocketManager.h"
|
|
|
+#import "KSVideoRecordManager.h"
|
|
|
+#import "TrackChooseView.h"
|
|
|
+
|
|
|
+#define PositionRate (12) // 图片上点的位置和位置文件的系数
|
|
|
+
|
|
|
+#define Left_Space (37)
|
|
|
+#define Button_Width (34)
|
|
|
+#define Button_Height (60)
|
|
|
+#define Button_Space (20)
|
|
|
+
|
|
|
+#define BACKVIEW_ALPHA (0.6f)
|
|
|
+typedef NS_ENUM(NSInteger, BUTTON_TAG) {
|
|
|
+ BUTTON_TAG_EVALUATING = 1000, // 评测
|
|
|
+ BUTTON_TAG_SELECT, // 选段
|
|
|
+ BUTTON_TAG_PLAY, // 播放
|
|
|
+ BUTTON_TAG_SWITCH, // 切换
|
|
|
+ BUTTON_TAG_REPEAT, // 重播
|
|
|
+ BUTTON_TAG_SPEED, // 调速
|
|
|
+ BUTTON_TAG_SETTING, // 设置
|
|
|
+};
|
|
|
+
|
|
|
+typedef NS_ENUM(NSInteger,COLOR_DISPLAYTYPE) {
|
|
|
+ COLOR_DISPLAYTYPE_NOMAL,
|
|
|
+ COLOR_DISPLAYTYPE_PROTECT,
|
|
|
+};
|
|
|
+
|
|
|
+@interface KSCloudViewController ()<PlayerEngineDelegate,KSAQRecordManagerDelegate,KSSliderDelegate,KSAudioSessionManagerDelegate>
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSAudioSessionManager *audioSessionManager;
|
|
|
+
|
|
|
+@property (nonatomic, assign) MetronomeType beatType;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL keepOrientation;
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIView *backView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIView *bgColorView; // 容器颜色视图
|
|
|
+
|
|
|
+@property (nonatomic, assign) COLOR_DISPLAYTYPE bgColorType; // 容器颜色类型
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIScrollView *displayScrollView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIButton *helpButton; // 帮助按钮
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSSliderView *sliderView;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL scorllEnable; // 是否能滑动
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSMutableArray *pageArray;
|
|
|
+
|
|
|
+@property (nonatomic, strong) KYSourceParseManager *manager;
|
|
|
+
|
|
|
+@property (nonatomic, assign) CGFloat scaleRate;
|
|
|
+
|
|
|
+@property (nonatomic, assign) CGFloat pageViewHeight;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isChooseMeasure; // 是否小节选择模式
|
|
|
+
|
|
|
+@property (nonatomic, assign) NSInteger startMeasure;
|
|
|
+
|
|
|
+@property (nonatomic, assign) NSInteger endMeasure;
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIView *cursorView; // 光标
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIButton *evaluateButton; // 评测按钮
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isEvaluating; // 是否开启评测模式
|
|
|
+
|
|
|
+@property (nonatomic, strong) MidiPlayerEngine *playerEngine; // player
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIView *headView;
|
|
|
+
|
|
|
+@property (nonatomic, assign) NSInteger lastPlayNodeIndex; // 上次播放的音符序号
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSString *songPath; // 音频文件路径
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSString *songName; // 曲目名称
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isPlaying; // 是否正在播放的状态
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isNeedRepeat; // 是否需要重复
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL soundCheckOn; // 是否校音
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL showFinger; // 显示指法开关
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL needShowFinger; // 是否需要显示指法
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL cameraOn; // 是否开启摄像头
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL eyeShieldOn; // 护眼模式是否开启
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL saveVideo; // 是否保存视频
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSString *level; // 评测级别
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL accompanyOn; // 是否开启伴奏
|
|
|
+
|
|
|
+@property (nonatomic, assign) NSInteger currentSpeed; // 当前速度
|
|
|
+// 是否发送了musicXML信息
|
|
|
+@property (nonatomic, assign) BOOL hasSendStartMessage;
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSAQRecordManager *AQManager;
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSWebSocketManager *socketManager;
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSMutableDictionary *evaluatParm;
|
|
|
+
|
|
|
+// 评测开始时发送recordStart消息的标识
|
|
|
+@property (nonatomic, assign) BOOL isCompareStart;
|
|
|
+// 校音开始时发送 checkStart消息标识
|
|
|
+@property (nonatomic, assign) BOOL isSoundCheckStart;
|
|
|
+
|
|
|
+// 自定义UI控件容器
|
|
|
+@property (nonatomic, strong) UIView *viewContainer;
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSVideoRecordManager *videoRecordManager;
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSMutableArray *nodeArray;
|
|
|
+
|
|
|
+@property (nonatomic, assign) NSInteger recordId;
|
|
|
+
|
|
|
+@property (nonatomic, strong) EvaluateResultAlert *resultAlert;
|
|
|
+
|
|
|
+@property (nonatomic, strong) NoWiredTipsAlert *wiredAlert;
|
|
|
+
|
|
|
+@property (nonatomic, assign) NSInteger checkPitch;
|
|
|
+
|
|
|
+@property (nonatomic, assign) long long correctTimeInterval;
|
|
|
+
|
|
|
+@property (nonatomic, strong) SoundCheckView *checkView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) CloudSongMessageModel *songMessageSource;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isVertical;
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSDictionary *fingerImageDic;
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSString *fullDicImage;
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIView *fingerDisplayView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) UIView *fingerContentView;
|
|
|
+
|
|
|
+@property (nonatomic, assign) CGRect defaultFrame;
|
|
|
+
|
|
|
+@property (nonatomic, assign) CGRect resizeFrame;
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSMutableArray *zipFileArray;
|
|
|
+
|
|
|
+@property (nonatomic, strong) CloudFeedbackView *feedbackView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) TrackChooseView *trackChooseView;
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+@implementation KSCloudViewController
|
|
|
+
|
|
|
+- (NSString *)getSaveZipPath {
|
|
|
+ // 在Documents目录下创建一个名为AudioSpeedFile的文件夹
|
|
|
+ NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"AudioZipFile"];
|
|
|
+ NSLog(@"%@",path);
|
|
|
+
|
|
|
+ NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
|
+ BOOL isDir = FALSE;
|
|
|
+ BOOL isDirExist = [fileManager fileExistsAtPath:path isDirectory:&isDir];
|
|
|
+ if(!(isDirExist && isDir)) {
|
|
|
+ BOOL bCreateDir = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
|
|
|
+ if(!bCreateDir){
|
|
|
+ NSLog(@"创建文件夹失败!");
|
|
|
+ }
|
|
|
+ NSLog(@"创建文件夹成功,文件路径%@",path);
|
|
|
+ }
|
|
|
+
|
|
|
+ NSLog(@"file path:%@",path);
|
|
|
+ return path;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ----- life cycle
|
|
|
+- (void)viewDidLoad {
|
|
|
+ [super viewDidLoad];
|
|
|
+ // Do any additional setup after loading the view.
|
|
|
+ self.ks_prefersNavigationBarHidden = YES;
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterBackground) name:@"appEnterBackground" object:nil];
|
|
|
+ self.view.backgroundColor = UIColor.whiteColor;
|
|
|
+ [self displayBaseConfig];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)viewWillAppear:(BOOL)animated {
|
|
|
+ [super viewWillAppear:animated];
|
|
|
+ [self connectSocketService];
|
|
|
+ // 切换到横屏
|
|
|
+ [self changeOrientation:YES];
|
|
|
+ [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)viewDidAppear:(BOOL)animated {
|
|
|
+ [super viewDidAppear:animated];
|
|
|
+ if (self.songMessageSource != nil) {
|
|
|
+ NSString *filePath = [self getSaveZipPath];
|
|
|
+ NSString *floderName = [[[[self.songMessageSource.zipUrl componentsSeparatedByString:@"/"] lastObject] componentsSeparatedByString:@"."] firstObject];
|
|
|
+ filePath = [NSString stringWithFormat:@"%@/%@",filePath,floderName];
|
|
|
+ NSError *error = nil;
|
|
|
+ NSArray *folderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:filePath error:&error];
|
|
|
+ if (folderContents.count == 0) {
|
|
|
+ [self downloadZipFileWithFloderName:floderName];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self configUnZipFilePathWithFloderName:floderName];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)viewWillDisappear:(BOOL)animated {
|
|
|
+ [super viewWillDisappear:animated];
|
|
|
+ [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
|
|
|
+ if (_AQManager) {
|
|
|
+ [_AQManager freeAudioQueue];
|
|
|
+ _AQManager = nil;
|
|
|
+ }
|
|
|
+ if (_socketManager) {
|
|
|
+ [self.socketManager SRWebSocketClose];
|
|
|
+ _socketManager = nil;
|
|
|
+ }
|
|
|
+ [self stopSession];
|
|
|
+
|
|
|
+ if (self.keepOrientation == NO) {
|
|
|
+ [self changeOrientation:NO];
|
|
|
+ // 如果退出评测页面 清除 playerEngine
|
|
|
+ if (self.playerEngine) {
|
|
|
+ [self.playerEngine cleanup];
|
|
|
+ self.playerEngine = nil;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ self.keepOrientation = NO;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)changeOrientation:(BOOL)isLandScape {
|
|
|
+ if (isLandScape) {
|
|
|
+ // 切换到横屏
|
|
|
+ AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
|
|
|
+ delegate.allowAutoRotate = YES;
|
|
|
+ [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
|
|
|
+ delegate.allowAutoRotate = NO;
|
|
|
+ [UIDevice switchNewOrientation:UIInterfaceOrientationPortrait];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ---- 获取和配置曲目相关参数
|
|
|
+- (void)configMessage:(CloudSongMessageModel *)songMessage {
|
|
|
+ self.songMessageSource = songMessage;
|
|
|
+ self.songName = songMessage.examSongName;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)configUnZipFilePathWithFloderName:(NSString *)name {
|
|
|
+ self.zipFileArray = [NSMutableArray array];
|
|
|
+ NSString *filePath = [self getSaveZipPath];
|
|
|
+ filePath = [NSString stringWithFormat:@"%@/%@",filePath,name];
|
|
|
+ NSError *error = nil;
|
|
|
+ NSArray *folderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:filePath error:&error];
|
|
|
+ for (NSString *fileName in folderContents) {
|
|
|
+ NSString *path = [NSString stringWithFormat:@"%@/%@", filePath, fileName];
|
|
|
+ [self.zipFileArray addObject:path];
|
|
|
+ }
|
|
|
+ NSLog(@"------%@",name);
|
|
|
+ [self querySourceMessage];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)downloadZipFileWithFloderName:(NSString *)name {
|
|
|
+
|
|
|
+ [self showhud];
|
|
|
+ [KSNetworkingManager downloadFileRequestWithFileUrl:self.songMessageSource.zipUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
|
|
|
+
|
|
|
+ } success:^(NSURL * _Nonnull fileUrl) {
|
|
|
+
|
|
|
+ // 解压zip包
|
|
|
+ NSString *filePath = [self getSaveZipPath];
|
|
|
+ BOOL success = [SSZipArchive unzipFileAtPath:fileUrl.path toDestination:filePath];
|
|
|
+ NSLog(@"%d", success);
|
|
|
+ if (success) {
|
|
|
+ [self removehub];
|
|
|
+ [self configUnZipFilePathWithFloderName:name];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self removehub];
|
|
|
+ [self MBPShow:@"资源解压失败"];
|
|
|
+ }
|
|
|
+ } faliure:^(NSError * _Nonnull error) {
|
|
|
+ [self removehub];
|
|
|
+ [self MBPShow:@"下载配置资源失败"];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)displayBaseConfig {
|
|
|
+ self.beatType = MetronomeType4V4;
|
|
|
+ NSString *savePath = [CloudSongMessageModel getSaveSpeedPath];
|
|
|
+ NSDictionary *speedDic = [NSMutableDictionary dictionaryWithContentsOfFile:savePath];
|
|
|
+ NSInteger speed = [speedDic integerValueForKey:self.songMessageSource.examSongId];
|
|
|
+ if (speed != 0) {
|
|
|
+ self.currentSpeed = speed;
|
|
|
+ }
|
|
|
+ [self queryFingerConfig];
|
|
|
+ [self queryCloudConfig];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)queryFingerConfig {
|
|
|
+
|
|
|
+ NSDictionary *fingerSubjectDic = [NSMutableDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"SubjectFinger" ofType:@"plist"]];
|
|
|
+
|
|
|
+ NSDictionary *imageDict = [NSMutableDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"FingerList" ofType:@"plist"]];
|
|
|
+ // 根据当前声部获取对应的音高对应图片
|
|
|
+ NSString *subjectId = self.songMessageSource.subjectId;
|
|
|
+
|
|
|
+ NSDictionary *subjectDic = [fingerSubjectDic dictionaryValueForKey:subjectId];
|
|
|
+ if (subjectDic) {
|
|
|
+ NSString *subjectName = [subjectDic stringValueForKey:@"name"];
|
|
|
+ BOOL isVertical = [subjectDic boolValueForKey:@"vertical"];
|
|
|
+ self.isVertical = isVertical;
|
|
|
+
|
|
|
+ NSDictionary *fingerSource = [imageDict dictionaryValueForKey:subjectName];
|
|
|
+ self.fingerImageDic = [NSDictionary dictionaryWithDictionary:fingerSource];
|
|
|
+
|
|
|
+ NSString *fullImageName = [NSString stringWithFormat:@"%@full",subjectName];
|
|
|
+ UIImage *bgImage = [UIImage imageNamed:fullImageName];
|
|
|
+
|
|
|
+ CGRect frame = CGRectZero;
|
|
|
+ CGFloat width = bgImage.size.width;
|
|
|
+ CGFloat height = bgImage.size.height;
|
|
|
+ if (isVertical) {
|
|
|
+ CGFloat contentHeight = CGRectGetHeight(self.defaultFrame);
|
|
|
+ if (height > contentHeight) {
|
|
|
+ width = contentHeight / height * width;
|
|
|
+ height = contentHeight;
|
|
|
+ }
|
|
|
+ frame = CGRectMake(CGRectGetWidth(self.defaultFrame) - width, (CGRectGetHeight(self.defaultFrame) - height) / 2.0f, width, height);
|
|
|
+ self.resizeFrame = CGRectMake(0, 0, CGRectGetWidth(self.defaultFrame)-width, CGRectGetHeight(self.defaultFrame));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ CGFloat contentWidth = CGRectGetWidth(self.defaultFrame);
|
|
|
+ if (width > contentWidth) {
|
|
|
+ height = width / contentWidth * height;
|
|
|
+ width = contentWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ frame = CGRectMake((CGRectGetWidth(self.defaultFrame) - width) / 2.0f, CGRectGetHeight(self.defaultFrame) - height, width, height);
|
|
|
+
|
|
|
+ self.resizeFrame = CGRectMake(0, 0, CGRectGetWidth(self.defaultFrame), CGRectGetHeight(self.defaultFrame) - height);
|
|
|
+ }
|
|
|
+ self.fingerDisplayView = [[UIView alloc] initWithFrame:frame];
|
|
|
+ self.fingerDisplayView.backgroundColor = [UIColor clearColor];
|
|
|
+ UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
|
|
|
+ [bgImageView setImage:bgImage];
|
|
|
+ [self.fingerDisplayView addSubview:bgImageView];
|
|
|
+
|
|
|
+ self.fingerContentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
|
|
|
+ self.fingerContentView.backgroundColor = [UIColor clearColor];
|
|
|
+ [self.fingerDisplayView addSubview:self.fingerContentView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setShowFinger:(BOOL)showFinger {
|
|
|
+ _showFinger = showFinger;
|
|
|
+ if (showFinger && self.fingerImageDic != nil) {
|
|
|
+ self.needShowFinger = YES;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.needShowFinger = NO;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)queryCloudConfig {
|
|
|
+ NSDictionary *config = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"cloudConfig"];
|
|
|
+ if (config == nil || config.allKeys.count == 0) {
|
|
|
+ NSMutableDictionary *newConfig = [NSMutableDictionary dictionary];
|
|
|
+ [newConfig setValue:@(NO) forKey:@"eyeshield"];
|
|
|
+ [newConfig setValue:@(NO) forKey:@"soundCheck"];
|
|
|
+ [newConfig setValue:@(NO) forKey:@"cameraOn"];
|
|
|
+ [newConfig setValue:@(YES) forKey:@"repeatOn"];
|
|
|
+ [newConfig setValue:@(YES) forKey:@"showFinger"];
|
|
|
+ [newConfig setValue:@"ADVANCED" forKey:@"level"];
|
|
|
+ [newConfig setValue:@(YES) forKey:@"accompanyOn"];
|
|
|
+ [newConfig setValue:@(NO) forKey:@"save"];
|
|
|
+ [newConfig setValue:self.songMessageSource.behaviorId forKey:@"behaviorId"];
|
|
|
+ [newConfig setValue:@"" forKey:@"tips-status"];
|
|
|
+ [[NSUserDefaults standardUserDefaults] setObject:newConfig forKey:@"cloudConfig"];
|
|
|
+ [[NSUserDefaults standardUserDefaults] synchronize];
|
|
|
+ config = [NSDictionary dictionaryWithDictionary:newConfig];
|
|
|
+ }
|
|
|
+
|
|
|
+ self.eyeShieldOn = [config boolValueForKey:@"eyeshield"];
|
|
|
+ self.soundCheckOn = [config boolValueForKey:@"soundCheck"];
|
|
|
+ self.cameraOn = [config boolValueForKey:@"cameraOn"];
|
|
|
+ self.isNeedRepeat = [config boolValueForKey:@"repeatOn"];
|
|
|
+ self.showFinger = [config boolValueForKey:@"showFinger"];
|
|
|
+ self.level = [config stringValueForKey:@"level"];
|
|
|
+ self.saveVideo = [config boolValueForKey:@"save"];
|
|
|
+ self.accompanyOn = [config boolValueForKey:@"accompanyOn"];
|
|
|
+
|
|
|
+ NSMutableDictionary *saveConfig = [NSMutableDictionary dictionaryWithDictionary:config];
|
|
|
+ [saveConfig setValue:self.songMessageSource.behaviorId forKey:@"behaviorId"];
|
|
|
+ [[NSUserDefaults standardUserDefaults] setObject:saveConfig forKey:@"cloudConfig"];
|
|
|
+ [[NSUserDefaults standardUserDefaults] synchronize];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)querySourceMessage {
|
|
|
+
|
|
|
+ dispatch_async(dispatch_queue_create("CloudSource", DISPATCH_QUEUE_SERIAL), ^{
|
|
|
+ // 未选择小节设置为-1;
|
|
|
+ self.startMeasure = -1;
|
|
|
+ self.endMeasure = -1;
|
|
|
+ NSString *measurePath = @"";
|
|
|
+ NSString *nodeSourePath = @"";
|
|
|
+ NSString *xmlFile = @"";
|
|
|
+ NSString *songPath = @"";
|
|
|
+ for (NSString *filePath in self.zipFileArray) {
|
|
|
+ if ([filePath containsString:@".mpos"]) {
|
|
|
+ measurePath = filePath;
|
|
|
+ }
|
|
|
+ else if ([filePath containsString:@".spos"]) {
|
|
|
+ nodeSourePath = filePath;
|
|
|
+ }
|
|
|
+ else if ([filePath containsString:@".musicxml"] || [filePath containsString:@".xml"]) {
|
|
|
+ xmlFile = filePath;
|
|
|
+ }
|
|
|
+ else if ([filePath containsString:@".mid"]) {
|
|
|
+ songPath = filePath;
|
|
|
+ self.songPath = songPath;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+// measurePath = [[NSBundle mainBundle] pathForResource:@"mus" ofType:@"mpos"];
|
|
|
+// nodeSourePath = [[NSBundle mainBundle] pathForResource:@"mus" ofType:@"spos"];
|
|
|
+// xmlFile = [[NSBundle mainBundle] pathForResource:@"Danza_Del_Fuego" ofType:@"xml"];
|
|
|
+// songPath = [[NSBundle mainBundle] pathForResource:@"Danza_Del_Fuego" ofType:@"mid"];
|
|
|
+
|
|
|
+ NSDictionary *xmlParm = [KSXMLInfoParse getInfoFromXMLMusicFile:xmlFile];
|
|
|
+ self.nodeArray = [NSMutableArray arrayWithArray:[xmlParm arrayValueForKey:@"noteInfo"]];
|
|
|
+ self.beatType = [self getBeatTypeFromString:[xmlParm stringValueForKey:@"beatType"]];
|
|
|
+ self.manager = [[KYSourceParseManager alloc] init];
|
|
|
+ [self.manager parseMessageSource:measurePath nodeSource:nodeSourePath];
|
|
|
+
|
|
|
+ [self configAudioSession];
|
|
|
+ // 预加载mid文件
|
|
|
+ [self configPlayerEngineWithSong:songPath];
|
|
|
+ dispatch_main_async_safe(^{
|
|
|
+ [self displayView];
|
|
|
+ });
|
|
|
+
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+- (MetronomeType)getBeatTypeFromString:(NSString *)typeString {
|
|
|
+ if ([typeString isEqualToString:@"1/4"]) {
|
|
|
+ return MetronomeType1V4;
|
|
|
+ }
|
|
|
+ else if ([typeString isEqualToString:@"2/4"]) {
|
|
|
+ return MetronomeType2V4;
|
|
|
+ }
|
|
|
+ else if ([typeString isEqualToString:@"3/4"]) {
|
|
|
+ return MetronomeType3V4;
|
|
|
+ }
|
|
|
+ else if ([typeString isEqualToString:@"4/4"]) {
|
|
|
+ return MetronomeType4V4;
|
|
|
+ }
|
|
|
+ else if ([typeString isEqualToString:@"3/8"]) {
|
|
|
+ return MetronomeType3V8;
|
|
|
+ }
|
|
|
+ else if ([typeString isEqualToString:@"6/8"]) {
|
|
|
+ return MetronomeType6V8;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return MetronomeType4V4;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)configAudioSession {
|
|
|
+ self.audioSessionManager = [[KSAudioSessionManager alloc] init];
|
|
|
+ self.audioSessionManager.delegate = self;
|
|
|
+ [self.audioSessionManager configAudioSession:AUDIOCONFIG_PLAYANDRECORD];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ----- 音频打断相关处理
|
|
|
+
|
|
|
+- (void)handerAudioInterruption {
|
|
|
+ // 处理
|
|
|
+ if (_isEvaluating) { // 如果是评测就停止评测
|
|
|
+ [self cancelEvaluating];
|
|
|
+ // 页面重置
|
|
|
+ }
|
|
|
+ else if (_isPlaying) { // 如果是播放就停止播放
|
|
|
+ [self stopPlayAction];
|
|
|
+ }
|
|
|
+}
|
|
|
+- (void)resumeAudioSession {
|
|
|
+ self.lastPlayNodeIndex = 0;
|
|
|
+ [self.playerEngine resumeAUGraph];
|
|
|
+}
|
|
|
+// 退到后台
|
|
|
+- (void)appEnterBackground {
|
|
|
+ [self handerAudioInterruption];
|
|
|
+}
|
|
|
+
|
|
|
+// 打断处理
|
|
|
+- (void)audioInterruption {
|
|
|
+ if (_videoRecordManager) {
|
|
|
+ [self.videoRecordManager resetSession];
|
|
|
+ }
|
|
|
+ [self handerAudioInterruption];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)stopSession {
|
|
|
+ if (_videoRecordManager) {
|
|
|
+ [self.videoRecordManager stopSession];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ----- socket 相关处理
|
|
|
+
|
|
|
+- (void)connectSocketService {
|
|
|
+
|
|
|
+ MJWeakSelf;
|
|
|
+ self.socketManager.didReceiveMessage = ^(id _Nonnull message) {
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ // api
|
|
|
+ NSMutableDictionary *sendMessage = [NSMutableDictionary dictionary];
|
|
|
+ [sendMessage setValue:@"sendResult" forKey:@"api"];
|
|
|
+ [sendMessage setValue:message forKey:@"content"];
|
|
|
+ NSLog(@"receive message -----%@",message);
|
|
|
+ // 处理服务端返回的数据
|
|
|
+ NSDictionary *messageDic = [weakSelf convertJsonStringToNSDictionary:message];
|
|
|
+ [weakSelf dealWithMessage:messageDic];
|
|
|
+ });
|
|
|
+ };
|
|
|
+ self.socketManager.connectionStatus = ^(BOOL isSuccess) {
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+
|
|
|
+ if (!isSuccess) {
|
|
|
+ NSLog(@"-----连接失败");
|
|
|
+ if (weakSelf.hasSendStartMessage) {
|
|
|
+ if (weakSelf.AQManager.isRunning) {
|
|
|
+ [weakSelf cancelEvaluating];
|
|
|
+ [weakSelf showErrorMessage:@"服务异常,请重试"];
|
|
|
+ // 处理断连事件
|
|
|
+ [weakSelf stopRecordService];
|
|
|
+ weakSelf.isCompareStart = NO;
|
|
|
+ weakSelf.isSoundCheckStart = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (weakSelf.evaluatParm) {
|
|
|
+ // 处理断连事件
|
|
|
+ [weakSelf cancelEvaluating];
|
|
|
+ [weakSelf showErrorMessage:@"服务异常,请重试"];
|
|
|
+ weakSelf.hasSendStartMessage = YES;
|
|
|
+ weakSelf.isCompareStart = NO;
|
|
|
+ weakSelf.isSoundCheckStart = NO;
|
|
|
+ }
|
|
|
+ else { // 其他断开
|
|
|
+ if (weakSelf.AQManager.isRunning) {
|
|
|
+
|
|
|
+ // 处理断连事件 停止评测
|
|
|
+ [weakSelf cancelEvaluating];
|
|
|
+ [weakSelf showErrorMessage:@"服务异常,请重试"];
|
|
|
+
|
|
|
+ [weakSelf stopRecordService];
|
|
|
+ weakSelf.isCompareStart = NO;
|
|
|
+ weakSelf.isSoundCheckStart = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ NSLog(@"-----连接成功");
|
|
|
+ if (weakSelf.hasSendStartMessage == NO && weakSelf.evaluatParm) {
|
|
|
+ NSDictionary *content = weakSelf.evaluatParm;
|
|
|
+ NSString *sendData = [weakSelf configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
|
|
|
+ [weakSelf sendDataToSocketService:sendData];
|
|
|
+ [weakSelf showBeatBeforeEvaluating];
|
|
|
+ weakSelf.hasSendStartMessage = YES;
|
|
|
+ NSLog(@"---- send musicXML message");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+ [self.socketManager SRWebSocketOpen];
|
|
|
+}
|
|
|
+
|
|
|
+- (NSDictionary *)convertJsonStringToNSDictionary:(NSString *)jsonString {
|
|
|
+ if (jsonString == nil) {
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+ NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
|
+ NSError *error;
|
|
|
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
|
|
|
+ if (error) {
|
|
|
+ NSLog(@"jsonString解析失败:%@", error);
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+ return json;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)sendDataToSocketService:(id)data {
|
|
|
+ if (_socketManager) {
|
|
|
+ [self.socketManager sendData:data];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理socket返回数据
|
|
|
+- (void)dealWithMessage:(NSDictionary *)messageDic {
|
|
|
+ if (_isEvaluating == NO) { // 非评测状态下不处理
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSString *commond = [[messageDic dictionaryValueForKey:@"header"] stringValueForKey:@"commond"];
|
|
|
+ if ([commond isEqualToString:@"measureScore"]) { // 评测返回数据
|
|
|
+ NSDictionary *body = [messageDic dictionaryValueForKey:@"body"];
|
|
|
+ NSInteger score = [body integerValueForKey:@"score"];
|
|
|
+ NSInteger measureIndex = [body integerValueForKey:@"measureIndex"];
|
|
|
+ [self displayScore:score measureIndex:measureIndex];
|
|
|
+ }
|
|
|
+ else if ([commond isEqualToString:@"overall"]) { // 弹窗结果
|
|
|
+ NSDictionary *body = [messageDic dictionaryValueForKey:@"body"];
|
|
|
+ self.recordId = [body integerValueForKey:@"recordId"];
|
|
|
+ NSInteger score = [body integerValueForKey:@"score"];
|
|
|
+ NSInteger intonation = [body integerValueForKey:@"intonation"];
|
|
|
+ NSInteger integrity = [body integerValueForKey:@"integrity"];
|
|
|
+ NSInteger cadence = [body integerValueForKey:@"cadence"];
|
|
|
+ self.resultAlert = [EvaluateResultAlert shareInstance];
|
|
|
+ self.resultAlert.frame = CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight);
|
|
|
+ [self.resultAlert configWithScore:score intonation:intonation cadence:cadence integrity:integrity];
|
|
|
+ MJWeakSelf;
|
|
|
+ [self.resultAlert resultCallback:^(EVALUATE_RESULT_ACTION action) {
|
|
|
+ [weakSelf resulteAlertAction:action];
|
|
|
+ }];
|
|
|
+ [self.view addSubview:self.resultAlert];
|
|
|
+ }
|
|
|
+ else if ([commond isEqualToString:@"checking"]) { // 校音
|
|
|
+ NSTimeInterval interval = [[NSDate date] timeIntervalSince1970];
|
|
|
+ long long currentMilliseconds = interval * 1000;
|
|
|
+
|
|
|
+ NSDictionary *body = [messageDic dictionaryValueForKey:@"body"];
|
|
|
+ float frequency = [body doubleValueForKey:@"frequency"];
|
|
|
+ float baseFrequency = [KSXMLInfoParse parsePitchToHZ:self.checkPitch];
|
|
|
+ if (frequency - baseFrequency > 5.0) { // 高了
|
|
|
+ self.correctTimeInterval = 0.0f;
|
|
|
+ [self.checkView showLightDisplay:LIGHTDISPLAY_HIGH];
|
|
|
+ }
|
|
|
+ else if (frequency - baseFrequency < -5.0f) { // 低了
|
|
|
+ self.correctTimeInterval = 0.0f;
|
|
|
+ [self.checkView showLightDisplay:LIGHTDISPLAY_LOW];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (self.correctTimeInterval == 0.0f) {
|
|
|
+ self.correctTimeInterval = currentMilliseconds;
|
|
|
+ }
|
|
|
+ else { // 如果一直持续超过1s
|
|
|
+ long long duration = currentMilliseconds - self.correctTimeInterval;
|
|
|
+ if (duration >= 1000) {
|
|
|
+ if (self.checkView) {
|
|
|
+ self.correctTimeInterval = 0.0;
|
|
|
+ [self.checkView showSuccessCheckEnd];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ [self.checkView showLightDisplay:LIGHTDISPLAY_RIGHT];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (NSString *)configDataCommond:(NSString *)commond body:(id)bodyMessage type:(NSString *)dataType {
|
|
|
+ NSMutableDictionary *parm = [NSMutableDictionary dictionary];
|
|
|
+ if (bodyMessage) {
|
|
|
+ [parm setValue:bodyMessage forKey:@"body"];
|
|
|
+ }
|
|
|
+ NSMutableDictionary *headerParm = [NSMutableDictionary dictionary];
|
|
|
+ if ([NSString isEmptyString:commond]) {
|
|
|
+ [headerParm setValue:@"" forKey:@"commond"];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [headerParm setValue:commond forKey:@"commond"];
|
|
|
+ }
|
|
|
+ if (![NSString isEmptyString:dataType]) {
|
|
|
+ [headerParm setValue:dataType forKey:@"type"];
|
|
|
+ }
|
|
|
+ [headerParm setValue:@(200) forKey:@"status"];
|
|
|
+ [parm setValue:headerParm forKey:@"header"];
|
|
|
+ return [parm mj_JSONString];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)sendEndMessage {
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ // 上传停止的信息 发送给服务端
|
|
|
+ NSString *endMessage = @"recordEnd";
|
|
|
+ NSString *endData = [self configDataCommond:endMessage body:nil type:@"SOUND_COMPARE"];
|
|
|
+ [self sendDataToSocketService:endData];
|
|
|
+ self.isCompareStart = NO;
|
|
|
+ NSLog(@"---- send end message");
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ------ 谱面相关
|
|
|
+// 将displayScroll view 显示的当前音符移动到显示的中间位置
|
|
|
+- (void)moveDisplayViewDisplay {
|
|
|
+
|
|
|
+ KSParseMessageModel *currentNode = self.manager.nodeMessageArray[self.lastPlayNodeIndex];
|
|
|
+
|
|
|
+ CGFloat positionY = (currentNode.positionY + currentNode.elementHeight / 2.0f) / PositionRate * self.scaleRate + currentNode.pageIndex * self.pageViewHeight;
|
|
|
+ CGFloat offsetY = 0.0f;
|
|
|
+ if (positionY < CGRectGetHeight(self.displayScrollView.frame) / 2.0f) {
|
|
|
+ offsetY = 0.0f;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ offsetY = positionY - CGRectGetHeight(self.displayScrollView.frame) / 2.0f;
|
|
|
+ }
|
|
|
+ [self.displayScrollView setContentOffset:CGPointMake(0, offsetY)];
|
|
|
+}
|
|
|
+
|
|
|
+// 渲染当前小节和评分
|
|
|
+- (void)displayScore:(NSInteger)score measureIndex:(NSInteger)measureIndex {
|
|
|
+ for (KSParseMessageModel *model in self.manager.measureMessageArray) {
|
|
|
+ if (model.elementIndex == measureIndex) {
|
|
|
+ KSMeasureLocationModel *measureLocation = [[KSMeasureLocationModel alloc] init];
|
|
|
+ measureLocation.positionX = model.positionX / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.positionY = model.positionY / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.measureWidth = model.elementWidth / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.measureHeight = model.elementHeight / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.pageIndex = model.pageIndex;
|
|
|
+
|
|
|
+ measureLocation.fillColor = [self getDisplayColorWithScore:score sourceLocation:measureLocation];
|
|
|
+
|
|
|
+ NSInteger index = model.pageIndex;
|
|
|
+ StaffImageDisplayView *view = self.pageArray[index];
|
|
|
+ [view addColorView:measureLocation];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (UIColor *)getDisplayColorWithScore:(NSInteger)score sourceLocation:(KSMeasureLocationModel *)model {
|
|
|
+ UIColor *measureBgColor;
|
|
|
+ UIColor *titleColor;
|
|
|
+ NSString *imageName = @"";
|
|
|
+
|
|
|
+ if (score >= 90) { // perfect
|
|
|
+ measureBgColor = HexRGBAlpha(0xffd4c0, 0.47f);
|
|
|
+
|
|
|
+ titleColor = HexRGB(0x516AFF);
|
|
|
+ imageName = @"score_perfect";
|
|
|
+ }
|
|
|
+ else if (score >= 70) { // great
|
|
|
+ measureBgColor = HexRGBAlpha(0xffb252, 0.48f);
|
|
|
+ titleColor = HexRGB(0xFF8E5A);
|
|
|
+ imageName = @"score_great";
|
|
|
+ }
|
|
|
+ else if (score >= 40) { // good
|
|
|
+ measureBgColor = HexRGBAlpha(0x01c1b5, 0.17f);
|
|
|
+ titleColor = HexRGB(0xFF958B);
|
|
|
+ imageName = @"score_good";
|
|
|
+ }
|
|
|
+ else { // bad
|
|
|
+ measureBgColor = HexRGBAlpha(0xff8e8e, 0.32f);
|
|
|
+ titleColor = HexRGB(0xEE4C6A);
|
|
|
+ imageName = @"score_bad";
|
|
|
+ }
|
|
|
+ CGFloat viewHeight = 40.0f;
|
|
|
+ CGRect viewFrame = CGRectMake(model.positionX, model.positionY+viewHeight, model.measureWidth, viewHeight);
|
|
|
+ [self showScoreViewInPage:model.pageIndex frame:viewFrame titleColor:titleColor score:score imageName:imageName];
|
|
|
+
|
|
|
+ return measureBgColor;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showScoreViewInPage:(NSInteger)pageIndex frame:(CGRect)frame titleColor:(UIColor *)titleColor score:(NSInteger)score imageName:(NSString *)imageName {
|
|
|
+ CGFloat distanceY = 20.0f;
|
|
|
+ ScoreAnimationView *view = [[ScoreAnimationView alloc] initWithFrame:frame animitionDistance:distanceY score:score titleColor:titleColor imageName:imageName];
|
|
|
+
|
|
|
+ StaffImageDisplayView *pageView = self.pageArray[pageIndex];
|
|
|
+ [view showInView:pageView];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ------ 评测结果处理
|
|
|
+- (void)showReportView {
|
|
|
+
|
|
|
+ KSBaseWKWebViewController *reportCtrl = [[KSBaseWKWebViewController alloc] init];
|
|
|
+ reportCtrl.url = [NSString stringWithFormat:@"%@/accompany/#/report/%zd",WEBHOST,self.recordId];
|
|
|
+ reportCtrl.hiddenNavBar = YES;
|
|
|
+ self.keepOrientation = YES;
|
|
|
+ reportCtrl.keepOrientation = YES;
|
|
|
+ [self.navigationController pushViewController:reportCtrl animated:YES];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)resulteAlertAction:(EVALUATE_RESULT_ACTION)action {
|
|
|
+ switch (action) {
|
|
|
+ case EVALUATE_RESULT_ACTION_CANCLE: // 退出评测模式
|
|
|
+ {
|
|
|
+ [self.resultAlert removeFromSuperview];
|
|
|
+ [self quitEvaluatingMode];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case EVALUATE_RESULT_ACTION_SHARE: // 分享
|
|
|
+ {
|
|
|
+ // share function
|
|
|
+
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case EVALUATE_RESULT_ACTION_SUBMIT: // 上传到云端
|
|
|
+ {
|
|
|
+ if (self.cameraOn && self.saveVideo) { // 上传视频到云端
|
|
|
+ [self videoUpdate];
|
|
|
+ }
|
|
|
+ else { // 直接发送消息显示音频
|
|
|
+ [self sendUploadMessage:nil];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case EVALUATE_RESULT_ACTION_PRACTICE: // 练习
|
|
|
+ {
|
|
|
+ [self.resultAlert removeFromSuperview];
|
|
|
+ [self quitEvaluatingMode];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case EVALUATE_RESULT_ACTION_TAYAGAIN: // 再试一次
|
|
|
+ {
|
|
|
+ [self.resultAlert removeFromSuperview];
|
|
|
+ [self clearAllColorDisplayView];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case EVALUATE_RESULT_ACTION_REPORT: // 报告
|
|
|
+ {
|
|
|
+ [self showReportView];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ------ recorder
|
|
|
+// 初始化录制引擎
|
|
|
+- (void)configRecordManager {
|
|
|
+ self.AQManager = [[KSAQRecordManager alloc] init];
|
|
|
+ self.AQManager.delegate = self;
|
|
|
+}
|
|
|
+// 开始录制
|
|
|
+- (void)startRecordService {
|
|
|
+ if (self.AQManager.isRunning) {
|
|
|
+ [self.AQManager stopRecord];
|
|
|
+ }
|
|
|
+ [self.AQManager startRecord];
|
|
|
+}
|
|
|
+// 停止录制
|
|
|
+- (void)stopRecordService {
|
|
|
+ if (self.AQManager.isRunning) {
|
|
|
+ [self.AQManager stopRecord];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark-------- KSAQRecordManagerDelegate
|
|
|
+- (void)audioRecordInterruption {
|
|
|
+
|
|
|
+ [self cancelEvaluating];
|
|
|
+ [self showErrorMessage:@"录制错误,请重试"];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)audioRouteChange:(AUDIODEVICE_TYPE)type {
|
|
|
+ NSString *valueStr = @"";
|
|
|
+ BOOL checkIsWired = NO;
|
|
|
+ switch (type) {
|
|
|
+ case AUDIODEVICE_TYPE_HEADPHONE:
|
|
|
+ {
|
|
|
+ valueStr = @"有线耳机";
|
|
|
+ checkIsWired = YES;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case AUDIODEVICE_TYPE_BLUETOOTH:
|
|
|
+ {
|
|
|
+ valueStr = @"蓝牙耳机";
|
|
|
+ checkIsWired = YES;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case AUDIODEVICE_TYPE_NONE:
|
|
|
+ {
|
|
|
+ valueStr = @"";
|
|
|
+ checkIsWired = NO;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (checkIsWired == NO) {
|
|
|
+ [self showNoWiredTipsAlertCallback:^{
|
|
|
+
|
|
|
+ }];
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)audioRecord:(KSAQRecordManager *)audioRecord didRecordAudioData:(void *)data length:(UInt32)length {
|
|
|
+
|
|
|
+ if (self.socketManager.socketReadyState != SR_OPEN) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSData *pushData = [[NSData alloc] initWithBytes:data length:length];
|
|
|
+ if (self.isCompareStart) { // 发送评测开始消息
|
|
|
+ NSLog(@"--------- send start message");
|
|
|
+ _isCompareStart = NO;
|
|
|
+ NSString *startMessage = @"recordStart";
|
|
|
+ NSString *startString = [self configDataCommond:startMessage body:nil type:@"SOUND_COMPARE"];
|
|
|
+ [self sendDataToSocketService:startString];
|
|
|
+ }
|
|
|
+ else if (self.isSoundCheckStart) { // 校音开始
|
|
|
+
|
|
|
+ NSLog(@"--------- send check start message");
|
|
|
+ _isSoundCheckStart = NO;
|
|
|
+ NSString *checkStartMessage = @"start";
|
|
|
+ NSString *startString = [self configDataCommond:checkStartMessage body:nil type:@"PITCH_DETECTION"];
|
|
|
+ [self sendDataToSocketService:startString];
|
|
|
+ }
|
|
|
+ [self sendDataToSocketService:pushData];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark --- player 播放引擎 播放和停止播放UI
|
|
|
+// 初始化播放引擎
|
|
|
+- (void)configPlayerEngineWithSong:(NSString *)songPath {
|
|
|
+ [self showhud];
|
|
|
+ self.playerEngine = [[MidiPlayerEngine alloc] init];
|
|
|
+ self.playerEngine.accompanyVolume = 0.3f;
|
|
|
+ self.playerEngine.reportTime = 10;
|
|
|
+ self.playerEngine.delegate = self;
|
|
|
+ [self.playerEngine configSoundFilePath:[[NSBundle mainBundle]
|
|
|
+ pathForResource:@"synthgms" ofType:@"sf2"]];
|
|
|
+ // 获取当前主声部对应的乐器id
|
|
|
+ self.playerEngine.instrumentId = 73;
|
|
|
+ [self.playerEngine loadMIDIFileWithString:songPath];
|
|
|
+
|
|
|
+
|
|
|
+ NSLog(@"--------- instanment track --- %d", self.playerEngine.baseInstrumentTrack);
|
|
|
+// [self.playerEngine volumeOtherTrackExcept:self.playerEngine.baseInstrumentTrack withVolume:0];
|
|
|
+ if (self.currentSpeed == 0) {
|
|
|
+ self.currentSpeed = self.playerEngine.baseRate;
|
|
|
+ }
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ [self MBPShow:@"加载完成"];
|
|
|
+ [self removehub];
|
|
|
+ CloudControlButton *speedButton = [self.headView viewWithTag:BUTTON_TAG_SPEED];
|
|
|
+ [speedButton showSpeed:self.currentSpeed];
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/* 播放时,显示光标 曲谱不可手动滑动,根据播放进度自动移动位置
|
|
|
+ 停止播放时,隐藏光标,光标回到初始位置,曲谱滑动禁用关闭。
|
|
|
+ */
|
|
|
+- (void)playAction { // 播放
|
|
|
+ CloudControlButton *playButton = [self.headView viewWithTag:BUTTON_TAG_PLAY];
|
|
|
+ playButton.isSelected = YES;
|
|
|
+ self.isPlaying = YES;
|
|
|
+ self.displayScrollView.scrollEnabled = NO;
|
|
|
+ [self showCursor];
|
|
|
+ if ([self.playerEngine isPlayingFile]) {
|
|
|
+ [self.playerEngine stopPlayingMIDIFile];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将displayScroll view 显示的当前音符移动到显示的中间位置
|
|
|
+ [self moveDisplayViewDisplay];
|
|
|
+
|
|
|
+ if (self.startMeasure != -1) {
|
|
|
+
|
|
|
+ [self.playerEngine setProgressTime:self.playerEngine.startTime];
|
|
|
+ [self.playerEngine playMIDIFile];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.playerEngine playMIDIFile];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)stopPlayAction { // 停止播放
|
|
|
+ self.isPlaying = NO;
|
|
|
+ [self hideCursor];
|
|
|
+ self.displayScrollView.scrollEnabled = YES;
|
|
|
+ if ([self.playerEngine isPlayingFile]) {
|
|
|
+ [self.playerEngine stopPlayingMIDIFile];
|
|
|
+ }
|
|
|
+ CloudControlButton *playButton = [self.headView viewWithTag:BUTTON_TAG_PLAY];
|
|
|
+ playButton.isSelected = NO;
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ---- PlayerEngineDelegate
|
|
|
+- (void)ProgressUpdated:(float)progress currentTime:(MusicTimeStamp)currentTime {
|
|
|
+
|
|
|
+ // 如果是最后一个节点
|
|
|
+ if (self.lastPlayNodeIndex == self.manager.nodeMessageArray.count - 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ KSParseMessageModel *nextNode = self.manager.nodeMessageArray[self.lastPlayNodeIndex+1];
|
|
|
+ if (nextNode.elementTimePosition <= currentTime*1000) {
|
|
|
+ KSParseMessageModel *currentNode = self.manager.nodeMessageArray[self.lastPlayNodeIndex];
|
|
|
+
|
|
|
+ self.lastPlayNodeIndex +=1;
|
|
|
+ [self moveCursorViewWithNode:nextNode];
|
|
|
+ // 如果换行或者到下一页
|
|
|
+ if (currentNode.positionY < nextNode.positionY || currentNode.pageIndex < nextNode.pageIndex) {
|
|
|
+ [self moveDisplayViewDisplay];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)playEnd {
|
|
|
+ // 判断是否需要重播
|
|
|
+ if (_isNeedRepeat && _isPlaying && _isEvaluating == NO) {
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ [self stopPlayAction];
|
|
|
+ self.lastPlayNodeIndex = 0;
|
|
|
+ [self showBeatBeforePlay];
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (_isEvaluating) { // 如果在评测状态下
|
|
|
+ [self endEvaluating];
|
|
|
+ }
|
|
|
+ [self resetPlayButtonStatus];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)resetPlayButtonStatus {
|
|
|
+ [self.playerEngine setProgressTime:0.0f];
|
|
|
+ CloudControlButton *playButton = [self.headView viewWithTag:BUTTON_TAG_PLAY];
|
|
|
+ playButton.isSelected = NO;
|
|
|
+ self.lastPlayNodeIndex = 0;
|
|
|
+ [self hideCursor];
|
|
|
+ self.displayScrollView.scrollEnabled = YES;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)GetMusicTotalTime:(float)time {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)quitEvaluatingMode {
|
|
|
+ self.isEvaluating = NO;
|
|
|
+ [self clearAllColorDisplayView];
|
|
|
+ CloudControlButton *evaluateButton = [self.headView viewWithTag:BUTTON_TAG_EVALUATING];
|
|
|
+ evaluateButton.isSelected = NO;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)showErrorMessage:(NSString *)message {
|
|
|
+ [self MBPShow:message];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)displayView {
|
|
|
+ [self.view addSubview:self.viewContainer];
|
|
|
+ _backView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight)];
|
|
|
+ _backView.backgroundColor = HexRGB(0x01c1b5);
|
|
|
+ [self.view addSubview:_backView];
|
|
|
+ _bgColorView = [[UIView alloc] initWithFrame:self.defaultFrame];
|
|
|
+ _bgColorView.layer.cornerRadius = 14.0f;
|
|
|
+ [self.backView addSubview:_bgColorView];
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ if (self.cameraOn) {
|
|
|
+ self.backView.alpha = BACKVIEW_ALPHA;
|
|
|
+ [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ _headView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KLandscapeWidth, 78)];
|
|
|
+ _headView.backgroundColor = [UIColor clearColor];
|
|
|
+ [self.backView addSubview:_headView];
|
|
|
+ // 配置顶部按钮
|
|
|
+ [self configButtonInView:_headView];
|
|
|
+
|
|
|
+ // 创建右侧帮助按钮
|
|
|
+ [self createHelpButton];
|
|
|
+
|
|
|
+ // 创建scorll view 容器添加图片
|
|
|
+ [self createDisplayScrollView];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- (void)createHelpButton {
|
|
|
+ self.helpButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
+ self.helpButton.frame = CGRectMake(KLandscapeWidth - 46, (KLandscapeHeight - 86) /2.0f, 46, 86);
|
|
|
+ [self.helpButton setImage:[UIImage imageNamed:@"cloud_help"] forState:UIControlStateNormal];
|
|
|
+ [self.helpButton addTarget:self action:@selector(helpAction) forControlEvents:UIControlEventTouchUpInside];
|
|
|
+ [self.view addSubview:self.helpButton];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)helpAction {
|
|
|
+ CloudHelpView *helpView = [CloudHelpView shareInstance];
|
|
|
+ helpView.frame = CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight);
|
|
|
+ MJWeakSelf;
|
|
|
+ [helpView feedbackCallback:^{
|
|
|
+ [weakSelf showFeedbackUI];
|
|
|
+ }];
|
|
|
+ [self.view addSubview:helpView];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showFeedbackUI {
|
|
|
+ MJWeakSelf;
|
|
|
+ self.feedbackView = [CloudFeedbackView shareInstance];
|
|
|
+ self.feedbackView.frame = CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight);
|
|
|
+ [self.feedbackView submitCallback:^(NSString * _Nonnull content, NSString * _Nonnull type) {
|
|
|
+ [weakSelf submitFeedbackAction:content type:type];
|
|
|
+ }];
|
|
|
+ [self.view addSubview:self.feedbackView];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)submitFeedbackAction:(NSString *)content type:(NSString *)type {
|
|
|
+ [self showhud];
|
|
|
+ [KSNetworkingManager cloudFeedbackRequest:KS_POST content:content type:type success:^(NSDictionary * _Nonnull dic) {
|
|
|
+ [self removehub];
|
|
|
+ if ([dic integerValueForKey:@"code"] == 200 && [dic boolValueForKey:@"status"]) {
|
|
|
+ [self MBPShow:@"反馈成功"];
|
|
|
+ [self.feedbackView removeFromSuperview];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self MBPShow:MESSAGEKEY];
|
|
|
+ }
|
|
|
+ } faliure:^(NSError * _Nonnull error) {
|
|
|
+ [self removehub];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)createDisplayScrollView {
|
|
|
+ [self.pageArray removeAllObjects];
|
|
|
+ if (self.needShowFinger) {
|
|
|
+ self.displayScrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.resizeFrame), CGRectGetHeight(self.resizeFrame));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.displayScrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.defaultFrame), CGRectGetHeight(self.defaultFrame));
|
|
|
+ }
|
|
|
+ [self.bgColorView addSubview:self.displayScrollView];
|
|
|
+ // 获取当前文件夹中所有图片
|
|
|
+ NSMutableArray *imgArray = [NSMutableArray array];
|
|
|
+ for (NSString *filePath in self.zipFileArray) {
|
|
|
+ if ([filePath containsString:@".png"]) {
|
|
|
+ [imgArray addObject:filePath];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+// NSLog(@"%@", imgArray.description);
|
|
|
+// for (int i = 1; i < 6; i++) {
|
|
|
+// NSString *name = [NSString stringWithFormat:@"mus-%d",i];
|
|
|
+// [imgArray addObject:[[NSBundle mainBundle] pathForResource:name ofType:@"png"]];
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ UIImage *img = [UIImage imageNamed:imgArray[0]];
|
|
|
+ CGFloat width = img.size.width;
|
|
|
+ CGFloat height = img.size.height;
|
|
|
+ CGFloat displayWidth = CGRectGetWidth(self.displayScrollView.frame);
|
|
|
+
|
|
|
+ self.scaleRate = displayWidth / width;
|
|
|
+ CGFloat viewHeight = self.scaleRate * height;
|
|
|
+ self.pageViewHeight = viewHeight;
|
|
|
+
|
|
|
+ self.bgColorType = self.eyeShieldOn ? COLOR_DISPLAYTYPE_PROTECT : COLOR_DISPLAYTYPE_NOMAL;
|
|
|
+
|
|
|
+ for (NSInteger i = 0; i < imgArray.count; i++) { // 创建 StaffImageDisplayView
|
|
|
+
|
|
|
+ StaffImageDisplayView *displayView = [[StaffImageDisplayView alloc] initWithImage:imgArray[i] pageIndex:i frame:CGRectMake(0, i * viewHeight, displayWidth, viewHeight)];
|
|
|
+ displayView.tag = 1000 + i;
|
|
|
+ MJWeakSelf;
|
|
|
+ [displayView viewTapActionCallback:^(CGFloat positionX, CGFloat positionY, NSInteger pageIndex) {
|
|
|
+ [weakSelf dealWithPositionX:positionX positionY:positionY pageIndex:pageIndex];
|
|
|
+ }];
|
|
|
+ [self.pageArray addObject:displayView];
|
|
|
+ [self.displayScrollView addSubview:displayView];
|
|
|
+ }
|
|
|
+ CGFloat contentHeight = viewHeight * imgArray.count;
|
|
|
+ if (contentHeight < self.displayScrollView.bounds.size.height) {
|
|
|
+ contentHeight = self.displayScrollView.bounds.size.height;
|
|
|
+ }
|
|
|
+ self.displayScrollView.contentSize = CGSizeMake(displayWidth, contentHeight);
|
|
|
+
|
|
|
+ if (self.needShowFinger) {
|
|
|
+ [self.bgColorView addSubview:self.fingerDisplayView];
|
|
|
+ }
|
|
|
+ if (self.isPlaying) {
|
|
|
+ self.displayScrollView.scrollEnabled = NO;
|
|
|
+ [self.displayScrollView addSubview:self.cursorView];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.displayScrollView.scrollEnabled = YES;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- (void)configButtonInView:(UIView *)displayView {
|
|
|
+ UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
+ backButton.frame = CGRectMake(21, 19, 32, 32);
|
|
|
+ backButton.layer.cornerRadius = 16.0f;
|
|
|
+ [backButton setImage:[UIImage imageNamed:@"cloud_back"] forState:UIControlStateNormal];
|
|
|
+ [backButton setTitleColor:HexRGB(0x000000) forState:UIControlStateNormal];
|
|
|
+ backButton.backgroundColor = [UIColor whiteColor];
|
|
|
+ [backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
|
|
|
+ [displayView addSubview:backButton];
|
|
|
+
|
|
|
+ if (![NSString isEmptyString:self.songName]) {
|
|
|
+ UIView *songNameView = [[UIView alloc] initWithFrame:CGRectMake(70, 17, 180, 40)];
|
|
|
+ songNameView.backgroundColor = [UIColor whiteColor];
|
|
|
+ songNameView.layer.cornerRadius = 20.0f;
|
|
|
+ [displayView addSubview:songNameView];
|
|
|
+
|
|
|
+ UIImageView *logoImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudSong_logo"]];
|
|
|
+ logoImage.frame = CGRectMake(7, 5, 28, 28);
|
|
|
+ [songNameView addSubview:logoImage];
|
|
|
+
|
|
|
+ UIImageView *nextImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudSong_next"]];
|
|
|
+ nextImage.frame = CGRectMake(CGRectGetWidth(songNameView.frame) - 9 - 14, 14, 9, 12);
|
|
|
+ [songNameView addSubview:nextImage];
|
|
|
+
|
|
|
+ UILabel *songLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(logoImage.frame) + 6, 0, CGRectGetMinX(nextImage.frame) - 5 - CGRectGetMaxX(logoImage.frame) - 6, 40)];
|
|
|
+ songLabel.textAlignment = NSTextAlignmentLeft;
|
|
|
+ songLabel.textColor = HexRGB(0x000000);
|
|
|
+ songLabel.font = [UIFont systemFontOfSize:16.0f weight:UIFontWeightMedium];
|
|
|
+ songLabel.text = self.songName;
|
|
|
+ [songNameView addSubview:songLabel];
|
|
|
+ }
|
|
|
+
|
|
|
+ NSArray *imageArray = @[@"evaluate_nomal",@"select_normal",@"play_play",@"switch_nomal",@"cloud_playback",@"cloud_speed",@"cloud_setting"];
|
|
|
+ NSArray *selectImageArray = @[@"evaluate_selected",@"select_choose",@"play_pause",@"switch_accompany",@"cloud_playback",@"cloud_speed",@"cloud_setting"];
|
|
|
+ NSArray *titleArray = @[@"评测",@"选段",@"播放",@"原声",@"重播",@"速度",@"设置"];
|
|
|
+ NSArray *selectTitleArray = @[@"评测",@"选段",@"暂停",@"伴奏",@"重播",@"速度",@"设置"];
|
|
|
+
|
|
|
+ for (NSInteger i = imageArray.count - 1; i >= 0; i--) {
|
|
|
+ CGRect frame = CGRectMake((KLandscapeWidth - 46) - (imageArray.count - i) * Button_Width - (imageArray.count - 1 - i)*Button_Space, 9, 34, 60);
|
|
|
+ MJWeakSelf;
|
|
|
+ CloudControlButton *button = [CloudControlButton createButtonWithImage:imageArray[i] selectImage:selectImageArray[i] buttonTitle:titleArray[i] selectTitle:selectTitleArray[i] tag:1000+i frame:frame callback:^(CloudControlButton * _Nonnull sender, BOOL isSelected) {
|
|
|
+ [weakSelf buttonClickAction:sender isSelected:isSelected];
|
|
|
+ }];
|
|
|
+ if (i == 5 && self.currentSpeed != 0) {
|
|
|
+ [button showSpeed:self.currentSpeed];
|
|
|
+ }
|
|
|
+ [displayView addSubview:button];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)buttonClickAction:(CloudControlButton *)sender isSelected:(BOOL)isSelected {
|
|
|
+ BUTTON_TAG type = sender.tag;
|
|
|
+ switch (type) {
|
|
|
+ case BUTTON_TAG_EVALUATING: // 评测开关
|
|
|
+ {
|
|
|
+ if (isSelected) { // 开启时设置其他按钮禁用 重新布局曲谱页面
|
|
|
+ self.isEvaluating = YES;
|
|
|
+
|
|
|
+ MJWeakSelf;
|
|
|
+ [self checkIsWiredHeadsetOnCallback:^{
|
|
|
+ if (weakSelf.soundCheckOn && ![weakSelf.songMessageSource.subjectId isEqualToString:@"23"]) { // 如果是打击乐跳过
|
|
|
+ [weakSelf startSoundCheck];
|
|
|
+ }
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else { // 恢复按钮 重新恢复曲谱布局
|
|
|
+ self.isEvaluating = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case BUTTON_TAG_SELECT:
|
|
|
+ {
|
|
|
+ if (isSelected) {
|
|
|
+ self.isChooseMeasure = YES;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self stopPlayAction];
|
|
|
+ self.isChooseMeasure = NO;
|
|
|
+ // 清理全部
|
|
|
+ self.startMeasure = -1;
|
|
|
+ self.endMeasure = -1;
|
|
|
+ self.lastPlayNodeIndex = 0;
|
|
|
+ self.playerEngine.startTime = 0.0f;
|
|
|
+ self.playerEngine.endTime = 0.0f;
|
|
|
+ [self.playerEngine setProgressTime:self.playerEngine.startTime];
|
|
|
+ [self clearAllColorDisplayView];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case BUTTON_TAG_PLAY:
|
|
|
+ {
|
|
|
+ if (isSelected) { // 播放
|
|
|
+
|
|
|
+ CGFloat rate = self.currentSpeed / self.playerEngine.baseRate;
|
|
|
+ [self.playerEngine setMusicPlayerSpeed:rate];
|
|
|
+ [self showBeatBeforePlay];
|
|
|
+ }
|
|
|
+ else { // 暂停
|
|
|
+ [self stopPlayAction];
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case BUTTON_TAG_SWITCH:
|
|
|
+ {
|
|
|
+ [self.trackChooseView showInView:self.view];
|
|
|
+// if (isSelected) { // 切换成伴奏
|
|
|
+// [self.playerEngine muteTrack:YES WithTrack:self.playerEngine.baseInstrumentArray];
|
|
|
+// }
|
|
|
+// else { // 切换成原声
|
|
|
+// [self.playerEngine muteTrack:NO WithTrack:self.playerEngine.baseInstrumentArray];
|
|
|
+// }
|
|
|
+
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case BUTTON_TAG_REPEAT: // 从头开始播放
|
|
|
+ {
|
|
|
+ [self.playerEngine setProgressTime:0.0f];
|
|
|
+ [self displayCourseToFirstNode];
|
|
|
+ CloudControlButton *playButton = [self.headView viewWithTag:BUTTON_TAG_PLAY];
|
|
|
+ playButton.isSelected = YES;
|
|
|
+ [self showBeatBeforePlay];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case BUTTON_TAG_SPEED: // 调速
|
|
|
+ {
|
|
|
+ // 显示调速控件
|
|
|
+ [self showSpeedView];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case BUTTON_TAG_SETTING: // 设置
|
|
|
+ {
|
|
|
+ [self showSettingView];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showSpeedView {
|
|
|
+ LFPopupMenu *popView = [[LFPopupMenu alloc] init];
|
|
|
+ popView.needBorder = YES;
|
|
|
+ popView.minWidth = 80;
|
|
|
+ [popView configWithCustomView:self.sliderView];
|
|
|
+
|
|
|
+ CloudControlButton *playButton = [self.headView viewWithTag:BUTTON_TAG_SPEED];
|
|
|
+ [popView showArrowToView:playButton];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showSettingView {
|
|
|
+ KSCloudSettingView *settingView = [KSCloudSettingView shareInstance];
|
|
|
+ settingView.frame = CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight);
|
|
|
+ // 设置回调
|
|
|
+ MJWeakSelf;
|
|
|
+ [settingView settingCallback:^(CLOUDSETTING_TYPE type, BOOL isOn, NSString * _Nonnull level) {
|
|
|
+ [weakSelf configViewDisplayType:type isOn:isOn level:level];
|
|
|
+ }];
|
|
|
+ [self.view addSubview:settingView];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setBgColorType:(COLOR_DISPLAYTYPE)bgColorType {
|
|
|
+ _bgColorType = bgColorType;
|
|
|
+ if (bgColorType == COLOR_DISPLAYTYPE_PROTECT) {
|
|
|
+ self.bgColorView.backgroundColor = HexRGB(0xfff4e1);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.bgColorView.backgroundColor = [UIColor whiteColor];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)configViewDisplayType:(CLOUDSETTING_TYPE)type isOn:(BOOL)isOn level:(NSString *)level {
|
|
|
+ switch (type) {
|
|
|
+ case CLOUDSETTING_TYPE_EYESHIELD: // 护眼模式
|
|
|
+ {
|
|
|
+ self.eyeShieldOn = isOn;
|
|
|
+ self.bgColorType = isOn ? COLOR_DISPLAYTYPE_PROTECT : COLOR_DISPLAYTYPE_NOMAL;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CLOUDSETTING_TYPE_SOUNDCHECK:
|
|
|
+ {
|
|
|
+ self.soundCheckOn = isOn;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CLOUDSETTING_TYPE_CAMERA:
|
|
|
+ {
|
|
|
+ self.backView.alpha = isOn ? BACKVIEW_ALPHA : 1.0f;
|
|
|
+ self.cameraOn = isOn;
|
|
|
+ [self openCamera:isOn];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CLOUDSETTING_TYPE_REPEAT:
|
|
|
+ {
|
|
|
+ self.isNeedRepeat = isOn;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CLOUDSETTING_TYPE_FINGER:
|
|
|
+ {
|
|
|
+ self.showFinger = isOn;
|
|
|
+ // 刷新页面布局
|
|
|
+ [self displayFingerView];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CLOUDSETTING_TYPE_LEVEL:
|
|
|
+ {
|
|
|
+ self.level = level;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CLOUDSETTING_TYPE_SAVEVIDEO:
|
|
|
+ {
|
|
|
+ self.saveVideo = isOn;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CLOUDSETTING_TYPE_ACCOMPANY:
|
|
|
+ {
|
|
|
+ self.accompanyOn = isOn;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)displayFingerView {
|
|
|
+ if (self.needShowFinger) {
|
|
|
+ [self.displayScrollView removeFromSuperview];
|
|
|
+ _displayScrollView = nil;
|
|
|
+ [self createDisplayScrollView];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.fingerDisplayView removeFromSuperview];
|
|
|
+ [self.displayScrollView removeFromSuperview];
|
|
|
+ _displayScrollView = nil;
|
|
|
+ [self createDisplayScrollView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)openCamera:(BOOL)isOpen {
|
|
|
+ if (isOpen) { // 开启摄像头
|
|
|
+ PREMISSIONTYPE canOpenCamera = [RecordCheckManager checkCameraPremissionAvaiable:YES showInView:self.view];
|
|
|
+ if (canOpenCamera == PREMISSIONTYPE_YES) {
|
|
|
+ [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (self->_videoRecordManager) {
|
|
|
+ [self.videoRecordManager removeDisplay];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)dealWithPositionX:(CGFloat)positionX positionY:(CGFloat)positionY pageIndex:(NSInteger)pageIndex {
|
|
|
+ // 如果不是选段模式 不计算
|
|
|
+ if (!self.isChooseMeasure) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找到当前选择的小节
|
|
|
+ // 获取当前图片上对应的x,y所对应的位置文件中的 点
|
|
|
+ CGFloat locationX = positionX / self.scaleRate * PositionRate;
|
|
|
+ CGFloat locationY = positionY / self.scaleRate * PositionRate;
|
|
|
+
|
|
|
+ for (KSParseMessageModel *model in self.manager.measureMessageArray) {
|
|
|
+
|
|
|
+ if (model.pageIndex == pageIndex) { // 先找到当前的page数据
|
|
|
+ // 判断是否落在小节X轴范围区间
|
|
|
+ BOOL isLocationInX = (model.positionX < locationX && (model.positionX + model.elementWidth) >= locationX);
|
|
|
+ // 判断是否落在小节Y轴范围区间
|
|
|
+ BOOL isLocationInY = (model.positionY < locationY && (model.positionY + model.elementHeight) >= locationY);
|
|
|
+ if (isLocationInX && isLocationInY) { // 认为当前小节是被选中小节
|
|
|
+ NSInteger chooseMeasure = model.elementIndex;
|
|
|
+ [self evaluateChooseMeasure:chooseMeasure];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)evaluateChooseMeasure:(NSInteger)chooseMeasure {
|
|
|
+ if (self.startMeasure == -1 && self.endMeasure == -1) {
|
|
|
+ self.startMeasure = chooseMeasure;
|
|
|
+ self.endMeasure = chooseMeasure;
|
|
|
+ }
|
|
|
+ if (self.startMeasure > chooseMeasure) {
|
|
|
+ self.startMeasure = chooseMeasure;
|
|
|
+ }
|
|
|
+ else if (self.startMeasure < chooseMeasure) {
|
|
|
+ self.endMeasure = chooseMeasure;
|
|
|
+ }
|
|
|
+ // 获取选段时间
|
|
|
+ NSInteger index = self.startMeasure;
|
|
|
+ BOOL chooseEndMeasure = NO;
|
|
|
+ if (self.endMeasure == self.manager.measureMessageArray.count-1) {
|
|
|
+ chooseEndMeasure = YES;
|
|
|
+ }
|
|
|
+ NSInteger endIndex = chooseEndMeasure ? self.endMeasure : self.endMeasure+1;
|
|
|
+ // 1.根据小节找到小节开始的时值
|
|
|
+ for (KSParseMessageModel *model in self.manager.measureMessageArray) {
|
|
|
+ if (index == model.elementIndex) {
|
|
|
+ self.playerEngine.startTime = model.elementTimePosition / 1000;
|
|
|
+ }
|
|
|
+ else if (chooseEndMeasure) { // 如果结束是最后一小节
|
|
|
+ self.playerEngine.endTime = 0.0f;
|
|
|
+ }
|
|
|
+ // 如果不是最后一个小节
|
|
|
+ else if (!chooseEndMeasure && endIndex == model.elementIndex) {
|
|
|
+ self.playerEngine.endTime = model.elementTimePosition / 1000;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新绘制layer颜色
|
|
|
+ NSMutableArray *colorViewArray = [NSMutableArray array];
|
|
|
+
|
|
|
+ for (KSParseMessageModel *model in self.manager.measureMessageArray) {
|
|
|
+ if (model.elementIndex >= self.startMeasure && model.elementIndex <= self.endMeasure) {
|
|
|
+
|
|
|
+ KSMeasureLocationModel *measureLocation = [[KSMeasureLocationModel alloc] init];
|
|
|
+ measureLocation.positionX = model.positionX / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.positionY = model.positionY / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.measureWidth = model.elementWidth / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.measureHeight = model.elementHeight / PositionRate * self.scaleRate;
|
|
|
+ measureLocation.pageIndex = model.pageIndex;
|
|
|
+ measureLocation.fillColor = HexRGB(0xffe7bd);
|
|
|
+ [colorViewArray addObject:measureLocation];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 先清除全部color
|
|
|
+ [self clearAllColorDisplayView];
|
|
|
+ // 添加
|
|
|
+ for (KSMeasureLocationModel *model in colorViewArray) {
|
|
|
+ NSInteger index = model.pageIndex;
|
|
|
+ StaffImageDisplayView *view = self.pageArray[index];
|
|
|
+ [view addColorView:model];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)clearAllColorDisplayView {
|
|
|
+
|
|
|
+ for (StaffImageDisplayView *view in self.pageArray) {
|
|
|
+ [view clearAllColorView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (NSMutableArray *)getAllImageFile:(NSString *)path {
|
|
|
+ NSMutableArray *filePathArray = [NSMutableArray array];
|
|
|
+ NSFileManager *fileManger = [NSFileManager defaultManager];
|
|
|
+ BOOL isDir = NO;
|
|
|
+ BOOL isExist = [fileManger fileExistsAtPath:path isDirectory:&isDir];
|
|
|
+ if (isExist) {
|
|
|
+ if (isDir) {
|
|
|
+ NSArray * dirArray = [fileManger contentsOfDirectoryAtPath:path error:nil];
|
|
|
+ NSString * subPath = nil;
|
|
|
+ for (NSString * str in dirArray) {
|
|
|
+ subPath = [path stringByAppendingPathComponent:str];
|
|
|
+ BOOL issubDir = NO;
|
|
|
+ [fileManger fileExistsAtPath:subPath isDirectory:&issubDir];
|
|
|
+ [self getAllImageFile:subPath];
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ NSString *fileName = [[path componentsSeparatedByString:@"/"] lastObject];
|
|
|
+ if ([fileName hasSuffix:@".png"]) {
|
|
|
+ //do anything you want
|
|
|
+ [filePathArray addObject:path];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ NSLog(@"this path is not exist!");
|
|
|
+ }
|
|
|
+ return filePathArray;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)backAction {
|
|
|
+ [self dismissViewControllerAnimated:YES completion:nil];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)showFingerView {
|
|
|
+
|
|
|
+ if (self.needShowFinger && self.fingerImageDic != nil) {
|
|
|
+
|
|
|
+ NSMutableDictionary *parm = self.nodeArray[self.lastPlayNodeIndex];
|
|
|
+ NSInteger noteValue = [parm integerValueForKey:@"note_value"];
|
|
|
+
|
|
|
+ NSArray *noteFingerArray = [self.fingerImageDic arrayValueForKey:[NSString stringWithFormat:@"%zd",noteValue]];
|
|
|
+ // 剔除
|
|
|
+ [self.fingerContentView removeAllSubViews];
|
|
|
+
|
|
|
+ if (noteFingerArray.count) {
|
|
|
+ NSString *imgStr = [noteFingerArray firstObject];
|
|
|
+ NSArray *imgArray = [imgStr componentsSeparatedByString:@","];
|
|
|
+
|
|
|
+ for (NSString *imgName in imgArray) {
|
|
|
+ UIImageView *fingerView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.fingerContentView.frame), CGRectGetHeight(self.fingerContentView.frame))];
|
|
|
+ [fingerView setImage:[UIImage imageNamed:imgName]];
|
|
|
+ [self.fingerContentView addSubview:fingerView];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)showCursor { // 显示光标
|
|
|
+ // 获取当前播放位置
|
|
|
+ [self.displayScrollView addSubview:self.cursorView];
|
|
|
+ if (self.startMeasure == -1) {
|
|
|
+ if (self.lastPlayNodeIndex == 0) {
|
|
|
+ [self displayCourseToFirstNode];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else { // 获取当前选择小节
|
|
|
+
|
|
|
+ NSInteger index = self.startMeasure;
|
|
|
+ Float64 timePosition = 0;
|
|
|
+
|
|
|
+ // 1.根据小节找到小节开始的时值
|
|
|
+ for (KSParseMessageModel *model in self.manager.measureMessageArray) {
|
|
|
+ if (index == model.elementIndex) {
|
|
|
+ timePosition = model.elementTimePosition;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 2.根据时值获取对应音符位置
|
|
|
+ for (KSParseMessageModel *nodeModel in self.manager.nodeMessageArray) {
|
|
|
+
|
|
|
+ if (nodeModel.elementTimePosition == timePosition) {
|
|
|
+
|
|
|
+ [self moveCursorViewWithNode:nodeModel];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ self.cursorView.hidden = NO;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)hideCursor { // 隐藏光标
|
|
|
+ // 重置播放位置
|
|
|
+ self.cursorView.hidden = YES;
|
|
|
+ [self.cursorView removeFromSuperview];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)displayCourseToFirstNode {
|
|
|
+ // 获取第一个音符位置
|
|
|
+ KSParseMessageModel *firstNode = self.manager.nodeMessageArray[0];
|
|
|
+ [self moveCursorViewWithNode:firstNode];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)moveCursorViewWithNode:(KSParseMessageModel *)nodeModel {
|
|
|
+ self.lastPlayNodeIndex = nodeModel.elementIndex;
|
|
|
+ CGFloat positionX = nodeModel.positionX / PositionRate * self.scaleRate;
|
|
|
+ CGFloat positionY = nodeModel.positionY / PositionRate * self.scaleRate + self.pageViewHeight*(nodeModel.pageIndex);
|
|
|
+ CGFloat cursorWidth = nodeModel.elementWidth / PositionRate * self.scaleRate;
|
|
|
+ CGFloat cursorHeight = nodeModel.elementHeight / PositionRate * self.scaleRate;
|
|
|
+ self.cursorView.frame = CGRectMake(positionX, positionY, cursorWidth, cursorHeight);
|
|
|
+
|
|
|
+ // 指法同步
|
|
|
+ [self showFingerView];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark -------- lazying
|
|
|
+- (CGRect)defaultFrame {
|
|
|
+ return CGRectMake(Left_Space, 78, KLandscapeWidth - Left_Space*2, KLandscapeHeight - 78 - 10);
|
|
|
+}
|
|
|
+- (UIView *)cursorView {
|
|
|
+ if (!_cursorView) {
|
|
|
+ _cursorView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
|
+ _cursorView.hidden = YES;
|
|
|
+ _cursorView.backgroundColor = HexRGBAlpha(0xff5a25, 0.4f);
|
|
|
+ }
|
|
|
+ return _cursorView;
|
|
|
+}
|
|
|
+- (UIScrollView *)displayScrollView {
|
|
|
+ if (!_displayScrollView) {
|
|
|
+ _displayScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.defaultFrame), CGRectGetHeight(self.defaultFrame))];
|
|
|
+ _displayScrollView.backgroundColor = [UIColor clearColor];
|
|
|
+ _displayScrollView.scrollEnabled = YES;
|
|
|
+ _displayScrollView.showsHorizontalScrollIndicator = NO;
|
|
|
+ _displayScrollView.showsVerticalScrollIndicator = NO;
|
|
|
+ _displayScrollView.bounces = NO;
|
|
|
+ }
|
|
|
+ return _displayScrollView;
|
|
|
+}
|
|
|
+
|
|
|
+- (NSMutableArray *)pageArray {
|
|
|
+ if (!_pageArray) {
|
|
|
+ _pageArray = [NSMutableArray array];
|
|
|
+ }
|
|
|
+ return _pageArray;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (KSWebSocketManager *)socketManager {
|
|
|
+ if (!_socketManager) {
|
|
|
+ _socketManager = [[KSWebSocketManager alloc] init];
|
|
|
+ }
|
|
|
+ return _socketManager;
|
|
|
+}
|
|
|
+
|
|
|
+- (KSSliderView *)sliderView {
|
|
|
+ if (!_sliderView) {
|
|
|
+ _sliderView = [[KSSliderView alloc] initWithFrame:CGRectMake(0, 0, 50, 250)];
|
|
|
+ _sliderView.backgroundColor = [UIColor clearColor];
|
|
|
+ _sliderView.rateValue = self.currentSpeed;
|
|
|
+ _sliderView.isVertical = YES;
|
|
|
+ _sliderView.delegate = self;
|
|
|
+ }
|
|
|
+ return _sliderView;
|
|
|
+}
|
|
|
+
|
|
|
+- (NoWiredTipsAlert *)wiredAlert {
|
|
|
+ if (!_wiredAlert) {
|
|
|
+ _wiredAlert = [NoWiredTipsAlert shareInstance];
|
|
|
+ _wiredAlert.frame = CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight);
|
|
|
+ }
|
|
|
+ return _wiredAlert;
|
|
|
+}
|
|
|
+- (UIView *)viewContainer {
|
|
|
+ if (!_viewContainer) {
|
|
|
+ _viewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight)];
|
|
|
+ }
|
|
|
+ return _viewContainer;
|
|
|
+}
|
|
|
+
|
|
|
+- (KSVideoRecordManager *)videoRecordManager {
|
|
|
+ if (!_videoRecordManager) {
|
|
|
+ MJWeakSelf;
|
|
|
+ _videoRecordManager = [[KSVideoRecordManager alloc] initSessionRecordCallback:^(BOOL isSuccess, NSString * _Nullable message) {
|
|
|
+ if (isSuccess) {
|
|
|
+ [weakSelf showSuccessMessage:message];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (![NSString isEmptyString:message]) {
|
|
|
+ [weakSelf MBPShow:message];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _videoRecordManager;
|
|
|
+}
|
|
|
+
|
|
|
+- (UIButton *)evaluateButton {
|
|
|
+ if (!_evaluateButton) {
|
|
|
+ _evaluateButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
+ _evaluateButton.layer.cornerRadius = 22.0f;
|
|
|
+ [_evaluateButton setBackgroundColor:HexRGB(0x01c1b5)];
|
|
|
+ _evaluateButton.frame = CGRectMake((KLandscapeWidth - 150)/2.0f, KLandscapeHeight - 44 - 20, 150, 44);
|
|
|
+ [_evaluateButton.titleLabel setFont:[UIFont systemFontOfSize:18.0f weight:UIFontWeightMedium]];
|
|
|
+ [_evaluateButton setTitle:@"开始演奏" forState:UIControlStateNormal];
|
|
|
+ [_evaluateButton setTitle:@"√结束演奏" forState:UIControlStateSelected];
|
|
|
+ [_evaluateButton addTarget:self action:@selector(evaluateButtonAction:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
+ }
|
|
|
+ return _evaluateButton;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)evaluateButtonAction:(UIButton *)sender {
|
|
|
+ sender.selected = !sender.selected;
|
|
|
+ if (sender.isSelected) {
|
|
|
+ [self startEvaluateing];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self endEvaluating];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showSuccessMessage:(NSString *)message {
|
|
|
+ [self MBPShow:message];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ----- KSSliderView delegate
|
|
|
+- (void)rateChangeAction:(NSInteger)rate {
|
|
|
+ self.currentSpeed = rate;
|
|
|
+ CloudControlButton *playButton = [self.headView viewWithTag:BUTTON_TAG_SPEED];
|
|
|
+ [playButton showSpeed:rate];
|
|
|
+
|
|
|
+ // 速度写入本地文件中
|
|
|
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
+
|
|
|
+ NSString *filePath = [CloudSongMessageModel getSaveSpeedPath];
|
|
|
+ NSDictionary *speedDic = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
|
|
|
+ NSMutableDictionary *saveDic = [NSMutableDictionary dictionaryWithDictionary:speedDic];
|
|
|
+ [saveDic setValue:@(rate) forKey:self.songMessageSource.examSongId];
|
|
|
+ NSError *err = nil;
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ [saveDic writeToURL:[NSURL fileURLWithPath:filePath] error:&err];
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ [saveDic writeToURL:[NSURL fileURLWithPath:filePath] atomically:YES];
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ double playRate = rate / self.playerEngine.baseRate;
|
|
|
+ [self.playerEngine setMusicPlayerSpeed:playRate];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ------ cloud function
|
|
|
+// 点击开始评测按钮
|
|
|
+- (void)startEvaluateing {
|
|
|
+ [self configRecordManager];
|
|
|
+ self.hasSendStartMessage = NO;
|
|
|
+ PREMISSIONTYPE isOk = [RecordCheckManager checkPermissionShowAlert:NO showInView:nil];
|
|
|
+ if (isOk == PREMISSIONTYPE_YES) {
|
|
|
+ [self configXMLInfo];
|
|
|
+ // 如果socket 连上了
|
|
|
+ if (self.socketManager.socketReadyState == SR_OPEN) {
|
|
|
+ NSDictionary *content = self.evaluatParm;
|
|
|
+ NSString *sendData = [self configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
|
|
|
+ [self sendDataToSocketService:sendData];
|
|
|
+ NSLog(@"------ send music xml info");
|
|
|
+ self.hasSendStartMessage = YES;
|
|
|
+ // 开始评测
|
|
|
+ [self showBeatBeforeEvaluating];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self connectSocketService];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 停止评测功能
|
|
|
+ [self cancelEvaluating];
|
|
|
+ [self showErrorMessage:@"没有麦克风权限"];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setIsEvaluating:(BOOL)isEvaluating {
|
|
|
+ _isEvaluating = isEvaluating;
|
|
|
+ if (isEvaluating) {
|
|
|
+ if (self.isPlaying) {
|
|
|
+ [self stopPlayAction];
|
|
|
+
|
|
|
+ }
|
|
|
+ if (self.needShowFinger == YES) {
|
|
|
+ self.needShowFinger = NO;
|
|
|
+ [self displayFingerView];
|
|
|
+ }
|
|
|
+ [self.view addSubview:self.evaluateButton];
|
|
|
+ [self ForbiddenButton:YES];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 重置页面数据
|
|
|
+ if (_isPlaying) { // 如果在评测状态下
|
|
|
+ [self stopPlayAction];
|
|
|
+ [self endEvaluating];
|
|
|
+ }
|
|
|
+ [self displayCourseToFirstNode];
|
|
|
+ [self.playerEngine setProgressTime:0.0f];
|
|
|
+ [self.evaluateButton removeFromSuperview];
|
|
|
+ [self ForbiddenButton:NO];
|
|
|
+ if (self.showFinger == YES && self.fingerImageDic != nil) {
|
|
|
+ self.needShowFinger = YES;
|
|
|
+ [self displayFingerView];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)ForbiddenButton:(BOOL)isForbidden {
|
|
|
+ for (NSInteger indexTag = 1001; indexTag < 1007; indexTag++) {
|
|
|
+ CloudControlButton *button = [self.headView viewWithTag:indexTag];
|
|
|
+ button.userInteractionEnabled = !isForbidden;
|
|
|
+ button.alpha = isForbidden ? 0.6f : 1.0f;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ----- 节拍
|
|
|
+- (void)showBeatBeforePlay {
|
|
|
+ KSCloudBeatView *beatView = [KSCloudBeatView shareInstanceWithBeatType:self.beatType speed:self.currentSpeed repeatCount:1 supplement:0];
|
|
|
+ MJWeakSelf;
|
|
|
+ [beatView startPlayWithEndCallback:^(BOOL isCancle) {
|
|
|
+ if (isCancle) { // 取消
|
|
|
+ [weakSelf resetPlayButtonStatus];
|
|
|
+ }
|
|
|
+ else { // 开始播放
|
|
|
+ [weakSelf playAction];
|
|
|
+ }
|
|
|
+ }];
|
|
|
+ [self.view addSubview:beatView];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showBeatBeforeEvaluating {
|
|
|
+ KSCloudBeatView *beatView = [KSCloudBeatView shareInstanceWithBeatType:self.beatType speed:self.currentSpeed repeatCount:1 supplement:0];
|
|
|
+ MJWeakSelf;
|
|
|
+ [beatView startPlayWithEndCallback:^(BOOL isCancle) {
|
|
|
+ if (isCancle) { // 取消
|
|
|
+ [weakSelf cancelEvaluating];
|
|
|
+ }
|
|
|
+ else { // 开始播放
|
|
|
+ [weakSelf startPlayAndRecord];
|
|
|
+ }
|
|
|
+ }];
|
|
|
+ [self.view addSubview:beatView];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ---- 评测功能
|
|
|
+
|
|
|
+- (void)startPlayAndRecord {
|
|
|
+ if (self.cameraOn && self.saveVideo) {
|
|
|
+ [self startCapture];
|
|
|
+ }
|
|
|
+ if (self.isChooseMeasure) { // 重置选段
|
|
|
+ [self resetChooseMeasureStatus];
|
|
|
+ }
|
|
|
+ [self clearAllColorDisplayView];
|
|
|
+ CGFloat rate = self.currentSpeed / self.playerEngine.baseRate;
|
|
|
+ [self.playerEngine setMusicPlayerSpeed:rate];
|
|
|
+ [self.playerEngine setProgressTime:0.0f];
|
|
|
+ [self displayCourseToFirstNode];
|
|
|
+ [self playAction];
|
|
|
+ [self startRecording];
|
|
|
+
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ // 发送时间校准消息
|
|
|
+ NSDictionary *bodyParm = @{
|
|
|
+ @"offsetTime" :@(0.000),
|
|
|
+ };
|
|
|
+ NSString *realStartTime = @"audioPlayStart";
|
|
|
+ NSString *realStart = [self configDataCommond:realStartTime body:bodyParm type:@"SOUND_COMPARE"];
|
|
|
+ [self sendDataToSocketService:realStart];
|
|
|
+ NSLog(@"--------- send check time message");
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+- (void)resetChooseMeasureStatus {
|
|
|
+ self.isChooseMeasure = NO;
|
|
|
+ // 清理全部
|
|
|
+ self.startMeasure = -1;
|
|
|
+ self.endMeasure = -1;
|
|
|
+ self.lastPlayNodeIndex = 0;
|
|
|
+ self.playerEngine.startTime = 0.0f;
|
|
|
+ self.playerEngine.endTime = 0.0f;
|
|
|
+ CloudControlButton *button = [self.headView viewWithTag:BUTTON_TAG_SELECT];
|
|
|
+ button.isSelected = NO;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)configXMLInfo {
|
|
|
+ NSMutableDictionary *dic = [NSMutableDictionary dictionary];
|
|
|
+ [dic setValue:@(0) forKey:@"beatLength"];
|
|
|
+ [dic setValue:@"IOS" forKey:@"platform"];
|
|
|
+
|
|
|
+ [dic setValue:self.songMessageSource.behaviorId forKey:@"behaviorId"];
|
|
|
+ [dic setValue:@"1638175609911244308" forKey:@"uuid"];
|
|
|
+ [dic setValue:self.songMessageSource.detailId forKey:@"detailId"];
|
|
|
+ [dic setValue:self.songMessageSource.examSongId forKey:@"examSongId"];
|
|
|
+ [dic setValue:self.songMessageSource.subjectId forKey:@"subjectId"];
|
|
|
+ [dic setValue:self.songMessageSource.examSongId forKey:@"id"];
|
|
|
+ [dic setValue:self.songMessageSource.xmlUrl forKey:@"xmlUrl"];
|
|
|
+ // head level
|
|
|
+ [dic setValue:self.level forKey:@"heardLevel"];
|
|
|
+
|
|
|
+ // 设置速度
|
|
|
+ [dic setValue:@(self.currentSpeed) forKey:@"speed"];
|
|
|
+ NSMutableArray *newNodeArray = [self configNodeArrayWithSpeed:self.currentSpeed];
|
|
|
+ [dic setValue:newNodeArray forKey:@"musicXmlInfos"];
|
|
|
+
|
|
|
+ self.evaluatParm = dic;
|
|
|
+}
|
|
|
+
|
|
|
+- (NSMutableArray *)configNodeArrayWithSpeed:(NSInteger)speed {
|
|
|
+ NSMutableArray *sourceArray = [NSMutableArray array];
|
|
|
+ if (speed == 120) {
|
|
|
+ sourceArray = [NSMutableArray arrayWithArray:self.nodeArray];
|
|
|
+ return sourceArray;
|
|
|
+ }
|
|
|
+ for (NSMutableDictionary *parm in self.nodeArray) {
|
|
|
+ NSMutableDictionary *node = [parm mutableCopy];
|
|
|
+ float timeSapmle = [node floatValueForKey:@"timeStamp"];
|
|
|
+ float duration = [node floatValueForKey:@"duration"];
|
|
|
+ timeSapmle = timeSapmle * (120.0 / speed);
|
|
|
+ duration = duration * (120.0 / speed);
|
|
|
+ [node setValue:@(timeSapmle) forKey:@"timeStamp"];
|
|
|
+ [node setValue:@(duration) forKey:@"duration"];
|
|
|
+ [sourceArray addObject:node];
|
|
|
+ }
|
|
|
+ return sourceArray;
|
|
|
+}
|
|
|
+
|
|
|
+/* 停止评测功能 */
|
|
|
+- (void)endEvaluating {
|
|
|
+ [self stopPlayAction];
|
|
|
+ self.evaluateButton.selected = NO;
|
|
|
+ self.evaluatParm = nil;
|
|
|
+ [self stopRecordService];
|
|
|
+ [self sendEndMessage];
|
|
|
+ [self stopCapatureVideo];
|
|
|
+}
|
|
|
+
|
|
|
+/* 取消评测*/
|
|
|
+- (void)cancelEvaluating {
|
|
|
+ [self stopPlayAction];
|
|
|
+ self.evaluateButton.selected = NO;
|
|
|
+ self.evaluatParm = nil;
|
|
|
+ [self stopRecordService];
|
|
|
+ [self sendEndMessage];
|
|
|
+ [self stopCapatureVideo];
|
|
|
+}
|
|
|
+
|
|
|
+// 如果开启了保存相册和摄像头 结束评测时停止录制
|
|
|
+- (void)stopCapatureVideo {
|
|
|
+ if (self.cameraOn && self.saveVideo) {
|
|
|
+ [self endCapture];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 是否带耳机检测
|
|
|
+- (void)checkIsWiredHeadsetOnCallback:(void(^)(void))callback {
|
|
|
+ AUDIODEVICE_TYPE type = [KSAQRecordManager queryAudioOutputDeviceType];
|
|
|
+ NSString *valueStr = @"";
|
|
|
+ BOOL checkIsWired = NO;
|
|
|
+ switch (type) {
|
|
|
+ case AUDIODEVICE_TYPE_HEADPHONE:
|
|
|
+ {
|
|
|
+ valueStr = @"有线耳机";
|
|
|
+ checkIsWired = YES;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case AUDIODEVICE_TYPE_BLUETOOTH:
|
|
|
+ {
|
|
|
+ valueStr = @"蓝牙耳机";
|
|
|
+ checkIsWired = YES;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case AUDIODEVICE_TYPE_NONE:
|
|
|
+ {
|
|
|
+ valueStr = @"";
|
|
|
+ checkIsWired = NO;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (checkIsWired == NO) { // 未带耳机弹窗提示
|
|
|
+ [self showNoWiredTipsAlertCallback:^{
|
|
|
+ callback();
|
|
|
+ }];
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showNoWiredTipsAlertCallback:(void(^)(void))callback {
|
|
|
+ if (![self.view.subviews containsObject:self.wiredAlert]) {
|
|
|
+ [self.wiredAlert showAlertInView:self.view callback:^{
|
|
|
+ callback();
|
|
|
+ }];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 开始录音*/
|
|
|
+- (void)startRecording {
|
|
|
+ self.isCompareStart = YES;
|
|
|
+ [self startRecordService];
|
|
|
+}
|
|
|
+
|
|
|
+/* 停止录音*/
|
|
|
+- (void)endRecording {
|
|
|
+ [self stopRecordService];
|
|
|
+}
|
|
|
+
|
|
|
+/* 开始录制*/
|
|
|
+- (void)startCapture {
|
|
|
+ // 评测的时候 生成video时会合成声轨
|
|
|
+ BOOL isIgnoreAudio = self.isEvaluating;
|
|
|
+ [self.videoRecordManager setIgnoreAudio:isIgnoreAudio];
|
|
|
+ [RecordCheckManager checkPhotoLibraryPremissionAvaiable:YES showInView:self.view];
|
|
|
+ self.videoRecordManager.audioUrl = self.AQManager.audioUrl;
|
|
|
+ [self.videoRecordManager startRecord];
|
|
|
+}
|
|
|
+
|
|
|
+/* 结束录制*/
|
|
|
+- (void)endCapture {
|
|
|
+ if (self->_videoRecordManager) {
|
|
|
+ [self.videoRecordManager stopRecord];
|
|
|
+ }
|
|
|
+}
|
|
|
+/* 开始校音*/
|
|
|
+- (void)startSoundCheck {
|
|
|
+ [self showSoundCheckView];
|
|
|
+
|
|
|
+ [self configRecordManager];
|
|
|
+ self.isCompareStart = NO;
|
|
|
+ self.isSoundCheckStart = YES;
|
|
|
+ [self startRecordService];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showSoundCheckView {
|
|
|
+ self.checkView = [SoundCheckView shareInstance];
|
|
|
+ MJWeakSelf;
|
|
|
+ [self.checkView configSoundCheckWithSubjectId:self.songMessageSource.subjectId callback:^(CLOSEACTION action) {
|
|
|
+ if (action == CLOSEACTION_SUCCESS) {
|
|
|
+ [weakSelf showSuccessMessage:@"校音成功"];
|
|
|
+ }
|
|
|
+ else if (action == CLOSEACTION_CLOSECHECK) { // 关闭校音
|
|
|
+ weakSelf.soundCheckOn = NO;
|
|
|
+ NSDictionary *config = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"cloudConfig"];
|
|
|
+ NSMutableDictionary *configDict = [NSMutableDictionary dictionaryWithDictionary:config];
|
|
|
+ [configDict setValue:@(NO) forKey:@"soundCheck"];
|
|
|
+ [[NSUserDefaults standardUserDefaults] setObject:configDict forKey:@"cloudConfig"];
|
|
|
+ [[NSUserDefaults standardUserDefaults] synchronize];
|
|
|
+ }
|
|
|
+ [weakSelf endSoundCheck];
|
|
|
+ }];
|
|
|
+ self.checkPitch = self.checkView.pitch;
|
|
|
+ self.checkView.frame = CGRectMake(0, 0, KLandscapeWidth, KLandscapeHeight);
|
|
|
+ [self.view addSubview:self.checkView];
|
|
|
+}
|
|
|
+
|
|
|
+/* 结束校音*/
|
|
|
+- (void)endSoundCheck {
|
|
|
+ // 结束校音
|
|
|
+ self.isSoundCheckStart = NO;
|
|
|
+ [self stopRecordService];
|
|
|
+}
|
|
|
+
|
|
|
+/* 上传视频*/
|
|
|
+- (void)videoUpdate {
|
|
|
+
|
|
|
+ if (self.videoRecordManager) {
|
|
|
+ MJWeakSelf;
|
|
|
+ [self.videoRecordManager uploadRecordVideoSuccess:^(NSString * _Nonnull videoUrl) {
|
|
|
+ if (videoUrl) {
|
|
|
+ [weakSelf sendUploadMessage:videoUrl];
|
|
|
+ }
|
|
|
+ } failure:^(NSString * _Nonnull desc) {
|
|
|
+ [weakSelf MBPShow:@"视频上传失败"];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)sendUploadMessage:(NSString *)videoUrl {
|
|
|
+ NSDictionary *header = @{@"commond" : @"videoUpdate",
|
|
|
+ @"type" : @"SOUND_COMPARE",
|
|
|
+ @"status" : @(200),
|
|
|
+ };
|
|
|
+ NSMutableDictionary *body = [NSMutableDictionary dictionary];
|
|
|
+ [body setValue:@(self.recordId) forKey:@"recordId"];
|
|
|
+ if ([NSString isEmptyString:videoUrl]) {
|
|
|
+ [body setValue:@(self.recordId) forKey:@"recordId"];
|
|
|
+ }
|
|
|
+
|
|
|
+ NSMutableDictionary *content = [NSMutableDictionary dictionary];
|
|
|
+ [content setValue:header forKey:@"header"];
|
|
|
+ [content setValue:body forKey:@"body"];
|
|
|
+ [content setValue:@"1638175609911244308" forKey:@"uuid"];
|
|
|
+
|
|
|
+ NSString *sendData = [content mj_JSONString];
|
|
|
+ [self sendDataToSocketService:sendData];
|
|
|
+ [self MBPShow:@"上传成功"];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)dealloc {
|
|
|
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
+ NSLog(@"------- cloud dealloc");
|
|
|
+}
|
|
|
+
|
|
|
+- (TrackChooseView *)trackChooseView {
|
|
|
+ if (!_trackChooseView) {
|
|
|
+ _trackChooseView = [TrackChooseView shareInstanceWithFullTrackArray:self.playerEngine.instrumentTrackNameArray];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_trackChooseView chooseTrackCallback:^(NSMutableArray * _Nullable trackNameArray) {
|
|
|
+ [weakSelf operationWithTrackNameArray:trackNameArray];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _trackChooseView;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)operationWithTrackNameArray:(NSMutableArray *)trackNameArray {
|
|
|
+ [self.playerEngine muteTrackWithTrackNameExclude:trackNameArray];
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+#pragma mark - Navigation
|
|
|
+
|
|
|
+// In a storyboard-based application, you will often want to do a little preparation before navigation
|
|
|
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
|
+ // Get the new view controller using [segue destinationViewController].
|
|
|
+ // Pass the selected object to the new view controller.
|
|
|
+}
|
|
|
+*/
|
|
|
+
|
|
|
+@end
|