view src/modplug/load_mid.cxx @ 2291:1457b35713d9

remove obsolete function
author mf0102 <0102@gmx.at>
date Thu, 10 Jan 2008 18:39:52 +0100
parents 3673c7ec4ea2
children
line wrap: on
line source

/*
 * This program is  free software; you can redistribute it  and modify it
 * under the terms of the GNU  General Public License as published by the
 * Free Software Foundation; either version 2  of the license or (at your
 * option) any later version.
 *
 * Authors: Olivier Lapicque <olivierl@jps.net>
*/

//////////////////////////////////////////////
// MIDI loader                              //
//////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"

#define MIDI_DRUMCHANNEL	10
#define MIDI_MAXTRACKS		64

UINT gnMidiImportSpeed = 3;
UINT gnMidiPatternLen = 128;

#pragma pack(1)

typedef struct MIDIFILEHEADER
{
	CHAR id[4];		// "MThd" = 0x6468544D
	DWORD len;		// 6
	WORD w1;		// 1?
	WORD wTrks;		// 2?
	WORD wDivision;	// F0
} MIDIFILEHEADER;


typedef struct MIDITRACKHEADER
{
	CHAR id[4];	// "MTrk" = 0x6B72544D
	DWORD len;
} MIDITRACKHEADER;

static LONG midivolumetolinear(UINT nMidiVolume)
{
	return (nMidiVolume * nMidiVolume << 16) / (127*127);
}

//////////////////////////////////////////////////////////////////////
// Midi Loader Internal Structures

#define CHNSTATE_NOTEOFFPENDING		0x0001

// MOD Channel State description (current volume, panning, etc...)
typedef struct MODCHANNELSTATE
{
	DWORD flags;	// Channel Flags
	WORD idlecount;
	WORD pitchsrc, pitchdest;	// Pitch Bend (current position/new position)
	BYTE parent;	// Midi Channel parent
	BYTE pan;		// Channel Panning			0-255
	BYTE note;		// Note On # (0=available)
} MODCHANNELSTATE;

// MIDI Channel State (Midi Channels 0-15)
typedef struct MIDICHANNELSTATE
{
	DWORD flags;		// Channel Flags
	WORD pitchbend;		// Pitch Bend Amount (14-bits unsigned)
	BYTE note_on[128];	// If note=on -> MOD channel # + 1 (0 if note=off)
	BYTE program;		// Channel Midi Program
	WORD bank;			// 0-16383
	// -- Controllers --------- function ---------- CC# --- range  --- init (midi) ---
	BYTE pan;			// Channel Panning			CC10	[0-255]		128 (64)
	BYTE expression;	// Channel Expression		CC11	0-128		128	(127)
	BYTE volume;		// Channel Volume			CC7		0-128		80	(100)
	BYTE modulation;	// Modulation				CC1		0-127		0
	BYTE pitchbendrange;// Pitch Bend Range								64
} MIDICHANNELSTATE;

typedef struct MIDITRACK
{
	LPCBYTE ptracks, ptrmax;
	DWORD status;
	LONG nexteventtime;
} MIDITRACK;

#pragma pack()



LPCSTR szMidiGroupNames[17] =
{
	"Piano",
	"Chromatic Percussion",
	"Organ",
	"Guitar",
	"Bass",
	"Strings",
	"Ensemble",
	"Brass",
	"Reed",
	"Pipe",
	"Synth Lead",
	"Synth Pad",
	"Synth Effects",
	"Ethnic",
	"Percussive",
	"Sound Effects",
	"Percussions"
};


