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

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

/****************************************************************************
 *	S3Mファイル仕様
 ****************************************************************************/

/* S3Mファイル構造の仕様は、複数バイトフィールドの境界整列を保証しています。
 * すべてのHalfWordフィールドは、HalfWord境界に整列しています。
 * すべてのWordフィールドは、Word境界に整列しています。
 * また、複数バイトフィールドのバイト並びは、P/ECEと同じLittleEndianです。
 * 以上の特性により、S3Mの構造体定義はshortやintを使って自然に表現できます。
 * ↓2004/11/17追記
 * 仕様上は必ず整列されているはずだったのですが、現実はそうでもないようです。
 * 一部のフィールド読み込みにLEHALFマクロを使う必要があります。
 * S3MMODULEHEADER.ordnumのコメントを参照してください。
 */

/* S3M Module header */
typedef struct _S3MMODULEHEADER {
		 char  song_name[28];				/* +0000, 28: Song name, max 28 chars (end with NUL (0)) */
		 char  _1ah;					/* +001c,  1: 1Ah */
	unsigned char  type;					/* +001d,  1: File type: 16=ST3 module */
	unsigned char  x001e[2];				/* +001e,  2: Reserved */
	unsigned short ordnum;					/* +0020,  2: Number of orders in file (should be even!)                                                                             */
								/*              (※ModplugTrackerで編集したS3Mの場合、ordnumは16の倍数に切り上げられるようです。                                   ) */
								/*              (  切り上げによって生じたorders[]の余白は、255(=Song終了)で埋められるようです。                                    ) */
								/*              (  この挙動に依存したコーディングは不要ですが、念のため記しておくことにします。                                    ) */
								/*              (※2004/11/17追記                                                                                                  ) */
								/*              (  仕様ではOrdnumが偶数でなければいけないのですが、現実に奇数のS3Mファイルが存在します。(PARTY92.S3M)              ) */
								/*              (  仕方がないので、奇数のOrdnumも許容することにします。Ordnumが奇数の場合、Ordersフィールドが奇数サイズになるため、) */
								/*              (  Ordersの後ろにあるParapointers to instrumentsとParapointers to patternsの各フィールドがHalfWord整列されません。 ) */
								/*              (  Parapointers to instrumentsとParapointers to patternsの各フィールドの読み込みは、LEHALFマクロを使ってください。 ) */
	unsigned short insnum;					/* +0022,  2: Number of instruments in file */
	unsigned short patnum;					/* +0024,  2: Number of patterns in file */
	unsigned short flags;					/* +0026,  2: [These are old flags for Ffv1. Not supported in ST3.01                                  */
								/*            | +  1: st2vibrato                                                                      */
								/*            | +  2: st2tempo                                                                        */
								/*            | +  4: amigaslides                                                                     */
								/*            | + 32: enable filter/sfx with sb                                                       */
								/*            ]                                                                                       */
								/*              +  8: 0vol optimizations                                                              */
								/*                      Automatically turn off looping notes whose volume is zero for >2 note rows.   */
								/*              + 16: amiga limits                                                                    */
								/*                      Disallow any notes that go beond the amiga hardware limits (like amiga does). */
								/*                      This means that sliding up stops at B#5 etc.                                  */
								/*                      Also affects some minor amiga compatibility issues.                           */
								/*              +128  special custom data in file                                                     */
	unsigned short cwt_v;					/* +0028,  2: Created with tracker / version: &0xfff=version, >>12=tracker                        */
								/*              ST3.00:0x1300                                                                     */
								/*              ST3.01:0x1301                                                                     */
								/*              (※ModplugTrackerは0x1320です。1991〜1992年の旧いS3Mファイルは0x3212のようです。) */
								/*              (  Trackerの種類を制限することには意味が無いので、このフィールドは検査しません。) */
	unsigned short ffv;					/* +002a,  2: FIle format version;                                                                    */
								/*              1=old version used long ago (samples signed)                                          */
								/*              2=standard (samples unsigned)                                                         */
								/*              (※1991年の初期のS3Mファイルも、1998年の新しいS3Mファイルも、2が設定されていました。) */
								/*              (  符号付きSampledataを持つ旧形式のS3Mファイルは、存在しないと考えていいと思います。) */
		 char  scrm[4];					/* +002c,  4: "SCRM" */
	unsigned char  g_v;					/* +0030,  1: global volume                                                                                                 */
								/*              Global volume directly divides the volume numbers used.                                                     */
								/*              So if the module has a note with volume 48 and master volume is 32, the note will be played with volume 24. */
								/*              This affects both Gravis & SoundBlasters.                                                                   */
								/*              (※長々と解説されていますが(^^;、直観どおりの普通のGlobal volumeです。0..64で全体音量を調整します。)        */
	unsigned char  i_s;					/* +0031,  1: initial speed (command A)                                                */
								/*              (※MODのSpeed、XMのTempoに相当。1[Row]当りのTick数で、6前後の値です。) */
	unsigned char  i_t;					/* +0032,  1: initial tempo (command T)                                                                   */
								/*              (※MODのTempo、XMのBPMに相当。1[Tick]の時間係数で、1[Tick]=(1/50)*(Tempo/125)[Sec]です。) */
	unsigned char  m_v;					/* +0033,  1: master volume                                                                                                                                    */
								/*              Master volume only affects the SoundBlaster.                                                                                                   */
								/*              It controls the amount of sample multiplication.                                                                                               */
								/*              The bigger the value the bigger the output volume (and thus quality) will be.                                                                  */
								/*              However if the value is too big, the mixer may have to clip the output to fit the 8 bit output stream.                                         */
								/*              The default value works pretty well.                                                                                                           */
								/*              Note that in stereo, the mastermul is internally multiplied by 11/8 inside the player since there is generally more room in the output stream. */
								/*              (※解説の冒頭に書かれているように、SoundBlasterのミキシングハードウェア制御専用みたいです。使いません。)                                       */
	unsigned char  x0034[10];				/* +0034, 10: Reserved */
	unsigned short special;					/* +003e,  2: pointer to special custom data (not used by ST3.01) */
	unsigned char  channel_settings[32];			/* +0040, 32: Channel settings:                                                                                                      */
								/*              bit   7: channel enabled                                                                                             */
								/*                         (※Documentには“bit 6”と書いてありますが誤りで、“bit 7”が正解だと思います。                         ) */
								/*                         (  0=有効,1=無効です。                                                                                  ) */
								/*                         (  実際にはbit 7-0全体で見て0..15を有効なSample Channelと見なすので、bit 7だけを調べる必要はありません。) */
								/*              bit 0-6: channel type                                                                                                */
								/*                          0.. 7: Left Sample Channel 1-8                                                                           */
								/*                          8..15: Right Sample Channel 1-8                                                                          */
								/*                         16..31: Adlib channels (9 melody + 5 drums)                                                               */
								/*                         (※Documentには“bit 0-7”と書いてありますが誤りで、“bit 6-0”が正解だと思います。)                      */
//	unsigned char  orders[/*ordnum*/];			/* +0060,ordnum                  : Orders; length=OrdNum (should be even)                                                               */
//								/*                                   Order list lists the order in which to play the patterns.                                          */
//								/*                                   255=-- is the end of tune mark and 254=++ is just a marker that is skipped.                        */
//								/*                                   (※“--”とか“++”とかは、Scream Tracker上ではそのように表示されていた、ということだと思います。) */
//								/*                                   (  “++”なんて滅多に使われないだろうと思いきや、SPLATFST.S3Mが使っていました。Skip判断必要です。) */
//								/*                                   (※2004/11/17追記: 奇数サイズも有り得ます。Ordnumのコメントを参照してください。                  ) */
//	unsigned short parapointers_to_instruments[/*insnum*/];	/* +0060+ordnum,insnum*2         : Parapointers to instruments; length=InsNum*2                                                       */
//	unsigned short parapointers_to_patterns[/*patnum*/];	/* +0060+ordnum+insnum*2,patnum*2: Parapointers to patterns; length=PatNum*2                                                          */
//								/*                                   Parapointers to file offset Y is (Y-Offset of file header)/16.                                   */
//								/*                                   You could think of parapointers as segments relative to the start of the S3M file.               */
//								/*                                   (※2004/11/17追記: HalfWord整列されていない場合があります。Ordnumのコメントを参照してください。) */
} S3MMODULEHEADER;						/* =0060+ordnum+insnum*2+patnum*2 */

