//
//  NewAudioPlay.m
//  X-BASIC for iOS
//
//  Created by AIG-Soft(iMac) on 2015/10/09.
//  Copyright © 2015年 AIG-Soft. All rights reserved.
//
// iOS8以降のみ
// 音質の操作はできるが、音データそのものの改変についてはAVPlayerよりやりにくい
// というか、ヘッダーを書き換えたあとの再発声の方法がないので使えない

#import "NewAudioPlay.h"
#import "FileControl2.h"

@interface NewAudioPlay()
{
	//	AVAudioUnitDistortionPreset currentPreset;
}
@property (nonatomic, strong) AVAudioEngine 		*engine;
@property (nonatomic, strong) AVAudioPlayerNode 	*playerNode;
//
@property (nonatomic, strong) AVAudioFile 			*audioFile;
@property (nonatomic, strong) AVAudioPCMBuffer		*audioBuffer;
@property (nonatomic, strong) AVAudioFormat 		*audioFormat;
//
@property (nonatomic, strong) AVAudioUnitReverb		*unitReverb;
@property (nonatomic, strong) AVAudioUnitDelay 		*unitDelay;
@property (nonatomic, strong) AVAudioUnitEQ 		*unitEq;
@property (nonatomic, strong) AVAudioUnitDistortion *unitDistortion;

@end

//------------------------------------

@implementation NewAudioPlay

-(instancetype)init
{
	printSelf2();
	self=[super init];
	if (self) {
		// これが中核
		self.engine = [[AVAudioEngine alloc] init];
		
		// それに対し、制御ノードをattachしていく
		[self setupPlayer];	// 演奏ノード(必須)
		
		// 以下音の補正系ノードをattach
		[self setupReverb:0.0];		// リバーブ
		[self setupDelay];			// ディレイ
//		[self setupEQ];
//		[self setupDistortion];
#if 1
		// ヘッドフォンの抜き差しで落ちるらしいので対策
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(handleInterruption:)
													 name:AVAudioEngineConfigurationChangeNotification
												   object:nil];
#endif
	}
	return self;
}

-(void)handleInterruption:(NSNotification *)notification
{
	printSelf2();
	nslog2(@"notification=%@",notification);
}

//---------------------------

// バッファーにファイルを読み込む
-(BOOL)readFileToBuffer
{
	printSelf2();
	AVAudioFrameCount length = (AVAudioFrameCount)self.audioFile.length;
	self.audioBuffer = [[AVAudioPCMBuffer alloc]initWithPCMFormat:self.audioFormat frameCapacity:length];
	NSError *error;
	[self.audioFile readIntoBuffer:self.audioBuffer error:&error];
	if (error) {
		nslog2(@"error:%@", error);
	}
	// NO=正常終了 , YES=エラー
	return (error!=nil);
}

// WAVのフォーマット情報のサンプリングレートのみ書き換える
-(void)setFormatWav:(double)rate
{
	printSelf2();
	nslog(@"wav=%f",rate);
	// setFile:時点で16bit-int WAVがFloat32と認識されているので、ここでもFloat32にしなければならない
//	self.audioFormat=[[AVAudioFormat alloc]initWithCommonFormat:AVAudioPCMFormatInt16
//													 sampleRate:rate
//													   channels:channels
//													interleaved:NO];
	self.audioFormat=[[AVAudioFormat alloc]initWithCommonFormat:AVAudioPCMFormatFloat32
													 sampleRate:rate
													   channels:self.audioFormat.channelCount
													interleaved:NO];
	nslog2(@"format=%@",self.audioFormat);
}


// 演奏するファイルを設定
-(BOOL)setFile:(NSString *)fname
{
	printSelf2();
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		return YES; // エラー
	}
	NSError *error;
	self.audioFile = [[AVAudioFile alloc] initForReading:[NSURL fileURLWithPath:path] error:&error];
	if (error) {
		nslog2(@"error:%@", error);
	}
	// .processingFormat = AVAudioFormat
	self.audioFormat=self.audioFile.processingFormat; // ファイルの持つフォーマット情報をそのまま設定
	nslog2(@"File format=%@",self.audioFormat);
	// この時点で16bit-int WAVがFloat32と認識されているので、setFormatWav:でもFloat32にしなければならない

	// NO=正常終了 , YES=エラー
	return (error!=nil);
}


// connectは2回以上行ってはいけない(落ちる)
-(BOOL)connectAndStart
{
	printSelf2();
	
	// ノードをつないでいく
	// [Player] -> [Reverb]
	//			-> [Output]
	
	AVAudioMixerNode *mixerNode = [self.engine mainMixerNode];
#if 0
	[self.engine connect:self.playerNode
//					  							to:self.unitEq 			format:self.audioFormat];
//	[self.engine connect:self.unitEq     		to:self.unitDistortion 	format:self.audioFormat];
//	[self.engine connect:self.unitDistortion 	to:self.unitDelay 		format:self.audioFormat];
//	[self.engine connect:self.unitDelay
												to:mixerNode 			format:self.audioFormat];
#else 
	[self.engine connect:self.playerNode 		to:self.unitReverb		format:self.audioFormat];
	[self.engine connect:self.unitReverb
	// delayは効果がわからないし、OFFにするにはconnectそのものを入れないようにしなければならないようなので封印
//					  							to:self.unitDelay 		format:self.audioFormat];
//	[self.engine connect:self.unitDelay
					  							to:mixerNode 			format:self.audioFormat];
#endif
	
	// エンジンを開始
	NSError *error=nil;
	[self.engine startAndReturnError:&error];
	if (error) {
		nslog2(@"error:%@", error);
	}
	
	// NO=正常終了 , YES=エラー
	return (error!=nil);
}


