/*	
 *	framit.c
 *
 *	P/ECE IT Driver
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Thu Nov 18 18:59:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/* * エラーコードの値は、行番号を負値にしたものです。
 *   プログラムを修正すると、エラーコードも変わる可能\性があります。
 *   エラーコードの値は、デバッグのためだけに利用してください。
 */
#define ERRNO	(-__LINE__)

/*
 *	-------	-------	-----------------------	-------	-----------------------
 *						PlayIT	
 *	Command	Name	Description		Support	Comment
 *	-------	-------	-----------------------	-------	-----------------------
 *	 1	Axy	Set speed		○	
 *	 2	Bxy	Position jump			
 *	 3	Cxy	Pattern break		○	
 *	 4	Dxy	Volume slide		○	
 *	 5	Exy	Portamento down		○	
 *	 6	Fxy	Portamento up		○	
 *	 7	Gxy	Tone portamento		○	
 *	 8	Hxy	Vibrato				
 *	 9	Ixy	Tremor				
 *	10	Jxy	Arpeggio			
 *	11	Kxy	Volslide+Vibrato	○	
 *	12	Lxy	Volslide+Toneporta	○	
 *	13	Mxy	Set channel volume	○	
 *	14	Nxy	Channel volslide	○	
 *	15	Oxy	Set offset		○	
 *	16	Pxy	Panning slide		○	
 *	17	Qxy	Retrigger note			
 *	18	Rxy	Tremolo				
 *	19	Sxy	Extended IT commands	○	
 *	20	Txy	Set tempo		○	
 *	21	Uxy	Fine vibrato			
 *	22	Vxy	Set global volume	○	
 *	23	Wxy	Global volume slide	○	
 *	24	Xxy	Set panning		○	
 *	25	Yxy	Panbrello			
 *	26	Zxy	Midi macro			
 *	-------	-------	-----------------------	-------	-----------------------
 *	19	S0x	---				
 *	19	S1x	Glissando control		
 *	19	S2x	---				
 *	19	S3x	Vibrato waveform		
 *	19	S4x	Tremolo waveform		
 *	19	S5x	Panbrello waveform		
 *	19	S6x	Fine pattern delay		
 *	19	S7x	Instr. control		○	
 *	19	S8x	Set panning			
 *	19	S9x	Sound control			
 *	19	SAx	Set high offset			
 *	19	SBx	Pattern loop			
 *	19	SCx	Note cut			
 *	19	SDx	Note delay			
 *	19	SEx	Pattern delay			
 *	19	SFx	Set active macro		
 *	-------	-------	-----------------------	-------	-----------------------
 */

/****************************************************************************
 *	P/ECE IT Driver
 ****************************************************************************/

void it_mix1_8bit(ITCHANNEL* channel, int volume, int frequency, int* mixbuf);
void it_mix1_16bit(ITCHANNEL* channel, int volume, int frequency, int* mixbuf);
void it_mix2(short* wbuff, const int* mixbuf);

