#include <windows.h>
#include <mmreg.h>
#include <msacm.h>
#include <wchar.h>
#include "kbbms_decoder.h"
/*
  [x86弱依存 メモリアクセスオーダー・アライ(ン)メント]
    BMSファイル読み込み、波形合成部です。
    16bitサンプルをメモリから読みとる部分のみx86に依存します。
    修正は容易なので放置します。
*/
#if defined(_MSC_VER)
#define inline __inline
#else
#define inline
#endif

#include "xstdlib.h"
//#include "kmp_com.h"
#include "wav_load.h"

/* 変更可能な値 */
#define SRATE 44100		/* 再生サンプルレート */
#define BMS_LINEBUFFER 4096	/* テキスト行の最大文字数 */

/* 変更は推奨できない値 */
#define BMS_FIX_BITS 14			/* 固定小数点数の精度 演奏精度に影響 */
#define BMS_DIVISION (192 * 4)		/* 小節の分解能 演奏精度に影響 */
#define BMS_MEASURE_LIMIT (1024)	/* 最大小節数 1000以上 */
#define BMS_LINKBUFFER_BLOCK (4096)	/* 気にする必要なし 中間バッファブロックのサイズ */
#define BMS_MAX_NOTE (36 * 16 + 36 * 20)
#define BMS_MAX_EXBPM (256)

enum BMS_WAV_TYPE {
	WAV_1CH_8BIT = 0x00,
	WAV_2CH_8BIT = 0x01,
	WAV_1CH_16BIT = 0x02,
	WAV_2CH_16BIT = 0x03,
	WAV_UNKNOWN
};
typedef struct BMS_NOTE_TAG BMS_NOTE;
struct BMS_NOTE_TAG {
	BMS_WAV *wavp;
	unsigned flag;
	signed posl;
	unsigned posh;
	BMS_NOTE *prev;
	BMS_NOTE *next;
};
typedef struct BMS_ITEM_TAG BMS_ITEM;
struct BMS_ITEM_TAG {
	unsigned time;
	unsigned short data;
	unsigned char cmd;
	unsigned char d3;
	BMS_ITEM *next;
};
enum BMS_CMD_TYPE {
	BMS_CMD_TEMPO,
	BMS_CMD_RATIO,
	BMS_CMD_NOTE,
	BMS_CMD_WAIT,
	BMS_CMD_EXBPM,
	BMS_CMD_END
};
typedef struct BMS_LINKBUFFER_TAG BMS_LINKBUFFER;
struct BMS_LINKBUFFER_TAG {
	unsigned char buf[BMS_LINKBUFFER_BLOCK];
	BMS_LINKBUFFER *next;
};
typedef struct {
	XFILE fp;
	unsigned lpos;
	unsigned rpos;
	char lineb[BMS_LINEBUFFER + 1];
} TEXT_FILE;
typedef struct {
	unsigned timeratio;	/* ch02 */
	BMS_ITEM gurdian;
} BMS_MEASURE;
typedef struct {
	unsigned bpm;
	unsigned linkn;
	BMS_LINKBUFFER *linktop;
	BMS_LINKBUFFER *linkb;
	unsigned measuren;
	BMS_MEASURE measure[BMS_MEASURE_LIMIT];
	wchar_t midipath[MAX_PATH];
	TEXT_FILE text;
} BMS_BMSLOADER;

typedef struct {
	int loaded;
	wchar_t wavpath[BMS_MAX_NOTE][MAX_PATH];
	wchar_t midipath[MAX_PATH];//追加
} BMS_WAVLOADER;

struct KMP_CTX {
	BMS_BMSLOADER *bmsloader;
	BMS_WAVLOADER *wavloader;
	unsigned char *data;

	unsigned srate;
	const unsigned char *cur_data;
	int first_flag;
	int end_flag;
	unsigned cur_bpm;
	unsigned cur_ratio;
	unsigned cur_spc;
	unsigned cnt_samplel;
	unsigned cnt_sampleh;
	unsigned out_smp;
	signed volwav;
	unsigned exbpm[BMS_MAX_EXBPM];
	BMS_NOTE keyonnote;	/* gurdian */
	BMS_NOTE note[BMS_MAX_NOTE];
    BMS_NOTE midinote;  //MIDI 用
	BMS_WAV  wav[BMS_MAX_NOTE];
    BMS_WAV  midwav;  //MIDI 用
    IKpiFile   *bmsfile;
    IKpiFolder *bmsfolder;
};

static void LinkBufferFree(KMP_CTX *ctx)
{
	while (ctx->bmsloader->linktop)
	{
		BMS_LINKBUFFER *item = ctx->bmsloader->linktop;
		ctx->bmsloader->linktop = item->next;
		free(item);
	}
}

