#include <windows.h>
#include <math.h>
#include <string.h>
#include <crtdbg.h>
#include <stdlib.h>
#include "kbfmoplmidi_decoder.h"
#include "kbfmoplmidi_config.h"

// VGMPlay.cpp : Definiert den Einsprungpunkt fE die Konsolenanwendung.
//

// TODO:	- Seeking Bugfix (hanging note)
//			- Drum Kanal: gegenseitiges Stoppen verschiedener Drum Sounds (z. Bsp. Hi-Hat)
//			- Mono-Mode / Poly-Mode
//			- Sustain (NoteOff, bei gleicher Notenhφhe), Portamento-Controller
//			- OPL3: 4-Op-Mode
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

#include "Stream.h"
#include "MidiPlay.h"
#include "chips\mamedef.h"
#include "VGMPlay.h"

#if defined(__cplusplus)
extern "C"
{
#endif

#include "chips\3812intf.h"	// OPL 2
#include "chips\3526intf.h"	// OPL 1
#include "chips\262intf.h"	// OPL 3

unsigned char OpenPortTalk(void);
void ClosePortTalk(void);
void outportb(unsigned short PortAddress, unsigned char byte);
unsigned char inportb(unsigned short PortAddress);

unsigned char MIDI1to0(unsigned long int SrcLen, unsigned char* SrcData,
						unsigned long int* RetDstLen, unsigned char** RetDstData);

#if defined(__cplusplus)
}
#endif

const unsigned char CHIP_COUNT = 0x03;

typedef struct chip_options
{
	bool Disabled;
} CHIP_OPTS;

// FM Register Group List
const unsigned char FM_REG_GRP[0x06] = {0x20, 0x40, 0x60, 0x80, 0xE0, 0xC0};
// Rhythm Sounds: Bass Drum, Snare Drum, Tom Tom, Cymbal, Hi-Hat
const unsigned char FM_RHYTHM_CH[0x05] = {0x06, 0x07, 0x08, 0x08, 0x07};
const unsigned char FM_RHYTHM_MSK[0x05] = {0x03, 0x02, 0x01, 0x02, 0x01};

const double PI = 3.14159265358979323846;
unsigned char scumm_lookup_table[64][32];


// Function Prototypes
int main(int argc, char* argv[]);
//static void WinNT_Check(void);
//static void ReadOptions(const char* AppName);
//static bool GetBoolFromStr(const char* TextStr);
//static bool GetBoolFromStr(const wchar_t* TextStr);
//static unsigned long int GetFileLength(const char* FileName);
//static bool OpenVGMFile(const char* FileName);
/*static wchar_t* ReadWStrFromFile(FILE* hFile, unsigned long int* FilePos,
								 unsigned long int EOFPos);*/
static void SwapBytes(void* Buffer, unsigned long int Bytes);
static void scumm_create_lookup_table(void);
static void ShowVGMTag(void);

signed char sign(double Value);
long int Round(double Value);
double RoundSpecial(double Value, double RoundTo);
static void PrintMinSec(unsigned long int SamplePos);
static void RestartPlaying(void);
static void StartSkipping(void);
static void StopSkipping(void);

static inline signed long int SampleVGM2Playback(signed long int SampleVal);
static inline signed long int SamplePlayback2VGM(signed long int SampleVal);

//static void OPL_Hardware_Detecton(void);
//static inline unsigned char OPL_HW_GetStatus(void);
//static inline void OPL_HW_WaitDelay(signed __int64 StartTime, double Delay);
//static inline void OPL_HW_WriteReg(unsigned short int Reg, unsigned char Data);
static inline void VGMLog_CmdWrite(unsigned char Cmd, unsigned char Reg, unsigned char Data);
static inline void OPL_Write(unsigned char ChipID, unsigned short int Register,
							 unsigned char Data);

static void InterpretFile(unsigned long int SampleCount);
static void JumpToMIDITick(unsigned short int TrackNo, signed long int Tick);
static unsigned long int GetMIDIDelay(unsigned long int* DelayLen);
static unsigned short int MIDINote2FNum(unsigned char Note, unsigned char Channel);
static void SetVolume(unsigned char ChipID, unsigned char FMChannel, unsigned char Channel,
					  double Volume);
static void PlayNote(unsigned char Command, unsigned char Value, unsigned char Velocity);
static void SetControllerAll(unsigned char Command, unsigned char CtrlNum, signed long int Value);
static void SetFMInstrument(unsigned char Channel, unsigned char FMChip, unsigned char FMChannel);
static void InterpretEventS(unsigned char Command, unsigned char Value1, unsigned char Value2);
static unsigned char GetLAAValueB(unsigned char* Data);
static unsigned short int GetLAAValueS(unsigned char* Data);
static unsigned long int GetLAAValueL(unsigned char* Data);
static void InterpretEventL(unsigned char Command, unsigned char Value,
							unsigned long int DataLen, unsigned char* Data);
static void InterpretMIDI(unsigned long int* SampleCount);

static inline signed long int Stream2Sample(signed long int* Stream, signed long int SampleCnt,
											unsigned short int Volume);
static inline signed short int Limit2Short(signed long int Value);
static inline void ProcessChipStream(WAVE_32BS* RetSample, unsigned char ChipID);
void FillBuffer(WAVE_16BS* Buffer, unsigned long int BufferSize);

/*extern*/ unsigned long int SampleRate;

FM_INSTRUMENT FMIns[0x100];	// 00 - 7F: Melody	80 - FF: Drums
//unsigned char DrumInsNote[0x80];
unsigned char FileMode;
unsigned char OPL_MODE;
unsigned char OPL_CHIPS;
unsigned char OPL_VCHIPS;
unsigned char WINNT_MODE;
VGM_HEADER VGMHead;
MID_HEADER MIDIHead;
unsigned long int VGMDataLen;
unsigned char* VGMData;
GD3_TAG VGMTag;
unsigned short int MidiTrk;
/*
HANDLE hPlayThread;
bool PlayThreadOpen;
bool PauseThread;
static bool CloseThread;
bool ThreadPauseEnable;
bool ThreadPauseConfrm;
*/
CMF_HEADER CMFHead;
unsigned short int CMFInsCount;
CMF_INSTRUMENT* CMFIns;

const int MAX_PORTNUM = 4;
int CurPortNum = 0;

FM_CHN* FMChn;
unsigned char* FMDrumReg;
unsigned char LastMidCmd = 0x90;
MIDI_CHN MidChn[MAX_PORTNUM][0x10];
// GM Mode - used to detect additional Drum Channels
//	00 - GM
//	01 - GS
//	02 - XG
unsigned char GMMode;
signed long int MMstTuning;
unsigned char MMstVolume;

unsigned char NoteHstCount;
NOTE_HISTORY NoteHistory[0x100];

unsigned long int TempoCount;
MIDI_TEMPO* MidTempo;
unsigned long int CurTempo;
unsigned long int BaseTempo;
unsigned long int TotalPbSamples;

unsigned long int CSR[0x100];
unsigned short int CVol[0x100];
unsigned long int SmpP[0x100];	// Current Sample (Playback Rate)
unsigned long int SmpA[0x100];	// Current Sample Old
unsigned long int SmpB[0x100];	// Current Sample New
unsigned long int LSL[0x100];	// Last Sample L
unsigned long int LSR[0x100];	// Last Sample R
double MasterVol;

signed int* StreamBufs[0x02];

unsigned long int VGMPos;
signed long int VGMSmplPos;
signed long int VGMSmplPlayed;
unsigned long int VGMSampleRate;
extern unsigned long int BlocksSent;
extern unsigned long int BlocksPlayed;
//bool PauseEmulate;
//bool LogToWave;
bool VGMEnd;
bool EndPlay;
bool PausePlay;
bool FadePlay;
bool SoftStop;
//bool SoftEnd;
bool RecalcPbTime;
const unsigned short int FMPort = 0;//νΙ 0 ΘΜΕ const ΙΟX by Kobarin
unsigned long int PlayingTime;
unsigned long int FadeTime;
unsigned long int FadeStart;
unsigned long int VGMMaxLoop;
unsigned long int CMFMaxLoop;
unsigned long int VGMLoop;
unsigned long int LAA2_LIdx;
bool BadNoteOff;
unsigned char VolumeCalc;
//char MidiPatchFile[MAX_PATH];
wchar_t MidiPatchFile[MAX_PATH];
bool WinFM_Mode;
unsigned char DefaultGMMode;
bool ErrMsg;
CHIP_OPTS ChipOpts[CHIP_COUNT];
double VolumeLevel;
bool ErrorHappened;
bool Ch16Drum;

static volatile bool Interpreting;
static bool SkipMode;
static unsigned char *OPLReg;
static unsigned char *OPLRegBak;
static unsigned char *OPLRegForce;

static int EmuCore;

#if defined(__cplusplus)
extern "C"
{
#endif

UINT8 CHIP_SAMPLING_MODE;
INT32 CHIP_SAMPLE_RATE;

#if defined(__cplusplus)
}
#endif

const char* ApplName;

#define INP_9X		_inp
#define OUTP_9X		_outp
#define INP_NT		inportb
#define OUTP_NT		outportb

// Delays in usec (Port Reads or microsec)
#define DELAY_OPL2_REG	 3.3f
#define DELAY_OPL2_DATA	23.0f
#define DELAY_OPL3_REG	 0.0f
#define DELAY_OPL3_DATA	 0.28f
signed __int64 HWusTime;

bool LogVGM;
FILE* hFileVGM;
unsigned long int LastVgmDelay;

static BOOL g_bIsLoop = FALSE;//add by Kobarin
static int g_nSampleRate_Cfg;//add by Kobarin