static ITCHANNEL*
get_next_channel_marker(ITDRIVER* driver, ITCHANNELMARKER* channel_marker)
{
	const unsigned char* pattern_position;
	int channel_variable;
	int mask_variable;
	int i_channel;
	ITCHANNEL* channel;

	pattern_position = driver->pattern_position; /* 取り出し */

	/* Channel variableを取得します。 */
	channel_variable = *pattern_position++;
	if(!channel_variable) { /* end of row */
		channel = NULL;
		goto L_EXIT;
	}
	i_channel = (channel_variable - 1) & 63;
	channel = &driver->channels[i_channel];

	/* Mask variableを取得します。 */
	if(channel_variable & 128) {
		mask_variable = *pattern_position++;
		channel->previous_mask_variable = mask_variable;
	} else {
		mask_variable = channel->previous_mask_variable;
	}

	/* Noteを取得します。 */
	if(mask_variable & 1) {
		channel_marker->note = *pattern_position++;
		channel->last_channel_marker.note = channel_marker->note;
	} else if(mask_variable & 16) {
		channel_marker->note = channel->last_channel_marker.note;
	} else {
		channel_marker->note = -1;
	}

	/* Instrumentを取得します。 */
	if(mask_variable & 2) {
		channel_marker->instrument = *pattern_position++;
		channel->last_channel_marker.instrument = channel_marker->instrument;
	} else if(mask_variable & 32) {
		channel_marker->instrument = channel->last_channel_marker.instrument;
	} else {
		channel_marker->instrument = -1;
	}

	/* Volume/Panningを取得します。 */
	if(mask_variable & 4) {
		channel_marker->volume_panning = *pattern_position++;
		channel->last_channel_marker.volume_panning = channel_marker->volume_panning;
	} else if(mask_variable & 64) {
		channel_marker->volume_panning = channel->last_channel_marker.volume_panning;
	} else {
		channel_marker->volume_panning = -1;
	}

	/* Command、Command valueを取得します。 */
	if(mask_variable & 8) {
		channel_marker->command       = *pattern_position++;
		channel_marker->command_value = *pattern_position++;
		channel->last_channel_marker.command       = channel_marker->command;
		channel->last_channel_marker.command_value = channel_marker->command_value;
	} else if(mask_variable & 128) {
		channel_marker->command       = channel->last_channel_marker.command;
		channel_marker->command_value = channel->last_channel_marker.command_value;
	} else {
		channel_marker->command       = -1;
		channel_marker->command_value = -1;
	}

L_EXIT:
	driver->pattern_position = pattern_position; /* 書き戻し */

	return channel;
}