static int LinkBufferPut(KMP_CTX *ctx, int c)
{
	if (!(ctx->bmsloader->linkn & (BMS_LINKBUFFER_BLOCK - 1)))
	{
		BMS_LINKBUFFER *cur;
		cur = (BMS_LINKBUFFER*)malloc(sizeof(BMS_LINKBUFFER));
		if (!cur) return 0;
		cur->next = 0;
		if (!ctx->bmsloader->linktop)
		{
			ctx->bmsloader->linktop = ctx->bmsloader->linkb = cur;
		}
		else
		{
			ctx->bmsloader->linkb->next = cur;
			ctx->bmsloader->linkb = cur;
		}
	}
	ctx->bmsloader->linkb->buf[ctx->bmsloader->linkn++ & (BMS_LINKBUFFER_BLOCK - 1)] = c;
	return 1;
}
static int LinkBufferPutVariable(KMP_CTX *ctx, unsigned c)
{
	int er = 0;
	if (c >= (1 << (7 * 4))) er |= !LinkBufferPut(ctx, 0x80 + ((c >> (7 * 4)) & 0x7f));
	if (c >= (1 << (7 * 3))) er |= !LinkBufferPut(ctx, 0x80 + ((c >> (7 * 3)) & 0x7f));
	if (c >= (1 << (7 * 2))) er |= !LinkBufferPut(ctx, 0x80 + ((c >> (7 * 2)) & 0x7f));
	if (c >= (1 << (7 * 1))) er |= !LinkBufferPut(ctx, 0x80 + ((c >> (7 * 1)) & 0x7f));
	er |= !LinkBufferPut(ctx, c & 0x7f);
	return !er;
}

static void LinkBufferInit(KMP_CTX *ctx)
{
	ctx->bmsloader->linktop = 0;
	ctx->bmsloader->linkb = 0;
	ctx->bmsloader->linkn = 0;
}

static void MeasureFree(KMP_CTX *ctx)
{
	int i;
	for (i = 0; i < BMS_MEASURE_LIMIT; i++)
	{
		while (ctx->bmsloader->measure[i].gurdian.next)
		{
			BMS_ITEM *item = ctx->bmsloader->measure[i].gurdian.next;
			ctx->bmsloader->measure[i].gurdian.next = item->next;
			free (item);
		}
	}
}

static void MeasureInit(KMP_CTX *ctx)
{
	int i;
	ctx->bmsloader->measuren = 0;
	for (i = 0; i < BMS_MEASURE_LIMIT; i++)
	{
		ctx->bmsloader->measure[i].gurdian.time = 0;
		ctx->bmsloader->measure[i].gurdian.cmd = BMS_CMD_END;
		ctx->bmsloader->measure[i].gurdian.data = 0;
		ctx->bmsloader->measure[i].gurdian.next = 0;
		ctx->bmsloader->measure[i].timeratio = 1 << BMS_FIX_BITS;
	}
}

static void MeasurePut(KMP_CTX *ctx, unsigned me, unsigned time, unsigned cmd, unsigned data)
{
	BMS_ITEM *cur, *prev;
	prev = &ctx->bmsloader->measure[me].gurdian;
	cur = (BMS_ITEM*)malloc(sizeof(BMS_ITEM));
	if (!cur) return;
	while (prev->next && prev->next->time <= time) prev = prev->next;
	cur->next = prev->next;
	prev->next = cur;
	cur->time = time;
	cur->cmd = cmd;
	cur->data = data;
}

static void TextFree(KMP_CTX *ctx)
{
	if (ctx->bmsloader->text.fp)
	{
		XCLOSE(ctx->bmsloader->text.fp);
		ctx->bmsloader->text.fp = 0;
	}
}
static void TextInit(KMP_CTX *ctx)
{
	ctx->bmsloader->text.fp = 0;
	ctx->bmsloader->text.lpos = ctx->bmsloader->text.rpos = BMS_LINEBUFFER;
	ctx->bmsloader->text.lineb[BMS_LINEBUFFER] = 0;
}

static int TextGets(KMP_CTX *ctx)
{
	unsigned l;
	l = ctx->bmsloader->text.rpos - ctx->bmsloader->text.lpos;
	if (l)
	{
		XMEMCPY(ctx->bmsloader->text.lineb, ctx->bmsloader->text.lineb + ctx->bmsloader->text.lpos, l);
	}
	ctx->bmsloader->text.rpos -= ctx->bmsloader->text.lpos;
	l = BMS_LINEBUFFER - ctx->bmsloader->text.rpos;
	if (l)
	{
		ctx->bmsloader->text.rpos += XREAD(ctx->bmsloader->text.fp, ctx->bmsloader->text.lineb + ctx->bmsloader->text.rpos, l);
	}
	for (l = 0; l < ctx->bmsloader->text.rpos; l++)
	{
		switch(ctx->bmsloader->text.lineb[l])
		{
		case 0x00: case 0x0a: case 0x0d: case 0x1a:
			break;
		default:
			continue;
		}
		break;
	}
	ctx->bmsloader->text.lineb[l] = 0;
	for (; l < ctx->bmsloader->text.rpos; l++)
	{
		switch(ctx->bmsloader->text.lineb[l])
		{
		case 0x00: case 0x0a: case 0x0d: case 0x1a:
			continue;
		}
		break;
	}
	ctx->bmsloader->text.lpos = l;
	return l > 0;
}
static int TextOpen(KMP_CTX *ctx)
{
	ctx->bmsloader->text.fp = ctx->bmsfile;//XOPENRB(ctx->bmspath);
    if (!ctx->bmsloader->text.fp) return 0;
	ctx->bmsloader->text.lpos = ctx->bmsloader->text.rpos = BMS_LINEBUFFER;
    ctx->bmsfile->AddRef();
	return 1;
}

