/*	
 *	frammod1.c
 *
 *	高速RAMに転送するモジュール
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2002 Naoyuki Sawa
 *
 *	* Tue Nov 19 12:30:00 JST 2002 Naoyuki Sawa
 *	- 旧clipdobj.cとclipmod.cから、コピー・修正して新規作成しました。
 *	* Sun Dec 8 05:43:00 JST 2002 Naoyuki Sawa
 *	- mod_mix()のループ音バグを修正。
 *	- テンポエフェクト対応。
 *	* Fri Jan 31 21:57:00 JST 2003 Naoyuki Sawa
 *	- new_LCDDrawObject()のPIXEL(p,m)マクロのmask判定部分を改良。
 *	 「(mask >> (m) & 1)」→「(mask & (1 << (m)))」に変更しました。
 *	  約3%高速化(全画面転送時)、コードサイズは20バイト少なくなりました。
 *	  この改良は「ryukの薫製工房」さん(http://www.interq.or.jp/earth/ryuk/)によるものです。
 *	- new_LCDDrawObject()のクリッピング時画面外判定処理を改良。
 *	  これまで、反転なし/ありそれぞれの処理の中で行っていたものを、共通化してまとめました。
 *	  コードサイズが4バイト少なくなりました。
 *	- 以上の変更を行った結果、現時点のコードサイズは0xfe0(4064)バイトです。
 *	* Sat Feb 1 14:08:00 JST 2003 Naoyuki Sawa
 *	- new_LCDDrawObject()にて、残りライン数の取得「y = obj.dh」を共通化、容量節約しました。
 *	- MODドライバを可変チャネル数に対応しました。
 *	* Tue Apr 1 19:00:00 JST 2003 Naoyuki Sawa
 *	- clipfram.cを分割し、このファイルを新設しました。
 */
#include "clip.h"

/****************************************************************************
 *	P/ECE MOD Driver (シーケンサー)
 ****************************************************************************/

