/*	
 *	clipmus.c
 *
 *	P/ECE標準音楽ライブラリ 補助ルーチン
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Mon May 24 06:00:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Tue Sep 23 18:35:17 JST 2014 Naoyuki Sawa
 *	- music_play(),music_stop(),music_stat()を追加しました。
 *	  これまで、個々のアプリケーションのapp.hでマクロ定義していましたが、今後は、ライブラリに含めることにしました。
 *	* Wed Sep 24 20:09:57 JST 2014 Naoyuki Sawa
 *	- music_loudness()を廃止し、music_init(),music_free(),music_filter_install()として汎用性を持たせました。
 *	  これまでのmusic_loudness()と同じ効果は、music_filter_install(music_filter_loudness)によって得られます。
 */
#include "clip.h"

//{{2014/09/23変更:music_play(),music_stop(),music_stat()を追加しました。これまで、個々のアプリケーションのapp.hでマクロ定義していましたが、今後は、ライブラリに含めることにしました。
//static int (*old_WaveDataOut)(int ch, PCEWAVEINFO* pwi);
//static int new_WaveDataOut(int ch, PCEWAVEINFO* pwi)
//{
//	short* p;
//	int i;
//	int v;
//	int len;
//
//	/* P/ECE標準音楽ライブラリの使用チャネルへの出力ならば、音量増幅します。
//	 * 念のため、16bit PCM形式であることを確認しておきます。
//	 * P/ECE標準音楽ライブラリの出力は、16bit PCM形式であるはずです。
//	 */
//	if((ch == music_wch) && (pwi->type & PW_TYPE_16BITPCM)) {
//		p = (short*)pwi->pData;
//		len = pwi->len;
//		for(i = 0; i < len; i++) {
//			v = *p << 2; /* 調整可。でも、4倍以上にすると音が割れるようです */
//			     if(v < -32767) v = -32767;
//			else if(v >  32767) v =  32767;
//			*p++ = v;
//		}
//	}
//
//	return old_WaveDataOut(ch, pwi);
//}
//void
//music_loudness(int enable)
//{
//	if(enable) {
//		if(old_WaveDataOut) return; /* 既にフックされていたら、何もしません */
//		old_WaveDataOut = pceVectorSetKs(KSNO_WaveDataOut, new_WaveDataOut);
//	} else {
//		if(!old_WaveDataOut) return; /* まだフックされていなければ、何もしません */
//		pceVectorSetKs(KSNO_WaveDataOut, old_WaveDataOut);
//		old_WaveDataOut = NULL; /* フック解除したことをマーク、忘れないで!! */
//	}
//}
//↓2014/09/23変更:music_play(),music_stop(),music_stat()を追加しました。これまで、個々のアプリケーションのapp.hでマクロ定義していましたが、今後は、ライブラリに含めることにしました。
static MUSICFILTER* music_filter;
void music_filter_install(MUSICFILTER* filter) {
	/* 音楽フィルタ関数を格納します。 */
	music_filter = filter;
}
#ifndef PIECE
void music_filter_loudness(short wbuff[/*len*/], int len) {
	do {
		int x = *wbuff << 2; /* 調整可。でも、4倍以上にすると音が割れるようです */
		if(x < SHRT_MIN) { x = SHRT_MIN; }
		if(x > SHRT_MAX) { x = SHRT_MAX; }
		*wbuff++ = x;
	} while(--len);
}
#else /*PIECE*/
void music_filter_loudness(short wbuff[/*len*/], int len);
asm("
		.code
		.align		1
		.global		music_filter_loudness
music_filter_loudness:
		xld.w		%r14, 32767			;//%r14 := SHRT_MAX
		not		%r15, %r14			;//%r15 := SHRT_MIN
		;//%r12 := wbuff
		;//%r13 := len
		;//%r14 := SHRT_MAX
		;//%r15 := SHRT_MIN
music_filter_loudness_L10:					;//do {
		ld.h		%r9, [%r12]			;//  %r9    := x = *wbuff
		sla		%r9, 2				;//  %r9    := x <<= 2				!INTERLOCK!
		cmp		%r9, %r14			;//  %psr   := x <=> SHRT_MAX
		jrle.d		3				;//  if(       x   > SHRT_MAX) {
		 cmp		%r9, %r15			;//  %psr   := x <=> SHRT_MIN		─┐	*delay*
		 ld.w		%r9, %r14			;//    %r9  := x  =  SHRT_MAX  }	　│
		jrge.d		3				;//  if(       x <   SHRT_MIN) {	←┘
		 sub		%r13, 1				;//  %r13   := i--			─┐	*delay*
		 ld.w		%r9, %r15			;//    %r9  := x  =  SHRT_MIN  }	　│
		ld.h		[%r12]+, %r9			;//  *wbuff++ = x			　│
		jrne		music_filter_loudness_L10	;//} while(i)				←┘
		ret
");
#endif/*PIECE*/
static int   new_pceWaveDataOut (int ch, PCEWAVEINFO* pwi);
static int (*old_pceWaveDataOut)(int ch, PCEWAVEINFO* pwi) = new_pceWaveDataOut;
static int   new_pceWaveDataOut (int ch, PCEWAVEINFO* pwi) {
	/* P/ECE標準音楽ライブラリの使用チャネルへの出力ならば…
	 *  - 念のため、16bit PCM形式であることを確認します。
	 *    P/ECE標準音楽ライブラリの出力は、16bit PCM形式であるはずです。 */
	if((ch == music_wch) && (pwi->type & PW_TYPE_16BITPCM)) {
		/* 音楽フィルタ関数が登録されていれば、音楽フィルタ関数を呼び出します。 */
		if(music_filter) { (*music_filter)((short*)pwi->pData, pwi->len); }
	}
	return old_pceWaveDataOut(ch, pwi);
}
static void hook_unhook_music() {
	old_pceWaveDataOut = (PCEKSENT)pceVectorSetKs(KSNO_WaveDataOut, old_pceWaveDataOut);
}
void music_init() {
	/* 音楽ライブラリを初期化します。 */
	InitMusic();
	/* P/ECE標準音楽ライブラリの使用チャネルへの出力フックを設定します。 */
	hook_unhook_music(); /* 1回呼ぶとフックする */
}
void music_free() {
    //{{2014/09/23削除:不要でした。実行しても害は無いが、無駄処理なので削除することにしました。
    //	/* 確実に音楽演奏を停止します。
    //	 * サウンドの停止はライブラリ内のpceAppExit()で行っているので、ここでは必要ありません。 */
    //	StopMusic();
    //}}2014/09/23削除:不要でした。実行しても害は無いが、無駄処理なので削除することにしました。
	/* P/ECE標準音楽ライブラリの使用チャネルへの出力フックを解除します。 */
	hook_unhook_music(); /* 2回呼ぶとフック解除 */
}
//}}2014/09/23変更:music_play(),music_stop(),music_stat()を追加しました。これまで、個々のアプリケーションのapp.hでマクロ定義していましたが、今後は、ライブラリに含めることにしました。

//{{2014/09/23追加:music_play(),music_stop(),music_stat()を追加しました。これまで、個々のアプリケーションのapp.hでマクロ定義していましたが、今後は、ライブラリに含めることにしました。
void music_play(const void* seq) {
	pceWaveAbort(music_wch);	//必要。StopMusic()を呼び出した後，次回のミキシング割り込みが発生するまでの間は、まだストリームバッファが使用中である。もしここでpceWaveAbort(music_wch)を行わないと、使用中のストリームバッファをPlayMusic()が破壊して暴走する。
	PlayMusic((unsigned char*)seq);	//警告抑制のためconstを除去するキャストです。
}
void music_stop() {
	/* - このAPIは、停止要求フラグをセットするだけで、まだ実際には再生処理が停止していない。次回のミキシング割り込みが発生した時に、再生処理が自然に停止する。
	 *   なお、ここでpceWaveAbort(music_wch)を呼んでミキシングを強制的に停止すると、MusicCheck()が終了状態に移行しない問題があるので、それはしないことにした。
	 * - music_stop()を呼び出した直後，次回のミキシング割り込みが発生するまでの間に、music_stat()を呼び出しても、その間は再生中(1)と判断されることに注意せよ。 */
	StopMusic();
}
int music_stat() {
	/* * Tue Sep 23 18:23:03 JST 2014 Naoyuki Sawa
	 * - MusicCheck()は、戻り値がマニュアルと逆です。music_stat()は、MusicCheck()のマニュアルどおりの意味に変換して返すことにしました。
	 *   ■参照資料:
	 *   □P/ECE開発環境⇒P/ECE音楽ライブラリマニュアル⇒MusicCheck(/usr/PIECE/docs/muslib/MusicCheck.html)
	 *   │音楽演奏中かどうかを調べます。
	 *   │void MusicCheck( void );
	 *   │戻り値： ==0 停止中 <>0 演奏中	←←←←←←←←←←間違いです。
	 *   □「まどかの世界」(http://www2.plala.or.jp/madoka/)さんの、2001/12/23の記事(http://www2.plala.or.jp/madoka/nikki/nikki0009.html)
	 *   │あと、前に書いていた演奏状態を調べるMusicCheckというＡＰＩですが、どうやら返してくる値の意味がマニュアルと逆のようです。
	 *   │０を返すと演奏中で、０以外は停止中みたいです。これは内部でFinishというグローバル変数が２かどうかの答えを返しているだけなので、
	 *   │直そうと思えば直せますが、もうちょっとちゃんと現象を調べてから、アクアプラスにメールでも書いてみるかな。
	 * - InitMusic()の後、初回のPlayMusic()⇒StopMusic()呼び出しまでの間、演奏中と判断される。これもP/ECE音楽ライブラリのバグのようです。 */
	return !MusicCheck();//0⇔1
}
//}}2014/09/23追加:music_play(),music_stop(),music_stat()を追加しました。これまで、個々のアプリケーションのapp.hでマクロ定義していましたが、今後は、ライブラリに含めることにしました。

