/*	
 *	framxm.c
 *
 *	P/ECE XM Driver
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Tue Nov 9 19:00:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/* * エラーコードの値は、行番号を負値にしたものです。
 *   プログラムを修正すると、エラーコードも変わる可能\性があります。
 *   エラーコードの値は、デバッグのためだけに利用してください。
 */
#define ERRNO	(-__LINE__)

/****************************************************************************
 *	P/ECE XM Driver
 ****************************************************************************/

int
xm_run(XMDRIVER* driver)
{
	XMNOTE note;
	int flags;
	int i_pattern;
	int i_channel;
	int real_note;
	int period;
	int volume_envelope_progress;
	XMCHANNEL* channel;
	XMINSTRUMENT* instrument;
	XMSAMPLE* sample;
	XMENVELOPEPOINT* volume_envelope_position;

	if(driver->stat) goto L_EXIT;

	/* 1[Tick]未満の時間経過をカウントアップします。
	 * BPMの設定値が125の場合に、1[Tick]=1/50[Sec]となります。
	 */
	driver->progress += driver->bpm;
	while(driver->progress >= 125) {
		driver->progress -= 125;

		/* 1[Tick]経過しました。
		 * 経過Tick数がTempoの設定値に達したら、1[Row]進みます。
		 */
		driver->tick++;
		while(driver->tick >= driver->tempo) {
			driver->tick -= driver->tempo;

			/* Position jumpが要求されていたら... */
			if(driver->position_jump != -1) {

				/* Orderを設定します。 */
				driver->order = driver->position_jump;
				if((driver->order < 0) ||
				   (driver->order > driver->song_length - 1)) {
					driver->stat = ERRNO; /* Position jump不正 */
					goto L_EXIT;
				}
				driver->row = 0; /* 必要!! */

				/* Patternを取得します。 */
				i_pattern = driver->pattern_order_table[driver->order];
				if((i_pattern < 0) ||
				   (i_pattern > driver->number_of_patterns - 1)) {
					driver->stat = ERRNO; /* Pattern不正 */
					goto L_EXIT;
				}
				driver->pattern = &driver->patterns[i_pattern];

				/* Packed pattern dataの先頭アドレスを取得します。 */
				driver->packed_pattern_position = driver->pattern->packed_pattern_data;

				/* Position jumpを解除します。 */
				driver->position_jump = -1;
			}

			/* Pattern breakが要求されているか、またはPatternが終了したら... */
			if((driver->pattern_break != -1) ||
			   (driver->row >= driver->pattern->number_of_rows_in_pattern)) {

				/* 次のOrderの先頭のRowへ進めます。 */
				driver->order++;
				driver->row = 0;

				/* Song終了を検出し、ループ処理を行います。 */
				if(driver->order >= driver->song_length) {
					driver->order = driver->restart_position;

					/* ループ開始位置がSongの範囲外ならば、非ループ曲と見なします。 */
					if((driver->order < 0) ||
					   (driver->order > driver->song_length - 1)) {
						driver->stat = ERRNO; /* 非ループ曲 */
						goto L_EXIT;
					}
				}

				/* Patternを取得します。 */
				i_pattern = driver->pattern_order_table[driver->order];
				if((i_pattern < 0) ||
				   (i_pattern > driver->number_of_patterns - 1)) {
					driver->stat = ERRNO; /* Pattern不正 */
					goto L_EXIT;
				}
				driver->pattern = &driver->patterns[i_pattern];

				/* Packed pattern dataの先頭アドレスを取得します。 */
				driver->packed_pattern_position = driver->pattern->packed_pattern_data;

				/* Pattern breakが要求されていたら... */
				if(driver->pattern_break != -1) {

					/* Pattern breakによって指定されたRowまで進めます。 */
					if((driver->pattern_break < 0) ||
					 //(driver->pattern_break > driver->pattern->number_of_rows_in_pattern)) {
					 //↓2004/11/16 Naoyuki Sawa 修正
					   (driver->pattern_break > driver->pattern->number_of_rows_in_pattern - 1)) {
						driver->stat = ERRNO; /* Pattern break不正 */
						goto L_EXIT;
					}
					while(driver->row < driver->pattern_break) {
						for(i_channel = 0; i_channel < driver->number_of_channels; i_channel++) {
							flags = *driver->packed_pattern_position++;
							if(flags & 0x80) {
								if(flags & (1 << 0)) driver->packed_pattern_position++; /* Note */
								if(flags & (1 << 1)) driver->packed_pattern_position++; /* Instrument */
								if(flags & (1 << 2)) driver->packed_pattern_position++; /* Volume column byte */
								if(flags & (1 << 3)) driver->packed_pattern_position++; /* Effect type */
								if(flags & (1 << 4)) driver->packed_pattern_position++; /* Effect parameter */
							} else {
								driver->packed_pattern_position += 5 - 1/*Noteは取得済み*/;
							}
						}
						driver->row++;
					}

					/* Pattern breakを解除します。 */
					driver->pattern_break = -1;
				}
			}

			/* 次回のために、Rowを進めておきます。 */
			driver->row++;

			/* 各チャネルについて... */
			for(i_channel = 0, channel = driver->channels;
			    i_channel < driver->number_of_channels;
			    i_channel++, channel++) {

				/* まず、確実にVolume column byteとEffectを解除します。 */
				channel->volume_column_byte = 0; /* 解除 */
				channel->effect_type = 0; /* 解除 */
				/* Effect parameterは継続する可能\性があるので、クリアしてはダメ!! */

				/* Packed pattern dataから、1チャネル分のNoteを取得します。 */
				flags = *driver->packed_pattern_position++;
				if(flags & 0x80) {
					memset(&note, 0, sizeof note);
					if(flags & (1 << 0)) note.note               = *driver->packed_pattern_position++;
					if(flags & (1 << 1)) note.instrument         = *driver->packed_pattern_position++;
					if(flags & (1 << 2)) note.volume_column_byte = *driver->packed_pattern_position++;
					if(flags & (1 << 3)) note.effect_type        = *driver->packed_pattern_position++;
					if(flags & (1 << 4)) note.effect_parameter   = *driver->packed_pattern_position++;
				} else {
					note.note               = flags;
					note.instrument         = *driver->packed_pattern_position++;
					note.volume_column_byte = *driver->packed_pattern_position++;
					note.effect_type        = *driver->packed_pattern_position++;
					note.effect_parameter   = *driver->packed_pattern_position++;
				}

				/* Instrumentが有効ならば... */
				if(note.instrument) {

					/* 有効なInstrumentならば、格納します。 */
					if((note.instrument >= 1) &&
					   (note.instrument <= driver->number_of_instruments)) {
						channel->pending_instrument = &driver->instruments[note.instrument - 1/*忘れないで!!*/];
					}
				}

				/* Noteが有効ならば... */
				if(note.note) {

					if((note.note >= 1) && (note.note <= 96)) { /* C-0..B-7 */

						/* 少なくとも一つ以上のSampleを持つInstrumentでの発声要求のみ受け付けます。
						 * XMエディット時の人為ミスだと思いますが、稀にSampleのないInstrumentでの発声要求があります。
						 * SampleのないInstrumentでの発声要求は、無視することにしました。(独自判断)
						 */
						instrument = channel->pending_instrument;
						if(instrument->number_of_samples_in_instrument) {

							/* Noteに対応するSampleを取得します。
							 * Sample number for all notesは、Relative note number加算前のNoteに対応しています!!
							 */
							sample = &instrument->samples[
								  instrument->sample_number_for_all_notes[note.note - 1/*忘れないで!!*/]];

							/* Relative note numberを加算し、Real noteを求めます。 */
							real_note = (note.note - 1/*忘れないで!!*/) + sample->relative_note_number;
							if((real_note >= 0) && (real_note <= 118)) { /* (仕様) */

								/* Periodを求めます。 */
								period = real_note << 8;

								/* Tone portaと併用した場合は、目標Periodを設定し、現在の発声を継続します。
								 * * 発声中のInstrumentが複数のSampleを持ち、各Sampleが異なるRelative noteを持っていると、
								 *   NoteによってPeriodの基準が違うため、Tone portaが正しくかからないことがあります。
								 * * いちおう Effect type 0x05 : Tone porta+Volume slide も判定していますが、
								 *   2004/11/14現在のところ、Tone porta+Volume slideは未実装です。
								 *   (Tone porta+Volume slideに伴うEffect parameterはVolume slideに適用し、
								 *    Tone portaに対するEffect parameterは前回値を引き継ぐ、という、変則的な仕様のため。)
								 */
								if((note.volume_column_byte >> 4 == 0xf) ||	/* Tone porta */
								   (note.effect_type == 0x03) ||		/* Tone porta */
								   (note.effect_type == 0x05)) {		/* Tone porta+Volume slide */
								/*0-H*/	//そのまま
								/*I*/	channel->tone_porta = period;

									/* ただしInstrument設定も併用した場合は、Tone portaと共に発声開始処理も行います。 */
									if(note.instrument) {
/*┌─────────────────────────────────────*/goto TONE_PORTA_WITH_INSTRUMENT;
/*│*/									}
/*│*/
/*│*/								/* それ以外は、通常の発声開始処理です。 */
/*│*/								} else {
/*↓*/								/*8*/	channel->period = period;
TONE_PORTA_WITH_INSTRUMENT: /*----------------------------------------------------------------------------------------------------------------------------*/
								/*0*/	channel->stat = XMCHANNEL_NOTEON;
								/*1*/	//channel->pending_instrument = そのまま
								/*2*/	channel->playing_instrument = instrument;
								/*3*/	channel->playing_sample = sample;
								/*4*/	channel->sample_position = sample->sample_data;
								/*5*/	channel->sample_direction = 0;
								/*6*/	channel->sample_output = 0;
								/*7*/	channel->volume = channel->playing_sample->volume;
								/*8*/	//channel->period = 上で設定
								/*9*/	channel->finetune = sample->finetune;
								/*A*/	channel->progress = 0;
								/*B*/	channel->volume_envelope_position = instrument->points_for_volume_envelope;
								/*C*/	channel->volume_envelope_progress = 0;
								/*D*/	channel->envelope_volume = 64;
								/*E*/	channel->fadeout_volume = 1 << 16;
								/*F*/	//channel->volume_column_byte = 既に解除してあります
								/*G*/	//channel->effect_type = 既に解除してあります
								/*H*/	//channel->effect_parameter = そのまま
								/*I*/	//channel->tone_porta = 設定不要、または上のifブロックで設定済み
								}
							}
						}

					} else { /* NoteOff */

						/* 現在NoteOnならば、NoteOffへ移行します。 */
						if(channel->stat == XMCHANNEL_NOTEON) {
							channel->stat = XMCHANNEL_NOTEOFF;
						}
					}
				}

				/* Volume column byteが有効ならば、格納します。 */
				if(note.volume_column_byte) {
					channel->volume_column_byte = note.volume_column_byte;
				}

				/* Effect typeが有効ならば、格納します。 */
				if(note.effect_type) {
					channel->effect_type = note.effect_type;

					/* Effect parameterが0以外ならば、無条件で格納します。 */
					if(note.effect_parameter) {
						channel->effect_parameter = note.effect_parameter;

					/* Effect parameterが0ならば、前回値を継続するか、または0を格納します。
					 * 以下のswitch〜caseで、コメントアウトされて★い　る★Effect typeは、前回値を継続します。
					 *                       コメントアウトされて★いない★Effect typeは、0を格納します。
					 */
					} else {
						switch(note.effect_type) {
						case 0x00: /* Arpeggio */
						//case 0x01: /* Porta up */
						//case 0x02: /* Porta down */
						//case 0x03: /* Tone porta */
						//case 0x04: /* Vibrato */
						//case 0x05: /* Tone porta+Volume slide */
						//case 0x06: /* Vibrato+Volume slide */
						//case 0x07: /* Tremolo */
						case 0x08: /* Set panning */
						case 0x09: /* Sample offset */
						//case 0x0a: /* Volume slide */
						case 0x0b: /* Position jump */
						case 0x0c: /* Set volume */
						case 0x0d: /* Pattern break */
						case 0x0e: /* Extended effects */ /* ※下位4bitが0か否かによって処理すべきですが、2004/11/14現在未対応です */
						case 0x0f: /* Set tempo/BPM */
						case 0x10: /* Set global volume */
						//case 0x11: /* Global volume slide */
						case 0x12: /* Unused */
						case 0x13: /* Unused */
						case 0x14: /* Unused */
						case 0x15: /* Set envelope position */
						case 0x16: /* Unused */
						case 0x17: /* Unused */
						case 0x18: /* Unused */
						//case 0x19: /* Panning slide */
						case 0x1a: /* Unused */
						//case 0x1b: /* Multi retrig note */
						case 0x1c: /* Unused */
						case 0x1d: /* Tremor */
						case 0x1e: /* Unused */
						case 0x1f: /* Unused */
						case 0x20: /* Unused */
						//case 0x21: /* Extra fine porta up/down */
							channel->effect_parameter = note.effect_parameter;
							break;
						}
					}
				}
			}
		}

		/* 各チャネルについて... */
		for(i_channel = 0, channel = driver->channels;
		    i_channel < driver->number_of_channels;
		    i_channel++, channel++) {

			/* 要注意!! Volume column byteとEffectは、Silent/NoteOn/NoteOffにかかわらず実行します。 */

			/* Volume column byteが有効ならば、実行します。 */
			if(channel->volume_column_byte) {
				switch(channel->volume_column_byte >> 4) {
				case 0x1: /* Set volume value - $10 */
				case 0x2: /* Set volume value - $10 */
				case 0x3: /* Set volume value - $10 */
				case 0x4: /* Set volume value - $10 */
				case 0x5: /* Set volume value - $10 */
					/* 0x10..0x50 = Vol 0..64
					 * 0x51..0x5fは未定義ですが、Vol 65..79と解釈することにしました。(独自判断)
					 */
					channel->volume = channel->volume_column_byte - 0x10;
					channel->volume_column_byte = 0; /* 解除 */
					break;
				case 0x6: /* Volume slide down */
					channel->volume -= channel->volume_column_byte & 0xf;
					if(channel->volume < 0) {
						channel->volume = 0;
					}
					break;
				case 0x7: /* Volume slide up */
					channel->volume += channel->volume_column_byte & 0xf;
					if(channel->volume > 64) {
						channel->volume = 64;
					}
					break;
				case 0x8: /* Fine volume slide down */
					channel->volume -= channel->volume_column_byte & 0xf;
					if(channel->volume < 0) {
						channel->volume = 0;
					}
					channel->volume_column_byte = 0; /* 解除 */
					break;
				case 0x9: /* Fine volume slide up */
					channel->volume += channel->volume_column_byte & 0xf;
					if(channel->volume > 64) {
						channel->volume = 64;
					}
					channel->volume_column_byte = 0; /* 解除 */
					break;
				//case 0xa: /* Set vibrato speed */
				//case 0xb: /* Vibrato */
				//case 0xc: /* Set panning */
				//case 0xd: /* Panning slide left */
				//case 0xe: /* Panning slide right */
				case 0xf: /* Tone porta */
					if(channel->period < channel->tone_porta) {
						channel->period += (channel->volume_column_byte & 0xf) << ((2 + 4)/*仕様？*/ + 2/*桁合せ*/);
						if(channel->period >= channel->tone_porta) {
							channel->period = channel->tone_porta;
						}
					}
					if(channel->period > channel->tone_porta) {
						channel->period -= (channel->volume_column_byte & 0xf) << ((2 + 4)/*仕様？*/ + 2/*桁合せ*/);
						if(channel->period <= channel->tone_porta) {
							channel->period = channel->tone_porta;
						}
					}
					break;
				}
			}

			/* Effect typeが有効ならば、実行します。 */
			if(channel->effect_type) {
				switch(channel->effect_type) {
				//case 0x00: /* Arpeggio */
				case 0x01: /* Porta up */
					channel->period += channel->effect_parameter << (2/*仕様？*/ + 2/*桁合せ*/);
					if(channel->period > 118 << 8) {
						channel->period = 118 << 8;
					}
					break;
				case 0x02: /* Porta down */
					channel->period -= channel->effect_parameter << (2/*仕様？*/ + 2/*桁合せ*/);
					if(channel->period < 0) {
						channel->period = 0;
					}
					break;
				case 0x03: /* Tone porta */
					if(channel->period < channel->tone_porta) {
						channel->period += channel->effect_parameter << (2/*仕様？*/ + 2/*桁合せ*/);
						if(channel->period >= channel->tone_porta) {
							channel->period = channel->tone_porta;
						}
					}
					if(channel->period > channel->tone_porta) {
						channel->period -= channel->effect_parameter << (2/*仕様？*/ + 2/*桁合せ*/);
						if(channel->period <= channel->tone_porta) {
							channel->period = channel->tone_porta;
						}
					}
					break;
				//case 0x04: /* Vibrato */
				//case 0x05: /* Tone porta+Volume slide */
				//case 0x06: /* Vibrato+Volume slide */
				//case 0x07: /* Tremolo */
				//case 0x08: /* Set panning */
				//case 0x09: /* Sample offset */
				case 0x0a: /* Volume slide */
					channel->volume += channel->effect_parameter >> 4;
					if(channel->volume > 64) {
						channel->volume = 64;
					}
					channel->volume -= channel->effect_parameter & 0xf;
					if(channel->volume < 0) {
						channel->volume = 0;
					}
					break;
				case 0x0b: /* Position jump */
					driver->position_jump = channel->effect_parameter;
					channel->effect_type = 0; /* 解除 */
					break;
				case 0x0c: /* Set volume */
					channel->volume = channel->effect_parameter;
					channel->effect_type = 0; /* 解除 */
					break;
				case 0x0d: /* Pattern break */
					driver->pattern_break = channel->effect_parameter;
					channel->effect_type = 0; /* 解除 */
					break;
				case 0x0e: /* Extended effects */
					switch(channel->effect_parameter >> 4) {
					case 0x1: /* Fine porta up */
						channel->period += (channel->effect_parameter & 0xf) << (2/*仕様？*/ + 2/*桁合せ*/);
						if(channel->period > 118 << 8) {
							channel->period = 118 << 8;
						}
						break;
					case 0x2: /* Fine porta down */
						channel->period -= (channel->effect_parameter & 0xf) << (2/*仕様？*/ + 2/*桁合せ*/);
						if(channel->period < 0) {
							channel->period = 0;
						}
						break;
					//case 0x3: /* Set gliss control */
					//case 0x4: /* Set vibrato control */
					//case 0x5: /* Set finetune */ /* MOD形式でのみ機能\します。XM形式では使いません。 */
					//case 0x6: /* Set loop begin/loop */
					//case 0x7: /* Set tremolo control */
					//case 0x9: /* Retrig note */
					case 0xa: /* Fine volume slide up */
						channel->volume += channel->effect_parameter & 0xf;
						if(channel->volume > 64) {
							channel->volume = 64;
						}
						break;
					case 0xb: /* Fine volume slide down */
						channel->volume -= channel->effect_parameter & 0xf;
						if(channel->volume < 0) {
							channel->volume = 0;
						}
						break;
					//case 0xc: /* Note cut */
					//case 0xd: /* Note delay */
					//case 0xe: /* Pattern delay */
					}
					break;
				case 0x0f: /* Set tempo/BPM */
					if(!channel->effect_parameter) {
						driver->stat = ERRNO;
						goto L_EXIT;
					}
					if(channel->effect_parameter < 0x20) {
						driver->tempo = channel->effect_parameter;
					} else {
						driver->bpm = channel->effect_parameter;
					}
					channel->effect_type = 0; /* 解除 */
					break;
				case 0x10: /* Set global volume */
					driver->global_volume = channel->effect_parameter;
					channel->effect_type = 0; /* 解除 */
					break;
				case 0x11: /* Global volume slide */
					driver->global_volume += channel->effect_parameter >> 4;
					if(driver->global_volume > 64) {
						driver->global_volume = 64;
					}
					driver->global_volume -= channel->effect_parameter & 0xf;
					if(driver->global_volume < 0) {
						driver->global_volume = 0;
					}
					break;
				//case 0x12: /* Unused */
				//case 0x13: /* Unused */
				//case 0x14: /* Unused */
				//case 0x15: /* Set envelope position */
				//case 0x16: /* Unused */
				//case 0x17: /* Unused */
				//case 0x18: /* Unused */
				//case 0x19: /* Panning slide */
				//case 0x1a: /* Unused */
				//case 0x1b: /* Multi retrig note */
				//case 0x1c: /* Unused */
				//case 0x1d: /* Tremor */
				//case 0x1e: /* Unused */
				//case 0x1f: /* Unused */
				//case 0x20: /* Unused */
				//case 0x21: /* Extra fine porta up/down */
				}
			}

			/* Silentでなければ... */
			if(channel->stat) {
				instrument = channel->playing_instrument;

				/* Volume envelopeが有効ならば、実行します。 */
				volume_envelope_position = channel->volume_envelope_position; /* 取り出し */
				if(volume_envelope_position) {
					volume_envelope_progress = channel->volume_envelope_progress; /* 取り出し */

					/* NoteOnで、Sustain pointに達していたら、Envelope volumeを維持します。 */
					if((channel->stat == XMCHANNEL_NOTEON) &&
					   (volume_envelope_position == instrument->volume_sustain_point)) {
						channel->envelope_volume = volume_envelope_position->y;

					/* 以下は、NoteOn/NoteOff共通です。 */
					} else {
ENVELOPE_LOOP:
						/* 最後のPointに到達するか、または現在のProgressよりも大きなxを持つPointの直前まで進めます。
						 * これにより、同じxを持つPointの並びをスキップするので、以降の処理でゼロ除算の心配はありません。
						 */
						while((volume_envelope_position < instrument->volume_loop_end_point) &&
						      (volume_envelope_progress >= (volume_envelope_position + 1)->x)) {
							volume_envelope_position++;
						}

						/* 最後のPointに到達した場合、ループ開始位置と最後のPointのxが同じでなければ、ループします。
						 * xが同じならば、ループしてはいけません。(ここで永久ループになってしまいます。)
						 */
						if((volume_envelope_position == instrument->volume_loop_end_point) &&
						   (instrument->volume_loop_start_point->x < instrument->volume_loop_end_point->x)) {
							volume_envelope_position = instrument->volume_loop_start_point;
							volume_envelope_progress = volume_envelope_position->x;
							goto ENVELOPE_LOOP;
						}

						/* 現在のPointと次のPointの間でyを補完する場合はこちら。 */
						if(volume_envelope_position < instrument->volume_loop_end_point) {
							channel->envelope_volume = ((volume_envelope_position + 1)->y - volume_envelope_position->y)
										 * ( volume_envelope_progress         - volume_envelope_position->x)
										 / ((volume_envelope_position + 1)->x - volume_envelope_position->x)
										 +                                      volume_envelope_position->y;
							volume_envelope_progress++;

						/* 最後のPointに到達していて、Volume loop start point == Volume loop end point の場合はこちら。 */
						} else {
							channel->envelope_volume = volume_envelope_position->y;
							/* Volume envelope progressはそのまま */
						}
					}

					channel->volume_envelope_position = volume_envelope_position; /* 書き戻し */
					channel->volume_envelope_progress = volume_envelope_progress; /* 書き戻し */
				}

				/* NoteOffならば、Volume fadeoutを実行します。 */
				if(channel->stat == XMCHANNEL_NOTEOFF) {
					channel->fadeout_volume -= instrument->volume_fadeout;
					if(channel->fadeout_volume <= 0) {
						channel->stat = XMCHANNEL_SILENT;
					}
				}
			}
		}
	}

L_EXIT:
	return driver->stat;
}

