/*	
 *	muslib.c
 *
 *	'PIECE 標準 音楽ドライバー'互換モジュール
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015 Naoyuki Sawa
 *
 *	* Thu Jan 15 21:58:14 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	- 当モジュールによって、muslib.libの大部分を置き換えます。
 *	  muslib.lib互換なので、ヘッダファイルは\usr\PIECE\includemuslib.hをそのまま使います。
 *	- 利点は以下の通りです:
 *	  ○コードサイズ節約
 *	    muslib.lib　…CODE=4432バイト,DATA+BSS=2192バイト	mus.o,seq.o,exp12.o,musfast.oの合計
 *	    当モジュール…CODE=3488バイト,DATA+BSS=2104バイト
 *	    尚、音色データはどちらも共通なので(後述)、上記のサイズから除外しています。
 *	  ○高速化
 *	    一例として、muslib.libが負荷33%のデータを、当モジュールは負荷18%で再生できました。
 *	- 欠点は以下の通りです:
 *	  ×高速化するために音質を少し犠牲にしました。具体的にはMakeWave()を参照してください。
 *	- 当モジュールを使用しても、muslib.libの以下の部分は引き続きリンクされます。
 *	  muslib.lib:wavetable.o,muslib.lib:wave\i_*.o
 *	  これらは音色データであり最適化できない部分なので、そのまま使用する事にしました。
 *	- 当モジュールは、高速RAMへコード転送する高速化手法をとっていません。
 *	  muslib.libと互換のために、SRAMのみで動作するようにしておく方が安全と思ったからです。
 *	- \usr\PIECE\sysdev\music\内のソースを理解しながら、あまり大幅に変更しない方針で最適化しました。
 *	  元ソースで多少バグっぽいと思う点が有っても、極端な問題でなければそのままにしてあります。
 *	  また、大幅な変更を伴うような、ソースの整理も行っていません。
 *	  敢えて、元ソースの処理を残すようにしたためとご理解ください。
 *	- 利便性のために非互換にした点があります。
 *	  ①inst[]を内包しました。アプリケーションが個別にinstdef.cをリンクする必要は有りません。
 *	    当初は、アプリケーション固有のinst[]を使用して、音色定義を変更する意図が有ったと思うのですが、
 *	    実際には、これまでにユーザーが作成したデータは標準音色を前提としており、音色固定と見なせます。
 *	    標準音色固定で不都合は無いと思うので、inst[]を内包することにしました。
 *	    inst[]をローカルシンボルに出来る(単純な名前のinstをグローバルにしなくて済む)、利点もあります。
 *	  ②ラウドネス4倍にしてあります。BuffFree_Mix2()を参照してください。
 *	    clipmus.hのmusic_filter_loudness()を使用しなくても、十分な音量だと思います。
 *	    ⇒2015/01/15追記:当モジュールの追加によりclipmus.*の補助が不要になり、clipmus.*を削除しました。
 *	                     clipmus.*に含まれていた、music_filter_install()の機能も削除しました。
 *	                     この機能はUSB Audioのサンプルでしか使用した事が無く、今後も不要だと思います。
 *	                     もし再度必要になった場合は、当モジュールの中に機能を追加すれば良いと思います。
 *	                     clipmus.h,clipmus.cはもう使用しませんが、今後music_filter_install()機能を再度追加する時の参考として、
 *	                     keep/09_PIECE標準音楽ライブラリ補助ルーチン削除前保存.20150115/に保存しておきました。
 */
#include "clip.h"

//□2015/01/23メモ
//「Ｐ/ＥＣＥ　ＭＭＬ文法リファレンス」【音長】音の切り方 ２(file:///C:/usr/PIECE/docs/muslib/mml.htm#mml14)には、
//＞q《0～100》数値の時間分、後ろから音をカットします。
//と書いてありますが、値の範囲が間違っています。
//mucc.exeのソース(\usr\PIECE\tools\mucc\mmlcv.c)のana_body()の'q'の処理を見ると、
//＞q《0～24》数値の時間分、後ろから音をカットします。
//と考えるのが正解です。実際にmucc.exeを使って試してみて、0～24の挙動である思いました。
//自作ツールdpmucc.exe(clip/tool/dpmucc/mml.c)も、0～24の範囲で指定するようにしました。

/****************************************************************************
 *	\usr\PIECE\sysdev\music\musdef.h より
 ****************************************************************************/
#define MAXCH		6	//\usr\PIECE\tools\mucc\mucc.cのconv()のsetup_mml(6)に一致
#define MAXNEST		8	//\usr\PIECE\tools\mucc\mmlcv.c
#define MAXNESTD	(MAXNEST*3)
/*--------------------------------------------------------------------------*/
//INST.wtype
#define IT_LOOP		0x10
#define IT_FAST		0x80
//音色データ
typedef struct tagINST {
	//\usr\PIECE\sysdev\music\wavetable.c,wave\i_*.cに合わせるため変更不可です
	unsigned char		wtype;		//+ 0,1
	unsigned char		param;		//+ 1,1
	unsigned char		resv1;		//+ 2,1
	unsigned char		resv2;		//+ 3,1
	unsigned short		pitch_fs;	//+ 4,2
	unsigned short		pitch_org;	//+ 6,2
	const char*		pData;		//+ 8,4
	unsigned		loop_top;	//+12,4
	unsigned		loop_end;	//+16,4
} INST;						//=20
/*--------------------------------------------------------------------------*/
//デバイスチャネル
typedef struct tagMDEVICE {
	//以下の20バイトはアセンブラのルーチンでも処理されるので位置を変えないこと
	unsigned		freqwk;		//+ 0,4
	unsigned		freq;		//+ 4,4
	const char*		pData;		//+ 8,4
	unsigned		loop_end;	//+12,4
	unsigned		loop_w;		//+16,4
	//					//=20
	unsigned		ipitch;			//音色から計算される基準の音程。まだ良く判っていない。
	unsigned		cpitch;			//ノートオン時に計算された音程。ビブラートやポルタメントは含めない値。
	int			ptwk1;			//ポルタメントによる音程増減の現在値。
	int			ptwk2;			//ビブラートによる音程増減の現在値。
	int			envwk;			//減衰量を表す値。gexp()で変換して使う。
	int			envAR;			//'E'コマンドで設定された値をSetEnv()で計算して格納する。
	int			envDR;			//'E'コマンドで設定された値をSetEnv()で計算して格納する。
	int			envSL;			//'E'コマンドで設定された値をSetEnv()で計算して格納する。
	int			envSR;			//'E'コマンドで設定された値をSetEnv()で計算して格納する。
	int			pvol;			//ボリューム
	int			pexp;			//音量調整
	short			vibwk;					//これはunsigned shortにしてはいけない。GenVibS2()を参照せよ。
	short			vibvv;					//unsigned shortにすべきではないか? SetVib()にてgexp()の戻り値を格納しているが、gexp()の戻り値の最大は符号無し16bitだから。
	short			vibv1;					//unsigned shortにすべきではないか? SetVib()にてgexp()の戻り値を格納しているが、gexp()の戻り値の最大は符号無し16bitだから。
	short			vibv2;					//unsigned shortにすべきではないか? SetVib()にてgexp()の戻り値を格納しているが、gexp()の戻り値の最大は符号無し16bitだから。
	short			vibdpwk;				//これはunsigned shortにしてはいけない。GenVibS0()を参照せよ。
	short			vibdpe;			//ポルタメントの深さの現在値
	void (*genwave)(struct tagMDEVICE* mp, int* p, int v, int cnt);		//音色によって設定
	void (*genenv )(struct tagMDEVICE* mp);					//エンベロープの区間によって設定
	void (*genvib )(struct tagMDEVICE* mp);					//ビブラートの区間によって設定
} MDEVICE;
/*--------------------------------------------------------------------------*/
//SEQWK.tatt
#define TATT_DRUMS	1	//ドラムパート								┬両方セットしても動作上は問題無いが、併用する事に意味が無い。
#define TATT_NOOFF	2	//無停止(常にレガート状態にするMMLでは明文化されていないようだ。)	┘
//SEQWK.tsta
#define TSTA_PORTASW	1	//ポルタメントオン
#define TSTA_INPORTA	2	//ポルタメント中		//厳密にはTSTA_INPORTAは無くても処理は可能だ。portamentにSeqNop()を入れればよいから。しかし、呼び出しオーバーヘッドを避けるためにTSTA_INPORTAを設けているのだろう。
#define TSTA_LEG1	4	//レガート中	MMLで'a&b&c'と書いた時に、aの前でセットされ、cの前でクリアされる。
#define TSTA_LEG2	8	//レガート中	MMLで'a&b&c'と書いた時に、aの前でセットされる。クリア条件は複雑なので、SeqNote()を参照せよ。レガートオフ直後の最終ノートオンを判別するためのフラグのようだ。
//シーケンサチャネル
typedef struct tagSEQWK {
	MDEVICE			mdwk;
	const unsigned char*	pseq;
	int			porvv;		//ポルタメントの目標音程に対する差分(+又は-の値から0へ近づいて行く)
	short			cnt;		//残り音長
	short			len;		//音長設定
	short			stop;		//残り何カウントでゲートオフするか	音符発声開始時にgateから計算する。
	short			gate;		//ゲートオフの割合			unsignedで良いのではないのか?		百分率×100=0～4100。4096以上を100%とする。		百分率を除算無しで近似計算するテクニック。v≦(2^19),0≦p≦100の時、(v*p/100)⇒min(v,(v*p*41)>>12)。尚、pが固定ならば、p*41を予め計算してもっと高速化できる。
	unsigned char		run;		//0=不使用チャネル,1=ゲートオフ中,2=ゲートオン中
	unsigned char		np;		//ネスト数×3。nestd[]をEmpty Ascendingスタックと見なしてスタックポインタとなる。
	unsigned char		tatt;		//トラック属性
	unsigned char		tsta;		//トラック状態(トランスポートとレガートのフラグ)
	char			trs;		//トランスポーズ
	char			dtn;		//デチューン
	char			porpd;				//ポルタメントパラメータ。muccが自動計算して出力しておりよくわからない。
	char			lnote;		//前回出力の音				unsignedで良いのではないのか?
	void (*portament)(struct tagSEQWK* psw);	//ポルタメント処理関数
	unsigned short		nestd[MAXNESTD];	//Empty Ascendingスタック。3要素づつ使って、+0=繰り返し回数,+1=リターンアドレス(オフセット),+2=ループアドレス(オフセット)	SeqRept,SeqNext,SeqBreakで使うときは+1と+2の用途が逆。動作上は問題無いが統一したい…
} SEQWK;
/*--------------------------------------------------------------------------*/
//※このブロックの定数がまだ良く判っていない。
#define PITCH16K	42903
#define PITCH440	26976
//16000/128*2.5*16=5000			16000/128=1秒間にBuffFreeが呼ばれる回数		16=tempoの倍数(SeqTempo()参照)		4分音符=24	T125[BPM]が指定されたらtempo=125*16=2000。フルスピードの1/2.5。フルスピードは16000/128*60/24=T312.5[BPM]。つまりMCOUNTはT312.5[BPM]に対応するtempo変数の設定値に相当する。
#define MCOUNT		5000
//16000
#define FOFS		PITCH16K
//              16384                 32
//              -----                 ------
#define IBASE0 (43008+PITCH440-69*256+60*256)
/*--------------------------------------------------------------------------*/
#define XSF1		10	//音程変化(ポルタメント)のシフト数
#define XSF2		14	//音程変化(ビブラート)のシフト数
/****************************************************************************
 *	\usr\PIECE\sysdev\music\instdef.c より
 ****************************************************************************/
