/*	
 *	clipdms2.h
 *
 *	P/ECE DMG-Sound Emulator (高速版)
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Mon Feb 28 20:00:00 JST 2005 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_DMS2_H__
#define __CLIP_DMS2_H__

/****************************************************************************
 *
 ****************************************************************************/

#define DMGSOUND2BUFLEN 320 /* = 16000/50 … 1[tick](=1/50[sec])当たりのサンプル数 */

#define DMGSOUND2_CLOCK (1<<22) /* = 4.19[MHz] (GameBoyマスタークロック) */

/* Ch1,2: 矩形波 */
typedef struct _DMGSOUND2SQUARE {
	/*** Length ***/
	int sound_length;		// + 0,4: 残り発声時間を(1/256[sec])単位で示します。
					//          -1ならば、明示的に停止するまで発声を続けます。
					//           0ならば、停止中であることを示します。

	/*** Envelope ***/
	unsigned char envelope_time;	// + 4,1: エンベロープ実行間隔を(1/256[sec])単位で示します。
					//          0ならば、エンベロープを行いません。
	unsigned char envelope_type;	// + 5,1: エンベロープ方向を示します。
					//          0ならば、音量を減少します。(音が小さくなる)
					//          1ならば、音量を増加します。(音が大きくなる)
	unsigned char envelope_volume;	// + 6,1: 現在のエンベロープ音量を示します。(0..15)
					//          0で消音、15で最大音量となります。
	unsigned char envelope_count;	// + 7,1: 次回エンベロープ実行までのダウンカウンタです。

	/*** Sweep (Ch1のみ) ***/
	unsigned char sweep_time;	// + 8,1: スイープ実行間隔を(1/256[sec])単位で示します。
					//          0ならば、スイープを行いません。
					//          Ch2はスイープ機能が無いので、0固定とします。
	unsigned char sweep_type;	// + 9,1: スイープ方向を示します。
					//          0ならば、周波数を増加します。(音が高くなる)
					//          1ならば、周波数を減少します。(音が低くなる)
	unsigned char sweep_shift;	// +10,1: 周波数変化量のシフト数を示します。
					//          スイープを一回実行することによる周波数変化は、次の通りです。
					//          frequency(t) = frequency(t-1) ± frequency(t-1)>>sweep_shift
	unsigned char sweep_count;	// +11,1: 次回スイープ実行までのダウンカウンタです。

	/*** Wave ***/
	unsigned char wave_duty;	// +12,1: 矩形波のデューティ比を示します。
					//                    01234567
					//          1: 12.5% (_-------)
					//          2: 25  % (__------)
					//          4: 50  % (____----)
					//          6: 75  % (______--)
	unsigned char wave_index;	// +13,1: 波形サンプル単位の進行カウンタです。(0..7)
					//          (wave_index＜wave_duty)ならば、(-envelope_volume)を出力します。
					//          (wave_index≧wave_duty)ならば、(+envelope_volume)を出力します。
	short wave_frequency;		// +14,2: 周波数を示します。(0x000..0x7ff)
					//          矩形波1波形単位の再生周波数は、
					//            Frequency = 4194304/(32*(2048-x))[Hz] (仕様)
					//          矩形波1波形分のサイクル数を、マスタークロックの倍数で表すと、
					//            Period    = (2048-x)*32
					//          矩形波1波形が8サンプルと見なすと、1サンプル分のサイクル数は、
					//            Period    = (2048-x)*32/8
					//                      = (2048-x)*4
	int wave_count;			// +16,4: マスタークロック単位の進行カウンタです。

	/*** Speaker ***/
	unsigned char speaker_volume;	// +20,1: 出力先スピーカー(左、右、または両方)の合計ボリューム (0..14)
} DMGSOUND2SQUARE;

/* Ch3: 波形メモリ */
typedef struct _DMGSOUND2WAVE {
	/*** Length ***/
	int sound_length;		// + 0,4: 残り発声時間を(1/256[sec])単位で示します。
					//          -1ならば、明示的に停止するまで発声を続けます。
					//           0ならば、停止中であることを示します。

	/*** Wave ***/
	unsigned char wave_shift;	// + 4,1: 波形データの出力レベルを示します。
					//          0: 消音
					//          1: そのまま出力
					//          2: 1bit右シフトして出力
					//          3: 2bit右シフトして出力
	unsigned char wave_index;	// + 5,1: 波形サンプル単位の進行カウンタです。(0..31)
					//          (wave_pattern[wave_index]>>(wave_shift-1))を出力します。
	short wave_frequency;		// + 6,2: 周波数を示します。(0x000..0x7ff)
					//          矩形パターン1波形単位の再生周波数は、
					//            Frequency = 4194304/(64*(2048-x))[Hz] (仕様)
					//          波形パターン1波形分のサイクル数を、マスタークロックの倍数で表すと、
					//            Period    = (2048-x)*64
					//          波形パターン1波形が32サンプルと見なすと、1サンプル分のサイクル数は、
					//            Period    = (2048-x)*64/32
					//                      = (2048-x)*2
	int wave_count;			// + 8,4: マスタークロック単位の進行カウンタです。

	/*** Speaker ***/
	unsigned char speaker_volume;	// +12,1: 出力先スピーカー(左、右、または両方)の合計ボリューム (0..14)
} DMGSOUND2WAVE;