int
it_run(ITDRIVER* driver)
{
	ITCHANNELMARKER channel_marker;
	int i_channel;
	int i_pattern;
	int i_sample;
	ITCHANNEL* channel;
	ITINSTRUMENT* instrument;
	ITSAMPLE* sample;

	if(driver->stat) goto L_EXIT;

	/* 1[Tick]未満の時間経過をカウントアップします。
	 * Tempoの設定値が125の場合に、1[Tick]=1/50[Sec]となります。
	 */
	driver->progress += driver->tempo;
	while(driver->progress >= 125) {
		driver->progress -= 125;

		/* 1[Tick]経過しました。
		 * 経過Tick数がSpeedの設定値に達したら、1[Row]進みます。
		 */
		driver->tick++;
		while(driver->tick >= driver->speed) {
			driver->tick -= driver->speed;

			/* Position jumpが要求されていたら... */
			if(driver->position_jump != -1) {

				/* Orderを設定します。
				 * * 不正なOrderへのPosition jumpは、Pattern取得時にSong終端として検出されます。
				 */
				driver->order = driver->position_jump;
				driver->row = 0; /* 必要!! */

				/* Patternを取得します。 */
				for(;;) {
					if((driver->order < 0) ||
					   (driver->order > driver->ordnum - 1)) {
						driver->stat = ERRNO; /* Song終端 */
						goto L_EXIT;
					}
					i_pattern = driver->orders[driver->order];
					if(i_pattern != 254) { /* Skipでない */
						if((i_pattern < 0) ||
						   (i_pattern > driver->patnum - 1)) {
							driver->stat = ERRNO; /* Pattern不正 */
							goto L_EXIT;
						}
						driver->pattern = &driver->patterns[i_pattern];
						if(driver->pattern->rows) { /* 欠番でない */
							driver->pattern_position = driver->pattern->packed_data;
							break;
						}
					}
					driver->order++;
				}

				/* Position jumpを解除します。 */
				driver->position_jump = -1;
			}

			/* Pattern breakが要求されているか、またはPatternが終了したら... */
			if((driver->pattern_break != -1) ||
			   (driver->row >= driver->pattern->rows)) {

				/* 次のOrderの先頭のRowへ進めます。
				 * * Song終端は、Pattern取得時に検出されます。
				 * * ITには、MODやXMにあったような、Song終端でのループ制御がありません。
				 *   ループするSongには、明示的なPosition jumpが設定必要です。
				 */
				driver->order++;
				driver->row = 0;

				/* Patternを取得します。 */
				for(;;) {
					if((driver->order < 0) ||
					   (driver->order > driver->ordnum - 1)) {
						driver->stat = ERRNO; /* Song終端 */
						goto L_EXIT;
					}
					i_pattern = driver->orders[driver->order];
					if(i_pattern != 254) { /* Skipでない */
						if((i_pattern < 0) ||
						   (i_pattern > driver->patnum - 1)) {
							driver->stat = ERRNO; /* Pattern不正 */
							goto L_EXIT;
						}
						driver->pattern = &driver->patterns[i_pattern];
						if(driver->pattern->rows) { /* 欠番でない */
							driver->pattern_position = driver->pattern->packed_data;
							break;
						}
					}
					driver->order++;
				}

				/* Pattern breakが要求されていたら... */
				if(driver->pattern_break != -1) {

					/* Pattern breakによって指定されたRowまで進めます。 */
					if((driver->pattern_break < 0) ||
					   (driver->pattern_break > driver->pattern->rows - 1)) {
						driver->stat = ERRNO; /* Pattern break不正 */
						goto L_EXIT;
					}
					while(driver->row < driver->pattern_break) {
						for(;;) {
							channel = get_next_channel_marker(driver, &channel_marker);
							if(!channel) {
								break; /* end of row */
							}
						}
						driver->row++;
					}

					/* Pattern breakを解除します。 */
					driver->pattern_break = -1;
				}
			}

			/* 次回のために、Rowを進めておきます。 */
			driver->row++;

			/* まず、確実にVolume/PanningとCommandを解除します。 */
			for(i_channel = 0, channel = driver->channels;
			    i_channel < 64;
			    i_channel++, channel++) {
				channel->volume_panning = -1; /* 解除 */
				channel->command = -1; /* 解除 */
				/* Command valueは継続する可能\性があるので、クリアしてはダメ!! */
			}

			/* 1[Row]分のPattern dataを処理します。 */
			for(;;) {

				/* Channel markerを取得します。 */
				channel = get_next_channel_marker(driver, &channel_marker);
				if(!channel) {
					break; /* end of row */
				}

				/* 無効Channelならばスキップします。 */
				if(channel->stat == ITCHANNEL_DISABLED) {
					continue;
				}

				/* Instrumentを実行します。 */
				if(channel_marker.instrument != -1) {
					if((channel_marker.instrument >= 1) &&
					   (channel_marker.instrument <= driver->insnum)) {
						channel->pending_instrument = &driver->instruments[channel_marker.instrument - 1];
					}
				}

				/* Noteを実行します。 */
				if(channel_marker.note != -1) {
					switch(channel_marker.note) {
					case 255: /* note off */
						/* ※TODO:仮です。 */
						channel->stat = ITCHANNEL_SILENT;
						break;

					case 254: /* notecut */
						/* ※TODO:仮です。 */
						channel->stat = ITCHANNEL_SILENT;
						break;

					default: /* note on */
						if((channel_marker.note >= 0) &&
						   (channel_marker.note <= 119)) { /* C-0 -> B-9 */

							/* 実効NoteとSampleを取得します。 */
							instrument = channel->pending_instrument;
							i_sample = instrument->note_sample_keyboard_table[channel_marker.note].sample;
							/*↑この順番必須!! 逆にするとchannel_marker.noteが先に破壊されてしまいます↓*/
							channel_marker.note = instrument->note_sample_keyboard_table[channel_marker.note].note;
							if(((channel_marker.note >= 0) && (channel_marker.note <= 119)) && /* C-0 -> B-9 */
							   ((i_sample >= 1) && (i_sample <= driver->smpnum))) {

								/* 有効Sampleならば... */
								sample = &driver->samples[i_sample - 1/*忘れないで!!*/];
								if(sample->sample_data) {

									/* 発声開始します。 */
									channel->stat = ITCHANNEL_NOTEON;
									channel->playing_instrument = instrument;
									channel->playing_sample = sample;
									channel->vol = sample->vol;
									channel->sample_position = sample->sample_data;
									channel->sample_direction = 0;
									channel->progress = 0;
									channel->pitch = channel_marker.note << 8;
								}
							}
						}
						break;
					}
				}

				/* Volume/Panningが有効ならば、格納します。 */
				if(channel_marker.volume_panning != -1) {
					channel->volume_panning = channel_marker.volume_panning;
				}

				/* Commandが有効ならば、格納します。 */
				if(channel_marker.command != -1) {
					channel->command = channel_marker.command;
					channel->command_value = channel_marker.command_value;
				}
			}
		}

		/* 各チャネルについて... */
		for(i_channel = 0, channel = driver->channels;
		    i_channel < 64;
		    i_channel++, channel++) {

			/* Volume/Panningが有効ならば、実行します。 */
			channel_marker.volume_panning = channel->volume_panning;
			if(channel_marker.volume_panning != -1) {
				if((channel_marker.volume_panning >= 0) &&
				   (channel_marker.volume_panning <= 64)) {
					channel->vol = channel_marker.volume_panning;
				} else {
					/* ※TODO: */
				}
			}

			/* Commandが有効ならば、実行します。 */
			channel_marker.command = channel->command; /* 取り出し */
			if(channel_marker.command) {
				channel_marker.command_value = channel->command_value;
				switch(channel_marker.command) {
				case  1: /* Axy Set speed */
					driver->speed = channel_marker.command_value;
					if(driver->speed < 1) {
						driver->stat = ERRNO; /* Speed不正 */
						goto L_EXIT;
					}
					channel_marker.command = -1; /* 解除 */
					break;
				case  2: /* Bxy Position jump */
					driver->position_jump = channel_marker.command_value;
					channel_marker.command = -1; /* 解除 */
					break;
				case  3: /* Cxy Pattern break */
					driver->pattern_break = channel_marker.command_value;
					channel_marker.command = -1; /* 解除 */
					break;
				//case  4: /* Dxy Volume slide */
				//case  5: /* Exy Portamento down */
				//case  6: /* Fxy Portamento up */
				//case  7: /* Gxy Tone portamento */
				//case  8: /* Hxy Vibrato */
				//case  9: /* Ixy Tremor */
				//case 10: /* Jxy Arpeggio */
				//case 11: /* Kxy Volslide+Vibrato */
				//case 12: /* Lxy Volslide+Toneporta */
				case 13: /* Mxy Set channel volume */
					channel->chnl_vol = channel_marker.command_value;
					channel_marker.command = -1; /* 解除 */
					break;
				//case 14: /* Nxy Channel volslide */
				//case 15: /* Oxy Set offset */
				//case 16: /* Pxy Panning slide */
				//case 17: /* Qxy Retrigger note */
				//case 18: /* Rxy Tremolo */
				//case 19: /* Sxy Extended IT commands */
				case 20: /* Txy Set tempo */
					driver->tempo = channel_marker.command_value;
					if(driver->tempo < 1) {
						driver->stat = ERRNO; /* Tempo不正 */
						goto L_EXIT;
					}
					channel_marker.command = -1; /* 解除 */
					break;
				//case 21: /* Uxy Fine vibrato */
				case 22: /* Vxy Set global volume */
					driver->gv = channel_marker.command_value;
					channel_marker.command = -1; /* 解除 */
					break;
				//case 23: /* Wxy Global volume slide */
				//case 24: /* Xxy Set panning */
				//case 25: /* Yxy Panbrello */
				//case 26: /* Zxy Midi macro */
				}
				channel->command = channel_marker.command; /* 書き戻し */
			}
		}
	}

L_EXIT:
	return driver->stat;
}