#if 0
static void ReadOptions(const char* AppName)
{
	char* FileName;
	FILE* hFile;
	char TempStr[0x40];
	unsigned long int StrLen;
	unsigned long int TempLng;
	char* LStr;
	char* RStr;
	unsigned char IniSection;
	CHIP_OPTS* TempCOpt;
	
	SampleRate = 44100;
	FadeTime = 0;
	VolumeLevel = 1.0f;
	LogToWave = false;
	VGMMaxLoop = 0x02;
	CMFMaxLoop = 0x01;
	CHIP_SAMPLING_MODE = 0x00;
	CHIP_SAMPLE_RATE = 0x00000000;
	PauseEmulate = false;
	strcpy(MidiPatchFile, "SB16_VXD_InsDrum.bin");
	VolumeCalc = 0x00;
	OPL_MODE = 0x00;
	OPL_CHIPS = 0x01;
	FMPort = 0x00;
	BadNoteOff = false;
	WinFM_Mode = false;
	DefaultGMMode = 0x00;
	ErrMsg = false;
	Ch16Drum = false;
	LogVGM = false;
	
	for (TempLng = 0x00; TempLng < CHIP_COUNT; TempLng ++)
	{
		TempCOpt = ChipOpts + TempLng;
		
		TempCOpt->Disabled = false;
	}
	
	if (AppName == NULL)
	{
		printf("Argument \"Application-Path\" is NULL!\nSkip loading INI.\n");
		return;
	}
	FileName = (char*)malloc(strlen(AppName) + 0x01);
	strcpy(FileName, AppName);
	LStr = strrchr(FileName, '.');
	RStr = strrchr(FileName, '\\');
	if (LStr <= RStr)
	{
		LStr = FileName + strlen(FileName);
		*LStr = '.';
	}
	strcpy(LStr + 0x01, "ini");
	
	hFile = fopen(FileName, "rt");
	if (hFile == NULL)
	{
		if (RStr == NULL)
			LStr = FileName;
		else
			LStr = RStr + 0x01;
		hFile = fopen(LStr, "rt");
		if (hFile == NULL)
		{
			printf("Failed to load INI.\n");
			return;
		}
	}
	
	IniSection = 0x00;
	while(! feof(hFile))
	{
		LStr = fgets(TempStr, 0x40, hFile);
		if (LStr == NULL)
			break;
		if (TempStr[0x00] == ';')	// Comment line
			continue;
		
		StrLen = strlen(TempStr) - 0x01;
		if (! StrLen)
			continue;
		if (TempStr[StrLen] == '\n')
			TempStr[StrLen] = 0x00;
		
		LStr = &TempStr[0x00];
		while(*LStr == ' ')
			LStr ++;
		if (LStr[0x00] == ';')	// Comment line
			continue;
		
		if (LStr[0x00] != '[')
			RStr = strchr(TempStr, '=');
		if (RStr == NULL)
			continue;
		
		if (LStr[0x00] == '[')
		{
			// Line pattern: [Group]
			LStr ++;
			RStr = strchr(TempStr, ']');
			if (RStr != NULL)
				RStr[0x00] = 0x00;
			
			if (! _strnicmp(LStr, "General", 0x10))
			{
				IniSection = 0x00;
			}
			else if (! _strnicmp(LStr, "YM3526", 0x10) || ! _strnicmp(LStr, "OPL-1", 0x10))
			{
				IniSection = 0x81;
			}
			else if (! _strnicmp(LStr, "YM3812", 0x10) || ! _strnicmp(LStr, "OPL-2", 0x10))
			{
				IniSection = 0x82;
			}
			else if (! _strnicmp(LStr, "YMF262", 0x10) || ! _strnicmp(LStr, "OPL-3", 0x10))
			{
				IniSection = 0x83;
			}
		}
		else
		{
			// Line pattern: Option = Value
			TempLng = RStr - TempStr;
			TempStr[TempLng] = 0x00;
			
			// Prepare Strings (trim the spaces)
			RStr = &TempStr[TempLng - 0x01];
			while(*RStr == ' ')
				*(RStr --) = 0x00;
			
			RStr = &TempStr[StrLen - 0x01];
			while(*RStr == ' ')
				*(RStr --) = 0x00;
			RStr = &TempStr[TempLng + 0x01];
			while(*RStr == ' ')
				RStr ++;
			
			switch(IniSection)
			{
			case 0x00:	// General Sction
				if (! _strnicmp(LStr, "SampleRate", 0x10))
				{
					SampleRate = strtoul(RStr, NULL, 0);
				}
				else if (! _strnicmp(LStr, "FadeTime", 0x10))
				{
					FadeTime = strtoul(RStr, NULL, 0);
				}
				else if (! _strnicmp(LStr, "LogSound", 0x10))
				{
					LogToWave = GetBoolFromStr(RStr);
				}
				else if (! _strnicmp(LStr, "LogVGM", 0x10))
				{
					LogVGM = GetBoolFromStr(RStr);
				}
				else if (! _strnicmp(LStr, "Volume", 0x10))
				{
					VolumeLevel = (double)strtod(RStr, NULL);
				}
				else if (! _strnicmp(LStr, "MaxLoops", 0x10))
				{
					VGMMaxLoop = strtoul(RStr, NULL, 0);
				}
				else if (! _strnicmp(LStr, "MaxLoopsCMF", 0x10))
				{
					CMFMaxLoop = strtoul(RStr, NULL, 0);
				}
				else if (! _strnicmp(LStr, "ChipSmplRate", 0x10))
				{
					CHIP_SAMPLE_RATE = strtol(RStr, NULL, 0);
				}
				else if (! _strnicmp(LStr, "EmulatePause", 0x10))
				{
					PauseEmulate = GetBoolFromStr(RStr);
				}
				else if (! _strnicmp(LStr, "PatchFile", 0x10))
				{
					strcpy(MidiPatchFile, RStr);
				}
				else if (! _strnicmp(LStr, "VolumeCalc", 0x10))
				{
					VolumeCalc = (unsigned char)strtoul(RStr, NULL, 0);
				}
				else if (! _strnicmp(LStr, "OPLMode", 0x10))
				{
					OPL_MODE = (unsigned char)strtoul(RStr, NULL, 0);
					if (OPL_MODE > 0x03)
						OPL_MODE = 0x00;
				}
				else if (! _strnicmp(LStr, "OPLChips", 0x10))
				{
					OPL_CHIPS = (unsigned char)strtoul(RStr, NULL, 0);
					if (OPL_CHIPS == 0x00 || OPL_CHIPS > 0x10)
						OPL_CHIPS = 0x01;
				}
				else if (! _strnicmp(LStr, "FMPort", 0x10))
				{
					FMPort = (unsigned short int)strtoul(RStr, NULL, 16);
				}
				else if (! _strnicmp(LStr, "BadNoteOffs", 0x10))
				{
					BadNoteOff = GetBoolFromStr(RStr);
				}
				else if (! _strnicmp(LStr, "WinFM_Mode", 0x10))
				{
					WinFM_Mode = GetBoolFromStr(RStr);
				}
				else if (! _strnicmp(LStr, "DefaultMode", 0x10))
				{
					DefaultGMMode = (unsigned char)strtoul(RStr, NULL, 0);
				}
				else if (! _strnicmp(LStr, "EnableErrorMsg", 0x10))
				{
					ErrMsg = GetBoolFromStr(RStr);
				}
				else if (! _strnicmp(LStr, "DrumChannel16", 0x10))
				{
					Ch16Drum = GetBoolFromStr(RStr);
				}
				break;
			case 0x81:	// YM3526
			case 0x82:	// YM3812
			case 0x83:	// YMF262
				TempCOpt = ChipOpts + (IniSection & 0x0F);
				
				if (! _strnicmp(LStr, "Disabled", 0x10))
				{
					TempCOpt->Disabled = GetBoolFromStr(RStr);
				}
				break;
			case 0xFF:	// Dummy Section
				break;
			}
		}
	}
	
	fclose(hFile);
	if (CHIP_SAMPLE_RATE == 0xFFFFFFFF)
		CHIP_SAMPLE_RATE = SampleRate;
	if (CHIP_SAMPLE_RATE)
		CHIP_SAMPLING_MODE = 0x02;
	if (FMPort)
	{
		//WinNT_Check();
		
		//if (! OPL_MODE)
		//	OPL_Hardware_Detecton();
		SampleRate = 49716;
	}
	if (LogVGM)
		SampleRate = 44100;
	
	free(FileName);//ΗΑ by Kobarin
	return;
}
static bool GetBoolFromStr(const char* TextStr)
{
	if (! _strnicmp(TextStr, "True", 0x10))
		return true;
	else if (! _strnicmp(TextStr, "False", 0x10))
		return false;
	else
		return strtol(TextStr, NULL, 0) ? true : false;
}
static bool GetBoolFromStr(const wchar_t* TextStr)
{
	if (! _wcsnicmp(TextStr, L"True", 0x10))
		return true;
	else if (!_wcsnicmp(TextStr, L"False", 0x10))
		return false;
	else
		return wcstol(TextStr, NULL, 0) ? true : false;
}
#endif
static void ReadOptions(IKpiConfig *pConfig)
{
	kbfmoplmidi_Settings settings;
	kbfmoplmidi_GetSettings(pConfig, &settings);

	SampleRate = 44100;
    g_nSampleRate_Cfg = 44100;
	FadeTime = 0;
	VolumeLevel = 1.0f;
	//LogToWave = false;
	VGMMaxLoop = 0x02;
	CMFMaxLoop = 0x01;
	CHIP_SAMPLING_MODE = 0x00;
	CHIP_SAMPLE_RATE = 0x00000000;
	//PauseEmulate = false;
	VolumeCalc = 0x00;
	OPL_MODE = 0x00;
	OPL_CHIPS = 0x01;
//	FMPort = 0x00;
	BadNoteOff = false;
	WinFM_Mode = false;
	DefaultGMMode = 0x00;
	ErrMsg = false;
	Ch16Drum = false;
	LogVGM = false;
	
	/*
    for (TempLng = 0x00; TempLng < CHIP_COUNT; TempLng ++)
	{
		ChipOpts[TempLng].Disabled = false;
	}
    */
    //[General]
    g_nSampleRate_Cfg = settings.nSampleRate;
    if(g_nSampleRate_Cfg){
        int minus = g_nSampleRate_Cfg < 0 ? -1 : 1;
        if(abs(g_nSampleRate_Cfg) < 8000){
            g_nSampleRate_Cfg = 8000*minus;
        }
        else if(abs(g_nSampleRate_Cfg) > 192000){
            g_nSampleRate_Cfg = 192000*minus;
        }
    }
    else{
        g_nSampleRate_Cfg = 44100;
    }
    FadeTime = settings.nFadeTime;
    
    //LogToWave = settings.bLogSoundp;//³
    
    //LogVGM = settings.bLogVGM;//³

    VolumeLevel = settings.dVolume;
    
    VGMMaxLoop = settings.nMaxLoops;
    CMFMaxLoop = settings.nMaxLoopsCMF;
	CHIP_SAMPLE_RATE = settings.nChipSmplRate;
    
    //PauseEmulate = settings.bEmulatePause;//³
    
    wcsncpy_s(MidiPatchFile, settings.szPatchFile, _TRUNCATE);
	
    VolumeCalc = settings.nVolumeCalc;
    OPL_MODE = settings.nOPLMode;
	if (OPL_MODE > 0x03){
    	OPL_MODE = 0x00;
    }
    OPL_CHIPS = settings.nOPLChips;
	if (OPL_CHIPS == 0x00 || OPL_CHIPS > 0x10){
		OPL_CHIPS = 0x01;
    }
    //FMPort = settings.bFMPort;//³
    
    BadNoteOff = settings.bBadNoteOffs;

    WinFM_Mode = settings.bWinFM_Mode;

    DefaultGMMode = settings.nDefaultMode;

    //ErrMsg = settings.bEnableErrorMsg;//³(νΙfalse(ρ\¦))
    
    Ch16Drum = settings.bDrumChannel16;

    //[YM3526]
    ChipOpts[0].Disabled = settings.bYM3526_Disable;
    //[YM3812]
    ChipOpts[1].Disabled = settings.bYM3812_Disable;
    //[YMF262]
    ChipOpts[2].Disabled = settings.bYMF262_Disable;
	
	if (CHIP_SAMPLE_RATE < 0)
		CHIP_SAMPLE_RATE = SampleRate;
	if (CHIP_SAMPLE_RATE)
		CHIP_SAMPLING_MODE = 0x02;
	EmuCore = settings.nEmuCore;
	/*
    if (FMPort)
	{
		//WinNT_Check();
		
		//if (! OPL_MODE)
		//	OPL_Hardware_Detecton();
		SampleRate = 49716;
	}
	if (LogVGM)
		SampleRate = 44100;
	*/
	return;
}
#if 0
static bool OpenVGMFile(const char* FileName)
{
	FILE* hFile;
	FILE* hInsFile;
	unsigned long int FileSize;
	unsigned long int fccHeader;
	unsigned long int CurPos;
	unsigned char TempByt;
	unsigned short int TempSht;
	unsigned long int TempLng;
	char* TempStr;
	unsigned char InsLine[0x1C];
	unsigned char* TempOp;
	
	/*hInsFile = fopen("InsOutput", "wb");
	for (TempByt = 0x00; TempByt < 128; TempByt ++)
	{
		memset(InsLine, 0x00, 0x1C);
		InsLine[0x00] = map_gm_to_fm[TempByt][0x00];
		InsLine[0x01] = map_gm_to_fm[TempByt][0x01] ^ 0x3F;
		InsLine[0x02] = ~map_gm_to_fm[TempByt][0x02];
		InsLine[0x03] = ~map_gm_to_fm[TempByt][0x03];
		InsLine[0x04] = map_gm_to_fm[TempByt][0x04] & 0x03;
		InsLine[0x05] = map_gm_to_fm[TempByt][0x05];
		InsLine[0x06] = map_gm_to_fm[TempByt][0x06] ^ 0x3F;
		InsLine[0x07] = ~map_gm_to_fm[TempByt][0x07];
		InsLine[0x08] = ~map_gm_to_fm[TempByt][0x08];
		InsLine[0x09] = map_gm_to_fm[TempByt][0x09] & 0x03;
		InsLine[0x16] = map_gm_to_fm[TempByt][0x04] >> 2;
		InsLine[0x17] = map_gm_to_fm[TempByt][0x09] >> 2;
		InsLine[0x18] = map_gm_to_fm[TempByt][0x0A];
		InsLine[0x1A] = map_gm_to_fm[TempByt][0x1D];
		fwrite(InsLine, 0x01, 0x1C, hInsFile);
	}
	for (TempSht = 0x00; TempSht < 128; TempSht ++)
	{
		TempByt = gm_percussion_lookup[TempSht];
		memset(InsLine, 0x00, 0x1C);
		if (TempByt != 0xFF)
		{
			if (TempByt >= 39)
				*(char*)NULL = 0;
			InsLine[0x00] = gm_percussion_to_fm[TempByt][0x00];
			InsLine[0x01] = gm_percussion_to_fm[TempByt][0x01] ^ 0x3F;
			InsLine[0x02] = ~gm_percussion_to_fm[TempByt][0x02];
			InsLine[0x03] = ~gm_percussion_to_fm[TempByt][0x03];
			InsLine[0x04] = gm_percussion_to_fm[TempByt][0x04] & 0x03;
			InsLine[0x05] = gm_percussion_to_fm[TempByt][0x05];
			InsLine[0x06] = gm_percussion_to_fm[TempByt][0x06] ^ 0x3F;
			InsLine[0x07] = ~gm_percussion_to_fm[TempByt][0x07];
			InsLine[0x08] = ~gm_percussion_to_fm[TempByt][0x08];
			InsLine[0x09] = gm_percussion_to_fm[TempByt][0x09] & 0x03;
			InsLine[0x16] = gm_percussion_to_fm[TempByt][0x04] >> 2;
			InsLine[0x17] = gm_percussion_to_fm[TempByt][0x09] >> 2;
			InsLine[0x18] = gm_percussion_to_fm[TempByt][0x0A];
			InsLine[0x1A] = gm_percussion_to_fm[TempByt][0x1D];
		}
		fwrite(InsLine, 0x01, 0x1C, hInsFile);
	}
	fclose(hInsFile);
	return false;*/
	
	FileSize = GetFileLength(FileName);
	
	hFile = fopen(FileName, "rb");
	if (hFile == NULL)
		return false;
	
	fseek(hFile, 0x00, SEEK_SET);
	fread(&fccHeader, 0x04, 0x01, hFile);
	switch(fccHeader)
	{
	case FCC_MIDH:
		FileMode = FM_MID;
		break;
	case FCC_CMF:
		FileMode = FM_CMF;
		break;
	case FCC_LAA2:
		FileMode = FM_LAA2;
		break;
	default:
		fread(&TempSht, 0x02, 0x01, hFile);
		if (TempSht == TCC_LAA1)
		{
			FileMode = FM_LAA1;
			break;
		}
		
		FileMode = FM_UNDEF;
		break;
	}
	if (FileMode == FM_UNDEF)
		goto OpenErr;
/*	
	VGMTag.strTrackNameE = L"";
	//VGMTag.strGameNameE = L"";
	VGMTag.strGameNameE = (wchar_t*)malloc(0x10 * sizeof(wchar_t*));
	wcscpy(VGMTag.strGameNameE, L"    Player");
	//VGMTag.strSystemNameE = L"";
	VGMTag.strSystemNameE = L"PC / MS-DOS";
	VGMTag.strAuthorNameE = L"";
	VGMTag.strReleaseDate = L"";
	VGMTag.strCreator = L"";
	VGMTag.strNotes = L"";
*/
//ͺC³ by Kobarin
	VGMTag.strTrackNameE = _wcsdup(L"");
	VGMTag.strGameNameE = (wchar_t*)malloc(0x10 * sizeof(wchar_t*));
	wcscpy(VGMTag.strGameNameE, L"    Player");
	VGMTag.strSystemNameE = _wcsdup(L"PC / MS-DOS");
	VGMTag.strAuthorNameE = _wcsdup(L"");
	VGMTag.strReleaseDate = _wcsdup(L"");
	VGMTag.strCreator = _wcsdup(L"");
	VGMTag.strNotes = _wcsdup(L"");

	VGMDataLen = FileSize;
	
	switch(FileMode)
	{
	case FM_MID:	// MIDI File
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL)
			goto OpenErr;
		fseek(hFile, 0x00, SEEK_SET);
		fread(VGMData, 0x01, VGMDataLen, hFile);
		fclose(hFile);
		hFile = NULL;
		
ReReadMIDIHead:
		CurPos = 0x00;
		memcpy(&TempLng, &VGMData[CurPos + 0x04], 0x04);
		SwapBytes(&TempLng, 0x04);
		CurPos += 0x08;
		
		memcpy(&MIDIHead, &VGMData[CurPos], sizeof(MID_HEADER));
		SwapBytes(&MIDIHead.shtFormat, 0x02);
		SwapBytes(&MIDIHead.shtTracks, 0x02);
		SwapBytes(&MIDIHead.shtResolution, 0x02);
		if (MIDIHead.shtFormat == 0x00 && MIDIHead.shtTracks > 0x01)
		{
			printf("Bad MIDI Header - Format 0 with multiple tracks!\n");
			VGMData[CurPos + 0x01] = 0x01;
			MIDIHead.shtFormat = 0x01;
		}
		CurPos += TempLng;
		
		if (MIDIHead.shtFormat == 0x01 && MIDIHead.shtTracks > 0x01)
		{
			TempByt = MIDI1to0(VGMDataLen, VGMData, &FileSize, &TempOp);
			free(VGMData);
			if (TempByt)
			{
				printf("MIDI1to0 Conversion failed!\n");
				return false;
			}
			
			VGMData = TempOp;
			VGMDataLen = FileSize;
			MIDIHead.shtTracks = 0x01;
			
			goto ReReadMIDIHead;
		}
		
		TempStr = (char*)malloc(0x100);
/*
#ifndef _DEBUG
		strcpy(TempStr, ApplName);
		*(strrchr(TempStr, '\\') + 0x01) = 0x00;
#else
		strcpy(TempStr, "");
#endif
*/
        GetModuleFileNameA(g_hKpi, TempStr, 0x100);
		*(strrchr(TempStr, '\\') + 0x01) = 0x00;
		strcat(TempStr, MidiPatchFile);
		hInsFile = fopen(TempStr, "rb");
		free(TempStr);
		if (hInsFile == NULL)
		{
			printf("Instrument File %s not found!\n", MidiPatchFile);
			return false;
		}
		
		CurPos = 0x00;
		fseek(hInsFile, CurPos, SEEK_SET);
		
		for (TempSht = 0x00; TempSht < 0x100; TempSht ++)
		{
			fread(InsLine, 0x01, 0x1C, hInsFile);
			memcpy(FMIns + TempSht, InsLine, 0x10);
			FMIns[TempSht].FeedbConnect = InsLine[0x18];
			FMIns[TempSht].Reserved[0x00] = InsLine[0x19];
			memcpy(&FMIns[TempSht].Reserved[0x01], &InsLine[0x16], 0x02);
			memcpy(&FMIns[TempSht].Reserved[0x03], &InsLine[0x1A], 0x02);
		}
		fclose(hInsFile);
		
		/*hInsFile = fopen("SB16_VXD_DrumNote_Win95.bin", "rb");
		memset(DrumInsNote, 0x00, 0x80);
		if (hInsFile != NULL)
		{
			for (TempSht = 0x00; TempSht < 0x80; TempSht ++)
			{
				if (feof(hInsFile))
					break;
				fseek(hInsFile, 0x01, SEEK_CUR);	// skip 1 Byte
				fread(&DrumInsNote[TempSht], 0x01, 0x80, hInsFile);
				if (DrumInsNote[TempSht] < 0x04)
					DrumInsNote[TempSht] = 0x00;
			}
			fclose(hInsFile);
		}
		for (TempSht = 0x00; TempSht < 0x80; TempSht ++)
		{
			if (! DrumInsNote[TempSht])
				DrumInsNote[TempSht] = TempSht ? TempSht : 0x01;
		}*/
		
		VGMTag.strGameNameE[0x00] = 'M';
		VGMTag.strGameNameE[0x01] = 'I';
		VGMTag.strGameNameE[0x02] = 'D';
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = 0x00000100 | MIDIHead.shtFormat;
		VGMHead.lngDataOffset = 0x0016;
		VGMSampleRate = MIDIHead.shtResolution;
		BaseTempo = 500000;
		VGMHead.lngTotalSamples = 0;
		
		if (! OPL_MODE)
			OPL_MODE = 0x03;
		break;
	case FM_CMF:	// CMF File
		fseek(hFile, 0x00, SEEK_SET);
		fread(&CMFHead, 0x01, sizeof(CMF_HEADER), hFile);
		
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL)
			goto OpenErr;
		fseek(hFile, 0x00, SEEK_SET);
		fread(VGMData, 0x01, VGMDataLen, hFile);
		fclose(hFile);
		hFile = NULL;
		
		if (CMFHead.shtVersion == 0x0100)
		{
			CMFHead.shtInstrumentCount &= 0x00FF;
			CMFHead.shtTempo = (unsigned short int)(60.0 *
								CMFHead.shtTickspQuarter / CMFHead.shtTickspSecond + 0.5);
		}
		
		if (CMFHead.shtOffsetTitle)
		{
			TempStr = (char*)&VGMData[CMFHead.shtOffsetTitle];
			TempLng = strlen(TempStr) + 0x01;
			VGMTag.strTrackNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t));
			mbstowcs(VGMTag.strTrackNameE, TempStr, TempLng);
		}
		VGMTag.strGameNameE[0x00] = 'C';
		VGMTag.strGameNameE[0x01] = 'M';
		VGMTag.strGameNameE[0x02] = 'F';
		if (CMFHead.shtOffsetAuthor)
		{
			TempStr = (char*)&VGMData[CMFHead.shtOffsetAuthor];
			TempLng = strlen(TempStr) + 0x01;
			VGMTag.strAuthorNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t));
			mbstowcs(VGMTag.strAuthorNameE, TempStr, TempLng);
		}
		if (CMFHead.shtOffsetComments)
		{
			TempStr = (char*)&VGMData[CMFHead.shtOffsetComments];
			TempLng = strlen(TempStr) + 0x01;
			VGMTag.strNotes = (wchar_t*)malloc(TempLng * sizeof(wchar_t));
			mbstowcs(VGMTag.strNotes, TempStr, TempLng);
		}
		
		CMFInsCount = CMFHead.shtInstrumentCount;
		CMFIns = (CMF_INSTRUMENT*)malloc(CMFInsCount * sizeof(CMF_INSTRUMENT));
		memcpy(CMFIns, &VGMData[CMFHead.shtOffsetInsData], CMFInsCount * sizeof(CMF_INSTRUMENT));
		
		for (TempLng = 0x00; TempLng < CMFHead.shtInstrumentCount; TempLng ++)
		{
			for (TempByt = 0x00; TempByt < 0x02; TempByt ++)
			{
				TempOp = FMIns[TempLng].Operator[TempByt];
				TempOp[0x00] = CMFIns[TempLng].Character[TempByt];
				TempOp[0x01] = CMFIns[TempLng].ScaleLevel[TempByt];
				TempOp[0x02] = CMFIns[TempLng].AttackDecay[TempByt];
				TempOp[0x03] = CMFIns[TempLng].SustnRelease[TempByt];
				TempOp[0x04] = CMFIns[TempLng].WaveSelect[TempByt];
			}
			FMIns[TempLng].FeedbConnect = CMFIns[TempLng].FeedbConnect;
			memcpy(FMIns[TempLng].Reserved, CMFIns[TempLng].Reserved, 0x05);
		}
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = CMFHead.shtVersion;
		VGMHead.lngDataOffset = CMFHead.shtOffsetMusData;
		VGMSampleRate = CMFHead.shtTickspSecond;
		BaseTempo = 1000000;
		VGMHead.lngTotalSamples = 0;
		
		if (! OPL_MODE)
			OPL_MODE = 0x02;
		break;
	case FM_LAA1:
		fseek(hFile, 0x00, SEEK_SET);
		fread(&VGMDataLen, 0x04, 0x01, hFile);
		
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL)
			goto OpenErr;
		fseek(hFile, 0x00, SEEK_SET);
		fread(VGMData, 0x01, VGMDataLen, hFile);
		fclose(hFile);
		hFile = NULL;
		
		CMFHead.shtTickspQuarter = VGMData[0x09];
		CurPos = 0x19;
		for (TempLng = 0x00; TempLng < 0x08; TempLng ++)
		{
			memcpy(InsLine, &VGMData[CurPos], 0x10);
			
			memcpy(FMIns + TempLng, InsLine + 0x03, 0x0A);
			FMIns[TempLng].FeedbConnect = InsLine[0x02];
			FMIns[TempLng].Reserved[0x00] = InsLine[0x0D];
			memcpy(&FMIns[TempLng].Reserved[0x01], &InsLine[0x00], 0x02);
			memcpy(&FMIns[TempLng].Reserved[0x03], &InsLine[0x0E], 0x02);
			
			CurPos += 0x10;
		}
		VGMTag.strGameNameE[0x00] = 'L';
		VGMTag.strGameNameE[0x01] = 'A';
		VGMTag.strGameNameE[0x02] = 'A';
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = 0x00000100;
		VGMHead.lngDataOffset = CurPos - 1;
		VGMSampleRate = CMFHead.shtTickspQuarter;
		BaseTempo = 250000;
		VGMHead.lngTotalSamples = 0;
		// if VGMData[0x0A] = true -> Play once
		
		if (! OPL_MODE)
			OPL_MODE = 0x02;
		break;
	case FM_LAA2:
		fseek(hFile, 0x04, SEEK_SET);
		fread(&FileSize, 0x04, 0x01, hFile);
		SwapBytes(&FileSize, sizeof(unsigned long int));
		TempLng = FileSize + 0x08;
		if (TempLng < VGMDataLen)
			VGMDataLen = TempLng;
		
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL)
			goto OpenErr;
		fseek(hFile, 0x00, SEEK_SET);
		fread(VGMData, 0x01, VGMDataLen, hFile);
		fclose(hFile);
		hFile = NULL;
		
		CurPos = 0x18;
		memcpy(&TempLng, &VGMData[CurPos + 0x04], 0x04);
		SwapBytes(&TempLng, 0x04);
		CurPos += 0x08;
		
		memcpy(&MIDIHead, &VGMData[CurPos], sizeof(MID_HEADER));
		SwapBytes(&MIDIHead.shtFormat, 0x02);
		SwapBytes(&MIDIHead.shtTracks, 0x02);
		SwapBytes(&MIDIHead.shtResolution, 0x02);
		CurPos += TempLng;
		
		// Instruments are loaded via SysEx-Data
		
		VGMTag.strGameNameE[0x00] = 'L';
		VGMTag.strGameNameE[0x01] = 'A';
		VGMTag.strGameNameE[0x02] = 'A';
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = 0x00000200;
		VGMHead.lngDataOffset = CurPos + 0x08;
		VGMSampleRate = MIDIHead.shtResolution;
		BaseTempo = 500000;
		VGMHead.lngTotalSamples = 0;
		
		if (! OPL_MODE)
			OPL_MODE = 0x02;
		break;
	}
	
	if (! VolumeCalc)
	{
		switch(FileMode)
		{
		case FM_MID:
			if (WinFM_Mode)
				VolumeCalc = 0x01;
			else
				VolumeCalc = 0x05;
			break;
		case FM_CMF:
			VolumeCalc = 0x03;
			break;
		case FM_LAA1:
			VolumeCalc = 0x02;
			break;
		case FM_LAA2:
			VolumeCalc = 0x04;
			break;
		}
	}
	if (VolumeCalc == 0x04)
		scumm_create_lookup_table();
	
	if (hFile)
		fclose(hFile);
	return true;