/* Digiplayer ST3/ samplefileformat */
typedef struct _S3MSAMPLEHEADER {
	unsigned char  type;			/* +0000,  1: Type; 1=Sample, 2=adlib melody, 3+=adlib drum                                                       */
		 				/*              (※Typeが1以外の場合、Digiplayer ST3/ samplefileformatではなく、adlib instrument formatです。   ) */
		 				/*              (  今回はAdlibをサポートしないので、adlib instrument formatを含むS3Mファイルは演奏不可とします。) */
						/*              (  また、ModplugTrackerで編集したS3Mの場合、Wavedataを持たない空のSampleは、Type=0となります。  ) */
						/*              (  空のSampleのheaderは、VolとC2spd以外、シグネチャも含めて全てゼロクリアされているようです。   ) */
						/*              (  シグネチャよりも先にTypeを検査し、Type=1以外のSampleは無効Sampleとしてスキップしてください。 ) */
		 char  dos_filename[13];	/* +0001, 13: Dos filename (12345678.ABC) (incl. NUL)                                                   */
						/*              (※Documentにはchar[12]で末尾のNULを含まないように書いてありますが、誤りだと思います。) */
						/*              (  char[13]で末尾のNULを含むと考えた方が、直後のmemsegフィールドとの整合性が取れます。) */
						/*              (  s3modのコードも、char[13]として実装されていました。                                ) */
	unsigned short memseg;			/* +000e,  2: Pointer to sampledata                                                                                   */
						/*              Inside a sample or S3M, MemSeg tells the parapointer to the actual sampledata.                        */
						/*              In files all 24 bits are used.                                                                        */
						/*              In memory the value points to the actual sample segment or Fxxx if sample is in EMS under handle xxx. */
						/*              In memory the first memseg byte is overwritten with 0 to create the dos filename terminator nul.      */
						/*              (※DocumentにはMemsegは000dから始まる3バイトと書かれていますが、誤りだと思います。        )           */
						/*              (  上の説明はDocumentからそのまま引用したもので、混乱するだけなので読まない方がいいです。 )           */
						/*              (  Memsegは、Sampledataの開始位置をファイル先頭からのParapointerで表すHalfWord値です。    )           */
						/*              (  s3modのコードも、そのように実装されていました。                                        )           */
		 int   length;			/* +0010,  4: Length                                                                                                                                     */
		 int   loop_begin;		/* +0014,  4: LoopBegin                                                                                                                                  */
		 int   loop_end;		/* +0018,  4: LoopEnd                                                                                                                                    */
						/*              Length / LoopBegin / LoopEnd are all 32 bit parameters although ST3 only support file sizes up to 64,000 bytes.                          */
						/*              Files bigger than that are clipped to 64,000 bytes when loaded to ST3.                                                                   */
						/*              NOTE that LoopEnd points to one byte AFTER the end of the sample, so LoopEnd=100 means that byte 99.9999 (fixed) is the last one played. */
	unsigned char  vol;			/* +001c,  1: Default volume 0..64 */
	unsigned char  x001d;			/* +001d,  1: Reserved */
	unsigned char  pack;			/* +001e,  1: Pack; 0=unpacked, 1=DP30ADPCM packing (not used by ST3.01) */
	unsigned char  flags;			/* +001f,  1: Flags                                                                                     */
						/*              +1=loop on                                                                              */
						/*              +2=stereo (after Length bytes for LEFT channel, another Length bytes for RIGHT channel) */
						/*              +4=16-bit sample (intel LO-HI byteorder)                                                */
						/*              (+2/+4 not supported by ST3.01)                                                         */
		 int   c2spd;			/* +0020,  4: C2spd; Herz for middle C. ST3 only uses lower 16 bits.                  */
						/*              Finetuning (C2SPD) is actually the frequency in herz for the note C4. */
						/*              Why is it C2SPD?                                                      */
						/*              Well, originally in ST2 the middle note was C2 and the name stuck.    */
						/*              Later in ST3 the middle note was raised to C4 for more octaves...     */
						/*              So actually C2SPD should be called C4SPD...                           */
						/*              (※ModplugTrackerはオクターブ表記が一つ大きいので、C5に相当します。)  */
	unsigned char  x0024[4];		/* +0024,  4: Reserved */
	unsigned short int_gp;			/* +0028,  2: Int:Gp; Internal: Address of sample in gravis memory /32 */
						/*              (only used while module in memory)                     */
	unsigned short int_512;			/* +002a,  2: Int:512; Internal: flags for soundblaster loop expansion */
						/*              (only used while module in memory)                     */
		 int   int_lastused;		/* +002c,  4: Int:las; Internal: last used position (only works with sb) */
						/*              (only used while module in memory)                       */
		 char  sample_name[28];		/* +0030, 28: Sample name, 28 characters max (incl. NUL) */
		 char  scrs[4];			/* +004c,  4: "SCRS" */
} S3MSAMPLEHEADER;				/* =0050 */