int
it_mix(ITDRIVER* driver, short wbuff[/*ITBUFLEN*/])
{
	static int mixbuf[ITBUFLEN]; /* ミキシングバッファ、ＳＴＡＴＩＣです！ */
	//
	int i_channel;
	int final_volume;
	int pitch;
	int frequency;
	ITCHANNEL* channel;
	ITINSTRUMENT* instrument;
	ITSAMPLE* sample;

	if(driver->stat) goto L_EXIT;

	/* まず、ミキシングバッファをクリアします。 */
	memset(mixbuf, 0, sizeof mixbuf);

	/* 発声中の各チャネルについて... */
	for(i_channel = 0, channel = driver->channels;
	    i_channel < 64;
	    i_channel++, channel++) {

		/* DisabledまたはSilentならば飛ばします。 */
		if(channel->stat <= ITCHANNEL_SILENT) continue;

		instrument = channel->playing_instrument;
		sample = channel->playing_sample;

		/* ボリュームを求めます。 */
		final_volume  = channel->vol;		/* Volume at which note is to be played (Ranges from 0 to  64) + 6   bit= 6bit */
		final_volume *= sample->gvl;		/* Sample volume                        (Ranges from 0 to  64) + 6   bit=12bit */
		final_volume *= instrument->gbv >> 1;	/* Instrument volume                    (Ranges from 0 to 128) +(7-1)bit=18bit */
		final_volume *= channel->chnl_vol;	/* Channel volume                       (Ranges from 0 to  64) + 6   bit=24bit */
		final_volume *= driver->gv >> 1;	/* Global volume                        (Ranges from 0 to 128) +(7-1)bit=30bit */
		final_volume >>= 20;			/*                                                             - 20  bit=10bit */
		if(!final_volume) continue; /* 無音 */

		/* 再生周波数を求めます。 */
		pitch = channel->pitch >> 8;
		if((pitch < 0) ||
		   (pitch > 119)) continue;
		frequency = sample->c5speed;
		frequency *= it_pitch_table[pitch % 12];
		frequency >>= (10 - pitch / 12) + (10/*小数部bit数*/ - 5);

		if(sample->flg & (1 << 1/*16 bit sample*/)) {
			it_mix1_16bit(channel, final_volume, frequency, mixbuf);
		} else { /* 8 bit sample */
			it_mix1_8bit(channel, final_volume, frequency, mixbuf);
		}
	}

	/* ミキシングバッファをクリッピングして、出力バッファへ書き込みます。 */
	it_mix2(wbuff, mixbuf);

L_EXIT:
	return driver->stat;
}

