/*	
 *	framdms.c
 *
 *	P/ECE DMG-Sound Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Thu Jan 15 20:09:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/****************************************************************************
 *	内部関数
 ****************************************************************************/

int
dmgsound_process(DMGSOUND* ds, int cycle)
{
	int so = ds->sound_output;
	int lf = 0;
	int ri = 0;
	int v;

	/* Ch#1 */
	v = dmgsound_tone_process(ds, &ds->ch1, cycle);		/* 符号付き5bit */
	if(so & 0x01) lf += v;
	if(so & 0x10) ri += v;

	/* Ch#2 */
	v = dmgsound_tone_process(ds, &ds->ch2, cycle);		/* 符号付き5bit */
	if(so & 0x02) lf += v;
	if(so & 0x20) ri += v;

	/* Ch#3 */
	v = dmgsound_pcm_process(ds, &ds->ch3, cycle);		/* 符号付き5bit */
	if(so & 0x04) lf += v;
	if(so & 0x40) ri += v;

	/* Ch#4 */
	v = dmgsound_noise_process(ds, &ds->ch4, cycle);	/* 符号付き5bit */
	if(so & 0x08) lf += v;
	if(so & 0x80) ri += v;

	/* 左右のボリュームを掛けて、足し合わせます。 */
	lf *= ds->volume[0]; /* {0〜7} */
	ri *= ds->volume[1]; /* {0〜7} */

	/* 符号付き5bit×4×{0〜7}×2 = 符号付き11bit */
	return lf + ri;
}

int
dmgsound_tone_process(DMGSOUND* ds, DMGSOUNDTONE* ch, int cycle)
{
	/* Sound Length */
	if(ch->sound_length > 0) {
		ch->sound_length -= cycle;
		if(ch->sound_length < 0) ch->sound_length = 0;
	}
	if(!ch->sound_length) return 0;

	/* Sweep */
	if(ch->sweep_time && ch->sweep_shift) {
		ch->sweep_progress -= cycle;
		while(ch->sweep_progress <= 0) {
			ch->sweep_progress += ch->sweep_time;
			if(!ch->sweep_up_down) { /* 0:増加 */
				ch->frequency += ch->frequency >> ch->sweep_shift;
				if(ch->frequency > 8192 - 1) {
					ch->sound_length = 0; /* スイープオーバーフローで停止?(※要確認) */
					return 0;
				}
			} else {                 /* 1:減少 */
				ch->frequency -= ch->frequency >> ch->sweep_shift;
				if(ch->frequency < 0) {
					ch->sound_length = 0; /* スイープアンダーフローで停止?(※要確認) */
					return 0;
				}
			}
		}
	}

	/* Envelope */
	if(ch->envelope_time) {
		ch->envelope_progress -= cycle;
		while(ch->envelope_progress <= 0) {
			ch->envelope_progress += ch->envelope_time;
			if(!ch->envelope_up_down) { /* 0:減少 */
				if(ch->envelope > 0) ch->envelope--;
			} else {                    /* 1:増加 */
				if(ch->envelope < 15) ch->envelope++;
			}
		}
	}
	if(!ch->envelope) return 0;

	/* Wave Pattern / Frequency */
	ch->frequency_progress -= cycle;
	while(ch->frequency_progress <= 0) {
		ch->frequency_progress += 8192 - ch->frequency;
		ch->wave_pattern = (ch->wave_pattern + 1) & 7;
	}

	return ch->wave_pattern < ch->wave_pattern_duty ?  ch->envelope
							: -ch->envelope;
}

int
dmgsound_pcm_process(DMGSOUND* ds, DMGSOUNDPCM* ch, int cycle)
{
	/* Sound ON/OFF */
	if(!ch->sound_on_off) return 0;

	/* Sound Length */
	if(ch->sound_length > 0) {
		ch->sound_length -= cycle;
		if(ch->sound_length < 0) ch->sound_length = 0;
	}
	if(!ch->sound_length) return 0;

	/* Output Level */
	if(!ch->output_level) return 0;

	/* Wave Pattern / Frequency */
	ch->frequency_progress -= cycle;
	while(ch->frequency_progress <= 0) {
		ch->frequency_progress += 4096 - ch->frequency;
		ch->wave_pattern = (ch->wave_pattern + 1) & 31;
	}

	return ch->wave_pattern_ram[ch->wave_pattern] >> (ch->output_level - 1);
}

int
dmgsound_noise_process(DMGSOUND* ds, DMGSOUNDNOISE* ch, int cycle)
{
	/* Sound Length */
	if(ch->sound_length > 0) {
		ch->sound_length -= cycle;
		if(ch->sound_length < 0) ch->sound_length = 0;
	}
	if(!ch->sound_length) return 0;

	/* Envelope */
	if(ch->envelope_time) {
		ch->envelope_progress -= cycle;
		while(ch->envelope_progress <= 0) {
			ch->envelope_progress += ch->envelope_time;
			if(!ch->envelope_up_down) { /* 0:減少 */
				if(ch->envelope > 0) ch->envelope--;
			} else {                    /* 1:増加 */
				if(ch->envelope < 15) ch->envelope++;
			}
		}
	}
	if(!ch->envelope) return 0;

	/* LFSR / Frequency */
	ch->frequency_progress -= cycle;
	while(ch->frequency_progress <= 0) {
		ch->frequency_progress += ch->frequency;
		if(ch->lfsr_bits) { /* 0:7bit */
			/* 7bit2結線のタップ: 0x41 を使用。 */
			ch->lfsr = (ch->lfsr >> 1) |
				  ((ch->lfsr & 0x40) ^ (ch->lfsr & 1) << 6);
		} else {            /* 1:15bit */
			/* 15bit2結線のタップ: 0x4001 を使用。 */
			ch->lfsr = (ch->lfsr >> 1) |
				  ((ch->lfsr & 0x4000) ^ (ch->lfsr & 1) << 14);
		}
	}

	return ch->lfsr & 1 ?  ch->envelope
			    : -ch->envelope;
}

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

void
dmgsound_mix(DMGSOUND* ds, short wbuff[/*count*/], int count)
{
	DDA progress;
	int cycle;
	int sample;

	/* サウンド回路停止中? */
	if(!ds->all_sound_on_off) {
		memset(wbuff, 0, count);
		return;
	}

	progress = ds->progress; /* 高速化のため、一時取り出し */
	while(count > 0) {
		/* 経過サイクル数を求めます。 */
		cycle = progress.u;
		progress.d += progress.n;
		if(progress.d >= 0) {
			progress.d -= SPEAKER_FREQUENCY;
			cycle++;
		}

		/* 1サンプルミキシングします。 */
		sample = dmgsound_process(ds, cycle);	/* 符号付き11bit */
		sample <<= (16 - 11) + 2/*調整可*/;

		/* クリッピングして格納します。 */
		if(sample < -0x7fff) sample = -0x7fff;
		if(sample >  0x7fff) sample =  0x7fff;
		*wbuff++ = sample;

		/* 残りサンプル数を減らします。 */
		count--;
	}
	ds->progress = progress; /* 書き戻し。忘れずないで!! */
}