extern const INST i_square0;	// 0
extern const INST i_saw0;	// 1
extern const INST i_triangle0;	// 2
extern const INST i_square;	// 3
extern const INST i_saw;	// 4
extern const INST i_triangle;	// 5
extern const INST i_BD909;	// 6 bdr
extern const INST i_SDGATE;	// 7 sdr
extern const INST i_SD909;	// 8 rim
extern const INST i_HO909;	// 9 ohh
extern const INST i_HC909;	//10 chh
extern const INST i_CYMBD;	//11 ccy 12 rcy
extern const INST i_TOMH1;	//13 htm
extern const INST i_TOMM1;	//14 mtm
extern const INST i_TOML1;	//15 ltm
extern const INST i_HANDCLAP;	//16 hcp
static const INST* const inst[]={
	&i_square0,		// 0
	&i_saw0,		// 1
	&i_triangle0,		// 2
	&i_square,		// 3
	&i_saw,			// 4
	&i_triangle,		// 5
	&i_BD909,		// 6 bdr
	&i_SDGATE,		// 7 sdr
	&i_SD909,		// 8 rim
	&i_HO909,		// 9 ohh
	&i_HC909,		//10 chh
	&i_CYMBD,		//11 ccy
	&i_CYMBD,		//12 rcy
	&i_TOMH1,		//13 htm
	&i_TOMM1,		//14 mtm
	&i_TOML1,		//15 ltm
	&i_HANDCLAP,		//16 hcp
};
/****************************************************************************
 *	\usr\PIECE\sysdev\music\exp12.c より
 ****************************************************************************/