void
it_mix1_8bit(ITCHANNEL* channel, int volume, int frequency, int* mixbuf)
{
	int n;
	int stat;
	ITSAMPLE* sample;
	int flg;
	const unsigned char* sample_end;
	const unsigned char* loop_begin;
	const unsigned char* loop_end;
	const unsigned char* susloop_begin;
	const unsigned char* susloop_end;
	const unsigned char* sample_position;
	int sample_direction;
	int progress;

	/* 8bit sample -> 16bit sample */
	volume <<= 8;

	/* 書き戻し不要 */
	stat = channel->stat;
	sample = channel->playing_sample;
	flg = sample->flg;
	sample_end = sample->sample_end;
	loop_begin = sample->loop_begin;
	loop_end = sample->loop_end;
	susloop_begin = sample->susloop_begin;
	susloop_end = sample->susloop_end;

	/* 書き戻し必要 */
	sample_position = channel->sample_position;
	sample_direction = channel->sample_direction;
	progress = channel->progress;

	n = ITBUFLEN;
	do {
		progress += frequency;
		while(progress >= SPEAKER_FREQUENCY) {
			progress -= SPEAKER_FREQUENCY;

			if(sample_direction == 0/*Forward*/) {
				sample_position += sizeof(char);
				if((stat == ITCHANNEL_NOTEON) &&
				   (flg & (1 << 5/*Sustain loop on*/)) &&
				   (sample_position >= susloop_end)) {
					if(flg & (1 << 7/*Ping Pong sustain loop*/)) {
						sample_position = susloop_end - 1;
						sample_direction = 1;
					} else { /* Forward sustain loop */
						sample_position = susloop_begin;
					}
				} else if((flg & (1 << 4/*Loop On*/)) &&
					  (sample_position >= loop_end)) {
					if(flg & (1 << 6/*Ping Pong loop*/)) {
						sample_position = loop_end - 1;
						sample_direction = 1;
					} else { /* Forward loop */
						sample_position = loop_begin;
					}
				} else { /* Loop Off, Sustain loop Off */
					if(sample_position >= sample_end) {
						channel->stat = ITCHANNEL_SILENT;
						return;
					}
				}

			} else { /* Backword */
				sample_position -= sizeof(char);
				if((stat == ITCHANNEL_NOTEON) &&
				   (flg & (1 << 5/*Sustain loop on*/)) &&
				   (sample_position < susloop_begin)) {
					sample_position = susloop_begin;
					sample_direction = 0;
				} else if((flg & (1 << 4/*Loop On*/)) &&
					  (sample_position < loop_begin)) {
					sample_position = loop_begin;
					sample_direction = 0;
				}
			}
		}

		*mixbuf++ += (char)LEBYTE(sample_position) * volume;
	} while(--n);

	/* 書き戻し */
	channel->sample_position = sample_position;
	channel->sample_direction = sample_direction;
	channel->progress = progress;
}