/****************************************************************************
 *	P/ECE S3M Driver
 ****************************************************************************/

#define S3MBUFLEN	320			/* = 16000/50 … 1[tick](=1/50[sec])当たりのサンプル数 */
#define S3MBUFLEN_5	(S3MBUFLEN / 5)		/* テンポずれ防止のため、1/5[tick]づつミキシングします */

/* アセンブラコードから直接参照するため、レイアウト固定です!! */
typedef struct _S3MSAMPLE {
	const unsigned char* sampledata;	/* + 0, 4: S3MSAMPLEHEADER.memseg    から算出、固定。Sample無効時はNULL */
	const unsigned char* loop_begin;	/* + 4, 4: S3MSAMPLEHEADER.loop_beginから算出、固定。Loop on時は(sampledata+loop_begin)。Loop off時は(NULL             ) */
	const unsigned char* loop_end;		/* + 8, 4: S3MSAMPLEHEADER.loop_end  から算出、固定。Loop on時は(sampledata+loop_end  )。Loop off時は(sampledata+length) */
	int vol;				/* +12, 4: S3MSAMPLEHEADER.vol       から取得、固定 */
	int c2spd;				/* +16, 4: S3MSAMPLEHEADER.c2spd     から取得、固定 */
} S3MSAMPLE;					/* =20 */