OpenErr:

	if (hFile)
		fclose(hFile);
	return false;
}
#endif
static bool OpenVGMFile(IKpiFile *pFile)
{
	FILE* hInsFile;
	unsigned long int FileSize;
	unsigned long int fccHeader;
    unsigned long int CurPos;
	unsigned char TempByt;
	unsigned short int TempSht;
	unsigned long int TempLng;
	wchar_t* TempStr;
	unsigned char InsLine[0x1C];
	unsigned char* TempOp;
	
    UINT64 qwSize = pFile->GetSize();
	if(qwSize > 128 * 1024 * 1024){//ε«·¬
        return false;
    }
	FileSize = (UINT)qwSize;
    pFile->Seek(0, FILE_BEGIN);
    if(pFile->Read(&fccHeader, sizeof(DWORD)) != sizeof(DWORD)){
        return false;
    }
	switch(fccHeader)
	{
	case FCC_MIDH:
		FileMode = FM_MID;
		break;
	case FCC_CMF:
		FileMode = FM_CMF;
		break;
	case FCC_LAA2:
		FileMode = FM_LAA2;
		break;
	default:
		if(pFile->Read(&TempSht, 0x02) != 0x02){
            goto OpenErr;
        }
		if (TempSht == TCC_LAA1)
		{
			FileMode = FM_LAA1;
			break;
		}
		FileMode = FM_UNDEF;
		break;
	}
	if (FileMode == FM_UNDEF)
		goto OpenErr;
//ͺC³ by Kobarin
	VGMTag.strTrackNameE = _wcsdup(L"");
//	VGMTag.strGameNameE = (wchar_t*)malloc(0x10 * sizeof(wchar_t));
//	wcscpy(VGMTag.strGameNameE, L"    Player");
	VGMTag.strGameNameE = _wcsdup(L"    Player");
	VGMTag.strSystemNameE = _wcsdup(L"PC / MS-DOS");
	VGMTag.strAuthorNameE = _wcsdup(L"");
	VGMTag.strReleaseDate = _wcsdup(L"");
	VGMTag.strCreator = _wcsdup(L"");
	VGMTag.strNotes = _wcsdup(L"");

	VGMDataLen = FileSize;
	
	switch(FileMode)
	{
	case FM_MID:	// MIDI File
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL)
			goto OpenErr;
		pFile->Seek(0x00, SEEK_SET);
		if(pFile->Read(VGMData, VGMDataLen) != VGMDataLen){
            goto OpenErr;
        }
ReReadMIDIHead:
		CurPos = 0x00;
		memcpy(&TempLng, &VGMData[CurPos + 0x04], 0x04);
		SwapBytes(&TempLng, 0x04);
		CurPos += 0x08;
		
		memcpy(&MIDIHead, &VGMData[CurPos], sizeof(MID_HEADER));
		SwapBytes(&MIDIHead.shtFormat, 0x02);
		SwapBytes(&MIDIHead.shtTracks, 0x02);
		SwapBytes(&MIDIHead.shtResolution, 0x02);
		if (MIDIHead.shtFormat == 0x00 && MIDIHead.shtTracks > 0x01)
		{
			printf("Bad MIDI Header - Format 0 with multiple tracks!\n");
			VGMData[CurPos + 0x01] = 0x01;
			MIDIHead.shtFormat = 0x01;
		}
		CurPos += TempLng;
		
		if (MIDIHead.shtFormat == 0x01 && MIDIHead.shtTracks > 0x01)
		{
			TempByt = MIDI1to0(VGMDataLen, VGMData, &FileSize, &TempOp);
			free(VGMData);
			if (TempByt)
			{
				printf("MIDI1to0 Conversion failed!\n");
				return false;
			}
			
			VGMData = TempOp;
			VGMDataLen = FileSize;
			MIDIHead.shtTracks = 0x01;
			
			goto ReReadMIDIHead;
		}
		
		//TempStr = (wchar_t*)malloc(0x100*sizeof(wchar_t));
/*
#ifndef _DEBUG
		strcpy(TempStr, ApplName);
		*(strrchr(TempStr, '\\') + 0x01) = 0x00;
#else
		strcpy(TempStr, "");
#endif
*/
        /*GetModuleFileNameW(g_hKpi, TempStr, 0x100);
        {
            //*(strrchr(TempStr, '\\') + 0x01) = 0x00;
            wchar_t *path_delimiter =wcsrchr(TempStr, L'\\');
            if(path_delimiter){
                path_delimiter[1] = 0;
            }
        }
		wcsncat_s(TempStr, 0x100, MidiPatchFile, _TRUNCATE);
		*/
		//hInsFile = _wfopen(MidiPatchFile, L"rb");
		_wfopen_s(&hInsFile, MidiPatchFile, L"rb");
		//free(TempStr);
		if (hInsFile == NULL)
		{
			//printf("Instrument File %s not found!\n", MidiPatchFile);
			return false;
		}
		
		CurPos = 0x00;
		fseek(hInsFile, CurPos, SEEK_SET);
		
		for (TempSht = 0x00; TempSht < 0x100; TempSht ++)
		{
			fread(InsLine, 0x01, 0x1C, hInsFile);
			memcpy(FMIns + TempSht, InsLine, 0x10);
			FMIns[TempSht].FeedbConnect = InsLine[0x18];
			FMIns[TempSht].Reserved[0x00] = InsLine[0x19];
			memcpy(&FMIns[TempSht].Reserved[0x01], &InsLine[0x16], 0x02);
			memcpy(&FMIns[TempSht].Reserved[0x03], &InsLine[0x1A], 0x02);
		}
		fclose(hInsFile);
		

		
		VGMTag.strGameNameE[0x00] = 'M';
		VGMTag.strGameNameE[0x01] = 'I';
		VGMTag.strGameNameE[0x02] = 'D';
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = 0x00000100 | MIDIHead.shtFormat;
		VGMHead.lngDataOffset = 0x0016;
		VGMSampleRate = MIDIHead.shtResolution;
		BaseTempo = 500000;
		VGMHead.lngTotalSamples = 0;
		
		if (! OPL_MODE)
			OPL_MODE = 0x03;
		break;
	case FM_CMF:	// CMF File
		pFile->Seek(0x00, SEEK_SET);
		if(pFile->Read(&CMFHead, sizeof(CMF_HEADER)) != sizeof(CMF_HEADER)){
            goto OpenErr;
        }
		
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL)
			goto OpenErr;
		pFile->Seek(0x00, SEEK_SET);
		if(pFile->Read(VGMData, VGMDataLen) != VGMDataLen){
            goto OpenErr;
        }
		
		if (CMFHead.shtVersion == 0x0100)
		{
			CMFHead.shtInstrumentCount &= 0x00FF;
			CMFHead.shtTempo = (unsigned short int)(60.0 *
								CMFHead.shtTickspQuarter / CMFHead.shtTickspSecond + 0.5);
		}
		
		if (CMFHead.shtOffsetTitle)
		{
			//TempStr = (char*)&VGMData[CMFHead.shtOffsetTitle];
			//TempLng = strlen(TempStr) + 0x01;
			//VGMTag.strTrackNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t));
			//mbstowcs(VGMTag.strTrackNameE, TempStr, TempLng);
		}
		VGMTag.strGameNameE[0x00] = 'C';
		VGMTag.strGameNameE[0x01] = 'M';
		VGMTag.strGameNameE[0x02] = 'F';
		if (CMFHead.shtOffsetAuthor)
		{
			//TempStr = (char*)&VGMData[CMFHead.shtOffsetAuthor];
			//TempLng = strlen(TempStr) + 0x01;
			//VGMTag.strAuthorNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t));
			//mbstowcs(VGMTag.strAuthorNameE, TempStr, TempLng);
		}
		if (CMFHead.shtOffsetComments)
		{
			//TempStr = (char*)&VGMData[CMFHead.shtOffsetComments];
			//TempLng = strlen(TempStr) + 0x01;
			//VGMTag.strNotes = (wchar_t*)malloc(TempLng * sizeof(wchar_t));
			//mbstowcs(VGMTag.strNotes, TempStr, TempLng);
		}
		
		CMFInsCount = CMFHead.shtInstrumentCount;
		CMFIns = (CMF_INSTRUMENT*)malloc(CMFInsCount * sizeof(CMF_INSTRUMENT));
		memcpy(CMFIns, &VGMData[CMFHead.shtOffsetInsData], CMFInsCount * sizeof(CMF_INSTRUMENT));
		
		for (TempLng = 0x00; TempLng < CMFHead.shtInstrumentCount; TempLng ++)
		{
			for (TempByt = 0x00; TempByt < 0x02; TempByt ++)
			{
				TempOp = FMIns[TempLng].Operator[TempByt];
				TempOp[0x00] = CMFIns[TempLng].Character[TempByt];
				TempOp[0x01] = CMFIns[TempLng].ScaleLevel[TempByt];
				TempOp[0x02] = CMFIns[TempLng].AttackDecay[TempByt];
				TempOp[0x03] = CMFIns[TempLng].SustnRelease[TempByt];
				TempOp[0x04] = CMFIns[TempLng].WaveSelect[TempByt];
			}
			FMIns[TempLng].FeedbConnect = CMFIns[TempLng].FeedbConnect;
			memcpy(FMIns[TempLng].Reserved, CMFIns[TempLng].Reserved, 0x05);
		}
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = CMFHead.shtVersion;
		VGMHead.lngDataOffset = CMFHead.shtOffsetMusData;
		VGMSampleRate = CMFHead.shtTickspSecond;
		BaseTempo = 1000000;
		VGMHead.lngTotalSamples = 0;
		
		if (! OPL_MODE)
			OPL_MODE = 0x02;
		break;
	case FM_LAA1:
		pFile->Seek(0x00, SEEK_SET);
		if(pFile->Read(&VGMDataLen, 0x04) != 0x04){
            goto OpenErr;
        }
        if(VGMDataLen > FileSize){
            goto OpenErr;
        }
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL){
			goto OpenErr;
        }
		pFile->Seek(0x00, SEEK_SET);
		if(pFile->Read(VGMData, VGMDataLen) != VGMDataLen){
            goto OpenErr;
        }
		
		CMFHead.shtTickspQuarter = VGMData[0x09];
		CurPos = 0x19;
		for (TempLng = 0x00; TempLng < 0x08; TempLng ++)
		{
			memcpy(InsLine, &VGMData[CurPos], 0x10);
			
			memcpy(FMIns + TempLng, InsLine + 0x03, 0x0A);
			FMIns[TempLng].FeedbConnect = InsLine[0x02];
			FMIns[TempLng].Reserved[0x00] = InsLine[0x0D];
			memcpy(&FMIns[TempLng].Reserved[0x01], &InsLine[0x00], 0x02);
			memcpy(&FMIns[TempLng].Reserved[0x03], &InsLine[0x0E], 0x02);
			
			CurPos += 0x10;
		}
		VGMTag.strGameNameE[0x00] = 'L';
		VGMTag.strGameNameE[0x01] = 'A';
		VGMTag.strGameNameE[0x02] = 'A';
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = 0x00000100;
		VGMHead.lngDataOffset = CurPos - 1;
		VGMSampleRate = CMFHead.shtTickspQuarter;
		BaseTempo = 250000;
		VGMHead.lngTotalSamples = 0;
		// if VGMData[0x0A] = true -> Play once
		
		if (! OPL_MODE)
			OPL_MODE = 0x02;
		break;
	case FM_LAA2:
		pFile->Seek(0x04, SEEK_SET);
        if(pFile->Read(&FileSize, 0x04) != 0x04){
            goto OpenErr;
        }
		SwapBytes(&FileSize, sizeof(unsigned long int));
		TempLng = FileSize + 0x08;
		if (TempLng < VGMDataLen)
			VGMDataLen = TempLng;
		// Read Data
		VGMData = (unsigned char*)malloc(VGMDataLen);
		if (VGMData == NULL)
			goto OpenErr;
		pFile->Seek(0x00, SEEK_SET);
		if(pFile->Read(VGMData, VGMDataLen) != VGMDataLen){
            goto OpenErr;
        }
		
		CurPos = 0x18;
		memcpy(&TempLng, &VGMData[CurPos + 0x04], 0x04);
		SwapBytes(&TempLng, 0x04);
		CurPos += 0x08;
		
		memcpy(&MIDIHead, &VGMData[CurPos], sizeof(MID_HEADER));
		SwapBytes(&MIDIHead.shtFormat, 0x02);
		SwapBytes(&MIDIHead.shtTracks, 0x02);
		SwapBytes(&MIDIHead.shtResolution, 0x02);
		CurPos += TempLng;
		
		// Instruments are loaded via SysEx-Data
		
		VGMTag.strGameNameE[0x00] = 'L';
		VGMTag.strGameNameE[0x01] = 'A';
		VGMTag.strGameNameE[0x02] = 'A';
		
		memset(&VGMHead, 0x00, sizeof(VGM_HEADER));
		VGMHead.lngEOFOffset = VGMDataLen;
		VGMHead.lngVersion = 0x00000200;
		VGMHead.lngDataOffset = CurPos + 0x08;
		VGMSampleRate = MIDIHead.shtResolution;
		BaseTempo = 500000;
		VGMHead.lngTotalSamples = 0;
		
		if (! OPL_MODE)
			OPL_MODE = 0x02;
		break;
	}
	
	if (! VolumeCalc)
	{
		switch(FileMode)
		{
		case FM_MID:
			if (WinFM_Mode)
				VolumeCalc = 0x01;
			else
				VolumeCalc = 0x05;
			break;
		case FM_CMF:
			VolumeCalc = 0x03;
			break;
		case FM_LAA1:
			VolumeCalc = 0x02;
			break;
		case FM_LAA2:
			VolumeCalc = 0x04;
			break;
		}
	}
	if (VolumeCalc == 0x04)
		scumm_create_lookup_table();
	
	return true;

OpenErr:

	return false;
}

static void SwapBytes(void* Buffer, unsigned long int Bytes)
{
	// Used to convert Little Endian to Big Endian
	char* TempBuf;
	unsigned long int PosA;
	unsigned long int PosB;
	char TempByt;
	
	TempBuf = (char*)Buffer;
	PosA = 0x00;
	PosB = Bytes;
	do
	{
		PosB --;
		TempByt = TempBuf[PosB];
		TempBuf[PosB] = TempBuf[PosA];
		TempBuf[PosA] = TempByt;
		PosA ++;
	} while(PosA < PosB);
	
	return;
}

static void scumm_create_lookup_table(void)
{
	int i, j;
	int sum;
	
	for (i = 0; i < 64; i++)
	{
		sum = i;
		for (j = 0; j < 32; j++)
		{
			scumm_lookup_table[i][j] = sum >> 5;
			sum += i;
		}
	}
	for (i = 0; i < 64; i++)
		scumm_lookup_table[i][0] = 0;
}

static int scumm_lookup_volume(int a, int b)
{
	if (b == 0)
		return 0;

	if (b == 31)
		return a;

	if (a < -63 || a > 63) {
		return b * (a + 1) >> 5;
	}

	if (b < 0) {
		if (a < 0) {
			return scumm_lookup_table[-a][-b];
		} else {
			return -scumm_lookup_table[a][-b];
		}
	} else {
		if (a < 0) {
			return -scumm_lookup_table[-a][b];
		} else {
			return scumm_lookup_table[a][b];
		}
	}
}

/*static wchar_t* ReadWStrFromFile(FILE* hFile, unsigned long int* FilePos,
								 unsigned long int EOFPos)
{
	unsigned long int CurPos;
	wchar_t* TextStr;
	wchar_t* TempStr;
	unsigned long int StrLen;
	
	CurPos = *FilePos;
	TextStr = (wchar_t*)malloc((EOFPos - CurPos));
	if (TextStr == NULL)
		return NULL;
	
	fseek(hFile, CurPos, SEEK_SET);
	TempStr = TextStr;
	StrLen = 0x00;
	do
	{
		fread(TempStr, sizeof(wchar_t), 0x01, hFile);
		TempStr ++;
		CurPos += 0x02;
		StrLen ++;
		if (CurPos >= EOFPos)
			break;
	} while(*(TempStr - 1));
	
	TextStr = (wchar_t*)realloc(TextStr, StrLen * sizeof(wchar_t));
	*FilePos = CurPos;
	
	return TextStr;
}*/

static void ShowVGMTag(void)
{
	printf("Track Title:\t%ls\n", VGMTag.strTrackNameE);
	printf("Game Name:\t%ls\n", VGMTag.strGameNameE);
	printf("System:\t\t%ls\n", VGMTag.strSystemNameE);
	printf("Composer:\t%ls\n", VGMTag.strAuthorNameE);
	printf("Release:\t%ls\n", VGMTag.strReleaseDate);
	printf("Version:\t%lu.%02lX\n", VGMHead.lngVersion >> 8, VGMHead.lngVersion & 0xFF);
	printf("VGM by:\t\t%ls\n", VGMTag.strCreator);
	printf("Notes:\t\t%ls\n", VGMTag.strNotes);
	printf("\n");
	
	printf("Used chips:\t");
	printf("%hux ", OPL_CHIPS);
	switch(OPL_MODE)
	{
	case 0x01:
		printf("OPL 1 (YM3526)");
		break;
	case 0x02:
		printf("OPL 2 (YM3812)");
		break;
	case 0x03:
		printf("OPL 3 (YMF262)");
		break;
	}
	printf("\n");
	printf("\n");
	
	return;
}

static bool GetMixerControl(HMIXEROBJ hmixer, MIXERCONTROL* mxc)
{
	// This function attempts to obtain a mixer control. Returns True if successful.
	MIXERLINECONTROLS mxlc;
	MIXERLINE mxl;
	HGLOBAL hmem;
	MMRESULT RetVal;
	
	mxl.cbStruct = sizeof(MIXERLINE);
	mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
	// Obtain a line corresponding to the component type
	RetVal = mixerGetLineInfo(hmixer, &mxl, MIXER_GETLINEINFOF_COMPONENTTYPE);
	if (RetVal != MMSYSERR_NOERROR)
		return false;
	
	mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
	mxlc.dwLineID = mxl.dwLineID;
	mxlc.dwControlID = MIXERCONTROL_CONTROLTYPE_MUTE;
	mxlc.cControls = 1;
	mxlc.cbmxctrl = sizeof(MIXERCONTROL);
	
	// Allocate a buffer for the control
	hmem = GlobalAlloc(0x40, sizeof(MIXERCONTROL));
	mxlc.pamxctrl = (MIXERCONTROL*)GlobalLock(hmem);
	mxc->cbStruct = sizeof(MIXERCONTROL);
	
	// Get the control
	RetVal = mixerGetLineControls(hmixer, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
	if (RetVal != MMSYSERR_NOERROR)
	{
		GlobalFree(hmem);
		return false;
	}
	
	// Copy the control into the destination structure
	memcpy(mxc, mxlc.pamxctrl, sizeof(MIXERCONTROL));
	GlobalFree(hmem);
	
	return true;
}

static bool SetMuteControl(HMIXEROBJ hmixer, MIXERCONTROL* mxc, bool mute)
{
	MIXERCONTROLDETAILS mxcd;
	MIXERCONTROLDETAILS_UNSIGNED vol;
	HGLOBAL hmem;
	MMRESULT RetVal;
	
	mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
	mxcd.dwControlID = mxc->dwControlID;
	mxcd.cChannels = 1;
	mxcd.cMultipleItems = 0;
	mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
	
	hmem = GlobalAlloc(0x40, sizeof(MIXERCONTROLDETAILS_UNSIGNED));
	mxcd.paDetails = GlobalLock(hmem);
	vol.dwValue = mute;
	
	memcpy(mxcd.paDetails, &vol, sizeof(MIXERCONTROLDETAILS_UNSIGNED));
	RetVal = mixerSetControlDetails(hmixer, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE);
	GlobalFree(hmem);
	
	if (RetVal != MMSYSERR_NOERROR)
		return false;
	
	return true;
}

signed char sign(double Value)
{
	if (Value > 0.0)
		return 1;
	else if (Value < 0.0)
		return -1;
	else
		return 0;
}

long int Round(double Value)
{
	// Alternative:	(fabs(Value) + 0.5) * sign(Value);
	return (long int)(Value + 0.5 * sign(Value));
}

double RoundSpecial(double Value, double RoundTo)
{
	return (long int)(Value / RoundTo + 0.5 * sign(Value)) * RoundTo;
}

static void PrintMinSec(unsigned long int SamplePos)
{
	double TimeSec;
	unsigned short int TimeMin;
	
	TimeSec = (double)RoundSpecial(SamplePos / (double)SampleRate, 0.01);
	//TimeSec = (double)SamplePos / (double)SampleRate;
	TimeMin = (unsigned short int)(TimeSec / 60);
	TimeSec -= TimeMin * 60;
	printf("%02hu:%05.2f", TimeMin, TimeSec);
	
	return;
}

static void RestartPlaying(void)
{
	//bool OldPThread;
	unsigned char CurChip;
	unsigned char CurChn;
	unsigned char CurOp;
	unsigned char TempByt;
	FM_CHN* TempFM;
	MIDI_CHN* TempMid;
	
/*	OldPThread = PauseThread;
	if (ThreadPauseEnable)
	{
		ThreadPauseConfrm = false;
		PauseThread = true;
		while(! ThreadPauseConfrm)
			Sleep(1);	// Wait until the Thread is finished
	}*/
	Interpreting = true;	// Avoid any Thread-Call
	
	VGMPos = VGMHead.lngDataOffset;
	VGMSmplPos = 0;
	VGMSmplPlayed = 0;
	RecalcPbTime = false;
	SoftStop = false;
	//SoftEnd = false;
	
	// !! Warning !!
	// Waveform Select Enable at 0x101 doesn't do anything on my SoundBlaster 16
	// Doing this on my Hoontech SoundTrack causes the FM Synth to stop producing any sound
	if (! FMPort)
	{
		switch(OPL_MODE)
		{
		case 0x01:
			for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
			{
				device_reset_ym3526(CurChip);
			}
			break;
		case 0x02:
			for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
			{
				device_reset_ym3812(CurChip);
			}
			break;
		case 0x03:
			for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
			{
				device_reset_ymf262(CurChip);
				
				//OPL_Write(CurChip << 1, 0x105, 0x01 << 0);	// Enable OPL3 Mode
			}
			break;
		}
	}
	
	for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
	{
		if (OPL_MODE == 0x03)
		{
			TempByt = CurChip << 1;
			OPL_Write(TempByt, 0x105, 0x01);	// Enable OPL3 Mode (even to support Dual OPL2)
			OPL_Write(TempByt, 0x104, 0x00);	// 4-Op-Mode Disable
		}
		else
		{
			TempByt = CurChip << 0;
			//OPL_Write(TempByt, 0x105, 0x00);	// Disable OPL3 Mode
		}
		
		OPL_Write(TempByt, 0x01, 0x20);	// Wave Select Enable
		OPL_Write(TempByt, 0x02, 0x00);	// Timer 1
		OPL_Write(TempByt, 0x03, 0x00);	// Timer 2
		OPL_Write(TempByt, 0x04, 0x00);	// IRQ mask clear
		OPL_Write(TempByt, 0x08, 0x00);	// Keyboard Split
		OPL_Write(TempByt, 0xBD, 0xC0);	// Set AM / Vibrato Depth
	}
	for (CurChip = 0x00; CurChip < OPL_VCHIPS; CurChip ++)
	{
		FMDrumReg[CurChip] = 0xC0;
		for (CurChn = 0x00; CurChn < 0x09; CurChn ++)
		{
			TempByt = (CurChn / 0x03) * 0x08 + (CurChn % 0x03);
			OPL_Write(CurChip, 0xA0 | CurChn, 0x00);
			OPL_Write(CurChip, 0xB0 | CurChn, 0x00);
			OPL_Write(CurChip, FM_REG_GRP[0x05] | CurChn, 0x00);
			for (CurOp = 0x00; CurOp < 0x02; CurOp ++)
			{
				OPL_Write(CurChip, FM_REG_GRP[0x00] | (TempByt + CurOp * 0x03), 0x00);
				OPL_Write(CurChip, FM_REG_GRP[0x01] | (TempByt + CurOp * 0x03), 0x00);
				OPL_Write(CurChip, FM_REG_GRP[0x02] | (TempByt + CurOp * 0x03), 0x00);
				OPL_Write(CurChip, FM_REG_GRP[0x03] | (TempByt + CurOp * 0x03), 0x00);
				OPL_Write(CurChip, FM_REG_GRP[0x04] | (TempByt + CurOp * 0x03), 0x00);
			}
		}
	}
	
	MidiTrk = 0x0000;
	VGMEnd = false;
	for (CurChip = 0x00; CurChip < OPL_VCHIPS; CurChip ++)
	{
		for (CurChn = 0x00; CurChn < 0x09; CurChn ++)
		{
			TempFM = FMChn + CurChip * 0x09 + CurChn;
			TempFM->NoteMsk = 0x00;
			TempFM->MidCh = 0xFF;
			TempFM->Ins = 0xFF;
			TempFM->NoteLast = 0xFF;
			TempFM->StnFact = 0x0000;
			TempFM->Delay = 0x7FFFFFFF;
		}
	}
	
	MMstTuning = 0x00000000;
	MMstVolume = 0x7F;

    CurPortNum = 0;
    for(int port = 0; port < MAX_PORTNUM; port++){

	for (CurChn = 0x00; CurChn < 0x10; CurChn ++)
	{
		TempMid = MidChn[port] + CurChn;
		switch(FileMode)
		{
		case FM_MID:
		//case FM_LAA2:
			TempMid->IsDrum = (CurChn == 0x09);	// General MIDI: Drum Channel 10
			if (Ch16Drum && CurChn == 0x0F)
				TempMid->IsDrum = true;
			break;
		default:
			TempMid->IsDrum = false;
			break;
		}
		TempMid->BnkSel[0x00] = 0x00;
		TempMid->BnkSel[0x01] = 0x00;
		TempMid->Ins = 0x00;			// Set Instrument to Piano
		TempMid->Pitch = 0x00000000;
		TempMid->Pan = 0x40;			// Set Panorama to Center
		TempMid->ModDepth = 0x00;
		TempMid->ModStep = 0x00;
		TempMid->ModPb = 0x0000;
		TempMid->RPNVal[0x00] = 0x00;
		TempMid->RPNVal[0x01] = 0x00;
		TempMid->PbDepth = 0x02;
		switch(FileMode)
		{
		case FM_MID:	// the default volme of MIDI files is 100
			TempMid->ChnVol[0x00] = 0x64;
		default:		// all others should be set to max (127)
			TempMid->ChnVol[0x00] = 0x7F;
			break;
		}
		TempMid->ChnVol[0x01] = 0x7F;
		TempMid->TuneFine = 0x00;
		TempMid->TuneCoarse = 0x00;
		TempMid->TunePb = 0x00000000;
		TempMid->SustainOn = 0x00;
	}

    }
	NoteHstCount = 0x00;
	CurTempo = 0x00;
	GMMode = DefaultGMMode;
	
	switch(FileMode)
	{
	case FM_LAA1:
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			if (OPL_MODE == 0x03)
				OPL_Write(CurChip << 1, 0x08, 0x40);	// Enable Keyboard Split
			else
				OPL_Write(CurChip << 0, 0x08, 0x40);	// Enable Keyboard Split
		}
		for (CurChn = 0x00; CurChn < 0x08; CurChn ++)
		{
			MidChn[CurPortNum][CurChn].Ins = CurChn;
		}
		break;
	case FM_LAA2:
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			if (OPL_MODE == 0x03)
				OPL_Write(CurChip << 1, 0x08, 0x40);	// Enable Keyboard Split
			else
				OPL_Write(CurChip << 0, 0x08, 0x40);	// Enable Keyboard Split
		}
		for (CurChn = 0x00; CurChn < 0x10; CurChn ++)
		{
			MidChn[CurPortNum][CurChn].Ins = CurChn;
		}
		break;
	}
	
	Interpreting = false;
//	PauseThread = OldPThread;
	
	return;
}

static void StartSkipping(void)
{
	memcpy(OPLRegBak, OPLReg, OPL_VCHIPS * 0x100);
	
	return;
}

static void StopSkipping(void)
{
	unsigned char CurChip;
	unsigned short int Reg;
	unsigned char Data;
	unsigned short int RegBase;
	
	for (CurChip = 0x00; CurChip < OPL_VCHIPS; CurChip ++)
	{
		RegBase = CurChip << 8;
		for (Reg = 0x00; Reg <= 0xFF; Reg ++)
		{
			if (OPLReg[RegBase | Reg] != OPLRegBak[RegBase | Reg])
			{
				Data = OPLReg[RegBase | Reg];
				OPLReg[RegBase | Reg] = ~OPLReg[RegBase | Reg];
				OPL_Write(CurChip, Reg, Data);
			}
		}
	}
	
	return;
}


