/*	
 *	framay3.c
 *
 *	P/ECE AY-3-8910 Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sat Feb 07 19:08:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Sun Mar 06 09:57:00 JST 2004 Naoyuki Sawa
 *	- ay38910_channel_process()に、音量0時の即時return判定を追加しました。
 *	  全チャネルを常に発声しているような曲では、かえって遅くなります。
 *	  しかし実際には、PSGミキシングの速度が問題になるのは、PSGを2個以上使っているケースだと思います。
 *	  PSGを2個以上使っているケースでは、音量0のチャネルが残っている場合が多く、高速化が期待できます。
 *	* Sun Mar 06 19:52:00 JST 2004 Naoyuki Sawa
 *	- 音量カーブの調整を行いました。
 *	  これまで、amplitude_level,envelope_levelは0〜15で、変化はリニアでしたが、
 *	  これではエンベロープ減衰がゆるやかすぎます。
 *	  そこで、それぞれ2乗して 0〜15^2 の値を格納することにしました。
 */
#include "clip.h"

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

void
ay38910_mix(AY38910* ay, short wbuff[/*count*/], int count)
{
	DDA clock_progress;
	int cycle;
	int sample;

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

		/* 1サンプルミキシングします。 */
		sample = ay38910_process(ay, cycle);	/* 符号付き11.5bit */
		sample <<= 16 - 10/*調整可*/;

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

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

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

int
ay38910_process(AY38910* ay, int cycle)
{
	/*===== ノイズ処理 =====*/
	if(ay->noise_enable) {
		ay->noise_progress -= cycle;
		while(ay->noise_progress <= 0) {
			ay->noise_progress += ay->noise_period;
			/* 実機の内部処理の仕様がわからないので、仮に[9bit tap=0x11]のM系列乱数を使うことにします。 */
			ay->noise_lfsr = (ay->noise_lfsr >> 1) |
					 (((ay->noise_lfsr >> 0) ^ (ay->noise_lfsr >> 4)) & 1) << 8;
			ay->noise_output = ay->noise_lfsr & 1;
		}
	}

	/*===== エンベロープ処理 =====*/
	if(ay->envelope_enable) {
		ay->envelope_progress -= cycle;
		while(ay->envelope_progress <= 0) {
			ay->envelope_progress += ay->envelope_period;
			ay->envelope_counter++;
			/* エンベロープレベル設定。
			 * エンベロープリセット直後は必ずここへ来て、エンベロープレベルが設定されます。
			 * ay38910_write()関数のReg#13処理の部分を参照してください。
			 */
			ay->envelope_level = ay->envelope_counter & 15;
			if(ay->envelope_shape_cycle & 1/*Hold*/) {
				if(ay->envelope_counter >= 16) {
					ay->envelope_counter = 16;
					ay->envelope_level = 15;
				}
			}
			if(ay->envelope_shape_cycle & 2/*Alternate*/) {
				if(ay->envelope_counter & 16) {
					ay->envelope_level ^= 15;
				}
			}
			if(!(ay->envelope_shape_cycle & 4/*Attack*/)) {
				ay->envelope_level ^= 15;
			}
			if(!(ay->envelope_shape_cycle & 8/*Continue*/)) {
				if(ay->envelope_counter >= 16) {
					ay->envelope_level = 0;
				}
			}
			ay->envelope_level *= ay->envelope_level;  /* 音量カーブの調整 */
		}
	}

	/*===== チャネル処理 =====*/
	return ay38910_channel_process(ay, &ay->channel[0], cycle)	/*  符号付き10bit */
	     + ay38910_channel_process(ay, &ay->channel[1], cycle)	/* +符号付き10bit */
	     + ay38910_channel_process(ay, &ay->channel[2], cycle);	/* +符号付き10bit */
}									/* =符号付き11.5bit */

int
ay38910_channel_process(AY38910* ay, AY38910CHANNEL* channel, int cycle)
{
	int output = 0;
	int level = channel->amplitude_mode ? ay->envelope_level : channel->amplitude_level;
	if(!level) return 0; /* 2004/03/07追加: 音量0なら何もしません。 */

	/*===== トーン処理 =====*/
	if(channel->tone_enable) {
		channel->tone_progress -= cycle;
		while(channel->tone_progress <= 0) {
			channel->tone_progress += channel->tone_period;
			channel->tone_output = !channel->tone_output;
		}
		if(channel->tone_output) {
			output += level;
		} else {
			output -= level;
		}
	}

	/*===== ノイズ処理 =====*/
	if(channel->noise_enable) {
		if(ay->noise_output) {
			output += level;
		} else {
			output -= level;
		}
	}

	return output;	/* 符号付き9bit×2 = 符号付き10bit */
}