LPCSTR szMidiProgramNames[128] =
{
	// 1-8: Piano
	"Acoustic Grand Piano",
	"Bright Acoustic Piano",
	"Electric Grand Piano",
	"Honky-tonk Piano",
	"Electric Piano 1",
	"Electric Piano 2",
	"Harpsichord",
	"Clavi",
	// 9-16: Chromatic Percussion
	"Celesta",
	"Glockenspiel",
	"Music Box",
	"Vibraphone",
	"Marimba",
	"Xylophone",
	"Tubular Bells",
	"Dulcimer",
	// 17-24: Organ
	"Drawbar Organ",
	"Percussive Organ",
	"Rock Organ",
	"Church Organ",
	"Reed Organ",
	"Accordion",
	"Harmonica",
	"Tango Accordion",
	// 25-32: Guitar
	"Acoustic Guitar (nylon)",
	"Acoustic Guitar (steel)",
	"Electric Guitar (jazz)",
	"Electric Guitar (clean)",
	"Electric Guitar (muted)",
	"Overdriven Guitar",
	"Distortion Guitar",
	"Guitar harmonics",
	// 33-40   Bass
	"Acoustic Bass",
	"Electric Bass (finger)",
	"Electric Bass (pick)",
	"Fretless Bass",
	"Slap Bass 1",
	"Slap Bass 2",
	"Synth Bass 1",
	"Synth Bass 2",
	// 41-48   Strings
	"Violin",
	"Viola",
	"Cello",
	"Contrabass",
	"Tremolo Strings",
	"Pizzicato Strings",
	"Orchestral Harp",
	"Timpani",
	// 49-56   Ensemble
	"String Ensemble 1",
	"String Ensemble 2",
	"SynthStrings 1",
	"SynthStrings 2",
	"Choir Aahs",
	"Voice Oohs",
	"Synth Voice",
	"Orchestra Hit",
	// 57-64   Brass
	"Trumpet",
	"Trombone",
	"Tuba",
	"Muted Trumpet",
	"French Horn",
	"Brass Section",
	"SynthBrass 1",
	"SynthBrass 2",
	// 65-72   Reed
	"Soprano Sax",
	"Alto Sax",
	"Tenor Sax",
	"Baritone Sax",
	"Oboe",
	"English Horn",
	"Bassoon",
	"Clarinet",
	// 73-80   Pipe
	"Piccolo",
	"Flute",
	"Recorder",
	"Pan Flute",
	"Blown Bottle",
	"Shakuhachi",
	"Whistle",
	"Ocarina",
	// 81-88   Synth Lead
	"Lead 1 (square)",
	"Lead 2 (sawtooth)",
	"Lead 3 (calliope)",
	"Lead 4 (chiff)",
	"Lead 5 (charang)",
	"Lead 6 (voice)",
	"Lead 7 (fifths)",
	"Lead 8 (bass + lead)",
	// 89-96   Synth Pad
	"Pad 1 (new age)",
	"Pad 2 (warm)",
	"Pad 3 (polysynth)",
	"Pad 4 (choir)",
	"Pad 5 (bowed)",
	"Pad 6 (metallic)",
	"Pad 7 (halo)",
	"Pad 8 (sweep)",
	// 97-104  Synth Effects
	"FX 1 (rain)",
	"FX 2 (soundtrack)",
	"FX 3 (crystal)",
	"FX 4 (atmosphere)",
	"FX 5 (brightness)",
	"FX 6 (goblins)",
	"FX 7 (echoes)",
	"FX 8 (sci-fi)",
	// 105-112 Ethnic
	"Sitar",
	"Banjo",
	"Shamisen",
	"Koto",
	"Kalimba",
	"Bag pipe",
	"Fiddle",
	"Shanai",
	// 113-120 Percussive
	"Tinkle Bell",
	"Agogo",
	"Steel Drums",
	"Woodblock",
	"Taiko Drum",
	"Melodic Tom",
	"Synth Drum",
	"Reverse Cymbal",
	// 121-128 Sound Effects
	"Guitar Fret Noise",
	"Breath Noise",
	"Seashore",
	"Bird Tweet",
	"Telephone Ring",
	"Helicopter",
	"Applause",
	"Gunshot"
};


// Notes 25-85
LPCSTR szMidiPercussionNames[61] =
{
	"Seq Click",
	"Brush Tap",
	"Brush Swirl",
	"Brush Slap",
	"Brush Swirl W/Attack",
	"Snare Roll",
	"Castanet",
	"Snare Lo",
	"Sticks",
	"Bass Drum Lo",
	"Open Rim Shot",
	"Acoustic Bass Drum",
	"Bass Drum 1",
	"Side Stick",
	"Acoustic Snare",
	"Hand Clap",
	"Electric Snare",
	"Low Floor Tom",
	"Closed Hi Hat",
	"High Floor Tom",
	"Pedal Hi-Hat",
	"Low Tom",
	"Open Hi-Hat",
	"Low-Mid Tom",
	"Hi Mid Tom",
	"Crash Cymbal 1",
	"High Tom",
	"Ride Cymbal 1",
	"Chinese Cymbal",
	"Ride Bell",
	"Tambourine",
	"Splash Cymbal",
	"Cowbell",
	"Crash Cymbal 2",
	"Vibraslap",
	"Ride Cymbal 2",
	"Hi Bongo",
	"Low Bongo",
	"Mute Hi Conga",
	"Open Hi Conga",
	"Low Conga",
	"High Timbale",
	"Low Timbale",
	"High Agogo",
	"Low Agogo",
	"Cabasa",
	"Maracas",
	"Short Whistle",
	"Long Whistle",
	"Short Guiro",
	"Long Guiro",
	"Claves",
	"Hi Wood Block",
	"Low Wood Block",
	"Mute Cuica",
	"Open Cuica",
	"Mute Triangle",
	"Open Triangle",
	"Shaker",
	"Jingle Bell",
	"Bell Tree",
};


const WORD kMidiChannelPriority[16] =
{
	0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0,	0xFFE0, 0xFFC0, 0xFF80, 0xFF00,
	0xFE00, 0xFDFF, 0xF800, 0xF000,	0xE000, 0xC000, 0x8000, 0x0000,
};


///////////////////////////////////////////////////////////////////////////
// Helper functions

static LONG getmidilong(LPCBYTE &p, LPCBYTE pmax)
//----------------------------------------------------------
{
	DWORD n;
	UINT a;

	a = (p < pmax) ? *(p++) : 0;
	n = 0;
	while (a&0x80)
	{
		n = (n<<7)|(a&0x7F);
		a = (p < pmax) ? *(p++) : 0;
	}
	return (n<<7)|(LONG)a;
}