static inline signed long int SampleVGM2Playback(signed long int SampleVal)
{
	signed __int64 TempQud;
	MIDI_TEMPO* TempTempo;
	unsigned long int TickCount;
	
	TempTempo = MidTempo + CurTempo;
	
	TickCount = SampleVal - TempTempo->BaseTick;
	TempQud = (signed __int64)TickCount * SampleRate * TempTempo->Tempo / 1000000 / VGMSampleRate;
	
	return TempTempo->SmplTime + (signed long int)TempQud;
}

static inline signed long int SamplePlayback2VGM(signed long int SampleVal)
{
	signed __int64 TempQud;
	unsigned long int TickCount;
	MIDI_TEMPO* TempTempo;
	
	TempTempo = MidTempo + CurTempo;
	
	TickCount = SampleVal - TempTempo->SmplTime;
	TempQud = (signed __int64)TickCount * VGMSampleRate * 1000000 / TempTempo->Tempo / SampleRate;	
	
	return TempTempo->BaseTick + (signed long int)TempQud;
}
static inline void VGMLog_CmdWrite(unsigned char Cmd, unsigned char Reg, unsigned char Data)
{
	unsigned long int DelayDiff;
	unsigned short int WrtDly;
	
	//DelayDiff = PlayingTime - LastVgmDelay;
	DelayDiff = VGMSmplPlayed - LastVgmDelay;
	while(DelayDiff)
	{
		if (DelayDiff > 0xFFFF)
			WrtDly = 0xFFFF;
		else
			WrtDly = (unsigned short int)DelayDiff;
		fputc(0x61, hFileVGM);
		fwrite(&WrtDly, 0x02, 0x01, hFileVGM);
		DelayDiff -= WrtDly;
	}
	LastVgmDelay = VGMSmplPlayed;	//PlayingTime;
	
	fputc(Cmd, hFileVGM);
	if (Cmd == 0x66)
		return;
	fputc(Reg, hFileVGM);
	fputc(Data, hFileVGM);
	
	return;
}

static inline void OPL_Write(unsigned char ChipID, unsigned short int Register,
							 unsigned char Data)
{
	if (ChipID >= OPL_VCHIPS)
	{
		if (ErrMsg && ! SkipMode)
			printf("Illegal Chip Write!\n");
		return;
	}
	
	unsigned char RegSet;
	unsigned short int RegAddr;
	
	RegAddr = (ChipID << 8) | Register;
	RegAddr %= OPL_VCHIPS << 8;
	if (FMPort)
	{
		//if (Data == OPLReg[RegAddr] && ! OPLRegForce[RegAddr])
		//	return;
		OPLReg[RegAddr] = Data;
		if (SkipMode)
			return;
		OPLRegForce[RegAddr] = 0x00;
		
		Register |= (ChipID & 0x01) << 8;
//		OPL_HW_WriteReg(Register, Data);
		return;
	}
	
	switch(OPL_MODE)
	{
	case 0x01:	// OPL 1
		ym3526_w(ChipID, 0x00, Register & 0xFF);
		ym3526_w(ChipID, 0x01, Data);
		//if (LogVGM)
		//	VGMLog_CmdWrite(0x5B + ChipID * 0x50, Register & 0xFF, Data);
		break;
	case 0x02:	// OPL 2
		ym3812_w(ChipID, 0x00, Register & 0xFF);
		ym3812_w(ChipID, 0x01, Data);
		//if (LogVGM)
		//	VGMLog_CmdWrite(0x5A + ChipID * 0x50, Register & 0xFF, Data);
		break;
	case 0x03:	// OPL 3
		RegSet = Register >> 8;
		RegSet |= (ChipID & 0x01);
		ChipID >>= 1;
		
		ymf262_w(ChipID, 0x00 | (RegSet << 1), Register & 0xFF);
		ymf262_w(ChipID, 0x01 | (RegSet << 1), Data);
		//if (LogVGM)
		//	VGMLog_CmdWrite((0x5E | RegSet) + ChipID * 0x50, Register & 0xFF, Data);
		break;
	}
	
	return;
}


static void InterpretFile(unsigned long int SampleCount)
{
	unsigned long int TempLng;
	
	if (VGMEnd)
	{
		//if (! LogVGM)
			VGMSmplPlayed += SampleCount;
		PlayingTime += SampleCount;
		return;
	}
	if (PausePlay && ! SkipMode)
		return;
	
	//if (Interpreting && SampleCount == 1)
	//	return;
	//while(Interpreting)
	//	Sleep(1);
	
	Interpreting = true;
	if (! SampleCount)
	{
		InterpretMIDI(&SampleCount);
	}
	else
	{
		while(SampleCount)
		{
			TempLng = SampleCount;
			InterpretMIDI(&TempLng);
			VGMSmplPlayed += TempLng;
			PlayingTime += TempLng;
			SampleCount -= TempLng;
		}
	}
	
	/*if (FMPort && FadePlay)
	{
		TempLng = PlayingTime % (SampleRate / 5);
		if (! TempLng)
		{
			RefreshVolume();
		}
	}*/
	
	Interpreting = false;
	
	return;
}

static void JumpToMIDITick(unsigned short int TrackNo, signed long int Tick)
{
	bool OldSM;
	unsigned char Command;
	//unsigned char TempByt;
	//unsigned short int TempSht;
	unsigned long int TempLng;
	signed long int TempSLng;
	unsigned long int DataLen;
	bool FileEnd;
	bool LoopBack;
	MIDI_TEMPO* TempTempo;
	signed long int TrkTickBase;
	
	OldSM = SkipMode;
	
	VGMPos = VGMHead.lngDataOffset;
	VGMSmplPos = 0;
	LastMidCmd = 0x00;
	MidiTrk = 0x0000;
	CurTempo = 0x00;
	FileEnd = false;
	
	TrkTickBase = 0;
	while(! FileEnd && VGMPos < VGMDataLen)
	{
		TempSLng = GetMIDIDelay(&DataLen);
		if (MidiTrk >= TrackNo && (VGMSmplPos - TrkTickBase) + TempSLng > Tick)
			break;
		VGMSmplPos += TempSLng;
		VGMPos += DataLen;
		
		Command = VGMData[VGMPos];
		if (Command & 0x80)
			VGMPos ++;
		else
			Command = LastMidCmd;
		
		switch(Command & 0xF0)
		{
		case 0xF0:
			switch(Command)
			{
			case 0xF0:
				DataLen = GetMIDIDelay(&TempLng);
				VGMPos += TempLng + DataLen;
				break;
			case 0xFF:
				LoopBack = false;
				switch(VGMData[VGMPos])
				{
				case 0x2F:	// End Of File
					FileEnd = true;
					
					if (FileMode == FM_MID || FileMode == FM_LAA2)
					{
						MidiTrk ++;
						if (MidiTrk < MIDIHead.shtTracks)
						{
							VGMPos ++;
							DataLen = GetMIDIDelay(&TempLng);
							VGMPos += TempLng + DataLen;
							
							LoopBack = true;
							VGMPos += 0x08;	// Skip "MTrk" and Track Length
							FileEnd = false;
						}
					}
					TrkTickBase = VGMSmplPos;
					break;
				case 0x51:
					TempLng = 0x00000000;
					memcpy(&TempLng, &VGMData[VGMPos + 0x02], 0x03);
					SwapBytes(&TempLng, 0x03);
					/*VGMSampleRate = (unsigned long int)((unsigned __int64)
									MIDIHead.shtResolution * 1000000 / TempLng);*/
					TempSLng = SampleVGM2Playback(VGMSmplPos);
					
					if (MidTempo[CurTempo].BaseTick != VGMSmplPos)
						CurTempo ++;
					TempTempo = MidTempo + CurTempo;
					TempTempo->BaseTick = VGMSmplPos;
					TempTempo->Tempo = TempLng;
					TempTempo->SmplTime = TempSLng;
					if (CurTempo + 0x01 > TempoCount)
						TempoCount = CurTempo + 0x01;
					break;
				}
				if (! LoopBack)
				{
					VGMPos ++;
					DataLen = GetMIDIDelay(&TempLng);
					VGMPos += TempLng + DataLen;
				}
				break;
			default:
				VGMPos += 0x01;
			}
			break;
		case 0x80:
		case 0x90:
		case 0xA0:
		case 0xB0:
		case 0xE0:
			VGMPos += 0x02;
			break;
		case 0xC0:
		case 0xD0:
			VGMPos += 0x01;
			break;
		}
		if (Command < 0xF0)
			LastMidCmd = Command;
	}
	VGMSmplPlayed = SampleVGM2Playback(TrkTickBase + Tick);
	RecalcPbTime = true;
	
	SkipMode = OldSM;
	
	return;
}

static unsigned long int GetMIDIDelay(unsigned long int* DelayLen)
{
	unsigned long int CurPos;
	unsigned long int DelayVal;
	
	CurPos = VGMPos;
	DelayVal = 0x00;
	do
	{
		DelayVal = (DelayVal << 7) | (VGMData[CurPos] & 0x7F);
	} while(VGMData[CurPos ++] & 0x80);
	
	if (DelayVal > 0x01000000)
	{
		if (ErrMsg && ! SkipMode)
			printf("Unrealistic large delay found!\n");
		DelayVal &= 0xFF;
	}
	if (DelayLen != NULL)
		*DelayLen = CurPos - VGMPos;
	return DelayVal;
}

static unsigned short int MIDINote2FNum(unsigned char Note, unsigned char Channel)
{
	const double CHIP_RATE = 3579545.0 / 72.0;	// ~49716
	double FreqVal;
	signed char BlockVal;
	unsigned short int KeyVal;
	signed long int CurPitch;
	double CurNote;
	MIDI_CHN* TempMid;
	
	TempMid = MidChn[CurPortNum] + Channel;
	CurPitch = MMstTuning + TempMid->TunePb + TempMid->Pitch + TempMid->ModPb;
	
	CurNote = Note + CurPitch / 8192.0;
	FreqVal = 440.0 * pow(2.0, (CurNote - 69) / 12.0);
	//BlockVal = ((signed short int)CurNote / 12) - 1;
	BlockVal = ((signed short int)(CurNote + 6) / 12) - 2;
	if (BlockVal < 0x00)
		BlockVal = 0x00;
	else if (BlockVal > 0x07)
		BlockVal = 0x07;
	//KeyVal = (unsigned short int)(FreqVal * pow(2, 20 - BlockVal) / CHIP_RATE + 0.5);
	KeyVal = (unsigned short int)(FreqVal * (1 << (20 - BlockVal)) / CHIP_RATE + 0.5);
	if (KeyVal > 0x03FF)
		KeyVal = 0x03FF;
	
	return (BlockVal << 10) | KeyVal;	// << (8+2)
}

static void SetVolume(unsigned char ChipID, unsigned char FMChannel, unsigned char Channel,
					  double Volume)
{
	bool RhythmOn;
	unsigned char TempByt;
	//unsigned short int TempSht;
	//unsigned long int TempLng;
	unsigned char OpBase;	// Operator Base
	FM_INSTRUMENT* TempIns;
	unsigned char OpMask;
	unsigned char OpNum;
	signed char OpVol;
	signed char NoteVol;
	double TempDbl;
	unsigned char CurOp;
	bool FixedVol;
	double RoundVal;
	
	RhythmOn = (FMChannel >= 0x06) && (FMDrumReg[ChipID] & 0x20);
	
	// Refresh Total Level (Volume)
	TempIns = FMIns + FMChn[ChipID * 0x09 + FMChannel].Ins;
	OpBase = (FMChannel / 0x03) * 0x08 + (FMChannel % 0x03);
	
	if (! RhythmOn)
	{
		OpMask = 0x03;
	}
	else
	{
		//OpNum = 0x01;
		switch(FileMode)
		{
		case FM_CMF:
			TempByt = Channel - 0x0B;
			break;
		}
		TempByt %= 0x05;	// Risks are no fun
		
		OpMask = FM_RHYTHM_MSK[TempByt];
	}
	
	if (FileMode == FM_MID && ! (TempIns->Reserved[0x02] || TempIns->Reserved[0x03]))
	{
		// Mute undefined voices (especially Drums)
		// They produce weird sounds
		Volume = 0.0f;
	}
	
	Volume *= MMstVolume / (double)0x7F;
	RoundVal = 0.5f;
	if (OPL_MODE == 0x03 && ! WinFM_Mode)	// only OPL3 supports Stereo
	{
		// an intelligent calculation of the volume, that Windows' FM driver does NOT do
		TempByt = MidChn[CurPortNum][Channel].Pan * 3 / 0x80;
		if (TempByt != 0x01)
		{
			//Volume /= 0.70710678f;	// +3db - verified with a YAMAHA DB-50XG
			Volume *= 1.4142136f;	// dbvol = 2 ^ (db/6) -> 3db = 2^(1/2) = sqrt(2)
			RoundVal = 0.0f;
		}
	}
	switch(FileMode)
	{
	case FM_LAA1:
		Volume *= (127.0f / 100.0f);	// 100 is default Volume
		break;
	}
	
	OpNum = 0x00;
	for (CurOp = 0x00; CurOp < 0x02; CurOp ++)
	{
		if (! (OpMask & (1 << CurOp)))
			continue;
		
		FixedVol = false;
		OpVol = TempIns->Operator[OpNum][0x01] & 0x3F;
		
		if (! (OpMask ^ 0x03))
		{
			if (CurOp == 0x00 && ! (TempIns->FeedbConnect & 0x01))
			{
				NoteVol = OpVol;
				FixedVol = true;
				goto SendVolume;
			}
		}
		if (FileMode == FM_LAA2 && VolumeCalc != 0x04)
		{
			OpVol = 0x00;
		}
		else if (FileMode == FM_LAA1 && TempIns->Reserved[0x02])
		{
			OpVol = 0x00;
		}
		
		switch(VolumeCalc)
		{
		case 0x01:	// Volume Calculation like Windows
			// the FM Base-Level is currently ignored
			
			// LookUp Table
			//NoteVol = midi_fm_vol_table[(unsigned char)(Volume * 0x1F)];
			
			// Formula that fits approximately
			if (Volume >= 1.0)
				Volume = 1.0;
			TempDbl = sqrt(sin(0.5 * PI * Volume));
			//TempDbl *= 0.9;	// verified behaviour
			NoteVol = (unsigned char)(TempDbl * 0x3F + RoundVal);
			NoteVol = ~NoteVol & 0x3F;
			break;
		case 0x02:	// Volume Calculation like AdPlug
			OpVol = ~OpVol & 0x3F;
			TempDbl = OpVol / (double)0x3F;	// FM Base-Level
			TempDbl *= TempDbl;	// FM-Level to real Volume
			TempDbl *= Volume;	// Base Level * Note Volume = Final Volume
			TempDbl = sqrt(TempDbl);	// Convert real Volume to FM-Level
			if (TempDbl >= 1.0)
				TempDbl = 1.0;
			NoteVol = (unsigned char)(TempDbl * 0x3F + RoundVal);
			NoteVol = ~NoteVol & 0x3F;
			break;
		case 0x03:	// Volume Calculation SBFMDRV.COM (for CMF MIDIs)
			// every 4 MIDI-Volume steps the FM Volume decreases by 2 steps
			NoteVol = (unsigned char)(Volume * 0x7F + 0x08 * RoundVal) >> 3;
			NoteVol = 0x10 - (NoteVol << 1);
			if (NoteVol < 0x00)
				NoteVol >>= 1;
			NoteVol = OpVol + NoteVol;
			if (NoteVol < 0x00)
				NoteVol = 0x00;
			break;
		case 0x04:	// SCUMM Volume Calculation (thanks to ScummVM)
			NoteVol = (unsigned char)(Volume * 0x3F + RoundVal);
			if (NoteVol > 0x3F)
				NoteVol = 0x3F;
			if (FileMode == FM_LAA1)
			{
				NoteVol = 0x3F - OpVol;
			}
			else if (FileMode == FM_LAA2 && ! VGMData[0x17])
			{
				NoteVol = OpVol;
			}
			else
			{
				OpVol = ~OpVol & 0x3F;
				NoteVol = OpVol + scumm_lookup_table[NoteVol][TempIns->Reserved[OpNum]];
				if (NoteVol > 0x3F)
					NoteVol = 0x3F;
				
				//NoteVol = volume_table[scumm_lookup_table[NoteVol][ChnVol]];
				OpVol = MidChn[CurPortNum][Channel].ChnVol[0x00] >> 2;
				NoteVol = volume_table[scumm_lookup_table[NoteVol][OpVol]];
			}
			NoteVol = ~NoteVol & 0x3F;
 			break;
		case 0x05:	// 100% correct GM volume
			OpVol = ~OpVol & 0x3F;
			// MIDI -> DB:	40 * Log10(MidVol / MaxMidVol)  (formula from GM Dev. Guide)
			// DB -> OPL:	-DB * (4/3)
			TempDbl = 40 * (log(Volume) / log(10.0));	// MIDI -> DB
			TempDbl = -4 * TempDbl / 3;	// DB -> FM
			//TempDbl += OpVol;	// add Operator Volume
			if (TempDbl >= 63.0)
				TempDbl = 63.0;
			else if (TempDbl < 0.0)
				TempDbl = 0.0;
			NoteVol = (unsigned char)(TempDbl + RoundVal);
			break;
		}
		
SendVolume:
		if (FMPort && ! FixedVol)
		{
			TempDbl = pow(2.0, -NoteVol / 8.0);
			
			TempDbl *= VolumeLevel * (MasterVol * MasterVol);	// Control Master Volume
			
			TempDbl = -8.0 * log(TempDbl) / log(2.0);
			if (TempDbl < 0.0)
				NoteVol = 0x00;
			else if (TempDbl >= 64.0)
				NoteVol = 0x3F;
			else
				NoteVol = (unsigned char)TempDbl;
		}
		
		TempByt = NoteVol | (TempIns->Operator[OpNum][0x01] & 0xC0);
		OPL_Write(ChipID, FM_REG_GRP[0x01] | (OpBase + CurOp * 0x03), TempByt);
		
		OpNum ++;
	}
	
	return;
}