static void BMSLoaderFree(KMP_CTX *ctx)
{
	if (!ctx->bmsloader) return;
	TextFree(ctx);
	MeasureFree(ctx);
	LinkBufferFree(ctx);
	free(ctx->bmsloader);
	ctx->bmsloader = 0;
}

static void skipspace(char **p)
{
	while (**p == ' ' || **p == '\t')
		(*p)++;
}

static int getexcdc(char *p, unsigned *ret)
{
	unsigned char c;
	c = *p;
	*ret = 0;
	if (c >= '0' && c <= '9')
		c = c - '0';
	else if (c >= 'a' && c <= 'z')
		c = c - 'a' + 10;
	else if (c >= 'A' && c <= 'Z')
		c = c - 'A' + 10;
	else
		return 0;
	*ret = c;
	return 1;
}

static int getexcdn(char **p, unsigned *ret, unsigned length)
{
	unsigned h, l;
	*ret = 0;
	if (!getexcdc(*p + 0, &h))
		return 0;
	if (!getexcdc(*p + 1, &l))
	{
		*p += 1;
		if (length == 2)
			return 0;
		l = h;
		h = 0;
	}
	else
		*p += 2;
	if (l < 16)
		*ret = (h << 4) + l;
	else
		*ret = (h << 4) + (h << 2) + l - 16 + 36 * 16 ;
    return 1;
}

inline static int getexcd2(char **p, unsigned *ret)
{
	return getexcdn(p, ret, 2);
}

inline static int getexcd(char **p, unsigned *ret)
{
	return getexcdn(p, ret, 0);
}

static int getnumex(char **p, unsigned *ret, unsigned radix, unsigned length)
{
	unsigned l;
	*ret = 0;
	for (l = 0; !length || l < length; l++)
	{
		unsigned char c;
		c = (*p)[l];
		switch (c)
		{
		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
			c = c - 'a' + 'A';
		case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
			c = c - 'A' + '0' + 10;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			c = c - '0';
			if (c >= radix) break;
			*ret = *ret * radix + c;
			continue;
		}
		break;
	}
	if (!l) return 0;
	*p += l;
	if (length && l != length) return 0;
	return 1;
}

inline static int getnum(char **p, unsigned *ret)
{
	return getnumex(p, ret, 10, 0);
}
inline static int getnumn(char **p, unsigned *ret, unsigned length)
{
	return getnumex(p, ret, 10, length);
}
inline static int gethex(char **p, unsigned *ret)
{
	return getnumex(p, ret, 16, 0);
}
inline static int gethexn(char **p, unsigned *ret, unsigned length)
{
	return getnumex(p, ret, 16, length);
}
inline static int getfixsub(char **p, unsigned *ret, unsigned *n1)
{
	unsigned l;
	*ret = 0;
	*n1 = 1;
	for (l = 0; 1; l++)
	{
		unsigned char c;
		c = (*p)[l];
		switch (c)
		{
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			c = c - '0';
			*ret = *ret * 10 + c;
			*n1 *= 10;
			if (*n1 > (1 << (BMS_FIX_BITS + 4))) break;
			continue;
		}
		break;
	}
	if (!l) return 0;
	*p += l;
	return 1;
}
static int getfix(char **p, unsigned *ret)
{
	int b;
	unsigned i, f, n1;
	b = getnum(p, &i);
	if (**p == '.')
	{
		(*p)++;
		b = 1;
		getfixsub(p, &f, &n1);
	}
	if (!b) return 0;
	*ret = (i << BMS_FIX_BITS) + XMULDIV(f, (1 << BMS_FIX_BITS), n1);
	return 1;
}