// Returns MOD tempo and tick multiplier
static int ConvertMidiTempo(int tempo_us, int *pTickMultiplier)
//-------------------------------------------------------------
{
	int nBestModTempo = 120;
	int nBestError = 1000000; // 1s
	int nBestMultiplier = 1;
	int nSpeed = gnMidiImportSpeed;
	for (int nModTempo=110; nModTempo<=240; nModTempo++)
	{
		int tick_us = (2500000) / nModTempo;
		int nFactor = (tick_us+tempo_us/2) / tempo_us;
		if (!nFactor) nFactor = 1;
		int nError = tick_us - tempo_us * nFactor;
		if (nError < 0) nError = -nError;
		if (nError < nBestError)
		{
			nBestError = nError;
			nBestModTempo = nModTempo;
			nBestMultiplier = nFactor;
		}
		if ((!nError) || ((nError<=1) && (nFactor==64))) break;
	}
	*pTickMultiplier = nBestMultiplier * nSpeed;
	return nBestModTempo;
}


////////////////////////////////////////////////////////////////////////////////
// Maps a midi instrument - returns the instrument number in the file
UINT CSoundFile::MapMidiInstrument(DWORD dwBankProgram, UINT nChannel, UINT nNote)
//--------------------------------------------------------------------------------
{
	INSTRUMENTHEADER *penv;
	UINT nProgram = dwBankProgram & 0x7F;
	UINT nBank = dwBankProgram >> 7;

	nNote &= 0x7F;
	if (nNote >= 120) return 0;
	for (UINT i=1; i<=m_nInstruments; i++) if (Headers[i])
	{
		INSTRUMENTHEADER *p = Headers[i];
		// Drum Kit ?
		if (nChannel == MIDI_DRUMCHANNEL)
		{
			if (nNote == p->nMidiDrumKey) return i;
		} else
		// Melodic Instrument
		{
			if (nProgram == p->nMidiProgram) return i;
		}
	}
	if ((m_nInstruments + 1 >= MAX_INSTRUMENTS) || (m_nSamples + 1 >= MAX_SAMPLES)) return 0;
	penv = new INSTRUMENTHEADER;
	if (!penv) return 0;
	memset(penv, 0, sizeof(INSTRUMENTHEADER));
	m_nSamples++;
	m_nInstruments++;
	Headers[m_nInstruments] = penv;
	penv->wMidiBank = nBank;
	penv->nMidiProgram = nProgram;
	penv->nMidiChannel = nChannel;
	if (nChannel == MIDI_DRUMCHANNEL) penv->nMidiDrumKey = nNote;
	penv->nGlobalVol = 128;
	penv->nFadeOut = 1024;
	penv->nPan = 128;
	penv->nPPC = 5*12;
	penv->nNNA = NNA_NOTEOFF;
	penv->nDCT = (nChannel == MIDI_DRUMCHANNEL) ? DCT_SAMPLE : DCT_NOTE;
	penv->nDNA = DNA_NOTEFADE;
	for (UINT j=0; j<120; j++)
	{
		int mapnote = j+1;
		if (nChannel == MIDI_DRUMCHANNEL)
		{
			mapnote = 61;
			/*mapnote = 61 + j - nNote;
			if (mapnote < 1) mapnote = 1;
			if (mapnote > 120) mapnote = 120;*/
		}
		penv->Keyboard[j] = m_nSamples;
		penv->NoteMap[j] = (BYTE)mapnote;
	}
	penv->dwFlags |= ENV_VOLUME;
	if (nChannel != MIDI_DRUMCHANNEL) penv->dwFlags |= ENV_VOLSUSTAIN;
	penv->VolEnv.nNodes=4;
	penv->VolEnv.Ticks[0]=0;
	penv->VolEnv.Values[0] = 64;
	penv->VolEnv.Ticks[1] = 10;
	penv->VolEnv.Values[1] = 64;
	penv->VolEnv.Ticks[2] = 15;
	penv->VolEnv.Values[2] = 48;
	penv->VolEnv.Ticks[3] = 20;
	penv->VolEnv.Values[3] = 0;
	penv->VolEnv.nSustainStart=1;
	penv->VolEnv.nSustainEnd=1;
	// Sample
	Ins[m_nSamples].nPan = 128;
	Ins[m_nSamples].nVolume = 256;
	Ins[m_nSamples].nGlobalVol = 64;
	if (nChannel != MIDI_DRUMCHANNEL)
	{
		// GM Midi Name
		strcpy((char*)penv->name, (char*)szMidiProgramNames[nProgram]);
		strcpy((char*)m_szNames[m_nSamples], (char*)szMidiProgramNames[nProgram]);
	} else
	{
		strcpy((char*)penv->name, "Percussions");
		if ((nNote >= 24) && (nNote <= 84))
			strcpy((char*)m_szNames[m_nSamples], (char*)szMidiPercussionNames[nNote-24]);
		else
			strcpy((char*)m_szNames[m_nSamples], "Percussions");
	}
	return m_nInstruments;
}


