/*	
 *	clipsds.h
 *
 *	P/ECE S-DSP Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Jan 06 06:00:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 */
#ifndef __CLIP_SDS_H__
#define __CLIP_SDS_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

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

/* 要注意!! 高速化のために『入力クロック = 2.048MHz』決め打ちで実装します。 */
#define SDSP_CLOCK	2048000

/* P/ECEスピーカー出力1サンプル毎に、S-DSPクロックが何サイクル進むか? */
#define SDSP_STEP	(SDSP_CLOCK / SPEAKER_FREQUENCY) /* 2048÷16=『128』 */

//
// ●Pitchの仕様
// 
// Pitch	0x0400	0x0800	0x1000	0x2000	0x3fff
// Octave	  -2	  -1	 原音	  +1	  +2
// 
// 入力クロック = 2.048MHz = 2048KHz
// サンプリングデータの録音レート = 32Khz (※要確認)
// 
// Pitch=0x1000 の時に 再生レート=入力クロック÷(2048÷32)
// 再生レートはPitchと入力クロックに比例する。
// 
// 従って、
// 
// 再生レート = 入力クロック×Pitch÷0x1000÷(2048÷32)
//            = 入力クロック×Pitch÷0x1000÷64
//            = 入力クロック×Pitch÷0x40000
// 
// ●クロック経過数からサンプリングデータ進行数を求める方法
// 
// Pitch=0x40000 の時に サンプリングデータ進行数=クロック経過数
// (実際にはPitchは0x0000〜0x3fffまでです)
// 
// サンプリングデータ進行数はPitchに比例する。
// 
// 従って、
// 
// サンプリングデータ進行数 = クロック経過数×Pitch÷0x40000
//
// ●P/ECEスピーカー出力に特化して考えると・・・
//
// P/ECEスピーカー出力1サンプル毎に、S-DSPクロックは128サイクル進む。
// (S-DSPクロック÷P/ECEスピーカークロック = 2.048MHz÷16KHz = 128)
// スピーカー出力1サンプル毎の、S-DSPサンプリングデータ進行数は、
//
// サンプリングデータ進行数 = 128×Pitch÷0x40000
//                          = Pitch÷0x800
//                          = Pitch >> 11
//
// 従って、
//
// ○進行カウンタには、スピーカー出力1サンプル毎にPitch値を加算する。
// ○進行カウンタは、小数部11ビットの固定小数と見なす。
// ○上位21ビットがサンプルデータのニブルアドレスオフセットとなります。
//   (実際には最大でも(16+1)ビットまで。それ以上は使わないはずです。)
//
// ●エンベロープジェネレータの更新間隔について
//
// 32KHzです。(S-DSP仕様)
//
// 従って、
//
// P/ECEスピーカー出力(16KHz)1サンプル毎に、2回分の更新を行います。
//

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

/***** Voice *****/
typedef struct _SDSPVOICE {
	int vol;			/* Volume (→0:MIN〜256:MAX) */
	int p;				/* Pitch */
	int srcn;			/* Source Number (→SRCN×4) */
	int adsr_gain;			/* Mode 0:GAIN/1:ADSR */
	int ar;				/* Attack Rate */
	int dr;				/* Decay Rate */
	int sl;				/* Sustain Level */
	int sr;				/* Sustain Rate */
	int gain;			/* Gain */
	int non;			/* Noise 0:OFF/1:ON */
	//
	int envx;			/* Envelope generator output (→ 7.16bit) */
	int outx;			/* ENVX×VOL output          (→15.16bit) */
	//
	int state;			/* 0:OFF=>4:ATTACK=>3:DECAY=>2:SUSTAIN=>1:RELEASE=>0:OFF */
	int progress;			/* 進行カウンタ (21.11bit、上述の説明を参照) */
} SDSPVOICE;

/* SDSVOICE.state */
#define SDSVOICE_ATTACK		4
#define SDSVOICE_DECAY		3
#define SDSVOICE_SUSTAIN	2
#define SDSVOICE_RELEASE	1

/***** S-DSP Chip *****/
typedef struct _SDSP {
	const unsigned char* mem;	/* Wave Memory (64KB、CPUと共有) */
	int addr;			/* Register Address */
	unsigned char reg[0x80];	/* Register Data */
	//
	SDSPVOICE voice[8];		/* Voice */
	int mvol;			/* Main Volume (→0:MIN〜256:MAX) */
	int mute;			/* Mute */
	int nck;			/* Noise Generator Clock */
	int dir;			/* Source Directory (→DIR×0x100) */
} SDSP;

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

void sdsp_voice_mix(SDSP* sdsp, SDSPVOICE* voice, int* mixbuf, int count);

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

/* SDSPコアをリセットします。
 * [in]
 *	sdsp		SDSP構造体。
 *	mem		波形メモリ。(64KB)
 *			SNESの構成では、CPUメインメモリを共有しています。
 *			CPUメインメモリの先頭アドレスを指定してください。
 * [note]
 *	* アプリケーションプログラムは、少なくとも最初に一度、この関数を呼び出してください。
 *	  その後は、SDSPリセットを行う場合に、必要に応じて呼び出してください。
 */
void sdsp_reset(SDSP* sdsp, const void* mem);

/* データを読み書きするレジスタアドレス設定を取得します。
 * [in]
 *	sdsp		SDSP構造体。
 * [out]
 *	戻り値		レジスタアドレス。
 * [note]
 *	* S-SMPメインメモリ空間の$00F2番地READに相当します。
 */
unsigned char sdsp_addr_read(SDSP* sdsp);

/* データを読み書きするレジスタアドレスを設定します。
 * [in]
 *	sdsp		SDSP構造体。
 *	addr		レジスタアドレス。(0x00〜0x7f)
 * [note]
 *	* S-SMPメインメモリ空間の$00F2番地WRITEに相当します。
 */
void sdsp_addr_write(SDSP* sdsp, unsigned char addr); /* $00F2 WRITE */

/* レジスタデータを読み込みます。
 * [in]
 *	sdsp		SDSP構造体。
 * [out]
 *	戻り値		レジスタデータ。
 * [note]
 *	* 読み込むレジスタは、あらかじめsdsp_addr_write()で設定しておきます。
 *	* S-SMPメインメモリ空間の$00F3番地READに相当します。
 */
unsigned char sdsp_data_read(SDSP* sdsp);

/* レジスタデータを書き込みます。
 * [in]
 *	sdsp		SDSP構造体。
 *	data		レジスタデータ。
 * [note]
 *	* 書き込むレジスタは、あらかじめsdsp_addr_write()で設定しておきます。
 *	* S-SMPメインメモリ空間の$00F3番地WRITEに相当します。
 */
void sdsp_data_write(SDSP* sdsp, unsigned char data);

/* 指定されたサンプル数のミキシングを行います。
 * [in]
 *	sdsp		SDSP構造体。
 *	wbuff		出力サンプルバッファ。
 *	count		出力サンプル数。
 */
void sdsp_mix(SDSP* sdsp, short wbuff[/*count*/], int count);

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif /*__CLIP_SDS_H__*/
