123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- //
- // ToneTuningViewController.m
- // KulexiuForStudent
- //
- // Created by 王智 on 2022/10/13.
- //
- #import "ToneTuningViewController.h"
- #import "TuningNavView.h"
- #import <KSTunerLibrary/KSTunerLibrary-Swift.h>
- #import "ToneTuningBodyView.h"
- #import "DialPlateView.h"
- #import "TuningForkSettingView.h"
- #import <AVFoundation/AVFoundation.h>
- #import "TunerSettingView.h"
- #import <KSChoosePicker.h>
- #import "WidgetViewController.h"
- #import "MetronomeManager.h"
- @interface ToneTuningViewController ()<TunerDelegate>
- @property (nonatomic, strong) TuningNavView *navView;
- @property (nonatomic, assign) BOOL isRuning;
- @property (nonatomic, strong) Tuner *tuner;
- @property (nonatomic, strong) ToneTuningBodyView *bodyView;
- @property (nonatomic, strong) DialPlateView *plateView;
- @property (nonatomic, assign) BOOL isTransfer;
- @property (nonatomic, strong) TuningForkSettingView *forkView;
- @property (nonatomic, assign) NSInteger chooseFrequence;
- @property (nonatomic, strong) TunerForkManager *forkManager;
- @property (nonatomic, strong) TunerSettingView *settingView;
- @property (nonatomic, strong) KSChoosePicker *instrumentPicker;
- @property (nonatomic, assign) NSInteger lastChooseIndex;
- @property (nonatomic, strong) NSArray *instrumentArray;
- @property (nonatomic, strong) NSArray *picthRateArray;
- // 判断是否选择移调乐器
- @property (nonatomic, assign) NSInteger toneChangeRate;
- @property (nonatomic, strong) NSArray *picthDescArray;
- // A4 频率
- @property (nonatomic, assign) NSInteger A4Frequence;
- @end
- @implementation ToneTuningViewController
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
- self.ks_prefersNavigationBarHidden = YES;
- [self configDefault];
- [self configUI];
- [self setupAudioSession];
- [self addNotification];
- }
- - (void)addNotification {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopPlayBeat) name:ERROR_NOTIFICATION object:nil];
- }
- - (void)configDefault {
- self.chooseFrequence = A4_FREQUENCE_INTABLE;
- self.A4Frequence = DEFALUT_A4FREQUENCE;
- self.isTransfer = NO;
- }
- - (void)setupAudioSession {
-
- AVAudioSession *audioSession = [AVAudioSession sharedInstance];
- NSError *err = nil;
- AVAudioSessionCategory category = AVAudioSessionCategoryPlayAndRecord;
- if (@available(iOS 10.0, *)) {
- [audioSession setCategory:category mode:AVAudioSessionModeDefault options:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionAllowBluetoothA2DP|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
- }
- else {
- [audioSession setCategory:category withOptions:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
- }
- [audioSession setActive:YES error:&err];
- }
- - (void)configUI {
- self.scrollView.backgroundColor = [UIColor clearColor];
- [self.scrollView mas_remakeConstraints:^(MASConstraintMaker *make) {
- make.left.right.mas_equalTo(self.view);
- make.top.mas_equalTo(self.view.mas_top).offset(kNaviBarHeight);
- make.bottom.mas_equalTo(self.view.mas_bottom);
- }];
-
- // 背景图
- UIImage *bgImage = [UIImage imageNamed:@"gadget_bg"];
- UIImageView *imageView = [[UIImageView alloc] initWithImage:bgImage];
- imageView.contentMode = UIViewContentModeRedraw;
- [self.view addSubview:imageView];
- [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
- make.left.right.top.bottom.mas_equalTo(self.view);
- }];
-
- [self.view bringSubviewToFront:self.scrollView];
-
- [self.view addSubview:self.navView];
- [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
- make.top.left.right.mas_equalTo(self.view);
- make.height.mas_equalTo(kNaviBarHeight);
- }];
- [self.scrollView addSubview:self.bodyView];
- self.bodyView.A4Frequence = self.A4Frequence;
- [self.bodyView mas_makeConstraints:^(MASConstraintMaker *make) {
- make.left.right.mas_equalTo(self.view);
- make.top.mas_equalTo(self.scrollView.mas_top);
- make.bottom.mas_equalTo(self.scrollView.mas_bottom);
- }];
- [self.bodyView.plateView addSubview:self.plateView];
- [self.plateView mas_makeConstraints:^(MASConstraintMaker *make) {
- make.top.bottom.left.right.mas_equalTo(self.bodyView.plateView);
- }];
- }
- - (void)viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- [self startTuner];
- }
- - (void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
- self.bodyView.speedValue.text = [NSString stringWithFormat:@"BPM=%d",METRONOME_MANAGER.speed];
- self.bodyView.metronomePlayStatus = METRONOME_MANAGER.isPlaying;
- self.bodyView.beatType.text = [METRONOME_MANAGER getTypeString];
- [self changeRhythm];
- }
- - (void)viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
- [self stopTuner];
- [self stopForkPlay];
- BOOL isBack = [self isViewPopDismiss];
- if (isBack) {
- [METRONOME_MANAGER stopPlay];
- [METRONOME_MANAGER resetDefaultConfig];
- [self removeTuner];
- }
- }
- - (void)startTuner {
- @try {
- if (self.isRuning == NO) {
- self.isRuning = YES;
- [self.tuner start];
- }
- } @catch (NSException *exception) {
- NSLog(@"--exception -- %@",exception);
- } @finally {
-
- }
- }
- - (void)stopTuner {
- if (self.isRuning) {
- self.isRuning = NO;
- [self.tuner stop];
- }
- }
- - (void)removeTuner {
- if (_tuner) {
- [_tuner freeTuner];
- _tuner = nil;
- NSLog(@"--- free tuner ");
- }
- }
- - (CAGradientLayer *)createGradientLayerFromColor:(UIColor *)fromColor startPoint:(CGPoint)startPoint endColor:(UIColor *)endColor endPoint:(CGPoint)endPoint bounds:(CGRect)bounds {
- CAGradientLayer *gradientLayer = [CAGradientLayer layer];
- gradientLayer.colors = @[(__bridge id)fromColor.CGColor, (__bridge id)endColor.CGColor];
- gradientLayer.startPoint = startPoint;
- gradientLayer.endPoint = endPoint;
- gradientLayer.frame = bounds;
- gradientLayer.locations = @[@(0),@(1.0f)];
- return gradientLayer;
- }
- - (TuningNavView *)navView {
- if (!_navView) {
- _navView = [TuningNavView shareInstance];
- MJWeakSelf;
- [_navView navActionCallback:^(BOOL isBack) {
- if (isBack) {
- [weakSelf backAction];
- }
- else {
- [weakSelf showSettingView];
- }
- }];
- }
- return _navView;
- }
- - (void)showSettingView {
- [self.settingView showView];
- }
- - (Tuner *)tuner {
- if (!_tuner) {
- _tuner = [[Tuner alloc] initWithThreshold:0 smoothing:0.25];
- _tuner.delegate = self;
- }
- return _tuner;
- }
- - (void)tunerDidUpdate:(Tuner *)tuner output:(TunerOutput *)output {
- if (output.amplitude < 0.01) {
-
- }
- else {
- TunerOutput *transferOutput = [tuner getTransferPitchWithA4Frequency:self.A4Frequence toneChangeRate:self.toneChangeRate output:output];
- /**
- 如果知道两个音a和b的频率,两个音相距的音分值n可用下列公式计算(类似分贝定义式的形式,目的是为了使指数形式的物理单位线性化,使其化为对数):
- n=1200 *log2(a/b) == 3986 *log10(a/b)
- */
- Float32 cents = log2(transferOutput.frequency/transferOutput.midFrequence) * 1200;
- // Float32 cents = log2(transferOutput.frequency / (transferOutput.frequency - transferOutput.distance)) *1200;
- if (lround(transferOutput.frequency) == lround(transferOutput.midFrequence)) {
- cents = 0;
- }
- if (cents > 50) {
- cents = 50;
- }
- else if (cents < -50) {
- cents = 50;
- }
- self.plateView.gaugeView.value = (NSInteger)cents;
-
- self.bodyView.distance = (NSInteger)cents;
-
- // 调整频率后
- float rate = 0.0;
- if (labs((int)cents) <= 10) {
- self.bodyView.frequenceCorrect = YES;
- }
- else {
- self.bodyView.frequenceCorrect = NO;
- }
- int distance = abs((int)cents) > 50 ? 50 : abs((int)cents);
-
- rate = (50.0 - distance) / 50;
- self.bodyView.rateFrequence = rate;
- if (self.isTransfer == NO) {
-
- self.bodyView.nomalPitch.text = [NSString stringWithFormat:@"%@%zd",output.pitch, output.octave];
- self.bodyView.pitchFrequenceLabel.text = [NSString stringWithFormat:@"%@%zd:%.0fHz", output.pitch, output.octave, output.midFrequence / A4_FREQUENCE_INTABLE *self.A4Frequence];
- self.bodyView.preFrequence.text = [NSString stringWithFormat:@"%@%zd",output.prePitch, output.preOctave];
- self.bodyView.nextFrequence.text = [NSString stringWithFormat:@"%@%zd",output.nextPitch, output.nextOctave];
- }
- else {
-
- self.bodyView.transferNomalPitch.text = [NSString stringWithFormat:@"%@%zd",transferOutput.pitch,transferOutput.octave];
- self.bodyView.transferDesc.text = self.settingView.transferPicthDesc;
- self.bodyView.transferPitch.text = [NSString stringWithFormat:@"%@%zd",transferOutput.transferPitch, transferOutput.transferOctave];
- self.bodyView.pitchFrequenceLabel.text = [NSString stringWithFormat:@"%@%zd:%.0fHz", transferOutput.transferPitch, transferOutput.transferOctave, transferOutput.midFrequence];
- self.bodyView.preFrequence.text = [NSString stringWithFormat:@"%@%zd",transferOutput.prePitch, transferOutput.preOctave];
- self.bodyView.nextFrequence.text = [NSString stringWithFormat:@"%@%zd",transferOutput.nextPitch, transferOutput.nextOctave];
- }
- }
- NSLog(@"-------- %@%zd --- distance :%f frequence : %f" , output.pitch, output.octave, output.distance, output.frequency);
-
- }
- #pragma mark ---- lazying
- - (ToneTuningBodyView *)bodyView {
- if (!_bodyView) {
- _bodyView = [ToneTuningBodyView shareInstance];
- MJWeakSelf;
- [_bodyView tuningViewAction:^(TUNINGACTION action, NSInteger frequence) {
- [weakSelf tuningAction:action frequence:frequence];
- }];
- }
- return _bodyView;
- }
- - (void)tuningAction:(TUNINGACTION)action frequence:(NSInteger)frequence {
- switch (action) {
- case TUNINGACTION_FORKSETTING: // 音叉设置
- {
- [self.forkView showView];
- // 停止播放节拍器
- [self stopPlayBeat];
- }
- break;
- case TUNINGACTION_FREQUENCE: // 调整频率
- {
- self.A4Frequence = frequence;
- self.settingView.A4Frequence = frequence;
- self.tuner.A4Frequence = frequence;
- self.forkView.A4Frequence = frequence;
- if (self.bodyView.isPlaying) {
- CGFloat changefrequence = frequence / A4_FREQUENCE_INTABLE * self.chooseFrequence;
- [self.forkManager changeFrequenceWithFrequence:changefrequence];
- }
- }
- break;
- case TUNINGACTION_PLAY: // 播放音叉
- {
- [self startForkPlay];
- // 停止播放节拍器
- [self stopPlayBeat];
- }
- break;
- case TUNINGACTION_STOP: // 停止音叉
- {
- [self stopForkPlay];
- }
- break;
- case TUNINGACTION_BEATPLAY:
- {
- [self startPlayBeat];
- // 停止音叉
- [self stopForkPlay];
- }
- break;
- case TUNINGACTION_BEATSTOP:
- {
- [self stopPlayBeat];
- }
- break;
- case TUNINGACTION_BEATCHOOSE:
- {
- [self beatChoose];
- }
- break;
- case TUNINGACTION_RHYTHMCHOOSE: // 选择节奏
- {
- [self chooseRhythmType];
- }
- break;
- case TUNINGACTION_METRONOME:
- {
- WidgetViewController *ctrl = [[WidgetViewController alloc] init];
- [self.navigationController pushViewController:ctrl animated:YES];
- }
- break;
- default:
- break;
- }
- }
- - (void)chooseRhythmType {
- [METRONOME_MANAGER rhythmChooseCallback:^(RHYTHM_TYPE thythmType) {
- [self stopPlayBeat];
- [self changeRhythm];
- }];
- }
- - (void)changeRhythm {
- // 显示
- NSString *imageName = [METRONOME_MANAGER imageWithType:METRONOME_MANAGER.rhythmType];
- [self.bodyView.rhythmImage setImage:[UIImage imageNamed:imageName]];
- }
- - (void)startForkPlay {
-
- CGFloat changefrequence = self.A4Frequence / A4_FREQUENCE_INTABLE * self.chooseFrequence;
- [self.forkManager setupWithFrequence:changefrequence];
- }
- - (void)stopForkPlay {
- self.bodyView.isPlaying = NO;
- [self.forkManager stop];
- }
- #pragma mark ---- 节拍器
- - (void)startPlayBeat {
- self.bodyView.metronomePlayStatus = YES;
- [METRONOME_MANAGER startPlay];
- }
- - (void)stopPlayBeat {
- self.bodyView.metronomePlayStatus = NO;
- [METRONOME_MANAGER stopPlay];
- }
- - (void)beatChoose {
- [METRONOME_MANAGER beatChooseCallback:^(KSWidgeMetronomeType type) {
- [self disPlayBeat];
- [self changeRhythm];
- }];
- }
- - (void)disPlayBeat {
- self.bodyView.metronomePlayStatus = NO;
- [self stopPlayBeat];
- self.bodyView.beatType.text = [METRONOME_MANAGER getTypeString];
- }
- - (DialPlateView *)plateView {
- if (!_plateView) {
- _plateView = [[DialPlateView alloc] init];
- }
- return _plateView;
- }
- #pragma mark ------ forkSetting
- - (TuningForkSettingView *)forkView {
- if (!_forkView) {
- _forkView = [TuningForkSettingView shareInstance];
- _forkView.frame = CGRectMake(0, 0, KPortraitWidth, KPortraitHeight);
- MJWeakSelf;
- [_forkView forkSettingAction:^(CGFloat frequence, BOOL isSure) {
- NSLog(@"----- frequence %f", frequence);
- weakSelf.chooseFrequence = frequence;
- if (weakSelf.bodyView.isPlaying) {
- CGFloat changefrequence = frequence / A4_FREQUENCE_INTABLE * self.A4Frequence;
- [weakSelf.forkManager changeFrequenceWithFrequence:changefrequence];
- }
- else {
- if (isSure) {
- [weakSelf stopForkPlay];
- }
- else {
- if (weakSelf.forkManager.isPlay) {
- CGFloat changefrequence = frequence / A4_FREQUENCE_INTABLE * self.A4Frequence;
- [weakSelf.forkManager changeFrequenceWithFrequence:changefrequence];
- }
- else {
- [weakSelf startForkPlay];
- }
- }
- }
- }];
- }
- return _forkView;
- }
- - (TunerForkManager *)forkManager {
- if (!_forkManager) {
- _forkManager = [[TunerForkManager alloc] init];
- }
- return _forkManager;
- }
- - (TunerSettingView *)settingView {
- if (!_settingView) {
- _settingView = [TunerSettingView shareInstance];
- _settingView.A4Frequence = self.A4Frequence;
- _settingView.frame = CGRectMake(0, 0, KPortraitWidth, KPortraitHeight);
- MJWeakSelf;
- [_settingView tunerSettingAction:^(TUNETSETTING_ACTION action, NSInteger toneChangeRate, NSInteger frequence) {
- [weakSelf tunerSettingAction:action picthRate:toneChangeRate frequence:frequence];
- }];
- }
- return _settingView;
- }
- - (void)tunerSettingAction:(TUNETSETTING_ACTION)action picthRate:(NSInteger)toneChangeRate frequence:(NSInteger)frequence {
- if (action == TUNETSETTING_ACTION_INSTRUMENT) {
- // 选择乐器
- MJWeakSelf;
- self.instrumentPicker = [[KSChoosePicker alloc] initWithTitle:@"" sourceData:self.instrumentArray lastChooseIndex:self.lastChooseIndex sureButtonColor:CLIENT_THEMECOLOR chooseReturnWithBlock:^(NSString * _Nonnull returnValue, NSInteger chooseIndex) {
- weakSelf.lastChooseIndex = chooseIndex;
- weakSelf.settingView.instrumentLabel.text = returnValue;
- NSInteger pitchRate = [weakSelf.picthRateArray[chooseIndex] integerValue];
- weakSelf.A4Frequence = frequence;
- weakSelf.settingView.toneChangeRate = pitchRate;
- weakSelf.settingView.transferPicthDesc = weakSelf.picthDescArray[chooseIndex];
- } cancel:^{
-
- }];
- [self.instrumentPicker showPicker];
- }
- else if (action == TUNETSETTING_ACTION_SURE) {
- // 设置成功
- self.A4Frequence = frequence;
- self.bodyView.A4Frequence = frequence;
- self.toneChangeRate = toneChangeRate;
- self.tuner.A4Frequence = frequence;
- self.forkView.A4Frequence = frequence;
- if (self.bodyView.isPlaying) {
- CGFloat changefrequence = frequence / A4_FREQUENCE_INTABLE * self.chooseFrequence;
- [self.forkManager changeFrequenceWithFrequence:changefrequence];
- }
- }
- }
- - (NSArray *)instrumentArray {
- if (!_instrumentArray) {
- _instrumentArray = @[@"没有移调",
- @"葫芦丝:C调",@"葫芦丝:D调",@"葫芦丝:降B调",@"葫芦丝:F调",@"葫芦丝:G调",
- @"排箫:C调",@"排箫:D调",@"排箫:E调",@"排箫:F调",@"排箫:G调",
- @"竖笛:C调",@"竖笛:中音F调",
- @"口风琴:C调",
- @"陶笛:SC调",@"陶笛:AC调",@"陶笛:AG调",@"陶笛:AF调",@"陶笛:BC调",
- @"长笛:C调",@"高音萨克斯:降B调",@"中音萨克斯:降E调",@"单簧管:降B调",@"双簧管:C调",@"小号:降B调",@"长号:C调",@"圆号:F调",@"大号:降B调",@"上低音号:C调",@"上低音号:降B调"];
- }
- return _instrumentArray;
- }
- - (NSArray *)picthRateArray {
- if (!_picthRateArray) {
- _picthRateArray = @[@0,
- @-12,@-2,@-11,@-5,@-7,
- @-12,@-14,@-16,@-17,@-19,
- @-12,@-5,
- @0,
- @-24,@-12,@-7,@-12,@0,
- @0,@-10,@-3,@-10,@0,@-10,@0,@-5,@-10,@0,@-10];
- }
- return _picthRateArray;
- }
- - (NSArray *)picthDescArray {
- if (!_picthDescArray) {
- _picthDescArray = @[@"C ins.",
- @"C ins.",@"D ins.",@"B♭ ins.",@"F ins.",@"G ins.",
- @"C ins.",@"D ins.",@"E ins.",@"F ins.",@"G ins.",
- @"C ins.",@"F ins.",
- @"C ins.",
- @"C ins.",@"C ins.",@"G ins.",@"F ins.",@"C ins.",
- @"C ins.",@"B♭ ins.",@"E♭ ins.",@"B♭ ins.",@"C ins.",@"B♭ ins.",@"C ins.",@"F ins.",@"B♭ ins.",@"C ins.",@"B♭ ins."];
- }
- return _picthDescArray;
- }
- - (void)setToneChangeRate:(NSInteger)toneChangeRate {
- _toneChangeRate = toneChangeRate;
- self.tuner.toneChangeRate = toneChangeRate;
- if (_toneChangeRate == 0) { //
- self.isTransfer = NO;
- }
- else {
- self.isTransfer = YES;
- }
- }
- - (void)setIsTransfer:(BOOL)isTransfer {
- _isTransfer = isTransfer;
- self.bodyView.isTransfer = isTransfer;
- }
- - (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- NSLog(@"----- toneturning dealloc");
- }
- /*
- #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
|