//
//  AudioPlay.m
//  X-BASIC'
//
//  Created by AIG-Soft(iMac) on 2012/10/25.
//  Copyright (c) 2012-15年 AIG-Soft. All rights reserved.
//

#import "AudioPlay.h"
#import "libs.h"
#import	"FileControl2.h"
#import	"adpcm.h"
#import "malloc2.h"

@implementation AudioPlay

//---------------------------------------------------------------------------------------------
/* 調査結果
 	_avAudioPlayer=nilで消音する
 	一旦_avAudioPlayerに渡されたdataを後で(play前に)書き換えても音は変わらない
 */
//---------------------------------------------------------------------------------------------
// 初期化系
//---------------------------------------------------------------------------------------------

// NSDataから初期化する
-(instancetype)initWithData:(NSData *)data
{
	printSelf();
	self = [super init];
	if (self!=nil) {
		_avAudioPlayer=[[AVAudioPlayer alloc]initWithData:data error:nil]; // これでdataがAVAudioPlayer内のバッファーに転送される様子。なので、この先data内を書き換えても無効。
		if (_avAudioPlayer!=nil) {
			_avAudioPlayer.enableRate=YES; // rate変更を許可する
			_avAudioPlayer.delegate=self;
		}
	}
	return (self);
}

// ファイルから初期化する
-(instancetype)initWithFile:(NSString *)fname
{
	printSelf();
	
	self=[super init];
	nslog(@"self=%@",self);
	if (self!=nil) {
		@autoreleasepool {
			nslog(@"ファイル名=%@",fname);
			NSString *path=getResourceOrSavedataPath(fname);
			if (path==nil) {
				// ファイルが見つからない
				nslog(@"ファイルが見つからない(getResourceOrSavedataPath)");
				// self=[AudioPlay alloc]なので、これの開放もしておく必要がある(そうしないとメモリリークを起こす)
				self=nil;
			} else {
				NSURL *url=[NSURL fileURLWithPath:path];
				nslog(@"url=%@",url);
				NSError *error;
				_avAudioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
				if (_avAudioPlayer!=nil) {
					_avAudioPlayer.enableRate=YES; // rate変更を許可する
					_avAudioPlayer.delegate=self;
				}
				nslog(@"error=%@",error);
				nslog(@"_avAudioPlayer=%@",_avAudioPlayer);
			}
		}
	}
	return (self);
}

// X68 ADPCMファイル(fadpcm=YES)またはPCMファイル(M44/M30/P16)(fadpcm=NO)から初期化する
-(instancetype)initWithADandPCMFile:(NSString *)fname rate:(int)rate multiple:(int)multiple fadpcm:(BOOL)fadpcm
{
	printSelf();
	nslog(@"fadpcm=%d",fadpcm);
	self=[super init];
	if (self!=nil) {
		BOOL ferr=NO;
		@autoreleasepool {
		
			nslog(@"ファイル名=%@",fname);
			NSString *path=getResourceOrSavedataPath(fname);
			if (path==nil) {
				nslog(@"ファイルが見つからない");
				ferr=YES;
			} else {
				SLong bufsize;
				UByte *wavbuff;
				if (fadpcm) {
					// rateはrateNo(サンプリングレート番号)
					wavbuff=adpcmFile2WavMem(nsStringTocString(path),rate		    ,&bufsize);
				} else {
					// rateはサンプリングレート(Hz)
					wavbuff=pcmFile2WavMem(  nsStringTocString(path),rate,0,multiple,&bufsize);
				}
				if (wavbuff==NULL) {
					nslog(@"変換できない");
					ferr=YES;
				} else {
					NSData *data=[NSData dataWithBytes:wavbuff length:bufsize];
					free2(wavbuff);
					if (data!=nil) {
						if (!(self = [self initWithData:data])) return nil;
					} else {
						// ファイルが見つからない時もこちら
						nslog(@"ファイルが見つからない(NSData)");
						ferr=YES;
					}
				}
			}
		}
		if (ferr) { // 内部でエラー発生時
			// self=[AudioPlay alloc]なので、これの開放もしておく必要がある(そうしないとメモリリークを起こす)
			self=nil;
		}
	}
	return (self);
}

-(instancetype)initWithADPCMFile:(NSString *)fname rate:(int)rate
{
	printSelf();
	return [self initWithADandPCMFile:fname rate:rate multiple:0 fadpcm:YES];
}

// PCMファイル(M44/M30/P16)から初期化する
-(instancetype)initWithPCMFile:(NSString *)fname rate:(int)rate multiple:(int)multiple
{
	printSelf();
	return [self initWithADandPCMFile:fname rate:rate multiple:multiple fadpcm:NO];
}

//---------------------------------------------------------------------------------------------
// 操作系
//---------------------------------------------------------------------------------------------