static void PlayNote(unsigned char Command, unsigned char Value, unsigned char Velocity)
{
	unsigned char MIDIChn;
	unsigned char CurChip;
	unsigned char CurChn;
	unsigned char NoteH;
	unsigned short int FNum;
	unsigned char TempByt;
	unsigned short int TempSht;
	unsigned long int TempLng;
	signed long int TempLngS;
	unsigned short int MinVal;
	bool NoteOn;
	bool RhythmMode;
	bool RhythmOn;
	bool DrumChn;
	NOTE_HISTORY* TempNH;
	FM_INSTRUMENT* TempIns;
	FM_CHN* TempFM;
	bool HstFlag;
	unsigned char ChFrom;
	unsigned char ChTo;
	unsigned char NoteMask;
	
	if (Command == 0xFF)
	{
		TempNH = NoteHistory + Velocity;
		MIDIChn = TempNH->MIDICh;
	}
	else
	{
		MIDIChn = Command & 0x0F;
	}
	
	RhythmMode = (FMDrumReg[0x00] >> 5) & 0x01;
	RhythmOn = false;
	switch(FileMode)
	{
	case FM_CMF:
		RhythmOn = (MIDIChn >= 0x0B) && RhythmMode;
		break;
	}
	DrumChn = MidChn[CurPortNum][MIDIChn].IsDrum || (MidChn[CurPortNum][MIDIChn].Ins & 0x80);
	if (! RhythmMode)
	{
		ChFrom = 0x00;
		ChTo = 0x09;
		NoteMask = 0x01;
		//if (MIDIChn != 0x09)
		//	return;
	}
	else
	{
		switch(RhythmOn)
		{
		case false:
			ChFrom = 0x00;
			ChTo = 0x06;
			NoteMask = 0x01;
			break;
		case true:
			switch(FileMode)
			{
			case FM_CMF:
				TempByt = MIDIChn - 0x0B;
				break;
			}
			TempByt %= 0x05;	// Risks are no fun
			
			CurChn = FM_RHYTHM_CH[TempByt];
			NoteMask = FM_RHYTHM_MSK[TempByt];
			if (NoteMask ^ 0x03)
				NoteMask = 0x01;
			
			ChFrom = CurChn;
			ChTo = CurChn + 0x01;
			break;
		}
	}
	
	if (Command == 0xFF)
	{
		TempNH = NoteHistory + Velocity;
		FNum = MIDINote2FNum(TempNH->NHeight, MIDIChn);
		NoteOn = TempNH->NoteOn;
		if (NoteOn)
			NoteOn = false;
	}
	else
	{
		TempSht = Value;
		switch(FileMode)
		{
		case FM_MID:
			// I think, these 2 lines are old
			//NoteH = MidChn[MIDIChn].IsDrum ? Value : MidChn[MIDIChn].Ins;
			//TempIns = FMIns + NoteH;
			
			if (DrumChn)
			{
				if (! MidChn[CurPortNum][MIDIChn].IsDrum)
				{
					// setting this to false may cause weird sounding drums if Ins >= 0x80
					// on the other side it enables Drum Finetuning
					if (true)
						TempSht = MidChn[CurPortNum][MIDIChn].Ins;
					else
						TempSht |= 0x80;
				}
				if (! (TempSht & 0x80))
				{
					if (WINFM_DRUM_FREQ[TempSht] == 0xFF)
						break;
					TempSht = WINFM_DRUM_FREQ[TempSht];
				}
				TempSht &= 0x7F;
			}
			break;
		case FM_LAA1:	// All LucasArts music files need this patch
		case FM_LAA2:
			TempSht -= 12;	// Transpose 1 Octave down
			break;
		}
		
		if (TempSht >= 0x8000)
			TempSht = 0x0000;
		else if (TempSht > 0x00FF)
			TempSht = 0x00FF;
		NoteH = (unsigned char)TempSht;
		
		FNum = MIDINote2FNum(NoteH, MIDIChn);
		if ((Command & 0xF0) == 0x80)
			NoteOn = false;
		else
			NoteOn = Velocity ? true : false;
	}
	
	if (NoteOn)
	{
		HstFlag = false;
		if (! RhythmOn)
		{
			for (CurChip = 0x00; CurChip < OPL_VCHIPS; CurChip ++)
			{
				for (CurChn = ChFrom; CurChn < ChTo; CurChn ++)
				{
					TempFM = FMChn + CurChip * 0x09 + CurChn;
					if (! (TempFM->NoteMsk & NoteMask))
					{
						HstFlag = true;
						break;
					}
				}
				if (HstFlag)
					break;
			}
			
			if (! HstFlag)
			{
				// No free channel
				MinVal = 0xFFFF;
				TempLngS = 0x80000000;
				TempNH = NoteHistory;
				for (TempSht = 0x00; TempSht < NoteHstCount; TempSht ++, TempNH ++)
				{
					if (! (TempNH->FMChn >= ChFrom && TempNH->FMChn < ChTo))
						continue;
					
					if (DrumChn && ! WinFM_Mode)
					{
						if (TempNH->MIDICh == MIDIChn && TempNH->Note == Value)
						{
							// stop the drum sound, because it's played again
							// my YAMAHA DB-50XG does this (and MS GS too)
							MinVal = TempSht;
							break;
						}
					}
					
					// like below, I can ignore the MinVal == 0xFFFF case
					if (TempNH->Delay > TempLngS)
					{
						MinVal = TempSht;
						TempLngS = TempNH->Delay;
					}
					else if (TempNH->Delay == TempLngS)
					{
						if (TempNH->Velocity < NoteHistory[MinVal].Velocity)
						{
							MinVal = TempSht;
							TempLngS = TempNH->Delay;
						}
					}
				}
				TempNH = NoteHistory + MinVal;
				CurChip = TempNH->FMChip;
				CurChn = TempNH->FMChn;
				TempNH->FMChip = 0xFF;
				TempNH->FMChn = 0x0F;
				TempSht = FNum & ~0x2000;	// really stop the note
				OPL_Write(CurChip, 0xB0 | CurChn, (TempSht & 0xFF00) >> 8);
				FMChn[CurChip * 0x09 + CurChn].NoteMsk &= ~NoteMask;
				if (ErrMsg && ! SkipMode && ! TempNH->Sustain)
					printf("FM Channel %hu-%hu (MIDI Chn %hu) stopped.\n",
							CurChip, CurChn, TempNH->MIDICh + 1);
				
				if (! TempNH->NoteOn)
				{
					// remove sustained note (to avoid program crashes and slowdowns)
					NoteHistory[MinVal] = NoteHistory[NoteHstCount - 0x01];
					NoteHstCount --;
				}
			}
			else
			{
				// Intelligent Search for the best Free Channel
				if (FileMode != FM_CMF)	// CMF Files sounds better without it
				{
					MinVal = 0xFFFF;
					TempLngS = 0x80000000;
					HstFlag = false;
					for (CurChip = 0x00; CurChip < OPL_VCHIPS; CurChip ++)
					{
						for (CurChn = ChFrom; CurChn < ChTo; CurChn ++)
						{
							TempFM = FMChn + CurChip * 0x09 + CurChn;
							if (DrumChn && ! WinFM_Mode)
							{
								if (TempFM->MidCh == MIDIChn && TempFM->NoteLast == Value)
								{
									// stop the drum sound, because it's played again
									// my YAMAHA DB-50XG does this (and MS GS too)
									MinVal = CurChip * 0x09 + CurChn;
									HstFlag = true;
									break;
								}
							}
							
							if (TempFM->NoteMsk & NoteMask)
								continue;
							// Mininum is 0x80000001, so I can ignore the MinVal == 0xFFFF case
							if (TempFM->Delay > TempLngS)
							{
								MinVal = CurChip * 0x09 + CurChn;
								TempLngS = TempFM->Delay;
							}
						}
						if (HstFlag)
							break;
					}
					CurChip = MinVal / 0x09;
					CurChn = MinVal % 0x09;
					TempNH = NoteHistory;
					for (TempSht = 0x00; TempSht < NoteHstCount; TempSht ++, TempNH ++)
					{
						if (! (TempNH->FMChn >= ChFrom && TempNH->FMChn < ChTo))
							continue;
						
						if (TempNH->FMChip == CurChip && TempNH->FMChn == CurChn)
						{
							TempNH->FMChip = 0xFF;
							TempNH->FMChn = 0x0F;
							TempSht = FNum & ~0x2000;	// really stop the note
							OPL_Write(CurChip, 0xB0 | CurChn, (TempSht & 0xFF00) >> 8);
							FMChn[CurChip * 0x09 + CurChn].NoteMsk &= ~NoteMask;
						}
					}
				}
			}
		}
		else
		{
			CurChip = 0x00;
			CurChn = ChFrom;
		}
		
		TempFM = FMChn + CurChip * 0x09 + CurChn;
		if (NoteHstCount >= 0x60)
		{
			printf("Many hanging Notes - Note Reset\n");
			TempNH = NoteHistory;
			TempLng = 0xFFFFFFFF;
			for (TempSht = 0x00; TempSht < NoteHstCount; TempSht ++, TempNH ++)
			{
				if (TempNH->FMChip == 0xFF && TempNH->FMChn == 0x0F)
				{
					if (TempLng == 0xFFFFFFFF)
						TempLng = TempSht;
				}
				else if (TempLng != 0xFFFFFFFF)
				{
					memmove(NoteHistory + TempLng, TempNH,
							sizeof(NOTE_HISTORY) * (NoteHstCount - TempSht));
					TempByt = (unsigned char)(TempSht - TempLng);
					TempLng = 0xFFFFFFFF;
					NoteHstCount -= TempByt;
					TempSht -= TempByt;
					TempNH -= TempByt;
					if (NoteHstCount < 0x20)
						break;
				}
			}
		}
		TempNH = NoteHistory + NoteHstCount;
		NoteHstCount ++;
		TempIns = FMIns + MidChn[CurPortNum][MIDIChn].Ins;
		TempNH->MIDICh = MIDIChn;
		TempNH->NoteOn = true;
		TempNH->Note = Value;
		TempNH->NHeight = NoteH;
		TempNH->Velocity = Velocity;
		TempNH->FMChip = CurChip;
		TempNH->FMChn = CurChn;
		// Sustain affects new Notes, Sostenuto doesn't
		TempNH->Sustain = MidChn[CurPortNum][MIDIChn].SustainOn & 0x01;
		
		// Sound sustaining
		if (! (TempIns->Operator[0x01][0x00] & 0x20))
		{
			// Evelope Type Sustain
			
			// Decay Rate fades (more [0] or less [F] slowly)
			//TempByt = (TempIns->Operator[0x01][0x02] & 0x0F) >> 0;
			//TempSht = 0x01 + TempByt;
			// Sustain Rate (Level) "cuts" the sound (after a short [0] or long [F] time)
			TempByt = (~TempIns->Operator[0x01][0x03] & 0xF0) >> 4;
			TempSht = 0x01 + TempByt;
			// "cut" means, it fades with Release Rate speed (slow [0] or fast [F])
			TempByt = (TempIns->Operator[0x01][0x03] & 0x0F) >> 0;
			TempSht *= 0x01 + TempByt;
		}
		else
		{
			// Evelope Type Decay
			
			// it starts with Decay Rate and fades
			TempByt = (TempIns->Operator[0x01][0x02] & 0x0F) >> 0;
			TempSht = 0x01 + TempByt;
			// and is held at Sustain Level (loud [0] or silent [F])
			TempByt = (TempIns->Operator[0x01][0x03] & 0xF0) >> 4;
			TempSht += TempByt;
		}
		TempFM->NoteMsk |= NoteMask;
		TempFM->MidCh = MIDIChn;
		TempFM->NoteLast = Value;
		TempFM->StnFact = TempSht;
		TempFM->Delay = 0x00000000;
	}
	else	// if (! NoteOn)
	{
		if (Command == 0xFF)
		{
			MinVal = Velocity;
		}
		else
		{
			HstFlag = false;
			TempNH = NoteHistory;
			MinVal = 0xFFFF;	// Notes that are already off must be cut first
			TempLngS = 0x80000000;
			for (TempSht = 0x00; TempSht < NoteHstCount; TempSht ++, TempNH ++)
			{
				if (TempNH->MIDICh == MIDIChn && TempNH->Note == Value && TempNH->Delay > TempLngS)
				{
					if (! TempNH->NoteOn)
						continue;
					HstFlag = true;
					MinVal = TempSht;
					TempLngS = TempNH->Delay;
				}
			}
			if (! HstFlag)
			{
				if (ErrMsg && ! SkipMode && ! BadNoteOff)
					printf("Note-Stop without Note!\n");
				return;
			}
		}
		TempNH = NoteHistory + MinVal;
		TempNH->NoteOn = false;
		CurChip = TempNH->FMChip;
		CurChn = TempNH->FMChn;
		if (CurChip != 0xFF && CurChn != 0x0F)
			TempFM = FMChn + CurChip * 0x09 + CurChn;
		else
			TempFM = NULL;
		if (TempNH->Sustain)
		{
			if (TempFM != NULL)
			{
				TempFM->StnFact = (unsigned short int)(TempFM->StnFact * 1.2);
			}
			return;
		}
		
		TempNH->Delay = 0x00000000;
		NoteHistory[MinVal] = NoteHistory[NoteHstCount - 0x01];
		NoteHstCount --;
		if (CurChip == 0xFF || CurChn == 0x0F)
		{
			//printf("Note already stopped.\n");
			return;
		}
		
		TempFM = FMChn + CurChip * 0x09 + CurChn;
		TempFM->NoteMsk &= ~NoteMask;
		TempIns = FMIns + TempFM->Ins;
		
		// Sound sustaining: use Release Rate
		TempByt = TempIns->Operator[0x01][0x03] & 0x0F;
		if (TempByt)
		{
			// if Release Rate is 0, nothing happens
			if (TempByt == 0x0F)
			{
				// Sound is cut immediately
				TempFM->StnFact = 0x0000;
				TempFM->Delay = 0x7FFFFFFF;
			}
			else
			{
				TempSht = 0x01 + TempByt * TempByt;
				TempFM->StnFact = TempSht;
				TempFM->Delay = -TempFM->Delay;
			}
		}
		
		if (FileMode == FM_MID && (DrumChn && ! WinFM_Mode))
		{
			TempFM->NoteMsk |= NoteMask << 4;	// special Flag for Drum-Note
			TempFM->StnFact >>= 1;
			return;
		}
	}
	
	if (! RhythmOn)	// Set "Key On"
		FNum |= (unsigned char)NoteOn << 13;	// << (8+5) - NoteOn-Bit
	/*if (CurChip == 0x02)
	{
		if (CurChn <= 0x01)
			FNum = 0;
	}*/
	if (NoteOn)
	{
		if (TempFM->NoteMsk & (NoteMask << 4))
		{
			OPL_Write(CurChip, 0xB0 | CurChn, (FNum & 0xDF00) >> 8);	// Turn Drum Note Off
			TempFM->NoteMsk &= ~(NoteMask << 4);
		}
		
		SetFMInstrument(MIDIChn, CurChip, CurChn);
		
		// Calc Pan Effect
		TempByt = MidChn[CurPortNum][MIDIChn].Pan * 3 / 0x80;
		switch(TempByt)
		{
		case 0x00:	// Left
			TempByt = 0x01;
			break;
		case 0x01:	// Center
			TempByt = 0x03;
			break;
		case 0x02:	// Right
			TempByt = 0x02;
			break;
		}
		TempByt <<= 4;
		
		TempIns = FMIns + TempFM->Ins;
		TempByt |= TempIns->FeedbConnect & ~0x30;
		OPL_Write(CurChip, FM_REG_GRP[0x05] | CurChn, TempByt);
		
		/*if (FileMode == FM_LAA2)
			TempLng = 0x7F * 0x7F * Velocity;
		else*/
		TempLng = MidChn[CurPortNum][MIDIChn].ChnVol[0x00] * MidChn[CurPortNum][MIDIChn].ChnVol[0x01] * Velocity;
		SetVolume(CurChip, CurChn, MIDIChn, TempLng / (double)0x001F417F);
		
		MidChn[CurPortNum][MIDIChn].ModStep = 0x00;
	}
	if (NoteOn || ! RhythmOn)
	{
		OPL_Write(CurChip, 0xA0 | CurChn, (FNum & 0x00FF) >> 0);
		OPL_Write(CurChip, 0xB0 | CurChn, (FNum & 0xFF00) >> 8);
	}
	
	if (RhythmOn)
	{
		switch(FileMode)
		{
		case FM_CMF:
			TempByt = MIDIChn - 0x0B;
			break;
		}
		TempByt = 0x04 - TempByt;
		FMDrumReg[CurChip] &= ~(0x01 << TempByt);
		if (NoteOn)
			OPL_Write(CurChip, 0xBD, FMDrumReg[CurChip]);
		FMDrumReg[CurChip] |= (unsigned char)NoteOn << TempByt;
		OPL_Write(CurChip, 0xBD, FMDrumReg[CurChip]);
	}
	
	return;
}

static void SetControllerAll(unsigned char Command, unsigned char CtrlNum, signed long int Value)
{
	unsigned char TempByt;
	unsigned short int TempSht;
	unsigned long int TempLng;
	double TempSng;
	unsigned char Channel;
	unsigned char CtrlVal;
	unsigned char CurNH;
	NOTE_HISTORY* TempNH;
	FM_INSTRUMENT* TempIns;
	MIDI_CHN* TempMid;
	bool ExecuteAlways;
	
	Channel = Command & 0x0F;
	TempMid = MidChn[CurPortNum] + Channel;
	ExecuteAlways = false;
	switch(Command & 0xF0)
	{
	case 0xB0:	// Controller
		CtrlVal = Value & 0x000000FF;
		
		switch(CtrlNum)
		{
		case 0x01:	// Modulation
			if (WinFM_Mode)
				return;
			if (CtrlVal == TempMid->ModDepth)
				return;
			
			TempMid->ModDepth = CtrlVal;
			
			//if (FMPort)
				return;	// Skip PitchBend for real driver
			
			Command = 0xE0 | (Command & 0x0F);
			break;
		case 0x07:	// Volume
		case 0x0B:	// Expression:
		case 0x87:	// Master Volume
			if (WinFM_Mode)
			{
				if (CtrlNum == 0x0B)
					return;
			}
			
			if (CtrlNum == 0x07)
			{
				if (CtrlVal == TempMid->ChnVol[0x00])
					return;
				
				TempMid->ChnVol[0x00] = CtrlVal;
			}
			else if (CtrlNum == 0x0B)
			{
				if (CtrlVal == TempMid->ChnVol[0x01])
					return;
				
				TempMid->ChnVol[0x01] = CtrlVal;
			}
			
			// Calc Volume Base
			TempLng = TempMid->ChnVol[0x00] * TempMid->ChnVol[0x01];
			break;
		case 0x0A:	// Panorama
			if (CtrlVal == TempMid->Pan)
				return;
			
			TempMid->Pan = CtrlVal;
			
			CtrlVal = TempMid->Pan * 3 / 0x80;
			switch(CtrlVal)
			{
			case 0x00:	// Left
				CtrlVal = 0x01;
				break;
			case 0x01:	// Center
				CtrlVal = 0x03;
				break;
			case 0x02:	// Right
				CtrlVal = 0x02;
				break;
			}
			CtrlVal <<= 4;
			
			TempLng = TempMid->ChnVol[0x00] * TempMid->ChnVol[0x01];
			break;
		case 0x40:	// Damper Pedal (Sustain)
			CtrlVal = (CtrlVal & 0x40) >> 6;
			CtrlVal <<= 0;
			if (CtrlVal == (TempMid->SustainOn & 0x01))
				return;
			
			TempMid->SustainOn &= ~0x01;
			TempMid->SustainOn |= CtrlVal;
			ExecuteAlways = true;
			break;
		case 0x42:	// Sostenuto
			if (WinFM_Mode)
			{
				if (CtrlNum == 0x0B)
					return;
			}
			
			CtrlVal = (CtrlVal & 0x40) >> 6;
			CtrlVal <<= 1;
			if (CtrlVal == (TempMid->SustainOn & 0x02))
				return;
			
			TempMid->SustainOn &= ~0x02;
			TempMid->SustainOn |= CtrlVal;
			ExecuteAlways = true;
			break;
		}
		
		break;
	case 0xE0:	// Pitch Bend
		if (Value == TempMid->Pitch && ! CtrlNum)
			return;
		
		TempMid->Pitch = Value;
		break;
	}
	
	TempNH = NoteHistory;
	for (CurNH = 0x00; CurNH < NoteHstCount; CurNH ++, TempNH ++)
	{
		if (TempNH->MIDICh != Channel)
			continue;
		if ((TempNH->FMChip == 0xFF || TempNH->FMChn == 0x0F) && ! ExecuteAlways)
			continue;
		
		switch(Command & 0xF0)
		{
		case 0xB0:
			switch(CtrlNum)
			{
			case 0x0A:
				TempIns = FMIns + FMChn[TempNH->FMChip * 0x09 + TempNH->FMChn].Ins;
				TempByt = CtrlVal | (TempIns->FeedbConnect & ~0x30);
				OPL_Write(TempNH->FMChip, FM_REG_GRP[0x05] | TempNH->FMChn, TempByt);
				// no break - Volume must be adjusted
				//if (WinFM_Mode)
				//	break;
			case 0x07:
			case 0x0B:
			case 0x87:
				TempSng = (TempLng * TempNH->Velocity) / (double)0x001F417F;
				SetVolume(TempNH->FMChip, TempNH->FMChn, TempNH->MIDICh, TempSng);
				break;
			case 0x40:
			case 0x42:
				TempNH->Sustain &= TempMid->SustainOn;
				TempNH->Sustain |= CtrlVal;
				
				if (WinFM_Mode)	// this really surprised me
					TempNH->NoteOn = false;
				
				if (! TempNH->NoteOn && ! TempNH->Sustain)
				{
					PlayNote(0xFF, 0x00, CurNH);
					CurNH --;	TempNH --;
				}
				break;
			}
			break;
		case 0xE0:
			// TempSht - Note FNum
			TempSht = MIDINote2FNum(TempNH->NHeight, Channel);
			TempSht |= 0x01 << 13;	// << (8+5) - NoteOn-Bit
			
			OPL_Write(TempNH->FMChip, 0xA0 | TempNH->FMChn, (TempSht & 0x00FF) >> 0);
			OPL_Write(TempNH->FMChip, 0xB0 | TempNH->FMChn, (TempSht & 0xFF00) >> 8);
			break;
		}
	}
	
#ifdef _DEBUG
	if (! SkipMode && (Command & 0xF0) == 0xB0 && CtrlNum == 0x40)
	{
		printf("Ch %hu Pedal %hu with %hu Notes.\n", Command & 0x0F, CtrlVal, NoteHstCount);
	}
#endif
	
	return;
}

static void SetFMInstrument(unsigned char Channel, unsigned char FMChip, unsigned char FMChannel)
{
	unsigned char TempByt;
	//unsigned short int TempSht;
	//unsigned long int TempLng;
	unsigned char ChnIns;
	unsigned char OpBase;	// Operator Base
	FM_INSTRUMENT* TempIns;
	FM_CHN* TempFM;
	bool RhythmOn;
	unsigned char OpMask;
	unsigned char CurOp;
	
	ChnIns = MidChn[CurPortNum][Channel].Ins;
	if (ChnIns < 0x80 && FileMode == FM_MID)
	{
		if ((GMMode == 0x01 && MidChn[CurPortNum][Channel].BnkSel[0x00] == 0x7F) ||
			GMMode == 0x03)
			ChnIns = MT32toGM[ChnIns];
	}
	TempFM = FMChn + FMChip * 0x09 + FMChannel;
	if (MidChn[CurPortNum][Channel].IsDrum)
		ChnIns = 0x80 | TempFM->NoteLast;
	
	//if (TempFM->Ins == ChnIns)
	//	return;
	
	TempIns = FMIns + ChnIns;
	OpBase = (FMChannel / 0x03) * 0x08 + (FMChannel % 0x03);
	RhythmOn = (FMChannel >= 0x06) && (FMDrumReg[FMChip] & 0x20);
	if (! RhythmOn)
	{
		OpMask = 0x03;
	}
	else
	{
		switch(FileMode)
		{
		case FM_CMF:
			TempByt = Channel - 0x0B;
			break;
		}
		TempByt %= 0x05;	// Risks are no fun
		
		OpMask = FM_RHYTHM_MSK[TempByt];
		/*if (TempByt == 0x04)	// Hi Hat
		{
			FMChannel = 0x01;	// PLAY.EXE really does this sometimes
			OpBase = 0x01;
		}*/
		
	}
	
	TempByt = 0x00;
	if (OpMask & 0x01)
	{
		// Write Operator 1
		for (CurOp = 0x00; CurOp < 0x05; CurOp ++)
		{
			if (CurOp == 0x01)
				continue;	// Skip Volume
			OPL_Write(FMChip, FM_REG_GRP[CurOp] | (OpBase + 0x00),
						TempIns->Operator[TempByt][CurOp]);
		}
		TempByt ++;
	}
	if (OpMask & 0x02)
	{
		// Write Operator 2
		for (CurOp = 0x00; CurOp < 0x05; CurOp ++)
		{
			if (CurOp == 0x01)
				continue;	// Skip Volume
			OPL_Write(FMChip, FM_REG_GRP[CurOp] | (OpBase + 0x03),
						TempIns->Operator[TempByt][CurOp]);
		}
		TempByt ++;
	}
	
	// includes Pan-Effect - is done by PlayNote
	//OPL_Write(FMChip, FM_REG_GRP[0x05] | FMChannel, TempByt);
	
	TempFM->Ins = ChnIns;
	
	return;
}

