changeset 2220:0cb173caf393

Automated merge with ssh://hg.atheme.org//hg/audacious-plugins
author Eugene Zagidullin <e.asphyx@gmail.com>
date Fri, 07 Dec 2007 21:35:28 +0300
parents d7acad3d50e8 (current diff) 6907fc39b53f (diff)
children b4a2a7197c05
files
diffstat 2 files changed, 1354 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modplug/load_mid.cxx	Fri Dec 07 21:35:28 2007 +0300
@@ -0,0 +1,1126 @@
+/*
+ * 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;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modplug/snd_eq.cxx	Fri Dec 07 21:35:28 2007 +0300
@@ -0,0 +1,228 @@
+/*
+ * 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>
+ *
+ * Name                Date             Description
+ * 
+ * Olivier Lapicque    --/--/--         Creation
+ * Trevor Nunes        26/01/04         conditional compilation for AMD,MMX calls
+ *
+*/
+#include "stdafx.h"
+#include "sndfile.h"
+#include <math.h>
+
+
+#define EQ_BANDWIDTH	2.0
+#define EQ_ZERO			0.000001
+#define REAL			float
+
+extern REAL MixFloatBuffer[];
+
+extern void StereoMixToFloat(const int *pSrc, float *pOut1, float *pOut2, UINT nCount);
+extern void FloatToStereoMix(const float *pIn1, const float *pIn2, int *pOut, UINT nCount);
+extern void MonoMixToFloat(const int *pSrc, float *pOut, UINT nCount);
+extern void FloatToMonoMix(const float *pIn, int *pOut, UINT nCount);
+
+typedef struct _EQBANDSTRUCT
+{
+	REAL a0, a1, a2, b1, b2;
+	REAL x1, x2, y1, y2;
+	REAL Gain, CenterFrequency;
+	BOOL bEnable;
+} EQBANDSTRUCT, *PEQBANDSTRUCT;
+
+UINT gEqLinearToDB[33] =
+{
+	16, 19, 22, 25, 28, 31, 34, 37,
+	40, 43, 46, 49, 52, 55, 58, 61,
+	64, 76, 88, 100, 112, 124, 136, 148,
+	160, 172, 184, 196, 208, 220, 232, 244, 256
+};
+
+
+//static REAL f2ic = (REAL)(1 << 28);
+//static REAL i2fc = (REAL)(1.0 / (1 << 28));
+
+static EQBANDSTRUCT gEQ[MAX_EQ_BANDS*2] =
+{
+	// Default: Flat EQ
+	{0,0,0,0,0, 0,0,0,0, 1,   120, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,   600, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,  1200, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,  3000, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,  6000, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1, 10000, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,   120, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,   600, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,  1200, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,  3000, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1,  6000, FALSE},
+	{0,0,0,0,0, 0,0,0,0, 1, 10000, FALSE},
+};
+
+void EQFilter(EQBANDSTRUCT *pbs, REAL *pbuffer, UINT nCount)
+//----------------------------------------------------------
+{
+	for (UINT i=0; i<nCount; i++)
+	{
+		REAL x = pbuffer[i];
+		REAL y = pbs->a1 * pbs->x1 + pbs->a2 * pbs->x2 + pbs->a0 * x + pbs->b1 * pbs->y1 + pbs->b2 * pbs->y2;
+		pbs->x2 = pbs->x1;
+		pbs->y2 = pbs->y1;
+		pbs->x1 = x;
+		pbuffer[i] = y;
+		pbs->y1 = y;
+	}
+}
+
+void CSoundFile::EQMono(int *pbuffer, UINT nCount)
+//------------------------------------------------
+{
+	MonoMixToFloat(pbuffer, MixFloatBuffer, nCount);
+	for (UINT b=0; b<MAX_EQ_BANDS; b++)
+	{
+		if ((gEQ[b].bEnable) && (gEQ[b].Gain != 1.0f))
+			EQFilter(&gEQ[b], MixFloatBuffer, nCount);
+	}
+	FloatToMonoMix(MixFloatBuffer, pbuffer, nCount);
+}
+
+void CSoundFile::EQStereo(int *pbuffer, UINT nCount)
+//--------------------------------------------------
+{
+	StereoMixToFloat(pbuffer, MixFloatBuffer, MixFloatBuffer+MIXBUFFERSIZE, nCount);
+		
+	for (UINT bl=0; bl<MAX_EQ_BANDS; bl++)
+	{
+		if ((gEQ[bl].bEnable) && (gEQ[bl].Gain != 1.0f))
+			EQFilter(&gEQ[bl], MixFloatBuffer, nCount);
+	}
+	for (UINT br=MAX_EQ_BANDS; br<MAX_EQ_BANDS*2; br++)
+	{
+		if ((gEQ[br].bEnable) && (gEQ[br].Gain != 1.0f))
+			EQFilter(&gEQ[br], MixFloatBuffer+MIXBUFFERSIZE, nCount);
+	}
+
+	FloatToStereoMix(MixFloatBuffer, MixFloatBuffer+MIXBUFFERSIZE, pbuffer, nCount);
+
+}
+
+void CSoundFile::InitializeEQ(BOOL bReset)
+//----------------------------------------
+{
+	REAL fMixingFreq = (REAL)gdwMixingFreq;
+	// Gain = 0.5 (-6dB) .. 2 (+6dB)
+	for (UINT band=0; band<MAX_EQ_BANDS*2; band++) if (gEQ[band].bEnable)
+	{
+		REAL k, k2, r, f;
+		REAL v0, v1;
+		BOOL b = bReset;
+
+		f = gEQ[band].CenterFrequency / fMixingFreq;
+		if (f > 0.45f) gEQ[band].Gain = 1;
+		// if (f > 0.25) f = 0.25;
+		// k = tan(PI*f);
+		k = f * 3.141592654f;
+		k = k + k*f;
+//		if (k > (REAL)0.707) k = (REAL)0.707;
+		k2 = k*k;
+		v0 = gEQ[band].Gain;
+		v1 = 1;
+		if (gEQ[band].Gain < 1.0)
+		{
+			v0 *= (0.5f/EQ_BANDWIDTH);
+			v1 *= (0.5f/EQ_BANDWIDTH);
+		} else
+		{
+			v0 *= (1.0f/EQ_BANDWIDTH);
+			v1 *= (1.0f/EQ_BANDWIDTH);
+		}
+		r = (1 + v0*k + k2) / (1 + v1*k + k2);
+		if (r != gEQ[band].a0)
+		{
+			gEQ[band].a0 = r;
+			b = TRUE;
+		}
+		r = 2 * (k2 - 1) / (1 + v1*k + k2);
+		if (r != gEQ[band].a1)
+		{
+			gEQ[band].a1 = r;
+			b = TRUE;
+		}
+		r = (1 - v0*k + k2) / (1 + v1*k + k2);
+		if (r != gEQ[band].a2)
+		{
+			gEQ[band].a2 = r;
+			b = TRUE;
+		}
+		r = - 2 * (k2 - 1) / (1 + v1*k + k2);
+		if (r != gEQ[band].b1)
+		{
+			gEQ[band].b1 = r;
+			b = TRUE;
+		}
+		r = - (1 - v1*k + k2) / (1 + v1*k + k2);
+		if (r != gEQ[band].b2)
+		{
+			gEQ[band].b2 = r;
+			b = TRUE;
+		}
+		if (b)
+		{
+			gEQ[band].x1 = 0;
+			gEQ[band].x2 = 0;
+			gEQ[band].y1 = 0;
+			gEQ[band].y2 = 0;
+		}
+	} else
+	{
+		gEQ[band].a0 = 0;
+		gEQ[band].a1 = 0;
+		gEQ[band].a2 = 0;
+		gEQ[band].b1 = 0;
+		gEQ[band].b2 = 0;
+		gEQ[band].x1 = 0;
+		gEQ[band].x2 = 0;
+		gEQ[band].y1 = 0;
+		gEQ[band].y2 = 0;
+	}
+}
+
+
+void CSoundFile::SetEQGains(const UINT *pGains, UINT nGains, const UINT *pFreqs, BOOL bReset)
+//-------------------------------------------------------------------------------------------
+{
+	for (UINT i=0; i<MAX_EQ_BANDS; i++)
+	{
+		REAL g, f = 0;
+		if (i < nGains)
+		{
+			UINT n = pGains[i];
+//			if (n > 32) n = 32;
+			g = 1.0 + (((double)n) / 64.0);
+			if (pFreqs) f = (REAL)(int)pFreqs[i];
+		} else
+		{
+			g = 1;
+		}
+		gEQ[i].Gain = g;
+		gEQ[i].CenterFrequency = f;
+		gEQ[i+MAX_EQ_BANDS].Gain = g;
+		gEQ[i+MAX_EQ_BANDS].CenterFrequency = f;
+		if (f > 20.0f && i < nGains) /* don't enable bands outside... */
+		{
+			gEQ[i].bEnable = TRUE;
+			gEQ[i+MAX_EQ_BANDS].bEnable = TRUE;
+		} else
+		{
+			gEQ[i].bEnable = FALSE;
+			gEQ[i+MAX_EQ_BANDS].bEnable = FALSE;
+		}
+	}
+	InitializeEQ(bReset);
+}