static int BMSLoaderLoadText(KMP_CTX *ctx)
{
	unsigned i, me, dd;
	if (!TextOpen(ctx)) return 0;
	while (TextGets(ctx))
	{
		unsigned ch, cmd, dl;
		char *dp, *p = ctx->bmsloader->text.lineb;
		if (*p++ != '#') continue;
		if (!XMEMCMPI(p, "MIDIFILE", 8))
		{
			p += 8;
			skipspace(&p);
            MultiByteToWideChar(CP_ACP, 0, p, -1, ctx->bmsloader->midipath, _countof(ctx->bmsloader->midipath));
            ctx->bmsloader->midipath[_countof(ctx->bmsloader->midipath)-1] = 0;
            wcsncpy_s(ctx->wavloader->midipath, ctx->bmsloader->midipath, _countof(ctx->wavloader->midipath));
			//XSTRCPYN(ctx->bmsloader->midipath, p, BMS_MAX_PATH);
			continue;
		}
		if (!XMEMCMPI(p, "BPM", 3))
		{
			p += 3;
			if (!getexcd(&p, &dd))
			{
				skipspace(&p);
				if (getnum(&p, &dd) && dd) ctx->bmsloader->bpm = dd;
			}
			else if (dd >= 0 && dd < BMS_MAX_EXBPM)
			{
				skipspace(&p);
				if (getfix(&p, &dl) && dl)
					ctx->exbpm[dd] = XMULDIV(dl, (BMS_DIVISION / 4), (1 << BMS_FIX_BITS));
			}
			continue;
		}
		if (!XMEMCMPI(p, "WAV", 3))
		{
			p += 3;
			if (getexcd(&p, &dd) && 0 < dd && dd < BMS_MAX_NOTE) 
			{
				skipspace(&p);
                MultiByteToWideChar(CP_ACP, 0, p, -1, ctx->wavloader->wavpath[dd], _countof(ctx->wavloader->wavpath[dd]));
                ctx->wavloader->wavpath[dd][_countof(ctx->wavloader->wavpath[dd])-1] = 0;
                //XSTRCPYN(ctx->wavloader->wavpath[dd], p, BMS_MAX_PATH);
			}
			continue;
		}
		if (!XMEMCMPI(p, "VOLWAV", 6))
		{
			p += 6;
			skipspace(&p);
			if (getnum(&p, &dd) && dd) ctx->volwav = (dd < 256) ? dd : 256;
			continue;
		}
		if (!getnumn(&p, &me, 3)) continue;
		if (!gethexn(&p, &ch, 2)) continue;
		if (*p++ != ':') continue;
		if (BMS_MEASURE_LIMIT <= me) continue;
		if (ctx->bmsloader->measuren < me) ctx->bmsloader->measuren = me;
		switch (ch)
		{
			default:
				continue;
			case 0x02:	/* tempo ratio */
				if (getfix(&p, &dd) && dd)
					ctx->bmsloader->measure[me].timeratio = dd;
				continue;
			case 0x03:	/* tempo */
				cmd = BMS_CMD_TEMPO;
				break;
			case 0x08:	/* exbpm */
				cmd = BMS_CMD_EXBPM;
				break;
			case 0x20: case 0x30:	/* EMS */
			case 0x01:	/* back wave */
			case 0x18: case 0x19: case 0x16: case 0x17: /* 1p visble */
			case 0x11: case 0x12: case 0x13: case 0x14: case 0x15:
			case 0x38: case 0x39: case 0x36: case 0x37: /* 1p invisble */
			case 0x31: case 0x32: case 0x33: case 0x34: case 0x35:
			case 0x28: case 0x29: case 0x26: case 0x27: /* 2p visble */
			case 0x21: case 0x22: case 0x23: case 0x24: case 0x25:
			case 0x48: case 0x49: case 0x46: case 0x47: /* 2p invisble */
			case 0x41: case 0x42: case 0x43: case 0x44: case 0x45:
			case 0x58: case 0x59: case 0x56: case 0x57: /* 1p long */
			case 0x51: case 0x52: case 0x53: case 0x54: case 0x55:
			case 0x68: case 0x69: case 0x66: case 0x67: /* 2p long */
			case 0x61: case 0x62: case 0x63: case 0x64: case 0x65:
				cmd = BMS_CMD_NOTE;
				break;
		}
		dl = 0;
		dp = p;
		while (gethexn(&dp, &dd, 2)) dl++;
		for (i = 0; i < dl; i++)
		{
			if (!getexcd2(&p, &dd) || !dd) continue;
			MeasurePut(ctx, me, XMULDIV(i, BMS_DIVISION, dl), cmd, dd);
		}
	}
	TextFree(ctx);
	return 1;
}

static int BMSLoaderBuildData(KMP_CTX *ctx)
{
	unsigned er, me, ti, tr, ra;
	unsigned firstgap = !ctx->bmsloader->midipath[0];
	BMS_LINKBUFFER *lk;
	unsigned char *p;
	er  = !LinkBufferPut(ctx, BMS_CMD_TEMPO);
	er |= !LinkBufferPutVariable(ctx, ctx->bmsloader->bpm);
	ra = 1 << BMS_FIX_BITS;
	for (ti = tr = me = 0; me <= ctx->bmsloader->measuren; me++)
	{
		BMS_ITEM *item;
		if (ra != ctx->bmsloader->measure[me].timeratio)
		{
			ra = ctx->bmsloader->measure[me].timeratio;
			if (tr)
			{
				if (!firstgap)
				{
					er |= !LinkBufferPut(ctx, BMS_CMD_WAIT);
					er |= !LinkBufferPutVariable(ctx, tr);
				}
				tr = 0;
			}
			er |= !LinkBufferPut(ctx, BMS_CMD_RATIO);
			er |= !LinkBufferPutVariable(ctx, ra);
		}
		for (item = ctx->bmsloader->measure[me].gurdian.next; item; item = item->next)
		{
			if (tr || ti != item->time)
			{
				if (!firstgap)
				{
					er |= !LinkBufferPut(ctx, BMS_CMD_WAIT);
					er |= !LinkBufferPutVariable(ctx, tr + item->time - ti);
				}
				tr = 0;
				ti = item->time;
			}
			if (item->cmd == BMS_CMD_NOTE) firstgap = 0;
			er |= !LinkBufferPut(ctx, item->cmd);
			er |= !LinkBufferPutVariable(ctx, item->data);
		}
		tr += BMS_DIVISION - ti;
		ti = 0;
	}
	if (!firstgap)
	{
		er |= !LinkBufferPut(ctx, BMS_CMD_WAIT);
		er |= !LinkBufferPutVariable(ctx, tr);
	}
	er |= !LinkBufferPut(ctx, BMS_CMD_END);
	if (er) return 0;
	MeasureFree(ctx);
    p = ctx->data = (unsigned char*)malloc(ctx->bmsloader->linkn);
	if (!ctx->data) return 0;
	lk = ctx->bmsloader->linktop;
	tr = ctx->bmsloader->linkn;
	while (tr >= BMS_LINKBUFFER_BLOCK/* && lk*/)
	{
		XMEMCPY(p, lk->buf, BMS_LINKBUFFER_BLOCK);
		tr -= BMS_LINKBUFFER_BLOCK;
		p  += BMS_LINKBUFFER_BLOCK;
		lk = lk->next;
	}
	if (tr) XMEMCPY(p, lk->buf, tr);
	LinkBufferFree(ctx);
	return 1;
}