/* アセンブラコードから直接参照するため、レイアウト固定です!! */
typedef struct _S3MCHANNEL {
	S3MSAMPLE* pending_sample;		/* + 0, 4: 次回の発声Sample。Channel無効時はNULL */
	S3MSAMPLE* playing_sample;		/* + 4, 4: 現在の発声Sample。Silent時はNULL */

	int vol;				/* + 8, 4: S3MSAMPLE.volから取得、Special commandで変化 */
	int period;				/* +12, 4: ST3 periodに相当、Amiga periodの4倍 */
	int progress;				/* +16, 4: Sampledate走査用の進行カウンタ */
	const unsigned char* sample_position;	/* +20, 4: Sampledata走査位置 */

	int special_command;			/* +24, 4: Special commandを保持 */
	int command_info;			/* +28, 4: Command infoを保持 */
	int tone_portamento;			/* +32, 4: Tone portamentoの目標Period */
} S3MCHANNEL;					/* =36 */

typedef struct _S3MDRIVER {
	int ordnum;				/* S3MMODULEHEADER.ordnumから取得、固定 */
	int insnum;				/* S3MMODULEHEADER.insnumから取得、固定 */
	int patnum;				/* S3MMODULEHEADER.patnumから取得、固定 */

	int g_v;				/* S3MMODULEHEADER.g_vから取得、Special commandで変化 */
	int speed;				/* S3MMODULEHEADER.i_sから取得、Special commandで変化 */
	int tempo;				/* S3MMODULEHEADER.i_tから取得、Special commandで変化 */

	int progress;				/* Tempo進行用カウンタ */
	int tick;				/* Speed進行用カウンタ */
	int order;				/* 現在のOrder */
	int row;				/* 次のRow */
	const unsigned char* pattern_position;	/* 次のPattern data */

	int stat;				/* 通常は0。演奏停止時に要因を格納 */
	int position_jump;			/* 通常は-1。Position jump時にorderを設定 */
	int pattern_break;			/* 通常は-1。Pattern break時にrowを設定 */

	const unsigned char* orders/*[ordnum]*/;	/* Direct */
	S3MSAMPLE* samples/*[insnum]*/;			/* Alloc */
	const unsigned char** patterns/*[patnum]*/;	/* Alloc */

	S3MCHANNEL channels[32];
} S3MDRIVER;