// バッファーを元に演奏準備
// ファイルからバッファーに入れ込んでから再生(バッファー内にフォーマットを構築するまではしない)
// option
//	AVAudioPlayerNodeBufferLoops				無限にループします。<-ループ音
//	AVAudioPlayerNodeBufferInterrupts			演奏中断されます。
//	AVAudioPlayerNodeBufferInterruptsAtLoop		ループポイントで、演奏中断します。	<-1ショット音
// @return NO=正常終了 , YES=エラー
- (BOOL)scheduleBuffer:(AVAudioPlayerNodeBufferOptions)option
{
	printSelf2();
	
//	// ノードをつないでエンジンを開始
//	BOOL ret=[self connectAndStart];
//	if (ret) return ret;
	
	// 演奏をスケジュールする
	[self.playerNode scheduleBuffer:self.audioBuffer
							 atTime:nil
							options:option
				  completionHandler:^{
					  nslog2(@"演奏終了(Buffer)");
					  // これをしないと.playing=NOにならない
					  [self.playerNode stop];
				  }];
	
	return (NO);
}


// ファイルを元に演奏準備
// @return NO=正常終了 , YES=エラー
- (BOOL)scheduleFile
{
	printSelf2();
	
//	// ノードをつないでエンジンを開始
//	BOOL ret=[self connectAndStart];
//	if (ret) return ret;
	
	// 演奏をスケジュールする
	[self.playerNode scheduleFile:self.audioFile
						   atTime:nil
				completionHandler:^{
					nslog2(@"演奏終了(File)");
					// これをしないと.playing=NOにならない
					[self.playerNode stop];
				}];
	
	return (NO);
}

//------------------------------------------------------
// コマンド

-(void)play
{
	[self.playerNode play];
}

- (void)playAtTime:(AVAudioTime * _Nullable)when
{
	[self.playerNode playAtTime:when];
}

-(void)pause
{
	[self.playerNode pause];
}

-(void)stop
{
	[self.playerNode stop];
}

//------------------------------------------------------
// setter & getter

// ボリュームを設定する
-(void)setVolume:(float)volume
{
	self.playerNode.volume = volume;
}

-(float)volume
{
	return self.playerNode.volume;
}

// パンポットを設定する
-(void)setPanpot:(float)panpot
{
	self.playerNode.pan = panpot;
}

-(float)panpot
{
	return self.playerNode.pan;
}

-(BOOL)playing
{
	return self.playerNode.playing;
}

//------------------------------------------------------
// ノードの生成
//------------------------------------------------------

- (void)setupPlayer
{
	printSelf2();
	self.playerNode = [[AVAudioPlayerNode alloc] init];
	[self.engine attachNode:self.playerNode];
}

//------------------------------------------------------
// リバーブ

-(void)setReverb:(float)reverb preset:(AVAudioUnitReverbPreset)preset
{
	self.reverb=reverb;
	[self.unitReverb loadFactoryPreset:preset];
}

-(void)setReverb:(float)reverb
{
	self.unitReverb.wetDryMix = reverb;	// %
}

-(float)reverb
{
	return self.unitReverb.wetDryMix;
}

- (void)setupReverb:(float)blend
{
	printSelf2();
	
	self.unitReverb = [[AVAudioUnitReverb alloc]init];
	self.unitReverb.wetDryMix = blend;	// %
	[self.engine attachNode:self.unitReverb];
}

//------------------------------------------------------
// ディレイ

-(void)setDelayTime:(NSTimeInterval)delayTime
{
	self.unitDelay.delayTime=delayTime;
}

-(NSTimeInterval)delayTime
{
	return self.unitDelay.delayTime;
}

-(void)setFeedback:(float)feedback
{
	self.unitDelay.feedback=feedback;
}

-(float)feedback
{
	return self.unitDelay.feedback;
}

-(void)setWetDryMix:(float)wetDryMix
{
	self.unitDelay.wetDryMix=wetDryMix;
}

-(float)wetDryMix
{
	return self.unitDelay.wetDryMix;
}

-(void)setLowPassCutoff:(float)lowPassCutoff
{
	self.unitDelay.lowPassCutoff=lowPassCutoff;
}

-(float)lowPassCutoff
{
	return self.unitDelay.lowPassCutoff;
}

- (void)setupDelay
{
	printSelf2();
	
	self.unitDelay = [[AVAudioUnitDelay alloc] init];
	self.unitDelay.delayTime=0;
	self.unitDelay.feedback=0;
	self.unitDelay.wetDryMix=0;
	self.unitDelay.lowPassCutoff=0;
	[self.engine attachNode:self.unitDelay];
}

//------------------------------------------------------

- (void)setupEQ
{
	printSelf2();
	
	self.unitEq = [[AVAudioUnitEQ alloc] initWithNumberOfBands:2];
	
	AVAudioUnitEQFilterParameters *filterParameters;
	filterParameters 			= self.unitEq.bands[0];
	filterParameters.filterType = AVAudioUnitEQFilterTypeHighPass;
	filterParameters.frequency	= 80;
	
	filterParameters 			= self.unitEq.bands[1];
	filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
	filterParameters.frequency 	= 500;
	filterParameters.bandwidth 	= 2.0;
	filterParameters.gain 		= 4.0;
	
	[self.engine attachNode:self.unitEq];
}

- (void)setupDistortion
{
	printSelf2();
	
	self.unitDistortion = [[AVAudioUnitDistortion alloc] init];
	
	[self.engine attachNode:self.unitDistortion];
}

// attachNode:
// detachNode:
//　総フレーム数（.length）を、サンプルレート（44100）で割って、秒
//------------------------------------------------------

@end