int
mod_seq(MODDRIVER* driver)
{
	MODHEADER* header = driver->header;
	MODSAMPLE* sample;
	MODCHANNEL* channel;
	int chno, smpno, period, effect, eff_ff, eff_0f, eff_f0;
	unsigned int *row, note;

	/* チックを進めます。 */
	driver->tick += driver->tempo;
	if(driver->tick >= driver->speed * 125) {
		driver->tick -= driver->speed * 125;

		/* ローを取得します。
		 * * ローやシーケンス番号は、エフェクトによって変更される可能性があるので、
		 *   ローからシーケンス番号への繰上げや終了判定は、ロー取得直前に行います。
		 */
		if(driver->rowno >= 64) {
			driver->rowno = 0;
			driver->seqno++;
		}
		if(driver->seqno >= header->seqlen) {
			if(header->seqrep < 127) { /* イコール127の場合も演奏終了です！ */
				driver->seqno = header->seqrep;
			} else {
				return 1; /* シーケンス終了 */
			}
		}
		row = driver->pattern[header->sequence[driver->seqno]] + (driver->chans * driver->rowno);
		driver->rowno++; /* 次回のためにローを進めておきます */

		/* 各チャネルのノートを処理します。 */
		for(chno = 0, channel = driver->channel; chno < driver->chans; chno++, channel++, row++) {
			note = SWAPW(*row);

			/* サンプル番号を変更します。 */
			smpno = (note >> 24 & 0xf0) | (note >> 12 & 0xf);
			if(smpno) {
				smpno--; /* ノート上のサンプル番号は#1ベースなので、#0ベースに補正 */
				sample = &header->sample[smpno];
				channel->smpno = smpno;
				channel->volume = sample->volume;
			}

			/* ピリオド値を変更します。 */
			period = note >> 16 & 0xfff;
			if(period) {
				channel->period = period; /* 発声開始 */
				channel->offset = 0;
			}

			/* エフェクトを格納します。 */
			effect = note & 0xfff;
			channel->effect = effect;
		}
	}

	/* 各チャネルのエフェクトを処理します。 */
	for(chno = 0, channel = driver->channel; chno < driver->chans; chno++, channel++) {
		effect = channel->effect;
		if(effect) {
			eff_ff = effect      & 0xff;	/* bit 7〜0 */
			eff_0f = effect      & 0x0f;	/* bit 3〜0 */
			eff_f0 = effect >> 4 & 0x0f;	/* bit 7〜4 */
			switch(  effect >> 8 & 0x0f) {	/* bit11〜8 */
			/*---------- 音程に関するエフェクト ----------*/
			case 0x1: /* Slide Up */
				channel->period -= eff_ff;
				if(channel->period < 0) channel->period = 0;
				/* エフェクト継続 */
				break;
			case 0x2: /* Slide Down */
				channel->period += eff_ff;
				if(channel->period > 0xfff) channel->period = 0xfff;
				/* エフェクト継続 */
				break;
			/*---------- 音量に関するエフェクト ----------*/
			case 0x5: /* Tone Portamento + Volume Slide */
			case 0x6: /* Vibrato + Volume Slide */
			case 0xa: /* Volume Slide */
				/* トーンポルタメント・ビブラートは未対応です。 */
				if(eff_0f) {
					channel->volume -= eff_0f;
					if(channel->volume < 0) channel->volume = 0;
				} else {
					channel->volume += eff_f0;
					if(channel->volume > 64) channel->volume = 64;
				}
				/* エフェクト継続 */
				break;
			case 0xc: /* Set Volume */
				channel->volume = eff_ff;
				channel->effect = 0; /* エフェクト解除 */
				break;
			/*---------- 演奏位置に関するエフェクト ----------*/
			case 0x9: /* Set Sample Offset */
				channel->offset = eff_ff << (8/*MOD仕様*/ + 8/*固定小数*/);
				channel->effect = 0; /* エフェクト解除 */
				break;
			case 0xb: /* Position Jump */
				driver->seqno = eff_ff;
				driver->rowno = 0;
				channel->effect = 0; /* エフェクト解除 */
				break;
			case 0xd: /* Pattern Break */
				/* 注意！同一ROWに二つ以上のPattern Breakを置かないで下さい！
				 * もしも同一ROWに二つのPattern Breakがあると、一度に二つ先のパターンへ進んでしまいます。
				 * Mod Plug Trackerでは、同一ROWに二つ以上のPattern Breakがあっても、一つしか先へ進まないのですが、
				 * P/ECE MOD Driverでは、容量制限が厳しいため、このチェックを行うことができませんでした。
				 * データ作成時に注意して、同一ROWに二つ以上のPattern Breakを置かないようにしてください。
				 */
				driver->seqno++;
				driver->rowno = eff_ff;
				channel->effect = 0; /* エフェクト解除 */
				break;
			case 0xf: /* Set Speed */
				if(eff_ff <= 32) {
					driver->speed = eff_ff; /*  0〜32:スピード設定 */
				} else {
					driver->tempo = eff_ff; /* 33〜255:テンポ設定  */
				}
				channel->effect = 0; /* エフェクト解除 */
				break;
			/*{{未対応エフェクト*/
			//case 0x0: /* Arpeggio */
			//case 0x3: /* Tone Portamento */
			//case 0x4: /* Vibrato */
			//case 0x7: /* Tremolo */
			//case 0x8: /* Set Panning Position */
			/**/#ifdef WIN32
			/**/default:
			/**/	fprintf(stderr, "IGN-EFF: %03x\n", effect);
			/**/	break;
			/**/#endif /*WIN32*/
			/*}}未対応エフェクト*/
			case 0xe: /* Extended Effects */
				switch(eff_f0) {
				/*---------- 音程に関する拡張エフェクト ----------*/
				case 0x1: /* Fine Slide Up */
					channel->period -= eff_0f;
					if(channel->period < 0) channel->period = 0;
					channel->effect = 0; /* エフェクト解除 */
					break;
				case 0x2: /* Fine Slide Down */
					channel->period += eff_0f;
					if(channel->period > 0xfff) channel->period = 0xfff;
					channel->effect = 0; /* エフェクト解除 */
					break;
				/*---------- 音量に関する拡張エフェクト ----------*/
				case 0xa: /* Fine Volume Slide Up */
					channel->volume += eff_0f;
					if(channel->volume > 64) channel->volume = 64;
					channel->effect = 0; /* エフェクト解除 */
					break;
				case 0xb: /* Fine Volume Slide Down */
					channel->volume -= eff_0f;
					if(channel->volume < 0) channel->volume = 0;
					channel->effect = 0; /* エフェクト解除 */
					break;
				/*{{未対応拡張エフェクト*/
				//case 0x0: /* Set Filter */
				//case 0x3: /* Glissando Control */
				//case 0x4: /* Set Vibrato Waveform */
				//case 0x5: /* Set Fine Tune */
				//case 0x6: /* Set/Jump to Loop */
				//case 0x7: /* Set Tremolo Waveform */
				//case 0x8: /* NOT USED */
				//case 0x9: /* Retrig Note */
				//case 0xc: /* Note Cut */
				//case 0xd: /* Note Delay */
				//case 0xe: /* Pattern Delay */
				//case 0xf: /* Invert Loop */
				/**/#ifdef WIN32
				/**/default:
				/**/	fprintf(stderr, "IGN-EFF: %03x\n", effect);
				/**/	break;
				/**/#endif /*WIN32*/
				/*}}未対応拡張エフェクト*/
				}
				break;
			}
		}
	}

	return 0;
}