int
xm_mix(XMDRIVER* driver, short wbuff[/*XMBUFLEN*/])
{
	static int mixbuf[XMBUFLEN]; /* ミキシングバッファ、ＳＴＡＴＩＣです！ */
	//
	int i;
	int i_channel;
	int frequency;
	int sample_output;
	int sample_direction;
	int progress;
	int final_volume;
	int period;
	const unsigned char* sample_position;
	const unsigned char* sample_end;
	const unsigned char* sample_loop_start;
	const unsigned char* sample_loop_end;
	XMCHANNEL* channel;
	XMSAMPLE* sample;
	int* p;

	if(driver->stat) goto L_EXIT;

	/* まず、ミキシングバッファをクリアします。 */
	memset(mixbuf, 0, sizeof mixbuf);

	/* 発声中の各チャネルについて... */
	for(i_channel = 0, channel = driver->channels;
	    i_channel < driver->number_of_channels;
	    i_channel++, channel++) {

		/* Silentならば飛ばします。 */
		if(!channel->stat) continue;

		/* 高速化のため、よく使うフィールドを取り出しておきます。
		 * これらのフィールドは、あとで書き戻す必要があります。
		 */
		sample_position = channel->sample_position;
		sample_output = channel->sample_output;
		progress = channel->progress;

		/* ボリュームを求めます。 */
		final_volume = (driver->global_volume         *	/*                       0〜 64 ( 6bit) */
				channel->volume               *	/*                       0〜 64 ( 6bit) */
				channel->envelope_volume      *	/*                       0〜 64 ( 6bit) */
				(channel->fadeout_volume >>  8)	/*   0〜65536 (16bit) => 0〜256 ( 8bit) */
			       )                         >> 16;	/*            (26bit) =>        (10bit) */
		if(!final_volume) continue; /* 無音 */

		/* 再生周波数を求めます。 */
		period = channel->period + (channel->finetune << 1);
		frequency = xm_linear_frequency_table[period % (12 * 256)]
						  << (period / (12 * 256));

		sample = channel->playing_sample;
		switch(sample->type & 0x13) {

		case 0x00: /* No loop, 8bit */
			sample_end = sample->sample_end;
			for(i = 0, p = mixbuf; i < XMBUFLEN; i++, p++) {
				progress += frequency;
				while(progress >= SPEAKER_FREQUENCY) {
					progress -= SPEAKER_FREQUENCY;
					sample_output += LEBYTE(sample_position) << 8; /* D31:16は不定 */
					sample_position += 1;
					if(sample_position >= sample_end) {
						channel->stat = XMCHANNEL_SILENT;
						goto L_NEXT;
					}
				}
				*p += (short)sample_output * final_volume;
			}
			break;

		case 0x10: /* No loop, 16bit */
			sample_end = sample->sample_end;
			for(i = 0, p = mixbuf; i < XMBUFLEN; i++, p++) {
				progress += frequency;
				while(progress >= SPEAKER_FREQUENCY) {
					progress -= SPEAKER_FREQUENCY;
					sample_output += LEHALF(sample_position); /* D31:16は不定 */
					sample_position += 2;
					if(sample_position >= sample_end) {
						channel->stat = XMCHANNEL_SILENT;
						goto L_NEXT;
					}
				}
				*p += (short)sample_output * final_volume;
			}
			break;

		case 0x01: /* Forward loop, 8bit */
			sample_loop_end = sample->sample_loop_end;
			for(i = 0, p = mixbuf; i < XMBUFLEN; i++, p++) {
				progress += frequency;
				while(progress >= SPEAKER_FREQUENCY) {
					progress -= SPEAKER_FREQUENCY;
					sample_output += LEBYTE(sample_position) << 8; /* D31:16は不定 */
					sample_position += 1;
					if(sample_position >= sample_loop_end) {
						sample_position = sample->sample_loop_start;
						sample_output = sample->sample_loop_output;
					}
				}
				*p += (short)sample_output * final_volume;
			}
			break;

		case 0x11: /* Forward loop, 16bit */
			sample_loop_end = sample->sample_loop_end;
			for(i = 0, p = mixbuf; i < XMBUFLEN; i++, p++) {
				progress += frequency;
				while(progress >= SPEAKER_FREQUENCY) {
					progress -= SPEAKER_FREQUENCY;
					sample_output += LEHALF(sample_position); /* D31:16は不定 */
					sample_position += 2;
					if(sample_position >= sample_loop_end) {
						sample_position = sample->sample_loop_start;
						sample_output = sample->sample_loop_output;
					}
				}
				*p += (short)sample_output * final_volume;
			}
			break;

		case 0x02: /* Ping-pong loop, 8bit */
			sample_direction = channel->sample_direction; /* 取り出し */
			sample_loop_start = sample->sample_loop_start;
			sample_loop_end = sample->sample_loop_end;
			for(i = 0, p = mixbuf; i < XMBUFLEN; i++, p++) {
				progress += frequency;
				while(progress >= SPEAKER_FREQUENCY) {
					progress -= SPEAKER_FREQUENCY;
					if(!sample_direction) {
						sample_output += LEBYTE(sample_position) << 8; /* D31:16は不定 */
						sample_position += 1;
						if(sample_position >= sample_loop_end) {
							sample_direction = 1;
						}
					} else {
						sample_position -= 1;
						sample_output -= LEBYTE(sample_position) << 8; /* D31:16は不定 */
						if(sample_position <= sample_loop_start) {
							sample_direction = 0;
						}
					}
				}
				*p += (short)sample_output * final_volume;
			}
			channel->sample_direction = sample_direction; /* 書き戻し */
			break;

		case 0x12: /* Ping-pong loop, 16bit */
			sample_direction = channel->sample_direction; /* 取り出し */
			sample_loop_start = sample->sample_loop_start;
			sample_loop_end = sample->sample_loop_end;
			for(i = 0, p = mixbuf; i < XMBUFLEN; i++, p++) {
				progress += frequency;
				while(progress >= SPEAKER_FREQUENCY) {
					progress -= SPEAKER_FREQUENCY;
					if(!sample_direction) {
						sample_output += LEHALF(sample_position); /* D31:16は不定 */
						sample_position += 2;
						if(sample_position >= sample_loop_end) {
							sample_direction = 1;
						}
					} else {
						sample_position -= 2;
						sample_output -= LEHALF(sample_position); /* D31:16は不定 */
						if(sample_position <= sample_loop_start) {
							sample_direction = 0;
						}
					}
				}
				*p += (short)sample_output * final_volume;
			}
			channel->sample_direction = sample_direction; /* 書き戻し */
			break;
		}
L_NEXT:
		/* 取り出したフィールドを書き戻します。 */
		channel->sample_position = sample_position;
		channel->sample_output = sample_output;
		channel->progress = progress;
	}

	/* ミキシングバッファをクリッピングして、出力バッファへ書き込みます。 */
	for(i = 0, p = mixbuf; i < XMBUFLEN; i++, p++, wbuff++) {
		/*   final_volume      (10bit)
		 * x sample_output     (16bit)
		 * x max_channels(=32) ( 5bit)
		 * ---------------------------
		 *                     (31bit)
		 */
		sample_output = *p >> ((31 - 16) - 4/*調整分*/);
		if(sample_output < -32767) sample_output = -32767;
		if(sample_output >  32767) sample_output =  32767;
		*wbuff = sample_output;
	}

L_EXIT:
	return driver->stat;
}