void
it_mix1_16bit(ITCHANNEL* channel, int volume, int frequency, int* mixbuf)
{
	int n;
	int stat;
	ITSAMPLE* sample;
	int flg;
	const unsigned char* sample_end;
	const unsigned char* loop_begin;
	const unsigned char* loop_end;
	const unsigned char* susloop_begin;
	const unsigned char* susloop_end;
	const unsigned char* sample_position;
	int sample_direction;
	int progress;

	/* 書き戻し不要 */
	stat = channel->stat;
	sample = channel->playing_sample;
	flg = sample->flg;
	sample_end = sample->sample_end;
	loop_begin = sample->loop_begin;
	loop_end = sample->loop_end;
	susloop_begin = sample->susloop_begin;
	susloop_end = sample->susloop_end;

	/* 書き戻し必要 */
	sample_position = channel->sample_position;
	sample_direction = channel->sample_direction;
	progress = channel->progress;

	n = ITBUFLEN;
	do {
		progress += frequency;
		while(progress >= SPEAKER_FREQUENCY) {
			progress -= SPEAKER_FREQUENCY;

			if(sample_direction == 0/*Forward*/) {
				sample_position += sizeof(short);
				if((stat == ITCHANNEL_NOTEON) &&
				   (flg & (1 << 5/*Sustain loop on*/)) &&
				   (sample_position >= susloop_end)) {
					if(flg & (1 << 7/*Ping Pong sustain loop*/)) {
						sample_position = susloop_end - 1;
						sample_direction = 1;
					} else { /* Forward sustain loop */
						sample_position = susloop_begin;
					}
				} else if((flg & (1 << 4/*Loop On*/)) &&
					  (sample_position >= loop_end)) {
					if(flg & (1 << 6/*Ping Pong loop*/)) {
						sample_position = loop_end - 1;
						sample_direction = 1;
					} else { /* Forward loop */
						sample_position = loop_begin;
					}
				} else { /* Loop Off, Sustain loop Off */
					if(sample_position >= sample_end) {
						channel->stat = ITCHANNEL_SILENT;
						return;
					}
				}

			} else { /* Backword */
				sample_position -= sizeof(short);
				if((stat == ITCHANNEL_NOTEON) &&
				   (flg & (1 << 5/*Sustain loop on*/)) &&
				   (sample_position < susloop_begin)) {
					sample_position = susloop_begin;
					sample_direction = 0;
				} else if((flg & (1 << 4/*Loop On*/)) &&
					  (sample_position < loop_begin)) {
					sample_position = loop_begin;
					sample_direction = 0;
				}
			}
		}

		*mixbuf++ += (short)LEHALF(sample_position) * volume;
	} while(--n);

	/* 書き戻し */
	channel->sample_position = sample_position;
	channel->sample_direction = sample_direction;
	channel->progress = progress;
}

void
it_mix2(short* wbuff, const int* mixbuf)
{
	int n;
	int output;

	n = ITBUFLEN;
	do {
		/*   final_volume      (10bit)
		 * x sample_output     (16bit)
		 * x max_channels(=64) ( 6bit)
		 * ---------------------------
		 *                     (32bit)
		 */
		output = *mixbuf++ >> ((32 - 16) - 4/*調整分*/);
		if(output >  32767) output =  32767;
		if(output < -32768) output = -32768;
		*wbuff++ = output;
	} while(--n);
}