static void InterpretEventS(unsigned char Command, unsigned char Value1, unsigned char Value2)
{
	unsigned char Channel;
	unsigned char TempByt;
	unsigned short int TempSht;
	unsigned long int TempLng;
	signed long int PitchVal;
	bool InsChange;
	FM_INSTRUMENT* TempIns;
	FM_CHN* TempFM;
	MIDI_CHN* TempMid;
	unsigned char InsAttr;
	NOTE_HISTORY* TempNH;
	
	Channel = Command & 0x0F;
	switch(Command & 0xF0)
	{
	case 0x80:	// Note Off
	case 0x90:	// Note On
		PlayNote(Command, Value1, Value2);
		if (BadNoteOff)
		{
			if ((Command & 0xF0) == 0x80 || Value2 == 0x00)
				PlayNote(Command, Value1, Value2);
		}
		break;
	case 0xA0:	// Note Aftertouch
		break;
	case 0xB0:	// Controller
		TempMid = MidChn[CurPortNum] + Channel;
		switch(Value1)
		{
		case 0x00:	// Bank Select MSB
			if (WinFM_Mode)
				break;
			
			switch(GMMode)
			{
			case 0x00:	// GM Mode
				// XG-like Drum Channel (sometimes the XG Reset is missing)
				if (Channel != 0x09)
					TempMid->IsDrum = (Value2 == 0x7F);
				break;
			case 0x01:	// GS Mode
				TempMid->BnkSel[0x00] = Value2;
				break;
			case 0x02:	// XG Mode
				TempMid->BnkSel[0x00] = Value2;
				TempMid->IsDrum = (Value2 == 0x7F);
				break;
			case 0x03:	// MT32 Mode
				break;
			}
			break;
		case 0x01:	// Modulation
			SetControllerAll(Command, Value1, Value2);
			break;
		case 0x06:	// Data Entry MSB
			TempSht = (TempMid->RPNVal[0x00] << 8) | (TempMid->RPNVal[0x01] << 0);
			TempMid->RPNVal[0x02] = Value2;
			if (WinFM_Mode)
				break;
			
			switch(TempSht)
			{
			case 0x0000:	// Pitch Bend Sensitivity
				TempMid->PbDepth = TempMid->RPNVal[0x02];
				if (TempMid->PbDepth > 0x20)
					TempMid->PbDepth = 0x20;	// Maximum Range is 24 semitones
				break;
			case 0x0001:	// Fine Tuning
				TempMid->TuneFine = TempMid->RPNVal[0x02] - 0x40;
				
				TempMid->TunePb = TempMid->TuneCoarse * 8192 +
									TempMid->TuneFine * 128;	// (8192 / 64)
				break;
			case 0x0002:	// Coarse Tuning
				TempMid->TuneCoarse = TempMid->RPNVal[0x02] - 0x40;
				if (TempMid->TuneCoarse < -0x18)
					TempMid->TuneCoarse = -0x18;
				else if (TempMid->TuneCoarse > +0x18)
					TempMid->TuneCoarse = +0x18;
				
				TempMid->TunePb = TempMid->TuneCoarse * 8192 +
									TempMid->TuneFine * 128;	// (8192 / 64)
				break;
			}
			break;
		case 0x07:	// Main Volume
			SetControllerAll(Command, Value1, Value2);
			break;
		case 0x0A:	// Pan
			SetControllerAll(Command, Value1, Value2);
			break;
		case 0x0B:	// Expression
			SetControllerAll(Command, Value1, Value2);
			break;
		case 0x20:	// Bank Select LSB
			if (WinFM_Mode)
				break;
			
			switch(GMMode)
			{
			case 0x00:	// GM Mode
				break;
			case 0x01:	// GS Mode
				TempMid->BnkSel[0x01] = Value2;
				break;
			case 0x02:	// XG Mode
				TempMid->BnkSel[0x01] = Value2;
				break;
			case 0x03:	// MT32 Mode
				break;
			}
			break;
		case 0x26:	// Data Entry LSB
			TempMid->RPNVal[0x03] = Value2 & 0x7F;
			
			switch(TempMid->RPNVal[0x00])
			{
			// Change FM Instruments
			// NRPN MSB:	0x40 - Melody Ins
			//				0x41 - Drum Ins
			// NRPN LSB:	Instrument Number
			// Data MSB:	Operator
			// Data LSB:	Value
			case 0xC0:
			case 0xC1:
				TempByt = ((TempMid->RPNVal[0x00] & 0x01) << 7) | (TempMid->RPNVal[0x01] << 0);
				TempIns = FMIns + TempByt;
				
				Command = (TempMid->RPNVal[0x02] & 0x08) >> 3;
				InsChange = true;
				switch(TempMid->RPNVal[0x02])
				{
				// Register 0x20:
				case 0x00:	// Amplitude Modulation (1 bit)
				case 0x08:
					InsAttr = (TempMid->RPNVal[0x03] & 0x40) >> 6;
					TempIns->Operator[Command][0x00] &= ~0x80;
					TempIns->Operator[Command][0x00] |= InsAttr << 7;
					break;
				case 0x01:	// Vibrato (1 bit)
				case 0x09:
					InsAttr = (TempMid->RPNVal[0x03] & 0x40) >> 6;
					TempIns->Operator[Command][0x00] &= ~0x40;
					TempIns->Operator[Command][0x00] |= InsAttr << 6;
					break;
				case 0x02:	// Envelope Generator Type (1 bit)
				case 0x0A:
					InsAttr = (TempMid->RPNVal[0x03] & 0x40) >> 6;
					TempIns->Operator[Command][0x00] &= ~0x20;
					TempIns->Operator[Command][0x00] |= InsAttr << 5;
					break;
				case 0x03:	// Keyboard Scaling Rate (1 bit)
				case 0x0B:
					InsAttr = (TempMid->RPNVal[0x03] & 0x40) >> 6;
					TempIns->Operator[Command][0x00] &= ~0x10;
					TempIns->Operator[Command][0x00] |= InsAttr << 4;
					break;
				case 0x04:	// Modulator Frequency Multiple (4 bits)
				case 0x0C:
					InsAttr = (TempMid->RPNVal[0x03] & 0x0F) >> 0;
					TempIns->Operator[Command][0x00] &= ~0x0F;
					TempIns->Operator[Command][0x00] |= InsAttr << 0;
					break;
				// Register 0x40:
				case 0x10:	// Level Key Scaling (2 bits)
				case 0x18:
					InsAttr = (TempMid->RPNVal[0x03] & 0x60) >> 5;
					TempIns->Operator[Command][0x01] &= ~0xC0;
					TempIns->Operator[Command][0x01] |= InsAttr << 6;
					break;
				case 0x11:	// Total Level (6 bits)
				case 0x19:
					InsAttr = (TempMid->RPNVal[0x03] & 0x7E) >> 1;
					TempIns->Operator[Command][0x01] &= ~0x3F;
					TempIns->Operator[Command][0x01] |= InsAttr << 0;
					break;
				// Register 0x60:
				case 0x20:	// Attack Rate (4 bits)
				case 0x28:
					InsAttr = (TempMid->RPNVal[0x03] & 0x78) >> 3;
					TempIns->Operator[Command][0x02] &= ~0xF0;
					TempIns->Operator[Command][0x02] |= InsAttr << 4;
					break;
				case 0x21:	// Decay Rate (4 bits)
				case 0x29:
					InsAttr = (TempMid->RPNVal[0x03] & 0x78) >> 3;
					TempIns->Operator[Command][0x02] &= ~0x0F;
					TempIns->Operator[Command][0x02] |= InsAttr << 0;
					break;
				// Register 0x80:
				case 0x30:	// Sustain Level (4 bits)
				case 0x38:
					InsAttr = (TempMid->RPNVal[0x03] & 0x78) >> 3;
					TempIns->Operator[Command][0x03] &= ~0xF0;
					TempIns->Operator[Command][0x03] |= InsAttr << 4;
					break;
				case 0x31:	// Release Rate (4 bits)
				case 0x39:
					InsAttr = (TempMid->RPNVal[0x03] & 0x78) >> 3;
					TempIns->Operator[Command][0x03] &= ~0x0F;
					TempIns->Operator[Command][0x03] |= InsAttr << 0;
					break;
				// Register 0xE0:
				case 0x40:	// Waveform Select (3 bits)
				case 0x48:
					InsAttr = (TempMid->RPNVal[0x03] & 0x07) >> 0;
					TempIns->Operator[Command][0x04] &= ~0x07;
					TempIns->Operator[Command][0x04] |= InsAttr << 0;
					break;
				// Register 0xC0:
				case 0x50:	// Feedback (3 bits)
					InsAttr = (TempMid->RPNVal[0x03] & 0x70) >> 4;
					TempIns->FeedbConnect &= ~0x0E;
					TempIns->FeedbConnect |= InsAttr << 1;
					break;
				case 0x51:	// Algorithm (1 bit)
					InsAttr = (TempMid->RPNVal[0x03] & 0x01) >> 0;
					TempIns->FeedbConnect &= ~0x01;
					TempIns->FeedbConnect |= InsAttr << 0;
					break;
				// Register 0xBD:
				case 0x70:	// Amplitude Modulation Depth (1 bits)
					InsAttr = (TempMid->RPNVal[0x03] & 0x40) >> 6;
					for (Command = 0x00; Command < OPL_VCHIPS; Command ++)
					{
						FMDrumReg[Command] &= ~0x80;
						FMDrumReg[Command] |= InsAttr << 7;
						OPL_Write(Command, 0xBD, FMDrumReg[Command]);
					}
					InsChange = false;
					break;
				case 0x71:	// Vibrato Depth (1 bits)
					InsAttr = (TempMid->RPNVal[0x03] & 0x40) >> 6;
					for (Command = 0x00; Command < OPL_VCHIPS; Command ++)
					{
						FMDrumReg[Command] &= ~0x40;
						FMDrumReg[Command] |= InsAttr << 6;
						OPL_Write(Command, 0xBD, FMDrumReg[Command]);
					}
					InsChange = false;
					break;
				}
				if (InsChange)
				{
					for (Command = 0x00; Command < OPL_VCHIPS; Command ++)
					{
						for (Channel = 0x00; Channel < 0x09; Channel ++)
						{
							TempFM = FMChn + Command * 0x09 + Channel;
							
							if (TempFM->Ins == TempByt)
								SetFMInstrument(TempFM->MidCh, Command, Channel);
						}
					}
				}
				break;
			case 0xC6:
				// Windows FM Mode On/Off
				if (TempMid->RPNVal[0x01] == 0x4D)
				{
					// C6 - NRPN MSB = 46, 4D - RPN LSB = 4D (-> can't be used without knowledge)
					// 46 4D - 'F' 'M' for FM-Mode
					// Data LSB must be sent to confirm change
					WinFM_Mode = (TempMid->RPNVal[0x02] >> 6) & 0x01;
					if (TempMid->RPNVal[0x03] == 'V')
						VolumeCalc = 0x01;
					else if (TempMid->RPNVal[0x03] == 'A')
						VolumeCalc = 0x05;
					break;
				}
			}
			break;
		case 0x40:	// Damper Pedal (Sustain)
			SetControllerAll(Command, Value1, Value2);
			break;
		case 0x42:	// Sostenuto
			SetControllerAll(Command, Value1, Value2);
			break;
		case 0x60:	// Data Increment
			break;
		case 0x61:	// Data Decrement
			break;
		case 0x62:	// NRPN LSB
			TempMid->RPNVal[0x01] = 0x80 | Value2;
			break;
		case 0x63:	// NRPN MSB
			TempMid->RPNVal[0x00] = 0x80 | Value2;
			break;
		case 0x64:	// RPN LSB
			TempMid->RPNVal[0x01] = 0x00 | Value2;
			break;
		case 0x65:	// RPN MSB
			TempMid->RPNVal[0x00] = 0x00 | Value2;
			break;
		case 0x6F:	// Loop-Controller
			if (Value2 == 0x00)
			{
				VGMHead.lngLoopOffset = VGMPos | 0x80000000;
				VGMHead.lngLoopSamples = VGMSmplPos;
				
				/*if (LogVGM)
				{
					PitchVal = ftell(hFileVGM) - 0x1C;
					fseek(hFileVGM, 0x1C, SEEK_SET);
					fwrite(&PitchVal, 0x04, 0x01, hFileVGM);
					TempLng = SampleVGM2Playback(VGMHead.lngTotalSamples) - VGMSmplPlayed;
					fwrite(&TempLng, 0x04, 0x01, hFileVGM);
					fseek(hFileVGM, 0x00, SEEK_END);
					
					for (Channel = 0x00; Channel < 0x09 * OPL_VCHIPS; Channel ++)
					{
						FMChn[Channel].Ins = 0xFF;
					}
				}*/
			}
			else
			{
			}
			break;
		case 0x78:	// All Sounds Off
			// TODO
			break;
		case 0x79:	// Reset All Controllers
			if (WinFM_Mode)
				break;
			// According to the GM Spec.:
			// Modulation = 0, Expression = 127, Volume = 100, Pan = 64
			// Sustain/Portamento/Sustenuto/Soft/Legati/Hold 2 = 0
			// reset NRPNs/RPNs (e.g. PB Range = 2)
			// reset Pitch Wheel, Chn Aftertouch, Note Aftertouch
			
			TempMid->Pitch = 0x0000;
			TempMid->ModDepth = 0x00;
			TempMid->ChnVol[0x01] = 0x7F;
			break;
		case 0x7B:	// All Notes Off
		case 0x7C:	// Omni Off (same effect as All Notes Off)
		case 0x7D:	// Omni On (same effect as All Notes Off)
			// Stop all Notes of the current Channel
			for (TempLng = 0x00; TempLng < NoteHstCount; TempLng ++)
			{
				TempNH = NoteHistory + TempLng;
				
				if (TempNH->MIDICh == Channel && TempNH->NoteOn)
				{
					PlayNote(0x80 | TempNH->MIDICh, TempNH->Note, 0x00);
					TempLng --;
				}
			}
			break;
		case 0x7E:	// Mono Mode On
			// 1. All Sounds Off
			// 2. Mono Mode On
			// TODO
			break;
		case 0x7F:	// Poly Mode On
			// 1. All Notes Off
			// 2. Poly Mode On
			// TODO
			break;
		default:
			switch(FileMode)
			{
			case FM_CMF:	// CMF specific controllers
				switch(Value1)
				{
				case 0x63:	// AM/VIB Control (custom for VGFM CMFs)
					for (TempByt = 0x00; TempByt < OPL_VCHIPS; TempByt ++)
					{
						FMDrumReg[TempByt] &= ~0xC0;
						FMDrumReg[TempByt] |= (Value2 & 0x07) << 6;
						OPL_Write(TempByt, 0xBD, FMDrumReg[TempByt]);
					}
					break;
				case 0x66:	// Marker
					break;
				case 0x67:	// Rhythm Mode
					for (TempByt = 0x00; TempByt < OPL_VCHIPS; TempByt ++)
					{
						FMDrumReg[TempByt] &= ~0x20;
						FMDrumReg[TempByt] |= Value2 ? 0x20 : 0x00;
						OPL_Write(TempByt, 0xBD, FMDrumReg[TempByt]);
					}
					break;
				case 0x68:	// Pitch Upward
					PitchVal = +Value2 * 32;	// (* 8192 / 256)
					SetControllerAll(0xE0 | Channel, 0x00, PitchVal);
					break;
				case 0x69:	// Pitch Downward
					PitchVal = -Value2 * 32;	// (* 8192 / 256)
					SetControllerAll(0xE0 | Channel, 0x00, PitchVal);
					break;
				}
				break;
			case FM_LAA1:
			case FM_LAA2:
				switch(Value1)
				{
				case 0x10:	// Pitch Bend Factor
					TempMid->PbDepth = Value2;
					break;
				case 0x11:	// Detune
					TempMid->TunePb = Value2 * 256;	// (8192 / 32)
					break;
				}
				break;
			}
			break;
		}
		break;
	case 0xC0:	// Patch Change
		if (FileMode == FM_CMF)
		{
			if (Value1 >= CMFInsCount)
			{
				//VGMPos += 0x01;
				//break;
				Value1 %= CMFInsCount;
			}
		}
		
		MidChn[CurPortNum][Command & 0x0F].Ins = Value1;
		break;
	case 0xD0:	// Channel Aftertouch
		break;
	case 0xE0:	// Pitch Bend
		PitchVal = (Value1 << 0) | (Value2 << 7);
		PitchVal -= 0x2000;
		PitchVal *= MidChn[CurPortNum][Channel].PbDepth;
		SetControllerAll(Command, 0x00, PitchVal);
		break;
	}
	
	return;
}

static unsigned char GetLAAValueB(unsigned char* Data)
{
	return ((Data[0x00] & 0x0F) << 4) | ((Data[0x01] & 0x0F) << 0);
}

static unsigned short int GetLAAValueS(unsigned char* Data)
{
	return ((Data[0x00] & 0x0F) << 12) | ((Data[0x01] & 0x0F) << 8) |
			((Data[0x02] & 0x0F) << 4) | ((Data[0x03] & 0x0F) << 0);
}

static unsigned long int GetLAAValueL(unsigned char* Data)
{
	return ((Data[0x00] & 0x0F) << 28) | ((Data[0x01] & 0x0F) << 24) |
			((Data[0x02] & 0x0F) << 20) | ((Data[0x03] & 0x0F) << 16) |
			((Data[0x04] & 0x0F) << 12) | ((Data[0x05] & 0x0F) << 8) |
			((Data[0x06] & 0x0F) << 4) | ((Data[0x07] & 0x0F) << 0);
}