static int BMSLoaderInit(KMP_CTX *ctx)
{
	ctx->bmsloader = (BMS_BMSLOADER*)malloc(sizeof(BMS_BMSLOADER));
	while (ctx->bmsloader)
	{
		unsigned i;
		ctx->bmsloader->bpm = 130;
		ctx->bmsloader->midipath[0] = 0;
		for (i = 0; i < BMS_MAX_EXBPM; i++)
		{
			ctx->exbpm[i] = 0;
		}
		TextInit(ctx);
		MeasureInit(ctx);
		LinkBufferInit(ctx);
		if (!BMSLoaderLoadText(ctx)) break;
		if (!BMSLoaderBuildData(ctx)) break;
		/* success */
		BMSLoaderFree(ctx);
		return 1;
	}
	BMSLoaderFree(ctx);
	return 0;
}

static void WAVLoaderFree(KMP_CTX *ctx)
{
	if (!ctx->wavloader) return;
	free(ctx->wavloader);
	ctx->wavloader = 0;
}


static int WAVLoaderInit(KMP_CTX *ctx)
{
	ctx->wavloader = (BMS_WAVLOADER*)malloc(sizeof(BMS_WAVLOADER));
	while (ctx->wavloader)
	{
		unsigned i;
		ctx->wavloader->loaded = 0;
		for (i = 0; i < BMS_MAX_NOTE; i++)
		{
			ctx->wavloader->wavpath[i][0] = 0;
		}
        ctx->wavloader->midipath[0] = 0;
		return 1;
	}
	WAVLoaderFree(ctx);
	return 0;
}

static void KeyOn(KMP_CTX *ctx, BMS_NOTE *note);

static int WAVLoaderLoad(KMP_CTX *ctx)
{
	unsigned i, j;
    BMS_WAV newwav;
	BMS_WAV *wavp = ctx->wav;
    BMS_NOTE *last_note = NULL;
	if (!ctx->wavloader) return 0;
	if (ctx->wavloader->loaded) return 1;
	for (i = 1; i < BMS_MAX_NOTE; i++)
	{
		if (!ctx->wavloader->wavpath[i][0]) continue;
//		for (j = 1;j < i && XSTRCMPI(ctx->wavloader->wavpath[i], ctx->wavloader->wavpath[j]); j++);
        for(j = 1; j < i && _wcsicmp(ctx->wavloader->wavpath[i], ctx->wavloader->wavpath[j]); j++);
		if (j < i)
		{
			ctx->note[i].wavp = ctx->note[j].wavp;  /* alias */
			ctx->note[i].flag = ctx->note[j].flag;
		}
		//else if (WavLoad(&newwav, kpilist, ctx->bmspath, ctx->wavloader->wavpath[i]))
		else if (WavLoad(&newwav, ctx->bmsfolder, ctx->wavloader->wavpath[i], ctx->srate))
		{
			*wavp = newwav;
			wavp->spd = XMULDIV(wavp->freq, (1 << BMS_FIX_BITS), ctx->srate);
			ctx->note[i].flag &= ~1;
			ctx->note[i].wavp = wavp++;
		}
	}
    ZeroMemory(&ctx->midinote, sizeof(ctx->midinote));
    ZeroMemory(&ctx->midwav, sizeof(ctx->midwav));
    if(ctx->wavloader->midipath[0]){
		if (WavLoad(&newwav, ctx->bmsfolder, ctx->wavloader->midipath, ctx->srate)){
			ctx->midwav = newwav;
            ctx->midinote.wavp = &ctx->midwav;
			ctx->midwav.spd = XMULDIV(ctx->midwav.freq, (1 << BMS_FIX_BITS), ctx->srate);
			ctx->midinote.flag &= ~1;
            KeyOn(ctx, &ctx->midinote);
		}
    }
	ctx->wavloader->loaded = 1;
	return ctx->wavloader->loaded;
}


void Close(KMP_CTX *ctx)
{
	unsigned i;
	if (!ctx) return;
	for (i = 0; i < BMS_MAX_NOTE; i++)
	{
		if (ctx->wav[i].ctx) ctx->wav[i].Close(ctx->wav[i].ctx);
		if (ctx->wav[i].p) free(ctx->wav[i].p);
	}
	if (ctx->data) free(ctx->data);
	if (ctx->bmsloader) BMSLoaderFree(ctx); /* 念のため？ */
	if (ctx->wavloader) WAVLoaderFree(ctx);
	free(ctx);
}

static void KeyOn(KMP_CTX *ctx, BMS_NOTE *note)
{
	BMS_NOTE *prev;
	if (!(note->flag & 0x81))
	{
		note->flag |= 0x80;
		prev = ctx->keyonnote.prev;
		note->prev = prev;
		note->next = &ctx->keyonnote;
		prev->next = ctx->keyonnote.prev = note;
	}
	note->posl = 0;
	note->posh = 0;
}