static const unsigned short TBL_expt[]={
	32768,			// 0		//0.5相当
	34716,			// 1
	36781,			// 2
	38968,			// 3
	41285,			// 4
	43740,			// 5
	46341,			// 6
	49097,			// 7
	52016,			// 8
	55109,			// 9
	58386,			//10
	61858,			//11
	65536-1,		//11+1		//1.0相当		//本当は65536だが(unsigned short)配列に収めるために-1した。聴いて違いが判らないだろう。		良く考えたらこのように-1するのが却って適切かも知れない。戻り値を16bitのフィールドに格納している処理が有るから。もし最大戻り値が65536だと0になってしまうと思うから。
};
/*--------------------------------------------------------------------------*/
#ifndef PIECE
static int gexp(int n) {
	int div12t1, div12t2, expt, expb;
	div12t1 =      ((n >> 8) % 12);		//音階
	div12t2 = 20 - ((n >> 8) / 12);		//オクターブ
	if(div12t2 < 0) { div12t2 =  0; }
	expt = TBL_expt[div12t1 + 0];
	expb = TBL_expt[div12t1 + 1] - expt;
    //{{mlt.hを使用可能。結果は同じです。
    //	return (expt + (     (expb * (unsigned char)n) >> 8)) >> div12t2;
    //↓mlt.hを使用可能。結果は同じです。
	return (expt + (mlt_h(expb,  (unsigned char)n) >> 8)) >> div12t2;
    //}}mlt.hを使用可能。結果は同じです。
}
#else //PIECE
/*static*/ int gexp(int n);
asm("
		.code
		.align		1
gexp:
		ld.ub		%r4, %r12		;//%r4  := (unsigned char)n
		sra		%r12, 8			;//%r12 :=  (n >> 8)
		xcall.d		__divsi3		;//%r10 := ((n >> 8) / 12), %ahr := ((n >> 8) % 12)				//ksvec200.sの__divsi3が%ahrに剰余を返す事に依存。
		ld.w		%r13, 12		;//%r13 :=             12					*delay*
		ld.w		%r5, %ahr		;//%r5  := div12t1 = ((n >> 8) % 12)
		sla		%r5, 1			;//%r5  := div12t1 * 2
		ext		TBL_expt+0@ah		;//%r6  := expt = TBL_expt[div12t1 + 0]
		ext		TBL_expt+0@al		;//
		ld.uh		%r6, [%r5]		;//
		ext		TBL_expt+2@ah		;//%r5  :=        TBL_expt[div12t1 + 1]
		ext		TBL_expt+2@al		;//
		ld.uh		%r5, [%r5]		;//
	;//	sub		%r5, %r6		;//%r5  := expb = TBL_expt[div12t1 + 1] - expt		─┐
		ld.w		%r7, 20			;//%r7  :=           20					　│
		sub		%r7, %r10		;//%r7  := div12t2 = 20 - ((n >> 8) / 12)		　│
		jrge.d		3			;//if(div12t2 < 0) {					　│
		 sub		%r5, %r6		;//%r5  := expb = TBL_expt[div12t1 + 1] - expt		←┘	*delay*
		 ld.w		%r7, 0			;//  %r7  := div12t2 = 0 }
		mlt.h		%r5, %r4		;//%alr :=          (expb * (unsigned char)n)
		ld.w		%r10, %alr		;//%r10 :=          (expb * (unsigned char)n)
		sra		%r10, 8			;//%r10 :=          (expb * (unsigned char)n) >> 8
		add		%r10, %r6		;//%r10 := (expt + ((expb * (unsigned char)n) >> 8))
	    ;//{{%r7を破壊しても良いのでxsraを最適化
	    ;//	xsra		%r10, %r7
	    ;//	ret
	    ;//↓%r7を破壊しても良いのでxsraを最適化
		cmp		%r7, 8			;//←─┐
		jrle		4			;//─┐│
		sra		%r10, 8			;//　││
		jp.d		-3			;//─│┘
		sub		%r7, 8			;//　│								*delay*
		ret.d					;//←┘
		sra		%r10, %r7		;//								*delay*
	    ;//}}%r7を破壊しても良いのでxsraを最適化
");
#endif//PIECE
/****************************************************************************
 *	\usr\PIECE\sysdev\music\mus.c より
 ****************************************************************************/
//波形読み出し
#ifndef PIECE
static void MakeWave(MDEVICE* mp, int* p, int v, int cnt) {
	unsigned freqwk   = mp->freqwk;
	unsigned freq     = mp->freq;
	const char* pData = mp->pData;
	unsigned loop_end = mp->loop_end;
	unsigned loop_w   = mp->loop_w;
	v >>= 1;		//(v=0～65535)→(v=0～32767)
	do {
		unsigned x = freqwk >>    14;
	    //{{補間処理。確かに聴いて違いは判るが負荷低減のためには省略可能。
	    //	unsigned y = freqwk & 0x3FFF;
	    //}}補間処理。確かに聴いて違いは判るが負荷低減のためには省略可能。
		int d1 = pData[x + 0];
	    //{{補間処理。確かに聴いて違いは判るが負荷低減のためには省略可能。
	    //	int d2 = pData[x + 1];
	    //	d2   -= d1;
	    //	d2   *=  y;
	    //	d2  >>= 14;
	    //	d1   += d2;
	    //}}補間処理。確かに聴いて違いは判るが負荷低減のためには省略可能。
	    //{{(d1=-128～127)×(v=0～32767)の乗算なのでmlt.hを使用可能。結果は同じです。
	    //	d1   *= v;
	    //↓(d1=-128～127)×(v=0～32767)の乗算なのでmlt.hを使用可能。結果は同じです。
	    	d1 = mlt_h(d1, v);
	    //}}(d1=-128～127)×(v=0～32767)の乗算なのでmlt.hを使用可能。結果は同じです。
		if((freqwk += freq) >= loop_end) {
			if(loop_w) {	//ループ
				freqwk -= loop_w;
			} else {	//非ループ
				mp->freq = 0;
				return;
			}
		}
		*p++ += d1 >> 7;
	} while(--cnt);
	mp->freqwk = freqwk;
}
#else //PIECE
/*static*/ void MakeWave(MDEVICE* mp, int* p, int v, int cnt);
asm("
		.code
		.align		1
MakeWave:
		ld.w		%r4, [%r12]+		;//%r4  := freqwk   = mp->freqwk
		ld.w		%r5, [%r12]+		;//%r5  := freq     = mp->freq
		ld.w		%r6, [%r12]+		;//%r6  := pData    = mp->pData
		ld.w		%r7, [%r12]+		;//%r7  := loop_end = mp->loop_end
		ld.w		%r9, [%r12]		;//%r9  := loop_w   = mp->loop_w
		sub		%r12, 16		;//%r12 :=            mp
		sra		%r14, 1			;//%r14 := v >>= 1
		ld.w		%r10, %r4		;//%r10 := x = freqwk			─┐
MakeWave_LOOP:						;//do {					　│
		xsrl		%r10, 14		;//  %r10 := x >>= 14			←┤
		add		%r10, %r6		;//  %r10 :=     &pData[x]		　│
		ld.b		%r10, [%r10]		;//  %r10 := d1 = pData[x]		　│
		ld.w		%r11, [%r13]		;//  %r11 := t = *p			　│	*anti-interlock*
		mlt.h		%r10, %r14		;//  %alr :=      (d1 * v)		　│
		ld.w		%r10, %alr		;//  %r10 :=      (d1 * v)		　│
		add		%r4, %r5		;//  %r4  := freqwk += freq		　│
		cmp		%r4, %r7		;//  if(freqwk >= loop_end) {		　│
		jrlt.d		MakeWave_NEXT		;//    					　│
		sra		%r10, 7			;//  %r10 :=      (d1 * v) >> 7		　│	*delay*
		cmp		%r9, 0			;//    if(!loop_w) { goto STOP }	　│
		jreq		MakeWave_STOP		;//    					　│
		sub		%r4, %r9		;//    %r4  := freqwk -= loop_w		　│
MakeWave_NEXT:						;//  }					　│
		add		%r11, %r10		;//  %r11 := t += (d1 * v) >> 7		　│
		ld.w		[%r13]+, %r11		;//  *p++ =  t				　│
		sub		%r15, 1			;//  %r15 := cnt--			　│
		jrne.d		MakeWave_LOOP		;//} while(cnt)				　│
		ld.w		%r10, %r4		;//%r10 := x = freqwk			─┘	*delay*
		xld.w		[%r12+0], %r4		;//mp->freqwk = x
		ret					;//return
MakeWave_STOP:						;//
		xld.w		[%r12+4], %r8		;//mp->freq = 0
		ret					;//return
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
//矩形波に最適化
#ifndef PIECE
static void MakeWaveSQR(MDEVICE* mp, int* p, int v, int cnt) {
	int x = mp->freqwk << 16;
	int y = mp->freq   << 16;
	int w = v >> 1;		//(v=0～65535)→(w=0～32767)
	do {
		int t = *p + w;			//+v/2
		if((x += y) < 0) { t -= v; }	//-v/2
		*p++ = t;
	} while(--cnt);
	mp->freqwk = x >> 16;
}
#else //PIECE
/*static*/ void MakeWaveSQR(MDEVICE* mp, int* p, int v, int cnt);
asm("
		.code
		.align		1
MakeWaveSQR:
		xld.w		%r4, [%r12+0]		;//%r4  := x = mp->freqwk
		xld.w		%r5, [%r12+4]		;//%r5  := y = mp->freq		*anti-interlock*
		xsla		%r4, 16			;//%r4  := x <<= 16		*anti-interlock*
		xsla		%r5, 16			;//%r5  := y <<= 16
		ld.w		%r6, %r14		;//%r6  := w = v
		sra		%r6, 1			;//%r6  := w >>= 1
		add		%r4, %r5		;//%r4  := x += y	─┐
MakeWaveSQR_LOOP:					;//do {			　│
		ld.w		%r7, [%r13]		;//  %r7  := t = *p	　│
		jrge.d		3			;//  if(x < 0) {	←┤	*anti-interlock*
		 add		%r7, %r6		;//  %r7  := t += w	　│	*delay*
		 sub		%r7, %r14		;//    %r7  := t -= v	　│
		ld.w		[%r13]+, %r7		;//  *p++  = t }	　│
		sub		%r15, 1			;//  %r15 := cnt--	　│
		jrne.d		MakeWaveSQR_LOOP	;//} while(cnt)		　│
		add		%r4, %r5		;//%r4  := x += y	─┘	*delay*
		xsra		%r4, 16			;//%r4  := x >>= 16
		xld.w		[%r12+0], %r4		;//mp->freqwk = x
		ret					;//return
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
//のこぎり波に最適化
#ifndef PIECE
static void MakeWaveSAW(MDEVICE* mp, int* p, int v, int cnt) {
	int x = mp->freqwk;
	int y = mp->freq;
	v >>= 1;		//(v=0～65535)→(v=0～32767)
	do {
	    //{{((short)x=-32768～32767)×(v=0～32767)の乗算なのでmlt.hを使用可能。結果は同じです。
	    //	*p++ += ((short)x * v) >> 15;
	    //↓((short)x=-32768～32767)×(v=0～32767)の乗算なのでmlt.hを使用可能。結果は同じです。
	    	*p++ += mlt_h(  x,  v) >> 15;
	    //}}((short)x=-32768～32767)×(v=0～32767)の乗算なのでmlt.hを使用可能。結果は同じです。
		x += y;
	} while(--cnt);
	mp->freqwk = x;
}
#else //PIECE
/*static*/ void MakeWaveSAW(MDEVICE* mp, int* p, int v, int cnt);
asm("
		.code
		.align		1
MakeWaveSAW:
		xld.w		%r4, [%r12+0]		;//%r4  := x = mp->freqwk
		xld.w		%r5, [%r12+4]		;//%r5  := y = mp->freq
		sra		%r14, 1			;//%r14 := v >>= 1
MakeWaveSAW_LOOP:					;//do {
		ld.w		%r6, [%r13]		;//  %r6  := t = *p
		mlt.h		%r4, %r14		;//  %alr :=      (x * v)
		ld.w		%r7, %alr		;//  %r7  :=      (x * v)
		xsra		%r7, 15			;//  %r7  :=      (x * v) >> 15
		add		%r6, %r7		;//  %r6  := t += (x * v) >> 15
		ld.w		[%r13]+, %r6		;//  *p++  = t
		sub		%r15, 1			;//  %r15 := cnt--
		jrne.d		MakeWaveSAW_LOOP	;//} while(cnt)
		add		%r4, %r5		;//%r4  := x += y	*delay*
		xld.w		[%r12+0], %r4		;//mp->freqwk = x
		ret					;//return
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
//三角波に最適化
#ifndef PIECE
static void MakeWaveTRI(MDEVICE* mp, int* p, int v, int cnt) {
	int x = mp->freqwk;
	int y = mp->freq;
	v >>= 1;		//(v=0～65535)→(v=0～32767)
	do {
	    //{{mlt.hを使用可能。結果は同じです。
	    //	*p++ += (abs((short)x * v ) >> 14) - v;
	    //↓mlt.hを使用可能。結果は同じです。
	    	*p++ += (abs(mlt_h( x,  v)) >> 14) - v;
	    //}}mlt.hを使用可能。結果は同じです。
		//           ~~~~~~~~~~~~~             符号付き31bit
		//       ~~~~~~~~~~~~~~~~~~            符号無し30bit
		//       ~~~~~~~~~~~~~~~~~~~~~~~~      符号無し16bit
		//      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 符号付き16bit
		x += y;
	} while(--cnt);
	mp->freqwk = x;
}
#else //PIECE
/*static*/ void MakeWaveTRI(MDEVICE* mp, int* p, int v, int cnt);
asm("
		.code
		.align		1
MakeWaveTRI:
		xld.w		%r4, [%r12+0]		;//%r4  := x = mp->freqwk
		xld.w		%r5, [%r12+4]		;//%r5  := y = mp->freq
		sra		%r14, 1			;//%r14 := v >>= 1
MakeWaveTRI_LOOP:					;//do {
		ld.w		%r6, [%r13]		;//  %r6  := t = *p
		mlt.h		%r4, %r14		;//  %alr :=          (x * v)
		ld.w		%r7, %alr		;//  %r7  :=          (x * v)
		cmp		%r7, 0			;//  %r7  :=       abs(x * v)
		jrge.d		4			;//  
		 sra		%r7, 8			;//  %r7  :=       abs(x * v) >> 8	*delay*
		 not		%r7, %r7		;//  
		 add		%r7, 1			;//  
		sra		%r7, 6			;//  %r7  :=       abs(x * v) >> 14
		sub		%r7, %r14		;//  %r7  :=      (abs(x * v) >> 14) - v
		add		%r6, %r7		;//  %r6  := t += (abs(x * v) >> 14) - v
		ld.w		[%r13]+, %r6		;//  *p++  = t
		sub		%r15, 1			;//  %r15 := cnt--
		jrne.d		MakeWaveTRI_LOOP	;//} while(cnt)
		add		%r4, %r5		;//%r4  := x += y			*delay*
		xld.w		[%r12+0], %r4		;//mp->freqwk = x
		ret					;//return
");
#endif//PIECE
/*==========================================================================*/
//エンベロープ・ジェネレーター
// - 元ソースのVOLSは演算精度を上げるための下駄だと思うが、聴いて違いが判らなかったので負荷低減のために省略した。
#define ENVSTART	( -4*12*256)	//アタックの開始時の減衰量
#define ENVSTOP		(-10*12*256)	//リリースが終了する減衰量
#define ENVRR		        4000	//リリースレート(固定)
/*--------------------------------------------------------------------------*/
//MDEVICE.genenv
static void GenEnvR(MDEVICE* mp) {	//リリース
	if((mp->envwk -= ENVRR) < ENVSTOP) {		//リリースレートを減算し、エンベロープ終了の減衰量を下回ったら…		サステインレートを指定しリリースレートは固定。OPLLのEGTYP=0と同じ。
		mp->envwk	= ENVSTOP;
		mp->freq	= 0;	//このデバイスチャネルが発声中でないことを示す。
	}
}
static void GenEnvS(MDEVICE* mp) {	//サステイン
	if((mp->envwk -= mp->envSR) < ENVSTOP) {	//サステインレートを減算し、エンベロープ終了の減衰量を下回ったら…		サステインレートを指定しリリースレートは固定。OPLLのEGTYP=0と同じ。
		mp->envwk	= ENVSTOP;
		mp->freq	= 0;	//このデバイスチャネルが発声中でないことを示す。
	}
}
static void GenEnvD(MDEVICE* mp) {	//ディケイ
	if((mp->envwk -= mp->envDR) < mp->envSL) {	//ディケイレートを減算し、サステインレベル未満になったら…
		mp->genenv	= GenEnvS;		//サステインへ移行する。
	}
}
static void GenEnvA(MDEVICE* mp) {	//アタック
	if((mp->envwk += mp->envAR) > 0) {		//アタックレートを加算し、減衰量0を超過したら…
		mp->envwk	= 0;
		mp->genenv	= GenEnvD;		//ディケイへ移行する。
	}
}
/*--------------------------------------------------------------------------*/
static void GenEnvInit(MDEVICE* mp) {
	mp->envwk	= mp->envAR ? ENVSTART : 0;	//減衰量の初期値を設定する。ただしアタックレート=0ならば減衰量=0(減衰無し)で開始する。		MMLの'E'コマンドを使えばSetEnv()が呼ばれて必ずenvAR!=0になる。envAR=0になるのは'@'による音色設定後'E'コマンドを呼んでいない場合のみ。
	mp->genenv	= GenEnvA;
}
/*--------------------------------------------------------------------------*/
static int SetEnvSpeed(int n) {
	return gexp((n * 192) + (4 * 12 * 256));
}
/*--------------------------------------------------------------------------*/
static void SetEnv(MDEVICE* mp, int ar, int dr, int sl, int sr) {
	mp->envAR	= SetEnvSpeed(ar);		//AR:アタックレート		指定値後に100%に到達			0-255(大きい程速い)
	mp->envDR	= SetEnvSpeed(dr);		//DR:ディケイレート		指定値後に継続音の音量に到達		0-255
	if((sl -= 127) > 0) { sl = 0; }			//SL:サスティンレベル		継続音の音量				0-127			⇒音量(0～127)を減衰量(-127～0)に変換して使用する。
	mp->envSL	= sl * 192;
	mp->envSR	= SetEnvSpeed(sr);		//SR:サスティンレート		アタックから継続音への移行速度		0-255
}
/*==========================================================================*/
//ビブラート・ジェネレーター
#define T1CONST 0x4000
/*--------------------------------------------------------------------------*/
//MDEVICE.genvib
static void GenVibS2(MDEVICE* mp) {	//最大になった後
	mp->vibwk	+= mp->vibvv;
	mp->ptwk2	= ((0x4000 - abs(mp->vibwk)) * mp->vibdpwk) >> 3;
	//		   ~~~~~~~~~~~~~~~~~~~~~~~~
	//		  (+0x4000⇒0⇒-0x3FFF⇒0⇒+0x4000)の往復
}
static void GenVibS1(MDEVICE* mp) {	//かかり始めてから最大になるまで
	if((mp->vibdpwk += mp->vibv2) >= mp->vibdpe) {
		mp->genvib	= GenVibS2;
		mp->vibdpwk	= mp->vibdpe;
	}
	GenVibS2(mp);
}
static void GenVibS0(MDEVICE* mp) {	//発音からかかり始めるまで
	if((mp->vibdpwk += mp->vibv1) >= 0) {
		mp->genvib	= GenVibS1;
		mp->vibwk	= 0;
		mp->vibdpwk	= 0;
	}
}
/*--------------------------------------------------------------------------*/
static void GenVibInit(MDEVICE* mp) {
	mp->genvib	= GenVibS0;
	mp->vibdpwk	= -T1CONST;
}
/*--------------------------------------------------------------------------*/
static void SetVib(MDEVICE* mp, int depth, int spd, int t1, int t2) {
	mp->vibdpe	= depth * 12 * 8;			//デプス	かかり具合の深さ			0-255(大きい程深い)																			┬意図通り動作する最大値は'Y255,191,127,191'
	mp->vibvv	= gexp((spd * 192) + (8 * 12 * 256));	//スピード	かかるスピード				0-255(大きい程早い)	spd>=192の時gexp(0xF000以上)が32768以上を返しshortに格納してオーバーフローする。	MMLのYa,b,c,dのbには実質191以下しか指定できない。	│良い感じに聞こえる設定の例:'Y30,145,70,50'
	mp->vibv1	= gexp((t1  * 192) + (8 * 12 * 256));	//ディレイ	発音からかかり始めるまでの時間		0-255(大きい程短い)	t1 >=192の時gexp(0xF000以上)が32768以上を返しshortに格納してオーバーフローする。	MMLのYa,b,c,dのcには実質191以下しか指定できない。	│
	mp->vibv2	= gexp((t2  * 192) + (8 * 12 * 256));	//レート	かかり始めてから最大になるまでの時間	0-255(大きい程短い)	t2 >=192の時gexp(0xF000以上)が32768以上を返しshortに格納してオーバーフローする。	MMLのYa,b,c,dのdには実質191以下しか指定できない。	┘
}
/*==========================================================================*/
//音色
static void SetInst(MDEVICE* mp, int no) {
	const INST* ip = inst[no];
//不要	mp->freqwk	= 0;
	mp->freq	= 0;
	mp->pData	= ip->pData;
	mp->loop_end	= ip->loop_end;
	mp->loop_w	= (ip->wtype & IT_LOOP) ? (ip->loop_end - ip->loop_top) : 0;
	mp->ipitch	= (ip->pitch_fs - ip->pitch_org) + (IBASE0 - FOFS);
	mp->envAR	= 0;									//音色を切り替える時アタックレートだけクリアするのか？		\usr\PIECE\docs\資料\musbin.txtに「0xea : エンベロープ　音色コマンドで各音色の指定値に設定されますので、」と書いてあるが、元ソースでアタックレートしか設定されていないようだ。仕様か？		⇒2015/01/23追記:アタックレート(envAR)だけリセットする処理で問題無いことが判った。GenEnvA()を参照せよ。(envAR=0)ならばGenEnvA()の完了条件(>0)を満たさない。他のパラメータに関係無くエンベロープ無しになる。つまり各音色の指定値とはエンベロープ無しの事なのだ。
	if(ip->wtype & IT_FAST) {
		static void (* const TBL_genwave[])(struct tagMDEVICE*,int*,int,int)={
			MakeWaveSQR,	// 0
			MakeWaveSAW,	// 1
			MakeWaveTRI,	// 2
		};
		mp->genwave = TBL_genwave[ip->param];
	} else {
		mp->genwave = MakeWave;
	}
}
/*--------------------------------------------------------------------------*/
//ボリューム
static void SetVol(MDEVICE* mp, int vol) {
	mp->pvol	= (vol * 192) + (11 * 12 * 256);
	mp->pexp	= 0;
}
/*--------------------------------------------------------------------------*/
//音量調整(設定)
static void SetExp(MDEVICE* mp, int exp) {
	mp->pexp	= (exp - 127) * 192;
}
//音量調整(増減)
static void SetExpRel(MDEVICE* mp, int expr) {
	mp->pexp	+= expr * 192;
}
/*--------------------------------------------------------------------------*/
//ノートオン
static void NoteOn(MDEVICE* mp, int pitch) {
	mp->freqwk	= 0;
	mp->freq	= 1;	//このデバイスチャネルが発声中であることを示す。
	mp->cpitch	= mp->ipitch + pitch;
	mp->ptwk1	= 0;
	mp->ptwk2	= 0;
	GenEnvInit(mp);
	GenVibInit(mp);
}
//レガート中の音程変更
static void NotePitch(MDEVICE* mp, int pitch) {
	mp->cpitch	= mp->ipitch + pitch;
}
//ノートオフ
static void NoteOff(MDEVICE* mp) {
	mp->genenv	= GenEnvR;
}
/****************************************************************************
 *	/usr/PIECE/sysdev/music/seq.c より
 ****************************************************************************/
//グローバル変数
	/*const*/char*		title		= "";
	/*const*/char*		title2		= "";
	unsigned char		music_wch	=  0;
static	         char		using_wch	= -1;
/*--------------------------------------------------------------------------*/
static	const unsigned char*	datatop;
static	const unsigned char*	drumsbase;
static	short			tempo;
static	short			tempowk;
static	char			runf;
static	SEQWK			seqwk[MAXCH];
/*--------------------------------------------------------------------------*/
#if 1
static int getadrs(const unsigned char* p) { return p[0] + (p[1] << 8); }
#else	//↑↓関数でもマクロでも偶然同じコードサイズになったので、関数のままにしておく事にした。
#define    getadrs(                     p)   LEHALF(p)
#endif
/*--------------------------------------------------------------------------*/
static void SeqNop(SEQWK* psw) {
	/** no job **/
}
/*--------------------------------------------------------------------------*/
static void SeqNop1(SEQWK* psw) {	//SeqMasterVol
	psw->pseq += 1;
}
/*--------------------------------------------------------------------------*/
static void SeqNop2(SEQWK* psw) {	//SeqMasterFade,SeqPartFade,SeqBend
	psw->pseq += 2;
}
/*--------------------------------------------------------------------------*/
static void StartSeq(const unsigned char* seqptr) {
	int i, partn;
	datatop		= seqptr;
	tempo		= 120 << 4;
	tempowk		= 0;
	runf		= 1;
	             partn = *seqptr++;		//[0]		//空チャネルがオフセット=0によって'終了/リターン'を指せるように、ファイル先頭(オフセット=0)に'終了/リターン'が入っている形式が有る。
	if(!partn) { partn = *seqptr++; }	//[1]
	if((partn < 1) || (partn > MAXCH)) { DIE(); }
	memset(seqwk, 0, sizeof seqwk);
	for(i = 0; i < partn; i++) {
		SEQWK* psw = &seqwk[i];
		psw->pseq	= datatop + getadrs(seqptr);	seqptr += 2;	//もし空チャネルならばpsw->pseqが'終了/リターン'を指すようになっているので、明示的に空チャネルを判定する必要は無い。
		psw->gate	= 1 * 41;	//gate=99			//百分率を除算無しで近似計算するテクニック。v≦(2^19),0≦p≦100の時、(v*p/100)⇒min(v,(v*p*41)>>12)。尚、pが固定ならば、p*41を予め計算してもっと高速化できる。
		psw->run	= 1;		//ゲートオフ中
		psw->portament	= SeqNop;
		SetInst(&psw->mdwk, 0);
		SetVol( &psw->mdwk, 127);
	}
	drumsbase	= datatop + getadrs(seqptr);		seqptr += 2;
	title	= (char*)(datatop + getadrs(seqptr));		seqptr += 2;		//オフセットが0ならばファイル先頭の0を指して自然に空文字列になる。処理で意識する必要は無い。
	title2	= (char*)(datatop + getadrs(seqptr));	//	seqptr += 2;		//オフセットが0ならばファイル先頭の0を指して自然に空文字列になる。処理で意識する必要は無い。
}
/*--------------------------------------------------------------------------*/
//SEQWK.portament
static void SeqPortaExecP(SEQWK* psw) {
	if((psw->mdwk.ptwk1 += psw->porvv) >= 0) {	//音程を増加して、目標音程との差分が0を超えたら…
		psw->mdwk.ptwk1 = 0;
		psw->tsta &= ~TSTA_INPORTA;		//ポルタメント中フラグを下げる
	}
}
static void SeqPortaExecN(SEQWK* psw) {
	if((psw->mdwk.ptwk1 -= psw->porvv) <= 0) {	//音程を減少して、目標音程との差分が0を下回ったら…
		psw->mdwk.ptwk1 = 0;
		psw->tsta &= ~TSTA_INPORTA;		//ポルタメント中フラグを下げる
	}
}
/*--------------------------------------------------------------------------*/
static void SeqNote(SEQWK* psw, int note) {
	int ntw, len, x;
	ntw = ((note + psw->trs) << 8) + psw->dtn;		//トランスポーズ,デチューン
	psw->cnt = psw->len;					//音長
	if((psw->tsta & TSTA_LEG2) && (psw->run == 2)) {	//レガート中(レガートオフ直後も含む),かつ,ゲートオン中ならば…
		NotePitch(&psw->mdwk, ntw);			//　ノートオンせずに音程のみ変更する。
	} else {						//それ以外ならば…
		NoteOn(&psw->mdwk, ntw);			//　ノートオンする。
		psw->run = 2;					//　ゲートオン中にする。
	}
	if((psw->tsta & (TSTA_LEG1|TSTA_LEG2)) == TSTA_LEG2) {	//レガートオフ直後の最終ノートオンならば…('a&b&c'の'c')
		psw->tsta &= ~TSTA_LEG2;			//　レガート中フラグを下げる。
	}
	if(psw->tsta & TSTA_PORTASW) {				//ポルタメント
		if(!(x = psw->porpd)) { x = psw->lnote - note; }		//開始音程を取得する。相対(-127～-1,1～127)，又は，(前回出力の音-今回出力の音)		実際にはmuccが自動計算しており、porpd=0になることは無さそうだ。
		psw->mdwk.ptwk1 = (x << (XSF1 + 8));				//XSF1=音程変化(ポルタメント)のシフト数，8=gexp()の小数部
		psw->portament  = (x < 0) ? SeqPortaExecP : SeqPortaExecN;	//開始音程が負ならば増加(SeqPortaExecP)，開始音程が正ならば減少(SeqPortaExecN)
		psw->tsta      |= TSTA_INPORTA;					//ポルタメント中
	}
	psw->lnote = note;	//次回に'前回出力の音'として参照するために、今回出力の音を覚えておく。
	x = 0;
	if(!(psw->tatt & TATT_NOOFF)) {				//無停止が設定されていなければ…	//TATT_NOOFFはMMLで「=2」又は「=3」とするとセットさせる。MMLのマニュアルには「=1」しか書いてないが、「=」は任意のフラグを任意のタイミングで指定できる。
		len = psw->len;					//残り何カウントで発声停止するかを計算する。
		x = (len * psw->gate) >> 12;			//百分率を除算無しで近似計算するテクニック。v≦(2^19),0≦p≦100の時、(v*p/100)⇒min(v,(v*p*41)>>12)。尚、pが固定ならば、p*41を予め計算してもっと高速化できる。
		if(x <       1) { x =       1; }
		if(x > len - 1) { x = len - 1; }
	}
	psw->stop = x;
}
static void SeqNote2(SEQWK* psw) {				//muccの処理を見ると、'?'とそれに続く音符の処理は別処理。実質、'?'に続く音符は通常通り0x80～にしか成り得ない。当コマンドの目的が不明だが、muccが出力するので処理せざるを得ない。SeqNopにして次のノートをそのまま処理しても良さそうだが…
	int note = *psw->pseq++;				//└→上記の疑問が解決した。'!!bg'とかのドラムマクロの中で普通のノートオンを使うとSeqDrums()が呼び出されて無限再帰してしまう。ドラムマクロの中でノートオン(=ドラム発声)するために'?'でエスケープするのが目的だ。エスケープが目的なので、処理内容が普通のノートオンと同じなのは当然であった。尚、muccがドラムマクロ中かどうかを自動判定して出力してくれても良さそうなのだがそうなっていないので明示的に'?'を書く必要が有る。
	if(note >= 0x80) { note -= (0x80 - 12); }
	SeqNote(psw, note);
}
static void SeqDrums(SEQWK* psw, int note) {			//ドラムマクロを呼び出す。noteをドラムマクロ番号と見なす。
	int np = psw->np;
	int ad = getadrs(drumsbase + (note << 1));		//インダイレクトアドレッシングでドラムマクロ定義のオフセットを取得する。
	psw->nestd[np++] = 1;					//繰り返し回数は1固定。
	psw->nestd[np++] = psw->pseq - datatop;			//リスターンアドレス。
	psw->nestd[np++] = ad;					//ループアドレス。		繰り返し回数1なので実質使わないのでは?
	psw->pseq        = datatop + ad;			//次に実行するコマンドのアドレスを格納する。
	psw->np          = np;					//スタックポインタを更新する。
}
/*--------------------------------------------------------------------------*/
//休符
static void SeqRest(SEQWK* psw) {
	if(psw->run == 2) {		//ゲートオン中ならば…
		NoteOff(&psw->mdwk);	//ノートオフする。
		psw->run = 1;		//ゲートオフ中にする。
	}
	psw->cnt = psw->len;		//休符の音長を設定する。
}
/*--------------------------------------------------------------------------*/
//音色
static void SeqInst(SEQWK* psw) {
	SetInst(&psw->mdwk, *psw->pseq++);
}
/*--------------------------------------------------------------------------*/
//テンポ
static void SeqTempo(SEQWK* psw) {
	tempo = *psw->pseq++ << 4;	//tempoの値は1分間の4分音符の数の16倍
}
/*--------------------------------------------------------------------------*/
//終了/リターン
static void SeqEnd(SEQWK* psw) {
	int np = psw->np;
	if(!np) {			//ネスト中でなければ…
		psw->run = 0;		//不使用チャネルに戻す。
		return;			//ここまで。
	}
	np -= 3;						//スタックフレームを破棄した場合のベースポインタを求める。Empty Ascendingスタックなので。	まだ破棄しない。
	if(--psw->nestd[np]) {					//ループ回数を減らし、残っていたら…
		psw->pseq = datatop + psw->nestd[np + 2];	//ループ位置へ戻る。			スタックフレームは破棄せずに継続して使用する。
	} else {
		psw->pseq = datatop + psw->nestd[np + 1];	//脱出位置へ抜ける。
		psw->np = np;					//スタックフレームを破棄する。
	}
}
/*--------------------------------------------------------------------------*/
//ゲート
static void SeqGate(SEQWK* psw) {
	psw->gate = (100 - *psw->pseq++) * 41;			//百分率を除算無しで近似計算するテクニック。v≦(2^19),0≦p≦100の時、(v*p/100)⇒min(v,(v*p*41)>>12)。尚、pが固定ならば、p*41を予め計算してもっと高速化できる。
}
/*--------------------------------------------------------------------------*/
//フロー制御
static void SeqJump(SEQWK* psw) {	//※まだMMLとの対応がよくわかっていない。
	psw->pseq = datatop + getadrs(psw->pseq);
}
static void SeqCall(SEQWK* psw) {	//※まだMMLとの対応がよくわかっていない。
	int np = psw->np;
	int ad = getadrs(psw->pseq);				//引数1～2 = 呼び出しアドレス(オフセット)
	psw->nestd[np++] = psw->pseq[2];			//引数3    = ループ回数									muccはCallコマンドに対してはループ回数1しか生成しない。
	psw->nestd[np++] = (psw->pseq + 3) - datatop;		//リターンアドレス(オフセット)		当コマンドの引数が3個有るのでその後ろ
	psw->nestd[np++] = ad;					//ループアドレス(オフセット)
	psw->pseq        = datatop + ad;			//次に実行するコマンドのアドレスを格納する。
	psw->np          = np;					//スタックポインタを更新する。
}
static void SeqRept(SEQWK* psw) {	//MMLの'['
	int np = psw->np;
	psw->nestd[np++] = *psw->pseq++;			//ループ回数
	psw->nestd[np++] = psw->pseq - datatop;			//ループアドレス(オフセット)
   //{{2015/01/15修正
   //	np++;							//リターンアドレスはまだ設定しない。
   //↓2015/01/15修正
	psw->nestd[np++] = 0;					//'[～:～]1'が予期しない動作になることを防ぐために、もし書かれた場合はオフセット0(多分終了コマンド)へ飛ぶことにした。元ソースでは対策されていない。														SeqBreak()のコメントも参照せよ。
   //}}2015/01/15修正
	psw->np          = np;					//スタックポインタを更新する。													！！！マニュアルには「回数を0で無限ループになります。」と書いてあるが、処理されていない。256回で終わるのではないか？
}
static void SeqNext(SEQWK* psw) {	//MMLの']'
	int np = psw->np - 3;					//スタックフレームを破棄した場合のベースポインタを求める。Empty Ascendingスタックなので。	まだ破棄しない。
	if(--psw->nestd[np]) {					//ループ回数を減らし、残っていたら…												！！！マニュアルには「回数を0で無限ループになります。」と書いてあるが、処理されていない。256回で終わるのではないか？
		psw->nestd[np + 2] = psw->pseq - datatop;	//リターンアドレスを設定する。
		psw->pseq = datatop + psw->nestd[np + 1];	//ループ位置へ戻る。
	} else {
		psw->np = np;					//スタックフレームを破棄する。
	}
}
/*--------------------------------------------------------------------------*/
//トゥーコーダ
static void SeqBreak(SEQWK* psw) {	//MMLの':'
	int np = psw->np - 3;					//スタックフレームを破棄した場合のベースポインタを求める。Empty Ascendingスタックなので。	まだ破棄しない。
	if(psw->nestd[np] == 1) {				//最後の繰り返しならば、抜ける。
		psw->pseq = datatop + psw->nestd[np + 2];	//リターンアドレスを設定する。		SeqNext()の初回のループ時に設定されている。もし'[～:～]1'というMMLを書くと、SeqNext()がリターンアドレスを設定する前にリターンアドレスを参照してしまい、不正な動作になるのではないか？					SeqRept()で暫定対策を行った。
		psw->np = np;					//スタックフレームを破棄する。
	}
}
/*--------------------------------------------------------------------------*/
//トランスポーズ
static void SeqTrs(SEQWK* psw) {
	psw->trs = *psw->pseq++;	//trsフィールドがchar型なので代入すれば自動的にchar型になる。
}
/*--------------------------------------------------------------------------*/
//エンベロープ
static void SeqEnv(SEQWK* psw) {
	SetEnv(&psw->mdwk, psw->pseq[0], psw->pseq[1], psw->pseq[2], psw->pseq[3]);
	psw->pseq += 4;
}
/*--------------------------------------------------------------------------*/
//ボリューム
static void SeqVol(SEQWK* psw) {
	SetVol(&psw->mdwk, *psw->pseq++);
}
/*--------------------------------------------------------------------------*/
//デチューン
static void SeqDtn(SEQWK* psw) {
	psw->dtn = *psw->pseq++;	//dtnフィールドがchar型なので代入すれば自動的にchar型になる。
}
/*--------------------------------------------------------------------------*/
//ポルタメント
static void SeqPortaPara(SEQWK* psw) {		//ポルタメントパラメータはmuccが計算している。計算方法の詳細が不明だが、とりあえず気にしなくて良さそうだ。
	psw->porpd = *psw->pseq++;
	psw->porvv = (gexp((*psw->pseq++ * 192) + (5 * 12 * 256)) << XSF1);
	psw->porvv *= tempo;
	psw->porvv /= MCOUNT;
}
//ポルタメントオン
static void SeqPortaOn(SEQWK* psw) {
	psw->tsta |= TSTA_PORTASW;			//ポルタメントスイッチオン
}
//ポルタメントオフ
static void SeqPortaOff(SEQWK* psw) {
	psw->tsta &= ~(TSTA_PORTASW|TSTA_INPORTA);	//ポルタメントスイッチオフ,,ポルタメント中フラグオフ
	psw->mdwk.ptwk1 = 0;
}
/*--------------------------------------------------------------------------*/
//トラック属性
static void SeqTatt(SEQWK* psw) {
	psw->tatt = *psw->pseq++;			//現状、意味を持つのはbit0(ドラム)とbit1(無停止)だけだが、他のbitが立っていても動作上は問題無い。
}
/*--------------------------------------------------------------------------*/
//ビブラート
static void SeqVibrato(SEQWK* psw) {
	SetVib(&psw->mdwk, psw->pseq[0], psw->pseq[1], psw->pseq[2], psw->pseq[3]);
	psw->pseq += 4;
}
/*--------------------------------------------------------------------------*/
//レガートオン
static void SeqLegOn(SEQWK* psw) {	//MMLで'a&b&c'と書いた時に、aの前に出力される。
	psw->tsta |= (TSTA_LEG1|TSTA_LEG2);	//レガート中,かつ,レガートオン後初回のノートオン前
}
//レガートオフ
static void SeqLegOff(SEQWK* psw) {	//MMLで'a&b&c'と書いた時に、cの前に出力される。
	psw->tsta &= ~TSTA_LEG1;		//レガート中フラグを下げる
}
/*--------------------------------------------------------------------------*/
//音量調整(設定)
static void SeqExp(SEQWK* psw) {	//音量調整値を設定する
	SetExp(&psw->mdwk, *psw->pseq++);
}
//音量調整(増減)
static void SeqExpRel(SEQWK* psw) {	//音量調整値を増減する		\usr\PIECE\tools\mucc\mmlcv.cが'-'を見ていないバグではないか？		「Ｐ/ＥＣＥ　ＭＭＬ文法リファレンス」の説明は間違いではないか？
	SetExpRel(&psw->mdwk, *(char*)psw->pseq++);
}
/*--------------------------------------------------------------------------*/
static void (* const SeqCmd[32])(SEQWK* psw) = {
	SeqRest,	//0xE0
	SeqGate,	//0xE1
	SeqJump,	//0xE2
	SeqCall,	//0xE3
	SeqRept,	//0xE4
	SeqNext,	//0xE5
	SeqTrs,		//0xE6
	SeqTempo,	//0xE7
	SeqInst,	//0xE8
	SeqVol,		//0xE9
	SeqEnv,		//0xEA
	SeqDtn,		//0xEB
	SeqNote2,	//0xEC
	SeqPortaPara,	//0xED
	SeqPortaOn,	//0xEE
	SeqPortaOff,	//0xEF
	SeqTatt,	//0xF0
	SeqVibrato,	//0xF1
	SeqNop1,	//0xF2	SeqMasterVol	muccはこのコマンドを生成しない。\usr\PIECE\sysdev\music\seq.cも未対応。従ってどういう挙動が正しいのか不明。今後も実装されることは無いだろう。
	SeqNop2,	//0xF3	SeqMasterFade	muccはこのコマンドを生成しない。\usr\PIECE\sysdev\music\seq.cも未対応。従ってどういう挙動が正しいのか不明。今後も実装されることは無いだろう。
	SeqNop2,	//0xF4	SeqPartFade	muccはこのコマンドを生成しない。\usr\PIECE\sysdev\music\seq.cも未対応。従ってどういう挙動が正しいのか不明。今後も実装されることは無いだろう。
	SeqNop2,	//0xF5	SeqBend		muccはこのコマンドを生成しない。\usr\PIECE\sysdev\music\seq.cも未対応。従ってどういう挙動が正しいのか不明。今後も実装されることは無いだろう。
	SeqBreak,	//0xF6
	SeqNop,		//0xF7
	SeqLegOn,	//0xF8
	SeqLegOff,	//0xF9
	SeqExp,		//0xFA
	SeqExpRel,	//0xFB
	SeqNop,		//0xFC
	SeqNop,		//0xFD
	SeqNop,		//0xFE
	SeqNop,		//0xFF
};
/*--------------------------------------------------------------------------*/
static void ProcSeq() {
	int i, x;
	//ポルタメント
	for(i = 0; i < MAXCH; i++) {
		SEQWK* psw = &seqwk[i];
		//このシーケンサチャネルが使用中ならば…
		if(psw->run) {
			if(psw->tsta & TSTA_INPORTA) {	//ポルタメント中ならば…
				(*psw->portament)(psw);
			}
		}
	}
	//テンポ
	if((tempowk -= tempo) >= 0) { return; }		//┬tempo=BPM×16
	    tempowk += MCOUNT;				//┘MCOUNT=T312.5[BPM]相当
	//シーケンサ
	runf = 0;			//演奏中フラグをクリアしておく。この下のループでシーケンサチャネルが一つでも使用中ならば1になる。
	for(i = 0; i < MAXCH; i++) {
		SEQWK* psw = &seqwk[i];
		//このシーケンサチャネルが使用中ならば…
		if(psw->run) {
			runf = 1;	//演奏中フラグをセットする。
			if(--psw->cnt <= 0) {			//残り音長が無くなったら…
				do {
					x = *psw->pseq++;
					//終了/リターン
					if(!x) {
						SeqEnd(psw);
						if(!psw->run) { break; }
					//音長/拡張音長
					} else if(x < 0x80) {
						if(x == 0x7F) {	//拡張音長
							x = *psw->pseq++;
							if(x < 127) { x += 256; }	//0x7F～0xFF:127～255(そのまま),0x00～0x7E:256～382(+256して使用)	参照資料:\usr\PIECE\docs\資料\musbin.txt
						}
						psw->len = x;
					//音符(C0-B7)
					} else if(x < 0xE0) {
						if(psw->tatt & TATT_DRUMS) {		//ドラムパートが設定されていたら…	//TATT_NOOFFはMMLで「=1」又は「=3」とするとセットさせる。MMLのマニュアルには「=1」しか書いてないが、「=」は任意のフラグを任意のタイミングで指定できる。
							SeqDrums(psw, x - 0x80);	//ノート番号をマクロ番号と見なして実行する。つまりドラムパートでノートを鳴らそうとしたら不正動作になる事になる。
						} else {				//ドラムパートが設定されていなければ…
							SeqNote(psw, x - (0x80 - 12));	//'o0c'を内部処理では音程'12'として扱うためのオフセット。
						}
					//コマンド
					} else {
						(*SeqCmd[x - 0xE0])(psw);
					}
				} while(psw->cnt <= 0);		//音長が設定されるまで繰り返す。
			} else {
				//ゲートタイム
				if((psw->run == 2) &&		//ゲートオン中ならば…
				   (psw->cnt <= psw->stop) &&	//ゲートオフ時間に達したら…
				  !(psw->tsta & TSTA_LEG1)) {	//レガート中でなければ…
					NoteOff(&psw->mdwk);	//ノートオフする。
					psw->run = 1;		//ゲートオフ中にする。
				}
			}
		}
	}
}
/****************************************************************************
 *	ローカル関数
 ****************************************************************************/
#define BLKN	4	//調整可。この値を増やすと遅延が増えるが処理遅れの許容が増える。
#define BLKS	128	//変更不可。上記のシーケンサ処理が値に依存して計算しているから。
static PCEWAVEINFO waveinfo[BLKN];	//WAVE情報構造体
static short wavebuff[BLKN][BLKS];	//WAVEバッファ
/*--------------------------------------------------------------------------*/
//展開バッファを切り詰めてWAVEバッファに格納する。
#ifndef PIECE
static void BuffFree_Mix2(short wbuff[/*BLKS*/], int mixbuf[/*BLKS*/]) {
	int i, x;
	i = BLKS;
	do {
		x = *mixbuf++;
		x <<= 2;												//ラウドネス(調整可)
		if(x > SHRT_MAX) { x = SHRT_MAX; }
		if(x < SHRT_MIN) { x = SHRT_MIN; }
		*wbuff++ = x;
	} while(--i);
}
#else //PIECE
/*static*/ void BuffFree_Mix2(short wbuff[/*BLKS*/], int mixbuf[/*BLKS*/]);
asm("
		.code
		.align		1
		.global		BuffFree_Mix2
BuffFree_Mix2:
		;//-------------------------------------------------------------;//
		;//%r12 = wbuff
		;//%r13 = mixbuf
		xld.w		%r4, 128					;//%r4     := i = BLKS
		xld.w		%r5, 32767					;//%r5     := SHRT_MAX
		not		%r6, %r5					;//%r6     := SHRT_MIN
BuffFree_Mix2_L1:								;//do {
		ld.w		%r7, [%r13]+					;//%r7     := x  =  *mixbuf++
		sla		%r7, 2						;//%r7     := x <<= 2			//ラウドネス(調整可)
		cmp		%r7, %r5					;//%psr    := x <=> SHRT_MAX
		jrle.d		3						;//if(        x   > SHRT_MAX) {
		 cmp		%r7, %r6					;//%psr    := x <=> SHRT_MIN		─┐		*delay*
		 ld.w		%r7, %r5					;//  %r7   := x  =  SHRT_MAX  }		　│
		jrge.d		3						;//if(        x <   SHRT_MIN) {		←┘
		 sub		%r4, 1						;//%r4     := i--			─┐		*delay*
		 ld.w		%r7, %r6					;//  %r7   := x  =  SHRT_MIN  }		　│
		ld.h		[%r12]+, %r7					;//*wbuff++ = x				　│
		jrne		BuffFree_Mix2_L1				;//} while(   i)			←┘
		;//-------------------------------------------------------------;//
		ret
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
static void BuffFree(PCEWAVEINFO* pwi) {
	int buff[BLKS], i, x;	//buff[BLKS]=512バイトなのでスタックに確保しても安全だろう。
	//展開バッファをクリアする。
	memset(buff, 0, sizeof buff);
	//シーケンサ処理を行う。
	ProcSeq();
	//各シーケンサチャネルについて…
	for(i = 0; i < MAXCH; i++) {
		SEQWK* psw = &seqwk[i];
		//このシーケンサチャネルが使用中ならば…
		if(psw->run) {
			MDEVICE* mp = &psw->mdwk;
			//このデバイスチャネルが発声中ならば…
			if(mp->freq) {
				//ビブラート・ジェネレーター
				(*mp->genvib)(mp);
				mp->freq = gexp(mp->cpitch + (mp->ptwk1 >> XSF1) + (mp->ptwk2 >> XSF2));
				//エンベロープ・ジェネレーター
				(*mp->genenv)(mp);
				x = mp->pvol + mp->pexp + mp->envwk;
				if(x <= 11) { x = 0; } else { x = gexp(x); }
				//波形読み出し
				(*mp->genwave)(mp, buff, x, BLKS);
			}
		}
	}
	//展開バッファを切り詰めてWAVEバッファに格納する。
	BuffFree_Mix2((short*)pwi->pData, buff);
}
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
void InitMusic() {
	int i;
	//WAVE情報構造体を初期化する。
	for(i = 0; i < BLKN; i++) {
		PCEWAVEINFO* pwi = &waveinfo[i];
		pwi->type	 = PW_TYPE_16BITPCM;		//輪にするのでPW_TYPE_CONT不要。
		pwi->pData	 = &wavebuff[i];
		pwi->len	 = BLKS;
		pwi->next	 = &waveinfo[(i + 1) % BLKN];	//輪にする。
		pwi->pfEndProc	 = BuffFree;
	}
}
/*--------------------------------------------------------------------------*/
void PlayMusic(unsigned char* seq) {
ENTER_CS;
	//まず確実に停止する。
	StopMusic();
	//シーケンサを開始する。
	StartSeq(seq);
	//サウンド出力を開始する。
	// - BLKS×BLKNの空白が先行出力されます。
	//   シーケンサの初回出力が反映するのはBLKNブロック後となります。
	//   常にBLKS×BLKN÷16000＝0.032秒の遅延(処理の余裕)が有ります。
	pceWaveDataOut(music_wch, waveinfo);
	using_wch =    music_wch;		//使用中のサウンドチャネルを記憶しておく。
LEAVE_CS;
}
/*--------------------------------------------------------------------------*/
void StopMusic() {
ENTER_CS;
	//サウンド出力中ならば停止する。
	if(using_wch >= 0) {
		pceWaveAbort(using_wch);
		             using_wch = -1;	//使用中のサウンドチャネルの記憶をクリア。
	}
	//シーケンサを停止する。
	memset(seqwk, 0, sizeof seqwk);
	runf = 0;
LEAVE_CS;
}
/*--------------------------------------------------------------------------*/
int MusicCheck() {
	//□本来の仕様:
	//│戻り値： ==0 停止中 <>0 演奏中
	//□P/ECE音楽ライブラリの挙動:
	//│戻り値： ==0 演奏中 <>0 停止中
	//P/ECE音楽ライブラリの挙動はバグだが既存のアプリケーションがそれに依存しているので同じ挙動にした。
	return !runf;	//│戻り値： ==0 演奏中 <>0 停止中
}



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



/*
\usr\PIECE\sysdev\music\mus.cを読んで勉強したテクニックのメモ。
いずれ、自分のライブラリに取り込もう。

//百分率を除算無しで近似計算するテクニック。
//v≦(2^19),0≦p≦100の時、(v*p/100)⇒min(v,(v*p*41)>>12)。
//尚、pが固定ならば、p*41を予め計算してもっと高速化できる。
#define apply_percent(v, p) ({				\
	unsigned __v__ = (v), __p__ = (p);		\
	unsigned __x__ = (__v__ * __p__ * 41) >> 12;	\
	(__x__ <= __v__) ? __x__ : __v__;


*/