static void InterpretEventL(unsigned char Command, unsigned char Value,
							unsigned long int DataLen, unsigned char* Data)
{
	unsigned char Channel;
	unsigned char TempByt;
	unsigned short int TempSht;
	//unsigned long int TempLng;
	signed long int TempSLng;
	signed long int PitchVal;
	FM_INSTRUMENT* TempIns;
	MIDI_CHN* TempMid;
	bool JumpDir;	// true - forward, false - backward
	
	switch(Command)
	{
	case 0xF0:
		if (WinFM_Mode)	// Disable Support for SysEx-Messages
			return;
		
		while(DataLen && Data[0x00] == 0xF0)
		{
			 Data ++;	DataLen --;
		}
		
		// Device Numbers are ignored
		switch(Data[0x00])
		{
		case 0x41:	// Roland ID
			// Data[0x01] == 0x1n - Device Number n
            if (DataLen == 10 && Data[0x02] == 0x42 && Data[0x03] == 0x12 && Data[0x04] == 0x40 && Data[0x05] == 0 && Data[0x06] == 0x04){
            // Master Volume
                MMstVolume = Data[0x07];
                for (Channel = 0x00; Channel < 0x10; Channel++)
                {
                    TempMid = MidChn[CurPortNum] + Channel;
                    SetControllerAll(0xB0 | Channel, 0x87, 0x00);
                }
            }
            else if (Data[0x02] == 0x42 && Data[0x03] == 0x12 && (Data[0x04] & 0x3F) == 0x00)
			{
				// Data[0x05]	Address High
				// Data[0x06]	Address Low
				switch(Data[0x05] & 0x70)
				{
				case 0x00:
					switch(Data[0x06])	
					{
					case 0x7F:	// GS On
						if (! Data[0x07])
							GMMode = 0x01;	// GS On - GS Mode
						else
							GMMode = 0x00;	// GS Off - GM Mode
						break;
					}
					break;
				case 0x10:
					if (GMMode != 0x01 && GMMode != 0x00)
						break;
					
					// Part Order
					// 10 1 2 3 4 5 6 7 8 9 11 12 13 14 15 16
					TempByt = Data[0x05] & 0x0F;
					if (TempByt == 0x00)
						Channel = 0x09;
					else if (TempByt <= 0x09)
						Channel = TempByt - 0x01;
					else //if (TempByt >= 0x0A)
						Channel = TempByt;
					TempMid = MidChn[CurPortNum] + Channel;
					switch(Data[0x06])
					{
					case 0x15:	// Drum Channel
						// Lock Ch 9 in GM Mode
						if (GMMode == 0x01 || (GMMode == 0x00 && Channel != 0x09))
							TempMid->IsDrum = (Data[0x07] > 0x00);
						break;
					}
					break;
				}
			}
			break;
		case 0x43:	// YAMAHA ID
			// Data[0x01] == 0x?n - Device Number n
            switch(Data[0x01] & 0x70)
			{
			case 0x00:	// XG Bulk Dump
				break;
			case 0x10:	// XG Parameter Change
				switch(Data[0x02])	// Model ID
				{
				case 0x4C:	// XG Model ID
					// Data[0x03]	Address High
					// Data[0x04]	Address Mid
					// Data[0x05]	Address Low
					switch(Data[0x03])	// Address High
					{
					case 0x00:	// System
						if (Data[0x04] != 0x00)
							break;
						
						if (Data[0x05] != 0x7E && GMMode != 0x02)
							break;
						switch(Data[0x05])	// Address Low
						{
						case 0x00:	// Master Tune (Address Low 00-03)
							TempSht = ((Data[0x06] & 0x0F) << 12) |
									  ((Data[0x07] & 0x0F) <<  8) |
									  ((Data[0x08] & 0x0F) <<  4) |
									  ((Data[0x09] & 0x0F) <<  0);
							MMstTuning = (TempSht - 0x0400) * 8;	// (8192 / 0x0400)
							for (Channel = 0x00; Channel < 0x10; Channel ++)
							{
								TempMid = MidChn[CurPortNum] + Channel;
								SetControllerAll(0xE0 | Channel, 0x01, TempMid->Pitch);
							}
							break;
						case 0x04:	// Master Volume
							MMstVolume = Data[0x06];
							for (Channel = 0x00; Channel < 0x10; Channel ++)
							{
								TempMid = MidChn[CurPortNum] + Channel;
								SetControllerAll(0xB0 | Channel, 0x87, 0x00);
							}
							break;
						case 0x06:	// Transpose
							PitchVal = Data[0x06] - 0x40;
							if (PitchVal < -0x18)
								PitchVal = -0x18;
							else if (PitchVal > +0x18)
								PitchVal = +0x18;
							break;
						case 0x7D:	// Drum Setup Reset
							TempByt = Data[0x06];	// Reset this Drum Setup
							break;
						case 0x7E:	// XG System On
							if (! Data[0x06])
								GMMode = 0x02;	// XG On - XG Mode
							else
								GMMode = 0x00;	// XG Off - GM Mode
							break;
						case 0x7F:
							if (! Data[0x06])
							{
								// Reset On
								// ...
							}
							break;
						}
						break;
					/*case 0x01:	// Information
						break;
					case 0x02:	// Effect 1
						break;
					case 0x08:	// Multi Part
						break;
					case 0x30:	// Drum Setup 1
						break;
					case 0x31:	// Drum Setup 2
						break;*/
					}
					break;
				case 0x27:	// Master Tuning Model ID
					switch(Data[0x03])
					{
					case 0x30:	// Master Tuning Sub ID
						// Data[0x06]	Master Tuning MSB
						// Data[0x07]	Master Tuning LSB
						TempSht = ((Data[0x06] & 0x7F) << 7) |
								  ((Data[0x07] & 0x7F) << 0);
						MMstTuning = (TempSht - 0x0400) * 8;	// (8192 / 0x0400)
						for (Channel = 0x00; Channel < 0x10; Channel ++)
						{
							TempMid = MidChn[CurPortNum] + Channel;
							SetControllerAll(0xE0 | Channel, 0x01, TempMid->Pitch);
						}
						break;
					}
					break;
				}
				break;
			/*case 0x20:	// XG Dump Request
				break;
			case 0x30:	// XG Parameter Request
				break;*/
			}
			break;
		case 0x7D:	// Lucas Arts Message (FM_LAA2 only)
			if (FileMode != FM_LAA2)
				return;
			
			switch(Data[0x01])
			{
			case 0x00:
				// Information from ScummVM:
				//   (BYTE is in dec, data starts at Data[0x02])
				//
				// Allocate new part.
				// There are 17 bytes of useful information here.
				// Here is what we know about them so far:
				//   BYTE 00: Channel #
				//   BYTE 02: BIT 01(0x01): Part on?(1 = yes)
				//            BIT 02(0x02): Reverb? (1 = yes) [bug #1088045]
				//   BYTE 04: Priority adjustment [guessing]
				//   BYTE 05: Volume(upper 4 bits) [guessing]
				//   BYTE 06: Volume(lower 4 bits) [guessing]
				//   BYTE 07: Pan(upper 4 bits) [bug #1088045]
				//   BYTE 08: Pan(lower 4 bits) [bug #1088045]
				//   BYTE 09: BIT 04(0x08): Percussion?(1 = yes)
				//   BYTE 13: Pitchbend range(upper 4 bits) [bug #1088045]
				//   BYTE 14: Pitchbend range(lower 4 bits) [bug #1088045]
				//   BYTE 15: Program(upper 4 bits)
				//   BYTE 16: Program(lower 4 bits)
				
				Channel = Data[0x02] & 0x0F;
				TempByt = GetLAAValueB(Data + 0x07);
				SetControllerAll(0xB0 | Channel, 0x07, TempByt);
				TempByt = GetLAAValueB(Data + 0x09);
				//SetControllerAll(0xB0 | Channel, 0x0A, TempByt);
				TempByt = GetLAAValueB(Data + 0x0F);
				//MidChn[Channel].PbDepth = TempByt;
				TempByt = GetLAAValueB(Data + 0x11);
				//InterpretEventS(0xC0 | Channel, TempByt, 0x00);
				break;
			case 0x01:	// Shut Down a (Song)
				break;
			case 0x02:	// Song Start
				break;
			case 0x10:	// Adlib Instrument Definition (Song)
				TempByt = Data[0x02];
				//TempByt |= (Data[0x01] & 0x0F) << 4;
				TempIns = &FMIns[TempByt];
				
				TempIns->Operator[0x00][0x00] =	 GetLAAValueB(Data + 0x04);
				TempIns->Operator[0x00][0x01] =	 GetLAAValueB(Data + 0x06) ^ 0x3F;
				TempIns->Operator[0x00][0x02] =	~GetLAAValueB(Data + 0x08);
				TempIns->Operator[0x00][0x03] =	~GetLAAValueB(Data + 0x0A);
				TempIns->Operator[0x00][0x04] =	 GetLAAValueB(Data + 0x0C);
				TempIns->Operator[0x01][0x00] =	 GetLAAValueB(Data + 0x0E);
				TempIns->Operator[0x01][0x01] =	 GetLAAValueB(Data + 0x10) ^ 0x3F;
				TempIns->Operator[0x01][0x02] =	~GetLAAValueB(Data + 0x12);
				TempIns->Operator[0x01][0x03] =	~GetLAAValueB(Data + 0x14);
				TempIns->Operator[0x01][0x04] =	 GetLAAValueB(Data + 0x16);
				TempIns->FeedbConnect =			 GetLAAValueB(Data + 0x18);
				
				TempIns->Reserved[0x00] = (TempIns->Operator[0x00][0x04] >> 2);
				TempIns->Operator[0x00][0x04] &= 0x03;
				TempIns->Reserved[0x01] = (TempIns->Operator[0x01][0x04] >> 2);
				TempIns->Operator[0x01][0x04] &= 0x03;
				break;
			case 0x11:	// Adlib Instrument Definition (Global)
				break;
			case 0x21:	// Parameter Adjust
				break;
			case 0x30:	// Hook - Jump
				// only 4 bit of every Data Byte are used
				// 0x02 - ignored
				// 0x03 - 0x04: Loop Index
				TempByt = GetLAAValueB(Data + 0x03);
				if (TempByt != LAA2_LIdx && ! SoftStop)
					break;	// Skip Coda-Jump
				
				// 0x05 - 0x08: Track ID
				TempSht = GetLAAValueS(Data + 0x05);
				// 0x09 - 0x0C: Measures (Base 1)
				TempSLng = GetLAAValueS(Data + 0x09) - 0x01;
				TempSLng *= VGMSampleRate;
				// 0x0D - 0x10: Ticks
				TempSLng += GetLAAValueS(Data + 0x0D);
				
				if (TempSht >= MIDIHead.shtTracks)
					break;
				
				if (TempSht < MidiTrk || (TempSht == MidiTrk && TempSLng < VGMSmplPos))
					JumpDir = false;
				else
					JumpDir = true;
				JumpToMIDITick(TempSht, TempSLng);
				
				// Jumping forward and Conditional Jumps don't affect the Loop Count
				if (! JumpDir && TempByt == LAA2_LIdx)
				{
					VGMLoop ++;
					if (VGMMaxLoop && VGMLoop >= VGMMaxLoop)
					{
						if (! SoftStop && MidiTrk + 0x01 < MIDIHead.shtTracks)
						{
							SoftStop = true;
							//SoftEnd = true;
						}
						else
						{
							FadePlay = true;
						}
					}
				}
				else if (SoftStop)
				{
					LAA2_LIdx = TempByt;
					SoftStop = false;
					VGMLoop = 0x00;
				}
				
				break;
			case 0x31:	// Hook - Global Transpose
				break;
			case 0x32:	// Hook - Part On/Off
				break;
			case 0x33:	// Hook - Set Volume
				break;
			case 0x34:	// Hook - Set Program
				break;
			case 0x35:	// Hook - Set Transpose
				break;
			case 0x40:	// Marker
				break;
			case 0x50:	// Loop
				break;
			case 0x51:	// End Loop
				break;
			case 0x60:	// Set instrument
				break;
			}
			break;
		case 0x7E:	// Universal Non-Real Time Message
			// Data[0x01] == 0x??	7F - ID of target device, ?n - Device Number n
			// Data[0x02] == 0x09	Sub-ID #1 - General MIDI
			// Data[0x03] == 0x01	Sub-ID #2 - General MIDI On
			if (Data[0x02] == 0x09 && Data[0x03] == 0x01)
			{
				GMMode = 0x00;
				for (Channel = 0x00; Channel < 0x10; Channel ++)
				{
					TempMid = MidChn[CurPortNum] + Channel;
					TempMid->IsDrum = (Channel == 0x09);	// GM Drum Channel (Ch 10)
				}
			}
			/*else if (Data[0x02] == 0x46 && Data[0x03] == 0x4D)
			{
				// 46 4D - 'F' 'M' for FM-Mode
				WinFM_Mode = true;
			}*/
			break;
		case 0x7F:	// Universal Real Time Message
			// Data[0x01] == 0x??	7F - ID of target device, ?n - Device Number n
			// Data[0x02] == 0x04	Sub-ID #1 - Device Control Message
			// Data[0x03] == 0x01	Sub-ID #2 - Master Volume
			if (Data[0x02] == 0x04 && Data[0x03] == 0x01)
			{
				// Data[0x04]	Volume LSB (ignored)
				// Data[0x05]	Volume MSB
				MMstVolume = Data[0x05];
				for (Channel = 0x00; Channel < 0x10; Channel ++)
				{
					TempMid = MidChn[CurPortNum] + Channel;
					SetControllerAll(0xB0 | Channel, 0x87, 0x00);
				}
			}
			break;
		}
		break;
	}
	
	return;
}
#if 0
static void strcpynspc(char *strDestination, const char *strSource)
{
	const char* CurSrc;
	char* CurDst;
	
	CurSrc = strSource;
	CurDst = strDestination;
	do
	{
		*CurDst = *CurSrc;
		if (*CurSrc > 0x20)	// ignore Spaces and Control Codes;
			CurDst ++;
		CurSrc ++;
	} while(*CurSrc);
	
	return;
}
#else
static void strcpynspc(char* strDestination, const char* strSource)
{
	const char* CurSrc;
	char* CurDst;

	CurSrc = strSource;
	CurDst = strDestination;
	while(*CurSrc)
	{
		while (*CurSrc <= 0x20){	// ignore Spaces and Control Codes;
			if(!*CurSrc++){
				*CurDst = 0;
				return;
			}
		}
		*CurDst++ = *CurSrc++;
	}
	*CurDst = 0;
	return;
}
#endif

static void InterpretMIDI(unsigned long int* SampleCount)
{
	signed long int SmplPlayed;
	unsigned char Command;
	unsigned char Channel;
	unsigned char TempByt;
	//unsigned short int TempSht;
	unsigned long int TempLng;
	signed long int TempSLng;
	double SmplDivdr;
	unsigned long int DataLen;
	signed long int PitchVal;
	bool FileEnd;
	bool LoopBack;
	MIDI_TEMPO* TempTempo;
	FM_CHN* TempFM;
	MIDI_CHN* TempMid;
	NOTE_HISTORY* TempNH;
	char TempStr[0x10];
	
	if (! *SampleCount)
	{
		MidiTrk = 0x0000;
		//TempLng = VGMPos;
		SmplPlayed = VGMSmplPos;
		VGMPos = VGMHead.lngDataOffset;
		FileEnd = false;
		TempoCount = 0x01;
		
		TempTempo = (MIDI_TEMPO*)NoteHistory;	// Used instead of an extra buffer
		TempTempo->BaseTick = 0;
		TempTempo->Tempo = BaseTempo;
		TempTempo->SmplTime = 0;
		while(! FileEnd && VGMPos < VGMDataLen)
		{
			VGMSmplPos += GetMIDIDelay(&DataLen);
			VGMPos += DataLen;
			
			Command = VGMData[VGMPos];
			if (Command & 0x80)
				VGMPos ++;
			else
				Command = LastMidCmd;
			Channel = Command & 0x0F;
			
			switch(Command & 0xF0)
			{
			case 0xF0:
				switch(Command)
				{
				case 0xF0:
					DataLen = GetMIDIDelay(&TempLng);
					VGMPos += TempLng + DataLen;
					break;
				case 0xFF:
					LoopBack = false;
					switch(VGMData[VGMPos])
					{
					case 0x2F:	// End Of File
						FileEnd = true;
						
						if (FileMode == FM_MID || FileMode == FM_LAA2)
						{
							MidiTrk ++;
							if (MidiTrk < MIDIHead.shtTracks)
							{
								VGMPos ++;
								DataLen = GetMIDIDelay(&TempLng);
								VGMPos += TempLng + DataLen;
								
								LoopBack = true;
								VGMPos += 0x08;	// Skip "MTrk" and Track Length
								FileEnd = false;
							}
						}
						break;
					case 0x51:
						TempLng = VGMSmplPos - TempTempo->BaseTick;
						//if (! TempLng)
						//	TempoCount --;
						TempLng = (unsigned long int)((unsigned __int64)TempLng * SampleRate *
									TempTempo->Tempo / 1000000 / VGMSampleRate);
						
						TempTempo->BaseTick = VGMSmplPos;
						TempTempo->SmplTime += TempLng;
						
						TempLng = 0x00000000;
						memcpy(&TempLng, &VGMData[VGMPos + 0x02], 0x03);
						SwapBytes(&TempLng, 0x03);
						TempTempo->Tempo = TempLng;
						
						TempoCount ++;
						break;
					}
					if (! LoopBack)
					{
						VGMPos ++;
						DataLen = GetMIDIDelay(&TempLng);
						VGMPos += TempLng + DataLen;
					}
					break;
				default:
					VGMPos += 0x01;
				}
				break;
			case 0x80:
			case 0x90:
			case 0xA0:
			//case 0xB0:
			case 0xE0:
				VGMPos += 0x02;
				break;
            case 0xB0:
                if(VGMData[VGMPos] == 111){//[v|Cg
                    g_bIsLoop = TRUE;    
                }
                VGMPos += 0x02;
                break;
			case 0xC0:
			case 0xD0:
				VGMPos += 0x01;
				break;
			}
			if (Command < 0xF0)
				LastMidCmd = Command;
		}
		VGMHead.lngLoopOffset = 0x00000000;
		VGMHead.lngTotalSamples = VGMSmplPos;
		VGMHead.lngLoopSamples = VGMHead.lngTotalSamples;
		VGMHead.lngEOFOffset = VGMPos;
		
		TempLng = VGMSmplPos - TempTempo->BaseTick;
		TempLng = (unsigned long int)((unsigned __int64)TempLng * SampleRate *
					TempTempo->Tempo / 1000000 / VGMSampleRate);
		TotalPbSamples = TempTempo->SmplTime + TempLng;
		
		MidTempo = (MIDI_TEMPO*)malloc(TempoCount * sizeof(MIDI_TEMPO));
		CurTempo = 0x00;
		TempTempo = MidTempo + CurTempo;
		TempTempo->BaseTick = 0;
		TempTempo->Tempo = BaseTempo;
		TempTempo->SmplTime = 0;
		TempoCount = 0x00;
		
		VGMPos = VGMHead.lngDataOffset;
		VGMSmplPos = SmplPlayed;
		MidiTrk = 0x0000;
		return;
	}
	
	if (*SampleCount > SampleRate)
		*SampleCount = SampleRate;	// dirty hack - needs bugfix
	SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + *SampleCount);
	while(true)
	{
		if (VGMPos >= VGMDataLen)
		{
			VGMEnd = true;
			break;
		}
		
		TempLng = GetMIDIDelay(&DataLen);
		if (VGMSmplPos + (signed long int)TempLng > SmplPlayed)
			break;
		VGMSmplPos += TempLng;
		VGMPos += DataLen;
		
		Command = VGMData[VGMPos];
		if (Command & 0x80)
			VGMPos ++;
		else
			Command = LastMidCmd;
		//Channel = Command & 0x0F;
		
		/*if (FMDrumReg[0x00] & 0x20)
		{
			if (Channel < 0x0B)
			{
				CurChip = Channel / 0x06;
				Channel = Channel % 0x06;
			}
			else
			{
				Channel -= 0x0B;
				CurChip = Channel / 0x03;
				Channel = Channel % 0x03;
				if (CurChip == 0x01 && Channel == 0x00)
					Channel = 0x02;
				Channel += 0x06;
#ifdef CMF_ONE_CHIP_DRUMS
				// Map all drums to one chip
				CurChip = 0x00;
#endif
			}
		}
		else
		{
			CurChip = Channel / 0x09;
			Channel = Channel % 0x09;
		}
		
		RhythmOn = (Channel >= 0x06) && (FMDrumReg[CurChip] & 0x20);*/
		switch(Command & 0xF0)
		{
		case 0xF0:	// SysEx and Meta Events
			switch(Command)
			{
			case 0xF0:	// System Exclusive Data
				DataLen = GetMIDIDelay(&TempLng);
				VGMPos += TempLng;
				InterpretEventL(Command, 0x00, DataLen, &VGMData[VGMPos]);
				if (! RecalcPbTime)
				{
					VGMPos += DataLen;
				}
				else
				{
					for (TempLng = 0x00; TempLng < NoteHstCount; TempLng ++)
					{
						TempNH = NoteHistory + TempLng;
						
						if (TempNH->NoteOn)
						{
							PlayNote(0x80 | TempNH->MIDICh, TempNH->Note, 0x00);
							TempLng --;
						}
					}
				}
				break;
			case 0xFF:	// Meta Events
				//InterpretEventL(Command, VGMData[VGMPos + 0x00],
				//				DataLen, &VGMData[VGMPos + 0x01]);
				LoopBack = false;
				switch(VGMData[VGMPos + 0x00])
				{
                case 0x21: //port
                    CurPortNum = VGMData[VGMPos + 0x02];
                    if (CurPortNum >= MAX_PORTNUM) {
                        CurPortNum = MAX_PORTNUM;
                    }
                    break;
                case 0x06:	// Marker
					if (FileMode == FM_MID)
					{
						VGMPos ++;
						DataLen = GetMIDIDelay(NULL);
						VGMPos --;
						
						TempLng = (DataLen < 0x10) ? DataLen : 0x0F;
						//strncpy(TempStr, (char*)&VGMData[VGMPos + 0x02], TempLng);
						memcpy(TempStr, (char*)&VGMData[VGMPos + 0x02], TempLng);
						TempStr[TempLng] = 0x00;
						strcpynspc(TempStr, TempStr);
						//_strupr(TempStr);
						_strupr_s(TempStr);
						
						TempByt = 0x00;
						TempLng = strncmp(TempStr, "LOOP", 0x04);
						if (TempLng)
						{
							if (! strcmp(TempStr, "START"))
								TempByt = 0x01;
							else if (! strcmp(TempStr, "END"))
								TempByt = 0x02;
						}
						else
						{
							if (strstr(TempStr, "FROMHERE"))
								TempByt = 0x01;
							else if (strstr(TempStr, "TOSTART"))
								TempByt = 0x02;
							else if (strstr(TempStr, "START"))
								TempByt = 0x01;
							else if (strstr(TempStr, "END"))
								TempByt = 0x02;
						}
						
						if (TempByt & 0x01)
						{
							VGMHead.lngLoopOffset = VGMPos;
							VGMHead.lngLoopSamples = VGMSmplPos;
						}
						else if (TempByt & 0x02)
						{
							FadePlay = false;
							VGMLoop ++;
							if (VGMMaxLoop && VGMLoop >= VGMMaxLoop)
								FadePlay = true;
							
							if (! (FadePlay && (unsigned long int)
								VGMSmplPos + VGMSampleRate / 2 < VGMHead.lngTotalSamples))
							{
								SmplPlayed -= (VGMSmplPos - VGMHead.lngLoopSamples);
								TempSLng = SampleVGM2Playback(VGMSmplPos);
								
								VGMPos = VGMHead.lngLoopOffset & 0x7FFFFFFF;
								VGMSmplPos = VGMHead.lngLoopSamples;
								TempLng = 0x01;
								while(MidTempo[TempLng].BaseTick <= VGMSmplPos &&
									TempLng < TempoCount)
									TempLng ++;
								CurTempo = TempLng - 0x01;
								
								PitchVal = SampleVGM2Playback(VGMSmplPos);
								VGMSmplPlayed -= (TempSLng - PitchVal);
								RecalcPbTime = true;
								
								for (TempLng = 0x00; TempLng < NoteHstCount; TempLng ++)
								{
									TempNH = NoteHistory + TempLng;
									
									if (TempNH->NoteOn)
									{
										PlayNote(0x80 | TempNH->MIDICh, TempNH->Note, 0x00);
										TempLng --;
									}
								}
							}
							else
							{
								FadePlay = false;
							}
						}
					}
					break;
				case 0x2F:	// End Of File
					VGMEnd = true;
					if (FileMode == FM_CMF)
					{
						if (CMFMaxLoop != 0x01 || (FileMode == FM_LAA1 && VGMData[0x0A]))
						{
							VGMPos = VGMHead.lngDataOffset;
							VGMSmplPos -= VGMHead.lngLoopSamples;
							TempLng = 0x01;
							while(MidTempo[TempLng].BaseTick <= VGMSmplPos &&
								TempLng < TempoCount)
								TempLng ++;
							CurTempo = TempLng - 0x01;
							
							TempLng = SampleVGM2Playback(VGMHead.lngLoopSamples);
							VGMSmplPlayed -= TempLng;
							RecalcPbTime = true;
							VGMLoop ++;
							
							VGMEnd = false;
							if (CMFMaxLoop && VGMLoop >= CMFMaxLoop)
								FadePlay = true;
							LoopBack = true;
						}
					}
					else if (FileMode == FM_LAA1)
					{
						if (! VGMData[0x0A])
						{
							VGMPos = VGMHead.lngDataOffset;
							VGMSmplPos -= VGMHead.lngLoopSamples;
							TempLng = 0x01;
							while(MidTempo[TempLng].BaseTick <= VGMSmplPos &&
								TempLng < TempoCount)
								TempLng ++;
							CurTempo = TempLng - 0x01;
							
							TempLng = SampleVGM2Playback(VGMHead.lngLoopSamples);
							VGMSmplPlayed -= TempLng;
							RecalcPbTime = true;
							VGMLoop ++;
							
							VGMEnd = false;
							if (VGMMaxLoop && VGMLoop >= VGMMaxLoop)
								FadePlay = true;
							LoopBack = true;
						}
					}
					else
					{
						// Handle Loop-Controller
						if (VGMHead.lngLoopOffset & 0x80000000)
						{
							if (VGMHead.lngLoopSamples >= (unsigned long int)VGMSmplPos)
								VGMHead.lngLoopOffset = 0x00000000;
							/*if (LogVGM)
							{
								VGMHead.lngLoopOffset = 0x00000000;
								EndPlay = true;
							}*/
						}
						if (VGMHead.lngLoopOffset & 0x80000000)
						{
							SmplPlayed -= (VGMSmplPos - VGMHead.lngLoopSamples);
							TempSLng = SampleVGM2Playback(VGMSmplPos);
							
							VGMPos = (VGMHead.lngLoopOffset & 0x7FFFFFFF) + 0x02;
							VGMSmplPos = VGMHead.lngLoopSamples;
							TempLng = 0x01;
							while(MidTempo[TempLng].BaseTick <= VGMSmplPos &&
								TempLng < TempoCount)
								TempLng ++;
							CurTempo = TempLng - 0x01;
							
							PitchVal = SampleVGM2Playback(VGMSmplPos);
							VGMSmplPlayed -= (TempSLng - PitchVal);
							RecalcPbTime = true;
							VGMLoop ++;
							
							VGMEnd = false;
							if (VGMMaxLoop && VGMLoop >= VGMMaxLoop)
								FadePlay = true;
							LoopBack = true;
						}
						
						if (VGMEnd)
						{
							MidiTrk ++;
							//if (! SoftEnd && MidiTrk < MIDIHead.shtTracks)
							if (FileMode != FM_LAA2 && MidiTrk < MIDIHead.shtTracks)
							{
								VGMPos ++;
								DataLen = GetMIDIDelay(&TempLng);
								VGMPos += TempLng + DataLen;
								
								LoopBack = true;
								VGMPos += 0x08;	// Skip "MTrk" and Track Length
								VGMEnd = false;
							}
						}
					}
					break;
				case 0x51:	// Tempo
					TempLng = 0x00000000;
					memcpy(&TempLng, &VGMData[VGMPos + 0x02], 0x03);
					SwapBytes(&TempLng, 0x03);
					/*VGMSampleRate = (unsigned long int)((unsigned __int64)
									MIDIHead.shtResolution * 1000000 / TempLng);*/
					TempSLng = SampleVGM2Playback(VGMSmplPos);
					
					if (MidTempo[CurTempo].BaseTick != VGMSmplPos)
						CurTempo ++;
					TempTempo = MidTempo + CurTempo;
					TempTempo->BaseTick = VGMSmplPos;
					TempTempo->Tempo = TempLng;
					TempTempo->SmplTime = TempSLng;
					if (CurTempo + 0x01 > TempoCount)
						TempoCount = CurTempo + 0x01;
					
					RecalcPbTime = true;
					break;
				}
				
				if (! LoopBack)
				{
					VGMPos ++;
					DataLen = GetMIDIDelay(&TempLng);
					VGMPos += TempLng + DataLen;
				}
				break;
			default:
				VGMPos += 0x01;
				break;
			}
			
			if (RecalcPbTime)
			{
				TempLng = SampleVGM2Playback(VGMSmplPos);
				*SampleCount = TempLng - VGMSmplPlayed;
				//SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + *SampleCount);
				RecalcPbTime = false;
				return;
			}
			
			break;
		case 0x80:	// Note Off
		case 0x90:	// Note On
		case 0xA0:	// Note Aftertouch
		case 0xB0:	// Controller
		case 0xE0:	// Pitch Bend
			InterpretEventS(Command, VGMData[VGMPos + 0x00], VGMData[VGMPos + 0x01]);
			VGMPos += 0x02;
			break;
		case 0xC0:	// Patch Change
		case 0xD0:	// Channel Aftertouch
			InterpretEventS(Command, VGMData[VGMPos + 0x00], 0x00);
			VGMPos += 0x01;
			break;
		}
		if (Command < 0xF0)
			LastMidCmd = Command;
		
		if (VGMEnd)
			break;
	}
	
	// Modulation Wheel
	if (! FMPort)
	{
		TempLng = (SampleRate / 1000);	// ultra accururate Vibrato
	}
	else if (LogVGM)
	{
		TempLng = (SampleRate / 100);	// high accururate Vibrato
	}
	else
	{
		// lower Vibrato accuracy to avoid spamming the real chips
		if (OPL_MODE == 0x03)
			TempLng = (SampleRate / 100);	// high accururate Vibrato
		else
			TempLng = (SampleRate / 40);	// low accururate Vibrato (every 25 ms)
	}
	SmplDivdr = SampleRate / 5.0f;
	for (Channel = 0x00; Channel < 0x10; Channel ++)
	{
		TempMid = MidChn[CurPortNum] + Channel;
		if (! TempMid->ModDepth)
		{
			if (TempMid->ModStep)
			{
				TempMid->ModStep = 0x00;
				TempMid->ModPb = 0x0000;
				SetControllerAll(0xE0 | Channel, 0x01, TempMid->Pitch);
			}
			continue;
		}
		
		TempMid->ModStep += (unsigned short int)*SampleCount;
		TempMid->ModStep %= SampleRate;
		if (TempMid->ModStep % TempLng)
			continue;
		
		TempByt = TempMid->ModDepth;
		PitchVal = (signed short int)
					(sin(2.0 * PI * TempMid->ModStep / SmplDivdr) * TempByt * 16 + 0.5);
		
		if (PitchVal != TempMid->ModPb)
		{
			TempMid->ModPb = (signed short int)PitchVal;
			SetControllerAll(0xE0 | Channel, 0x01, TempMid->Pitch);
		}
	}
	
	if (FMPort && FadePlay)
	{
		TempLng = PlayingTime % (SampleRate / 5);
		if (! TempLng)
		{
			for (Channel = 0x00; Channel < 0x10; Channel ++)
			{
				TempMid = MidChn[CurPortNum] + Channel;
				SetControllerAll(0xB0 | Channel, 0x87, 0x00);
			}
		}
	}
	
	// Sustain (not the pedal)
	for (Command = 0x00; Command < OPL_VCHIPS; Command ++)
	{
		for (Channel = 0x00; Channel < 0x09; Channel ++)
		{
			TempFM = FMChn + Command * 0x09 + Channel;
			
			PitchVal = TempFM->Delay;
			if (TempFM->NoteMsk & 0x0F)
			{
				TempFM->Delay -= TempFM->StnFact * *SampleCount;
				if (TempFM->Delay > PitchVal)	// Subtraction Overflow ?
				{
					TempFM->Delay = 0x80000001;	// +1 to make Minimum-Search simpler
					TempFM->StnFact = 0x0000;
				}
			}
			else
			{
				TempFM->Delay += TempFM->StnFact * *SampleCount;
				if (TempFM->Delay < PitchVal)	// Addition Overflow ?
				{
					TempFM->Delay = 0x7FFFFFFF;
					TempFM->StnFact = 0x0000;
				}
			}
		}
	}
	for (TempLng = 0x00; TempLng < NoteHstCount; TempLng ++)
	{
		TempNH = NoteHistory + TempLng;
		
		TempNH->Delay += *SampleCount;
	}
	
	return;
}