-(void)operation:(NSInteger)ope intArg:(NSInteger)intArg_
{
	nslog(@"operation:intArg=%d",(int)intArg_);
	self.intArg=intArg_;
	[self operation:ope];
}

-(void)operation:(NSInteger)ope doubleArg:(double)doubleArg_
{
	nslog(@"operation:doubleArg=%f",doubleArg_);
	self.floatArg=(float)doubleArg_;
	[self operation:ope];
}

-(void)operation:(NSInteger)ope floatArg:(float)floatArg_
{
	nslog(@"operation:floatArg=%f",floatArg_);
	self.floatArg=floatArg_;
	[self operation:ope];
}

-(void)operation:(NSInteger)ope floatArg1:(float)floatArg1_ floatArg2:(float)floatArg2_
{
	nslog(@"operation:floatArg=%f,%f",floatArg1_,floatArg2_);
	self.floatArg =floatArg1_;
	self.floatArg2=floatArg2_;
	[self operation:ope];
}

-(void)operation:(NSInteger)ope
{
	nslog(@"AudioPlayer.operation:%d",(int)ope);
	@autoreleasepool {
		[self performSelectorOnMainThread:@selector(operation_:)
							   withObject:[NSValue valueWithBytes:&ope objCType:@encode(NSUInteger)]
							waitUntilDone:YES];
		nslog(@"命令終了");
	}
}

// これはメインスレッドで実行される
- (void)operation_:(NSValue *)val
{
	@autoreleasepool { // 別スレッドのお約束
		nslog(@"AudioPlayer.operation_:%@",val);
		nslog(@"isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
		NSInteger ope;
		[val getValue:&ope];
		switch (ope) {
			case AUDIOPLAY_NONE:
				nslog(@"AUDIOPLAY_NONE");
				break;
			//
			case AUDIOPLAY_PLAY:
				// ここでメモリリークが発生する(初回だけ)；が、無視するしかないみたい
				nslog(@"AUDIOPLAY_PLAY");
				[_avAudioPlayer play];
	#ifdef USE_NOWPLAYING
				nowPlaying=PLAYING_NOW; // 演奏終了(V2.2)
	#endif
				break;
			//
			case AUDIOPLAY_CONT:
				// ここでメモリリークが発生する(初回だけ)；が、無視するしかないみたい
				nslog(@"AUDIOPLAY_CONT");
				[_avAudioPlayer play];
	#ifdef USE_NOWPLAYING
				nowPlaying=PLAYING_NOW; // 演奏終了(V2.2)
	#endif
				break;
			//
			case AUDIOPLAY_PAUSE:
				nslog(@"AUDIOPLAY_PAUSE");
				[_avAudioPlayer pause];
	#ifdef USE_NOWPLAYING
				nowPlaying=PLAYING_PAUSE; // 演奏一時停止(V2.2)
	#endif
				break;
			//
			case AUDIOPLAY_STOP:
				nslog(@"AUDIOPLAY_STOP");
				[_avAudioPlayer stop];
#ifdef USE_NOWPLAYING
				nowPlaying=PLAYING_PAUSE; // 演奏一時停止(V2.2)
#endif
				break;
			//
			case AUDIOPLAY_END:
				nslog(@"AUDIOPLAY_END");
				[self end];
				break;
			//
			case AUDIOPLAY_SPEED:
				nslog(@"speed=%f",_floatArg);
				_avAudioPlayer.rate	 =_floatArg;
				break;
			//
			case AUDIOPLAY_VOLUME:
				nslog(@"volume=%f",_floatArg);
				_avAudioPlayer.volume=_floatArg;
				break;
			//
			case AUDIOPLAY_PAN_AND_VOLUME:
				_avAudioPlayer.volume=_floatArg2;
			case AUDIOPLAY_PAN:
				nslog(@"pan=%f",_floatArg);
				_avAudioPlayer.pan	 =_floatArg;
				break;
			//
			case AUDIOPLAY_LOOP:
				nslog(@"loop=%d",(int)_intArg);
				_avAudioPlayer.numberOfLoops=_intArg;
				break;
		}
	} // @autoreleasepool
}

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

-(void)end
{
	printSelf();
	[_avAudioPlayer stop]; // 先に止めてから
	#ifdef USE_NOWPLAYING
	nowPlaying=PLAYING_NONE; // 演奏終了(V2.2)
	#endif
	self.avAudioPlayer=nil; // これで解放もされる
}

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

-(BOOL)audioplayStat
{
	printSelf();
#ifdef USE_NOWPLAYING
	return nowPlaying; // V2.2
#else
	return _avAudioPlayer.playing;
#endif
}

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

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
// 演奏終了時デリゲート
{
	nslog(@"audioPlayerDidFinishPlaying:successfully=%d",flag);
#ifdef USE_NOWPLAYING
	nowPlaying=PLAYING_NONE; // 演奏終了(V2.2)
#endif
}

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

@end
