/*	
 *	frammod2.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_mix(MODDRIVER* driver, short wbuff[/*MODBUFLEN*/])
{
	static int mixbuf[MODBUFLEN]; /* ミキシングバッファ、ＳＴＡＴＩＣです！ */

	MODHEADER* header = driver->header;
	int i, chno, period, length, repofs, replen, volume, offset, step;
	int* pmix;
	short* pout;
	char* smpdat;
	MODSAMPLE* sample;
	MODCHANNEL* channel;

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

	/* 発生中の各チャネルについて... */
	for(chno = 0, channel = driver->channel; chno < driver->chans; chno++, channel++) {
		if(!(period = channel->period)) continue;

		/* サンプル情報などを取り出しておきます。 */
		sample = &header->sample[channel->smpno];
		smpdat = driver->smpdat[channel->smpno];
		length = SWAPH(sample->length) * 2 << 8; /* 24.8bit */
		repofs = SWAPH(sample->repofs) * 2 << 8; /* 24.8bit */
		replen = SWAPH(sample->replen) * 2 << 8; /* 24.8bit */
		if(replen <= 1 * 2 << 8) replen = 0; /* ※歴史上の理由(？)でreplen=1もループなしとする */
		volume = channel->volume;
		offset = channel->offset;

		/* ピリオド値からサンプルオフセット増分を求めます。
		 * * 定数の由来：
		 *	AmigaのPAL基本クロック(?) = 7093789.2[Hz] ⇒ 二分周してサウンド回路に供給？
		 *	P/ECEのサウンド出力周波数 =   16000  [Hz]
		 * * サンプルオフセット増分：
		 *	再生レート    = 7093789.2 / (period * 2)
		 *	増分[24.8bit] = (再生レート / 16000) * 256
		 *	              = ((7093789.2 / (period * 2)) / 16000) * 256
		 *	              = (((7093789.2 / 2) / period) / 16000) * 256
		 *	              = ((7093789.2 / (2 * 16000)) / period) * 256
		 *	              = ((7093789.2 * 256) / (2 * 16000)) / period
		 *	              = 56750.3136 / period
		 */
		step = 56750 / period;

		for(i = 0, pmix = mixbuf; i < MODBUFLEN; i++, pmix++) {
LOOP:
			/* サンプルオフセットをサンプルデータ内にクリピングします。 */
			if(!replen) {
				/* ループなし：
				 * 0→lengthまで再生して、発声停止します。
				 */
				if(offset >= length) {
					channel->period = 0; /* 発声停止 */
					goto NO_LOOP;
				}
			} else {
				/* ループあり：
				 * 0→(repofs+replen),repofs→(repofs+replen),...
				 * ※(repofs+replen)〜lengthの区間は再生しません。
				 */
				if(offset >= repofs + replen) {
					offset -= replen;
					goto LOOP;
				}
			}

			/* ミキシングバッファにサンプル値×ボリュームを加算します。 */
			*pmix += smpdat[offset >> 8] * volume;

			/* サンプルオフセットを進めます。 */
			offset += step;
		}
NO_LOOP:
		/* サンプルオフセットを書き戻します。 */
		channel->offset = offset;
	}

	/* ミキシングバッファ(32ビット)から出力バッファ(16ビット)へコピーします。 */
	for(i = 0, pmix = mixbuf, pout = wbuff; i < MODBUFLEN; i++, pmix++, pout++) {
		volume = *pmix << 1/*音量増幅、調整可*/;
		     if(volume < -32767) volume = -32767;
		else if(volume >  32767) volume =  32767;
		*pout = volume;
	}

	return 0;
}