/* Ch4: ノイズ */
typedef struct _DMGSOUND2NOISE {
	/*** Length ***/
	int sound_length;		// + 0,4: 残り発声時間を(1/256[sec])単位で示します。
					//          -1ならば、明示的に停止するまで発声を続けます。
					//           0ならば、停止中であることを示します。

	/*** Envelope ***/
	unsigned char envelope_time;	// + 4,1: エンベロープ実行間隔を(1/256[sec])単位で示します。
					//          0ならば、エンベロープを行いません。
	unsigned char envelope_type;	// + 5,1: エンベロープ方向を示します。
					//          0ならば、音量を減少します。(音が小さくなる)
					//          1ならば、音量を増加します。(音が大きくなる)
	unsigned char envelope_volume;	// + 6,1: 現在のエンベロープ音量を示します。(0..15)
					//          0で消音、15で最大音量となります。
	unsigned char envelope_count;	// + 7,1: 次回エンベロープ実行までのダウンカウンタです。

	/*** LFSR ***/
	unsigned char lfsr_type;	// + 8,1: 1 = 15 steps (long mode), 6 = 7 steps (short mode)
					//          正しい仕様がわからないので、ファミコンAPU互換で実装してみました。
					// + 9,1: (パディング)
	unsigned short lfsr;		// +10,2: LFSR (Linear Feedback Shift Register)
	int lfsr_frequency;		// +12,4: マスタークロックサイクル単位のLFSRシフト周期を示します。
					//          {1,2,4,6,8,10,12,14}<<{3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18}
	int lfsr_count;			// +16,4: マスタークロック単位の進行カウンタです。

	/*** Speaker ***/
	unsigned char speaker_volume;	// +20,1: 出力先スピーカー(左、右、または両方)の合計ボリューム (0..14)
} DMGSOUND2NOISE;

typedef struct _DMGSOUND2 {
	short conv_250_256;	/* 16000/64=250→256[Hz]変換用 */

	/*** Registers ***/
	unsigned char nr10;	/* $FF10 (NR10) */
	unsigned char nr11;	/* $FF11 (NR11) */
	unsigned char nr12;	/* $FF12 (NR12) */
	unsigned char nr13;	/* $FF13 (NR13) */
	unsigned char nr14;	/* $FF14 (NR14) */
	unsigned char nr21;	/* $FF16 (NR21) */
	unsigned char nr22;	/* $FF17 (NR22) */
	unsigned char nr23;	/* $FF18 (NR23) */
	unsigned char nr24;	/* $FF19 (NR24) */
	unsigned char nr30;	/* $FF1A (NR30) */
	unsigned char nr31;	/* $FF1B (NR31) */
	unsigned char nr32;	/* $FF1C (NR32) */
	unsigned char nr33;	/* $FF1D (NR33) */
	unsigned char nr34;	/* $FF1E (NR34) */
	unsigned char nr41;	/* $FF20 (NR41) */
	unsigned char nr42;	/* $FF21 (NR42) */
	unsigned char nr43;	/* $FF22 (NR43) */
	unsigned char nr44;	/* $FF23 (NR44) */
	unsigned char nr50;	/* $FF24 (NR50) */
	unsigned char nr51;	/* $FF25 (NR51) */
	unsigned char nr52;	/* $FF26 (NR52) */

	/*** Channels ***/
	DMGSOUND2SQUARE ch1;	/* Ch1: 矩形波 (Sweep有り) */
	DMGSOUND2SQUARE ch2;	/* Ch2: 矩形波 (Sweep無し) */
	DMGSOUND2WAVE   ch3;	/* Ch3: 波形メモリ */
	DMGSOUND2NOISE  ch4;	/* Ch4: ノイズ */
} DMGSOUND2;

/****************************************************************************
 *	グローバル変数
 ****************************************************************************/

extern DMGSOUND2 dmgsound2;

/* $FF30-$FF3F (Wave Pattern RAM)
 * - あらかじめ上位4bit、下位4bitの順に展開し、各4bitサンプルを
 *   (0..15)-[x2]->(0..30)-[-15]->(-15..+15) に変換しておきます。
 */
extern char dmgsound2_wave_pattern[32];

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

void dmgsound2_square_frame(DMGSOUND2SQUARE* ch);
void dmgsound2_wave_frame  (DMGSOUND2WAVE  * ch);
void dmgsound2_noise_frame (DMGSOUND2NOISE * ch);
void dmgsound2_square_mix  (DMGSOUND2SQUARE* ch, short wbuff[/*64*/]);
void dmgsound2_wave_mix    (DMGSOUND2WAVE  * ch, short wbuff[/*64*/]);
void dmgsound2_noise_mix   (DMGSOUND2NOISE * ch, short wbuff[/*64*/]);
void dmgsound2_volume_shift(short wbuff[/*DMGSOUND2BUFLEN*/]);

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

/* DMG-Soundコアをリセットします。
 */
void dmgsound2_reset();

/* DMG-Soundのレジスタを読み出します。
 * [in]
 *	addr		レジスタアドレス。($FF10..$FF3F)
 * [out]
 *	戻り値		読み出したデータ。
 */
int dmgsound2_read(int addr);

/* DMG-Soundのレジスタへ書き込みます。
 * [in]
 *	addr		レジスタアドレス。($FF10..$FF3F)
 *	data		書き込むデータ。
 */
void dmgsound2_write(int addr, int data);

/* 出力サンプリングレート=SPEAKER_OUTPUT[Hz]で、DMGSOUND2BUFLENサンプルのミキシングを行います。
 * [in]
 *	wbuff		出力サンプルバッファ。
 */
void dmgsound2_mix(short wbuff[/*DMGSOUND2BUFLEN*/]);

#endif /*__CLIP_DMS2_H__*/