static void KeyOff(KMP_CTX *ctx, BMS_NOTE *note)
{
	if (note->flag & 0x80)
	{
		BMS_NOTE *prev, *next;
		note->flag &= ~0x80;
		prev = note->prev;
		next = note->next;
		next->prev = prev;
		prev->next = next;
	}
}

static void KeyOffAll(KMP_CTX *ctx)
{
	unsigned i;
	ctx->keyonnote.next = &ctx->keyonnote;
	ctx->keyonnote.prev = &ctx->keyonnote;
	for (i = 0; i < BMS_MAX_NOTE; i++) ctx->note[i].flag &= 1;
    ctx->midinote.flag &= 1;
}

static unsigned ReadVariable(const unsigned char **p)
{
	unsigned ret = 0;
	do {
		ret <<= 7;
		ret += **p & 0x7f;
	} while (*((*p)++) & 0x80);
	return ret;
}

static void Step(KMP_CTX *ctx)
{
	unsigned clock;
	const unsigned char *p = ctx->cur_data;
	while (*p != BMS_CMD_END)
	{
		switch (*p++)
		{
		case BMS_CMD_TEMPO:
			ctx->cur_bpm = (BMS_DIVISION / 4) * ReadVariable(&p);
			ctx->cur_spc = XMULDIV(ctx->cur_ratio, ctx->srate * 60, ctx->cur_bpm);
			break;
		case BMS_CMD_EXBPM:
			clock = ReadVariable(&p);
			if (clock >= 0 && clock < BMS_MAX_EXBPM && ctx->exbpm[clock])
			{
				ctx->cur_bpm = ctx->exbpm[clock];
				ctx->cur_spc = XMULDIV(ctx->cur_ratio, ctx->srate * 60, ctx->cur_bpm);
			}
			break;
		case BMS_CMD_RATIO:
			ctx->cur_ratio = ReadVariable(&p);
			ctx->cur_spc = XMULDIV(ctx->cur_ratio, ctx->srate * 60, ctx->cur_bpm);
			break;
		case BMS_CMD_NOTE:
			KeyOn(ctx, &ctx->note[ReadVariable(&p)]);
			break;
		case BMS_CMD_WAIT:
			clock = ReadVariable(&p);
			while (clock--)
			{
				ctx->cnt_samplel += ctx->cur_spc;
				ctx->cnt_sampleh += ctx->cnt_samplel >> BMS_FIX_BITS;
				ctx->cnt_samplel &= (1 << BMS_FIX_BITS) - 1;
			}
			break;
		}
		if (!ctx->cnt_sampleh) continue;
		ctx->cur_data = p;
		return;
	}
	/* ctx->cnt_sampleh = 1 << 20; */
	ctx->end_flag = 1;
}

static void Start(KMP_CTX *ctx)
{
	KeyOffAll(ctx);
	ctx->cur_data = ctx->data;
	ctx->cur_ratio = (1 << BMS_FIX_BITS);
	ctx->cur_bpm = (BMS_DIVISION / 4) * 130;
	/* sample / clock (fixed point) */
	ctx->cur_spc = XMULDIV(ctx->cur_ratio, ctx->srate * 60, ctx->cur_bpm);
	ctx->cnt_samplel = 0;
	ctx->cnt_sampleh = 0;
	ctx->end_flag = 0;
	ctx->out_smp = 0;
	ctx->first_flag = 1;
    if(ctx->midinote.wavp){
        KeyOn(ctx, &ctx->midinote);
    }
}

//KMP_CTX *Open(const char *path, unsigned srate)
KMP_CTX *Open(IKpiFile *pFile, IKpiFolder *pFolder, unsigned srate)
{
	KMP_CTX *ctx;
	ctx = (KMP_CTX*)malloc(sizeof(KMP_CTX));
	ZeroMemory(ctx, sizeof(KMP_CTX));
    do{
		unsigned i;
		ctx->srate = (srate == 0) ? SRATE : srate;
		ctx->data = 0;
		ctx->bmsloader = 0;
		ctx->wavloader = 0;
		for (i = 0; i < BMS_MAX_NOTE; i++)
		{
			ctx->wav[i].p = 0;
			ctx->wav[i].ctx = 0;
			ctx->note[i].wavp = 0;
			ctx->note[i].flag = 1;
		}
        ctx->midinote.flag = 1;
		ctx->volwav = 100;
		//XSTRCPYN(ctx->bmspath, path, BMS_MAX_PATH);
        ctx->bmsfile = pFile;
        ctx->bmsfolder = pFolder;
		if (!WAVLoaderInit(ctx)) break;
		if (!BMSLoaderInit(ctx)) break;
		Start(ctx);
		return ctx;
	}while(0);
	Close(ctx);
	return 0;
}