/////////////////////////////////////////////////////////////////
// Loader Status
#define MIDIGLOBAL_SONGENDED		0x0001
#define MIDIGLOBAL_FROZEN			0x0002
#define MIDIGLOBAL_UPDATETEMPO		0x0004
#define MIDIGLOBAL_UPDATEMASTERVOL	0x0008
// Midi Globals
#define MIDIGLOBAL_GMSYSTEMON		0x0100
#define MIDIGLOBAL_XGSYSTEMON		0x0200


BOOL CSoundFile::ReadMID(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
	const MIDIFILEHEADER *pmfh = (const MIDIFILEHEADER *)lpStream;
	const MIDITRACKHEADER *pmth;
	MODCHANNELSTATE chnstate[MAX_BASECHANNELS];
	MIDICHANNELSTATE midichstate[16];
	MIDITRACK miditracks[MIDI_MAXTRACKS];
	DWORD dwMemPos, dwGlobalFlags, tracks, tempo;
	UINT row, pat, midimastervol;
	short int division;
	int midi_clock, nTempoUsec, nPPQN, nTickMultiplier;

	// Fix import parameters
	if (gnMidiImportSpeed < 2) gnMidiImportSpeed = 2;
	if (gnMidiImportSpeed > 6) gnMidiImportSpeed = 6;
	if (gnMidiPatternLen < 64) gnMidiPatternLen = 64;
	if (gnMidiPatternLen > 256) gnMidiPatternLen = 256;
	// Detect RMI files
	if ((dwMemLength > 12)
	 && (memcmp(lpStream, "RIFF",4) == 0)
	 && (memcmp(lpStream, "RMID",4) == 0))
	{
		lpStream += 12;
		dwMemLength -= 12;
		while (dwMemLength > 8)
		{
			char *id = (char*)lpStream;
			DWORD len = *(DWORD *)(lpStream+4);
			lpStream += 8;
			dwMemLength -= 8;
			if ((memcmp(id, "data",4) == 0) && (len < dwMemLength))
			{
				dwMemLength = len;
				pmfh = (const MIDIFILEHEADER *)lpStream;
				break;
			}
			if (len >= dwMemLength) return FALSE;
			lpStream += len;
			dwMemLength -= len;
		}
	}
	// MIDI File Header
	if ((dwMemLength < sizeof(MIDIFILEHEADER)+8) || (memcmp(pmfh->id, "MThd",4) != 0)) return FALSE;
	dwMemPos = 8 + bswapBE32(pmfh->len);
	if (dwMemPos >= dwMemLength - 8) return FALSE;
	pmth = (MIDITRACKHEADER *)(lpStream+dwMemPos);
	tracks = bswapBE16(pmfh->wTrks);
	if ((!tracks) || (memcmp(pmth->id, "MTrk", 4) != 0)) return FALSE;
	if (tracks > MIDI_MAXTRACKS) tracks = MIDI_MAXTRACKS;
	// Reading File...
	m_nType = MOD_TYPE_MID;
	m_nChannels = 32;
	m_nSamples = 0;
	m_nInstruments = 0;
	m_dwSongFlags |= (SONG_LINEARSLIDES | SONG_INSTRUMENTMODE);
	m_szNames[0][0] = 0;
	// MIDI->MOD Tempo Conversion
	division = bswapBE16(pmfh->wDivision);
	if (division < 0)
	{
		int nFrames = -(division>>8);
		int nSubFrames = (division & 0xff);
		nPPQN = nFrames * nSubFrames / 2;
		if (!nPPQN) nPPQN = 1;
	} else
	{
		nPPQN = (division) ? division : 96;
	}
	nTempoUsec = 500000 / nPPQN;
	tempo = ConvertMidiTempo(nTempoUsec, &nTickMultiplier);
	m_nDefaultTempo = tempo;
	m_nDefaultSpeed = gnMidiImportSpeed;
	m_nDefaultGlobalVolume = 256;
	midimastervol = m_nDefaultGlobalVolume;
	
	// Initializing 
	memset(Order, 0xFF, sizeof(Order));
	memset(chnstate, 0, sizeof(chnstate));
	memset(miditracks, 0, sizeof(miditracks));
	memset(midichstate, 0, sizeof(midichstate));
	// Initializing Patterns
	Order[0] = 0;
	for (UINT ipat=0; ipat<MAX_PATTERNS; ipat++) PatternSize[ipat] = gnMidiPatternLen;
	// Initializing Channels
	for (UINT ics=0; ics<MAX_BASECHANNELS; ics++)
	{
		// Channel settings
		ChnSettings[ics].nPan = 128;
		ChnSettings[ics].nVolume = 64;
		ChnSettings[ics].dwFlags = 0;
		// Channels state
		chnstate[ics].pan = 128;
		chnstate[ics].pitchsrc = 0x2000;
		chnstate[ics].pitchdest = 0x2000;
	}
	// Initializing Track Pointers
	for (UINT itrk=0; itrk<tracks; itrk++)
	{
		miditracks[itrk].nexteventtime = -1;
		miditracks[itrk].status = 0x2F;
		pmth = (MIDITRACKHEADER *)(lpStream+dwMemPos);
		if (dwMemPos + 8 >= dwMemLength) break;
		DWORD len = bswapBE32(pmth->len);
		if ((memcmp(pmth->id, "MTrk", 4) == 0) && (dwMemPos + 8 + len <= dwMemLength))
		{
			// Initializing midi tracks
			miditracks[itrk].ptracks = lpStream+dwMemPos+8;
			miditracks[itrk].ptrmax = miditracks[itrk].ptracks + len;
			miditracks[itrk].nexteventtime = getmidilong(miditracks[itrk].ptracks, miditracks[itrk].ptrmax);
		}
		dwMemPos += 8 + len;
	}
	// Initializing midi channels state
	for (UINT imidi=0; imidi<16; imidi++)
	{
		midichstate[imidi].pan = 128;			// middle
		midichstate[imidi].expression = 128;	// no attenuation
		midichstate[imidi].volume = 80;			// GM specs defaults to 100
		midichstate[imidi].pitchbend = 0x2000;	// Pitch Bend Amount
		midichstate[imidi].pitchbendrange = 64;	// Pitch Bend Range: +/- 2 semitones
	}
	////////////////////////////////////////////////////////////////////////////
	// Main Midi Sequencer Loop
	pat = 0;
	row = 0;
	midi_clock = 0;
	dwGlobalFlags = MIDIGLOBAL_UPDATETEMPO | MIDIGLOBAL_FROZEN;
	do
	{
		// Allocate current pattern if not allocated yet
		if (!Patterns[pat])
		{
			Patterns[pat] = AllocatePattern(PatternSize[pat], m_nChannels);
			if (!Patterns[pat]) break;
		}
		dwGlobalFlags |= MIDIGLOBAL_SONGENDED;
		MODCOMMAND *m = Patterns[pat] + row * m_nChannels;
		// Parse Tracks
		for (UINT trk=0; trk<tracks; trk++) if (miditracks[trk].ptracks)
		{
			MIDITRACK *ptrk = &miditracks[trk];
			dwGlobalFlags &= ~MIDIGLOBAL_SONGENDED;
			while ((ptrk->ptracks) && (ptrk->nexteventtime >= 0) && (midi_clock+(nTickMultiplier>>2) >= ptrk->nexteventtime))
			{
				if (ptrk->ptracks[0] & 0x80) ptrk->status = *(ptrk->ptracks++);
				switch(ptrk->status)
				{
				/////////////////////////////////////////////////////////////////////
				// End Of Track
				case 0x2F:
				// End Of Song
				case 0xFC:
					ptrk->ptracks = NULL;
					break;

				/////////////////////////////////////////////////////////////////////
				// SYSEX messages
				case 0xF0:
				case 0xF7:
					{
						LONG len = getmidilong(ptrk->ptracks, ptrk->ptrmax);
						if ((len > 1) && (ptrk->ptracks + len <ptrk->ptrmax) && (ptrk->ptracks[len-1] == 0xF7))
						{
							DWORD dwSysEx1 = 0, dwSysEx2 = 0;
							if (len >= 4) dwSysEx1 = (*((DWORD *)(ptrk->ptracks))) & 0x7F7F7F7F;
							if (len >= 8) dwSysEx2 = (*((DWORD *)(ptrk->ptracks+4))) & 0x7F7F7F7F;
							// GM System On
							if ((len == 5) && (dwSysEx1 == 0x01097F7E))
							{
								dwGlobalFlags |= MIDIGLOBAL_GMSYSTEMON;
							} else
							// XG System On
							if ((len == 8) && ((dwSysEx1 & 0xFFFFF0FF) == 0x004c1043) && (dwSysEx2 == 0x77007e00))
							{
								dwGlobalFlags |= MIDIGLOBAL_XGSYSTEMON;
							} else
							// Midi Master Volume
							if ((len == 7) && (dwSysEx1 == 0x01047F7F))
							{
								midimastervol = midivolumetolinear(ptrk->ptracks[5] & 0x7F) >> 8;
								if (midimastervol < 16) midimastervol = 16;
								dwGlobalFlags |= MIDIGLOBAL_UPDATEMASTERVOL;
							}
						}
						ptrk->ptracks += len;
					}
					break;
				
				//////////////////////////////////////////////////////////////////////
				// META-events: FF.code.len.data[len]
				case 0xFF:
					{
						UINT i = *(ptrk->ptracks++);
						LONG len = getmidilong(ptrk->ptracks, ptrk->ptrmax);
						if (ptrk->ptracks+len > ptrk->ptrmax)
						{
							// EOF
							ptrk->ptracks = NULL;
						} else
						switch(i)
						{
						// FF.01 [text]: Song Information
						case 0x01:
							if (!len) break;
							if ((len < 32) && (!m_szNames[0][0]))
							{
								memcpy(m_szNames[0], ptrk->ptracks, len);
								m_szNames[0][len] = 0;
							} else
							if ((!m_lpszSongComments) && (ptrk->ptracks[0]) && (ptrk->ptracks[0] < 0x7F))
							{
								m_lpszSongComments = new char [len+1];
								if (m_lpszSongComments)
								{
									memcpy(m_lpszSongComments, ptrk->ptracks, len);
									m_lpszSongComments[len] = 0;
								}
							}
							break;
						// FF.02 [text]: Song Copyright
						case 0x02:
							if (!len) break;
							if ((!m_lpszSongComments) && (ptrk->ptracks[0]) && (ptrk->ptracks[0] < 0x7F) && (len > 7))
							{
								m_lpszSongComments = new char [len+1];
								if (m_lpszSongComments)
								{
									memcpy(m_lpszSongComments, ptrk->ptracks, len);
									m_lpszSongComments[len] = 0;
								}
							}
							break;
						// FF.03: Sequence Name
						case 0x03:
						// FF.06: Sequence Text (->Pattern names)
						case 0x06:
							if ((len > 1) && (!trk))
							{
								UINT k = (len < 32) ? len : 31;
								CHAR s[32];
								memcpy(s, ptrk->ptracks, k);
								s[k] = 0;
								if ((!strnicmp((char*)s, "Copyri", 6)) || (!s[0])) break;
								if (i == 0x03)
								{
									if (!m_szNames[0][0]) strcpy((char*)m_szNames[0], (char*)s);
								}
							}
							break;
						// FF.07: Cue Point (marker)
						// FF.20: Channel Prefix
						// FF.2F: End of Track
						case 0x2F:
							ptrk->status = 0x2F;
							ptrk->ptracks = NULL;
							break;
						// FF.51 [tttttt]: Set Tempo
						case 0x51:
							{
								LONG l = ptrk->ptracks[0];
								l = (l << 8) | ptrk->ptracks[1];
								l = (l << 8) | ptrk->ptracks[2];
								if (l <= 0) break;
								nTempoUsec = l / nPPQN;
								if (nTempoUsec < 100) nTempoUsec = 100;
								tempo = ConvertMidiTempo(nTempoUsec, &nTickMultiplier);
								dwGlobalFlags |= MIDIGLOBAL_UPDATETEMPO;
							}
							break;
						// FF.58: Time Signature
						// FF.7F: Sequencer-Specific
						}
						if (ptrk->ptracks) ptrk->ptracks += len;
					}
					break;

				//////////////////////////////////////////////////////////////////////////
				// Regular Voice Events
				default:
				{
					UINT midich = (ptrk->status & 0x0F)+1;
					UINT midist = ptrk->status & 0xF0;
					MIDICHANNELSTATE *pmidich = &midichstate[midich-1];
					UINT note, velocity;

					switch(midist)
					{
					//////////////////////////////////
					// Note Off:	80.note.velocity
					case 0x80:
					// Note On:		90.note.velocity
					case 0x90:
						note = ptrk->ptracks[0] & 0x7F;
						velocity = (midist == 0x90) ? (ptrk->ptracks[1] & 0x7F) : 0;
						ptrk->ptracks += 2;
						// Note On: 90.note.velocity
						if (velocity)
						{
							// Start counting rows
							dwGlobalFlags &= ~MIDIGLOBAL_FROZEN;
							// if the note is already playing, we reuse this channel
							UINT nchn = pmidich->note_on[note];
							if ((nchn) && (chnstate[nchn-1].parent != midich)) nchn = 0;
							// or else, we look for an available child channel
							if (!nchn)
							{
								for (UINT i=0; i<m_nChannels; i++) if (chnstate[i].parent == midich)
								{
									if ((!chnstate[i].note) && ((!m[i].note) || (m[i].note & 0x80)))
									{
										// found an available channel
										nchn = i+1;
										break;
									}
								}
							}
							// still nothing? in this case, we try to allocate a new mod channel
							if (!nchn)
							{
								for (UINT i=0; i<m_nChannels; i++) if (!chnstate[i].parent)
								{
									nchn = i+1;
									chnstate[i].parent = midich;
									break;
								}
							}
							// still not? we have to steal a voice from another channel
							// We found our channel: let's do the note on
							if (nchn)
							{
								pmidich->note_on[note] = nchn;
								nchn--;
								chnstate[nchn].pitchsrc = pmidich->pitchbend;
								chnstate[nchn].pitchdest = pmidich->pitchbend;
								chnstate[nchn].flags &= ~CHNSTATE_NOTEOFFPENDING;
								chnstate[nchn].idlecount = 0;
								chnstate[nchn].note = note+1;
								int realnote = note;
								if (midich != 10)
								{
									realnote += (((int)pmidich->pitchbend - 0x2000) * pmidich->pitchbendrange) / (0x2000*32);
									if (realnote < 0) realnote = 0;
									if (realnote > 119) realnote = 119;
								}
								m[nchn].note = realnote+1;
								m[nchn].instr = MapMidiInstrument(pmidich->program + ((UINT)pmidich->bank << 7), midich, note);
								m[nchn].volcmd = VOLCMD_VOLUME;
								LONG vol = midivolumetolinear(velocity) >> 8;
								vol = (vol * (LONG)pmidich->volume * (LONG)pmidich->expression) >> 13;
								if (vol > 256) vol = 256;
								if (vol < 4) vol = 4;
								m[nchn].vol = (BYTE)(vol>>2);
								// Channel Panning
								if ((!m[nchn].command) && (pmidich->pan != chnstate[nchn].pan))
								{
									chnstate[nchn].pan = pmidich->pan;
									m[nchn].param = pmidich->pan;
									m[nchn].command = CMD_PANNING8;
								}
							}
						} else
						// Note Off; 90.note.00
						if (!(dwGlobalFlags & MIDIGLOBAL_FROZEN))
						{
							UINT nchn = pmidich->note_on[note];
							if (nchn)
							{
								nchn--;
								chnstate[nchn].flags |= CHNSTATE_NOTEOFFPENDING;
								chnstate[nchn].note = 0;
								pmidich->note_on[note] = 0;
							} else
							{
								for (UINT i=0; i<m_nChannels; i++)
								{
									if ((chnstate[i].parent == midich) && (chnstate[i].note == note+1))
									{
										chnstate[i].note = 0;
										chnstate[i].flags |= CHNSTATE_NOTEOFFPENDING;
									}
								}
							}
						}
						break;

					///////////////////////////////////
					// A0.xx.yy: Aftertouch
					case 0xA0:
						{
							ptrk->ptracks += 2;
						}
						break;

					///////////////////////////////////
					// B0: Control Change
					case 0xB0:
						{
							UINT controller = ptrk->ptracks[0];
							UINT value = ptrk->ptracks[1] & 0x7F;
							ptrk->ptracks += 2;
							switch(controller)
							{
							// Bn.00.xx: Bank Select MSB (GS)
							case 0x00:
								pmidich->bank &= 0x7F;
								pmidich->bank |= (value << 7);
								break;
							// Bn.01.xx: Modulation Depth
							case 0x01:
								pmidich->pitchbendrange = value;
								break;
							// Bn.07.xx: Volume
							case 0x07:
								pmidich->volume = (BYTE)(midivolumetolinear(value) >> 9);
								break;
							// Bn.0B.xx: Expression
							case 0x0B:
								pmidich->expression = (BYTE)(midivolumetolinear(value) >> 9);
								break;
							// Bn.0A.xx: Pan
							case 0x0A:
								pmidich->pan = value * 2;
								break;
							// Bn.20.xx: Bank Select LSB (GS)
							case 0x20:
								pmidich->bank &= (0x7F << 7);
								pmidich->bank |= value;
								break;
							// Bn.79.00: Reset All Controllers (GM)
							case 0x79:
								pmidich->modulation = 0;
								pmidich->expression = 128;
								pmidich->pitchbend = 0x2000;
								pmidich->pitchbendrange = 64;
								// Should also reset pedals (40h-43h), NRP, RPN, aftertouch
								break;
							// Bn.78.00: All Sound Off (GS)
							// Bn.7B.00: All Notes Off (GM)
							case 0x78:
							case 0x7B:
								if (value == 0x00)
								{
									// All Notes Off
									for (UINT k=0; k<m_nChannels; k++)
									{
										if (chnstate[k].note)
										{
											chnstate[k].flags |= CHNSTATE_NOTEOFFPENDING;
											chnstate[k].note = 0;
										}
									}
								}
								break;
							////////////////////////////////////
							// Controller List
							//
							// Bn.02.xx: Breath Control
							// Bn.04.xx: Foot Pedal
							// Bn.05.xx: Portamento Time (Glissando Time)
							// Bn.06.xx: Data Entry MSB
							// Bn.08.xx: Balance
							// Bn.10-13.xx: GP Control #1-#4
							// Bn.20-3F.xx: Data LSB for controllers 0-31
							// Bn.26.xx: Data Entry LSB
							// Bn.40.xx: Hold Pedal #1
							// Bn.41.xx: Portamento (GS)
							// Bn.42.xx: Sostenuto (GS)
							// Bn.43.xx: Soft Pedal (GS)
							// Bn.44.xx: Legato Pedal
							// Bn.45.xx: Hold Pedal #2
							// Bn.46.xx: Sound Variation
							// Bn.47.xx: Sound Timbre
							// Bn.48.xx: Sound Release Time
							// Bn.49.xx: Sound Attack Time
							// Bn.4A.xx: Sound Brightness
							// Bn.4B-4F.xx: Sound Control #6-#10
							// Bn.50-53.xx: GP Control #5-#8
							// Bn.54.xx: Portamento Control (GS)
							// Bn.5B.xx: Reverb Level (GS)
							// Bn.5C.xx: Tremolo Depth
							// Bn.5D.xx: Chorus Level (GS)
							// Bn.5E.xx: Celeste Depth
							// Bn.5F.xx: Phaser Depth
							// Bn.60.xx: Data Increment
							// Bn.61.xx: Data Decrement
							// Bn.62.xx: Non-RPN Parameter LSB (GS)
							// Bn.63.xx: Non-RPN Parameter MSB (GS)
							// Bn.64.xx: RPN Parameter LSB (GM)
							// Bn.65.xx: RPN Parameter MSB (GM)
							// Bn.7A.00: Local On/Off
							// Bn.7C.00: Omni Mode Off
							// Bn.7D.00: Omni Mode On
							// Bn.7E.mm: Mono Mode On
							// Bn.7F.00: Poly Mode On
							}
						}
						break;

					////////////////////////////////
					// C0.pp: Program Change
					case 0xC0:
						{
							pmidich->program = ptrk->ptracks[0] & 0x7F;
							ptrk->ptracks++;
						}
						break;

					////////////////////////////////
					// D0: Channel Aftertouch (Polyphonic Key Pressure)
					case 0xD0:
						{
							ptrk->ptracks++;
						}
						break;
					
					////////////////////////////////
					// E0: Pitch Bend
					case 0xE0:
						{
							pmidich->pitchbend = (WORD)(((UINT)ptrk->ptracks[1] << 7) + (ptrk->ptracks[0] & 0x7F));
							for (UINT i=0; i<128; i++) if (pmidich->note_on[i])
							{
								UINT nchn = pmidich->note_on[i]-1;
								if (chnstate[nchn].parent == midich)
								{
									chnstate[nchn].pitchdest = pmidich->pitchbend;
								}
							}
							ptrk->ptracks+=2;
						}
						break;

					//////////////////////////////////////
					// F0 & Unsupported commands: skip it
					default:
						ptrk->ptracks++;
					}
				}} // switch+default
				// Process to next event
				if (ptrk->ptracks)
				{
					ptrk->nexteventtime += getmidilong(ptrk->ptracks, ptrk->ptrmax);
				}
				if (ptrk->ptracks >= ptrk->ptrmax) ptrk->ptracks = NULL;
			}
			// End reached?
			if (ptrk->ptracks >= ptrk->ptrmax) ptrk->ptracks = NULL;
		}

		////////////////////////////////////////////////////////////////////
		// Move to next row
		if (!(dwGlobalFlags & MIDIGLOBAL_FROZEN))
		{
			// Check MOD channels status
			for (UINT ichn=0; ichn<m_nChannels; ichn++)
			{
				// Pending Global Effects ?
				if (!m[ichn].command)
				{
					if ((chnstate[ichn].pitchsrc != chnstate[ichn].pitchdest) && (chnstate[ichn].parent))
					{
						int newpitch = chnstate[ichn].pitchdest;
						int pitchbendrange = midichstate[chnstate[ichn].parent-1].pitchbendrange;
						// +/- 256 for +/- pitch bend range
						int slideamount = (newpitch - (int)chnstate[ichn].pitchsrc) / (int)32;
						if (slideamount)
						{
							const int ppdiv = (16 * 128 * (gnMidiImportSpeed-1));
							newpitch = (int)chnstate[ichn].pitchsrc + slideamount;
							if (slideamount < 0)
							{
								int param = (-slideamount * pitchbendrange + ppdiv/2) / ppdiv;
								if (param >= 0x80) param = 0x80;
								if (param > 0)
								{
									m[ichn].param = (BYTE)param;
									m[ichn].command = CMD_PORTAMENTODOWN;
								}
							} else
							{
								int param = (slideamount * pitchbendrange + ppdiv/2) / ppdiv;
								if (param >= 0x80) param = 0x80;
								if (param > 0)
								{
									m[ichn].param = (BYTE)param;
									m[ichn].command = CMD_PORTAMENTOUP;
								}
							}
						}
						chnstate[ichn].pitchsrc = (WORD)newpitch;

					} else
					if (dwGlobalFlags & MIDIGLOBAL_UPDATETEMPO)
					{
						m[ichn].command = CMD_TEMPO;
						m[ichn].param = (BYTE)tempo;
						dwGlobalFlags &= ~MIDIGLOBAL_UPDATETEMPO;
					} else
					if (dwGlobalFlags & MIDIGLOBAL_UPDATEMASTERVOL)
					{
						m[ichn].command = CMD_GLOBALVOLUME;
						m[ichn].param = midimastervol >> 1; // 0-128
						dwGlobalFlags &= ~MIDIGLOBAL_UPDATEMASTERVOL;
					}
				}
				// Check pending noteoff events for m[ichn]
				if (!m[ichn].note)
				{
					if (chnstate[ichn].flags & CHNSTATE_NOTEOFFPENDING)
					{
						chnstate[ichn].flags &= ~CHNSTATE_NOTEOFFPENDING;
						m[ichn].note = 0xFF;
					}
					// Check State of channel
					chnstate[ichn].idlecount++;
					if ((chnstate[ichn].note) && (chnstate[ichn].idlecount >= 50))
					{
						chnstate[ichn].note = 0;
						m[ichn].note = 0xFF;	// only if not drum channel ?
					} else
					if (chnstate[ichn].idlecount >= 500) // 20secs of inactivity
					{
						chnstate[ichn].idlecount = 0;
						chnstate[ichn].parent = 0;
					}
				}
			}

			if ((++row) >= PatternSize[pat])
			{
				pat++;
				if (pat >= MAX_PATTERNS-1) break;
				Order[pat] = pat;
				Order[pat+1] = 0xFF;
				row = 0;
			}
		}

		// Increase midi clock
		midi_clock += nTickMultiplier;
	} while (!(dwGlobalFlags & MIDIGLOBAL_SONGENDED));
	return TRUE;
}