static inline signed long int Stream2Sample(signed long int* Stream, signed long int SampleCnt,
											unsigned short int Volume)
{
	if (SampleCnt <= 0x01)
		return Stream[0x00] * Volume;
	if (SampleCnt == 0x02)
		return (Stream[0x00] + Stream[0x01]) * Volume >> 1;
	
	signed long int CurSmpl;
	signed long int SmplVal;
	
	SmplVal = 0x00;
	for (CurSmpl = 0x00; CurSmpl < SampleCnt; CurSmpl ++)
	{
		SmplVal += *(Stream++);
	}
	
	return SmplVal * Volume / SampleCnt;
}

static inline signed short int Limit2Short(signed long int Value)
{
	signed long int NewValue;
	
	NewValue = Value;
	if (NewValue < -0x8000)
		NewValue = -0x8000;
	if (NewValue > 0x7FFF)
		NewValue = 0x7FFF;
	
	return (signed short int)NewValue;
}

static inline void ProcessChipStream(WAVE_32BS* RetSample, unsigned char ChipID)
{
	unsigned long int* CurCSR;
	unsigned short int* CurCVol;
	unsigned long int* CurSmpP;
	unsigned long int* CurSmpA;
	unsigned long int* CurSmpB;
	signed long int BufSize;
	signed long int* CurBufL;
	signed long int* CurBufR;
	unsigned long int* CurLSL;
	unsigned long int* CurLSR;
	
	CurCSR = CSR + ChipID;
	CurCVol = CVol + ChipID;
	CurSmpP = SmpP + ChipID;
	CurSmpA = SmpA + ChipID;
	CurSmpB = SmpB + ChipID;
	CurBufL = (signed long int*)StreamBufs[0x00];
	CurBufR = (signed long int*)StreamBufs[0x01];
	CurLSL = LSL + ChipID;
	CurLSR = LSR + ChipID;
	
	*CurSmpA = *CurSmpB;
	(*CurSmpP) ++;
	*CurSmpB = (unsigned long int)((unsigned __int64)(*CurSmpP) * (*CurCSR) / SampleRate);
	if (*CurSmpA >= *CurCSR)
	{
		*CurSmpP -= SampleRate;
		*CurSmpA -= *CurCSR;
		*CurSmpB -= *CurCSR;
	}
	BufSize = *CurSmpB - *CurSmpA;
	if (BufSize > 0)
	{
		switch(OPL_MODE)
		{
		case 0x01:
			//if(!ChipOpts[0].Disabled)
				ym3526_stream_update(ChipID, StreamBufs, BufSize);
			//else{ 
			//	ZeroMemory(StreamBufs[0], BufSize * sizeof(int));
			//	ZeroMemory(StreamBufs[1], BufSize * sizeof(int));
			//};
			break;
		case 0x02:
			//if (!ChipOpts[1].Disabled)
				ym3812_stream_update(ChipID, StreamBufs, BufSize);
			//else {
			//	ZeroMemory(StreamBufs[0], BufSize * sizeof(int));
			//	ZeroMemory(StreamBufs[1], BufSize * sizeof(int));
			//};
			break;
		case 0x03:
			//if (!ChipOpts[2].Disabled)
				ymf262_stream_update(ChipID, StreamBufs, BufSize);
			//else {
			//	ZeroMemory(StreamBufs[0], BufSize * sizeof(int));
			//	ZeroMemory(StreamBufs[1], BufSize * sizeof(int));
			//};
			break;
		default:
			break;
		}
		*CurLSL = CurBufL[BufSize - 1];
		*CurLSR = CurBufR[BufSize - 1];
		
		RetSample->Left += Stream2Sample(CurBufL, BufSize, *CurCVol);
		RetSample->Right += Stream2Sample(CurBufR, BufSize, *CurCVol);
	}
	else
	{
		RetSample->Left += (*CurLSL) * (*CurCVol);
		RetSample->Right += (*CurLSR) * (*CurCVol);
	}
	
	return;
}

void FillBuffer(WAVE_16BS* Buffer, unsigned long int BufferSize)
{
	unsigned long int CurSmpl;
	WAVE_32BS TempBuf;
	signed long int CurMstVol;
	double TempSng;
	unsigned char CurChip;
	
	//memset(Buffer, 0x00, sizeof(WAVE_16BS) * BufferSize);
	
	if (FadePlay)
	{
		if (! FadeStart)
		{
			FadeStart = PlayingTime;
			if (! FadeTime)
			{
				//FadeTime = (SampleVal * 1000 / VGMSampleRate) * 8;
				FadeTime = 5000;
			}
		}
		
		TempSng = (PlayingTime - FadeStart) / (double)SampleRate;
		MasterVol = 1.0f - TempSng / (FadeTime * 0.001f);
		if (MasterVol < 0.0f)
		{
			MasterVol = 0.0f;
			EndPlay = true;
		}
	}
	TempSng = VolumeLevel * (MasterVol * MasterVol);
	CurMstVol = (signed long int)(0x100 * TempSng);
	
	if (Buffer == NULL)
	{
		for (CurSmpl = 0x00; CurSmpl < BufferSize; CurSmpl ++)
			InterpretFile(1);
		return;
	}
	
	for (CurSmpl = 0x00; CurSmpl < BufferSize; CurSmpl ++)
	{
		InterpretFile(1);
		
		TempBuf.Left = 0x00;
		TempBuf.Right = 0x00;
		if (! FMPort)
		{
			for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
			{
				ProcessChipStream(&TempBuf, CurChip);
			}
		}
		TempBuf.Left = ((TempBuf.Left >> 7) * CurMstVol) >> 8;
		TempBuf.Right = ((TempBuf.Right >> 7) * CurMstVol) >> 8;
		Buffer[CurSmpl].Left = Limit2Short(TempBuf.Left);
		Buffer[CurSmpl].Right = Limit2Short(TempBuf.Right);
	}
	
	return;
}
static LONG g_lInstanceCount = 0;
//////////////////////////////////////////////////////////////////////////
KbFmOplMidiDecoder::KbFmOplMidiDecoder(IKpiConfig *pConfig)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    if(InterlockedIncrement(&g_lInstanceCount) > 1){
        m_bCanOpen = FALSE;
    }
    else{
        m_bCanOpen = TRUE;
        ReadOptions(pConfig);
    }
    m_dwEndSample = 0;
    m_dwSilentSample = 0;
}
//////////////////////////////////////////////////////////////////////////
KbFmOplMidiDecoder::~KbFmOplMidiDecoder(void)
{
    if(!m_bCanOpen){
        return;
    }
    if(OPLReg){
        free(OPLReg);
        OPLReg = NULL;
    }
    if(OPLRegBak){
        free(OPLRegBak);
        OPLRegBak = NULL;
    }
    if(OPLRegForce){
        free(OPLRegForce);
        OPLRegForce = NULL;
    }
    if(FMDrumReg){
        free(FMDrumReg);
        FMDrumReg = NULL;
    }
    if(FMChn){
        free(FMChn);
        FMChn = NULL;
    }
    if(VGMData){
        free(VGMData);
        VGMData = NULL;
    }
    if(VGMTag.strTrackNameE){
        free(VGMTag.strTrackNameE);
        VGMTag.strTrackNameE = NULL;
    }
    if(VGMTag.strGameNameE){
        free(VGMTag.strGameNameE);
        VGMTag.strGameNameE = NULL;
    }
    if(VGMTag.strSystemNameE){
        free(VGMTag.strSystemNameE);
        VGMTag.strSystemNameE = NULL;
    }
    if(VGMTag.strAuthorNameE){
        free(VGMTag.strAuthorNameE);
        VGMTag.strAuthorNameE = NULL;
    }
    if(VGMTag.strReleaseDate){
        free(VGMTag.strReleaseDate);
        VGMTag.strReleaseDate = NULL;
    }
    if(VGMTag.strCreator){
        free(VGMTag.strCreator);
        VGMTag.strCreator = NULL;
    }
    if(VGMTag.strNotes){
        free(VGMTag.strNotes);
        VGMTag.strNotes = NULL;
    }
	if(StreamBufs[0x00]){
        free(StreamBufs[0x00]);
        StreamBufs[0x00] = NULL;
    }
    if(StreamBufs[0x01]){
        free(StreamBufs[0x01]);
        StreamBufs[0x01] = NULL;
    }
    if(MidTempo){
        free(MidTempo);
        MidTempo = NULL;
    }
    int CurChip;
	switch(OPL_MODE)
	{
	case 0x01:
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			device_stop_ym3526(CurChip);
		}
		break;
	case 0x02:
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			device_stop_ym3812(CurChip);
		}
		break;
	case 0x03:
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			device_stop_ymf262(CurChip);
		}
		break;
	}
    g_bIsLoop = FALSE;
    ZeroMemory(&VGMHead, sizeof(VGMHead));
    VGMEnd = false;
    InterlockedDecrement(&g_lInstanceCount);
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbFmOplMidiDecoder::Open(const KPI_MEDIAINFO *cpRequest, 
                                    IKpiFile *pFile, 
                                    IKpiFolder *pFolder)
{
	if(!m_bCanOpen)return 0;
	//char tmp[] = " abc d e f g ";
	//strcpynspc(tmp, tmp);//strcpynspc ͺoOΑΔ’½ΜΕC³
    if(g_nSampleRate_Cfg < 0){//{Μ©ηΜvπ³
        SampleRate = -g_nSampleRate_Cfg;
    }
    else{//vͺ κΞvΙ]€
        if(cpRequest && cpRequest->dwSampleRate){
            SampleRate = cpRequest->dwSampleRate;
        }
        else{//vͺΘ―κΞ g_nSampleRate_Cfg
            SampleRate = g_nSampleRate_Cfg;
        }
    }
    if(!OpenVGMFile(pFile)){
        return 0;
    }
    kpi_InitMediaInfo(&m_MediaInfo);
    m_MediaInfo.dwSampleRate = SampleRate;
    m_MediaInfo.nBitsPerSample = -64;//32;
    m_MediaInfo.dwChannels = 2;
    m_MediaInfo.dwNumber = 1;
    m_MediaInfo.dwCount = 1;
    return 1;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbFmOplMidiDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if (m_MediaInfo.dwCount == 0 || m_MediaInfo.dwNumber != dwNumber) {
        return 0;
    }
    if (!ppMediaInfo) {
        return 1;
    }
    if (m_MediaInfo.dwCount) {
        *ppMediaInfo = &m_MediaInfo;
    }
    else {
        *ppMediaInfo = NULL;
    }

	unsigned char CurChip;
	
//	printf("Initializing ...\r");
	
	/*if (ChipOpts.YM3812.Disabled)
		VGMHead.lngHzYM3812 = 0;
	if (ChipOpts.YM3526.Disabled)
		VGMHead.lngHzYM3526 = 0;
	if (ChipOpts.YMF262.Disabled)
		VGMHead.lngHzYMF262 = 0;*/
		
	//memset(&CVol, 0x00, sizeof(unsigned short int));
	memset(&CVol, 0x00, sizeof(CVol));
	/*if (FMPort)
	{
		mixerOpen(&hmixer, 0x00, 0x00, 0x00, 0x00);
		GetMixerControl((HMIXEROBJ)hmixer, &mixctrl);
		
		if (WINNT_MODE)
		{
			if (OpenPortTalk())
			{
				_getch();
				return;
			}
		}
	}*/
	switch(OPL_MODE)
	{
	case 0x01:
		VGMHead.lngHzYM3526 = 3579545;
		OPL_VCHIPS = OPL_CHIPS;
		if (FMPort)
			break;
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			CSR[CurChip] = device_start_ym3526(CurChip, VGMHead.lngHzYM3526);
			CVol[CurChip] = 0x100;
			//device_reset_ym3526(CurChip);
		}
		break;
	case 0x02:
		VGMHead.lngHzYM3812 = 3579545;
		OPL_VCHIPS = OPL_CHIPS;
		if (FMPort)
			break;
		ym3812_set_emu_core(EmuCore);
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			CSR[CurChip] = device_start_ym3812(CurChip, VGMHead.lngHzYM3812);
			CVol[CurChip] = 0x100;
			//device_reset_ym3812(CurChip);
		}
		break;
	case 0x03:
		VGMHead.lngHzYMF262 = 14318180;
		OPL_VCHIPS = OPL_CHIPS * 2;
		if (FMPort)
			break;
		ymf262_set_emu_core(EmuCore);
		for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
		{
			CSR[CurChip] = device_start_ymf262(CurChip, VGMHead.lngHzYMF262);
			CVol[CurChip] = 0x100;
			//device_reset_ymf262(CurChip);
			
			//OPL_Write(CurChip << 1, 0x105, 0x01 << 0);	// Enable OPL3 Mode
		}
		break;
	}
	OPLReg = (unsigned char*)malloc(OPL_VCHIPS * 0x100);
	OPLRegBak = (unsigned char*)malloc(OPL_VCHIPS * 0x100);
	OPLRegForce = (unsigned char*)malloc(OPL_VCHIPS * 0x100);
	memset(OPLReg, 0x00, OPL_VCHIPS * 0x100);
	memset(OPLRegForce, 0x01, OPL_VCHIPS * 0x100);
	
	FMDrumReg = (unsigned char*)malloc(OPL_VCHIPS);

    VGMLoop = 0x00;
	LAA2_LIdx = 0x00;
	EndPlay = false;
	PausePlay = false;
	FadePlay = false;
	SoftStop = false;
	//SoftEnd = false;
	FadeStart = 0;
	MasterVol = 1.0f;
//	PauseThread = true;
	
	FMChn = (FM_CHN*)malloc(0x09 * OPL_VCHIPS * sizeof(FM_CHN));
	memset(SmpP, 0x00, 0x100 * sizeof(unsigned long int));
	memset(SmpA, 0x00, 0x100 * sizeof(unsigned long int));
	memset(SmpB, 0x00, 0x100 * sizeof(unsigned long int));
	memset(LSL, 0x00, 0x100 * sizeof(unsigned long int));
	memset(LSR, 0x00, 0x100 * sizeof(unsigned long int));
	
	hFileVGM = NULL;
	/*if (LogVGM)
	{
		VGM_HEADER_LOG VGMHeadL;
		unsigned long int ClockAdd;
		
		if (OPL_CHIPS > 1)
			ClockAdd = 0x40000000;
		else
			ClockAdd = 0x00;
		memset(&VGMHeadL, 0x00, sizeof(VGM_HEADER_LOG));
		VGMHeadL.fccVGM = FCC_VGM;
		VGMHeadL.lngVersion = 0x00000151;
		VGMHeadL.lngDataOffset = sizeof(VGM_HEADER_LOG) - 0x34;
		if (VGMHead.lngHzYM3526)
			VGMHeadL.lngHzYM3526 = VGMHead.lngHzYM3526 | ClockAdd;
		if (VGMHead.lngHzYM3812)
			VGMHeadL.lngHzYM3812 = VGMHead.lngHzYM3812 | ClockAdd;
		if (VGMHead.lngHzYMF262)
			VGMHeadL.lngHzYMF262 = VGMHead.lngHzYMF262 | ClockAdd;
		
		hFileVGM = fopen("VGMLog.vgm", "wb");
		fwrite(&VGMHeadL, 0x01, sizeof(VGM_HEADER_LOG), hFileVGM);
		LastVgmDelay = 0;
	}
	
	printf("Calc Length ...\r");
	*/
	Interpreting = false;
	SkipMode = true;	// Disable Error Messages
	InterpretFile(0);
	SkipMode = false;
	VGMPlaySt = VGMPos;
	VGMPlayEnd = VGMHead.lngEOFOffset;
	VGMPlayEnd -= VGMPlaySt;
	
	PlaySmpl = 0x100;
	StreamBufs[0x00] = (signed int*)malloc(PlaySmpl * sizeof(signed int));
	StreamBufs[0x01] = (signed int*)malloc(PlaySmpl * sizeof(signed int));

    m_MediaInfo.qwLength = kpi_SampleTo100ns(TotalPbSamples,//VGMHead.lngTotalSamples, 
                                             m_MediaInfo.dwSampleRate);
    if(g_bIsLoop){//[vΘ
    //{ΜͺmΑΔ’ι€ΘΜΕ[vΤΝΚmΉΈA[vΘΕ ι±ΖΎ―πmηΉι
        m_MediaInfo.qwLoop = -1;
        m_MediaInfo.qwFadeOut = -1;
    }
    return m_MediaInfo.dwCount;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbFmOplMidiDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{//V[NiίθlΝV[NγΜΔΆΚuj
    RestartPlaying();
    m_dwEndSample = 0;
    m_dwSilentSample = 0;
    return 0;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbFmOplMidiDecoder::Render(BYTE* out, DWORD dwSizeSample)
{
    //FillBuffer((WAVE_16BS*)out, dwSizeSample);
	//return dwSizeSample;
    if(VGMEnd){
        //πΝIΉγΕε5bΤΔΆ
        if (m_dwEndSample >= m_MediaInfo.dwSampleRate * 5) {
            return 0;
        }
        //(ΩΪ)³Ήͺ0.5b±’½η»Μ_ΕtIΉ
        if (m_dwSilentSample >= (m_MediaInfo.dwSampleRate * 5 / 10)) {
            return 0;
        }
    }
    DWORD dwRetSample;
	WAVE_32BS TempBuf;
	unsigned char CurChip;

    struct WAVE_64F
    {
        double l;
        double r;
    };
    const double k = VolumeLevel / (double)(1 << 22);
    //const double k = 1.0 / (double)(1 << 22);
    WAVE_64F *out64f = (WAVE_64F*)out;
	for (dwRetSample = 0; dwRetSample < dwSizeSample; dwRetSample ++)
	{
        //if(VGMEnd){
        //    break;
        //}
		InterpretFile(1);
        TempBuf.Left = 0x00;
		TempBuf.Right = 0x00;
		if (! FMPort)
		{
			for (CurChip = 0x00; CurChip < OPL_CHIPS; CurChip ++)
			{
				ProcessChipStream(&TempBuf, CurChip);
			}
		}
        out64f->l = TempBuf.Left * k;//65536.0;
        out64f->r = TempBuf.Right * k;//65536.0;
        if (VGMEnd) {//SMFπΝIΉ
            //Εε5bΤΔΆ
            if (++m_dwEndSample >= m_MediaInfo.dwSampleRate * 5) {
                return dwRetSample;
            }
            //(ΩΪ)³Ήͺ0.5b±’½η»Μ_ΕtIΉ
            const double limit = 0.001;
            if((out64f->l < -limit) || (out64f->l > limit) ||
               (out64f->r < -limit) || (out64f->r > limit)){
//            if(out64f->l != 0 || out64f->r != 0){
				m_dwSilentSample = 0;
            }
            else{
                if(++m_dwSilentSample >= (m_MediaInfo.dwSampleRate * 5/10)){
                    return dwRetSample;
                }
            }
            // break;
        }
        out64f++;
    }
    return dwRetSample;
}
//////////////////////////////////////////////////////////////////////////