#define MIXBUF_SIZE 128
static void MixSub(KMP_CTX *ctx, void *buf, unsigned smp)
{
	signed mixbuf[MIXBUF_SIZE * 2];
	signed *mixbufp = mixbuf;
	signed short *bufp = (signed short*)buf;
	signed vall1, vall2;
	signed valr1, valr2;
	BMS_NOTE *note;
	unsigned i;
	for (i = 0; i < smp; i++)
	{
		*mixbufp++ = 0;
		*mixbufp++ = 0;
	}
	for (note = ctx->keyonnote.next; note != &ctx->keyonnote; note = note->next){
		mixbufp = mixbuf;
		switch (note->wavp->type)
		{
		case WAV_1CH_16BIT:
			for (i = 0; i < smp;)
			{
				if (((note->posh + 2) << 1) > note->wavp->load)
				{
					if (note->wavp->ctx)
					{
						note->wavp->Loader(note->wavp->ctx, note->wavp);
						continue;
					}
					KeyOff(ctx, note);
					break;
				}
				vall1 = ((signed short *)note->wavp->p)[note->posh + 0];
				vall2 = ((signed short *)note->wavp->p)[note->posh + 1];
				vall1 += ((vall2 - vall1) * note->posl) >> BMS_FIX_BITS;
				*mixbufp++ += vall1;
				*mixbufp++ += vall1;
				note->posl += note->wavp->spd;
				note->posh += note->posl >> BMS_FIX_BITS;
				note->posl &= (1 << BMS_FIX_BITS) - 1;
				if (note->posh >= note->wavp->len)
				{
					KeyOff(ctx, note);
					break;
				}
				i++;
			}
			break;
		case WAV_2CH_16BIT:
			for (i = 0; i < smp;)
			{
				if (((note->posh + 2) << 2) > note->wavp->load)
				{
					if (note->wavp->ctx)
					{
						note->wavp->Loader(note->wavp->ctx, note->wavp);
						continue;
					}
					KeyOff(ctx, note);
					break;
				}
				vall1 = ((signed short *)note->wavp->p)[note->posh * 2 + 0];
				valr1 = ((signed short *)note->wavp->p)[note->posh * 2 + 1];
				vall2 = ((signed short *)note->wavp->p)[note->posh * 2 + 2];
				valr2 = ((signed short *)note->wavp->p)[note->posh * 2 + 3];
				vall1 += ((vall2 - vall1) * note->posl) >> BMS_FIX_BITS;
				valr1 += ((valr2 - valr1) * note->posl) >> BMS_FIX_BITS;
				*mixbufp++ += vall1;
				*mixbufp++ += valr1;
				note->posl += note->wavp->spd;
				note->posh += note->posl >> BMS_FIX_BITS;
				note->posl &= (1 << BMS_FIX_BITS) - 1;
				if (note->posh >= note->wavp->len)
				{
					KeyOff(ctx, note);
					break;
				}
				i++;
			}
			break;
		}

	}
	mixbufp = mixbuf;
	smp += smp;
	for (i = 0; i < smp; i++)
	{
		vall1 = (*mixbufp++ * ctx->volwav) >> 7;
		if (vall1 > 0x7fff)
			vall1 = 0x7fff;
		else if (vall1 < -0x8000)
			vall1 = -0x8000;
		*bufp++ = vall1;
	}
}

static void Mix(KMP_CTX *ctx, void *buf, unsigned smp)
{
	while (smp > MIXBUF_SIZE)
	{
		MixSub(ctx, buf, MIXBUF_SIZE);
		buf = (char *)buf + MIXBUF_SIZE * 4;
		smp -= MIXBUF_SIZE;
	}
	MixSub(ctx, buf, smp);
}

static void MixSkip(KMP_CTX *ctx, unsigned smp)
{
	BMS_NOTE *note;
	unsigned i;
	for (note = ctx->keyonnote.next; note != &ctx->keyonnote; note = note->next){
        for (i = 0; i < smp; i++)
		{
			note->posl += note->wavp->spd;
			note->posh += note->posl >> BMS_FIX_BITS;
			note->posl &= (1 << BMS_FIX_BITS) - 1;
			if (note->posh >= note->wavp->len)
			{
				KeyOff(ctx, note);
				break;
			}
		}
	}
}

static void FirstCheck(KMP_CTX *ctx)
{
	if (ctx->first_flag) 
	{
		ctx->first_flag = 0;
		WAVLoaderLoad(ctx);
		Step(ctx);
	}
}

unsigned Write(KMP_CTX *ctx, void *buf, unsigned smp)
{
	unsigned char *bufp = (unsigned char*)buf;
	//if (ctx->end_flag && ctx->keyonnote.next == &ctx->keyonnote) return 0;
    if(ctx->end_flag && ctx->keyonnote.next == &ctx->keyonnote){
        if(!ctx->midwav.ctx && ctx->midinote.posh >= ctx->midwav.len){
            return 0;
        }
    }
	FirstCheck(ctx);
	ctx->out_smp += smp;
	while (!ctx->end_flag && smp >= ctx->cnt_sampleh)
	{
		Mix(ctx, bufp, ctx->cnt_sampleh);
		bufp += ctx->cnt_sampleh << 2;
		smp -= ctx->cnt_sampleh;
		ctx->cnt_sampleh = 0;
		Step(ctx);
	}
	if (smp)
	{
		Mix(ctx, bufp, smp);
		bufp += smp << 2;
		ctx->cnt_sampleh -= smp;
	}
	return (bufp - (unsigned char *)buf)>>2;
}