extern const short s3m_note_frequency_table[12];

int s3m_init(S3MDRIVER* driver, const void* data, int initial_row);
void s3m_free(S3MDRIVER* driver);
int s3m_init_pattern(S3MDRIVER* driver);
int s3m_seek_pattern(S3MDRIVER* driver, int row);
int s3m_run(S3MDRIVER* driver);
int s3m_mix(S3MDRIVER* driver, short wbuff[/*S3MBUFLEN_5*/]);
int s3m_stream_callback(short* wbuff, int param);

void s3m_mix1(S3MCHANNEL* channel, int volume, int frequency, int* mixbuf);
void s3m_mix2(short* wbuff, const int* mixbuf);

/* S3MCHANNEL.periodの範囲 (独自判断、調整可) */
#define S3M_PERIOD_MIN	(14317056 / (16000 * 32))	/* C2spd=16000[Hz]と仮定して、5[octave]上まで */
#define S3M_PERIOD_MAX	(14317056)			/* 1[Hz]まで */

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

/* S3Mファイルの再生を開始します。
 * [in]
 *	data		S3Mデータの先頭アドレス。
 *	initial_row	演奏開始Row。
 * [out]
 *	戻り値		再生開始に成功したら0を返します。
 *			再生開始に失敗したら0以外の値を返します。
 * [note]
 *	* この関数は内部でstream_play()を呼び出しています。
 *	* stream_play()を呼ぶと、サウンド割り込みベクタがフックされます。
 *	  プログラム終了前に必ずs3m_stop()を呼んで、ベクタを復元してください。
 *	* 演奏開始Row指定を利用すると、同じSampleセットを利用する複数の曲をひとつのS3Mファイルにまとめることができます。
 *	  演奏開始Rowを利用する場合は、先頭のPatternにジャンプテーブルとなるPosition jumpを並べておいてください。
 *	  ＜例＞
 *		++---------------+---------------+---------------++
 *		|| --- -- -- B01 | --- -- -- A06 | --- -- -- T96 || ... BGM#0: Order= 1〜, Speed=6, Tempo=150
 *		|| --- -- -- B0A | --- -- -- A06 | --- -- -- T7D || ... BGM#1: Order=10〜, Speed=6, Tempo=125
 *		|| --- -- -- B14 | --- -- -- A05 | --- -- -- T96 || ... BGM#2: Order=20〜, Speed=5, Tempo=150
 *		|| --- -- -- --- | --- -- -- --- | --- -- -- --- ||
 *	  この機能はゲームBGMなど、同じSampleセットを利用する複数曲のSampleを共有し、メモリを節約するために用意しました。
 *	  それぞれの曲がループする場合はPosition jumpを、ループしない場合はSet speed = 0を、曲の終端に配置してください。
 *	  この機能を利用しない(ジャンプテーブルのない)通常のS3Mファイルの場合は、演奏開始Row = 0を指定してください。
 */
int s3m_play(const void* data, int initial_row);

/* S3Mファイルの再生を停止します。
 * [note]
 *	* 再生中でなければ、この関数は何もしません。
 */
void s3m_stop();

/* 演奏状態を取得します。
 * [out]
 *	戻り値		演奏中ならば1を返します。
 *			停止中ならば0を返します。
 *			演奏終了またはエラー発生ならば負の値を返します。
 */
int s3m_stat();

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

#endif /*__CLIP_S3M_H__*/