unsigned WriteSkip(KMP_CTX *ctx, unsigned smp)
{
	unsigned w = 0;
	if (ctx->end_flag && ctx->keyonnote.next == &ctx->keyonnote) return 0;
	FirstCheck(ctx);
	ctx->out_smp += smp;
	while (!ctx->end_flag && smp >= ctx->cnt_sampleh)
	{
		MixSkip(ctx, ctx->cnt_sampleh);
		w += ctx->cnt_sampleh;
		smp -= ctx->cnt_sampleh;
		ctx->cnt_sampleh = 0;
		Step(ctx);
	}
	if (smp)
	{
		MixSkip(ctx, smp);
		w += smp;
		ctx->cnt_sampleh -= smp;
	}
	return w;
}

unsigned GetLength(KMP_CTX *ctx)
{
	const unsigned char *p = ctx->data;
	unsigned ms = 0, bpm = 130 * (BMS_DIVISION / 4), ratio = (1 << BMS_FIX_BITS), clock = 0, exbpm = 0;
	if (!p) return 0;
	while (*p != BMS_CMD_END)
	{
		switch (*p++)
		{
		case BMS_CMD_TEMPO:
			ms += XMULDIV(clock, 60 * 1000 * ratio, (1 << BMS_FIX_BITS) * bpm);
			clock = 0;
			bpm = ReadVariable(&p) * (BMS_DIVISION / 4);
			continue;
		case BMS_CMD_EXBPM:
			ms += XMULDIV(clock, 60 * 1000 * ratio, (1 << BMS_FIX_BITS) * bpm);
			clock = 0;
			exbpm = ReadVariable(&p);
			if (exbpm >= 0 && exbpm < BMS_MAX_EXBPM && ctx->exbpm[exbpm])
				bpm = ctx->exbpm[exbpm];
			continue;
		case BMS_CMD_RATIO:
			ms += XMULDIV(clock, 60 * 1000 * ratio, (1 << BMS_FIX_BITS) * bpm);
			clock = 0;
			ratio = ReadVariable(&p);
			continue;
		case BMS_CMD_NOTE:
			ReadVariable(&p);
			continue;
		case BMS_CMD_WAIT:
			clock += ReadVariable(&p);
			break;
		}
	}
	ms += XMULDIV(clock, 60 * 1000 * ratio, (1 << BMS_FIX_BITS) * bpm);
	return ms;
}
//////////////////////////////////////////////////////////////////////////
KbBmsDecoder::KbBmsDecoder(void)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_ctx = NULL;
    m_pFile = NULL;
    m_pFolder = NULL;
}
//////////////////////////////////////////////////////////////////////////
KbBmsDecoder::~KbBmsDecoder(void)
{
    KMP_CTX *ctx = (KMP_CTX*)m_ctx;
	if (!ctx) return;
	unsigned i;
	for (i = 0; i < BMS_MAX_NOTE; i++)
	{
		if (ctx->wav[i].ctx) ctx->wav[i].Close(ctx->wav[i].ctx);
		if (ctx->wav[i].p) free(ctx->wav[i].p);
	}
    if(ctx->midwav.ctx){
        ctx->midwav.Close(ctx->midwav.ctx);
    }
    free(ctx->midwav.p);
    if (ctx->data) free(ctx->data);
	if (ctx->bmsloader) BMSLoaderFree(ctx); /* 念のため？ */
	if (ctx->wavloader) WAVLoaderFree(ctx);
	free(ctx);
    if(m_pFile){
        m_pFile->Release();
    }
    if(m_pFolder){
        m_pFolder->Release();
    }
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbBmsDecoder::Open(const KPI_MEDIAINFO *cpRequest, 
                                        IKpiFile   *pFile, 
                                        IKpiFolder *pFolder)
{
    KMP_CTX *ctx = ::Open(pFile, pFolder, cpRequest->dwSampleRate);
    if(!ctx){
        return 0;
    }
    m_ctx = ctx;
    m_MediaInfo.dwSampleRate = ctx->srate;
    m_MediaInfo.dwChannels = 2;
    m_MediaInfo.nBitsPerSample = 16;
	m_MediaInfo.dwSeekableFlags = 0;//KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
	m_MediaInfo.dwUnitSample = 0;
    m_MediaInfo.qwLength = GetLength(ctx) * 10000;
    m_MediaInfo.dwCount = 1;
    m_pFile = pFile;
    pFile->AddRef();
    m_pFolder = pFolder;
    pFolder->AddRef();
    return 1;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbBmsDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){
        return 0;
    }
    if (!ppMediaInfo) {
    //タイトル/アーティスト取得のみ
        return dwNumber;
    }
    m_MediaInfo.dwNumber = dwNumber;
    *ppMediaInfo = &m_MediaInfo;
    return m_MediaInfo.dwNumber;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbBmsDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    KMP_CTX *ctx = (KMP_CTX*)m_ctx;
    FirstCheck(ctx);
	if (qwPosSample != ctx->out_smp){
		if (qwPosSample < ctx->out_smp){
			Start(ctx);
			FirstCheck(ctx);
		}
		qwPosSample -= ctx->out_smp;
		if (qwPosSample) WriteSkip(ctx, qwPosSample);
	}
	return ctx->out_smp;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbBmsDecoder::Render(BYTE* pBuffer, DWORD dwSizeSample)
{
    KMP_CTX *ctx = (KMP_CTX*)m_ctx;
    return Write(ctx, pBuffer, dwSizeSample);
}
//////////////////////////////////////////////////////////////////////////
