[Scummvm-cvs-logs] scummvm master -> dbc8d3c530dd12cbe2cea4d9099f0ec05fedf6d0

clone2727 clone2727 at gmail.com
Thu Jul 9 07:34:55 CEST 2015


This automated email contains information about 42 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
6f01600e12 AUDIO: Fix abuse of driver IDs in OPL code.
2fa1ce51dd GUI: Simplify OPL option code a bit.
f1f29302f5 AUDIO: Remove the legacy OPL API
2e8f9dcec9 AUDIO: Remove the sample rate configuration from the OPL code
b9307ef1a4 AUDIO: Introduce a callback to the OPL code
0bb13b358e SCUMM: Use the built-in OPL timer for Player_AD
ed8830fcc8 AUDIO: Use the built-in OPL timer for MidiDriver_ADLIB
24add3c745 SCI: Use the built-in OPL timer
984cd9b018 CINE: Use the built-in OPL timer
3c7c217f44 CRUISE: Use the built-in OPL timer
5803dffead KYRA: Use the built-in OPL timer
b638efe0fa PARALLACTION: Use the built-in OPL timer
b122ec2790 SKY: Use the built-in OPL timer
4a4ad97fd3 QUEEN: Use the built-in OPL timer
5024ae136a TSAGE: Use the built-in OPL timer
4c6724c5fa MADS: Use the built-in OPL timer
cc6e304af1 AUDIO: Limit the DOSBox start(0) hack to only being called once
5b06eef159 AUDIO: Allow for changing the OPL timer rate
73e8ac2a9b GOB: Use the built-in OPL timer
dcb75fcaf1 SHERLOCK: Use the built-in OPL timer
0c5d40e94c AGOS: Use the built-in OPL timer
22d985f3c2 AUDIO: Use the built-in OPL timer for MidiDriver_Miles_AdLib
f7c785b37b SKY: Implement original music volume handling
e31da911c9 GOB: Implement custom AdLib volume control
b367ea548d QUEEN: Implement original music volume handling
dce05c520b AUDIO: Be consistent with calling stop() in OPL destructors
8bcbcd6c16 AUDIO: Change callback frequency without restarting the audio stream
bed9da8b9d AUDIO: Remove all AudioStream access to OPL
4d56585112 AUDIO: Add a class representing a real OPL
a45ff5a6a9 CONFIGURE: Ensure the USE_ALSA define ends up in config.mk
40820eebf5 AUDIO: Add experimental hardware OPL support using ALSA
b630eca437 AUDIO: Fix bug in ALSA AdLib driver
beed23c441 AUDIO: List OPL3 support for ALSA AdLib driver
82f585871b SCI: Check OPL init return code
56c0238f9b SCI: Delete OPL when init fails
be345083a0 AUDIO: Update 2nd operator panning for AdLib register 0xc0
1bdcf6e836 AUDIO: Reset ALSA AdLib on exit
1287a56429 AUDIO: Fix ALSA AdLib OPL2 waveform mask
f0606aa8f1 AUDIO: Reset OPL registers in ALSA driver
fde8abf8cc AUDIO: Move the common AdLib MidiDriver out of softsynth
bb8132beb8 AUDIO: Move ALSA OPL 'driver' out of softsynth
dbc8d3c530 Merge pull request #600 from clone2727/opl_alsa


Commit: 6f01600e12ba14acde8a6557716783861dc014d1
    https://github.com/scummvm/scummvm/commit/6f01600e12ba14acde8a6557716783861dc014d1
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2015-07-07T20:19:41-04:00

Commit Message:
AUDIO: Fix abuse of driver IDs in OPL code.

If the driver id did not match the array index, the wrong driver entry would
be accessed causing a crash in the worst case.

Changed paths:
    audio/fmopl.cpp
    audio/fmopl.h



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 30229ea..00e49ef 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -63,6 +63,15 @@ Config::DriverId Config::parse(const Common::String &name) {
 	return -1;
 }
 
+const Config::EmulatorDescription *Config::findDriver(DriverId id) {
+	for (int i = 0; _drivers[i].name; ++i) {
+		if (_drivers[i].id == id)
+			return &_drivers[i];
+	}
+
+	return 0;
+}
+
 Config::DriverId Config::detect(OplType type) {
 	uint32 flags = 0;
 	switch (type) {
@@ -90,8 +99,11 @@ Config::DriverId Config::detect(OplType type) {
 	// When a valid driver is selected, check whether it supports
 	// the requested OPL chip.
 	if (drv != -1 && drv != kAuto) {
+		const EmulatorDescription *driverDesc = findDriver(drv);
 		// If the chip is supported, just use the driver.
-		if ((flags & _drivers[drv].flags)) {
+		if (!driverDesc) {
+			warning("The selected OPL driver %d could not be found", drv);
+		} else if ((flags & driverDesc->flags)) {
 			return drv;
 		} else {
 			// Else we will output a warning and just
diff --git a/audio/fmopl.h b/audio/fmopl.h
index 85ac606..28dd456 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -71,6 +71,12 @@ public:
 	static DriverId parse(const Common::String &name);
 
 	/**
+	 * @return The driver description for the given id or 0 in case it is not
+	 *         available.
+	 */
+	static const EmulatorDescription *findDriver(DriverId id);
+
+	/**
 	 * Detects a driver for the specific type.
 	 *
 	 * @return Returns a valid driver id on success, -1 otherwise.


Commit: 2fa1ce51dd3cb00eef289094fe9f660c237254bc
    https://github.com/scummvm/scummvm/commit/2fa1ce51dd3cb00eef289094fe9f660c237254bc
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2015-07-07T20:19:42-04:00

Commit Message:
GUI: Simplify OPL option code a bit.

Changed paths:
    gui/options.cpp



diff --git a/gui/options.cpp b/gui/options.cpp
index 726b89d..ba247e5 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -445,11 +445,9 @@ void OptionsDialog::close() {
 
 		if (_oplPopUp) {
 			if (_enableAudioSettings) {
-				const OPL::Config::EmulatorDescription *ed = OPL::Config::getAvailable();
-				while (ed->name && ed->id != (int)_oplPopUp->getSelectedTag())
-					++ed;
+				const OPL::Config::EmulatorDescription *ed = OPL::Config::findDriver(_oplPopUp->getSelectedTag());
 
-				if (ed->name)
+				if (ed)
 					ConfMan.set("opl_driver", ed->name, _domain);
 				else
 					ConfMan.removeKey("opl_driver", _domain);


Commit: f1f29302f5401c4782985cec4d47d069b99036ee
    https://github.com/scummvm/scummvm/commit/f1f29302f5401c4782985cec4d47d069b99036ee
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:42-04:00

Commit Message:
AUDIO: Remove the legacy OPL API

Changed paths:
    audio/fmopl.cpp
    audio/fmopl.h
    engines/cine/sound.cpp
    engines/cruise/sound.cpp
    engines/kyra/sound_adlib.cpp
    engines/mads/nebular/sound_nebular.cpp
    engines/mads/nebular/sound_nebular.h
    engines/mads/sound.cpp
    engines/mads/sound.h
    engines/queen/midiadlib.cpp
    engines/sky/music/adlibchannel.cpp
    engines/sky/music/adlibchannel.h
    engines/sky/music/adlibmusic.cpp
    engines/sky/music/adlibmusic.h
    engines/tsage/sound.cpp
    engines/tsage/sound.h



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 00e49ef..3ee5573 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -174,40 +174,3 @@ OPL *Config::create(DriverId driver, OplType type) {
 bool OPL::_hasInstance = false;
 
 } // End of namespace OPL
-
-void OPLDestroy(FM_OPL *OPL) {
-	delete OPL;
-}
-
-void OPLResetChip(FM_OPL *OPL) {
-	OPL->reset();
-}
-
-void OPLWrite(FM_OPL *OPL, int a, int v) {
-	OPL->write(a, v);
-}
-
-unsigned char OPLRead(FM_OPL *OPL, int a) {
-	return OPL->read(a);
-}
-
-void OPLWriteReg(FM_OPL *OPL, int r, int v) {
-	OPL->writeReg(r, v);
-}
-
-void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length) {
-	OPL->readBuffer(buffer, length);
-}
-
-FM_OPL *makeAdLibOPL(int rate) {
-	FM_OPL *opl = OPL::Config::create();
-
-	if (opl) {
-		if (!opl->init(rate)) {
-			delete opl;
-			opl = 0;
-		}
-	}
-
-	return opl;
-}
diff --git a/audio/fmopl.h b/audio/fmopl.h
index 28dd456..b8dbdc1 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -165,23 +165,4 @@ public:
 
 } // End of namespace OPL
 
-// Legacy API
-// !You should not write any new code using the legacy API!
-typedef OPL::OPL FM_OPL;
-
-void OPLDestroy(FM_OPL *OPL);
-
-void OPLResetChip(FM_OPL *OPL);
-void OPLWrite(FM_OPL *OPL, int a, int v);
-unsigned char OPLRead(FM_OPL *OPL, int a);
-void OPLWriteReg(FM_OPL *OPL, int r, int v);
-void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);
-
-/**
- * Legacy factory to create an AdLib (OPL2) chip.
- *
- * !You should not write any new code using the legacy API!
- */
-FM_OPL *makeAdLibOPL(int rate);
-
 #endif
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index 069a478..8da6dba 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -128,7 +128,7 @@ protected:
 	UpdateCallback _upCb;
 	void *_upRef;
 
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	int _sampleRate;
 	Audio::Mixer *_mixer;
 	Audio::SoundHandle _soundHandle;
@@ -282,8 +282,12 @@ void PCSoundDriver::resetChannel(int channel) {
 
 AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	: _upCb(0), _upRef(0), _mixer(mixer) {
+
 	_sampleRate = _mixer->getOutputRate();
-	_opl = makeAdLibOPL(_sampleRate);
+	_opl = OPL::Config::create();
+	if (!_opl || !_opl->init(_sampleRate))
+		error("Failed to create OPL");
+
 	memset(_channelsVolumeTable, 0, sizeof(_channelsVolumeTable));
 	memset(_instrumentsTable, 0, sizeof(_instrumentsTable));
 	initCard();
@@ -292,7 +296,7 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 
 AdLibSoundDriver::~AdLibSoundDriver() {
 	_mixer->stopHandle(_soundHandle);
-	OPLDestroy(_opl);
+	delete _opl;
 }
 
 void AdLibSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) {
@@ -322,23 +326,23 @@ void AdLibSoundDriver::stopChannel(int channel) {
 		channel = 6;
 	}
 	if (ins->mode == 0 || channel == 6) {
-		OPLWriteReg(_opl, 0xB0 | channel, 0);
+		_opl->writeReg(0xB0 | channel, 0);
 	}
 	if (ins->mode != 0) {
 		_vibrato &= ~(1 << (10 - ins->channel));
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
 void AdLibSoundDriver::stopAll() {
 	int i;
 	for (i = 0; i < 18; ++i) {
-		OPLWriteReg(_opl, 0x40 | _operatorsTable[i], 63);
+		_opl->writeReg(0x40 | _operatorsTable[i], 63);
 	}
 	for (i = 0; i < 9; ++i) {
-		OPLWriteReg(_opl, 0xB0 | i, 0);
+		_opl->writeReg(0xB0 | i, 0);
 	}
-	OPLWriteReg(_opl, 0xBD, 0);
+	_opl->writeReg(0xBD, 0);
 }
 
 int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
@@ -348,26 +352,26 @@ int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
 
 void AdLibSoundDriver::initCard() {
 	_vibrato = 0x20;
-	OPLWriteReg(_opl, 0xBD, _vibrato);
-	OPLWriteReg(_opl, 0x08, 0x40);
+	_opl->writeReg(0xBD, _vibrato);
+	_opl->writeReg(0x08, 0x40);
 
 	static const int oplRegs[] = { 0x40, 0x60, 0x80, 0x20, 0xE0 };
 
 	for (int i = 0; i < 9; ++i) {
-		OPLWriteReg(_opl, 0xB0 | i, 0);
+		_opl->writeReg(0xB0 | i, 0);
 	}
 	for (int i = 0; i < 9; ++i) {
-		OPLWriteReg(_opl, 0xC0 | i, 0);
+		_opl->writeReg(0xC0 | i, 0);
 	}
 
 	for (int j = 0; j < 5; j++) {
 		for (int i = 0; i < 18; ++i) {
-			OPLWriteReg(_opl, oplRegs[j] | _operatorsTable[i], 0);
+			_opl->writeReg(oplRegs[j] | _operatorsTable[i], 0);
 		}
 	}
 
-	OPLWriteReg(_opl, 1, 0x20);
-	OPLWriteReg(_opl, 1, 0);
+	_opl->writeReg(1, 0x20);
+	_opl->writeReg(1, 0);
 }
 
 void AdLibSoundDriver::update(int16 *buf, int len) {
@@ -379,7 +383,7 @@ void AdLibSoundDriver::update(int16 *buf, int len) {
 		}
 		samplesLeft -= count;
 		len -= count;
-		YM3812UpdateOne(_opl, buf, count);
+		_opl->readBuffer(buf, count);
 		if (samplesLeft == 0) {
 			if (_upCb) {
 				(*_upCb)(_upRef);
@@ -408,32 +412,32 @@ void AdLibSoundDriver::setupInstrument(const byte *data, int channel) {
 
 	if (ins->mode == 0 || ins->channel == 6) {
 		reg = &ins->regMod;
-		OPLWriteReg(_opl, 0x20 | mod, reg->vibrato);
+		_opl->writeReg(0x20 | mod, reg->vibrato);
 		if (reg->freqMod) {
 			tmp = reg->outputLevel & 0x3F;
 		} else {
 			tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel];
 			tmp = 63 - (2 * tmp + 127) / (2 * 127);
 		}
-		OPLWriteReg(_opl, 0x40 | mod, tmp | (reg->keyScaling << 6));
-		OPLWriteReg(_opl, 0x60 | mod, reg->attackDecay);
-		OPLWriteReg(_opl, 0x80 | mod, reg->sustainRelease);
+		_opl->writeReg(0x40 | mod, tmp | (reg->keyScaling << 6));
+		_opl->writeReg(0x60 | mod, reg->attackDecay);
+		_opl->writeReg(0x80 | mod, reg->sustainRelease);
 		if (ins->mode != 0) {
-			OPLWriteReg(_opl, 0xC0 | ins->channel, reg->feedbackStrength);
+			_opl->writeReg(0xC0 | ins->channel, reg->feedbackStrength);
 		} else {
-			OPLWriteReg(_opl, 0xC0 | channel, reg->feedbackStrength);
+			_opl->writeReg(0xC0 | channel, reg->feedbackStrength);
 		}
-		OPLWriteReg(_opl, 0xE0 | mod, ins->waveSelectMod);
+		_opl->writeReg(0xE0 | mod, ins->waveSelectMod);
 	}
 
 	reg = &ins->regCar;
-	OPLWriteReg(_opl, 0x20 | car, reg->vibrato);
+	_opl->writeReg(0x20 | car, reg->vibrato);
 	tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel];
 	tmp = 63 - (2 * tmp + 127) / (2 * 127);
-	OPLWriteReg(_opl, 0x40 | car, tmp | (reg->keyScaling << 6));
-	OPLWriteReg(_opl, 0x60 | car, reg->attackDecay);
-	OPLWriteReg(_opl, 0x80 | car, reg->sustainRelease);
-	OPLWriteReg(_opl, 0xE0 | car, ins->waveSelectCar);
+	_opl->writeReg(0x40 | car, tmp | (reg->keyScaling << 6));
+	_opl->writeReg(0x60 | car, reg->attackDecay);
+	_opl->writeReg(0x80 | car, reg->sustainRelease);
+	_opl->writeReg(0xE0 | car, ins->waveSelectCar);
 }
 
 void AdLibSoundDriver::loadRegisterInstrument(const byte *data, AdLibRegisterSoundInstrument *reg) {
@@ -490,16 +494,16 @@ void AdLibSoundDriverINS::setChannelFrequency(int channel, int frequency) {
 		if (channel == 6)
 			oct = 0;
 		freq = _freqTable[note % 12];
-		OPLWriteReg(_opl, 0xA0 | channel, freq);
+		_opl->writeReg(0xA0 | channel, freq);
 		freq = (oct << 2) | ((freq & 0x300) >> 8);
 		if (ins->mode == 0) {
 			freq |= 0x20;
 		}
-		OPLWriteReg(_opl, 0xB0 | channel, freq);
+		_opl->writeReg(0xB0 | channel, freq);
 	}
 	if (ins->mode != 0) {
 		_vibrato |= 1 << (10 - ins->channel);
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
@@ -515,16 +519,16 @@ void AdLibSoundDriverINS::playSample(const byte *data, int size, int channel, in
 	if (ins->mode == 0 || channel == 6) {
 		uint16 note = 12;
 		int freq = _freqTable[note % 12];
-		OPLWriteReg(_opl, 0xA0 | channel, freq);
+		_opl->writeReg(0xA0 | channel, freq);
 		freq = ((note / 12) << 2) | ((freq & 0x300) >> 8);
 		if (ins->mode == 0) {
 			freq |= 0x20;
 		}
-		OPLWriteReg(_opl, 0xB0 | channel, freq);
+		_opl->writeReg(0xB0 | channel, freq);
 	}
 	if (ins->mode != 0) {
 		_vibrato |= 1 << (10 - ins->channel);
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
@@ -562,15 +566,15 @@ void AdLibSoundDriverADL::setChannelFrequency(int channel, int frequency) {
 	}
 
 	freq = _freqTable[note % 12];
-	OPLWriteReg(_opl, 0xA0 | channel, freq);
+	_opl->writeReg(0xA0 | channel, freq);
 	freq = (oct << 2) | ((freq & 0x300) >> 8);
 	if (ins->mode == 0) {
 		freq |= 0x20;
 	}
-	OPLWriteReg(_opl, 0xB0 | channel, freq);
+	_opl->writeReg(0xB0 | channel, freq);
 	if (ins->mode != 0) {
 		_vibrato |= 1 << (10 - channel);
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
@@ -580,11 +584,11 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in
 	setupInstrument(data, channel);
 	AdLibSoundInstrument *ins = &_instrumentsTable[channel];
 	if (ins->mode != 0 && ins->channel == 6) {
-		OPLWriteReg(_opl, 0xB0 | channel, 0);
+		_opl->writeReg(0xB0 | channel, 0);
 	}
 	if (ins->mode != 0) {
 		_vibrato &= ~(1 << (10 - ins->channel));
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 	if (ins->mode != 0) {
 		channel = ins->channel;
@@ -599,15 +603,15 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in
 		note = ins->amDepth;
 	}
 	int freq = _freqTable[note % 12];
-	OPLWriteReg(_opl, 0xA0 | channel, freq);
+	_opl->writeReg(0xA0 | channel, freq);
 	freq = ((note / 12) << 2) | ((freq & 0x300) >> 8);
 	if (ins->mode == 0) {
 		freq |= 0x20;
 	}
-	OPLWriteReg(_opl, 0xB0 | channel, freq);
+	_opl->writeReg(0xB0 | channel, freq);
 	if (ins->mode != 0) {
 		_vibrato |= 1 << (10 - channel);
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp
index 0b0fab8..0bf9bf7 100644
--- a/engines/cruise/sound.cpp
+++ b/engines/cruise/sound.cpp
@@ -135,7 +135,7 @@ public:
 	void adjustVolume(int channel, int volume);
 
 protected:
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	int _sampleRate;
 	Audio::Mixer *_mixer;
 	Audio::SoundHandle _soundHandle;
@@ -303,7 +303,9 @@ void PCSoundDriver::syncSounds() {
 AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	: _mixer(mixer) {
 	_sampleRate = _mixer->getOutputRate();
-	_opl = makeAdLibOPL(_sampleRate);
+	_opl = OPL::Config::create();
+	if (!_opl || !_opl->init(_sampleRate))
+		error("Failed to create OPL");
 
 	for (int i = 0; i < 5; ++i) {
 		_channelsVolumeTable[i].original = 0;
@@ -319,7 +321,7 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 
 AdLibSoundDriver::~AdLibSoundDriver() {
 	_mixer->stopHandle(_soundHandle);
-	OPLDestroy(_opl);
+	delete _opl;
 }
 
 void AdLibSoundDriver::syncSounds() {
@@ -368,22 +370,22 @@ void AdLibSoundDriver::stopChannel(int channel) {
 		channel = 6;
 	}
 	if (ins->mode == 0 || channel == 6) {
-		OPLWriteReg(_opl, 0xB0 | channel, 0);
+		_opl->writeReg(0xB0 | channel, 0);
 	}
 	if (ins->mode != 0) {
 		_vibrato &= ~(1 << (10 - ins->channel));
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
 void AdLibSoundDriver::stopAll() {
 	for (int i = 0; i < 18; ++i)
-		OPLWriteReg(_opl, 0x40 | _operatorsTable[i], 63);
+		_opl->writeReg(0x40 | _operatorsTable[i], 63);
 
 	for (int i = 0; i < 9; ++i)
-		OPLWriteReg(_opl, 0xB0 | i, 0);
+		_opl->writeReg(0xB0 | i, 0);
 
-	OPLWriteReg(_opl, 0xBD, 0);
+	_opl->writeReg(0xBD, 0);
 }
 
 int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
@@ -393,26 +395,26 @@ int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
 
 void AdLibSoundDriver::initCard() {
 	_vibrato = 0x20;
-	OPLWriteReg(_opl, 0xBD, _vibrato);
-	OPLWriteReg(_opl, 0x08, 0x40);
+	_opl->writeReg(0xBD, _vibrato);
+	_opl->writeReg(0x08, 0x40);
 
 	static const int oplRegs[] = { 0x40, 0x60, 0x80, 0x20, 0xE0 };
 
 	for (int i = 0; i < 9; ++i) {
-		OPLWriteReg(_opl, 0xB0 | i, 0);
+		_opl->writeReg(0xB0 | i, 0);
 	}
 	for (int i = 0; i < 9; ++i) {
-		OPLWriteReg(_opl, 0xC0 | i, 0);
+		_opl->writeReg(0xC0 | i, 0);
 	}
 
 	for (int j = 0; j < 5; j++) {
 		for (int i = 0; i < 18; ++i) {
-			OPLWriteReg(_opl, oplRegs[j] | _operatorsTable[i], 0);
+			_opl->writeReg(oplRegs[j] | _operatorsTable[i], 0);
 		}
 	}
 
-	OPLWriteReg(_opl, 1, 0x20);
-	OPLWriteReg(_opl, 1, 0);
+	_opl->writeReg(1, 0x20);
+	_opl->writeReg(1, 0);
 }
 
 void AdLibSoundDriver::update(int16 *buf, int len) {
@@ -424,7 +426,7 @@ void AdLibSoundDriver::update(int16 *buf, int len) {
 		}
 		samplesLeft -= count;
 		len -= count;
-		YM3812UpdateOne(_opl, buf, count);
+		_opl->readBuffer(buf, count);
 		if (samplesLeft == 0) {
 			if (_upCb) {
 				(*_upCb)(_upRef);
@@ -457,32 +459,32 @@ void AdLibSoundDriver::setupInstrument(const AdLibSoundInstrument *ins, int chan
 
 	if (ins->mode == 0 || ins->channel == 6) {
 		reg = &ins->regMod;
-		OPLWriteReg(_opl, 0x20 | mod, reg->vibrato);
+		_opl->writeReg(0x20 | mod, reg->vibrato);
 		if (reg->freqMod) {
 			tmp = reg->outputLevel & 0x3F;
 		} else {
 			tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel].adjusted;
 			tmp = 63 - (2 * tmp + 127) / (2 * 127);
 		}
-		OPLWriteReg(_opl, 0x40 | mod, tmp | (reg->keyScaling << 6));
-		OPLWriteReg(_opl, 0x60 | mod, reg->attackDecay);
-		OPLWriteReg(_opl, 0x80 | mod, reg->sustainRelease);
+		_opl->writeReg(0x40 | mod, tmp | (reg->keyScaling << 6));
+		_opl->writeReg(0x60 | mod, reg->attackDecay);
+		_opl->writeReg(0x80 | mod, reg->sustainRelease);
 		if (ins->mode != 0) {
-			OPLWriteReg(_opl, 0xC0 | ins->channel, reg->feedbackStrength);
+			_opl->writeReg(0xC0 | ins->channel, reg->feedbackStrength);
 		} else {
-			OPLWriteReg(_opl, 0xC0 | channel, reg->feedbackStrength);
+			_opl->writeReg(0xC0 | channel, reg->feedbackStrength);
 		}
-		OPLWriteReg(_opl, 0xE0 | mod, ins->waveSelectMod);
+		_opl->writeReg(0xE0 | mod, ins->waveSelectMod);
 	}
 
 	reg = &ins->regCar;
-	OPLWriteReg(_opl, 0x20 | car, reg->vibrato);
+	_opl->writeReg(0x20 | car, reg->vibrato);
 	tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel].adjusted;
 	tmp = 63 - (2 * tmp + 127) / (2 * 127);
-	OPLWriteReg(_opl, 0x40 | car, tmp | (reg->keyScaling << 6));
-	OPLWriteReg(_opl, 0x60 | car, reg->attackDecay);
-	OPLWriteReg(_opl, 0x80 | car, reg->sustainRelease);
-	OPLWriteReg(_opl, 0xE0 | car, ins->waveSelectCar);
+	_opl->writeReg(0x40 | car, tmp | (reg->keyScaling << 6));
+	_opl->writeReg(0x60 | car, reg->attackDecay);
+	_opl->writeReg(0x80 | car, reg->sustainRelease);
+	_opl->writeReg(0xE0 | car, ins->waveSelectCar);
 }
 
 void AdLibSoundDriver::loadRegisterInstrument(const byte *data, AdLibRegisterSoundInstrument *reg) {
@@ -551,15 +553,15 @@ void AdLibSoundDriverADL::setChannelFrequency(int channel, int frequency) {
 	}
 
 	freq = _freqTable[note % 12];
-	OPLWriteReg(_opl, 0xA0 | channel, freq);
+	_opl->writeReg(0xA0 | channel, freq);
 	freq = ((note / 12) << 2) | ((freq & 0x300) >> 8);
 	if (ins->mode == 0) {
 		freq |= 0x20;
 	}
-	OPLWriteReg(_opl, 0xB0 | channel, freq);
+	_opl->writeReg(0xB0 | channel, freq);
 	if (ins->mode != 0) {
 		_vibrato |= 1 << (10 - channel);
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
@@ -570,11 +572,11 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in
 	setupInstrument(data, channel);
 	AdLibSoundInstrument *ins = &_instrumentsTable[channel];
 	if (ins->mode != 0 && ins->channel == 6) {
-		OPLWriteReg(_opl, 0xB0 | channel, 0);
+		_opl->writeReg(0xB0 | channel, 0);
 	}
 	if (ins->mode != 0) {
 		_vibrato &= ~(1 << (10 - ins->channel));
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 	if (ins->mode != 0) {
 		channel = ins->channel;
@@ -589,15 +591,15 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in
 		note = ins->amDepth;
 	}
 	int freq = _freqTable[note % 12];
-	OPLWriteReg(_opl, 0xA0 | channel, freq);
+	_opl->writeReg(0xA0 | channel, freq);
 	freq = ((note / 12) << 2) | ((freq & 0x300) >> 8);
 	if (ins->mode == 0) {
 		freq |= 0x20;
 	}
-	OPLWriteReg(_opl, 0xB0 | channel, freq);
+	_opl->writeReg(0xB0 | channel, freq);
 	if (ins->mode != 0) {
 		_vibrato |= 1 << (10 - channel);
-		OPLWriteReg(_opl, 0xBD, _vibrato);
+		_opl->writeReg(0xBD, _vibrato);
 	}
 }
 
diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index c0e0f67..203931f 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -88,7 +88,7 @@ public:
 			int32 render = MIN(samplesLeft, _samplesTillCallback);
 			samplesLeft -= render;
 			_samplesTillCallback -= render;
-			YM3812UpdateOne(_adlib, buffer, render);
+			_adlib->readBuffer(buffer, render);
 			buffer += render;
 		}
 		return numSamples;
@@ -365,7 +365,7 @@ private:
 	uint8 _unkValue19;
 	uint8 _unkValue20;
 
-	FM_OPL *_adlib;
+	OPL::OPL *_adlib;
 
 	uint8 *_soundData;
 	uint32 _soundDataSize;
@@ -427,8 +427,9 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
 
 	_mixer = mixer;
 
-	_adlib = makeAdLibOPL(getRate());
-	assert(_adlib);
+	_adlib = OPL::Config::create();
+	if (!_adlib || !_adlib->init(getRate()))
+		error("Failed to create OPL");
 
 	memset(_channels, 0, sizeof(_channels));
 	_soundData = 0;
@@ -471,7 +472,7 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
 
 AdLibDriver::~AdLibDriver() {
 	_mixer->stopHandle(_soundHandle);
-	OPLDestroy(_adlib);
+	delete _adlib;
 	_adlib = 0;
 }
 
@@ -877,7 +878,7 @@ void AdLibDriver::resetAdLibState() {
 // New calling style: writeOPL(0xAB, 0xCD)
 
 void AdLibDriver::writeOPL(byte reg, byte val) {
-	OPLWriteReg(_adlib, reg, val);
+	_adlib->writeReg(reg, val);
 }
 
 void AdLibDriver::initChannel(Channel &channel) {
diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp
index 240c18f..b0a0938 100644
--- a/engines/mads/nebular/sound_nebular.cpp
+++ b/engines/mads/nebular/sound_nebular.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "audio/audiostream.h"
+#include "audio/fmopl.h"
 #include "audio/decoders/raw.h"
 #include "common/algorithm.h"
 #include "common/debug.h"
@@ -156,7 +157,7 @@ AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
 
 /*-----------------------------------------------------------------------*/
 
-ASound::ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset) {
+ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filename, int dataOffset) {
 	// Open up the appropriate sound file
 	if (!_soundFile.open(filename))
 		error("Could not open file - %s", filename.c_str());
@@ -984,7 +985,7 @@ const ASound1::CommandPtr ASound1::_commandList[42] = {
 	&ASound1::command40, &ASound1::command41
 };
 
-ASound1::ASound1(Audio::Mixer *mixer, FM_OPL *opl)
+ASound1::ASound1(Audio::Mixer *mixer, OPL::OPL *opl)
 	: ASound(mixer, opl, "asound.001", 0x1520) {
 	_cmd23Toggle = false;
 
@@ -1285,7 +1286,7 @@ const ASound2::CommandPtr ASound2::_commandList[44] = {
 	&ASound2::command40, &ASound2::command41, &ASound2::command42, &ASound2::command43
 };
 
-ASound2::ASound2(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) {
+ASound2::ASound2(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) {
 	_command12Param = 0xFD;
 
 	// Load sound samples
@@ -1656,7 +1657,7 @@ const ASound3::CommandPtr ASound3::_commandList[61] = {
 	&ASound3::command60
 };
 
-ASound3::ASound3(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.003", 0x15B0) {
+ASound3::ASound3(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.003", 0x15B0) {
 	_command39Flag = false;
 
 	// Load sound samples
@@ -2060,7 +2061,7 @@ const ASound4::CommandPtr ASound4::_commandList[61] = {
 	&ASound4::command60
 };
 
-ASound4::ASound4(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.004", 0x14F0) {
+ASound4::ASound4(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.004", 0x14F0) {
 	// Load sound samples
 	_soundFile.seek(_dataOffset + 0x122);
 	for (int i = 0; i < 210; ++i)
@@ -2316,7 +2317,7 @@ const ASound5::CommandPtr ASound5::_commandList[42] = {
 	&ASound5::command40, &ASound5::command41
 };
 
-ASound5::ASound5(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) {
+ASound5::ASound5(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) {
 	// Load sound samples
 	_soundFile.seek(_dataOffset + 0x144);
 	for (int i = 0; i < 164; ++i)
@@ -2557,7 +2558,7 @@ const ASound6::CommandPtr ASound6::_commandList[30] = {
 	&ASound6::nullCommand, &ASound6::command29
 };
 
-ASound6::ASound6(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.006", 0x1390) {
+ASound6::ASound6(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.006", 0x1390) {
 	// Load sound samples
 	_soundFile.seek(_dataOffset + 0x122);
 	for (int i = 0; i < 200; ++i)
@@ -2713,7 +2714,7 @@ const ASound7::CommandPtr ASound7::_commandList[38] = {
 	&ASound7::command36, &ASound7::command37
 };
 
-ASound7::ASound7(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.007", 0x1460) {
+ASound7::ASound7(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.007", 0x1460) {
 	// Load sound samples
 	_soundFile.seek(_dataOffset + 0x122);
 	for (int i = 0; i < 214; ++i)
@@ -2919,7 +2920,7 @@ const ASound8::CommandPtr ASound8::_commandList[38] = {
 	&ASound8::command36, &ASound8::command37
 };
 
-ASound8::ASound8(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.008", 0x1490) {
+ASound8::ASound8(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.008", 0x1490) {
 	// Load sound samples
 	_soundFile.seek(_dataOffset + 0x122);
 	for (int i = 0; i < 174; ++i)
@@ -3175,7 +3176,7 @@ const ASound9::CommandPtr ASound9::_commandList[52] = {
 	&ASound9::command48, &ASound9::command49, &ASound9::command50, &ASound9::command51
 };
 
-ASound9::ASound9(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.009", 0x16F0) {
+ASound9::ASound9(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.009", 0x16F0) {
 	_v1 = _v2 = 0;
 	_soundPtr = nullptr;
 
diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h
index 9bc1a49..e095632 100644
--- a/engines/mads/nebular/sound_nebular.h
+++ b/engines/mads/nebular/sound_nebular.h
@@ -28,9 +28,12 @@
 #include "common/mutex.h"
 #include "common/queue.h"
 #include "audio/audiostream.h"
-#include "audio/fmopl.h"
 #include "audio/mixer.h"
 
+namespace OPL {
+class OPL;
+}
+
 namespace MADS {
 
 class SoundManager;
@@ -273,7 +276,7 @@ protected:
 	int nullCommand() { return 0; }
 public:
 	Audio::Mixer *_mixer;
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	Audio::SoundHandle _soundHandle;
 	AdlibChannel _channels[ADLIB_CHANNEL_COUNT];
 	AdlibChannel *_activeChannelPtr;
@@ -318,7 +321,7 @@ public:
 	 * @param filename		Specifies the adlib sound player file to use
 	 * @param dataOffset	Offset in the file of the data segment
 	 */
-	ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset);
+	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filename, int dataOffset);
 
 	/**
 	 * Destructor
@@ -433,7 +436,7 @@ private:
 	void command111213();
 	int command2627293032();
 public:
-	ASound1(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound1(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -485,7 +488,7 @@ private:
 	void command9Randomize();
 	void command9Apply(byte *data, int val, int incr);
 public:
-	ASound2(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound2(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -545,7 +548,7 @@ private:
 	void command9Randomize();
 	void command9Apply(byte *data, int val, int incr);
 public:
-	ASound3(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound3(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -583,7 +586,7 @@ private:
 
 	void method1();
 public:
-	ASound4(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound4(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -629,7 +632,7 @@ private:
 	int command42();
 	int command43();
 public:
-	ASound5(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound5(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -658,7 +661,7 @@ private:
 	int command25();
 	int command29();
 public:
-	ASound6(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound6(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -690,7 +693,7 @@ private:
 	int command36();
 	int command37();
 public:
-	ASound7(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound7(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -733,7 +736,7 @@ private:
 	void method1(byte *pData);
 	void adjustRange(byte *pData, byte v, int incr);
 public:
-	ASound8(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound8(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
@@ -792,7 +795,7 @@ private:
 	int command59();
 	int command60();
 public:
-	ASound9(Audio::Mixer *mixer, FM_OPL *opl);
+	ASound9(Audio::Mixer *mixer, OPL::OPL *opl);
 
 	virtual int command(int commandId, int param);
 };
diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp
index 7b9388e..09bc3a3 100644
--- a/engines/mads/sound.cpp
+++ b/engines/mads/sound.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "audio/audiostream.h"
+#include "audio/fmopl.h"
 #include "audio/decoders/raw.h"
 #include "common/memstream.h"
 #include "mads/sound.h"
diff --git a/engines/mads/sound.h b/engines/mads/sound.h
index 16128f8..9882f65 100644
--- a/engines/mads/sound.h
+++ b/engines/mads/sound.h
@@ -37,7 +37,7 @@ class SoundManager {
 private:
 	MADSEngine *_vm;
 	Audio::Mixer *_mixer;
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	Nebular::ASound *_driver;
 	bool _pollSoundEnabled;
 	bool _soundPollFlag;
diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp
index 25175c2..6bf0d59 100644
--- a/engines/queen/midiadlib.cpp
+++ b/engines/queen/midiadlib.cpp
@@ -81,7 +81,7 @@ private:
 	void adlibSetChannel0x20(int channel);
 	void adlibSetChannel0xE0(int channel);
 
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	int _midiNumberOfChannels;
 	int _adlibNoteMul;
 	int _adlibWaveformSelect;
@@ -121,7 +121,10 @@ private:
 
 int AdLibMidiDriver::open() {
 	MidiDriver_Emulated::open();
-	_opl = makeAdLibOPL(getRate());
+	_opl = OPL::Config::create();
+	if (!_opl || !_opl->init(getRate()))
+		error("Failed to create OPL");
+
 	adlibSetupCard();
 	for (int i = 0; i < 11; ++i) {
 		_adlibChannelsVolume[i] = 0;
@@ -134,7 +137,7 @@ int AdLibMidiDriver::open() {
 
 void AdLibMidiDriver::close() {
 	_mixer->stopHandle(_mixerSoundHandle);
-	OPLDestroy(_opl);
+	delete _opl;
 }
 
 void AdLibMidiDriver::send(uint32 b) {
@@ -194,7 +197,7 @@ void AdLibMidiDriver::metaEvent(byte type, byte *data, uint16 length) {
 
 void AdLibMidiDriver::generateSamples(int16 *data, int len) {
 	memset(data, 0, sizeof(int16) * len);
-	YM3812UpdateOne(_opl, data, len);
+	_opl->readBuffer(data, len);
 }
 
 void AdLibMidiDriver::handleSequencerSpecificMetaEvent1(int channel, const uint8 *data) {
@@ -238,7 +241,7 @@ void AdLibMidiDriver::handleMidiEvent0x90_NoteOn(int channel, int param1, int pa
 }
 
 void AdLibMidiDriver::adlibWrite(uint8 port, uint8 value) {
-	OPLWriteReg(_opl, port, value);
+	_opl->writeReg(port, value);
 }
 
 void AdLibMidiDriver::adlibSetupCard() {
diff --git a/engines/sky/music/adlibchannel.cpp b/engines/sky/music/adlibchannel.cpp
index 8400fef..b57f20f 100644
--- a/engines/sky/music/adlibchannel.cpp
+++ b/engines/sky/music/adlibchannel.cpp
@@ -29,7 +29,7 @@
 
 namespace Sky {
 
-AdLibChannel::AdLibChannel(FM_OPL *opl, uint8 *pMusicData, uint16 startOfData) {
+AdLibChannel::AdLibChannel(OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData) {
 	_opl = opl;
 	_musicData = pMusicData;
 	_channelData.loopPoint = startOfData;
@@ -95,7 +95,7 @@ void AdLibChannel::updateVolume(uint16 pVolume) {
 */
 void AdLibChannel::setRegister(uint8 regNum, uint8 value) {
 	if (_adlibRegMirror[regNum] != value) {
-		OPLWriteReg (_opl, regNum, value);
+		_opl->writeReg(regNum, value);
 		_adlibRegMirror[regNum] = value;
 	}
 }
diff --git a/engines/sky/music/adlibchannel.h b/engines/sky/music/adlibchannel.h
index 80dae93..240dd5c 100644
--- a/engines/sky/music/adlibchannel.h
+++ b/engines/sky/music/adlibchannel.h
@@ -60,13 +60,13 @@ typedef struct {
 
 class AdLibChannel : public ChannelBase {
 public:
-	AdLibChannel (FM_OPL *opl, uint8 *pMusicData, uint16 startOfData);
+	AdLibChannel (OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData);
 	virtual ~AdLibChannel();
 	virtual uint8 process(uint16 aktTime);
 	virtual void updateVolume(uint16 pVolume);
 	virtual bool isActive();
 private:
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	uint8 *_musicData;
 	AdLibChannelType _channelData;
 
diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp
index dd64c5b..2b73cb1 100644
--- a/engines/sky/music/adlibmusic.cpp
+++ b/engines/sky/music/adlibmusic.cpp
@@ -22,6 +22,7 @@
 
 
 #include "common/endian.h"
+#include "common/textconsole.h"
 
 #include "sky/music/adlibmusic.h"
 #include "sky/music/adlibchannel.h"
@@ -34,14 +35,16 @@ AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pD
 	_driverFileBase = 60202;
 	_sampleRate = pMixer->getOutputRate();
 
-	_opl = makeAdLibOPL(_sampleRate);
+	_opl = OPL::Config::create();
+	if (!_opl || !_opl->init(_sampleRate))
+		error("Failed to create OPL");
 
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibMusic::~AdLibMusic() {
-	OPLDestroy(_opl);
 	_mixer->stopHandle(_soundHandle);
+	delete _opl;
 }
 
 int AdLibMusic::readBuffer(int16 *data, const int numSamples) {
@@ -61,7 +64,7 @@ int AdLibMusic::readBuffer(int16 *data, const int numSamples) {
 			render = (remaining > _nextMusicPoll) ? _nextMusicPoll : remaining;
 			remaining -= render;
 			_nextMusicPoll -= render;
-			YM3812UpdateOne(_opl, data, render);
+			_opl->readBuffer(data, render);
 			data += render;
 			if (_nextMusicPoll == 0) {
 				pollMusic();
@@ -102,7 +105,7 @@ void AdLibMusic::setupChannels(uint8 *channelData) {
 void AdLibMusic::startDriver() {
 	uint16 cnt = 0;
 	while (_initSequence[cnt] || _initSequence[cnt + 1]) {
-		OPLWriteReg (_opl, _initSequence[cnt], _initSequence[cnt + 1]);
+		_opl->writeReg(_initSequence[cnt], _initSequence[cnt + 1]);
 		cnt += 2;
 	}
 }
diff --git a/engines/sky/music/adlibmusic.h b/engines/sky/music/adlibmusic.h
index 886eef0..9a0796d 100644
--- a/engines/sky/music/adlibmusic.h
+++ b/engines/sky/music/adlibmusic.h
@@ -25,7 +25,10 @@
 
 #include "sky/music/musicbase.h"
 #include "audio/audiostream.h"
-#include "audio/fmopl.h"
+
+namespace OPL {
+class OPL;
+}
 
 namespace Sky {
 
@@ -42,7 +45,7 @@ public:
 	virtual void setVolume(uint16 param);
 
 private:
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	Audio::SoundHandle _soundHandle;
 	uint8 *_initSequence;
 	uint32 _sampleRate, _nextMusicPoll;
diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index b95b614..455b58d 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -20,9 +20,9 @@
  *
  */
 
+#include "audio/fmopl.h"
 #include "audio/decoders/raw.h"
 #include "common/config-manager.h"
-#include "audio/decoders/raw.h"
 #include "audio/audiostream.h"
 #include "tsage/core.h"
 #include "tsage/globals.h"
diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h
index 49558b4..c222a6e 100644
--- a/engines/tsage/sound.h
+++ b/engines/tsage/sound.h
@@ -27,12 +27,15 @@
 #include "common/mutex.h"
 #include "common/queue.h"
 #include "audio/audiostream.h"
-#include "audio/fmopl.h"
 #include "audio/mixer.h"
 #include "common/list.h"
 #include "tsage/saveload.h"
 #include "tsage/core.h"
 
+namespace OPL {
+class OPL;
+}
+
 namespace TsAGE {
 
 class Sound;
@@ -450,7 +453,7 @@ class AdlibSoundDriver: public SoundDriver, Audio::AudioStream {
 private:
 	GroupData _groupData;
 	Audio::Mixer *_mixer;
-	FM_OPL *_opl;
+	OPL::OPL *_opl;
 	Audio::SoundHandle _soundHandle;
 	int _sampleRate;
 	byte _portContents[256];


Commit: 2e8f9dcec93653f1bd1f115662f9fcf3a5581fd8
    https://github.com/scummvm/scummvm/commit/2e8f9dcec93653f1bd1f115662f9fcf3a5581fd8
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:42-04:00

Commit Message:
AUDIO: Remove the sample rate configuration from the OPL code

Changed paths:
    audio/fmopl.h
    audio/miles_adlib.cpp
    audio/softsynth/adlib.cpp
    audio/softsynth/opl/dosbox.cpp
    audio/softsynth/opl/dosbox.h
    audio/softsynth/opl/mame.cpp
    audio/softsynth/opl/mame.h
    engines/agos/drivers/accolade/adlib.cpp
    engines/cine/sound.cpp
    engines/cruise/sound.cpp
    engines/gob/sound/adlib.cpp
    engines/kyra/sound_adlib.cpp
    engines/mads/nebular/sound_nebular.cpp
    engines/mads/nebular/sound_nebular.h
    engines/mads/sound.cpp
    engines/parallaction/adlib.cpp
    engines/queen/midiadlib.cpp
    engines/sci/sound/drivers/adlib.cpp
    engines/scumm/players/player_ad.cpp
    engines/sherlock/scalpel/drivers/adlib.cpp
    engines/sky/music/adlibmusic.cpp
    engines/tsage/sound.cpp



diff --git a/audio/fmopl.h b/audio/fmopl.h
index b8dbdc1..aaa8edd 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -108,10 +108,9 @@ public:
 	/**
 	 * Initializes the OPL emulator.
 	 *
-	 * @param rate	output sample rate
 	 * @return		true on success, false on failure
 	 */
-	virtual bool init(int rate) = 0;
+	virtual bool init() = 0;
 
 	/**
 	 * Reinitializes the OPL emulator
diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp
index 903b0a9..4560a81 100644
--- a/audio/miles_adlib.cpp
+++ b/audio/miles_adlib.cpp
@@ -298,8 +298,6 @@ MidiDriver_Miles_AdLib::~MidiDriver_Miles_AdLib() {
 }
 
 int MidiDriver_Miles_AdLib::open() {
-	int rate = _mixer->getOutputRate();
-
 	if (_modeOPL3) {
 		// Try to create OPL3 first
 		_opl = OPL::Config::create(OPL::Config::kOpl3);
@@ -319,7 +317,7 @@ int MidiDriver_Miles_AdLib::open() {
 		return -1;
 	}
 
-	_opl->init(rate);
+	_opl->init();
 
 	MidiDriver_Emulated::open();
 
diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp
index 9851934..49e69ec 100644
--- a/audio/softsynth/adlib.cpp
+++ b/audio/softsynth/adlib.cpp
@@ -1434,7 +1434,7 @@ int MidiDriver_ADLIB::open() {
 		_opl3Mode = false;
 	}
 #endif
-	_opl->init(getRate());
+	_opl->init();
 
 	_regCache = (byte *)calloc(256, 1);
 
diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp
index 5c3d833..bcc73a9 100644
--- a/audio/softsynth/opl/dosbox.cpp
+++ b/audio/softsynth/opl/dosbox.cpp
@@ -32,6 +32,7 @@
 #include "dosbox.h"
 #include "dbopl.h"
 
+#include "audio/mixer.h"
 #include "common/system.h"
 #include "common/scummsys.h"
 #include "common/util.h"
@@ -156,7 +157,7 @@ void OPL::free() {
 	_emulator = 0;
 }
 
-bool OPL::init(int rate) {
+bool OPL::init() {
 	free();
 
 	memset(&_reg, 0, sizeof(_reg));
@@ -167,19 +168,19 @@ bool OPL::init(int rate) {
 		return false;
 
 	DBOPL::InitTables();
-	_emulator->Setup(rate);
+	_rate = g_system->getMixer()->getOutputRate();
+	_emulator->Setup(_rate);
 
 	if (_type == Config::kDualOpl2) {
 		// Setup opl3 mode in the hander
 		_emulator->WriteReg(0x105, 1);
 	}
 
-	_rate = rate;
 	return true;
 }
 
 void OPL::reset() {
-	init(_rate);
+	init();
 }
 
 void OPL::write(int port, int val) {
diff --git a/audio/softsynth/opl/dosbox.h b/audio/softsynth/opl/dosbox.h
index 513a49f..d3df6ba 100644
--- a/audio/softsynth/opl/dosbox.h
+++ b/audio/softsynth/opl/dosbox.h
@@ -87,7 +87,7 @@ public:
 	OPL(Config::OplType type);
 	~OPL();
 
-	bool init(int rate);
+	bool init();
 	void reset();
 
 	void write(int a, int v);
diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp
index da75ba7..1a5810f 100644
--- a/audio/softsynth/opl/mame.cpp
+++ b/audio/softsynth/opl/mame.cpp
@@ -31,6 +31,8 @@
 
 #include "mame.h"
 
+#include "audio/mixer.h"
+#include "common/system.h"
 #include "common/textconsole.h"
 #include "common/util.h"
 
@@ -50,11 +52,11 @@ OPL::~OPL() {
 	_opl = 0;
 }
 
-bool OPL::init(int rate) {
+bool OPL::init() {
 	if (_opl)
 		MAME::OPLDestroy(_opl);
 
-	_opl = MAME::makeAdLibOPL(rate);
+	_opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate());
 	return (_opl != 0);
 }
 
diff --git a/audio/softsynth/opl/mame.h b/audio/softsynth/opl/mame.h
index bd479d9..080cc6d 100644
--- a/audio/softsynth/opl/mame.h
+++ b/audio/softsynth/opl/mame.h
@@ -181,7 +181,7 @@ public:
 	OPL() : _opl(0) {}
 	~OPL();
 
-	bool init(int rate);
+	bool init();
 	void reset();
 
 	void write(int a, int v);
diff --git a/engines/agos/drivers/accolade/adlib.cpp b/engines/agos/drivers/accolade/adlib.cpp
index 11edc7c..61f209b 100644
--- a/engines/agos/drivers/accolade/adlib.cpp
+++ b/engines/agos/drivers/accolade/adlib.cpp
@@ -213,8 +213,6 @@ MidiDriver_Accolade_AdLib::~MidiDriver_Accolade_AdLib() {
 }
 
 int MidiDriver_Accolade_AdLib::open() {
-	int rate = _mixer->getOutputRate();
-
 //	debugC(kDebugLevelAdLibDriver, "AdLib: starting driver");
 
 	_opl = OPL::Config::create(OPL::Config::kOpl2);
@@ -222,7 +220,7 @@ int MidiDriver_Accolade_AdLib::open() {
 	if (!_opl)
 		return -1;
 
-	_opl->init(rate);
+	_opl->init();
 
 	MidiDriver_Emulated::open();
 
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index 8da6dba..2ac0f91 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -285,7 +285,7 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 
 	_sampleRate = _mixer->getOutputRate();
 	_opl = OPL::Config::create();
-	if (!_opl || !_opl->init(_sampleRate))
+	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
 
 	memset(_channelsVolumeTable, 0, sizeof(_channelsVolumeTable));
diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp
index 0bf9bf7..1f49604 100644
--- a/engines/cruise/sound.cpp
+++ b/engines/cruise/sound.cpp
@@ -304,7 +304,7 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	: _mixer(mixer) {
 	_sampleRate = _mixer->getOutputRate();
 	_opl = OPL::Config::create();
-	if (!_opl || !_opl->init(_sampleRate))
+	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
 
 	for (int i = 0; i < 5; ++i) {
diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index 65b43ca..b92e20b 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -136,7 +136,7 @@ void AdLib::createOPL() {
 	}
 
 	_opl = OPL::Config::create(OPL::Config::parse(oplDriver), OPL::Config::kOpl2);
-	if (!_opl || !_opl->init(_rate)) {
+	if (!_opl || !_opl->init()) {
 		delete _opl;
 
 		error("Could not create an AdLib emulator");
diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index 203931f..ad3395b 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -428,7 +428,7 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
 	_mixer = mixer;
 
 	_adlib = OPL::Config::create();
-	if (!_adlib || !_adlib->init(getRate()))
+	if (!_adlib || !_adlib->init())
 		error("Failed to create OPL");
 
 	memset(_channels, 0, sizeof(_channels));
diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp
index b0a0938..10cbc73 100644
--- a/engines/mads/nebular/sound_nebular.cpp
+++ b/engines/mads/nebular/sound_nebular.cpp
@@ -211,7 +211,7 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filenam
 	_mixer = mixer;
 	_opl = opl;
 
-	_opl->init(getRate());
+	_opl->init();
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1,
 		Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 
@@ -861,6 +861,10 @@ int ASound::readBuffer(int16 *buffer, const int numSamples) {
 	return numSamples;
 }
 
+int ASound::getRate() const {
+	return g_system->getMixer()->getOutputRate();
+}
+
 void ASound::setVolume(int volume) {
 	_masterVolume = volume;
 	if (!volume)
diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h
index e095632..8c1d7f8 100644
--- a/engines/mads/nebular/sound_nebular.h
+++ b/engines/mads/nebular/sound_nebular.h
@@ -385,7 +385,7 @@ public:
 	/**
 	 * Return sample rate
 	 */
-	virtual int getRate() const { return 11025; }
+	virtual int getRate() const;
 
 	/**
 	 * Set the volume
diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp
index 09bc3a3..4a35edb 100644
--- a/engines/mads/sound.cpp
+++ b/engines/mads/sound.cpp
@@ -40,7 +40,7 @@ SoundManager::SoundManager(MADSEngine *vm, Audio::Mixer *mixer) {
 	_masterVolume = 255;
 
 	_opl = OPL::Config::create();
-	_opl->init(11025);
+	_opl->init();
 
 	// Validate sound files
 	switch (_vm->getGameID()) {
diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp
index 7c1dd16..a2defa9 100644
--- a/engines/parallaction/adlib.cpp
+++ b/engines/parallaction/adlib.cpp
@@ -351,7 +351,7 @@ int AdLibDriver::open() {
 	MidiDriver_Emulated::open();
 
 	_opl = OPL::Config::create();
-	_opl->init(getRate());
+	_opl->init();
 	_opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms)
 
 	// Reset the OPL registers.
diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp
index 6bf0d59..369ac99 100644
--- a/engines/queen/midiadlib.cpp
+++ b/engines/queen/midiadlib.cpp
@@ -122,7 +122,7 @@ private:
 int AdLibMidiDriver::open() {
 	MidiDriver_Emulated::open();
 	_opl = OPL::Config::create();
-	if (!_opl || !_opl->init(getRate()))
+	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
 
 	adlibSetupCard();
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index fcfda2f..3c95381 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -215,8 +215,6 @@ static const int ym3812_note[13] = {
 };
 
 int MidiDriver_AdLib::openAdLib(bool isSCI0) {
-	int rate = _mixer->getOutputRate();
-
 	_stereo = STEREO;
 
 	debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1"));
@@ -233,7 +231,7 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) {
 	if (!_opl)
 		return -1;
 
-	_opl->init(rate);
+	_opl->init();
 
 	setRegister(0xBD, 0);
 	setRegister(0x08, 0);
diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp
index adcda68..b22180b 100644
--- a/engines/scumm/players/player_ad.cpp
+++ b/engines/scumm/players/player_ad.cpp
@@ -38,7 +38,7 @@ namespace Scumm {
 Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer)
 	: _vm(scumm), _mixer(mixer), _rate(mixer->getOutputRate()) {
 	_opl2 = OPL::Config::create();
-	if (!_opl2->init(_rate)) {
+	if (!_opl2->init()) {
 		error("Could not initialize OPL2 emulator");
 	}
 
diff --git a/engines/sherlock/scalpel/drivers/adlib.cpp b/engines/sherlock/scalpel/drivers/adlib.cpp
index 91641fc..3c5a655 100644
--- a/engines/sherlock/scalpel/drivers/adlib.cpp
+++ b/engines/sherlock/scalpel/drivers/adlib.cpp
@@ -285,8 +285,6 @@ private:
 };
 
 int MidiDriver_SH_AdLib::open() {
-	int rate = _mixer->getOutputRate();
-
 	debugC(kDebugLevelAdLibDriver, "AdLib: starting driver");
 
 	_opl = OPL::Config::create(OPL::Config::kOpl2);
@@ -294,7 +292,7 @@ int MidiDriver_SH_AdLib::open() {
 	if (!_opl)
 		return -1;
 
-	_opl->init(rate);
+	_opl->init();
 
 	MidiDriver_Emulated::open();
 
diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp
index 2b73cb1..e410b3f 100644
--- a/engines/sky/music/adlibmusic.cpp
+++ b/engines/sky/music/adlibmusic.cpp
@@ -36,7 +36,7 @@ AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pD
 	_sampleRate = pMixer->getOutputRate();
 
 	_opl = OPL::Config::create();
-	if (!_opl || !_opl->init(_sampleRate))
+	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
 
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index 455b58d..6ff983e 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -2746,7 +2746,7 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() {
 	_sampleRate = _mixer->getOutputRate();
 	_opl = OPL::Config::create();
 	assert(_opl);
-	_opl->init(_sampleRate);
+	_opl->init();
 
 	_samplesTillCallback = 0;
 	_samplesTillCallbackRemainder = 0;


Commit: b9307ef1a4420ef61aa35c747d2f3f11875d3b72
    https://github.com/scummvm/scummvm/commit/b9307ef1a4420ef61aa35c747d2f3f11875d3b72
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:42-04:00

Commit Message:
AUDIO: Introduce a callback to the OPL code

Currently unused, but ready to be hooked up to various classes using it.

Changed paths:
    audio/fmopl.cpp
    audio/fmopl.h
    audio/softsynth/opl/dosbox.cpp
    audio/softsynth/opl/dosbox.h
    audio/softsynth/opl/mame.cpp
    audio/softsynth/opl/mame.h



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 3ee5573..c9cb104 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -22,10 +22,12 @@
 
 #include "audio/fmopl.h"
 
+#include "audio/mixer.h"
 #include "audio/softsynth/opl/dosbox.h"
 #include "audio/softsynth/opl/mame.h"
 
 #include "common/config-manager.h"
+#include "common/system.h"
 #include "common/textconsole.h"
 #include "common/translation.h"
 
@@ -171,6 +173,80 @@ OPL *Config::create(DriverId driver, OplType type) {
 	}
 }
 
+void OPL::start(TimerCallback *callback, int timerFrequency) {
+	_callback.reset(callback);
+	startCallbacks(timerFrequency);
+}
+
+void OPL::stop() {
+	stopCallbacks();
+	_callback.reset();
+}
+
 bool OPL::_hasInstance = false;
 
+EmulatedOPL::EmulatedOPL() :
+	_nextTick(0),
+	_samplesPerTick(0),
+	_baseFreq(0) {
+}
+
+EmulatedOPL::~EmulatedOPL() {
+	// Stop callbacks, just in case. If it's still playing at this
+	// point, there's probably a bigger issue, though.
+	stopCallbacks();
+}
+
+int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) {
+	const int stereoFactor = isStereo() ? 2 : 1;
+	int len = numSamples / stereoFactor;
+	int step;
+
+	do {
+		step = len;
+		if (step > (_nextTick >> FIXP_SHIFT))
+			step = (_nextTick >> FIXP_SHIFT);
+
+		generateSamples(buffer, step * stereoFactor);
+
+		_nextTick -= step << FIXP_SHIFT;
+		if (!(_nextTick >> FIXP_SHIFT)) {
+			if (_callback && _callback->isValid())
+				(*_callback)();
+
+			_nextTick += _samplesPerTick;
+		}
+
+		buffer += step * stereoFactor;
+		len -= step;
+	} while (len);
+
+	return numSamples;
+}
+
+int EmulatedOPL::getRate() const {
+	return g_system->getMixer()->getOutputRate();
+}
+
+void EmulatedOPL::startCallbacks(int timerFrequency) {
+	_baseFreq = timerFrequency;
+	assert(_baseFreq != 0);
+
+	int d = getRate() / _baseFreq;
+	int r = getRate() % _baseFreq;
+
+	// This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ
+	// but less prone to arithmetic overflow.
+
+	_samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq;
+
+	// TODO: Eventually start mixer playback here
+	//g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+void EmulatedOPL::stopCallbacks() {
+	// TODO: Eventually stop mixer playback here
+	//g_system->getMixer()->stopHandle(*_handle);
+}
+
 } // End of namespace OPL
diff --git a/audio/fmopl.h b/audio/fmopl.h
index aaa8edd..0e2e2b2 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -23,6 +23,8 @@
 #ifndef AUDIO_FMOPL_H
 #define AUDIO_FMOPL_H
 
+#include "common/func.h"
+#include "common/ptr.h"
 #include "common/scummsys.h"
 
 namespace Common {
@@ -98,6 +100,8 @@ private:
 	static const EmulatorDescription _drivers[];
 };
 
+typedef Common::Functor0<void> TimerCallback;
+
 class OPL {
 private:
 	static bool _hasInstance;
@@ -154,12 +158,83 @@ public:
 	 * So if you request 4 samples from a stereo OPL, you will get
 	 * a total of two left channel and two right channel samples.
 	 */
-	virtual void readBuffer(int16 *buffer, int length) = 0;
+	virtual int readBuffer(int16 *buffer, const int numSamples) = 0;
 
 	/**
 	 * Returns whether the setup OPL mode is stereo or not
 	 */
 	virtual bool isStereo() const = 0;
+
+	/**
+	 * Start the OPL with callbacks.
+	 */
+	void start(TimerCallback *callback, int timerFrequency = kDefaultCallbackFrequency);
+
+	/**
+	 * Stop the OPL
+	 */
+	void stop();
+
+	enum {
+		/**
+		 * The default callback frequency that start() uses
+		 */
+		kDefaultCallbackFrequency = 250
+	};
+
+protected:
+	/**
+	 * Start the callbacks.
+	 */
+	virtual void startCallbacks(int timerFrequency) = 0;
+
+	/**
+	 * Stop the callbacks.
+	 */
+	virtual void stopCallbacks() = 0;
+
+	/**
+	 * The functor for callbacks.
+	 */
+	Common::ScopedPtr<TimerCallback> _callback;
+};
+
+class EmulatedOPL : public OPL {
+public:
+	EmulatedOPL();
+	virtual ~EmulatedOPL();
+
+	// OPL API
+	int readBuffer(int16 *buffer, const int numSamples);
+
+	int getRate() const;
+
+protected:
+	// OPL API
+	void startCallbacks(int timerFrequency);
+	void stopCallbacks();
+
+	/**
+	 * Read up to 'length' samples.
+	 *
+	 * Data will be in native endianess, 16 bit per sample, signed.
+	 * For stereo OPL, buffer will be filled with interleaved
+	 * left and right channel samples, starting with a left sample.
+	 * Furthermore, the samples in the left and right are summed up.
+	 * So if you request 4 samples from a stereo OPL, you will get
+	 * a total of two left channel and two right channel samples.
+	 */
+	virtual void generateSamples(int16 *buffer, int numSamples) = 0;
+
+private:
+	int _baseFreq;
+
+	enum {
+		FIXP_SHIFT = 16
+	};
+
+	int _nextTick;
+	int _samplesPerTick;
 };
 
 } // End of namespace OPL
diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp
index bcc73a9..f6f17c5 100644
--- a/audio/softsynth/opl/dosbox.cpp
+++ b/audio/softsynth/opl/dosbox.cpp
@@ -153,6 +153,7 @@ OPL::~OPL() {
 }
 
 void OPL::free() {
+	stopCallbacks();
 	delete _emulator;
 	_emulator = 0;
 }
@@ -176,6 +177,9 @@ bool OPL::init() {
 		_emulator->WriteReg(0x105, 1);
 	}
 
+	// FIXME: Remove this once EmulatedOPL is actually controlling playback
+	start(0);
+
 	return true;
 }
 
@@ -308,7 +312,7 @@ void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) {
 	_emulator->WriteReg(fullReg, val);
 }
 
-void OPL::readBuffer(int16 *buffer, int length) {
+void OPL::generateSamples(int16 *buffer, int length) {
 	// For stereo OPL cards, we divide the sample count by 2,
 	// to match stereo AudioStream behavior.
 	if (_type != Config::kOpl2)
diff --git a/audio/softsynth/opl/dosbox.h b/audio/softsynth/opl/dosbox.h
index d3df6ba..c52f067 100644
--- a/audio/softsynth/opl/dosbox.h
+++ b/audio/softsynth/opl/dosbox.h
@@ -69,7 +69,7 @@ namespace DBOPL {
 struct Chip;
 } // end of namespace DBOPL
 
-class OPL : public ::OPL::OPL {
+class OPL : public ::OPL::EmulatedOPL {
 private:
 	Config::OplType _type;
 	uint _rate;
@@ -95,8 +95,10 @@ public:
 
 	void writeReg(int r, int v);
 
-	void readBuffer(int16 *buffer, int length);
 	bool isStereo() const { return _type != Config::kOpl2; }
+
+protected:
+	void generateSamples(int16 *buffer, int length);
 };
 
 } // End of namespace DOSBox
diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp
index 1a5810f..fe23d30 100644
--- a/audio/softsynth/opl/mame.cpp
+++ b/audio/softsynth/opl/mame.cpp
@@ -48,15 +48,22 @@ namespace OPL {
 namespace MAME {
 
 OPL::~OPL() {
+	stopCallbacks();
 	MAME::OPLDestroy(_opl);
 	_opl = 0;
 }
 
 bool OPL::init() {
-	if (_opl)
+	if (_opl) {
+		stopCallbacks();
 		MAME::OPLDestroy(_opl);
+	}
 
 	_opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate());
+
+	// FIXME: Remove this once EmulatedOPL is actually controlling playback
+	start(0);
+
 	return (_opl != 0);
 }
 
@@ -76,7 +83,7 @@ void OPL::writeReg(int r, int v) {
 	MAME::OPLWriteReg(_opl, r, v);
 }
 
-void OPL::readBuffer(int16 *buffer, int length) {
+void OPL::generateSamples(int16 *buffer, int length) {
 	MAME::YM3812UpdateOne(_opl, buffer, length);
 }
 
diff --git a/audio/softsynth/opl/mame.h b/audio/softsynth/opl/mame.h
index 080cc6d..67d80bb 100644
--- a/audio/softsynth/opl/mame.h
+++ b/audio/softsynth/opl/mame.h
@@ -174,7 +174,7 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);
 FM_OPL *makeAdLibOPL(int rate);
 
 // OPL API implementation
-class OPL : public ::OPL::OPL {
+class OPL : public ::OPL::EmulatedOPL {
 private:
 	FM_OPL *_opl;
 public:
@@ -189,8 +189,10 @@ public:
 
 	void writeReg(int r, int v);
 
-	void readBuffer(int16 *buffer, int length);
 	bool isStereo() const { return false; }
+
+protected:
+	void generateSamples(int16 *buffer, int length);
 };
 
 } // End of namespace MAME


Commit: 0bb13b358e1126f25bd8e6053da2b4343269252f
    https://github.com/scummvm/scummvm/commit/0bb13b358e1126f25bd8e6053da2b4343269252f
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:42-04:00

Commit Message:
SCUMM: Use the built-in OPL timer for Player_AD

Changed paths:
    engines/scumm/players/player_ad.cpp
    engines/scumm/players/player_ad.h



diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp
index b22180b..ea59a22 100644
--- a/engines/scumm/players/player_ad.cpp
+++ b/engines/scumm/players/player_ad.cpp
@@ -42,19 +42,12 @@ Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer)
 		error("Could not initialize OPL2 emulator");
 	}
 
-	_samplesPerCallback = _rate / AD_CALLBACK_FREQUENCY;
-	_samplesPerCallbackRemainder = _rate % AD_CALLBACK_FREQUENCY;
-	_samplesTillCallback = 0;
-	_samplesTillCallbackRemainder = 0;
-
 	memset(_registerBackUpTable, 0, sizeof(_registerBackUpTable));
 	writeReg(0x01, 0x00);
 	writeReg(0xBD, 0x00);
 	writeReg(0x08, 0x00);
 	writeReg(0x01, 0x20);
 
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
 	_engineMusicTimer = 0;
 	_soundPlaying = -1;
 
@@ -78,6 +71,9 @@ Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer)
 
 	_musicVolume = _sfxVolume = 255;
 	_isSeeking = false;
+
+	_opl2->start(new Common::Functor0Mem<void, Player_AD>(this, &Player_AD::onTimer), AD_CALLBACK_FREQUENCY);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 Player_AD::~Player_AD() {
@@ -244,36 +240,18 @@ void Player_AD::saveLoadWithSerializer(Serializer *ser) {
 	}
 }
 
-int Player_AD::readBuffer(int16 *buffer, const int numSamples) {
+void Player_AD::onTimer() {
 	Common::StackLock lock(_mutex);
 
-	int len = numSamples;
-
-	while (len > 0) {
-		if (!_samplesTillCallback) {
-			if (_curOffset) {
-				updateMusic();
-			}
-
-			updateSfx();
-
-			_samplesTillCallback = _samplesPerCallback;
-			_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
-			if (_samplesTillCallbackRemainder >= AD_CALLBACK_FREQUENCY) {
-				++_samplesTillCallback;
-				_samplesTillCallbackRemainder -= AD_CALLBACK_FREQUENCY;
-			}
-		}
-
-		const int samplesToRead = MIN(len, _samplesTillCallback);
-		_opl2->readBuffer(buffer, samplesToRead);
-
-		buffer += samplesToRead;
-		len -= samplesToRead;
-		_samplesTillCallback -= samplesToRead;
+	if (_curOffset) {
+		updateMusic();
 	}
 
-	return numSamples;
+	updateSfx();
+}
+
+int Player_AD::readBuffer(int16 *buffer, const int numSamples) {
+	return _opl2->readBuffer(buffer, numSamples);
 }
 
 void Player_AD::setupVolume() {
diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h
index 63a8503..9662b08 100644
--- a/engines/scumm/players/player_ad.h
+++ b/engines/scumm/players/player_ad.h
@@ -62,6 +62,9 @@ public:
 	virtual bool endOfData() const { return false; }
 	virtual int getRate() const { return _rate; }
 
+	// Timer callback
+	void onTimer();
+
 private:
 	ScummEngine *const _vm;
 	Common::Mutex _mutex;
@@ -75,11 +78,6 @@ private:
 
 	OPL::OPL *_opl2;
 
-	int _samplesPerCallback;
-	int _samplesPerCallbackRemainder;
-	int _samplesTillCallback;
-	int _samplesTillCallbackRemainder;
-
 	int _soundPlaying;
 	int32 _engineMusicTimer;
 


Commit: ed8830fcc807f9620185e143558f90f9b2cb8aea
    https://github.com/scummvm/scummvm/commit/ed8830fcc807f9620185e143558f90f9b2cb8aea
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:43-04:00

Commit Message:
AUDIO: Use the built-in OPL timer for MidiDriver_ADLIB

Changed paths:
    audio/softsynth/adlib.cpp



diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp
index 49e69ec..c7b5297 100644
--- a/audio/softsynth/adlib.cpp
+++ b/audio/softsynth/adlib.cpp
@@ -948,9 +948,12 @@ public:
 
 
 	// AudioStream API
+	int readBuffer(int16 *data, const int numSamples);
 	bool isStereo() const { return _opl->isStereo(); }
 	int getRate() const { return _mixer->getOutputRate(); }
 
+	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
 private:
 	bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games
 #ifdef ENABLE_OPL3
@@ -963,6 +966,9 @@ private:
 	byte *_regCacheSecondary;
 #endif
 
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
 	int _timerCounter;
 
 	uint16 _channelTable2[9];
@@ -1403,6 +1409,8 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)
 	_timerIncrease = 0xD69;
 	_timerThreshold = 0x411B;
 	_opl = 0;
+	_adlibTimerProc = 0;
+        _adlibTimerParam = 0;
 }
 
 int MidiDriver_ADLIB::open() {
@@ -1452,6 +1460,7 @@ int MidiDriver_ADLIB::open() {
 	}
 #endif
 
+	_opl->start(new Common::Functor0Mem<void, MidiDriver_ADLIB>(this, &MidiDriver_ADLIB::onTimer));
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 
 	return 0;
@@ -1617,13 +1626,17 @@ void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) {
 #endif
 
 void MidiDriver_ADLIB::generateSamples(int16 *data, int len) {
-	if (_opl->isStereo()) {
-		len *= 2;
-	}
-	_opl->readBuffer(data, len);
+	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
+}
+
+int MidiDriver_ADLIB::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
 }
 
 void MidiDriver_ADLIB::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
+
 	_timerCounter += _timerIncrease;
 	while (_timerCounter >= _timerThreshold) {
 		_timerCounter -= _timerThreshold;
@@ -1655,6 +1668,11 @@ void MidiDriver_ADLIB::onTimer() {
 	}
 }
 
+void MidiDriver_ADLIB::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+	_adlibTimerProc = timerProc;
+	_adlibTimerParam = timerParam;
+}
+
 void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) {
 	AdLibVoice *tmp;
 


Commit: 24add3c7457f5a13f659b9f2e5c8c8f46ef785c4
    https://github.com/scummvm/scummvm/commit/24add3c7457f5a13f659b9f2e5c8c8f46ef785c4
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:43-04:00

Commit Message:
SCI: Use the built-in OPL timer

Changed paths:
    engines/sci/sound/drivers/adlib.cpp



diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 3c95381..7035435 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -61,11 +61,15 @@ public:
 	MidiChannel *getPercussionChannel() { return NULL; }
 
 	// AudioStream
+	int readBuffer(int16 *data, const int numSamples);
 	bool isStereo() const { return _stereo; }
 	int getRate() const { return _mixer->getOutputRate(); }
 
 	// MidiDriver_Emulated
 	void generateSamples(int16 *buf, int len);
+	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
+	void onTimer();
 
 	void setVolume(byte volume);
 	void playSwitch(bool play);
@@ -140,6 +144,9 @@ private:
 	byte *_rhythmKeyMap;
 	Common::Array<AdLibPatch> _patches;
 
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
 	void loadInstrument(const byte *ins);
 	void voiceOn(int voice, int note, int velocity);
 	void voiceOff(int voice);
@@ -239,6 +246,7 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) {
 
 	MidiDriver_Emulated::open();
 
+	_opl->start(new Common::Functor0Mem<void, MidiDriver_AdLib>(this, &MidiDriver_AdLib::onTimer));
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
 
 	return 0;
@@ -323,10 +331,22 @@ void MidiDriver_AdLib::send(uint32 b) {
 	}
 }
 
+int MidiDriver_AdLib::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
+}
+
 void MidiDriver_AdLib::generateSamples(int16 *data, int len) {
-	if (isStereo())
-		len <<= 1;
-	_opl->readBuffer(data, len);
+	// Dummy implementation
+}
+
+void MidiDriver_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+	_adlibTimerProc = timerProc;
+	_adlibTimerParam = timerParam;
+}
+
+void MidiDriver_AdLib::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
 
 	// Increase the age of the notes
 	for (int i = 0; i < kVoices; i++) {


Commit: 984cd9b01875ae1cfcf6fb6fa12293e9e125a73b
    https://github.com/scummvm/scummvm/commit/984cd9b01875ae1cfcf6fb6fa12293e9e125a73b
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:43-04:00

Commit Message:
CINE: Use the built-in OPL timer

Changed paths:
    engines/cine/sound.cpp



diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index 2ac0f91..a20ad5d 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -119,7 +119,7 @@ public:
 	virtual int getRate() const { return _sampleRate; }
 
 	void initCard();
-	void update(int16 *buf, int len);
+	void onTimer();
 	void setupInstrument(const byte *data, int channel);
 	void loadRegisterInstrument(const byte *data, AdLibRegisterSoundInstrument *reg);
 	virtual void loadInstrument(const byte *data, AdLibSoundInstrument *asi) = 0;
@@ -291,6 +291,7 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	memset(_channelsVolumeTable, 0, sizeof(_channelsVolumeTable));
 	memset(_instrumentsTable, 0, sizeof(_instrumentsTable));
 	initCard();
+	_opl->start(new Common::Functor0Mem<void, AdLibSoundDriver>(this, &AdLibSoundDriver::onTimer), 50);
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
@@ -346,8 +347,7 @@ void AdLibSoundDriver::stopAll() {
 }
 
 int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
-	update(buffer, numSamples);
-	return numSamples;
+	return _opl->readBuffer(buffer, numSamples);
 }
 
 void AdLibSoundDriver::initCard() {
@@ -374,23 +374,9 @@ void AdLibSoundDriver::initCard() {
 	_opl->writeReg(1, 0);
 }
 
-void AdLibSoundDriver::update(int16 *buf, int len) {
-	static int samplesLeft = 0;
-	while (len != 0) {
-		int count = samplesLeft;
-		if (count > len) {
-			count = len;
-		}
-		samplesLeft -= count;
-		len -= count;
-		_opl->readBuffer(buf, count);
-		if (samplesLeft == 0) {
-			if (_upCb) {
-				(*_upCb)(_upRef);
-			}
-			samplesLeft = _sampleRate / 50;
-		}
-		buf += count;
+void AdLibSoundDriver::onTimer() {
+	if (_upCb) {
+		(*_upCb)(_upRef);
 	}
 }
 


Commit: 3c7c217f4418d3ce9865f8950462a5e9dc0a9ef9
    https://github.com/scummvm/scummvm/commit/3c7c217f4418d3ce9865f8950462a5e9dc0a9ef9
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:43-04:00

Commit Message:
CRUISE: Use the built-in OPL timer

Changed paths:
    engines/cruise/sound.cpp



diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp
index 1f49604..7d48e6e 100644
--- a/engines/cruise/sound.cpp
+++ b/engines/cruise/sound.cpp
@@ -125,7 +125,7 @@ public:
 	virtual int getRate() const { return _sampleRate; }
 
 	void initCard();
-	void update(int16 *buf, int len);
+	void onTimer();
 	void setupInstrument(const byte *data, int channel);
 	void setupInstrument(const AdLibSoundInstrument *ins, int channel);
 	void loadRegisterInstrument(const byte *data, AdLibRegisterSoundInstrument *reg);
@@ -313,10 +313,12 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	}
 	memset(_instrumentsTable, 0, sizeof(_instrumentsTable));
 	initCard();
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 
 	_musicVolume = ConfMan.getBool("music_mute") ? 0 : MIN(255, ConfMan.getInt("music_volume"));
 	_sfxVolume = ConfMan.getBool("sfx_mute") ? 0 : MIN(255, ConfMan.getInt("sfx_volume"));
+
+	_opl->start(new Common::Functor0Mem<void, AdLibSoundDriver>(this, &AdLibSoundDriver::onTimer), 50);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibSoundDriver::~AdLibSoundDriver() {
@@ -389,8 +391,7 @@ void AdLibSoundDriver::stopAll() {
 }
 
 int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
-	update(buffer, numSamples);
-	return numSamples;
+	return _opl->readBuffer(buffer, numSamples);
 }
 
 void AdLibSoundDriver::initCard() {
@@ -417,23 +418,9 @@ void AdLibSoundDriver::initCard() {
 	_opl->writeReg(1, 0);
 }
 
-void AdLibSoundDriver::update(int16 *buf, int len) {
-	static int samplesLeft = 0;
-	while (len != 0) {
-		int count = samplesLeft;
-		if (count > len) {
-			count = len;
-		}
-		samplesLeft -= count;
-		len -= count;
-		_opl->readBuffer(buf, count);
-		if (samplesLeft == 0) {
-			if (_upCb) {
-				(*_upCb)(_upRef);
-			}
-			samplesLeft = _sampleRate / 50;
-		}
-		buf += count;
+void AdLibSoundDriver::onTimer() {
+	if (_upCb) {
+		(*_upCb)(_upRef);
 	}
 }
 


Commit: 5803dffead8cdf51bac64f00d095e5f5604d9e27
    https://github.com/scummvm/scummvm/commit/5803dffead8cdf51bac64f00d095e5f5604d9e27
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:43-04:00

Commit Message:
KYRA: Use the built-in OPL timer

Changed paths:
    engines/kyra/sound_adlib.cpp



diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index ad3395b..999bd96 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -72,28 +72,10 @@ public:
 
 	// AudioStream API
 	int readBuffer(int16 *buffer, const int numSamples) {
-		int32 samplesLeft = numSamples;
-		memset(buffer, 0, sizeof(int16) * numSamples);
-		while (samplesLeft) {
-			if (!_samplesTillCallback) {
-				callback();
-				_samplesTillCallback = _samplesPerCallback;
-				_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
-				if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) {
-					_samplesTillCallback++;
-					_samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND;
-				}
-			}
-
-			int32 render = MIN(samplesLeft, _samplesTillCallback);
-			samplesLeft -= render;
-			_samplesTillCallback -= render;
-			_adlib->readBuffer(buffer, render);
-			buffer += render;
-		}
-		return numSamples;
+		return _adlib->readBuffer(buffer, numSamples);
 	}
 
+
 	bool isStereo() const { return false; }
 	bool endOfData() const { return false; }
 	int getRate() const { return _mixer->getOutputRate(); }
@@ -334,11 +316,6 @@ private:
 	// _unkTable2_2[]  - One of the tables in _unkTable2[]
 	// _unkTable2_3[]  - One of the tables in _unkTable2[]
 
-	int32 _samplesPerCallback;
-	int32 _samplesPerCallbackRemainder;
-	int32 _samplesTillCallback;
-	int32 _samplesTillCallbackRemainder;
-
 	int _curChannel;
 	uint8 _soundTrigger;
 
@@ -452,13 +429,6 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
 
 	_tablePtr1 = _tablePtr2 = 0;
 
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
-	_samplesPerCallback = getRate() / CALLBACKS_PER_SECOND;
-	_samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND;
-	_samplesTillCallback = 0;
-	_samplesTillCallbackRemainder = 0;
-
 	_syncJumpMask = 0;
 
 	_musicVolume = 0;
@@ -468,6 +438,9 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
 
 	_programQueueStart = _programQueueEnd = 0;
 	_retrySounds = false;
+
+	_adlib->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::callback), CALLBACKS_PER_SECOND);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibDriver::~AdLibDriver() {


Commit: b638efe0fad55e18513a9c0453100c0d3b9dd8c3
    https://github.com/scummvm/scummvm/commit/b638efe0fad55e18513a9c0453100c0d3b9dd8c3
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:43-04:00

Commit Message:
PARALLACTION: Use the built-in OPL timer

Changed paths:
    engines/parallaction/adlib.cpp



diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp
index a2defa9..302530b 100644
--- a/engines/parallaction/adlib.cpp
+++ b/engines/parallaction/adlib.cpp
@@ -285,8 +285,13 @@ public:
 
 	bool isStereo() const { return false; }
 	int getRate() const { return _mixer->getOutputRate(); }
+	int readBuffer(int16 *data, const int numSamples);
 
-	void generateSamples(int16 *buf, int len);
+	void generateSamples(int16 *buf, int len) {}
+	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+		_adlibTimerProc = timerProc;
+		_adlibTimerParam = timerParam;
+	}
 
 protected:
 	OPL::OPL *_opl;
@@ -320,6 +325,12 @@ protected:
 	void muteMelodicVoice(uint8 voice);
 
 	void initVoices();
+
+private:
+	void onTimer();
+
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
 };
 
 MidiDriver *createAdLibDriver() {
@@ -364,6 +375,7 @@ int AdLibDriver::open() {
 
 	initVoices();
 
+	_opl->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::onTimer));
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 	return 0;
 }
@@ -777,9 +789,13 @@ MidiChannel *AdLibDriver::allocateChannel() {
 	return NULL;
 }
 
-void AdLibDriver::generateSamples(int16 *buf, int len) {
-	memset(buf, 0, sizeof(int16) * len);
-	_opl->readBuffer(buf, len);
+int AdLibDriver::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
+}
+
+void AdLibDriver::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
 }
 
 void AdLibDriver::initVoices() {


Commit: b122ec279073a003ce4fb1dd0cd652e3393c18e0
    https://github.com/scummvm/scummvm/commit/b122ec279073a003ce4fb1dd0cd652e3393c18e0
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:44-04:00

Commit Message:
SKY: Use the built-in OPL timer

Changed paths:
    engines/sky/music/adlibmusic.cpp
    engines/sky/music/adlibmusic.h



diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp
index e410b3f..c13d615 100644
--- a/engines/sky/music/adlibmusic.cpp
+++ b/engines/sky/music/adlibmusic.cpp
@@ -39,6 +39,7 @@ AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pD
 	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
 
+	_opl->start(new Common::Functor0Mem<void, AdLibMusic>(this, &AdLibMusic::onTimer), 50);
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
@@ -48,31 +49,12 @@ AdLibMusic::~AdLibMusic() {
 }
 
 int AdLibMusic::readBuffer(int16 *data, const int numSamples) {
-	if (_musicData == NULL) {
-		// no music loaded
-		memset(data, 0, numSamples * sizeof(int16));
-	} else if ((_currentMusic == 0) || (_numberOfChannels == 0)) {
-		// music loaded but not played as of yet
-		memset(data, 0, numSamples * sizeof(int16));
-		// poll anyways as pollMusic() can activate the music
+	return _opl->readBuffer(data, numSamples);
+}
+
+void AdLibMusic::onTimer() {
+	if (_musicData != NULL)
 		pollMusic();
-		_nextMusicPoll = _sampleRate / 50;
-	} else {
-		uint32 render;
-		uint remaining = numSamples;
-		while (remaining) {
-			render = (remaining > _nextMusicPoll) ? _nextMusicPoll : remaining;
-			remaining -= render;
-			_nextMusicPoll -= render;
-			_opl->readBuffer(data, render);
-			data += render;
-			if (_nextMusicPoll == 0) {
-				pollMusic();
-				_nextMusicPoll = _sampleRate / 50;
-			}
-		}
-	}
-	return numSamples;
 }
 
 void AdLibMusic::setupPointers() {
@@ -90,7 +72,6 @@ void AdLibMusic::setupPointers() {
 		_musicDataLoc = READ_LE_UINT16(_musicData + 0x1201);
 		_initSequence = _musicData + 0xE91;
 	}
-	_nextMusicPoll = 0;
 }
 
 void AdLibMusic::setupChannels(uint8 *channelData) {
@@ -112,6 +93,7 @@ void AdLibMusic::startDriver() {
 
 void AdLibMusic::setVolume(uint16 param) {
 	_musicVolume = param;
+	// FIXME: This is bad. There's no real volume control here.
 	_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, 2 * param);
 }
 
diff --git a/engines/sky/music/adlibmusic.h b/engines/sky/music/adlibmusic.h
index 9a0796d..fe2e5ac 100644
--- a/engines/sky/music/adlibmusic.h
+++ b/engines/sky/music/adlibmusic.h
@@ -48,12 +48,14 @@ private:
 	OPL::OPL *_opl;
 	Audio::SoundHandle _soundHandle;
 	uint8 *_initSequence;
-	uint32 _sampleRate, _nextMusicPoll;
+	uint32 _sampleRate;
 	virtual void setupPointers();
 	virtual void setupChannels(uint8 *channelData);
 	virtual void startDriver();
 
 	void premixerCall(int16 *buf, uint len);
+
+	void onTimer();
 };
 
 } // End of namespace Sky


Commit: 4a4ad97fd3747f2bca749960e3e894cfc90c5f68
    https://github.com/scummvm/scummvm/commit/4a4ad97fd3747f2bca749960e3e894cfc90c5f68
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:44-04:00

Commit Message:
QUEEN: Use the built-in OPL timer

Changed paths:
    engines/queen/midiadlib.cpp



diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp
index 369ac99..4797e5d 100644
--- a/engines/queen/midiadlib.cpp
+++ b/engines/queen/midiadlib.cpp
@@ -41,8 +41,10 @@ public:
 	void metaEvent(byte type, byte *data, uint16 length);
 	MidiChannel *allocateChannel() { return 0; }
 	MidiChannel *getPercussionChannel() { return 0; }
+	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
 
 	// AudioStream
+	int readBuffer(int16 *data, const int numSamples);
 	bool isStereo() const { return false; }
 	int getRate() const { return _mixer->getOutputRate(); }
 
@@ -81,6 +83,8 @@ private:
 	void adlibSetChannel0x20(int channel);
 	void adlibSetChannel0xE0(int channel);
 
+	void onTimer();
+
 	OPL::OPL *_opl;
 	int _midiNumberOfChannels;
 	int _adlibNoteMul;
@@ -100,6 +104,9 @@ private:
 	uint16 _adlibChannelsVolume[11];
 	uint16 _adlibMetaSequenceData[28];
 
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
 	static const uint8 _adlibChannelsMappingTable1[];
 	static const uint8 _adlibChannelsNoFeedback[];
 	static const uint8 _adlibChannelsMappingTable2[];
@@ -131,6 +138,8 @@ int AdLibMidiDriver::open() {
 		adlibSetNoteVolume(i, 0);
 		adlibTurnNoteOff(i);
 	}
+
+	_opl->start(new Common::Functor0Mem<void, AdLibMidiDriver>(this, &AdLibMidiDriver::onTimer));
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 	return 0;
 }
@@ -196,8 +205,21 @@ void AdLibMidiDriver::metaEvent(byte type, byte *data, uint16 length) {
 }
 
 void AdLibMidiDriver::generateSamples(int16 *data, int len) {
-	memset(data, 0, sizeof(int16) * len);
-	_opl->readBuffer(data, len);
+	// Dummy implementation
+}
+
+int AdLibMidiDriver::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
+}
+
+void AdLibMidiDriver::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+	_adlibTimerProc = timerProc;
+	_adlibTimerParam = timerParam;
+}
+
+void AdLibMidiDriver::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
 }
 
 void AdLibMidiDriver::handleSequencerSpecificMetaEvent1(int channel, const uint8 *data) {


Commit: 5024ae136a73d90b1d5a450aed0f990f226e3056
    https://github.com/scummvm/scummvm/commit/5024ae136a73d90b1d5a450aed0f990f226e3056
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:44-04:00

Commit Message:
TSAGE: Use the built-in OPL timer

Changed paths:
    engines/tsage/sound.cpp
    engines/tsage/sound.h



diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index 6ff983e..ddfaa0a 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -2748,13 +2748,6 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() {
 	assert(_opl);
 	_opl->init();
 
-	_samplesTillCallback = 0;
-	_samplesTillCallbackRemainder = 0;
-	_samplesPerCallback = getRate() / CALLBACKS_PER_SECOND;
-	_samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND;
-
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
 	Common::fill(_channelVoiced, _channelVoiced + ADLIB_CHANNEL_COUNT, false);
 	memset(_channelVolume, 0, ADLIB_CHANNEL_COUNT * sizeof(int));
 	memset(_v4405E, 0, ADLIB_CHANNEL_COUNT * sizeof(int));
@@ -2772,6 +2765,9 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() {
 		_channelVoiced[i] = false;
 		_pitchBlend[i] = 0;
 	}
+
+	_opl->start(new Common::Functor0Mem<void, AdlibSoundDriver>(this, &AdlibSoundDriver::onTimer), CALLBACKS_PER_SECOND);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdlibSoundDriver::~AdlibSoundDriver() {
@@ -3019,33 +3015,16 @@ void AdlibSoundDriver::setFrequency(int channel) {
 		((dataWord >> 8) & 3) | (var2 << 2));
 }
 
-int AdlibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
+int AdlibSoundDriver::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
+}
+
+void AdlibSoundDriver::onTimer() {
 	Common::StackLock slock1(SoundManager::sfManager()._serverDisabledMutex);
 	Common::StackLock slock2(SoundManager::sfManager()._serverSuspendedMutex);
 
-	int32 samplesLeft = numSamples;
-	memset(buffer, 0, sizeof(int16) * numSamples);
-	while (samplesLeft) {
-		if (!_samplesTillCallback) {
-			SoundManager::sfUpdateCallback(NULL);
-			flush();
-
-			_samplesTillCallback = _samplesPerCallback;
-			_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
-			if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) {
-				_samplesTillCallback++;
-				_samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND;
-			}
-		}
-
-		int32 render = MIN<int>(samplesLeft, _samplesTillCallback);
-		samplesLeft -= render;
-		_samplesTillCallback -= render;
-
-		_opl->readBuffer(buffer, render);
-		buffer += render;
-	}
-	return numSamples;
+	SoundManager::sfUpdateCallback(NULL);
+	flush();
 }
 
 /*--------------------------------------------------------------------------*/
diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h
index c222a6e..7ea1e65 100644
--- a/engines/tsage/sound.h
+++ b/engines/tsage/sound.h
@@ -460,10 +460,6 @@ private:
 	const byte *_patchData;
 	int _masterVolume;
 	Common::Queue<RegisterValue> _queue;
-	int _samplesTillCallback;
-	int _samplesTillCallbackRemainder;
-	int _samplesPerCallback;
-	int _samplesPerCallbackRemainder;
 
 	bool _channelVoiced[ADLIB_CHANNEL_COUNT];
 	int _channelVolume[ADLIB_CHANNEL_COUNT];
@@ -499,12 +495,13 @@ public:
 	virtual void setPitch(int channel, int pitchBlend);
 
 	// AudioStream interface
-	virtual int readBuffer(int16 *buffer, const int numSamples);
+	virtual int readBuffer(int16 *data, const int numSamples);
 	virtual bool isStereo() const { return false; }
 	virtual bool endOfData() const { return false; }
 	virtual int getRate() const { return _sampleRate; }
 
-	void update(int16 *buf, int len);
+private:
+	void onTimer();
 };
 
 class SoundBlasterDriver: public SoundDriver {


Commit: 4c6724c5faabad1618e5d538ec27a1c334ed4c6e
    https://github.com/scummvm/scummvm/commit/4c6724c5faabad1618e5d538ec27a1c334ed4c6e
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:44-04:00

Commit Message:
MADS: Use the built-in OPL timer

Changed paths:
    engines/mads/nebular/sound_nebular.cpp
    engines/mads/nebular/sound_nebular.h



diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp
index 10cbc73..0a1c1ea 100644
--- a/engines/mads/nebular/sound_nebular.cpp
+++ b/engines/mads/nebular/sound_nebular.cpp
@@ -189,11 +189,6 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filenam
 	_randomSeed = 1234;
 	_amDep = _vibDep = _splitPoint = true;
 
-	_samplesTillCallback = 0;
-	_samplesTillCallbackRemainder = 0;
-	_samplesPerCallback = getRate() / CALLBACKS_PER_SECOND;
-	_samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND;
-
 	for (int i = 0; i < 11; ++i) {
 		_channelData[i]._field0 = 0;
 		_channelData[i]._freqMask = 0;
@@ -211,15 +206,15 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filenam
 	_mixer = mixer;
 	_opl = opl;
 
-	_opl->init();
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1,
-		Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
 	// Initialize the Adlib
 	adlibInit();
 
 	// Reset the adlib
 	command0();
+
+	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1,
+		Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 ASound::~ASound() {
@@ -833,32 +828,14 @@ void ASound::updateFNumber() {
 	write2(8, hiReg, val2);
 }
 
-int ASound::readBuffer(int16 *buffer, const int numSamples) {
-	Common::StackLock slock(_driverMutex);
-
-	int32 samplesLeft = numSamples;
-	memset(buffer, 0, sizeof(int16) * numSamples);
-	while (samplesLeft) {
-		if (!_samplesTillCallback) {
-			poll();
-			flush();
-
-			_samplesTillCallback = _samplesPerCallback;
-			_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
-			if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) {
-				_samplesTillCallback++;
-				_samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND;
-			}
-		}
-
-		int32 render = MIN<int>(samplesLeft, _samplesTillCallback);
-		samplesLeft -= render;
-		_samplesTillCallback -= render;
+int ASound::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
+}
 
-		_opl->readBuffer(buffer, render);
-		buffer += render;
-	}
-	return numSamples;
+void ASound::onTimer() {
+	Common::StackLock slock(_driverMutex);
+	poll();
+	flush();
 }
 
 int ASound::getRate() const {
diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h
index 8c1d7f8..1267914 100644
--- a/engines/mads/nebular/sound_nebular.h
+++ b/engines/mads/nebular/sound_nebular.h
@@ -195,6 +195,11 @@ private:
 	void processSample();
 
 	void updateFNumber();
+
+	/**
+	 * Timer function for OPL
+	 */
+	void onTimer();
 protected:
 	int _commandParam;
 
@@ -309,10 +314,6 @@ public:
 	int _activeChannelReg;
 	int _v11;
 	bool _amDep, _vibDep, _splitPoint;
-	int _samplesPerCallback;
-	int _samplesPerCallbackRemainder;
-	int _samplesTillCallback;
-	int _samplesTillCallbackRemainder;
 public:
 	/**
 	 * Constructor


Commit: cc6e304af1b7021c7ee471f55c0674dac1bfb253
    https://github.com/scummvm/scummvm/commit/cc6e304af1b7021c7ee471f55c0674dac1bfb253
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:44-04:00

Commit Message:
AUDIO: Limit the DOSBox start(0) hack to only being called once

Changed paths:
    audio/softsynth/opl/dosbox.cpp



diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp
index f6f17c5..09200c7 100644
--- a/audio/softsynth/opl/dosbox.cpp
+++ b/audio/softsynth/opl/dosbox.cpp
@@ -178,7 +178,8 @@ bool OPL::init() {
 	}
 
 	// FIXME: Remove this once EmulatedOPL is actually controlling playback
-	start(0);
+	if (!_callback)
+		start(0);
 
 	return true;
 }


Commit: 5b06eef1597ce701a4f6d16854be077b952d29f7
    https://github.com/scummvm/scummvm/commit/5b06eef1597ce701a4f6d16854be077b952d29f7
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:44-04:00

Commit Message:
AUDIO: Allow for changing the OPL timer rate

Changed paths:
    audio/fmopl.cpp
    audio/fmopl.h



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index c9cb104..07c5b44 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -183,6 +183,14 @@ void OPL::stop() {
 	_callback.reset();
 }
 
+void OPL::setCallbackFrequency(int timerFrequency) {
+	if (!isRunning())
+		return;
+
+	stopCallbacks();
+	startCallbacks(timerFrequency);
+}
+
 bool OPL::_hasInstance = false;
 
 EmulatedOPL::EmulatedOPL() :
@@ -249,4 +257,10 @@ void EmulatedOPL::stopCallbacks() {
 	//g_system->getMixer()->stopHandle(*_handle);
 }
 
+bool EmulatedOPL::isRunning() const {
+	// TODO
+	//return g_system->getMixer()->isSoundHandleActive(*_handle);
+	return true;
+}
+
 } // End of namespace OPL
diff --git a/audio/fmopl.h b/audio/fmopl.h
index 0e2e2b2..243cd29 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -175,6 +175,17 @@ public:
 	 */
 	void stop();
 
+	/**
+	 * Is the OPL running?
+	 */
+	virtual bool isRunning() const = 0;
+
+	/**
+	 * Change the callback frequency. This has no effect if start()
+	 * has not already been called.
+	 */
+	void setCallbackFrequency(int timerFrequency);
+
 	enum {
 		/**
 		 * The default callback frequency that start() uses
@@ -206,6 +217,7 @@ public:
 
 	// OPL API
 	int readBuffer(int16 *buffer, const int numSamples);
+	bool isRunning() const;
 
 	int getRate() const;
 


Commit: 73e8ac2a9b51fc4d278c87677b815f1f6c308775
    https://github.com/scummvm/scummvm/commit/73e8ac2a9b51fc4d278c87677b815f1f6c308775
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:45-04:00

Commit Message:
GOB: Use the built-in OPL timer

Changed paths:
    engines/gob/sound/adlib.cpp
    engines/gob/sound/adlib.h
    engines/gob/sound/adlplayer.cpp
    engines/gob/sound/adlplayer.h
    engines/gob/sound/musplayer.cpp
    engines/gob/sound/musplayer.h



diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index b92e20b..20fbced 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -93,7 +93,7 @@ const uint16 AdLib::kHihatParams    [kParamCount] = {
 	  0,  1,  0, 15, 11,  0,  7,  5,  0,  0,  0,  0,  0,  0   };
 
 
-AdLib::AdLib(Audio::Mixer &mixer) : _mixer(&mixer), _opl(0),
+AdLib::AdLib(Audio::Mixer &mixer, int callbackFreq) : _mixer(&mixer), _opl(0),
 	_toPoll(0), _repCount(0), _first(true), _playing(false), _ended(true) {
 
 	_rate = _mixer->getOutputRate();
@@ -103,6 +103,7 @@ AdLib::AdLib(Audio::Mixer &mixer) : _mixer(&mixer), _opl(0),
 	createOPL();
 	initOPL();
 
+	_opl->start(new Common::Functor0Mem<void, AdLib>(this, &AdLib::onTimer), callbackFreq);
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle,
 			this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
@@ -144,38 +145,34 @@ void AdLib::createOPL() {
 }
 
 int AdLib::readBuffer(int16 *buffer, const int numSamples) {
+	return _opl->readBuffer(buffer, numSamples);
+}
+
+void AdLib::onTimer() {
 	Common::StackLock slock(_mutex);
 
-	// Nothing to do, fill with silence
-	if (!_playing) {
-		memset(buffer, 0, numSamples * sizeof(int16));
-		return numSamples;
+	// Nothing to do
+	if (!_playing)
+		return;
+
+	// Check if there's anything to do on this step
+	// If not, decrease the poll number and move on
+	if (_toPoll > 0) {
+		_toPoll--;
+		return;
 	}
 
-	// Read samples from the OPL, polling in more music when necessary
-	uint32 samples = numSamples;
-	while (samples && _playing) {
-		if (_toPoll) {
-			const uint32 render = MIN(samples, _toPoll);
-
-			_opl->readBuffer(buffer, render);
-
-			buffer  += render;
-			samples -= render;
-			_toPoll -= render;
-
-		} else {
-			// Song ended, fill the rest with silence
-			if (_ended) {
-				memset(buffer, 0, samples * sizeof(int16));
-				samples = 0;
-				break;
-			}
-
-			// Poll more music
-			_toPoll = pollMusic(_first);
-			_first  = false;
+	// Poll until we have to delay until the next poll
+	while (_toPoll == 0 && _playing) {
+		// Song ended, break out
+		if (_ended) {
+			_toPoll = 0;
+			break;
 		}
+
+		// Poll more music
+		_toPoll = pollMusic(_first);
+		_first  = false;
 	}
 
 	// Song ended, loop if requested
@@ -195,8 +192,6 @@ int AdLib::readBuffer(int16 *buffer, const int numSamples) {
 		} else
 			_playing = false;
 	}
-
-	return numSamples;
 }
 
 bool AdLib::isStereo() const {
@@ -204,7 +199,7 @@ bool AdLib::isStereo() const {
 }
 
 bool AdLib::endOfData() const {
-	return !_playing;
+	return false;
 }
 
 bool AdLib::endOfStream() const {
@@ -231,10 +226,6 @@ void AdLib::setRepeating(int32 repCount) {
 	_repCount = repCount;
 }
 
-uint32 AdLib::getSamplesPerSecond() const {
-	return _rate * (isStereo() ? 2 : 1);
-}
-
 void AdLib::startPlay() {
 	Common::StackLock slock(_mutex);
 
@@ -639,4 +630,8 @@ void AdLib::setFreq(uint8 voice, uint16 note, bool on) {
 	writeOPL(0xB0 + voice, value);
 }
 
+void AdLib::setTimerFrequency(int timerFrequency) {
+	_opl->setCallbackFrequency(timerFrequency);
+}
+
 } // End of namespace Gob
diff --git a/engines/gob/sound/adlib.h b/engines/gob/sound/adlib.h
index 8071249..6a62152 100644
--- a/engines/gob/sound/adlib.h
+++ b/engines/gob/sound/adlib.h
@@ -37,7 +37,7 @@ namespace Gob {
 /** Base class for a player of an AdLib music format. */
 class AdLib : public Audio::AudioStream {
 public:
-	AdLib(Audio::Mixer &mixer);
+	AdLib(Audio::Mixer &mixer, int callbackFrequency);
 	virtual ~AdLib();
 
 	bool isPlaying() const;    ///< Are we currently playing?
@@ -120,8 +120,6 @@ protected:
 	static const int kOPLMidC      = 48; ///< A mid C for the OPL.
 
 
-	/** Return the number of samples per second. */
-	uint32 getSamplesPerSecond() const;
 
 	/** Write a value into an OPL register. */
 	void writeOPL(byte reg, byte val);
@@ -135,7 +133,7 @@ protected:
 	/** The callback function that's called for polling more AdLib commands.
 	 *
 	 *  @param  first Is this the first poll since the start of the song?
-	 *  @return The number of samples until the next poll.
+	 *  @return The number of ticks until the next poll.
 	 */
 	virtual uint32 pollMusic(bool first) = 0;
 
@@ -207,6 +205,11 @@ protected:
 	/** Switch a voice off. */
 	void noteOff(uint8 voice);
 
+	/**
+	 * Set the OPL timer frequency
+	 */
+	void setTimerFrequency(int timerFrequency);
+
 private:
 	static const uint8 kOperatorType  [kOperatorCount];
 	static const uint8 kOperatorOffset[kOperatorCount];
@@ -300,6 +303,11 @@ private:
 	void changePitch(uint8 voice, uint16 pitchBend);
 
 	void setFreq(uint8 voice, uint16 note, bool on);
+
+	/**
+	 * Callback function for OPL
+	 */
+	void onTimer();
 };
 
 } // End of namespace Gob
diff --git a/engines/gob/sound/adlplayer.cpp b/engines/gob/sound/adlplayer.cpp
index 384a928..e5a2760 100644
--- a/engines/gob/sound/adlplayer.cpp
+++ b/engines/gob/sound/adlplayer.cpp
@@ -28,7 +28,7 @@
 
 namespace Gob {
 
-ADLPlayer::ADLPlayer(Audio::Mixer &mixer) : AdLib(mixer),
+ADLPlayer::ADLPlayer(Audio::Mixer &mixer) : AdLib(mixer, 1000),
 	_songData(0), _songDataSize(0), _playPos(0) {
 
 }
@@ -135,14 +135,7 @@ uint32 ADLPlayer::pollMusic(bool first) {
 	if (delay & 0x80)
 		delay = ((delay & 3) << 8) | *_playPos++;
 
-	return getSampleDelay(delay);
-}
-
-uint32 ADLPlayer::getSampleDelay(uint16 delay) const {
-	if (delay == 0)
-		return 0;
-
-	return ((uint32)delay * getSamplesPerSecond()) / 1000;
+	return delay;
 }
 
 void ADLPlayer::rewind() {
diff --git a/engines/gob/sound/adlplayer.h b/engines/gob/sound/adlplayer.h
index 3edd238..bd43cc0 100644
--- a/engines/gob/sound/adlplayer.h
+++ b/engines/gob/sound/adlplayer.h
@@ -76,8 +76,6 @@ private:
 	bool readHeader  (Common::SeekableReadStream &adl, int &timbreCount);
 	bool readTimbres (Common::SeekableReadStream &adl, int  timbreCount);
 	bool readSongData(Common::SeekableReadStream &adl);
-
-	uint32 getSampleDelay(uint16 delay) const;
 };
 
 } // End of namespace Gob
diff --git a/engines/gob/sound/musplayer.cpp b/engines/gob/sound/musplayer.cpp
index 7001a57..bf90d44 100644
--- a/engines/gob/sound/musplayer.cpp
+++ b/engines/gob/sound/musplayer.cpp
@@ -27,7 +27,7 @@
 
 namespace Gob {
 
-MUSPlayer::MUSPlayer(Audio::Mixer &mixer) : AdLib(mixer),
+MUSPlayer::MUSPlayer(Audio::Mixer &mixer) : AdLib(mixer, 60),
 	_songData(0), _songDataSize(0), _playPos(0), _songID(0) {
 
 }
@@ -43,15 +43,6 @@ void MUSPlayer::unload() {
 	unloadMUS();
 }
 
-uint32 MUSPlayer::getSampleDelay(uint16 delay) const {
-	if (delay == 0)
-		return 0;
-
-	uint32 freq = (_ticksPerBeat * _tempo) / 60;
-
-	return ((uint32)delay * getSamplesPerSecond()) / freq;
-}
-
 void MUSPlayer::skipToTiming() {
 	while (*_playPos < 0x80)
 		_playPos++;
@@ -67,7 +58,7 @@ uint32 MUSPlayer::pollMusic(bool first) {
 	}
 
 	if (first)
-		return getSampleDelay(*_playPos++);
+		return *_playPos++;
 
 	uint16 delay = 0;
 	while (delay == 0) {
@@ -100,6 +91,7 @@ uint32 MUSPlayer::pollMusic(bool first) {
 				uint32 denom = *_playPos++;
 
 				_tempo = _baseTempo * num + ((_baseTempo * denom) >> 7);
+				setTimerFrequency((_ticksPerBeat * _tempo) / 60);
 
 				_playPos++;
 			} else {
@@ -182,7 +174,7 @@ uint32 MUSPlayer::pollMusic(bool first) {
 			delay += *_playPos++;
 	}
 
-	return getSampleDelay(delay);
+	return delay;
 }
 
 void MUSPlayer::rewind() {
@@ -193,6 +185,8 @@ void MUSPlayer::rewind() {
 
 	setPercussionMode(_soundMode != 0);
 	setPitchRange(_pitchBendRange);
+
+	setTimerFrequency((_ticksPerBeat * _tempo) / 60);
 }
 
 bool MUSPlayer::loadSND(Common::SeekableReadStream &snd) {
diff --git a/engines/gob/sound/musplayer.h b/engines/gob/sound/musplayer.h
index c76c5aa..7c1189b 100644
--- a/engines/gob/sound/musplayer.h
+++ b/engines/gob/sound/musplayer.h
@@ -97,7 +97,6 @@ private:
 	bool readMUSHeader(Common::SeekableReadStream &mus);
 	bool readMUSSong  (Common::SeekableReadStream &mus);
 
-	uint32 getSampleDelay(uint16 delay) const;
 	void setInstrument(uint8 voice, uint8 instrument);
 	void skipToTiming();
 


Commit: dcb75fcaf12ebf194e0b68e841c3eee9739e4d1e
    https://github.com/scummvm/scummvm/commit/dcb75fcaf12ebf194e0b68e841c3eee9739e4d1e
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:45-04:00

Commit Message:
SHERLOCK: Use the built-in OPL timer

Changed paths:
    engines/sherlock/scalpel/drivers/adlib.cpp



diff --git a/engines/sherlock/scalpel/drivers/adlib.cpp b/engines/sherlock/scalpel/drivers/adlib.cpp
index 3c5a655..033b474 100644
--- a/engines/sherlock/scalpel/drivers/adlib.cpp
+++ b/engines/sherlock/scalpel/drivers/adlib.cpp
@@ -219,7 +219,8 @@ uint16 frequencyLookUpTable[SHERLOCK_ADLIB_NOTES_COUNT] = {
 class MidiDriver_SH_AdLib : public MidiDriver_Emulated {
 public:
 	MidiDriver_SH_AdLib(Audio::Mixer *mixer)
-		: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0) {
+		: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0),
+		  _adlibTimerProc(0), _adlibTimerParam(0) {
 		memset(_voiceChannelMapping, 0, sizeof(_voiceChannelMapping));
 	}
 	virtual ~MidiDriver_SH_AdLib() { }
@@ -232,6 +233,7 @@ public:
 	MidiChannel *getPercussionChannel() { return NULL; }
 
 	// AudioStream
+	int readBuffer(int16 *data, const int numSamples);
 	bool isStereo() const { return false; }
 	int getRate() const { return _mixer->getOutputRate(); }
 	int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; }
@@ -240,6 +242,8 @@ public:
 	// MidiDriver_Emulated
 	void generateSamples(int16 *buf, int len);
 
+	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
 	void setVolume(byte volume);
 	virtual uint32 property(int prop, uint32 param);
 
@@ -261,16 +265,17 @@ private:
 	OPL::OPL *_opl;
 	int _masterVolume;
 
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
 	// points to a MIDI channel for each of the new voice channels
 	byte _voiceChannelMapping[SHERLOCK_ADLIB_VOICES_COUNT];
 
 	// stores information about all FM voice channels
 	adlib_ChannelEntry _channels[SHERLOCK_ADLIB_VOICES_COUNT];
 
-protected:
 	void onTimer();
 
-private:
 	void resetAdLib();
 	void resetAdLibOperatorRegisters(byte baseRegister, byte value);
 	void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value);
@@ -296,6 +301,7 @@ int MidiDriver_SH_AdLib::open() {
 
 	MidiDriver_Emulated::open();
 
+	_opl->start(new Common::Functor0Mem<void, MidiDriver_SH_AdLib>(this, &MidiDriver_SH_AdLib::onTimer));
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
 
 	return 0;
@@ -316,6 +322,12 @@ void MidiDriver_SH_AdLib::setVolume(byte volume) {
 // original driver did this before MIDI data processing on each tick
 // we do it atm after MIDI data processing
 void MidiDriver_SH_AdLib::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
+
+	// this should/must get called per tick
+	// original driver did this before MIDI data processing on each tick
+	// we do it atm after MIDI data processing
 	for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
 		if (_channels[FMvoiceChannel].inUse) {
 			_channels[FMvoiceChannel].inUseTimer++;
@@ -416,7 +428,11 @@ void MidiDriver_SH_AdLib::send(uint32 b) {
 }
 
 void MidiDriver_SH_AdLib::generateSamples(int16 *data, int len) {
-	_opl->readBuffer(data, len);
+	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
+}
+
+int MidiDriver_SH_AdLib::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
 }
 
 void MidiDriver_SH_AdLib::noteOn(byte MIDIchannel, byte note, byte velocity) {
@@ -625,6 +641,11 @@ uint32 MidiDriver_SH_AdLib::property(int prop, uint32 param) {
 	return 0;
 }
 
+void MidiDriver_SH_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+	_adlibTimerProc = timerProc;
+	_adlibTimerParam = timerParam;
+}
+
 MidiDriver *MidiDriver_SH_AdLib_create() {
 	return new MidiDriver_SH_AdLib(g_system->getMixer());
 }


Commit: 0c5d40e94c6f10d63763a72c70d06fe2fa15de29
    https://github.com/scummvm/scummvm/commit/0c5d40e94c6f10d63763a72c70d06fe2fa15de29
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:45-04:00

Commit Message:
AGOS: Use the built-in OPL timer

Changed paths:
    engines/agos/drivers/accolade/adlib.cpp



diff --git a/engines/agos/drivers/accolade/adlib.cpp b/engines/agos/drivers/accolade/adlib.cpp
index 61f209b..33a7595 100644
--- a/engines/agos/drivers/accolade/adlib.cpp
+++ b/engines/agos/drivers/accolade/adlib.cpp
@@ -125,6 +125,7 @@ public:
 	MidiChannel *getPercussionChannel() { return NULL; }
 
 	// AudioStream
+	int readBuffer(int16 *data, const int numSamples);
 	bool isStereo() const { return false; }
 	int getRate() const { return _mixer->getOutputRate(); }
 	int getPolyphony() const { return AGOS_ADLIB_VOICES_COUNT; }
@@ -138,6 +139,8 @@ public:
 
 	bool setupInstruments(byte *instrumentData, uint16 instrumentDataSize, bool useMusicDrvFile);
 
+	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
 private:
 	bool _musicDrvMode;
 
@@ -170,16 +173,17 @@ private:
 	OPL::OPL *_opl;
 	int _masterVolume;
 
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
 	// points to a MIDI channel for each of the new voice channels
 	byte _voiceChannelMapping[AGOS_ADLIB_VOICES_COUNT];
 
 	// stores information about all FM voice channels
 	ChannelEntry _channels[AGOS_ADLIB_VOICES_COUNT];
 
-protected:
 	void onTimer();
 
-private:
 	void resetAdLib();
 	void resetAdLibOperatorRegisters(byte baseRegister, byte value);
 	void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value);
@@ -193,7 +197,8 @@ private:
 };
 
 MidiDriver_Accolade_AdLib::MidiDriver_Accolade_AdLib(Audio::Mixer *mixer)
-		: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0) {
+		: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0),
+		  _adlibTimerProc(0), _adlibTimerParam(0) {
 	memset(_channelMapping, 0, sizeof(_channelMapping));
 	memset(_instrumentMapping, 0, sizeof(_instrumentMapping));
 	memset(_instrumentVolumeAdjust, 0, sizeof(_instrumentVolumeAdjust));
@@ -224,6 +229,7 @@ int MidiDriver_Accolade_AdLib::open() {
 
 	MidiDriver_Emulated::open();
 
+	_opl->start(new Common::Functor0Mem<void, MidiDriver_Accolade_AdLib>(this, &MidiDriver_Accolade_AdLib::onTimer));
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
 
 	resetAdLib();
@@ -269,6 +275,8 @@ void MidiDriver_Accolade_AdLib::setVolume(byte volume) {
 }
 
 void MidiDriver_Accolade_AdLib::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
 }
 
 void MidiDriver_Accolade_AdLib::resetAdLib() {
@@ -367,7 +375,16 @@ void MidiDriver_Accolade_AdLib::send(uint32 b) {
 }
 
 void MidiDriver_Accolade_AdLib::generateSamples(int16 *data, int len) {
-	_opl->readBuffer(data, len);
+	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
+}
+
+int MidiDriver_Accolade_AdLib::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
+}
+
+void MidiDriver_Accolade_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+	_adlibTimerProc = timerProc;
+	_adlibTimerParam = timerParam;
 }
 
 void MidiDriver_Accolade_AdLib::noteOn(byte FMvoiceChannel, byte note, byte velocity) {


Commit: 22d985f3c265162419ab8c65dd47b48ac385f463
    https://github.com/scummvm/scummvm/commit/22d985f3c265162419ab8c65dd47b48ac385f463
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:45-04:00

Commit Message:
AUDIO: Use the built-in OPL timer for MidiDriver_Miles_AdLib

Changed paths:
    audio/miles_adlib.cpp



diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp
index 4560a81..7f3f05a 100644
--- a/audio/miles_adlib.cpp
+++ b/audio/miles_adlib.cpp
@@ -129,6 +129,7 @@ public:
 	MidiChannel *getPercussionChannel() { return NULL; }
 
 	// AudioStream
+	int readBuffer(int16 *data, const int numSamples);
 	bool isStereo() const { return _modeStereo; }
 	int getRate() const { return _mixer->getOutputRate(); }
 	int getPolyphony() const { return _modePhysicalFmVoicesCount; }
@@ -140,6 +141,8 @@ public:
 	void setVolume(byte volume);
 	virtual uint32 property(int prop, uint32 param);
 
+	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
 private:
 	bool _modeOPL3;
 	byte _modePhysicalFmVoicesCount;
@@ -222,6 +225,9 @@ private:
 	OPL::OPL *_opl;
 	int _masterVolume;
 
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
 	// stores information about all MIDI channels (not the actual OPL FM voice channels!)
 	MidiChannelEntry _midiChannels[MILES_MIDI_CHANNEL_COUNT];
 
@@ -238,10 +244,8 @@ private:
 	bool circularPhysicalAssignment;
 	byte circularPhysicalAssignmentFmVoice;
 
-protected:
 	void onTimer();
 
-private:
 	void resetData();
 	void resetAdLib();
 	void resetAdLibOperatorRegisters(byte baseRegister, byte value);
@@ -272,7 +276,8 @@ private:
 };
 
 MidiDriver_Miles_AdLib::MidiDriver_Miles_AdLib(Audio::Mixer *mixer, InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount)
-	: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0) {
+	: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0),
+	  _adlibTimerProc(0), _adlibTimerParam(0) {
 
 	_instrumentTablePtr = instrumentTablePtr;
 	_instrumentTableCount = instrumentTableCount;
@@ -321,6 +326,7 @@ int MidiDriver_Miles_AdLib::open() {
 
 	MidiDriver_Emulated::open();
 
+	_opl->start(new Common::Functor0Mem<void, MidiDriver_Miles_AdLib>(this, &MidiDriver_Miles_AdLib::onTimer));
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 
 	resetAdLib();
@@ -340,6 +346,8 @@ void MidiDriver_Miles_AdLib::setVolume(byte volume) {
 }
 
 void MidiDriver_Miles_AdLib::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
 }
 
 void MidiDriver_Miles_AdLib::resetData() {
@@ -437,10 +445,16 @@ void MidiDriver_Miles_AdLib::send(uint32 b) {
 }
 
 void MidiDriver_Miles_AdLib::generateSamples(int16 *data, int len) {
-	if (_modeStereo)
-		len *= 2;
+	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
+}
+
+int MidiDriver_Miles_AdLib::readBuffer(int16 *data, const int numSamples) {
+	return _opl->readBuffer(data, numSamples);
+}
 
-	_opl->readBuffer(data, len);
+void MidiDriver_Miles_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+	_adlibTimerProc = timerProc;
+	_adlibTimerParam = timerParam;
 }
 
 int16 MidiDriver_Miles_AdLib::searchFreeVirtualFmVoiceChannel() {


Commit: f7c785b37b5fb00029ebb04362f8a733f556e3dd
    https://github.com/scummvm/scummvm/commit/f7c785b37b5fb00029ebb04362f8a733f556e3dd
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:45-04:00

Commit Message:
SKY: Implement original music volume handling

Changed paths:
    engines/sky/music/adlibchannel.cpp
    engines/sky/music/adlibchannel.h
    engines/sky/music/adlibmusic.cpp



diff --git a/engines/sky/music/adlibchannel.cpp b/engines/sky/music/adlibchannel.cpp
index b57f20f..c7acb9b 100644
--- a/engines/sky/music/adlibchannel.cpp
+++ b/engines/sky/music/adlibchannel.cpp
@@ -45,6 +45,8 @@ AdLibChannel::AdLibChannel(OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData)
 	_channelData.frequency = 0;
 	_channelData.instrumentData = NULL;
 
+	_musicVolume = 128;
+
 	uint16 instrumentDataLoc;
 
 	if (SkyEngine::_systemVars.gameVersion == 109) {
@@ -86,7 +88,7 @@ bool AdLibChannel::isActive() {
 }
 
 void AdLibChannel::updateVolume(uint16 pVolume) {
-	// Do nothing. The mixer handles the music volume for us.
+	_musicVolume = pVolume;
 }
 
 /*	This class uses the same area for the register mirror as the original
@@ -208,6 +210,8 @@ void AdLibChannel::setupChannelVolume(uint8 volume) {
 	uint32 resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op2 + 1)) << 1;
 	resVol &= 0xFFFF;
 	resVol *= (_channelData.channelVolume + 1) << 1;
+	resVol >>= 8;
+	resVol *= _musicVolume << 1;
 	resVol >>= 16;
 	assert(resVol < 0x81);
 	resultOp = ((_channelData.instrumentData->scalingLevel << 6) & 0xC0) | _opOutputTable[resVol];
@@ -216,6 +220,8 @@ void AdLibChannel::setupChannelVolume(uint8 volume) {
 		resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op1 + 1)) << 1;
 		resVol &= 0xFFFF;
 		resVol *= (_channelData.channelVolume + 1) << 1;
+		resVol >>= 8;
+		resVol *= _musicVolume << 1;
 		resVol >>= 16;
 	} else
 		resVol = _channelData.instrumentData->totOutLev_Op1;
diff --git a/engines/sky/music/adlibchannel.h b/engines/sky/music/adlibchannel.h
index 240dd5c..4504e3b 100644
--- a/engines/sky/music/adlibchannel.h
+++ b/engines/sky/music/adlibchannel.h
@@ -68,6 +68,7 @@ public:
 private:
 	OPL::OPL *_opl;
 	uint8 *_musicData;
+	uint16 _musicVolume;
 	AdLibChannelType _channelData;
 
 	InstrumentStruct *_instruments;
diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp
index c13d615..3607dfb 100644
--- a/engines/sky/music/adlibmusic.cpp
+++ b/engines/sky/music/adlibmusic.cpp
@@ -40,7 +40,7 @@ AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pD
 		error("Failed to create OPL");
 
 	_opl->start(new Common::Functor0Mem<void, AdLibMusic>(this, &AdLibMusic::onTimer), 50);
-	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibMusic::~AdLibMusic() {
@@ -93,8 +93,8 @@ void AdLibMusic::startDriver() {
 
 void AdLibMusic::setVolume(uint16 param) {
 	_musicVolume = param;
-	// FIXME: This is bad. There's no real volume control here.
-	_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, 2 * param);
+	for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
+		_channels[cnt]->updateVolume(_musicVolume);
 }
 
 bool AdLibMusic::isStereo() const {


Commit: e31da911c98824b746d8804a63df9695216bc08e
    https://github.com/scummvm/scummvm/commit/e31da911c98824b746d8804a63df9695216bc08e
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:45-04:00

Commit Message:
GOB: Implement custom AdLib volume control

Changed paths:
    engines/gob/gob.cpp
    engines/gob/sound/adlib.cpp
    engines/gob/sound/adlib.h
    engines/gob/sound/sound.cpp
    engines/gob/sound/sound.h



diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
index 5ab3271..24bdb85 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -389,6 +389,9 @@ void GobEngine::syncSoundSettings() {
 	Engine::syncSoundSettings();
 
 	_init->updateConfig();
+
+	if (_sound)
+		_sound->adlibSyncVolume();
 }
 
 void GobEngine::pauseGame() {
diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index 20fbced..866eecf 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -37,6 +37,28 @@ static const int kPitchTomToSnare =  7;
 static const int kPitchSnareDrum  = kPitchTom + kPitchTomToSnare;
 
 
+// Attenuation map for GUI volume slider
+// Note: no volume control in the original engine
+const uint8 AdLib::kVolumeTable[Audio::Mixer::kMaxMixerVolume + 1] = {
+	63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 61, 59, 57, 56, 55,
+	53, 52, 51, 50, 49, 48, 47, 46, 46, 45, 44, 43, 43, 42, 41, 41,
+	40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 33,
+	32, 32, 31, 31, 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27,
+	27, 26, 26, 26, 26, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23,
+	22, 22, 22, 22, 21, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19,
+	19, 19, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 16,
+	16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 13,
+	13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11,
+	11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10,  9,  9,  9,
+	 9,  9,  9,  9,  9,  8,  8,  8,  8,  8,  8,  8,  8,  8,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  6,  6,  6,  6,  6,  6,  6,  6,  6,
+	 6,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  4,  4,  4,  4,  4,
+	 4,  4,  4,  4,  4,  4,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
+	 3,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  1,  1,  1,
+	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,
+	 0
+};
+
 // Is the operator a modulator (0) or a carrier (1)?
 const uint8 AdLib::kOperatorType[kOperatorCount] = {
 	0, 0, 0, 1, 1, 1,
@@ -94,7 +116,7 @@ const uint16 AdLib::kHihatParams    [kParamCount] = {
 
 
 AdLib::AdLib(Audio::Mixer &mixer, int callbackFreq) : _mixer(&mixer), _opl(0),
-	_toPoll(0), _repCount(0), _first(true), _playing(false), _ended(true) {
+	_toPoll(0), _repCount(0), _first(true), _playing(false), _ended(true), _volume(0) {
 
 	_rate = _mixer->getOutputRate();
 
@@ -103,8 +125,10 @@ AdLib::AdLib(Audio::Mixer &mixer, int callbackFreq) : _mixer(&mixer), _opl(0),
 	createOPL();
 	initOPL();
 
+	syncVolume();
+
 	_opl->start(new Common::Functor0Mem<void, AdLib>(this, &AdLib::onTimer), callbackFreq);
-	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle,
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle,
 			this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
@@ -433,6 +457,13 @@ void AdLib::writeKeyScaleLevelVolume(uint8 oper) {
 	volume = (63 - (_operatorParams[oper][kParamLevel] & 0x3F)) * _operatorVolume[oper];
 	volume = 63 - ((2 * volume + kMaxVolume) / (2 * kMaxVolume));
 
+	// Adjust carriers for GUI volume slider
+	if (kOperatorType[oper] == 1) {
+		volume += kVolumeTable[_volume];
+		if (volume > 63)
+			volume = 63;
+	}
+
 	uint8 keyScale = _operatorParams[oper][kParamKeyScaleLevel] << 6;
 
 	writeOPL(0x40 + kOperatorOffset[oper], volume | keyScale);
@@ -634,4 +665,19 @@ void AdLib::setTimerFrequency(int timerFrequency) {
 	_opl->setCallbackFrequency(timerFrequency);
 }
 
+void AdLib::syncVolume() {
+	Common::StackLock slock(_mutex);
+
+	bool mute = false;
+	if (ConfMan.hasKey("mute"))
+		mute = ConfMan.getBool("mute");
+
+	_volume = (mute ? 0 : ConfMan.getInt("music_volume"));
+
+	if (_playing) {
+		for(int i = 0; i < kOperatorCount; i++)
+			writeKeyScaleLevelVolume(i);
+	}
+}
+
 } // End of namespace Gob
diff --git a/engines/gob/sound/adlib.h b/engines/gob/sound/adlib.h
index 6a62152..2c83b15 100644
--- a/engines/gob/sound/adlib.h
+++ b/engines/gob/sound/adlib.h
@@ -53,6 +53,7 @@ public:
 
 	void startPlay();
 	void stopPlay();
+	void syncVolume();
 
 // AudioStream API
 	int  readBuffer(int16 *buffer, const int numSamples);
@@ -211,6 +212,8 @@ protected:
 	void setTimerFrequency(int timerFrequency);
 
 private:
+	static const uint8 kVolumeTable[Audio::Mixer::kMaxMixerVolume + 1];
+
 	static const uint8 kOperatorType  [kOperatorCount];
 	static const uint8 kOperatorOffset[kOperatorCount];
 	static const uint8 kOperatorVoice [kOperatorCount];
@@ -235,6 +238,8 @@ private:
 
 	Common::Mutex _mutex;
 
+	int _volume;
+
 	uint32 _rate;
 
 	uint32 _toPoll;
diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp
index d2b2d3d..9b19b9c 100644
--- a/engines/gob/sound/sound.cpp
+++ b/engines/gob/sound/sound.cpp
@@ -425,6 +425,16 @@ int32 Sound::adlibGetRepeating() const {
 	return false;
 }
 
+void Sound::adlibSyncVolume() {
+	if (!_hasAdLib)
+		return;
+
+	if (_adlPlayer)
+		_adlPlayer->syncVolume();
+	if (_mdyPlayer)
+		_mdyPlayer->syncVolume();
+}
+
 void Sound::adlibSetRepeating(int32 repCount) {
 	if (!_hasAdLib)
 		return;
diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h
index c959959..6ebc323 100644
--- a/engines/gob/sound/sound.h
+++ b/engines/gob/sound/sound.h
@@ -96,6 +96,7 @@ public:
 	int32 adlibGetRepeating() const;
 
 	void adlibSetRepeating(int32 repCount);
+	void adlibSyncVolume();
 
 
 	// Infogrames


Commit: b367ea548d7c129e14fec6164ebe5610d3edeb46
    https://github.com/scummvm/scummvm/commit/b367ea548d7c129e14fec6164ebe5610d3edeb46
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:46-04:00

Commit Message:
QUEEN: Implement original music volume handling

Changed paths:
  A engines/queen/midiadlib.h
    engines/queen/midiadlib.cpp
    engines/queen/music.cpp



diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp
index 4797e5d..457767a 100644
--- a/engines/queen/midiadlib.cpp
+++ b/engines/queen/midiadlib.cpp
@@ -23,109 +23,10 @@
 #include "common/endian.h"
 #include "common/textconsole.h"
 
-#include "audio/fmopl.h"
-#include "audio/softsynth/emumidi.h"
+#include "engines/queen/midiadlib.h"
 
 namespace Queen {
 
-class AdLibMidiDriver : public MidiDriver_Emulated {
-public:
-
-	AdLibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { _adlibWaveformSelect = 0; }
-	~AdLibMidiDriver() {}
-
-	// MidiDriver
-	int open();
-	void close();
-	void send(uint32 b);
-	void metaEvent(byte type, byte *data, uint16 length);
-	MidiChannel *allocateChannel() { return 0; }
-	MidiChannel *getPercussionChannel() { return 0; }
-	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
-
-	// AudioStream
-	int readBuffer(int16 *data, const int numSamples);
-	bool isStereo() const { return false; }
-	int getRate() const { return _mixer->getOutputRate(); }
-
-	// MidiDriver_Emulated
-	void generateSamples(int16 *buf, int len);
-
-private:
-
-	void handleMidiEvent0x90_NoteOn(int channel, int param1, int param2);
-	void handleSequencerSpecificMetaEvent1(int channel, const uint8 *data);
-	void handleSequencerSpecificMetaEvent2(uint8 value);
-	void handleSequencerSpecificMetaEvent3(uint8 value);
-
-	void adlibWrite(uint8 port, uint8 value);
-	void adlibSetupCard();
-	void adlibSetupChannels(int fl);
-	void adlibResetAmpVibratoRhythm(int am, int vib, int kso);
-	void adlibResetChannels();
-	void adlibSetAmpVibratoRhythm();
-	void adlibSetCSMKeyboardSplit();
-	void adlibSetNoteMul(int mul);
-	void adlibSetWaveformSelect(int fl);
-	void adlibSetPitchBend(int channel, int range);
-	void adlibPlayNote(int channel);
-	uint8 adlibPlayNoteHelper(int channel, int note1, int note2, int oct);
-	void adlibTurnNoteOff(int channel);
-	void adlibTurnNoteOn(int channel, int note);
-	void adlibSetupChannelFromSequence(int channel, const uint8 *src, int fl);
-	void adlibSetupChannel(int channel, const uint16 *src, int fl);
-	void adlibSetNoteVolume(int channel, int volume);
-	void adlibSetupChannelHelper(int channel);
-	void adlibSetChannel0x40(int channel);
-	void adlibSetChannel0xC0(int channel);
-	void adlibSetChannel0x60(int channel);
-	void adlibSetChannel0x80(int channel);
-	void adlibSetChannel0x20(int channel);
-	void adlibSetChannel0xE0(int channel);
-
-	void onTimer();
-
-	OPL::OPL *_opl;
-	int _midiNumberOfChannels;
-	int _adlibNoteMul;
-	int _adlibWaveformSelect;
-	int _adlibAMDepthEq48;
-	int _adlibVibratoDepthEq14;
-	int _adlibRhythmEnabled;
-	int _adlibKeyboardSplitOn;
-	int _adlibVibratoRhythm;
-	uint8 _midiChannelsFreqTable[9];
-	uint8 _adlibChannelsLevelKeyScalingTable[11];
-	uint8 _adlibSetupChannelSequence1[14 * 18];
-	uint16 _adlibSetupChannelSequence2[14];
-	int16 _midiChannelsNote2Table[9];
-	uint8 _midiChannelsNote1Table[9];
-	uint8 _midiChannelsOctTable[9];
-	uint16 _adlibChannelsVolume[11];
-	uint16 _adlibMetaSequenceData[28];
-
-	Common::TimerManager::TimerProc _adlibTimerProc;
-	void *_adlibTimerParam;
-
-	static const uint8 _adlibChannelsMappingTable1[];
-	static const uint8 _adlibChannelsNoFeedback[];
-	static const uint8 _adlibChannelsMappingTable2[];
-	static const uint8 _adlibChannelsMappingTable3[];
-	static const uint8 _adlibChannelsKeyScalingTable1[];
-	static const uint8 _adlibChannelsKeyScalingTable2[];
-	static const uint8 _adlibChannelsVolumeTable[];
-	static const uint8 _adlibInitSequenceData1[];
-	static const uint8 _adlibInitSequenceData2[];
-	static const uint8 _adlibInitSequenceData3[];
-	static const uint8 _adlibInitSequenceData4[];
-	static const uint8 _adlibInitSequenceData5[];
-	static const uint8 _adlibInitSequenceData6[];
-	static const uint8 _adlibInitSequenceData7[];
-	static const uint8 _adlibInitSequenceData8[];
-	static const int16 _midiChannelsNoteTable[];
-	static const int16 _midiNoteFreqTable[];
-};
-
 int AdLibMidiDriver::open() {
 	MidiDriver_Emulated::open();
 	_opl = OPL::Config::create();
@@ -140,7 +41,7 @@ int AdLibMidiDriver::open() {
 	}
 
 	_opl->start(new Common::Functor0Mem<void, AdLibMidiDriver>(this, &AdLibMidiDriver::onTimer));
-	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 	return 0;
 }
 
@@ -176,6 +77,11 @@ void AdLibMidiDriver::send(uint32 b) {
 	}
 }
 
+void AdLibMidiDriver::setVolume(uint32 volume) {
+	for (int i = 0; i < _midiNumberOfChannels; ++i)
+		adlibSetChannelVolume(i, volume * 64 / 256 + 64);
+}
+
 void AdLibMidiDriver::metaEvent(byte type, byte *data, uint16 length) {
 	int event = 0;
 	if (length > 4 && READ_BE_UINT32(data) == 0x3F00) {
@@ -278,6 +184,7 @@ void AdLibMidiDriver::adlibSetupCard() {
 		_midiChannelsFreqTable[i] = 0;
 	}
 	memset(_adlibChannelsLevelKeyScalingTable, 127, 11);
+	memset(_adlibChannelsVolumeTable, 128, 11);
 	adlibSetupChannels(0);
 	adlibResetAmpVibratoRhythm(0, 0, 0);
 	adlibSetNoteMul(1);
@@ -473,6 +380,11 @@ void AdLibMidiDriver::adlibSetNoteVolume(int channel, int volume) {
 	}
 }
 
+void AdLibMidiDriver::adlibSetChannelVolume(int channel, uint8 volume) {
+	if (channel < (_adlibRhythmEnabled ? 11 : 9))
+		_adlibChannelsVolumeTable[channel] = volume;
+}
+
 void AdLibMidiDriver::adlibSetupChannelHelper(int channel) {
 	adlibSetAmpVibratoRhythm();
 	adlibSetCSMKeyboardSplit();
@@ -583,10 +495,6 @@ const uint8 AdLibMidiDriver::_adlibChannelsKeyScalingTable2[] = {
 	0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 16, 255, 14, 255, 17, 255, 13, 255
 };
 
-const uint8 AdLibMidiDriver::_adlibChannelsVolumeTable[] = {
-	128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128
-};
-
 const uint8 AdLibMidiDriver::_adlibInitSequenceData1[] = {
 	1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0
 };
@@ -642,8 +550,4 @@ const int16 AdLibMidiDriver::_midiNoteFreqTable[] = {
 	-363, -361, -359, -356, -354, -351, -349, -347, -344, -342, -339, -337
 };
 
-MidiDriver *C_Player_CreateAdLibMidiDriver(Audio::Mixer *mixer) {
-	return new AdLibMidiDriver(mixer);
-}
-
 } // End of namespace Queen
diff --git a/engines/queen/midiadlib.h b/engines/queen/midiadlib.h
new file mode 100644
index 0000000..d559025
--- /dev/null
+++ b/engines/queen/midiadlib.h
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/fmopl.h"
+#include "audio/mididrv.h"
+#include "audio/softsynth/emumidi.h"
+
+namespace Queen {
+
+class AdLibMidiDriver : public MidiDriver_Emulated {
+public:
+
+	AdLibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { _adlibWaveformSelect = 0; }
+	~AdLibMidiDriver() {}
+
+	// MidiDriver
+	int open();
+	void close();
+	void send(uint32 b);
+	void metaEvent(byte type, byte *data, uint16 length);
+	MidiChannel *allocateChannel() { return 0; }
+	MidiChannel *getPercussionChannel() { return 0; }
+	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
+	// AudioStream
+	int readBuffer(int16 *data, const int numSamples);
+	bool isStereo() const { return false; }
+	int getRate() const { return _mixer->getOutputRate(); }
+
+	// MidiDriver_Emulated
+	void generateSamples(int16 *buf, int len);
+
+	void setVolume(uint32 volume);
+
+private:
+
+	void handleMidiEvent0x90_NoteOn(int channel, int param1, int param2);
+	void handleSequencerSpecificMetaEvent1(int channel, const uint8 *data);
+	void handleSequencerSpecificMetaEvent2(uint8 value);
+	void handleSequencerSpecificMetaEvent3(uint8 value);
+
+	void adlibWrite(uint8 port, uint8 value);
+	void adlibSetupCard();
+	void adlibSetupChannels(int fl);
+	void adlibResetAmpVibratoRhythm(int am, int vib, int kso);
+	void adlibResetChannels();
+	void adlibSetAmpVibratoRhythm();
+	void adlibSetCSMKeyboardSplit();
+	void adlibSetNoteMul(int mul);
+	void adlibSetWaveformSelect(int fl);
+	void adlibSetPitchBend(int channel, int range);
+	void adlibPlayNote(int channel);
+	uint8 adlibPlayNoteHelper(int channel, int note1, int note2, int oct);
+	void adlibTurnNoteOff(int channel);
+	void adlibTurnNoteOn(int channel, int note);
+	void adlibSetupChannelFromSequence(int channel, const uint8 *src, int fl);
+	void adlibSetupChannel(int channel, const uint16 *src, int fl);
+	void adlibSetChannelVolume(int channel, uint8 volume);
+	void adlibSetNoteVolume(int channel, int volume);
+	void adlibSetupChannelHelper(int channel);
+	void adlibSetChannel0x40(int channel);
+	void adlibSetChannel0xC0(int channel);
+	void adlibSetChannel0x60(int channel);
+	void adlibSetChannel0x80(int channel);
+	void adlibSetChannel0x20(int channel);
+	void adlibSetChannel0xE0(int channel);
+
+	void onTimer();
+
+	OPL::OPL *_opl;
+	int _midiNumberOfChannels;
+	int _adlibNoteMul;
+	int _adlibWaveformSelect;
+	int _adlibAMDepthEq48;
+	int _adlibVibratoDepthEq14;
+	int _adlibRhythmEnabled;
+	int _adlibKeyboardSplitOn;
+	int _adlibVibratoRhythm;
+	uint8 _midiChannelsFreqTable[9];
+	uint8 _adlibChannelsLevelKeyScalingTable[11];
+	uint8 _adlibSetupChannelSequence1[14 * 18];
+	uint16 _adlibSetupChannelSequence2[14];
+	int16 _midiChannelsNote2Table[9];
+	uint8 _midiChannelsNote1Table[9];
+	uint8 _midiChannelsOctTable[9];
+	uint16 _adlibChannelsVolume[11];
+	uint16 _adlibMetaSequenceData[28];
+	uint8 _adlibChannelsVolumeTable[11];
+
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
+	static const uint8 _adlibChannelsMappingTable1[];
+	static const uint8 _adlibChannelsNoFeedback[];
+	static const uint8 _adlibChannelsMappingTable2[];
+	static const uint8 _adlibChannelsMappingTable3[];
+	static const uint8 _adlibChannelsKeyScalingTable1[];
+	static const uint8 _adlibChannelsKeyScalingTable2[];
+	static const uint8 _adlibInitSequenceData1[];
+	static const uint8 _adlibInitSequenceData2[];
+	static const uint8 _adlibInitSequenceData3[];
+	static const uint8 _adlibInitSequenceData4[];
+	static const uint8 _adlibInitSequenceData5[];
+	static const uint8 _adlibInitSequenceData6[];
+	static const uint8 _adlibInitSequenceData7[];
+	static const uint8 _adlibInitSequenceData8[];
+	static const int16 _midiChannelsNoteTable[];
+	static const int16 _midiNoteFreqTable[];
+};
+
+} // End of namespace Queen
diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp
index 93d6527..600956a 100644
--- a/engines/queen/music.cpp
+++ b/engines/queen/music.cpp
@@ -23,6 +23,7 @@
 #include "common/config-manager.h"
 #include "common/events.h"
 
+#include "queen/midiadlib.h"
 #include "queen/music.h"
 #include "queen/queen.h"
 #include "queen/resource.h"
@@ -33,8 +34,6 @@
 
 namespace Queen {
 
-extern MidiDriver *C_Player_CreateAdLibMidiDriver(Audio::Mixer *);
-
 MidiMusic::MidiMusic(QueenEngine *vm)
 	: _isPlaying(false), _isLooping(false),
 	_randomLoop(false), _masterVolume(192),
@@ -69,7 +68,7 @@ MidiMusic::MidiMusic(QueenEngine *vm)
 //		if (READ_LE_UINT16(_musicData + 2) != infoOffset) {
 //			defaultAdLibVolume = _musicData[infoOffset];
 //		}
-		_driver = C_Player_CreateAdLibMidiDriver(vm->_mixer);
+		_driver = new AdLibMidiDriver(g_system->getMixer());
 	} else {
 		_driver = MidiDriver::createMidi(dev);
 		if (_nativeMT32) {
@@ -117,6 +116,9 @@ void MidiMusic::setVolume(int volume) {
 		if (_channelsTable[i])
 			_channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255);
 	}
+
+	if (_adlib)
+		static_cast<AdLibMidiDriver*>(_driver)->setVolume(volume);
 }
 
 void MidiMusic::playSong(uint16 songNum) {


Commit: dce05c520b2e1f273f395c986573f532ee698921
    https://github.com/scummvm/scummvm/commit/dce05c520b2e1f273f395c986573f532ee698921
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:46-04:00

Commit Message:
AUDIO: Be consistent with calling stop() in OPL destructors

Changed paths:
    audio/fmopl.cpp
    audio/softsynth/opl/dosbox.cpp
    audio/softsynth/opl/mame.cpp



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 07c5b44..638da6c 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -201,8 +201,10 @@ EmulatedOPL::EmulatedOPL() :
 
 EmulatedOPL::~EmulatedOPL() {
 	// Stop callbacks, just in case. If it's still playing at this
-	// point, there's probably a bigger issue, though.
-	stopCallbacks();
+	// point, there's probably a bigger issue, though. The subclass
+	// needs to call stop() or the pointer can still use be used in
+	// the mixer thread at the same time.
+	stop();
 }
 
 int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) {
diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp
index 09200c7..6e8b4a9 100644
--- a/audio/softsynth/opl/dosbox.cpp
+++ b/audio/softsynth/opl/dosbox.cpp
@@ -149,11 +149,11 @@ OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(0) {
 }
 
 OPL::~OPL() {
+	stop();
 	free();
 }
 
 void OPL::free() {
-	stopCallbacks();
 	delete _emulator;
 	_emulator = 0;
 }
diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp
index fe23d30..d43f638 100644
--- a/audio/softsynth/opl/mame.cpp
+++ b/audio/softsynth/opl/mame.cpp
@@ -48,7 +48,7 @@ namespace OPL {
 namespace MAME {
 
 OPL::~OPL() {
-	stopCallbacks();
+	stop();
 	MAME::OPLDestroy(_opl);
 	_opl = 0;
 }


Commit: 8bcbcd6c167e8e7169f006da459f3cbe450a4a59
    https://github.com/scummvm/scummvm/commit/8bcbcd6c167e8e7169f006da459f3cbe450a4a59
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:47-04:00

Commit Message:
AUDIO: Change callback frequency without restarting the audio stream

Changed paths:
    audio/fmopl.cpp
    audio/fmopl.h



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 638da6c..9af7aff 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -183,14 +183,6 @@ void OPL::stop() {
 	_callback.reset();
 }
 
-void OPL::setCallbackFrequency(int timerFrequency) {
-	if (!isRunning())
-		return;
-
-	stopCallbacks();
-	startCallbacks(timerFrequency);
-}
-
 bool OPL::_hasInstance = false;
 
 EmulatedOPL::EmulatedOPL() :
@@ -239,6 +231,17 @@ int EmulatedOPL::getRate() const {
 }
 
 void EmulatedOPL::startCallbacks(int timerFrequency) {
+	setCallbackFrequency(timerFrequency);
+	// TODO: Eventually start mixer playback here
+	//g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+void EmulatedOPL::stopCallbacks() {
+	// TODO: Eventually stop mixer playback here
+	//g_system->getMixer()->stopHandle(*_handle);
+}
+
+void EmulatedOPL::setCallbackFrequency(int timerFrequency) {
 	_baseFreq = timerFrequency;
 	assert(_baseFreq != 0);
 
@@ -249,20 +252,6 @@ void EmulatedOPL::startCallbacks(int timerFrequency) {
 	// but less prone to arithmetic overflow.
 
 	_samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq;
-
-	// TODO: Eventually start mixer playback here
-	//g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-}
-
-void EmulatedOPL::stopCallbacks() {
-	// TODO: Eventually stop mixer playback here
-	//g_system->getMixer()->stopHandle(*_handle);
-}
-
-bool EmulatedOPL::isRunning() const {
-	// TODO
-	//return g_system->getMixer()->isSoundHandleActive(*_handle);
-	return true;
 }
 
 } // End of namespace OPL
diff --git a/audio/fmopl.h b/audio/fmopl.h
index 243cd29..8baa9aa 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -176,15 +176,10 @@ public:
 	void stop();
 
 	/**
-	 * Is the OPL running?
+	 * Change the callback frequency. This must only be called from a
+	 * timer proc.
 	 */
-	virtual bool isRunning() const = 0;
-
-	/**
-	 * Change the callback frequency. This has no effect if start()
-	 * has not already been called.
-	 */
-	void setCallbackFrequency(int timerFrequency);
+	virtual void setCallbackFrequency(int timerFrequency) = 0;
 
 	enum {
 		/**
@@ -218,6 +213,7 @@ public:
 	// OPL API
 	int readBuffer(int16 *buffer, const int numSamples);
 	bool isRunning() const;
+	void setCallbackFrequency(int timerFrequency);
 
 	int getRate() const;
 


Commit: bed9da8b9dbbaa19d317f71663e42875c1717fda
    https://github.com/scummvm/scummvm/commit/bed9da8b9dbbaa19d317f71663e42875c1717fda
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:47-04:00

Commit Message:
AUDIO: Remove all AudioStream access to OPL

Changed paths:
    audio/fmopl.cpp
    audio/fmopl.h
    audio/miles_adlib.cpp
    audio/softsynth/adlib.cpp
    audio/softsynth/opl/dosbox.cpp
    audio/softsynth/opl/mame.cpp
    engines/agos/drivers/accolade/adlib.cpp
    engines/cine/sound.cpp
    engines/cruise/sound.cpp
    engines/gob/sound/adlib.cpp
    engines/gob/sound/adlib.h
    engines/gob/sound/musplayer.cpp
    engines/kyra/sound_adlib.cpp
    engines/mads/nebular/sound_nebular.cpp
    engines/mads/nebular/sound_nebular.h
    engines/parallaction/adlib.cpp
    engines/queen/midiadlib.cpp
    engines/queen/midiadlib.h
    engines/queen/music.cpp
    engines/sci/sound/drivers/adlib.cpp
    engines/scumm/players/player_ad.cpp
    engines/scumm/players/player_ad.h
    engines/sherlock/scalpel/drivers/adlib.cpp
    engines/sky/music/adlibmusic.cpp
    engines/sky/music/adlibmusic.h
    engines/tsage/sound.cpp
    engines/tsage/sound.h



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 9af7aff..4bc902e 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -188,7 +188,8 @@ bool OPL::_hasInstance = false;
 EmulatedOPL::EmulatedOPL() :
 	_nextTick(0),
 	_samplesPerTick(0),
-	_baseFreq(0) {
+	_baseFreq(0),
+	_handle(new Audio::SoundHandle()) {
 }
 
 EmulatedOPL::~EmulatedOPL() {
@@ -197,6 +198,8 @@ EmulatedOPL::~EmulatedOPL() {
 	// needs to call stop() or the pointer can still use be used in
 	// the mixer thread at the same time.
 	stop();
+
+	delete _handle;
 }
 
 int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) {
@@ -232,13 +235,11 @@ int EmulatedOPL::getRate() const {
 
 void EmulatedOPL::startCallbacks(int timerFrequency) {
 	setCallbackFrequency(timerFrequency);
-	// TODO: Eventually start mixer playback here
-	//g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 void EmulatedOPL::stopCallbacks() {
-	// TODO: Eventually stop mixer playback here
-	//g_system->getMixer()->stopHandle(*_handle);
+	g_system->getMixer()->stopHandle(*_handle);
 }
 
 void EmulatedOPL::setCallbackFrequency(int timerFrequency) {
diff --git a/audio/fmopl.h b/audio/fmopl.h
index 8baa9aa..091e0bd 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -23,10 +23,16 @@
 #ifndef AUDIO_FMOPL_H
 #define AUDIO_FMOPL_H
 
+#include "audio/audiostream.h"
+
 #include "common/func.h"
 #include "common/ptr.h"
 #include "common/scummsys.h"
 
+namespace Audio {
+class SoundHandle;
+}
+
 namespace Common {
 class String;
 }
@@ -149,23 +155,6 @@ public:
 	virtual void writeReg(int r, int v) = 0;
 
 	/**
-	 * Read up to 'length' samples.
-	 *
-	 * Data will be in native endianess, 16 bit per sample, signed.
-	 * For stereo OPL, buffer will be filled with interleaved
-	 * left and right channel samples, starting with a left sample.
-	 * Furthermore, the samples in the left and right are summed up.
-	 * So if you request 4 samples from a stereo OPL, you will get
-	 * a total of two left channel and two right channel samples.
-	 */
-	virtual int readBuffer(int16 *buffer, const int numSamples) = 0;
-
-	/**
-	 * Returns whether the setup OPL mode is stereo or not
-	 */
-	virtual bool isStereo() const = 0;
-
-	/**
 	 * Start the OPL with callbacks.
 	 */
 	void start(TimerCallback *callback, int timerFrequency = kDefaultCallbackFrequency);
@@ -205,17 +194,18 @@ protected:
 	Common::ScopedPtr<TimerCallback> _callback;
 };
 
-class EmulatedOPL : public OPL {
+class EmulatedOPL : public OPL, protected Audio::AudioStream {
 public:
 	EmulatedOPL();
 	virtual ~EmulatedOPL();
 
 	// OPL API
-	int readBuffer(int16 *buffer, const int numSamples);
-	bool isRunning() const;
 	void setCallbackFrequency(int timerFrequency);
 
+	// AudioStream API
+	int readBuffer(int16 *buffer, const int numSamples);
 	int getRate() const;
+	bool endOfData() const { return false; }
 
 protected:
 	// OPL API
@@ -243,6 +233,8 @@ private:
 
 	int _nextTick;
 	int _samplesPerTick;
+
+	Audio::SoundHandle *_handle;
 };
 
 } // End of namespace OPL
diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp
index 7f3f05a..bf5c9d4 100644
--- a/audio/miles_adlib.cpp
+++ b/audio/miles_adlib.cpp
@@ -116,9 +116,9 @@ uint16 milesAdLibVolumeSensitivityTable[] = {
 };
 
 
-class MidiDriver_Miles_AdLib : public MidiDriver_Emulated {
+class MidiDriver_Miles_AdLib : public MidiDriver {
 public:
-	MidiDriver_Miles_AdLib(Audio::Mixer *mixer, InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount);
+	MidiDriver_Miles_AdLib(InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount);
 	virtual ~MidiDriver_Miles_AdLib();
 
 	// MidiDriver
@@ -128,15 +128,8 @@ public:
 	MidiChannel *allocateChannel() { return NULL; }
 	MidiChannel *getPercussionChannel() { return NULL; }
 
-	// AudioStream
-	int readBuffer(int16 *data, const int numSamples);
-	bool isStereo() const { return _modeStereo; }
-	int getRate() const { return _mixer->getOutputRate(); }
-	int getPolyphony() const { return _modePhysicalFmVoicesCount; }
-	bool hasRhythmChannel() const { return false; }
-
-	// MidiDriver_Emulated
-	void generateSamples(int16 *buf, int len);
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
 	void setVolume(byte volume);
 	virtual uint32 property(int prop, uint32 param);
@@ -228,6 +221,8 @@ private:
 	Common::TimerManager::TimerProc _adlibTimerProc;
 	void *_adlibTimerParam;
 
+	bool _isOpen;
+
 	// stores information about all MIDI channels (not the actual OPL FM voice channels!)
 	MidiChannelEntry _midiChannels[MILES_MIDI_CHANNEL_COUNT];
 
@@ -275,9 +270,9 @@ private:
 	void pitchBendChange(byte MIDIchannel, byte parameter1, byte parameter2);
 };
 
-MidiDriver_Miles_AdLib::MidiDriver_Miles_AdLib(Audio::Mixer *mixer, InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount)
-	: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0),
-	  _adlibTimerProc(0), _adlibTimerParam(0) {
+MidiDriver_Miles_AdLib::MidiDriver_Miles_AdLib(InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount)
+	: _masterVolume(15), _opl(0),
+	  _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) {
 
 	_instrumentTablePtr = instrumentTablePtr;
 	_instrumentTableCount = instrumentTableCount;
@@ -324,10 +319,9 @@ int MidiDriver_Miles_AdLib::open() {
 
 	_opl->init();
 
-	MidiDriver_Emulated::open();
+	_isOpen = true;
 
 	_opl->start(new Common::Functor0Mem<void, MidiDriver_Miles_AdLib>(this, &MidiDriver_Miles_AdLib::onTimer));
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 
 	resetAdLib();
 
@@ -335,9 +329,8 @@ int MidiDriver_Miles_AdLib::open() {
 }
 
 void MidiDriver_Miles_AdLib::close() {
-	_mixer->stopHandle(_mixerSoundHandle);
-
 	delete _opl;
+	_isOpen = false;
 }
 
 void MidiDriver_Miles_AdLib::setVolume(byte volume) {
@@ -444,14 +437,6 @@ void MidiDriver_Miles_AdLib::send(uint32 b) {
 	}
 }
 
-void MidiDriver_Miles_AdLib::generateSamples(int16 *data, int len) {
-	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
-}
-
-int MidiDriver_Miles_AdLib::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void MidiDriver_Miles_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
 	_adlibTimerProc = timerProc;
 	_adlibTimerParam = timerParam;
@@ -1283,7 +1268,7 @@ MidiDriver *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, c
 	// Free instrument file/stream data
 	delete[] streamDataPtr;
 
-	return new MidiDriver_Miles_AdLib(g_system->getMixer(), instrumentTablePtr, instrumentTableCount);
+	return new MidiDriver_Miles_AdLib(instrumentTablePtr, instrumentTableCount);
 }
 
 } // End of namespace Audio
diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp
index c7b5297..f609164 100644
--- a/audio/softsynth/adlib.cpp
+++ b/audio/softsynth/adlib.cpp
@@ -927,18 +927,20 @@ static void createLookupTable() {
 //
 ////////////////////////////////////////
 
-class MidiDriver_ADLIB : public MidiDriver_Emulated {
+class MidiDriver_ADLIB : public MidiDriver {
 	friend class AdLibPart;
 	friend class AdLibPercussionChannel;
 
 public:
-	MidiDriver_ADLIB(Audio::Mixer *mixer);
+	MidiDriver_ADLIB();
 
 	int open();
 	void close();
 	void send(uint32 b);
 	void send(byte channel, uint32 b); // Supports higher than channel 15
 	uint32 property(int prop, uint32 param);
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
 	void setPitchBendRange(byte channel, uint range);
 	void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
@@ -946,12 +948,6 @@ public:
 	MidiChannel *allocateChannel();
 	MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported
 
-
-	// AudioStream API
-	int readBuffer(int16 *data, const int numSamples);
-	bool isStereo() const { return _opl->isStereo(); }
-	int getRate() const { return _mixer->getOutputRate(); }
-
 	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
 
 private:
@@ -980,7 +976,8 @@ private:
 	AdLibPart _parts[32];
 	AdLibPercussionChannel _percussion;
 
-	void generateSamples(int16 *buf, int len);
+	bool _isOpen;
+
 	void onTimer();
 	void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
 	void partKeyOff(AdLibPart *part, byte note);
@@ -1382,8 +1379,7 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins
 
 // MidiDriver method implementations
 
-MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)
-	: MidiDriver_Emulated(mixer) {
+MidiDriver_ADLIB::MidiDriver_ADLIB() {
 	uint i;
 
 	_scummSmallHeader = false;
@@ -1411,13 +1407,14 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)
 	_opl = 0;
 	_adlibTimerProc = 0;
         _adlibTimerParam = 0;
+	_isOpen = false;
 }
 
 int MidiDriver_ADLIB::open() {
 	if (_isOpen)
 		return MERR_ALREADY_OPEN;
 
-	MidiDriver_Emulated::open();
+	_isOpen = true;
 
 	int i;
 	AdLibVoice *voice;
@@ -1461,8 +1458,6 @@ int MidiDriver_ADLIB::open() {
 #endif
 
 	_opl->start(new Common::Functor0Mem<void, MidiDriver_ADLIB>(this, &MidiDriver_ADLIB::onTimer));
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
 	return 0;
 }
 
@@ -1471,7 +1466,8 @@ void MidiDriver_ADLIB::close() {
 		return;
 	_isOpen = false;
 
-	_mixer->stopHandle(_mixerSoundHandle);
+	// Stop the OPL timer
+	_opl->stop();
 
 	uint i;
 	for (i = 0; i < ARRAYSIZE(_voices); ++i) {
@@ -1625,14 +1621,6 @@ void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) {
 }
 #endif
 
-void MidiDriver_ADLIB::generateSamples(int16 *data, int len) {
-	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
-}
-
-int MidiDriver_ADLIB::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void MidiDriver_ADLIB::onTimer() {
 	if (_adlibTimerProc)
 		(*_adlibTimerProc)(_adlibTimerParam);
@@ -2318,7 +2306,7 @@ MusicDevices AdLibEmuMusicPlugin::getDevices() const {
 }
 
 Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
-	*mididriver = new MidiDriver_ADLIB(g_system->getMixer());
+	*mididriver = new MidiDriver_ADLIB();
 
 	return Common::kNoError;
 }
diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp
index 6e8b4a9..3d90ec9 100644
--- a/audio/softsynth/opl/dosbox.cpp
+++ b/audio/softsynth/opl/dosbox.cpp
@@ -177,10 +177,6 @@ bool OPL::init() {
 		_emulator->WriteReg(0x105, 1);
 	}
 
-	// FIXME: Remove this once EmulatedOPL is actually controlling playback
-	if (!_callback)
-		start(0);
-
 	return true;
 }
 
diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp
index d43f638..696169b 100644
--- a/audio/softsynth/opl/mame.cpp
+++ b/audio/softsynth/opl/mame.cpp
@@ -61,9 +61,6 @@ bool OPL::init() {
 
 	_opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate());
 
-	// FIXME: Remove this once EmulatedOPL is actually controlling playback
-	start(0);
-
 	return (_opl != 0);
 }
 
diff --git a/engines/agos/drivers/accolade/adlib.cpp b/engines/agos/drivers/accolade/adlib.cpp
index 33a7595..d227106 100644
--- a/engines/agos/drivers/accolade/adlib.cpp
+++ b/engines/agos/drivers/accolade/adlib.cpp
@@ -112,9 +112,9 @@ const uint16 frequencyLookUpTableMusicDrv[12] = {
 //
 // I have currently not implemented dynamic channel allocation.
 
-class MidiDriver_Accolade_AdLib : public MidiDriver_Emulated {
+class MidiDriver_Accolade_AdLib : public MidiDriver {
 public:
-	MidiDriver_Accolade_AdLib(Audio::Mixer *mixer);
+	MidiDriver_Accolade_AdLib();
 	virtual ~MidiDriver_Accolade_AdLib();
 
 	// MidiDriver
@@ -124,15 +124,8 @@ public:
 	MidiChannel *allocateChannel() { return NULL; }
 	MidiChannel *getPercussionChannel() { return NULL; }
 
-	// AudioStream
-	int readBuffer(int16 *data, const int numSamples);
-	bool isStereo() const { return false; }
-	int getRate() const { return _mixer->getOutputRate(); }
-	int getPolyphony() const { return AGOS_ADLIB_VOICES_COUNT; }
-	bool hasRhythmChannel() const { return false; }
-
-	// MidiDriver_Emulated
-	void generateSamples(int16 *buf, int len);
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
 	void setVolume(byte volume);
 	virtual uint32 property(int prop, uint32 param);
@@ -176,6 +169,8 @@ private:
 	Common::TimerManager::TimerProc _adlibTimerProc;
 	void *_adlibTimerParam;
 
+	bool _isOpen;
+
 	// points to a MIDI channel for each of the new voice channels
 	byte _voiceChannelMapping[AGOS_ADLIB_VOICES_COUNT];
 
@@ -196,9 +191,9 @@ private:
 	void noteOff(byte FMvoiceChannel, byte note, bool dontCheckNote);
 };
 
-MidiDriver_Accolade_AdLib::MidiDriver_Accolade_AdLib(Audio::Mixer *mixer)
-		: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0),
-		  _adlibTimerProc(0), _adlibTimerParam(0) {
+MidiDriver_Accolade_AdLib::MidiDriver_Accolade_AdLib()
+		: _masterVolume(15), _opl(0),
+		  _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) {
 	memset(_channelMapping, 0, sizeof(_channelMapping));
 	memset(_instrumentMapping, 0, sizeof(_instrumentMapping));
 	memset(_instrumentVolumeAdjust, 0, sizeof(_instrumentVolumeAdjust));
@@ -227,10 +222,9 @@ int MidiDriver_Accolade_AdLib::open() {
 
 	_opl->init();
 
-	MidiDriver_Emulated::open();
+	_isOpen = true;
 
 	_opl->start(new Common::Functor0Mem<void, MidiDriver_Accolade_AdLib>(this, &MidiDriver_Accolade_AdLib::onTimer));
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
 
 	resetAdLib();
 
@@ -264,9 +258,8 @@ int MidiDriver_Accolade_AdLib::open() {
 }
 
 void MidiDriver_Accolade_AdLib::close() {
-	_mixer->stopHandle(_mixerSoundHandle);
-
 	delete _opl;
+	_isOpen = false;
 }
 
 void MidiDriver_Accolade_AdLib::setVolume(byte volume) {
@@ -374,14 +367,6 @@ void MidiDriver_Accolade_AdLib::send(uint32 b) {
 	}
 }
 
-void MidiDriver_Accolade_AdLib::generateSamples(int16 *data, int len) {
-	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
-}
-
-int MidiDriver_Accolade_AdLib::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void MidiDriver_Accolade_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
 	_adlibTimerProc = timerProc;
 	_adlibTimerParam = timerParam;
@@ -886,7 +871,7 @@ MidiDriver *MidiDriver_Accolade_AdLib_create(Common::String driverFilename) {
 	if (!driverData)
 		error("ACCOLADE-ADLIB: error during readDriver()");
 
-	MidiDriver_Accolade_AdLib *driver = new MidiDriver_Accolade_AdLib(g_system->getMixer());
+	MidiDriver_Accolade_AdLib *driver = new MidiDriver_Accolade_AdLib();
 	if (driver) {
 		if (!driver->setupInstruments(driverData, driverDataSize, isMusicDrvFile)) {
 			delete driver;
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index a20ad5d..0c788b8 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -101,7 +101,7 @@ struct AdLibSoundInstrument {
 	byte amDepth;
 };
 
-class AdLibSoundDriver : public PCSoundDriver, Audio::AudioStream {
+class AdLibSoundDriver : public PCSoundDriver {
 public:
 	AdLibSoundDriver(Audio::Mixer *mixer);
 	virtual ~AdLibSoundDriver();
@@ -112,12 +112,6 @@ public:
 	virtual void stopChannel(int channel);
 	virtual void stopAll();
 
-	// AudioStream interface
-	virtual int readBuffer(int16 *buffer, const int numSamples);
-	virtual bool isStereo() const { return false; }
-	virtual bool endOfData() const { return false; }
-	virtual int getRate() const { return _sampleRate; }
-
 	void initCard();
 	void onTimer();
 	void setupInstrument(const byte *data, int channel);
@@ -129,9 +123,7 @@ protected:
 	void *_upRef;
 
 	OPL::OPL *_opl;
-	int _sampleRate;
 	Audio::Mixer *_mixer;
-	Audio::SoundHandle _soundHandle;
 
 	byte _vibrato;
 	int _channelsVolumeTable[4];
@@ -283,7 +275,6 @@ void PCSoundDriver::resetChannel(int channel) {
 AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	: _upCb(0), _upRef(0), _mixer(mixer) {
 
-	_sampleRate = _mixer->getOutputRate();
 	_opl = OPL::Config::create();
 	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
@@ -292,11 +283,9 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	memset(_instrumentsTable, 0, sizeof(_instrumentsTable));
 	initCard();
 	_opl->start(new Common::Functor0Mem<void, AdLibSoundDriver>(this, &AdLibSoundDriver::onTimer), 50);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibSoundDriver::~AdLibSoundDriver() {
-	_mixer->stopHandle(_soundHandle);
 	delete _opl;
 }
 
@@ -346,10 +335,6 @@ void AdLibSoundDriver::stopAll() {
 	_opl->writeReg(0xBD, 0);
 }
 
-int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
-	return _opl->readBuffer(buffer, numSamples);
-}
-
 void AdLibSoundDriver::initCard() {
 	_vibrato = 0x20;
 	_opl->writeReg(0xBD, _vibrato);
diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp
index 7d48e6e..f57435f 100644
--- a/engines/cruise/sound.cpp
+++ b/engines/cruise/sound.cpp
@@ -108,7 +108,7 @@ struct VolumeEntry {
 	int adjusted;
 };
 
-class AdLibSoundDriver : public PCSoundDriver, Audio::AudioStream {
+class AdLibSoundDriver : public PCSoundDriver {
 public:
 	AdLibSoundDriver(Audio::Mixer *mixer);
 	virtual ~AdLibSoundDriver();
@@ -118,12 +118,6 @@ public:
 	virtual void stopChannel(int channel);
 	virtual void stopAll();
 
-	// AudioStream interface
-	virtual int readBuffer(int16 *buffer, const int numSamples);
-	virtual bool isStereo() const { return false; }
-	virtual bool endOfData() const { return false; }
-	virtual int getRate() const { return _sampleRate; }
-
 	void initCard();
 	void onTimer();
 	void setupInstrument(const byte *data, int channel);
@@ -136,9 +130,7 @@ public:
 
 protected:
 	OPL::OPL *_opl;
-	int _sampleRate;
 	Audio::Mixer *_mixer;
-	Audio::SoundHandle _soundHandle;
 
 	byte _vibrato;
 	VolumeEntry _channelsVolumeTable[5];
@@ -302,7 +294,6 @@ void PCSoundDriver::syncSounds() {
 
 AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	: _mixer(mixer) {
-	_sampleRate = _mixer->getOutputRate();
 	_opl = OPL::Config::create();
 	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
@@ -318,11 +309,9 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer)
 	_sfxVolume = ConfMan.getBool("sfx_mute") ? 0 : MIN(255, ConfMan.getInt("sfx_volume"));
 
 	_opl->start(new Common::Functor0Mem<void, AdLibSoundDriver>(this, &AdLibSoundDriver::onTimer), 50);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibSoundDriver::~AdLibSoundDriver() {
-	_mixer->stopHandle(_soundHandle);
 	delete _opl;
 }
 
@@ -390,10 +379,6 @@ void AdLibSoundDriver::stopAll() {
 	_opl->writeReg(0xBD, 0);
 }
 
-int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) {
-	return _opl->readBuffer(buffer, numSamples);
-}
-
 void AdLibSoundDriver::initCard() {
 	_vibrato = 0x20;
 	_opl->writeReg(0xBD, _vibrato);
diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index 866eecf..995cc48 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -118,8 +118,6 @@ const uint16 AdLib::kHihatParams    [kParamCount] = {
 AdLib::AdLib(Audio::Mixer &mixer, int callbackFreq) : _mixer(&mixer), _opl(0),
 	_toPoll(0), _repCount(0), _first(true), _playing(false), _ended(true), _volume(0) {
 
-	_rate = _mixer->getOutputRate();
-
 	initFreqs();
 
 	createOPL();
@@ -128,13 +126,9 @@ AdLib::AdLib(Audio::Mixer &mixer, int callbackFreq) : _mixer(&mixer), _opl(0),
 	syncVolume();
 
 	_opl->start(new Common::Functor0Mem<void, AdLib>(this, &AdLib::onTimer), callbackFreq);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle,
-			this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLib::~AdLib() {
-	_mixer->stopHandle(_handle);
-
 	delete _opl;
 }
 
@@ -168,10 +162,6 @@ void AdLib::createOPL() {
 	}
 }
 
-int AdLib::readBuffer(int16 *buffer, const int numSamples) {
-	return _opl->readBuffer(buffer, numSamples);
-}
-
 void AdLib::onTimer() {
 	Common::StackLock slock(_mutex);
 
@@ -218,22 +208,6 @@ void AdLib::onTimer() {
 	}
 }
 
-bool AdLib::isStereo() const {
-	return _opl->isStereo();
-}
-
-bool AdLib::endOfData() const {
-	return false;
-}
-
-bool AdLib::endOfStream() const {
-	return false;
-}
-
-int AdLib::getRate() const {
-	return _rate;
-}
-
 bool AdLib::isPlaying() const {
 	return _playing;
 }
diff --git a/engines/gob/sound/adlib.h b/engines/gob/sound/adlib.h
index 2c83b15..28ebf9d 100644
--- a/engines/gob/sound/adlib.h
+++ b/engines/gob/sound/adlib.h
@@ -35,7 +35,7 @@ namespace OPL {
 namespace Gob {
 
 /** Base class for a player of an AdLib music format. */
-class AdLib : public Audio::AudioStream {
+class AdLib {
 public:
 	AdLib(Audio::Mixer &mixer, int callbackFrequency);
 	virtual ~AdLib();
@@ -55,13 +55,6 @@ public:
 	void stopPlay();
 	void syncVolume();
 
-// AudioStream API
-	int  readBuffer(int16 *buffer, const int numSamples);
-	bool isStereo()    const;
-	bool endOfData()   const;
-	bool endOfStream() const;
-	int  getRate()     const;
-
 protected:
 	enum kVoice {
 		kVoiceMelody0   =  0,
@@ -233,7 +226,6 @@ private:
 
 
 	Audio::Mixer *_mixer;
-	Audio::SoundHandle _handle;
 	OPL::OPL *_opl;
 
 	Common::Mutex _mutex;
diff --git a/engines/gob/sound/musplayer.cpp b/engines/gob/sound/musplayer.cpp
index bf90d44..2c0330e 100644
--- a/engines/gob/sound/musplayer.cpp
+++ b/engines/gob/sound/musplayer.cpp
@@ -57,8 +57,12 @@ uint32 MUSPlayer::pollMusic(bool first) {
 		return 0;
 	}
 
-	if (first)
+	if (first) {
+		// Set the timer frequency on first run.
+		// Do not set it in rewind() for thread safety reasons.
+		setTimerFrequency((_ticksPerBeat * _tempo) / 60);
 		return *_playPos++;
+	}
 
 	uint16 delay = 0;
 	while (delay == 0) {
@@ -185,8 +189,6 @@ void MUSPlayer::rewind() {
 
 	setPercussionMode(_soundMode != 0);
 	setPitchRange(_pitchBendRange);
-
-	setTimerFrequency((_ticksPerBeat * _tempo) / 60);
 }
 
 bool MUSPlayer::loadSND(Common::SeekableReadStream &snd) {
diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index 999bd96..1d741d8 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -55,7 +55,7 @@
 
 namespace Kyra {
 
-class AdLibDriver : public Audio::AudioStream {
+class AdLibDriver {
 public:
 	AdLibDriver(Audio::Mixer *mixer, int version);
 	~AdLibDriver();
@@ -70,16 +70,6 @@ public:
 
 	void callback();
 
-	// AudioStream API
-	int readBuffer(int16 *buffer, const int numSamples) {
-		return _adlib->readBuffer(buffer, numSamples);
-	}
-
-
-	bool isStereo() const { return false; }
-	bool endOfData() const { return false; }
-	int getRate() const { return _mixer->getOutputRate(); }
-
 	void setSyncJumpMask(uint16 mask) { _syncJumpMask = mask; }
 
 	void setMusicVolume(uint8 volume);
@@ -388,7 +378,6 @@ private:
 
 	Common::Mutex _mutex;
 	Audio::Mixer *_mixer;
-	Audio::SoundHandle _soundHandle;
 
 	uint8 _musicVolume, _sfxVolume;
 
@@ -440,11 +429,9 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
 	_retrySounds = false;
 
 	_adlib->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::callback), CALLBACKS_PER_SECOND);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibDriver::~AdLibDriver() {
-	_mixer->stopHandle(_soundHandle);
 	delete _adlib;
 	_adlib = 0;
 }
diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp
index 0a1c1ea..711f82a 100644
--- a/engines/mads/nebular/sound_nebular.cpp
+++ b/engines/mads/nebular/sound_nebular.cpp
@@ -213,16 +213,12 @@ ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filenam
 	command0();
 
 	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1,
-		Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 ASound::~ASound() {
 	Common::List<CachedDataEntry>::iterator i;
 	for (i = _dataCache.begin(); i != _dataCache.end(); ++i)
 		delete[] (*i)._data;
-
-	_mixer->stopHandle(_soundHandle);
 }
 
 void ASound::validate() {
@@ -828,20 +824,12 @@ void ASound::updateFNumber() {
 	write2(8, hiReg, val2);
 }
 
-int ASound::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void ASound::onTimer() {
 	Common::StackLock slock(_driverMutex);
 	poll();
 	flush();
 }
 
-int ASound::getRate() const {
-	return g_system->getMixer()->getOutputRate();
-}
-
 void ASound::setVolume(int volume) {
 	_masterVolume = volume;
 	if (!volume)
diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h
index 1267914..2b80b08 100644
--- a/engines/mads/nebular/sound_nebular.h
+++ b/engines/mads/nebular/sound_nebular.h
@@ -145,7 +145,7 @@ struct CachedDataEntry {
 /**
  * Base class for the sound player resource files
  */
-class ASound : public Audio::AudioStream {
+class ASound {
 private:
 	Common::List<CachedDataEntry> _dataCache;
 	uint16 _randomSeed;
@@ -282,7 +282,6 @@ protected:
 public:
 	Audio::Mixer *_mixer;
 	OPL::OPL *_opl;
-	Audio::SoundHandle _soundHandle;
 	AdlibChannel _channels[ADLIB_CHANNEL_COUNT];
 	AdlibChannel *_activeChannelPtr;
 	AdlibChannelData _channelData[11];
@@ -367,27 +366,6 @@ public:
 	 */
 	CachedDataEntry &getCachedData(byte *pData);
 
-	// AudioStream interface
-	/**
-	 * Main buffer read
-	 */
-	virtual int readBuffer(int16 *buffer, const int numSamples);
-
-	/**
-	 * Mono sound only
-	 */
-	virtual bool isStereo() const { return false; }
-
-	/**
-	 * Data is continuously pushed, so definitive end
-	 */
-	virtual bool endOfData() const { return false; }
-
-	/**
-	 * Return sample rate
-	 */
-	virtual int getRate() const;
-
 	/**
 	 * Set the volume
 	 */
diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp
index 302530b..568ad19 100644
--- a/engines/parallaction/adlib.cpp
+++ b/engines/parallaction/adlib.cpp
@@ -25,7 +25,7 @@
 
 #include "audio/fmopl.h"
 #include "audio/mpu401.h"
-#include "audio/softsynth/emumidi.h"
+#include "audio/mididrv.h"
 
 namespace Parallaction {
 
@@ -270,11 +270,13 @@ struct MelodicVoice {
 	int8 _octave;
 };
 
-class AdLibDriver : public MidiDriver_Emulated {
+class AdLibDriver : public MidiDriver {
 public:
-	AdLibDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) {
+	AdLibDriver(Audio::Mixer *mixer) {
 		for (uint i = 0; i < 16; ++i)
 			_channels[i].init(this, i);
+
+		_isOpen = false;
 	}
 
 	int open();
@@ -282,12 +284,9 @@ public:
 	void send(uint32 b);
 	MidiChannel *allocateChannel();
 	MidiChannel *getPercussionChannel() { return &_channels[9]; }
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
-	bool isStereo() const { return false; }
-	int getRate() const { return _mixer->getOutputRate(); }
-	int readBuffer(int16 *data, const int numSamples);
-
-	void generateSamples(int16 *buf, int len) {}
 	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
 		_adlibTimerProc = timerProc;
 		_adlibTimerParam = timerParam;
@@ -331,6 +330,7 @@ private:
 
 	Common::TimerManager::TimerProc _adlibTimerProc;
 	void *_adlibTimerParam;
+	bool _isOpen;
 };
 
 MidiDriver *createAdLibDriver() {
@@ -359,7 +359,7 @@ int AdLibDriver::open() {
 	if (_isOpen)
 		return MERR_ALREADY_OPEN;
 
-	MidiDriver_Emulated::open();
+	_isOpen = true;
 
 	_opl = OPL::Config::create();
 	_opl->init();
@@ -376,7 +376,6 @@ int AdLibDriver::open() {
 	initVoices();
 
 	_opl->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::onTimer));
-	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 	return 0;
 }
 
@@ -385,7 +384,6 @@ void AdLibDriver::close() {
 		return;
 
 	_isOpen = false;
-	_mixer->stopHandle(_mixerSoundHandle);
 
 	delete _opl;
 }
@@ -789,10 +787,6 @@ MidiChannel *AdLibDriver::allocateChannel() {
 	return NULL;
 }
 
-int AdLibDriver::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void AdLibDriver::onTimer() {
 	if (_adlibTimerProc)
 		(*_adlibTimerProc)(_adlibTimerParam);
diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp
index 457767a..f5bc0f4 100644
--- a/engines/queen/midiadlib.cpp
+++ b/engines/queen/midiadlib.cpp
@@ -28,7 +28,7 @@
 namespace Queen {
 
 int AdLibMidiDriver::open() {
-	MidiDriver_Emulated::open();
+	_isOpen = true;
 	_opl = OPL::Config::create();
 	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
@@ -41,12 +41,10 @@ int AdLibMidiDriver::open() {
 	}
 
 	_opl->start(new Common::Functor0Mem<void, AdLibMidiDriver>(this, &AdLibMidiDriver::onTimer));
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 	return 0;
 }
 
 void AdLibMidiDriver::close() {
-	_mixer->stopHandle(_mixerSoundHandle);
 	delete _opl;
 }
 
@@ -110,14 +108,6 @@ void AdLibMidiDriver::metaEvent(byte type, byte *data, uint16 length) {
 	warning("Unhandled meta event %d len %d", event, length);
 }
 
-void AdLibMidiDriver::generateSamples(int16 *data, int len) {
-	// Dummy implementation
-}
-
-int AdLibMidiDriver::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void AdLibMidiDriver::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
 	_adlibTimerProc = timerProc;
 	_adlibTimerParam = timerParam;
diff --git a/engines/queen/midiadlib.h b/engines/queen/midiadlib.h
index d559025..8692e51 100644
--- a/engines/queen/midiadlib.h
+++ b/engines/queen/midiadlib.h
@@ -22,14 +22,17 @@
 
 #include "audio/fmopl.h"
 #include "audio/mididrv.h"
-#include "audio/softsynth/emumidi.h"
 
 namespace Queen {
 
-class AdLibMidiDriver : public MidiDriver_Emulated {
+class AdLibMidiDriver : public MidiDriver {
 public:
 
-	AdLibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { _adlibWaveformSelect = 0; }
+	AdLibMidiDriver() {
+		_adlibWaveformSelect = 0;
+		_isOpen = false;
+	}
+
 	~AdLibMidiDriver() {}
 
 	// MidiDriver
@@ -40,14 +43,8 @@ public:
 	MidiChannel *allocateChannel() { return 0; }
 	MidiChannel *getPercussionChannel() { return 0; }
 	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
-
-	// AudioStream
-	int readBuffer(int16 *data, const int numSamples);
-	bool isStereo() const { return false; }
-	int getRate() const { return _mixer->getOutputRate(); }
-
-	// MidiDriver_Emulated
-	void generateSamples(int16 *buf, int len);
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
 	void setVolume(uint32 volume);
 
@@ -74,8 +71,8 @@ private:
 	void adlibTurnNoteOn(int channel, int note);
 	void adlibSetupChannelFromSequence(int channel, const uint8 *src, int fl);
 	void adlibSetupChannel(int channel, const uint16 *src, int fl);
-	void adlibSetChannelVolume(int channel, uint8 volume);
 	void adlibSetNoteVolume(int channel, int volume);
+	void adlibSetChannelVolume(int channel, uint8 volume);
 	void adlibSetupChannelHelper(int channel);
 	void adlibSetChannel0x40(int channel);
 	void adlibSetChannel0xC0(int channel);
@@ -106,6 +103,7 @@ private:
 	uint16 _adlibMetaSequenceData[28];
 	uint8 _adlibChannelsVolumeTable[11];
 
+	bool _isOpen;
 	Common::TimerManager::TimerProc _adlibTimerProc;
 	void *_adlibTimerParam;
 
diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp
index 600956a..9f74aab 100644
--- a/engines/queen/music.cpp
+++ b/engines/queen/music.cpp
@@ -68,7 +68,7 @@ MidiMusic::MidiMusic(QueenEngine *vm)
 //		if (READ_LE_UINT16(_musicData + 2) != infoOffset) {
 //			defaultAdLibVolume = _musicData[infoOffset];
 //		}
-		_driver = new AdLibMidiDriver(g_system->getMixer());
+		_driver = new AdLibMidiDriver();
 	} else {
 		_driver = MidiDriver::createMidi(dev);
 		if (_nativeMT32) {
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 7035435..aac8a0d 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -27,7 +27,7 @@
 #include "common/textconsole.h"
 
 #include "audio/fmopl.h"
-#include "audio/softsynth/emumidi.h"
+#include "audio/mididrv.h"
 
 #include "sci/resource.h"
 #include "sci/sound/drivers/mididriver.h"
@@ -43,30 +43,27 @@ namespace Sci {
 // FIXME: We don't seem to be sending the polyphony init data, so disable this for now
 #define ADLIB_DISABLE_VOICE_MAPPING
 
-class MidiDriver_AdLib : public MidiDriver_Emulated {
+class MidiDriver_AdLib : public MidiDriver {
 public:
 	enum {
 		kVoices = 9,
 		kRhythmKeys = 62
 	};
 
-	MidiDriver_AdLib(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { }
+	MidiDriver_AdLib(Audio::Mixer *mixer) :_playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0), _isOpen(false) { }
 	virtual ~MidiDriver_AdLib() { }
 
 	// MidiDriver
+	int open() { return -1; } // Dummy implementation (use openAdLib)
 	int openAdLib(bool isSCI0);
 	void close();
 	void send(uint32 b);
 	MidiChannel *allocateChannel() { return NULL; }
 	MidiChannel *getPercussionChannel() { return NULL; }
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
-	// AudioStream
-	int readBuffer(int16 *data, const int numSamples);
-	bool isStereo() const { return _stereo; }
-	int getRate() const { return _mixer->getOutputRate(); }
-
-	// MidiDriver_Emulated
-	void generateSamples(int16 *buf, int len);
+	// MidiDriver
 	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
 
 	void onTimer();
@@ -137,6 +134,7 @@ private:
 	bool _stereo;
 	bool _isSCI0;
 	OPL::OPL *_opl;
+	bool _isOpen;
 	bool _playSwitch;
 	int _masterVolume;
 	Channel _channels[MIDI_CHANNELS];
@@ -227,7 +225,7 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) {
 	debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1"));
 	_isSCI0 = isSCI0;
 
-	_opl = OPL::Config::create(isStereo() ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2);
+	_opl = OPL::Config::create(_stereo ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2);
 
 	// Try falling back to mono, thus plain OPL2 emualtor, when no Dual OPL2 is available.
 	if (!_opl && _stereo) {
@@ -244,17 +242,14 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) {
 	setRegister(0x08, 0);
 	setRegister(0x01, 0x20);
 
-	MidiDriver_Emulated::open();
+	_isOpen = true;
 
 	_opl->start(new Common::Functor0Mem<void, MidiDriver_AdLib>(this, &MidiDriver_AdLib::onTimer));
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
 
 	return 0;
 }
 
 void MidiDriver_AdLib::close() {
-	_mixer->stopHandle(_mixerSoundHandle);
-
 	delete _opl;
 	delete[] _rhythmKeyMap;
 }
@@ -331,14 +326,6 @@ void MidiDriver_AdLib::send(uint32 b) {
 	}
 }
 
-int MidiDriver_AdLib::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
-void MidiDriver_AdLib::generateSamples(int16 *data, int len) {
-	// Dummy implementation
-}
-
 void MidiDriver_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
 	_adlibTimerProc = timerProc;
 	_adlibTimerParam = timerParam;
@@ -702,7 +689,7 @@ void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLe
 	if (!_playSwitch)
 		velocity = 0;
 
-	if (isStereo()) {
+	if (_stereo) {
 		int velLeft = velocity;
 		int velRight = velocity;
 
@@ -752,7 +739,7 @@ void MidiDriver_AdLib::setRegister(int reg, int value, int channels) {
 		_opl->write(0x221, value);
 	}
 
-	if (isStereo()) {
+	if (_stereo) {
 		if (channels & kRightChannel) {
 			_opl->write(0x222, reg);
 			_opl->write(0x223, value);
diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp
index ea59a22..5c0d443 100644
--- a/engines/scumm/players/player_ad.cpp
+++ b/engines/scumm/players/player_ad.cpp
@@ -36,7 +36,7 @@ namespace Scumm {
 #define AD_CALLBACK_FREQUENCY 472
 
 Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer)
-	: _vm(scumm), _mixer(mixer), _rate(mixer->getOutputRate()) {
+	: _vm(scumm), _mixer(mixer) {
 	_opl2 = OPL::Config::create();
 	if (!_opl2->init()) {
 		error("Could not initialize OPL2 emulator");
@@ -73,12 +73,9 @@ Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer)
 	_isSeeking = false;
 
 	_opl2->start(new Common::Functor0Mem<void, Player_AD>(this, &Player_AD::onTimer), AD_CALLBACK_FREQUENCY);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 Player_AD::~Player_AD() {
-	_mixer->stopHandle(_soundHandle);
-
 	stopAllSounds();
 	Common::StackLock lock(_mutex);
 	delete _opl2;
@@ -250,10 +247,6 @@ void Player_AD::onTimer() {
 	updateSfx();
 }
 
-int Player_AD::readBuffer(int16 *buffer, const int numSamples) {
-	return _opl2->readBuffer(buffer, numSamples);
-}
-
 void Player_AD::setupVolume() {
 	// Setup the correct volume
 	_musicVolume = CLIP<int>(ConfMan.getInt("music_volume"), 0, Audio::Mixer::kMaxChannelVolume);
diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h
index 9662b08..b8cd8dc 100644
--- a/engines/scumm/players/player_ad.h
+++ b/engines/scumm/players/player_ad.h
@@ -41,7 +41,7 @@ class ScummEngine;
 /**
  * Sound output for v3/v4 AdLib data.
  */
-class Player_AD : public MusicEngine, public Audio::AudioStream {
+class Player_AD : public MusicEngine {
 public:
 	Player_AD(ScummEngine *scumm, Audio::Mixer *mixer);
 	virtual ~Player_AD();
@@ -56,12 +56,6 @@ public:
 
 	virtual void saveLoadWithSerializer(Serializer *ser);
 
-	// AudioStream API
-	virtual int readBuffer(int16 *buffer, const int numSamples);
-	virtual bool isStereo() const { return false; }
-	virtual bool endOfData() const { return false; }
-	virtual int getRate() const { return _rate; }
-
 	// Timer callback
 	void onTimer();
 
@@ -69,8 +63,6 @@ private:
 	ScummEngine *const _vm;
 	Common::Mutex _mutex;
 	Audio::Mixer *const _mixer;
-	const int _rate;
-	Audio::SoundHandle _soundHandle;
 
 	void setupVolume();
 	int _musicVolume;
diff --git a/engines/sherlock/scalpel/drivers/adlib.cpp b/engines/sherlock/scalpel/drivers/adlib.cpp
index 033b474..29a39f0 100644
--- a/engines/sherlock/scalpel/drivers/adlib.cpp
+++ b/engines/sherlock/scalpel/drivers/adlib.cpp
@@ -216,11 +216,11 @@ uint16 frequencyLookUpTable[SHERLOCK_ADLIB_NOTES_COUNT] = {
 	0x1DE6, 0x1E03, 0x1E22, 0x1E42, 0x1E65, 0x1E89
 };
 
-class MidiDriver_SH_AdLib : public MidiDriver_Emulated {
+class MidiDriver_SH_AdLib : public MidiDriver {
 public:
 	MidiDriver_SH_AdLib(Audio::Mixer *mixer)
-		: MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0),
-		  _adlibTimerProc(0), _adlibTimerParam(0) {
+		: _masterVolume(15), _opl(0),
+		  _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) {
 		memset(_voiceChannelMapping, 0, sizeof(_voiceChannelMapping));
 	}
 	virtual ~MidiDriver_SH_AdLib() { }
@@ -231,17 +231,12 @@ public:
 	void send(uint32 b);
 	MidiChannel *allocateChannel() { return NULL; }
 	MidiChannel *getPercussionChannel() { return NULL; }
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
-	// AudioStream
-	int readBuffer(int16 *data, const int numSamples);
-	bool isStereo() const { return false; }
-	int getRate() const { return _mixer->getOutputRate(); }
 	int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; }
 	bool hasRhythmChannel() const { return false; }
 
-	// MidiDriver_Emulated
-	void generateSamples(int16 *buf, int len);
-
 	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
 
 	void setVolume(byte volume);
@@ -268,6 +263,8 @@ private:
 	Common::TimerManager::TimerProc _adlibTimerProc;
 	void *_adlibTimerParam;
 
+	bool _isOpen;
+
 	// points to a MIDI channel for each of the new voice channels
 	byte _voiceChannelMapping[SHERLOCK_ADLIB_VOICES_COUNT];
 
@@ -299,16 +296,16 @@ int MidiDriver_SH_AdLib::open() {
 
 	_opl->init();
 
-	MidiDriver_Emulated::open();
+	_isOpen = true;
 
 	_opl->start(new Common::Functor0Mem<void, MidiDriver_SH_AdLib>(this, &MidiDriver_SH_AdLib::onTimer));
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
 
 	return 0;
 }
 
 void MidiDriver_SH_AdLib::close() {
-	_mixer->stopHandle(_mixerSoundHandle);
+	// Stop the OPL timer
+	_opl->stop();
 
 	delete _opl;
 }
@@ -427,14 +424,6 @@ void MidiDriver_SH_AdLib::send(uint32 b) {
 	}
 }
 
-void MidiDriver_SH_AdLib::generateSamples(int16 *data, int len) {
-	// Dummy implementation until we no longer inherit from MidiDriver_Emulated
-}
-
-int MidiDriver_SH_AdLib::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void MidiDriver_SH_AdLib::noteOn(byte MIDIchannel, byte note, byte velocity) {
 	int16  oldestInUseChannel = -1;
 	uint16 oldestInUseTimer   = 0;
diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp
index 3607dfb..be5e7b2 100644
--- a/engines/sky/music/adlibmusic.cpp
+++ b/engines/sky/music/adlibmusic.cpp
@@ -33,25 +33,18 @@ namespace Sky {
 
 AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pDisk) {
 	_driverFileBase = 60202;
-	_sampleRate = pMixer->getOutputRate();
 
 	_opl = OPL::Config::create();
 	if (!_opl || !_opl->init())
 		error("Failed to create OPL");
 
 	_opl->start(new Common::Functor0Mem<void, AdLibMusic>(this, &AdLibMusic::onTimer), 50);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdLibMusic::~AdLibMusic() {
-	_mixer->stopHandle(_soundHandle);
 	delete _opl;
 }
 
-int AdLibMusic::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void AdLibMusic::onTimer() {
 	if (_musicData != NULL)
 		pollMusic();
@@ -97,16 +90,4 @@ void AdLibMusic::setVolume(uint16 param) {
 		_channels[cnt]->updateVolume(_musicVolume);
 }
 
-bool AdLibMusic::isStereo() const {
-	return false;
-}
-
-bool AdLibMusic::endOfData() const {
-	return false;
-}
-
-int AdLibMusic::getRate() const {
-	return _sampleRate;
-}
-
 } // End of namespace Sky
diff --git a/engines/sky/music/adlibmusic.h b/engines/sky/music/adlibmusic.h
index fe2e5ac..7b51f2d 100644
--- a/engines/sky/music/adlibmusic.h
+++ b/engines/sky/music/adlibmusic.h
@@ -32,21 +32,16 @@ class OPL;
 
 namespace Sky {
 
-class AdLibMusic : public Audio::AudioStream, public MusicBase {
+class AdLibMusic : public MusicBase {
 public:
 	AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk);
 	~AdLibMusic();
 
 	// AudioStream API
-	int readBuffer(int16 *buffer, const int numSamples);
-	bool isStereo() const;
-	bool endOfData() const;
-	int getRate() const;
 	virtual void setVolume(uint16 param);
 
 private:
 	OPL::OPL *_opl;
-	Audio::SoundHandle _soundHandle;
 	uint8 *_initSequence;
 	uint32 _sampleRate;
 	virtual void setupPointers();
diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index ddfaa0a..0d3fb55 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -2743,7 +2743,6 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() {
 	_groupData._pData = &adlib_group_data[0];
 
 	_mixer = g_vm->_mixer;
-	_sampleRate = _mixer->getOutputRate();
 	_opl = OPL::Config::create();
 	assert(_opl);
 	_opl->init();
@@ -2767,12 +2766,10 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() {
 	}
 
 	_opl->start(new Common::Functor0Mem<void, AdlibSoundDriver>(this, &AdlibSoundDriver::onTimer), CALLBACKS_PER_SECOND);
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 }
 
 AdlibSoundDriver::~AdlibSoundDriver() {
 	DEALLOCATE(_patchData);
-	_mixer->stopHandle(_soundHandle);
 	delete _opl;
 }
 
@@ -3015,10 +3012,6 @@ void AdlibSoundDriver::setFrequency(int channel) {
 		((dataWord >> 8) & 3) | (var2 << 2));
 }
 
-int AdlibSoundDriver::readBuffer(int16 *data, const int numSamples) {
-	return _opl->readBuffer(data, numSamples);
-}
-
 void AdlibSoundDriver::onTimer() {
 	Common::StackLock slock1(SoundManager::sfManager()._serverDisabledMutex);
 	Common::StackLock slock2(SoundManager::sfManager()._serverSuspendedMutex);
diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h
index 7ea1e65..68755a4 100644
--- a/engines/tsage/sound.h
+++ b/engines/tsage/sound.h
@@ -449,13 +449,11 @@ public:
 
 #define ADLIB_CHANNEL_COUNT 9
 
-class AdlibSoundDriver: public SoundDriver, Audio::AudioStream {
+class AdlibSoundDriver: public SoundDriver {
 private:
 	GroupData _groupData;
 	Audio::Mixer *_mixer;
 	OPL::OPL *_opl;
-	Audio::SoundHandle _soundHandle;
-	int _sampleRate;
 	byte _portContents[256];
 	const byte *_patchData;
 	int _masterVolume;
@@ -494,12 +492,6 @@ public:
 	virtual void proc38(int channel, int cmd, int value);
 	virtual void setPitch(int channel, int pitchBlend);
 
-	// AudioStream interface
-	virtual int readBuffer(int16 *data, const int numSamples);
-	virtual bool isStereo() const { return false; }
-	virtual bool endOfData() const { return false; }
-	virtual int getRate() const { return _sampleRate; }
-
 private:
 	void onTimer();
 };


Commit: 4d5658511228328a9e56d32448f9b598ecda696c
    https://github.com/scummvm/scummvm/commit/4d5658511228328a9e56d32448f9b598ecda696c
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:47-04:00

Commit Message:
AUDIO: Add a class representing a real OPL

Changed paths:
    audio/fmopl.cpp
    audio/fmopl.h



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 4bc902e..b0b3273 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -29,6 +29,7 @@
 #include "common/config-manager.h"
 #include "common/system.h"
 #include "common/textconsole.h"
+#include "common/timer.h"
 #include "common/translation.h"
 
 namespace OPL {
@@ -185,6 +186,63 @@ void OPL::stop() {
 
 bool OPL::_hasInstance = false;
 
+RealOPL::RealOPL() : _baseFreq(0), _remainingTicks(0) {
+}
+
+RealOPL::~RealOPL() {
+	// Stop callbacks, just in case. If it's still playing at this
+	// point, there's probably a bigger issue, though. The subclass
+	// needs to call stop() or the pointer can still use be used in
+	// the mixer thread at the same time.
+	stop();
+}
+
+void RealOPL::setCallbackFrequency(int timerFrequency) {
+	stopCallbacks();
+	startCallbacks(timerFrequency);
+}
+
+void RealOPL::startCallbacks(int timerFrequency) {
+	_baseFreq = timerFrequency;
+	assert(_baseFreq > 0);
+
+	// We can't request more a timer faster than 100Hz. We'll handle this by calling
+	// the proc multiple times in onTimer() later on.
+	if (timerFrequency > kMaxFreq)
+		timerFrequency = kMaxFreq;
+
+	_remainingTicks = 0;
+	g_system->getTimerManager()->installTimerProc(timerProc, 1000000 / timerFrequency, this, "RealOPL");
+}
+
+void RealOPL::stopCallbacks() {
+	g_system->getTimerManager()->removeTimerProc(timerProc);
+	_baseFreq = 0;
+	_remainingTicks = 0;
+}
+
+void RealOPL::timerProc(void *refCon) {
+	static_cast<RealOPL *>(refCon)->onTimer();
+}
+
+void RealOPL::onTimer() {
+	uint callbacks = 1;
+
+	if (_baseFreq > kMaxFreq) {
+		// We run faster than our max, so run the callback multiple
+		// times to approximate the actual timer callback frequency.
+		uint totalTicks = _baseFreq + _remainingTicks;		
+		callbacks = totalTicks / kMaxFreq;
+		_remainingTicks = totalTicks % kMaxFreq;
+	}
+
+	// Call the callback multiple times. The if is on the inside of the
+	// loop in case the callback removes itself.
+	for (uint i = 0; i < callbacks; i++)
+		if (_callback && _callback->isValid())
+			(*_callback)();
+}
+
 EmulatedOPL::EmulatedOPL() :
 	_nextTick(0),
 	_samplesPerTick(0),
diff --git a/audio/fmopl.h b/audio/fmopl.h
index 091e0bd..ba0872d 100644
--- a/audio/fmopl.h
+++ b/audio/fmopl.h
@@ -106,8 +106,14 @@ private:
 	static const EmulatorDescription _drivers[];
 };
 
+/**
+ * The type of the OPL timer callback functor.
+ */
 typedef Common::Functor0<void> TimerCallback;
 
+/**
+ * A representation of a Yamaha OPL chip.
+ */
 class OPL {
 private:
 	static bool _hasInstance;
@@ -194,6 +200,43 @@ protected:
 	Common::ScopedPtr<TimerCallback> _callback;
 };
 
+/**
+ * An OPL that represents a real OPL, as opposed to an emulated one.
+ *
+ * This will use an actual timer instead of using one calculated from
+ * the number of samples in an AudioStream::readBuffer call.
+ */
+class RealOPL : public OPL {
+public:
+	RealOPL();
+	virtual ~RealOPL();
+
+	// OPL API
+	void setCallbackFrequency(int timerFrequency);
+
+protected:
+	// OPL API
+	void startCallbacks(int timerFrequency);
+	void stopCallbacks();
+
+private:
+	static void timerProc(void *refCon);
+	void onTimer();
+
+	uint _baseFreq;
+	uint _remainingTicks;
+
+	enum {
+		kMaxFreq = 100
+	};
+};
+
+/**
+ * An OPL that represents an emulated OPL.
+ *
+ * This will send callbacks based on the number of samples
+ * decoded in readBuffer().
+ */
 class EmulatedOPL : public OPL, protected Audio::AudioStream {
 public:
 	EmulatedOPL();


Commit: a45ff5a6a94e6ace8528ac6bb157f7045f43c3b4
    https://github.com/scummvm/scummvm/commit/a45ff5a6a94e6ace8528ac6bb157f7045f43c3b4
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:47-04:00

Commit Message:
CONFIGURE: Ensure the USE_ALSA define ends up in config.mk

Changed paths:
    configure



diff --git a/configure b/configure
index 78f147f..48dfdbd 100755
--- a/configure
+++ b/configure
@@ -3556,7 +3556,7 @@ if test "$_alsa" = yes ; then
 	LIBS="$LIBS $ALSA_LIBS -lasound"
 	INCLUDES="$INCLUDES $ALSA_CFLAGS"
 fi
-define_in_config_h_if_yes "$_alsa" 'USE_ALSA'
+define_in_config_if_yes "$_alsa" 'USE_ALSA'
 echo "$_alsa"
 
 #


Commit: 40820eebf561d1a7c35f07151d534253ad2a7ea5
    https://github.com/scummvm/scummvm/commit/40820eebf561d1a7c35f07151d534253ad2a7ea5
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:47-04:00

Commit Message:
AUDIO: Add experimental hardware OPL support using ALSA

Changed paths:
  A audio/softsynth/opl/alsa.cpp
    audio/fmopl.cpp
    audio/module.mk



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index b0b3273..ded4506 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -34,12 +34,21 @@
 
 namespace OPL {
 
+// Factory functions
+
+#ifdef USE_ALSA
+namespace ALSA {
+	OPL *create(Config::OplType type);
+} // End of namespace ALSA
+#endif // USE_ALSA
+
 // Config implementation
 
 enum OplEmulator {
 	kAuto = 0,
 	kMame = 1,
-	kDOSBox = 2
+	kDOSBox = 2,
+	kALSA = 3
 };
 
 OPL::OPL() {
@@ -54,6 +63,9 @@ const Config::EmulatorDescription Config::_drivers[] = {
 #ifndef DISABLE_DOSBOX_OPL
 	{ "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
 #endif
+#ifdef USE_ALSA
+	{ "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 },
+#endif
 	{ 0, 0, 0, 0 }
 };
 
@@ -166,6 +178,11 @@ OPL *Config::create(DriverId driver, OplType type) {
 		return new DOSBox::OPL(type);
 #endif
 
+#ifdef USE_ALSA
+	case kALSA:
+		return ALSA::create(type);
+#endif
+
 	default:
 		warning("Unsupported OPL emulator %d", driver);
 		// TODO: Maybe we should add some dummy emulator too, which just outputs
diff --git a/audio/module.mk b/audio/module.mk
index abbeed6..1539163 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -58,6 +58,11 @@ MODULE_OBJS := \
 	softsynth/sid.o \
 	softsynth/wave6581.o
 
+ifdef USE_ALSA
+MODULE_OBJS += \
+	softsynth/opl/alsa.o
+endif
+
 ifndef USE_ARM_SOUND_ASM
 MODULE_OBJS += \
 	rate.o
diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp
new file mode 100644
index 0000000..7ca433d
--- /dev/null
+++ b/audio/softsynth/opl/alsa.cpp
@@ -0,0 +1,339 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/* OPL implementation for hardware OPL using ALSA Direct FM API.
+ *
+ * Caveats and limitations:
+ * - Pretends to be a softsynth (emitting silence).
+ * - Dual OPL2 mode requires OPL3 hardware.
+ * - Every register write leads to a series of register writes on the hardware,
+ *   due to the lack of direct register access in the ALSA Direct FM API.
+ * - No timers
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/scummsys.h"
+
+#include "common/debug.h"
+#include "common/str.h"
+#include "audio/fmopl.h"
+
+#include <sys/ioctl.h>
+#include <alsa/asoundlib.h>
+#include <sound/asound_fm.h>
+
+namespace OPL {
+namespace ALSA {
+
+class OPL : public ::OPL::RealOPL {
+private:
+	enum {
+		kOpl2Voices = 9,
+		kVoices = 18,
+		kOpl2Operators = 18,
+		kOperators = 36
+	};
+
+	Config::OplType _type;
+	int _iface;
+	snd_hwdep_t *_opl;
+	snd_dm_fm_voice _oper[kOperators];
+	snd_dm_fm_note _voice[kVoices];
+	snd_dm_fm_params _params;
+	int index[2];
+	static const int voiceToOper0[kVoices];
+	static const int regOffsetToOper[0x20];
+
+	void writeOplReg(int c, int r, int v);
+	void clear();
+
+public:
+	OPL(Config::OplType type);
+	~OPL();
+
+	bool init();
+	void reset();
+
+	void write(int a, int v);
+	byte read(int a);
+
+	void writeReg(int r, int v);
+};
+
+const int OPL::voiceToOper0[OPL::kVoices] =
+	{ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
+
+const int OPL::regOffsetToOper[0x20] =
+	{ 0,  1,  2,  3,  4,  5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
+	 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
+}
+
+OPL::~OPL() {
+	stop();
+
+	if (_opl)
+		snd_hwdep_close(_opl);
+}
+
+void OPL::clear() {
+	index[0] = index[1] = 0;
+
+	memset(_oper, 0, sizeof(_oper));
+	memset(_voice, 0, sizeof(_voice));
+	memset(&_params, 0, sizeof(_params));
+
+	for (int i = 0; i < kOperators; ++i) {
+		_oper[i].op = (i / 3) % 2;
+		_oper[i].voice = (i / 6) * 3 + (i % 3);
+	}
+
+	for (int i = 0; i < kVoices; ++i)
+		_voice[i].voice = i;
+
+	// For OPL3 hardware we need to set up the panning in OPL2 modes
+	if (_iface == SND_HWDEP_IFACE_OPL3) {
+		if (_type == Config::kDualOpl2) {
+			for (int i = 0; i < kOpl2Operators; ++i)
+				_oper[i].left = 1; // FIXME below
+			for (int i = kOpl2Operators; i < kOperators; ++i)
+				_oper[i].right = 1;
+		} else if (_type == Config::kOpl2) {
+			for (int i = 0; i < kOpl2Operators; ++i) {
+				_oper[i].left = 1;
+				_oper[i].right = 1;			
+			}
+		}
+	}
+}
+
+bool OPL::init() {
+	clear();
+
+	int card = -1;
+	snd_ctl_t *ctl;
+	snd_hwdep_info_t *info;
+	snd_hwdep_info_alloca(&info);
+
+	int iface = SND_HWDEP_IFACE_OPL3;
+	if (_type == Config::kOpl2)
+		iface = SND_HWDEP_IFACE_OPL2;
+
+	// Look for OPL hwdep interface
+	while (!snd_card_next(&card) && card >= 0) {
+		int dev = -1;
+		Common::String name = Common::String::format("hw:%d", card);
+
+		if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
+			continue;
+
+		while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
+			name = Common::String::format("hw:%d,%d", card, dev);
+
+			if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) 
+				continue;
+
+			if (!snd_hwdep_info(_opl, info)) {
+				int found = snd_hwdep_info_get_iface(info);
+				// OPL3 can be used for (Dual) OPL2 mode
+				if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
+					snd_ctl_close(ctl);
+					_iface = found;
+					reset();
+					return true;
+				}
+			}
+
+			// Wrong interface, try next device
+			snd_hwdep_close(_opl);
+			_opl = nullptr;
+		}
+
+		snd_ctl_close(ctl);
+	}
+
+	return false;
+}
+
+void OPL::reset() {
+	snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
+	if (_iface != SND_HWDEP_IFACE_OPL2)
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
+	clear();
+}
+
+void OPL::write(int port, int val) {
+	val &= 0xff;
+	int chip = (port & 2) >> 1;
+
+	if (port & 1) {
+		switch(_type) {
+		case Config::kOpl2:
+			writeOplReg(0, index[0], val);
+			break;
+		case Config::kDualOpl2:
+			if (port & 8) {
+				writeOplReg(0, index[0], val);
+				writeOplReg(1, index[1], val);
+			} else
+				writeOplReg(chip, index[chip], val);
+			break;
+		case Config::kOpl3:
+			writeOplReg(chip, index[chip], val);
+		}
+	} else {
+		switch(_type) {
+		case Config::kOpl2:
+			index[0] = val;
+			break;
+		case Config::kDualOpl2:
+			if (port & 8) {
+				index[0] = val;
+				index[1] = val;
+			} else
+				index[chip] = val;
+			break;
+		case Config::kOpl3:
+			index[chip] = val;
+		}
+	}
+}
+
+byte OPL::read(int port) {
+	return 0;
+}
+
+void OPL::writeReg(int r, int v) {
+	switch (_type) {
+	case Config::kOpl2:
+		writeOplReg(0, r, v);
+		break;
+	case Config::kDualOpl2:
+		writeOplReg(0, r, v);
+		writeOplReg(1, r, v);
+		break;
+	case Config::kOpl3:
+		writeOplReg(r >= 100, r & 0xff, v);
+	}
+}
+
+void OPL::writeOplReg(int c, int r, int v) {
+	if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
+	} else if (r == 0x08 && c == 0) {
+		_params.kbd_split = (v >> 6) & 0x1;
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
+	} else if (r == 0xbd && c == 0) {
+		_params.hihat = v & 0x1;
+		_params.cymbal = (v >> 1) & 0x1;
+		_params.tomtom = (v >> 2) & 0x1;
+		_params.snare = (v >> 3) & 0x1;
+		_params.bass = (v >> 4) & 0x1;
+		_params.rhythm = (v >> 5) & 0x1;
+		_params.vib_depth = (v >> 6) & 0x1;
+		_params.am_depth = (v >> 7) & 0x1;
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
+	} else if (r < 0xa0 || r >= 0xe0) {
+		// Operator
+		int idx = regOffsetToOper[r & 0x1f];
+
+		if (idx == -1)
+			return;
+
+		if (c == 1)
+			idx += kOpl2Operators;
+
+		switch (r & 0xf0) {
+		case 0x20:
+		case 0x30:
+			_oper[idx].harmonic = v & 0xf;
+			_oper[idx].kbd_scale = (v >> 4) & 0x1;
+			_oper[idx].do_sustain = (v >> 5) & 0x1;
+			_oper[idx].vibrato = (v >> 6) & 0x1;
+			_oper[idx].am = (v >> 7) & 0x1;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0x40:
+		case 0x50:
+			_oper[idx].volume = ~v & 0x3f;
+			_oper[idx].scale_level = (v >> 6) & 0x3;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0x60:
+		case 0x70:
+			_oper[idx].decay = v & 0xf;
+			_oper[idx].attack = (v >> 4) & 0xf;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0x80:
+		case 0x90:
+			_oper[idx].release = v & 0xf;
+			_oper[idx].sustain = (v >> 4) & 0xf;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0xe0:
+		case 0xf0:
+			_oper[idx].waveform = v & 0x7;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+		}
+	} else {
+		// Voice
+		int idx = r & 0xf;
+
+		if (idx >= kOpl2Voices)
+			return;
+
+		if (c == 1)
+			idx += kOpl2Voices;
+
+		int opIdx = voiceToOper0[idx];
+
+		switch (r & 0xf0) {
+		case 0xa0:
+			_voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
+			break;
+		case 0xb0:
+			_voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
+			_voice[idx].octave = (v >> 2) & 0x7;
+			_voice[idx].key_on = (v >> 5) & 0x1;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
+			break;
+		case 0xc0:
+			_oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
+			_oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
+			if (_type == Config::kOpl3) {
+				_oper[opIdx].left = (v >> 4) & 0x1;
+				_oper[opIdx].right = (v >> 5) & 0x1;
+			}
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
+		}
+	}
+}
+
+OPL *create(Config::OplType type) {
+	return new OPL(type);
+}
+
+} // End of namespace ALSA
+} // End of namespace OPL


Commit: b630eca437a7800c14984bb65dc5cb5c5bc28597
    https://github.com/scummvm/scummvm/commit/b630eca437a7800c14984bb65dc5cb5c5bc28597
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:47-04:00

Commit Message:
AUDIO: Fix bug in ALSA AdLib driver

Thanks clone2727

Changed paths:
    audio/softsynth/opl/alsa.cpp



diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp
index 7ca433d..8fea0fd 100644
--- a/audio/softsynth/opl/alsa.cpp
+++ b/audio/softsynth/opl/alsa.cpp
@@ -233,7 +233,7 @@ void OPL::writeReg(int r, int v) {
 		writeOplReg(1, r, v);
 		break;
 	case Config::kOpl3:
-		writeOplReg(r >= 100, r & 0xff, v);
+		writeOplReg(r >= 0x100, r & 0xff, v);
 	}
 }
 


Commit: beed23c441b5b36e7081589a004c7ab9bb253829
    https://github.com/scummvm/scummvm/commit/beed23c441b5b36e7081589a004c7ab9bb253829
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:47-04:00

Commit Message:
AUDIO: List OPL3 support for ALSA AdLib driver

Thanks clone2727

Changed paths:
    audio/fmopl.cpp



diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index ded4506..cc00ace 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -64,7 +64,7 @@ const Config::EmulatorDescription Config::_drivers[] = {
 	{ "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
 #endif
 #ifdef USE_ALSA
-	{ "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 },
+	{ "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
 #endif
 	{ 0, 0, 0, 0 }
 };


Commit: 82f585871bdb435d6f30d7005456dba0fb90c985
    https://github.com/scummvm/scummvm/commit/82f585871bdb435d6f30d7005456dba0fb90c985
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:48-04:00

Commit Message:
SCI: Check OPL init return code

Changed paths:
    engines/sci/sound/drivers/adlib.cpp



diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index aac8a0d..da222fb 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -236,7 +236,8 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) {
 	if (!_opl)
 		return -1;
 
-	_opl->init();
+	if (!_opl->init())
+		return -1;
 
 	setRegister(0xBD, 0);
 	setRegister(0x08, 0);


Commit: 56c0238f9bc5f013cae0d487ef88dae3c29f6305
    https://github.com/scummvm/scummvm/commit/56c0238f9bc5f013cae0d487ef88dae3c29f6305
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:48-04:00

Commit Message:
SCI: Delete OPL when init fails

Changed paths:
    engines/sci/sound/drivers/adlib.cpp



diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index da222fb..4f557be 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -236,8 +236,11 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) {
 	if (!_opl)
 		return -1;
 
-	if (!_opl->init())
+	if (!_opl->init()) {
+		delete _opl;
+		_opl = nullptr;
 		return -1;
+	}
 
 	setRegister(0xBD, 0);
 	setRegister(0x08, 0);


Commit: be345083a03a110d9f6ebc9310152bf079dfab7e
    https://github.com/scummvm/scummvm/commit/be345083a03a110d9f6ebc9310152bf079dfab7e
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:48-04:00

Commit Message:
AUDIO: Update 2nd operator panning for AdLib register 0xc0

Changed paths:
    audio/softsynth/opl/alsa.cpp



diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp
index 8fea0fd..934fb34 100644
--- a/audio/softsynth/opl/alsa.cpp
+++ b/audio/softsynth/opl/alsa.cpp
@@ -323,8 +323,8 @@ void OPL::writeOplReg(int c, int r, int v) {
 			_oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
 			_oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
 			if (_type == Config::kOpl3) {
-				_oper[opIdx].left = (v >> 4) & 0x1;
-				_oper[opIdx].right = (v >> 5) & 0x1;
+				_oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
+				_oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
 			}
 			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
 		}


Commit: 1bdcf6e83647a9509598d49bae6ddec1fbd25715
    https://github.com/scummvm/scummvm/commit/1bdcf6e83647a9509598d49bae6ddec1fbd25715
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:48-04:00

Commit Message:
AUDIO: Reset ALSA AdLib on exit

Changed paths:
    audio/softsynth/opl/alsa.cpp



diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp
index 934fb34..bb15719 100644
--- a/audio/softsynth/opl/alsa.cpp
+++ b/audio/softsynth/opl/alsa.cpp
@@ -92,8 +92,10 @@ OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
 OPL::~OPL() {
 	stop();
 
-	if (_opl)
+	if (_opl) {
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
 		snd_hwdep_close(_opl);
+	}
 }
 
 void OPL::clear() {


Commit: 1287a56429d0cd85567daa130fc6518ee2030b70
    https://github.com/scummvm/scummvm/commit/1287a56429d0cd85567daa130fc6518ee2030b70
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:48-04:00

Commit Message:
AUDIO: Fix ALSA AdLib OPL2 waveform mask

Changed paths:
    audio/softsynth/opl/alsa.cpp



diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp
index bb15719..79ff589 100644
--- a/audio/softsynth/opl/alsa.cpp
+++ b/audio/softsynth/opl/alsa.cpp
@@ -295,7 +295,7 @@ void OPL::writeOplReg(int c, int r, int v) {
 			break;
 		case 0xe0:
 		case 0xf0:
-			_oper[idx].waveform = v & 0x7;
+			_oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
 			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
 		}
 	} else {


Commit: f0606aa8f131b72bf76e08a82f6deb5016d5d8bb
    https://github.com/scummvm/scummvm/commit/f0606aa8f131b72bf76e08a82f6deb5016d5d8bb
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2015-07-07T20:19:48-04:00

Commit Message:
AUDIO: Reset OPL registers in ALSA driver

Changed paths:
    audio/softsynth/opl/alsa.cpp



diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp
index 79ff589..6b9e48e 100644
--- a/audio/softsynth/opl/alsa.cpp
+++ b/audio/softsynth/opl/alsa.cpp
@@ -179,9 +179,17 @@ bool OPL::init() {
 
 void OPL::reset() {
 	snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
-	if (_iface != SND_HWDEP_IFACE_OPL2)
+	if (_iface == SND_HWDEP_IFACE_OPL3)
 		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
+
 	clear();
+
+	// Sync up with the hardware
+	snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
+	for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
+	for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
 }
 
 void OPL::write(int port, int val) {


Commit: fde8abf8ccea92efec16a83dc4fa6b872f9cc6d2
    https://github.com/scummvm/scummvm/commit/fde8abf8ccea92efec16a83dc4fa6b872f9cc6d2
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:19:49-04:00

Commit Message:
AUDIO: Move the common AdLib MidiDriver out of softsynth

Changed paths:
  A audio/adlib.cpp
  R audio/softsynth/adlib.cpp
    audio/module.mk



diff --git a/audio/adlib.cpp b/audio/adlib.cpp
new file mode 100644
index 0000000..f609164
--- /dev/null
+++ b/audio/adlib.cpp
@@ -0,0 +1,2318 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/softsynth/emumidi.h"
+#include "common/debug.h"
+#include "common/error.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+#include "common/types.h"
+#include "common/util.h"
+#include "audio/fmopl.h"
+#include "audio/musicplugin.h"
+#include "common/translation.h"
+
+#ifdef DEBUG_ADLIB
+static int g_tick;
+#endif
+
+// Only include OPL3 when we actually have an AdLib emulator builtin, which
+// supports OPL3.
+#ifndef DISABLE_DOSBOX_OPL
+#define ENABLE_OPL3
+#endif
+
+class MidiDriver_ADLIB;
+struct AdLibVoice;
+
+// We use packing for the following two structs, because the code
+// does simply copy them over from byte streams, without any
+// serialization. Check AdLibPart::sysEx_customInstrument for an
+// example of this.
+//
+// It might be very well possible, that none of the compilers we support
+// add any padding bytes at all, since the structs contain only variables
+// of the type 'byte'. But better safe than sorry.
+#include "common/pack-start.h"
+struct InstrumentExtra {
+	byte a, b, c, d, e, f, g, h;
+} PACKED_STRUCT;
+
+struct AdLibInstrument {
+	byte modCharacteristic;
+	byte modScalingOutputLevel;
+	byte modAttackDecay;
+	byte modSustainRelease;
+	byte modWaveformSelect;
+	byte carCharacteristic;
+	byte carScalingOutputLevel;
+	byte carAttackDecay;
+	byte carSustainRelease;
+	byte carWaveformSelect;
+	byte feedback;
+	byte flagsA;
+	InstrumentExtra extraA;
+	byte flagsB;
+	InstrumentExtra extraB;
+	byte duration;
+} PACKED_STRUCT;
+#include "common/pack-end.h"
+
+class AdLibPart : public MidiChannel {
+	friend class MidiDriver_ADLIB;
+
+protected:
+//	AdLibPart *_prev, *_next;
+	AdLibVoice *_voice;
+	int16 _pitchBend;
+	byte _pitchBendFactor;
+	//int8 _transposeEff;
+	byte _volEff;
+	int8 _detuneEff;
+	byte _modWheel;
+	bool _pedal;
+	byte _program;
+	byte _priEff;
+	byte _pan;
+	AdLibInstrument _partInstr;
+#ifdef ENABLE_OPL3
+	AdLibInstrument _partInstrSecondary;
+#endif
+
+protected:
+	MidiDriver_ADLIB *_owner;
+	bool _allocated;
+	byte _channel;
+
+	void init(MidiDriver_ADLIB *owner, byte channel);
+	void allocate() { _allocated = true; }
+
+public:
+	AdLibPart() {
+		_voice = 0;
+		_pitchBend = 0;
+		_pitchBendFactor = 2;
+		//_transposeEff = 0;
+		_volEff = 0;
+		_detuneEff = 0;
+		_modWheel = 0;
+		_pedal = 0;
+		_program = 0;
+		_priEff = 0;
+		_pan = 64;
+
+		_owner = 0;
+		_allocated = false;
+		_channel = 0;
+
+		memset(&_partInstr, 0, sizeof(_partInstr));
+#ifdef ENABLE_OPL3
+		memset(&_partInstrSecondary, 0, sizeof(_partInstrSecondary));
+#endif
+	}
+
+	MidiDriver *device();
+	byte getNumber() { return _channel; }
+	void release() { _allocated = false; }
+
+	void send(uint32 b);
+
+	// Regular messages
+	void noteOff(byte note);
+	void noteOn(byte note, byte velocity);
+	void programChange(byte program);
+	void pitchBend(int16 bend);
+
+	// Control Change messages
+	void controlChange(byte control, byte value);
+	void modulationWheel(byte value);
+	void volume(byte value);
+	void panPosition(byte value);
+	void pitchBendFactor(byte value);
+	void detune(byte value);
+	void priority(byte value);
+	void sustain(bool value);
+	void effectLevel(byte value) { return; } // Not supported
+	void chorusLevel(byte value) { return; } // Not supported
+	void allNotesOff();
+
+	// SysEx messages
+	void sysEx_customInstrument(uint32 type, const byte *instr);
+};
+
+// FYI (Jamieson630)
+// It is assumed that any invocation to AdLibPercussionChannel
+// will be done through the MidiChannel base class as opposed to the
+// AdLibPart base class. If this were NOT the case, all the functions
+// listed below would need to be virtual in AdLibPart as well as MidiChannel.
+class AdLibPercussionChannel : public AdLibPart {
+	friend class MidiDriver_ADLIB;
+
+protected:
+	void init(MidiDriver_ADLIB *owner, byte channel);
+
+public:
+	~AdLibPercussionChannel();
+
+	void noteOff(byte note);
+	void noteOn(byte note, byte velocity);
+	void programChange(byte program) { }
+
+	// Control Change messages
+	void modulationWheel(byte value) { }
+	void pitchBendFactor(byte value) { }
+	void detune(byte value) { }
+	void priority(byte value) { }
+	void sustain(bool value) { }
+
+	// SysEx messages
+	void sysEx_customInstrument(uint32 type, const byte *instr);
+
+private:
+	byte _notes[256];
+	AdLibInstrument *_customInstruments[256];
+};
+
+struct Struct10 {
+	byte active;
+	int16 curVal;
+	int16 count;
+	uint16 maxValue;
+	int16 startValue;
+	byte loop;
+	byte tableA[4];
+	byte tableB[4];
+	int8 unk3;
+	int8 modWheel;
+	int8 modWheelLast;
+	uint16 speedLoMax;
+	uint16 numSteps;
+	int16 speedHi;
+	int8 direction;
+	uint16 speedLo;
+	uint16 speedLoCounter;
+};
+
+struct Struct11 {
+	int16 modifyVal;
+	byte param, flag0x40, flag0x10;
+	Struct10 *s10;
+};
+
+struct AdLibVoice {
+	AdLibPart *_part;
+	AdLibVoice *_next, *_prev;
+	byte _waitForPedal;
+	byte _note;
+	byte _channel;
+	byte _twoChan;
+	byte _vol1, _vol2;
+	int16 _duration;
+
+	Struct10 _s10a;
+	Struct11 _s11a;
+	Struct10 _s10b;
+	Struct11 _s11b;
+
+#ifdef ENABLE_OPL3
+	byte _secTwoChan;
+	byte _secVol1, _secVol2;
+#endif
+
+	AdLibVoice() { memset(this, 0, sizeof(AdLibVoice)); }
+};
+
+struct AdLibSetParams {
+	byte registerBase;
+	byte shift;
+	byte mask;
+	byte inversion;
+};
+
+static const byte g_operator1Offsets[9] = {
+	0, 1, 2, 8,
+	9, 10, 16, 17,
+	18
+};
+
+static const byte g_operator2Offsets[9] = {
+	3, 4, 5, 11,
+	12, 13, 19, 20,
+	21
+};
+
+static const AdLibSetParams g_setParamTable[] = {
+	{0x40, 0, 63, 63},  // level
+	{0xE0, 2, 0, 0},    // unused
+	{0x40, 6, 192, 0},  // level key scaling
+	{0x20, 0, 15, 0},   // modulator frequency multiple
+	{0x60, 4, 240, 15}, // attack rate
+	{0x60, 0, 15, 15},  // decay rate
+	{0x80, 4, 240, 15}, // sustain level
+	{0x80, 0, 15, 15},  // release rate
+	{0xE0, 0, 3, 0},    // waveformSelect select
+	{0x20, 7, 128, 0},  // amp mod
+	{0x20, 6, 64, 0},   // vib
+	{0x20, 5, 32, 0},   // eg typ
+	{0x20, 4, 16, 0},   // ksr
+	{0xC0, 0, 1, 0},    // decay alg
+	{0xC0, 1, 14, 0}    // feedback
+};
+
+static const byte g_paramTable1[16] = {
+	29, 28, 27, 0,
+	3, 4, 7, 8,
+	13, 16, 17, 20,
+	21, 30, 31, 0
+};
+
+static const uint16 g_maxValTable[16] = {
+	0x2FF, 0x1F, 0x7, 0x3F,
+	0x0F, 0x0F, 0x0F, 0x3,
+	0x3F, 0x0F, 0x0F, 0x0F,
+	0x3, 0x3E, 0x1F, 0
+};
+
+static const uint16 g_numStepsTable[] = {
+	1, 2, 4, 5,
+	6, 7, 8, 9,
+	10, 12, 14, 16,
+	18, 21, 24, 30,
+	36, 50, 64, 82,
+	100, 136, 160, 192,
+	240, 276, 340, 460,
+	600, 860, 1200, 1600
+};
+
+static const byte g_noteFrequencies[] = {
+	90, 91, 92, 92, 93, 94, 94, 95,
+	96, 96, 97, 98, 98, 99, 100, 101,
+	101, 102, 103, 104, 104, 105, 106, 107,
+	107, 108, 109, 110, 111, 111, 112, 113,
+	114, 115, 115, 116, 117, 118, 119, 120,
+	121, 121, 122, 123, 124, 125, 126, 127,
+	128, 129, 130, 131, 132, 132, 133, 134,
+	135, 136, 137, 138, 139, 140, 141, 142,
+	143, 145, 146, 147, 148, 149, 150, 151,
+	152, 153, 154, 155, 157, 158, 159, 160,
+	161, 162, 163, 165, 166, 167, 168, 169,
+	171, 172, 173, 174, 176, 177, 178, 180,
+	181, 182, 184, 185, 186, 188, 189, 190,
+	192, 193, 194, 196, 197, 199, 200, 202,
+	203, 205, 206, 208, 209, 211, 212, 214,
+	215, 217, 218, 220, 222, 223, 225, 226,
+	228, 230, 231, 233, 235, 236, 238, 240,
+	242, 243, 245, 247, 249, 251, 252, 254
+};
+
+static const AdLibInstrument g_gmInstruments[128] = {
+	// 0x00
+	{ 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
+	{ 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
+	{ 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A },
+	{ 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	// 0x10
+	{ 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
+	{ 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
+	{ 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 },
+	{ 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A },
+	{ 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B },
+	// 0x20
+	{ 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 },
+	{ 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
+	{ 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
+	{ 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	{ 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	// 0x30
+	{ 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	{ 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	// 0x40
+	{ 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	// 0x50
+	{ 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	// 0x60
+	{ 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C },
+	{ 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 },
+	{ 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
+	{ 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	{ 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
+	{ 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	{ 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	// 0x70
+	{ 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	{ 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	{ 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	{ 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	{ 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F },
+	{ 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	{ 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	{ 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 },
+	{ 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	{ 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	{ 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }
+};
+
+static AdLibInstrument g_gmPercussionInstruments[39] = {
+	{ 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	{ 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	{ 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 },
+	{ 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
+	{ 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
+	{ 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
+	{ 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	{ 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	{ 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
+	{ 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	{ 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	{ 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	{ 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	{ 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	{ 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
+	{ 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
+	{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
+	{ 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	{ 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	{ 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	{ 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }
+};
+
+#ifdef ENABLE_OPL3
+static const AdLibInstrument g_gmInstrumentsOPL3[128][2] = {
+	{ { 0xC2, 0xC2, 0x0A, 0x6B, 0xA0, 0xC2, 0x08, 0x0D, 0x88, 0xC8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
+	  { 0x02, 0x00, 0x0C, 0x78, 0x61, 0x04, 0x4C, 0x0B, 0x9A, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 } },
+	{ { 0x22, 0x53, 0x0E, 0x8A, 0x60, 0x14, 0x06, 0x1D, 0x7A, 0xB8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x22, 0x5A, 0x0E, 0x8A, 0x40, 0x14, 0x2F, 0x0E, 0x7A, 0x88, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x06, 0x00, 0x1C, 0x79, 0x70, 0x02, 0x00, 0x4B, 0x79, 0xA8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x06, 0x00, 0x1A, 0x79, 0x60, 0x02, 0x00, 0x4C, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC2, 0x80, 0x0B, 0x89, 0x90, 0xC2, 0x06, 0x1B, 0xA8, 0xB0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
+	  { 0x04, 0x28, 0x5D, 0xB8, 0x01, 0x02, 0x00, 0x3C, 0x70, 0x88, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0xD3, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x87, 0x40, 0x3A, 0x5A, 0x94, 0x82, 0x04, 0x3D, 0x59, 0xAC, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x8C, 0x80, 0x05, 0xEA, 0xA9, 0x82, 0x04, 0x3D, 0xAA, 0xB0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x8C, 0x80, 0x06, 0x98, 0xA9, 0x86, 0x10, 0x36, 0x7A, 0xFD, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x85, 0x40, 0x0D, 0xEC, 0xE1, 0x84, 0x58, 0x3E, 0xCB, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x84, 0x40, 0x0D, 0xEB, 0xE0, 0x84, 0x48, 0x3E, 0xCA, 0xC0, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC9, 0x40, 0x2B, 0x78, 0x8A, 0xC2, 0x0A, 0x4C, 0x8A, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A },
+	  { 0xCA, 0x40, 0x47, 0xCA, 0xB4, 0xC2, 0x00, 0x57, 0x8A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A } },
+	{ { 0x2A, 0x0E, 0x17, 0x89, 0x50, 0x22, 0x0C, 0x1B, 0x09, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x2A, 0x1A, 0x19, 0x8A, 0x00, 0x22, 0x38, 0x0B, 0x0A, 0x00, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x06, 0x0A, 0x08, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x2F, 0x0A, 0x08, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC5, 0x0A, 0x05, 0xDC, 0xB8, 0x84, 0x06, 0x00, 0xEC, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x09, 0x10, 0x04, 0x5B, 0xA5, 0x02, 0x08, 0x00, 0xEC, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xED, 0x0F, 0x5B, 0xC8, 0xC8, 0xE2, 0x9F, 0x4A, 0xE9, 0xF9, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE6, 0x40, 0x0A, 0xA7, 0x64, 0xE2, 0x8B, 0x6A, 0x79, 0xB1, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE8, 0x4F, 0x3A, 0xD7, 0xF8, 0xE2, 0x97, 0x49, 0xF9, 0xF9, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC9, 0x02, 0x16, 0x9A, 0xAB, 0xC4, 0x15, 0x46, 0xBA, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE1, 0x08, 0x2F, 0xF7, 0xE1, 0xF3, 0x42, 0x8F, 0xC7, 0xC2, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE3, 0x00, 0x2D, 0xF7, 0xC1, 0xE4, 0x40, 0x7F, 0xC7, 0xD2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x01, 0x8C, 0x9F, 0xDA, 0xE8, 0xE4, 0x50, 0x9F, 0xDA, 0xF2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x02, 0x80, 0x9F, 0xDA, 0x00, 0xE3, 0x50, 0x9F, 0xD9, 0xFA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE5, 0x0F, 0x7D, 0xB8, 0x5A, 0xA2, 0x0C, 0x7C, 0xC7, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x06, 0x4C, 0xAC, 0x56, 0x31, 0x02, 0x08, 0x8D, 0x46, 0xDC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0xC2, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xF2, 0x00, 0x9F, 0xDB, 0xA9, 0xE1, 0x00, 0x8F, 0xD7, 0xBA, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x88, 0x9C, 0x50, 0xC8, 0xE2, 0x18, 0x70, 0xC4, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE6, 0x00, 0x9C, 0x50, 0xB0, 0xE4, 0x00, 0x70, 0xC4, 0xA0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
+	  { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } },
+	{ { 0x42, 0x53, 0x3E, 0xEB, 0x48, 0xD4, 0x05, 0x1D, 0xA9, 0xC9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
+	  { 0x42, 0x54, 0x6F, 0xEB, 0x61, 0xD4, 0x02, 0x2E, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } },
+	{ { 0xC2, 0x00, 0x59, 0x17, 0xB1, 0xC2, 0x1E, 0x6D, 0x98, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 },
+	  { 0xC2, 0x00, 0x08, 0xB3, 0x99, 0xC2, 0x06, 0x2B, 0x58, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 } },
+	{ { 0xC6, 0x01, 0x2D, 0xA7, 0x88, 0xC2, 0x08, 0x0E, 0xA7, 0xC1, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC4, 0x00, 0x2D, 0xA7, 0x91, 0xC2, 0x02, 0x0E, 0xA7, 0xD1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC2, 0x0C, 0x06, 0x06, 0xA9, 0xC2, 0x3F, 0x08, 0xB8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A },
+	  { 0xC1, 0x00, 0x68, 0x50, 0xB8, 0xC2, 0x00, 0x48, 0x84, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A } },
+	{ { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0xB1, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC2, 0x2F, 0x6F, 0x79, 0x00, 0xC8, 0x0F, 0x5E, 0x98, 0xB9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B },
+	  { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B } },
+	{ { 0xC2, 0x41, 0x3D, 0x96, 0x88, 0xC4, 0xCA, 0x0E, 0xC7, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 },
+	  { 0xC2, 0x04, 0x58, 0xC9, 0x90, 0xC2, 0x94, 0x2C, 0xB9, 0xF0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 } },
+	{ { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
+	  { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } },
+	{ { 0xF2, 0x0A, 0x0D, 0xD7, 0x80, 0xE4, 0x1F, 0x5E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xD2, 0x06, 0x9A, 0xD7, 0xA0, 0xC2, 0x1F, 0x59, 0xB8, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
+	  { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } },
+	{ { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE1, 0x00, 0x4E, 0xDB, 0x92, 0xE3, 0x18, 0x6F, 0xE9, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x6F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x02, 0x0F, 0x66, 0xAA, 0xA1, 0x02, 0x64, 0x29, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	  { 0x02, 0x00, 0x65, 0xAA, 0xF1, 0x02, 0x4A, 0x28, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
+	{ { 0x16, 0x4A, 0x04, 0xBA, 0x71, 0xC2, 0x48, 0x2E, 0xCA, 0xF0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x14, 0xC0, 0x66, 0x08, 0x90, 0xC2, 0x48, 0x2C, 0x0A, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0x02, 0x0A, 0x01, 0x7A, 0xB1, 0x02, 0x12, 0x2A, 0xEA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x02, 0x06, 0x75, 0x05, 0xB1, 0x01, 0x3F, 0x28, 0xEA, 0xF9, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x16 } },
+	{ { 0x62, 0x53, 0x9C, 0xBA, 0x61, 0x62, 0x5A, 0xAD, 0xCA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xF2, 0x40, 0x9F, 0x8A, 0x98, 0xE2, 0x11, 0x7F, 0xB8, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE6, 0x80, 0x9C, 0x99, 0x82, 0xE2, 0x04, 0x8D, 0x78, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE0, 0x44, 0x8A, 0xA9, 0x5B, 0xE1, 0x06, 0x8D, 0x79, 0xBA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE8, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x06, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	  { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
+	{ { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xF2, 0x02, 0xAF, 0xFB, 0x90, 0xF6, 0x54, 0x8F, 0xE9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x00, 0x9F, 0xFA, 0xB0, 0xF2, 0x58, 0x7F, 0xEA, 0xF8, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xF2, 0x00, 0xAF, 0x88, 0xA8, 0xF2, 0x46, 0x6E, 0xC9, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xD2, 0x00, 0x7B, 0x88, 0xA8, 0xD2, 0x4C, 0x69, 0xE9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x8F, 0x4E, 0x78, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xD2, 0x02, 0x85, 0x89, 0xC8, 0xD2, 0x94, 0x77, 0x49, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x02, 0x9F, 0xB8, 0x90, 0x22, 0x8A, 0x9F, 0xE8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC2, 0x00, 0x86, 0xB8, 0x98, 0x02, 0x8F, 0x89, 0xE8, 0xF9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x1A, 0x90, 0xF8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xD2, 0x10, 0x69, 0x18, 0xCF, 0xD4, 0x14, 0x5B, 0x04, 0xFD, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x00, 0x5F, 0xF9, 0x88, 0xE4, 0x9E, 0x8F, 0xF8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC2, 0x00, 0x97, 0xF9, 0x90, 0xC9, 0x80, 0x69, 0x98, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x62, 0x0C, 0x6E, 0xD8, 0x79, 0x2A, 0x09, 0x7D, 0xD8, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x02, 0x04, 0x8A, 0xD8, 0x80, 0x0C, 0x12, 0x85, 0xD8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x00, 0x7E, 0x89, 0x70, 0xE6, 0x8F, 0x80, 0xF8, 0xF0, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC4, 0x00, 0x67, 0x59, 0x70, 0xC6, 0x8A, 0x77, 0xA8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x80, 0x88, 0x48, 0x98, 0xE2, 0x1E, 0x8E, 0xC9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xF2, 0x40, 0xA8, 0xB9, 0x80, 0xE2, 0x0C, 0x89, 0x09, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x80, 0x86, 0xB9, 0xA8, 0xE2, 0x14, 0x9F, 0xD7, 0xB0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC2, 0x80, 0x94, 0x09, 0x78, 0xC2, 0x00, 0x97, 0x97, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x00, 0x68, 0x68, 0xAA, 0xE2, 0x0A, 0x9B, 0xB3, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC2, 0x00, 0x86, 0x68, 0xA0, 0xC2, 0x00, 0x77, 0x47, 0xE0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x00, 0xA6, 0x87, 0x81, 0xE2, 0x0A, 0x7E, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x00, 0x89, 0x40, 0x79, 0xE2, 0x00, 0x7E, 0xC9, 0x90, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x80, 0xAA, 0xB8, 0x90, 0xE2, 0x00, 0x9E, 0xF9, 0xC0, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE6, 0x80, 0x9D, 0xB8, 0x51, 0xE2, 0x00, 0x9E, 0xF9, 0xA0, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE6, 0x00, 0x6F, 0xDA, 0xC9, 0xE2, 0x05, 0x2F, 0xD8, 0xAA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x00, 0x4F, 0xDA, 0xC8, 0xE2, 0x00, 0x0F, 0xD8, 0xD0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xEB, 0x80, 0x8C, 0xCB, 0x72, 0xE2, 0x86, 0xAF, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xEB, 0xC3, 0x9C, 0xCB, 0xA2, 0xE2, 0x4C, 0xAE, 0xCA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE5, 0x40, 0xDB, 0x3B, 0x78, 0xE2, 0x80, 0xBE, 0xCA, 0xE1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x80, 0x8E, 0xCB, 0xC0, 0xE2, 0x90, 0xAE, 0xCA, 0xFB, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE7, 0x40, 0xEB, 0xCA, 0x80, 0xE2, 0x03, 0xBF, 0xBA, 0xC2, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE3, 0x80, 0xDB, 0xCA, 0x40, 0xE2, 0x08, 0xDF, 0xBA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x61, 0x00, 0xBE, 0x99, 0xFA, 0xE3, 0x40, 0xCF, 0xCA, 0xF9, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x62, 0x00, 0xCE, 0x9A, 0xA8, 0xE2, 0x45, 0xCF, 0xCA, 0xA0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C },
+	  { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C } },
+	{ { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xEC, 0x00, 0x7D, 0xDA, 0x80, 0xE2, 0x00, 0x5E, 0x9B, 0xA8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE6, 0x0A, 0x4C, 0xC9, 0x60, 0xE2, 0x07, 0x0C, 0x7A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE9, 0xC0, 0xEE, 0xD8, 0x83, 0xE2, 0x05, 0xDD, 0xAA, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xED, 0x48, 0xDE, 0xD8, 0xB4, 0xE1, 0x00, 0xDD, 0xAA, 0xA9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xDA, 0x00, 0x8F, 0xAC, 0x92, 0x22, 0x05, 0x8D, 0x8A, 0xE9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xEF, 0x00, 0x8C, 0xAA, 0x67, 0x25, 0x00, 0x9D, 0xAB, 0xC1, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x62, 0x82, 0xCB, 0x7A, 0xD8, 0xE6, 0x56, 0xAF, 0xDB, 0xE0, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x62, 0x84, 0xBB, 0xAA, 0xCA, 0xCF, 0x41, 0xAC, 0xDA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xC2, 0x41, 0xAC, 0xBB, 0xBB, 0xC2, 0x85, 0x0E, 0xCB, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 },
+	  { 0xC2, 0x03, 0x6A, 0x5B, 0xA4, 0xC2, 0x0D, 0x2A, 0xBB, 0xFC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 } },
+	{ { 0x75, 0x00, 0x0E, 0xBB, 0xB2, 0xE2, 0x1E, 0x0A, 0xA9, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
+	  { 0x62, 0x00, 0x04, 0x9A, 0xE8, 0xE2, 0x00, 0x0A, 0x48, 0xFD, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } },
+	{ { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	  { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
+	{ { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
+	  { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } },
+	{ { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	  { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
+	{ { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0xC9, 0x0C, 0x88, 0xD9, 0x6A, 0xC2, 0x14, 0x3A, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0xC5, 0x00, 0x98, 0xD9, 0x92, 0xC1, 0x16, 0x6E, 0xF9, 0xE8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
+	{ { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x01, 0x0C, 0x44, 0xE6, 0xE8, 0x01, 0x3F, 0x0C, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	  { 0x02, 0x3F, 0x05, 0x08, 0xF8, 0x03, 0x3F, 0x3C, 0xF9, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
+	{ { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
+	{ { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
+	{ { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F },
+	  { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F } },
+	{ { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	  { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
+	{ { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	  { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
+	{ { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 },
+	  { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 } },
+	{ { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	  { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
+	{ { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
+	{ { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }
+};
+
+static const AdLibInstrument g_gmPercussionInstrumentsOPL3[39][2] = {
+	{ { 0x1A, 0x3F, 0x15, 0x05, 0xF8, 0x02, 0x21, 0x2B, 0xE4, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	  { 0x11, 0x18, 0x15, 0x00, 0xF8, 0x12, 0x00, 0x2B, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
+	{ { 0x11, 0x12, 0x04, 0x07, 0xF8, 0x02, 0x18, 0x0B, 0xE5, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0x11, 0x28, 0x06, 0x04, 0xF8, 0x02, 0x1E, 0x1B, 0x02, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
+	{ { 0x0A, 0x3F, 0x0B, 0x01, 0xF8, 0x1F, 0x13, 0x46, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 },
+	  { 0x04, 0x18, 0x06, 0x01, 0xB0, 0x10, 0x00, 0x07, 0x00, 0x90, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 } },
+	{ { 0x00, 0x3F, 0x0F, 0x00, 0xF8, 0x10, 0x0A, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x02, 0x14, 0x04, 0x00, 0xC0, 0x11, 0x08, 0x07, 0x00, 0xC6, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x0F, 0x3F, 0x0B, 0x00, 0xF8, 0x1F, 0x07, 0x19, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x0E, 0x32, 0x76, 0x03, 0xF8, 0x1F, 0x0F, 0x77, 0xD4, 0xFC, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x00, 0x3F, 0x1F, 0x00, 0xFA, 0x1F, 0x0C, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x07, 0x11, 0x13, 0x00, 0xA0, 0x13, 0x00, 0x07, 0x00, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x4A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x02, 0x22, 0x05, 0xB6, 0xF8, 0x04, 0x0A, 0x59, 0x03, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0xCF, 0x7F, 0x08, 0xFF, 0xFA, 0x00, 0xC0, 0x2D, 0xF7, 0xE3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0xD2, 0x7F, 0x04, 0x0F, 0xFA, 0x10, 0xCD, 0x24, 0x07, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x43, 0x17, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x12, 0x13, 0x09, 0x96, 0xF8, 0x44, 0x0A, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0xCF, 0x7F, 0x08, 0xCF, 0xFA, 0x00, 0x40, 0x2A, 0xF8, 0x8B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
+	  { 0xCF, 0x7F, 0x05, 0x07, 0xFA, 0x00, 0x40, 0x25, 0x08, 0xC3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } },
+	{ { 0x12, 0x3F, 0x06, 0x17, 0xF8, 0x03, 0x1D, 0x0B, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x12, 0x1A, 0x08, 0x96, 0xF8, 0x44, 0x00, 0x08, 0x03, 0xF8, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0xCF, 0x7F, 0x08, 0xCD, 0xFA, 0x00, 0x40, 0x1A, 0x69, 0xB3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
+	  { 0xCD, 0x3F, 0x36, 0x05, 0xFC, 0x0F, 0x47, 0x46, 0x06, 0xDF, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } },
+	{ { 0x13, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x0D, 0x0A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x12, 0x14, 0x09, 0x96, 0xF8, 0x44, 0x02, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0x15, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x12, 0x00, 0x07, 0x96, 0xE8, 0x44, 0x02, 0x08, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x16, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
+	  { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x1E, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } },
+	{ { 0x17, 0x3F, 0x04, 0x09, 0xF8, 0x03, 0x18, 0x0D, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x12, 0x00, 0x07, 0x96, 0xF8, 0x44, 0x02, 0x08, 0xF9, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0xCF, 0x3F, 0x0F, 0x5E, 0xF8, 0xC6, 0x0C, 0x00, 0xCA, 0xFB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0xCF, 0x3F, 0x04, 0x57, 0xF8, 0xC5, 0x13, 0x06, 0x05, 0xFF, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0xCF, 0x3F, 0x7E, 0x9D, 0xF8, 0xC8, 0xC0, 0x0A, 0xBA, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
+	  { 0xCF, 0x3F, 0x77, 0x09, 0xF8, 0xC2, 0xC0, 0x08, 0xB5, 0xEA, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
+	{ { 0xCF, 0x3F, 0x4D, 0x9F, 0xF8, 0xC6, 0x00, 0x08, 0xDA, 0xAB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	  { 0xCF, 0x3F, 0x47, 0x06, 0xF8, 0xCD, 0x00, 0x07, 0x05, 0xB3, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
+	{ { 0xCF, 0x3F, 0x5D, 0xAA, 0xF2, 0xC0, 0x8A, 0x67, 0x99, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0xCF, 0x3F, 0x9A, 0x69, 0xF8, 0xCF, 0x88, 0x88, 0x48, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0xCF, 0x3F, 0x4A, 0xFD, 0xF8, 0xCF, 0x00, 0x59, 0xEA, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0xCF, 0x3F, 0x48, 0x06, 0xF8, 0xCF, 0x00, 0x54, 0x04, 0xF9, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x0F, 0x18, 0x0A, 0xFA, 0xAB, 0x06, 0x06, 0x06, 0x39, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x03, 0x18, 0x04, 0x09, 0xAC, 0x05, 0x07, 0x08, 0x07, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0xCF, 0x3F, 0x2B, 0xFC, 0xF8, 0xCC, 0xC4, 0x0B, 0xEA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
+	  { 0xCF, 0x3F, 0x25, 0x06, 0xF8, 0xCC, 0xD7, 0x05, 0x02, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } },
+	{ { 0x05, 0x1A, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x0C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	  { 0x01, 0x00, 0x09, 0x08, 0x40, 0x13, 0x00, 0x2A, 0x0A, 0xD8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
+	{ { 0x04, 0x19, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x2C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
+	  { 0x04, 0x00, 0x07, 0x08, 0x40, 0x12, 0x00, 0x29, 0x08, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
+	{ { 0x04, 0x0A, 0x04, 0x00, 0xD8, 0x01, 0x02, 0x0D, 0xFA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
+	  { 0x04, 0x00, 0x03, 0x09, 0x93, 0x02, 0x00, 0x28, 0x09, 0xE8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
+	{ { 0x15, 0x14, 0x05, 0x00, 0xF9, 0x01, 0x03, 0x5C, 0xE9, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0x05, 0x00, 0x03, 0x03, 0x49, 0x02, 0x00, 0x58, 0x08, 0xE0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
+	{ { 0x10, 0x10, 0x05, 0x08, 0xF8, 0x01, 0x03, 0x0D, 0xEA, 0xE8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
+	  { 0x10, 0x00, 0x0C, 0x0C, 0x48, 0x02, 0x00, 0x08, 0xB9, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
+	{ { 0x11, 0x00, 0x06, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
+	  { 0x15, 0x00, 0x04, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } },
+	{ { 0x13, 0x26, 0x04, 0x6A, 0xFB, 0x01, 0x00, 0x08, 0x5A, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
+	  { 0x12, 0x26, 0x03, 0x6A, 0xFB, 0x02, 0x00, 0x06, 0x5A, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } },
+	{ { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC4, 0x00, 0x18, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0xCF, 0x4E, 0x05, 0xA6, 0xA0, 0xC6, 0x00, 0x16, 0xF8, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC3, 0x00, 0x18, 0xF8, 0x98, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0xCF, 0x4E, 0x06, 0xAA, 0xA0, 0xC5, 0x00, 0x19, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0xCB, 0x3F, 0x8F, 0x00, 0xFA, 0xC5, 0x06, 0x98, 0xD6, 0xBB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
+	  { 0xC0, 0x00, 0xF0, 0x00, 0x00, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } },
+	{ { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x19, 0x0B, 0x55, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x1B, 0x10, 0x57, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x05, 0x11, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xA8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x02, 0x11, 0x13, 0x00, 0xC8, 0x02, 0x00, 0x05, 0x00, 0x80, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0x04, 0x08, 0x15, 0x00, 0x90, 0x01, 0x00, 0x08, 0x00, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
+	  { 0x03, 0x08, 0x14, 0x00, 0x90, 0x02, 0x00, 0x07, 0x00, 0xA8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
+	{ { 0xDA, 0x00, 0x53, 0x30, 0xC0, 0x07, 0x10, 0x49, 0xC4, 0xDA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0xD2, 0x00, 0x56, 0x30, 0x90, 0x06, 0x00, 0x46, 0x56, 0x62, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
+	{ { 0x1C, 0x00, 0x07, 0xBC, 0xC8, 0x0C, 0x0A, 0x0B, 0x6A, 0xF2, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
+	  { 0x18, 0x00, 0x07, 0xBC, 0x88, 0x09, 0x00, 0x0B, 0x6A, 0xBA, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
+	{ { 0x0A, 0x0E, 0x7F, 0x00, 0xF9, 0x13, 0x16, 0x28, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
+	  { 0x01, 0x0E, 0x54, 0x00, 0xF9, 0x15, 0x03, 0x27, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }
+};
+#endif
+
+static const byte g_gmPercussionInstrumentMap[128] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
+	0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0xFF, 0xFF, 0x17, 0x18, 0x19, 0x1A,
+	0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x21, 0x22, 0x23, 0xFF, 0xFF,
+	0x24, 0x25, 0xFF, 0xFF, 0xFF, 0x26, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static byte g_volumeLookupTable[64][32];
+
+static const byte g_volumeTable[] = {
+	0, 4, 7, 11,
+	13, 16, 18, 20,
+	22, 24, 26, 27,
+	29, 30, 31, 33,
+	34, 35, 36, 37,
+	38, 39, 40, 41,
+	42, 43, 44, 44,
+	45, 46, 47, 47,
+	48, 49, 49, 50,
+	51, 51, 52, 53,
+	53, 54, 54, 55,
+	55, 56, 56, 57,
+	57, 58, 58, 59,
+	59, 60, 60, 60,
+	61, 61, 62, 62,
+	62, 63, 63, 63
+};
+
+static int lookupVolume(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 g_volumeLookupTable[-a][-b];
+		} else {
+			return -g_volumeLookupTable[a][-b];
+		}
+	} else {
+		if (a < 0) {
+			return -g_volumeLookupTable[-a][b];
+		} else {
+			return g_volumeLookupTable[a][b];
+		}
+	}
+}
+
+static void createLookupTable() {
+	int i, j;
+	int sum;
+
+	for (i = 0; i < 64; i++) {
+		sum = i;
+		for (j = 0; j < 32; j++) {
+			g_volumeLookupTable[i][j] = sum >> 5;
+			sum += i;
+		}
+	}
+	for (i = 0; i < 64; i++)
+		g_volumeLookupTable[i][0] = 0;
+}
+
+////////////////////////////////////////
+//
+// AdLib MIDI driver
+//
+////////////////////////////////////////
+
+class MidiDriver_ADLIB : public MidiDriver {
+	friend class AdLibPart;
+	friend class AdLibPercussionChannel;
+
+public:
+	MidiDriver_ADLIB();
+
+	int open();
+	void close();
+	void send(uint32 b);
+	void send(byte channel, uint32 b); // Supports higher than channel 15
+	uint32 property(int prop, uint32 param);
+	bool isOpen() const { return _isOpen; }
+	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
+
+	void setPitchBendRange(byte channel, uint range);
+	void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
+
+	MidiChannel *allocateChannel();
+	MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported
+
+	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
+private:
+	bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games
+#ifdef ENABLE_OPL3
+	bool _opl3Mode;
+#endif
+
+	OPL::OPL *_opl;
+	byte *_regCache;
+#ifdef ENABLE_OPL3
+	byte *_regCacheSecondary;
+#endif
+
+	Common::TimerManager::TimerProc _adlibTimerProc;
+	void *_adlibTimerParam;
+
+	int _timerCounter;
+
+	uint16 _channelTable2[9];
+	int _voiceIndex;
+	int _timerIncrease;
+	int _timerThreshold;
+	uint16 _curNotTable[9];
+	AdLibVoice _voices[9];
+	AdLibPart _parts[32];
+	AdLibPercussionChannel _percussion;
+
+	bool _isOpen;
+
+	void onTimer();
+	void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
+	void partKeyOff(AdLibPart *part, byte note);
+
+	void adlibKeyOff(int chan);
+	void adlibNoteOn(int chan, byte note, int mod);
+	void adlibNoteOnEx(int chan, byte note, int mod);
+	int adlibGetRegValueParam(int chan, byte data);
+	void adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2);
+#ifdef ENABLE_OPL3
+	void adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan);
+#endif
+	byte adlibGetRegValue(byte reg) {
+		return _regCache[reg];
+	}
+#ifdef ENABLE_OPL3
+	byte adlibGetRegValueSecondary(byte reg) {
+		return _regCacheSecondary[reg];
+	}
+#endif
+	void adlibSetParam(int channel, byte param, int value, bool primary = true);
+	void adlibKeyOnOff(int channel);
+	void adlibWrite(byte reg, byte value);
+#ifdef ENABLE_OPL3
+	void adlibWriteSecondary(byte reg, byte value);
+#endif
+	void adlibPlayNote(int channel, int note);
+
+	AdLibVoice *allocateVoice(byte pri);
+
+	void mcOff(AdLibVoice *voice);
+
+	static void linkMc(AdLibPart *part, AdLibVoice *voice);
+	void mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11);
+	void mcInitStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags,
+					   const InstrumentExtra *ie);
+
+	void struct10Init(Struct10 *s10, const InstrumentExtra *ie);
+	static byte struct10OnTimer(Struct10 *s10, Struct11 *s11);
+	static void struct10Setup(Struct10 *s10);
+	static int randomNr(int a);
+	void mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
+};
+
+// MidiChannel method implementations
+
+void AdLibPart::init(MidiDriver_ADLIB *owner, byte channel) {
+	_owner = owner;
+	_channel = channel;
+	_priEff = 127;
+	programChange(0);
+}
+
+MidiDriver *AdLibPart::device() {
+	return _owner;
+}
+
+void AdLibPart::send(uint32 b) {
+	_owner->send(_channel, b);
+}
+
+void AdLibPart::noteOff(byte note) {
+#ifdef DEBUG_ADLIB
+	debug(6, "%10d: noteOff(%d)", g_tick, note);
+#endif
+	_owner->partKeyOff(this, note);
+}
+
+void AdLibPart::noteOn(byte note, byte velocity) {
+#ifdef DEBUG_ADLIB
+	debug(6, "%10d: noteOn(%d,%d)", g_tick, note, velocity);
+#endif
+	_owner->partKeyOn(this, &_partInstr, note, velocity,
+#ifdef ENABLE_OPL3
+			&_partInstrSecondary,
+#else
+			NULL,
+#endif
+			_pan);
+}
+
+void AdLibPart::programChange(byte program) {
+	if (program > 127)
+		return;
+
+	/*
+	uint i;
+	uint count = 0;
+	for (i = 0; i < ARRAYSIZE(g_gmInstruments[0]); ++i)
+		count += g_gmInstruments[program][i];
+	if (!count)
+		warning("No AdLib instrument defined for GM program %d", (int)program);
+	*/
+	_program = program;
+#ifdef ENABLE_OPL3
+	if (!_owner->_opl3Mode) {
+#endif
+		memcpy(&_partInstr, &g_gmInstruments[program], sizeof(AdLibInstrument));
+#ifdef ENABLE_OPL3
+	} else {
+		memcpy(&_partInstr,          &g_gmInstrumentsOPL3[program][0], sizeof(AdLibInstrument));
+		memcpy(&_partInstrSecondary, &g_gmInstrumentsOPL3[program][1], sizeof(AdLibInstrument));
+	}
+#endif
+}
+
+void AdLibPart::pitchBend(int16 bend) {
+	AdLibVoice *voice;
+
+	_pitchBend = bend;
+	for (voice = _voice; voice; voice = voice->_next) {
+#ifdef ENABLE_OPL3
+		if (!_owner->_opl3Mode) {
+#endif
+			_owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/,
+								  (_pitchBend * _pitchBendFactor >> 6) + _detuneEff);
+#ifdef ENABLE_OPL3
+		} else {
+			_owner->adlibNoteOn(voice->_channel, voice->_note, _pitchBend >> 1);
+		}
+#endif
+	}
+}
+
+void AdLibPart::controlChange(byte control, byte value) {
+	switch (control) {
+	case 0:
+	case 32:
+		// Bank select. Not supported
+		break;
+	case 1:
+		modulationWheel(value);
+		break;
+	case 7:
+		volume(value);
+		break;
+	case 10:
+		panPosition(value);
+		break;
+	case 16:
+		pitchBendFactor(value);
+		break;
+	case 17:
+		detune(value);
+		break;
+	case 18:
+		priority(value);
+		break;
+	case 64:
+		sustain(value > 0);
+		break;
+	case 91:
+		// Effects level. Not supported.
+		break;
+	case 93:
+		// Chorus level. Not supported.
+		break;
+	case 119:
+		// Unknown, used in Simon the Sorcerer 2
+		break;
+	case 121:
+		// reset all controllers
+		modulationWheel(0);
+		pitchBendFactor(0);
+		detune(0);
+		sustain(0);
+		break;
+	case 123:
+		allNotesOff();
+		break;
+	default:
+		warning("AdLib: Unknown control change message %d (%d)", (int)control, (int)value);
+	}
+}
+
+void AdLibPart::modulationWheel(byte value) {
+	AdLibVoice *voice;
+
+	_modWheel = value;
+	for (voice = _voice; voice; voice = voice->_next) {
+		if (voice->_s10a.active && voice->_s11a.flag0x40)
+			voice->_s10a.modWheel = _modWheel >> 2;
+		if (voice->_s10b.active && voice->_s11b.flag0x40)
+			voice->_s10b.modWheel = _modWheel >> 2;
+	}
+}
+
+void AdLibPart::volume(byte value) {
+	AdLibVoice *voice;
+
+	_volEff = value;
+	for (voice = _voice; voice; voice = voice->_next) {
+#ifdef ENABLE_OPL3
+		if (!_owner->_opl3Mode) {
+#endif
+			_owner->adlibSetParam(voice->_channel, 0, g_volumeTable[g_volumeLookupTable[voice->_vol2][_volEff >> 2]]);
+			if (voice->_twoChan) {
+				_owner->adlibSetParam(voice->_channel, 13, g_volumeTable[g_volumeLookupTable[voice->_vol1][_volEff >> 2]]);
+			}
+#ifdef ENABLE_OPL3
+		} else {
+			_owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_vol2    + 1) * _volEff) >> 7], true);
+			_owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_secVol2 + 1) * _volEff) >> 7], false);
+			if (voice->_twoChan) {
+				_owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_vol1    + 1) * _volEff) >> 7], true);
+			}
+			if (voice->_secTwoChan) {
+				_owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_secVol1 + 1) * _volEff) >> 7], false);
+			}
+		}
+#endif
+	}
+}
+
+void AdLibPart::panPosition(byte value) {
+	_pan = value;
+}
+
+void AdLibPart::pitchBendFactor(byte value) {
+#ifdef ENABLE_OPL3
+	// Not supported in OPL3 mode.
+	if (_owner->_opl3Mode) {
+		return;
+	}
+#endif
+
+	AdLibVoice *voice;
+
+	_pitchBendFactor = value;
+	for (voice = _voice; voice; voice = voice->_next) {
+		_owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/,
+							  (_pitchBend * _pitchBendFactor >> 6) + _detuneEff);
+	}
+}
+
+void AdLibPart::detune(byte value) {
+	// Sam&Max's OPL3 driver uses this for a completly different purpose. It
+	// is related to voice allocation. We ignore this for now.
+	// TODO: We probably need to look how the interpreter side of Sam&Max's
+	// iMuse version handles all this too. Implementing the driver side here
+	// would be not that hard.
+#ifdef ENABLE_OPL3
+	if (_owner->_opl3Mode) {
+		//_maxNotes = value;
+		return;
+	}
+#endif
+
+	AdLibVoice *voice;
+
+	_detuneEff = value;
+	for (voice = _voice; voice; voice = voice->_next) {
+		_owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/,
+							  (_pitchBend * _pitchBendFactor >> 6) + _detuneEff);
+	}
+}
+
+void AdLibPart::priority(byte value) {
+	_priEff = value;
+}
+
+void AdLibPart::sustain(bool value) {
+	AdLibVoice *voice;
+
+	_pedal = value;
+	if (!value) {
+		for (voice = _voice; voice; voice = voice->_next) {
+			if (voice->_waitForPedal)
+				_owner->mcOff(voice);
+		}
+	}
+}
+
+void AdLibPart::allNotesOff() {
+	while (_voice)
+		_owner->mcOff(_voice);
+}
+
+void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr) {
+	// Sam&Max allows for instrument overwrites, but we will not support it
+	// until we can find any track actually using it.
+#ifdef ENABLE_OPL3
+	if (_owner->_opl3Mode) {
+		warning("AdLibPart::sysEx_customInstrument: Used in OPL3 mode");
+		return;
+	}
+#endif
+
+	if (type == 'ADL ') {
+		memcpy(&_partInstr, instr, sizeof(AdLibInstrument));
+	}
+}
+
+// MidiChannel method implementations for percussion
+
+AdLibPercussionChannel::~AdLibPercussionChannel() {
+	for (int i = 0; i < ARRAYSIZE(_customInstruments); ++i) {
+		delete _customInstruments[i];
+	}
+}
+
+void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) {
+	AdLibPart::init(owner, channel);
+	_priEff = 0;
+	_volEff = 127;
+
+	// Initialize the custom instruments data
+	memset(_notes, 0, sizeof(_notes));
+	memset(_customInstruments, 0, sizeof(_customInstruments));
+}
+
+void AdLibPercussionChannel::noteOff(byte note) {
+	if (_customInstruments[note]) {
+		note = _notes[note];
+	}
+
+	// This used to ignore note off events, since the builtin percussion
+	// instrument data has a duration value, which causes the percussion notes
+	// to stop automatically. This is not the case for (Groovie's) custom
+	// percussion instruments though. Also the OPL3 driver of Sam&Max actually
+	// does not handle the duration value, so we need it there too.
+	 _owner->partKeyOff(this, note);
+}
+
+void AdLibPercussionChannel::noteOn(byte note, byte velocity) {
+	const AdLibInstrument *inst = NULL;
+	const AdLibInstrument *sec  = NULL;
+
+	// The custom instruments have priority over the default mapping
+	// We do not support custom instruments in OPL3 mode though.
+#ifdef ENABLE_OPL3
+	if (!_owner->_opl3Mode) {
+#endif
+		inst = _customInstruments[note];
+		if (inst)
+			note = _notes[note];
+#ifdef ENABLE_OPL3
+	}
+#endif
+
+	if (!inst) {
+		// Use the default GM to FM mapping as a fallback
+		byte key = g_gmPercussionInstrumentMap[note];
+		if (key != 0xFF) {
+#ifdef ENABLE_OPL3
+			if (!_owner->_opl3Mode) {
+#endif
+				inst = &g_gmPercussionInstruments[key];
+#ifdef ENABLE_OPL3
+			} else {
+				inst = &g_gmPercussionInstrumentsOPL3[key][0];
+				sec  = &g_gmPercussionInstrumentsOPL3[key][1];
+			}
+#endif
+		}
+	}
+
+	if (!inst) {
+		debug(2, "No instrument FM definition for GM percussion key %d", (int)note);
+		return;
+	}
+
+	_owner->partKeyOn(this, inst, note, velocity, sec, _pan);
+}
+
+void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *instr) {
+	// We do not allow custom instruments in OPL3 mode right now.
+#ifdef ENABLE_OPL3
+	if (_owner->_opl3Mode) {
+		warning("AdLibPercussionChannel::sysEx_customInstrument: Used in OPL3 mode");
+		return;
+	}
+#endif
+
+	if (type == 'ADLP') {
+		byte note = instr[0];
+		_notes[note] = instr[1];
+
+		// Allocate memory for the new instruments
+		if (!_customInstruments[note]) {
+			_customInstruments[note] = new AdLibInstrument;
+			memset(_customInstruments[note], 0, sizeof(AdLibInstrument));
+		}
+
+		// Save the new instrument data
+		_customInstruments[note]->modCharacteristic     = instr[2];
+		_customInstruments[note]->modScalingOutputLevel = instr[3];
+		_customInstruments[note]->modAttackDecay        = instr[4];
+		_customInstruments[note]->modSustainRelease     = instr[5];
+		_customInstruments[note]->modWaveformSelect     = instr[6];
+		_customInstruments[note]->carCharacteristic     = instr[7];
+		_customInstruments[note]->carScalingOutputLevel = instr[8];
+		_customInstruments[note]->carAttackDecay        = instr[9];
+		_customInstruments[note]->carSustainRelease     = instr[10];
+		_customInstruments[note]->carWaveformSelect     = instr[11];
+		_customInstruments[note]->feedback               = instr[12];
+	}
+}
+
+// MidiDriver method implementations
+
+MidiDriver_ADLIB::MidiDriver_ADLIB() {
+	uint i;
+
+	_scummSmallHeader = false;
+#ifdef ENABLE_OPL3
+	_opl3Mode = false;
+#endif
+
+	_regCache = 0;
+#ifdef ENABLE_OPL3
+	_regCacheSecondary = 0;
+#endif
+
+	_timerCounter = 0;
+	_voiceIndex = -1;
+	for (i = 0; i < ARRAYSIZE(_curNotTable); ++i) {
+		_curNotTable[i] = 0;
+	}
+
+	for (i = 0; i < ARRAYSIZE(_parts); ++i) {
+		_parts[i].init(this, i + ((i >= 9) ? 1 : 0));
+	}
+	_percussion.init(this, 9);
+	_timerIncrease = 0xD69;
+	_timerThreshold = 0x411B;
+	_opl = 0;
+	_adlibTimerProc = 0;
+        _adlibTimerParam = 0;
+	_isOpen = false;
+}
+
+int MidiDriver_ADLIB::open() {
+	if (_isOpen)
+		return MERR_ALREADY_OPEN;
+
+	_isOpen = true;
+
+	int i;
+	AdLibVoice *voice;
+
+	for (i = 0, voice = _voices; i != ARRAYSIZE(_voices); i++, voice++) {
+		voice->_channel = i;
+		voice->_s11a.s10 = &voice->_s10b;
+		voice->_s11b.s10 = &voice->_s10a;
+	}
+
+	// Try to use OPL3 when requested.
+#ifdef ENABLE_OPL3
+	if (_opl3Mode) {
+		_opl = OPL::Config::create(OPL::Config::kOpl3);
+	}
+
+	// Initialize plain OPL2 when no OPL3 is intiailized already.
+	if (!_opl) {
+#endif
+		_opl = OPL::Config::create();
+#ifdef ENABLE_OPL3
+		_opl3Mode = false;
+	}
+#endif
+	_opl->init();
+
+	_regCache = (byte *)calloc(256, 1);
+
+	adlibWrite(8, 0x40);
+	adlibWrite(0xBD, 0x00);
+#ifdef ENABLE_OPL3
+	if (!_opl3Mode) {
+#endif
+		adlibWrite(1, 0x20);
+		createLookupTable();
+#ifdef ENABLE_OPL3
+	} else {
+		_regCacheSecondary = (byte *)calloc(256, 1);
+		adlibWriteSecondary(5, 1);
+	}
+#endif
+
+	_opl->start(new Common::Functor0Mem<void, MidiDriver_ADLIB>(this, &MidiDriver_ADLIB::onTimer));
+	return 0;
+}
+
+void MidiDriver_ADLIB::close() {
+	if (!_isOpen)
+		return;
+	_isOpen = false;
+
+	// Stop the OPL timer
+	_opl->stop();
+
+	uint i;
+	for (i = 0; i < ARRAYSIZE(_voices); ++i) {
+		if (_voices[i]._part)
+			mcOff(&_voices[i]);
+	}
+
+	// Turn off the OPL emulation
+	delete _opl;
+	_opl = 0;
+
+	free(_regCache);
+#ifdef ENABLE_OPL3
+	free(_regCacheSecondary);
+#endif
+}
+
+void MidiDriver_ADLIB::send(uint32 b) {
+	send(b & 0xF, b & 0xFFFFFFF0);
+}
+
+void MidiDriver_ADLIB::send(byte chan, uint32 b) {
+	//byte param3 = (byte) ((b >> 24) & 0xFF);
+	byte param2 = (byte)((b >> 16) & 0xFF);
+	byte param1 = (byte)((b >>  8) & 0xFF);
+	byte cmd    = (byte)(b & 0xF0);
+
+	AdLibPart *part;
+	if (chan == 9)
+		part = &_percussion;
+	else
+		part = &_parts[chan];
+
+	switch (cmd) {
+	case 0x80:// Note Off
+		part->noteOff(param1);
+		break;
+	case 0x90: // Note On
+		part->noteOn(param1, param2);
+		break;
+	case 0xA0: // Aftertouch
+		break; // Not supported.
+	case 0xB0: // Control Change
+		part->controlChange(param1, param2);
+		break;
+	case 0xC0: // Program Change
+		part->programChange(param1);
+		break;
+	case 0xD0: // Channel Pressure
+		break; // Not supported.
+	case 0xE0: // Pitch Bend
+		part->pitchBend((param1 | (param2 << 7)) - 0x2000);
+		break;
+	case 0xF0: // SysEx
+		// We should never get here! SysEx information has to be
+		// sent via high-level semantic methods.
+		warning("MidiDriver_ADLIB: Receiving SysEx command on a send() call");
+		break;
+
+	default:
+		warning("MidiDriver_ADLIB: Unknown send() command 0x%02X", cmd);
+	}
+}
+
+uint32 MidiDriver_ADLIB::property(int prop, uint32 param) {
+	switch (prop) {
+	case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm
+		_scummSmallHeader = (param > 0);
+		if (_scummSmallHeader) {
+			_timerIncrease = 473;
+			_timerThreshold = 1000;
+		} else {
+			_timerIncrease = 0xD69;
+			_timerThreshold = 0x411B;
+		}
+		return 1;
+
+	case PROP_SCUMM_OPL3: // Sam&Max OPL3 support.
+#ifdef ENABLE_OPL3
+		_opl3Mode = (param > 0);
+#endif
+		return 1;
+	}
+
+	return 0;
+}
+
+void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) {
+#ifdef ENABLE_OPL3
+	// Not supported in OPL3 mode.
+	if (_opl3Mode) {
+		return;
+	}
+#endif
+
+	AdLibVoice *voice;
+	AdLibPart *part = &_parts[channel];
+
+	part->_pitchBendFactor = range;
+	for (voice = part->_voice; voice; voice = voice->_next) {
+		adlibNoteOn(voice->_channel, voice->_note/* + part->_transposeEff*/,
+					  (part->_pitchBend * part->_pitchBendFactor >> 6) + part->_detuneEff);
+	}
+}
+
+void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
+	_parts[channel].sysEx_customInstrument(type, instr);
+}
+
+MidiChannel *MidiDriver_ADLIB::allocateChannel() {
+	AdLibPart *part;
+	uint i;
+
+	for (i = 0; i < ARRAYSIZE(_parts); ++i) {
+		part = &_parts[i];
+		if (!part->_allocated) {
+			part->allocate();
+			return part;
+		}
+	}
+	return NULL;
+}
+
+// All the code brought over from IMuseAdLib
+
+void MidiDriver_ADLIB::adlibWrite(byte reg, byte value) {
+	if (_regCache[reg] == value) {
+		return;
+	}
+#ifdef DEBUG_ADLIB
+	debug(6, "%10d: adlibWrite[%x] = %x", g_tick, reg, value);
+#endif
+	_regCache[reg] = value;
+
+	_opl->writeReg(reg, value);
+}
+
+#ifdef ENABLE_OPL3
+void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) {
+	assert(_opl3Mode);
+
+	if (_regCacheSecondary[reg] == value) {
+		return;
+	}
+#ifdef DEBUG_ADLIB
+	debug(6, "%10d: adlibWriteSecondary[%x] = %x", g_tick, reg, value);
+#endif
+	_regCacheSecondary[reg] = value;
+
+	_opl->writeReg(reg | 0x100, value);
+}
+#endif
+
+void MidiDriver_ADLIB::onTimer() {
+	if (_adlibTimerProc)
+		(*_adlibTimerProc)(_adlibTimerParam);
+
+	_timerCounter += _timerIncrease;
+	while (_timerCounter >= _timerThreshold) {
+		_timerCounter -= _timerThreshold;
+#ifdef DEBUG_ADLIB
+		g_tick++;
+#endif
+		// Sam&Max's OPL3 driver does not have any timer handling like this.
+#ifdef ENABLE_OPL3
+		if (!_opl3Mode) {
+#endif
+			AdLibVoice *voice = _voices;
+			for (int i = 0; i != ARRAYSIZE(_voices); i++, voice++) {
+				if (!voice->_part)
+					continue;
+				if (voice->_duration && (voice->_duration -= 0x11) <= 0) {
+					mcOff(voice);
+					return;
+				}
+				if (voice->_s10a.active) {
+					mcIncStuff(voice, &voice->_s10a, &voice->_s11a);
+				}
+				if (voice->_s10b.active) {
+					mcIncStuff(voice, &voice->_s10b, &voice->_s11b);
+				}
+			}
+#ifdef ENABLE_OPL3
+		}
+#endif
+	}
+}
+
+void MidiDriver_ADLIB::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+	_adlibTimerProc = timerProc;
+	_adlibTimerParam = timerParam;
+}
+
+void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) {
+	AdLibVoice *tmp;
+
+	adlibKeyOff(voice->_channel);
+
+	tmp = voice->_prev;
+
+	if (voice->_next)
+		voice->_next->_prev = tmp;
+	if (tmp)
+		tmp->_next = voice->_next;
+	else
+		voice->_part->_voice = voice->_next;
+	voice->_part = NULL;
+}
+
+void MidiDriver_ADLIB::mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) {
+	byte code;
+	AdLibPart *part = voice->_part;
+
+	code = struct10OnTimer(s10, s11);
+
+	if (code & 1) {
+		switch (s11->param) {
+		case 0:
+			voice->_vol2 = s10->startValue + s11->modifyVal;
+			if (!_scummSmallHeader) {
+				adlibSetParam(voice->_channel, 0,
+								g_volumeTable[g_volumeLookupTable[voice->_vol2]
+											  [part->_volEff >> 2]]);
+			} else {
+				adlibSetParam(voice->_channel, 0, voice->_vol2);
+			}
+			break;
+		case 13:
+			voice->_vol1 = s10->startValue + s11->modifyVal;
+			if (voice->_twoChan && !_scummSmallHeader) {
+				adlibSetParam(voice->_channel, 13,
+								g_volumeTable[g_volumeLookupTable[voice->_vol1]
+											  [part->_volEff >> 2]]);
+			} else {
+				adlibSetParam(voice->_channel, 13, voice->_vol1);
+			}
+			break;
+		case 30:
+			s11->s10->modWheel = (char)s11->modifyVal;
+			break;
+		case 31:
+			s11->s10->unk3 = (char)s11->modifyVal;
+			break;
+		default:
+			adlibSetParam(voice->_channel, s11->param,
+							s10->startValue + s11->modifyVal);
+			break;
+		}
+	}
+
+	if (code & 2 && s11->flag0x10)
+		adlibKeyOnOff(voice->_channel);
+}
+
+void MidiDriver_ADLIB::adlibKeyOff(int chan) {
+	byte reg = chan + 0xB0;
+	adlibWrite(reg, adlibGetRegValue(reg) & ~0x20);
+#ifdef ENABLE_OPL3
+	if (_opl3Mode) {
+		adlibWriteSecondary(reg, adlibGetRegValueSecondary(reg) & ~0x20);
+	}
+#endif
+}
+
+byte MidiDriver_ADLIB::struct10OnTimer(Struct10 *s10, Struct11 *s11) {
+	byte result = 0;
+	int i;
+
+	if (s10->count && (s10->count -= 17) <= 0) {
+		s10->active = 0;
+		return 0;
+	}
+
+	i = s10->curVal + s10->speedHi;
+	s10->speedLoCounter += s10->speedLo;
+	if (s10->speedLoCounter >= s10->speedLoMax) {
+		s10->speedLoCounter -= s10->speedLoMax;
+		i += s10->direction;
+	}
+	if (s10->curVal != i || s10->modWheel != s10->modWheelLast) {
+		s10->curVal = i;
+		s10->modWheelLast = s10->modWheel;
+		i = lookupVolume(i, s10->modWheelLast);
+		if (i != s11->modifyVal) {
+			s11->modifyVal = i;
+			result = 1;
+		}
+	}
+
+	if (!--s10->numSteps) {
+		s10->active++;
+		if (s10->active > 4) {
+			if (s10->loop) {
+				s10->active = 1;
+				result |= 2;
+				struct10Setup(s10);
+			} else {
+				s10->active = 0;
+			}
+		} else {
+			struct10Setup(s10);
+		}
+	}
+
+	return result;
+}
+
+void MidiDriver_ADLIB::adlibSetParam(int channel, byte param, int value, bool primary) {
+	const AdLibSetParams *as;
+	byte reg;
+
+	assert(channel >= 0 && channel < 9);
+#ifdef ENABLE_OPL3
+	assert(!_opl3Mode || (param == 0 || param == 13));
+#endif
+
+	if (param <= 12) {
+		reg = g_operator2Offsets[channel];
+	} else if (param <= 25) {
+		param -= 13;
+		reg = g_operator1Offsets[channel];
+	} else if (param <= 27) {
+		param -= 13;
+		reg = channel;
+	} else if (param == 28 || param == 29) {
+		if (param == 28)
+			value -= 15;
+		else
+			value -= 383;
+		value <<= 4;
+		_channelTable2[channel] = value;
+		adlibPlayNote(channel, _curNotTable[channel] + value);
+		return;
+	} else {
+		return;
+	}
+
+	as = &g_setParamTable[param];
+	if (as->inversion)
+		value = as->inversion - value;
+	reg += as->registerBase;
+#ifdef ENABLE_OPL3
+	if (primary) {
+#endif
+		adlibWrite(reg, (adlibGetRegValue(reg) & ~as->mask) | (((byte)value) << as->shift));
+#ifdef ENABLE_OPL3
+	} else {
+		adlibWriteSecondary(reg, (adlibGetRegValueSecondary(reg) & ~as->mask) | (((byte)value) << as->shift));
+	}
+#endif
+}
+
+void MidiDriver_ADLIB::adlibKeyOnOff(int channel) {
+#ifdef ENABLE_OPL3
+	assert(!_opl3Mode);
+#endif
+
+	byte val;
+	byte reg = channel + 0xB0;
+	assert(channel >= 0 && channel < 9);
+
+	val = adlibGetRegValue(reg);
+	adlibWrite(reg, val & ~0x20);
+	adlibWrite(reg, val | 0x20);
+}
+
+void MidiDriver_ADLIB::struct10Setup(Struct10 *s10) {
+	int b, c, d, e, f, g, h;
+	byte t;
+
+	b = s10->unk3;
+	f = s10->active - 1;
+
+	t = s10->tableA[f];
+	e = g_numStepsTable[g_volumeLookupTable[t & 0x7F][b]];
+	if (t & 0x80) {
+		e = randomNr(e);
+	}
+	if (e == 0)
+		e++;
+
+	s10->numSteps = s10->speedLoMax = e;
+
+	if (f != 2) {
+		c = s10->maxValue;
+		g = s10->startValue;
+		t = s10->tableB[f];
+		d = lookupVolume(c, (t & 0x7F) - 31);
+		if (t & 0x80) {
+			d = randomNr(d);
+		}
+		if (d + g > c) {
+			h = c - g;
+		} else {
+			h = d;
+			if (d + g < 0)
+				h = -g;
+		}
+		h -= s10->curVal;
+	} else {
+		h = 0;
+	}
+
+	s10->speedHi = h / e;
+	if (h < 0) {
+		h = -h;
+		s10->direction = -1;
+	} else {
+		s10->direction = 1;
+	}
+
+	s10->speedLo = h % e;
+	s10->speedLoCounter = 0;
+}
+
+void MidiDriver_ADLIB::adlibPlayNote(int channel, int note) {
+	byte old, oct, notex;
+	int note2;
+	int i;
+
+	note2 = (note >> 7) - 4;
+	note2 = (note2 < 128) ? note2 : 0;
+
+	oct = (note2 / 12);
+	if (oct > 7)
+		oct = 7 << 2;
+	else
+		oct <<= 2;
+	notex = note2 % 12 + 3;
+
+	old = adlibGetRegValue(channel + 0xB0);
+	if (old & 0x20) {
+		old &= ~0x20;
+		if (oct > old) {
+			if (notex < 6) {
+				notex += 12;
+				oct -= 4;
+			}
+		} else if (oct < old) {
+			if (notex > 11) {
+				notex -= 12;
+				oct += 4;
+			}
+		}
+	}
+
+	i = (notex << 3) + ((note >> 4) & 0x7);
+	adlibWrite(channel + 0xA0, g_noteFrequencies[i]);
+	adlibWrite(channel + 0xB0, oct | 0x20);
+}
+
+int MidiDriver_ADLIB::randomNr(int a) {
+	static byte _randSeed = 1;
+	if (_randSeed & 1) {
+		_randSeed >>= 1;
+		_randSeed ^= 0xB8;
+	} else {
+		_randSeed >>= 1;
+	}
+	return _randSeed * a >> 8;
+}
+
+void MidiDriver_ADLIB::partKeyOff(AdLibPart *part, byte note) {
+	AdLibVoice *voice;
+
+	for (voice = part->_voice; voice; voice = voice->_next) {
+		if (voice->_note == note) {
+			if (part->_pedal)
+				voice->_waitForPedal = true;
+			else
+				mcOff(voice);
+		}
+	}
+}
+
+void MidiDriver_ADLIB::partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) {
+	AdLibVoice *voice;
+
+	voice = allocateVoice(part->_priEff);
+	if (!voice)
+		return;
+
+	linkMc(part, voice);
+	mcKeyOn(voice, instr, note, velocity, second, pan);
+}
+
+AdLibVoice *MidiDriver_ADLIB::allocateVoice(byte pri) {
+	AdLibVoice *ac, *best = NULL;
+	int i;
+
+	for (i = 0; i < 9; i++) {
+		if (++_voiceIndex >= 9)
+			_voiceIndex = 0;
+		ac = &_voices[_voiceIndex];
+		if (!ac->_part)
+			return ac;
+		if (!ac->_next) {
+			if (ac->_part->_priEff <= pri) {
+				pri = ac->_part->_priEff;
+				best = ac;
+			}
+		}
+	}
+
+	/* SCUMM V3 games don't have note priorities, first comes wins. */
+	if (_scummSmallHeader)
+		return NULL;
+
+	if (best)
+		mcOff(best);
+	return best;
+}
+
+void MidiDriver_ADLIB::linkMc(AdLibPart *part, AdLibVoice *voice) {
+	voice->_part = part;
+	voice->_next = (AdLibVoice *)part->_voice;
+	part->_voice = voice;
+	voice->_prev = NULL;
+
+	if (voice->_next)
+		voice->_next->_prev = voice;
+}
+
+void MidiDriver_ADLIB::mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) {
+	AdLibPart *part = voice->_part;
+	byte vol1, vol2;
+#ifdef ENABLE_OPL3
+	byte secVol1 = 0, secVol2 = 0;
+#endif
+
+	voice->_twoChan = instr->feedback & 1;
+	voice->_note = note;
+	voice->_waitForPedal = false;
+	voice->_duration = instr->duration;
+	if (voice->_duration != 0)
+		voice->_duration *= 63;
+
+	if (!_scummSmallHeader) {
+#ifdef ENABLE_OPL3
+		if (_opl3Mode)
+			vol1 = (instr->modScalingOutputLevel & 0x3F) + (velocity * ((instr->modWaveformSelect >> 3) + 1)) / 64;
+		else
+#endif
+		vol1 = (instr->modScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->modWaveformSelect >> 2];
+	} else {
+		vol1 = 0x3f - (instr->modScalingOutputLevel & 0x3F);
+	}
+	if (vol1 > 0x3F)
+		vol1 = 0x3F;
+	voice->_vol1 = vol1;
+
+	if (!_scummSmallHeader) {
+#ifdef ENABLE_OPL3
+		if (_opl3Mode)
+			vol2 = (instr->carScalingOutputLevel & 0x3F) + (velocity * ((instr->carWaveformSelect >> 3) + 1)) / 64;
+		else
+#endif
+		vol2 = (instr->carScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->carWaveformSelect >> 2];
+	} else {
+		vol2 = 0x3f - (instr->carScalingOutputLevel & 0x3F);
+	}
+	if (vol2 > 0x3F)
+		vol2 = 0x3F;
+	voice->_vol2 = vol2;
+
+#ifdef ENABLE_OPL3
+	if (_opl3Mode) {
+		voice->_secTwoChan = second->feedback & 1;
+		secVol1 = (second->modScalingOutputLevel & 0x3F) + (velocity * ((second->modWaveformSelect >> 3) + 1)) / 64;
+		if (secVol1 > 0x3F) {
+			secVol1 = 0x3F;
+		}
+		voice->_secVol1 = secVol1;
+		secVol2 = (second->carScalingOutputLevel & 0x3F) + (velocity * ((second->carWaveformSelect >> 3) + 1)) / 64;
+		if (secVol2 > 0x3F) {
+			secVol2 = 0x3F;
+		}
+		voice->_secVol2 = secVol2;
+	}
+#endif
+
+	if (!_scummSmallHeader) {
+#ifdef ENABLE_OPL3
+		if (!_opl3Mode) {
+#endif
+			int c = part->_volEff >> 2;
+			vol2 = g_volumeTable[g_volumeLookupTable[vol2][c]];
+			if (voice->_twoChan)
+				vol1 = g_volumeTable[g_volumeLookupTable[vol1][c]];
+#ifdef ENABLE_OPL3
+		} else {
+			vol2    = g_volumeTable[((vol2    + 1) * part->_volEff) >> 7];
+			secVol2 = g_volumeTable[((secVol2 + 1) * part->_volEff) >> 7];
+			if (voice->_twoChan)
+				vol1    = g_volumeTable[((vol1    + 1) * part->_volEff) >> 7];
+			if (voice->_secTwoChan)
+				secVol1 = g_volumeTable[((secVol1 + 1) * part->_volEff) >> 7];
+		}
+#endif
+	}
+
+	adlibSetupChannel(voice->_channel, instr, vol1, vol2);
+#ifdef ENABLE_OPL3
+	if (!_opl3Mode) {
+#endif
+		adlibNoteOnEx(voice->_channel, /*part->_transposeEff + */note, part->_detuneEff + (part->_pitchBend * part->_pitchBendFactor >> 6));
+
+		if (instr->flagsA & 0x80) {
+			mcInitStuff(voice, &voice->_s10a, &voice->_s11a, instr->flagsA, &instr->extraA);
+		} else {
+			voice->_s10a.active = 0;
+		}
+
+		if (instr->flagsB & 0x80) {
+			mcInitStuff(voice, &voice->_s10b, &voice->_s11b, instr->flagsB, &instr->extraB);
+		} else {
+			voice->_s10b.active = 0;
+		}
+#ifdef ENABLE_OPL3
+	} else {
+		adlibSetupChannelSecondary(voice->_channel, second, secVol1, secVol2, pan);
+		adlibNoteOnEx(voice->_channel, note, part->_pitchBend >> 1);
+	}
+#endif
+}
+
+void MidiDriver_ADLIB::adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2) {
+	assert(chan >= 0 && chan < 9);
+
+	byte channel = g_operator1Offsets[chan];
+	adlibWrite(channel + 0x20, instr->modCharacteristic);
+	adlibWrite(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1);
+	adlibWrite(channel + 0x60, 0xff & (~instr->modAttackDecay));
+	adlibWrite(channel + 0x80, 0xff & (~instr->modSustainRelease));
+	adlibWrite(channel + 0xE0, instr->modWaveformSelect);
+
+	channel = g_operator2Offsets[chan];
+	adlibWrite(channel + 0x20, instr->carCharacteristic);
+	adlibWrite(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2);
+	adlibWrite(channel + 0x60, 0xff & (~instr->carAttackDecay));
+	adlibWrite(channel + 0x80, 0xff & (~instr->carSustainRelease));
+	adlibWrite(channel + 0xE0, instr->carWaveformSelect);
+
+	adlibWrite((byte)chan + 0xC0, instr->feedback
+#ifdef ENABLE_OPL3
+			| (_opl3Mode ? 0x30 : 0)
+#endif
+			);
+}
+
+#ifdef ENABLE_OPL3
+void MidiDriver_ADLIB::adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan) {
+	assert(chan >= 0 && chan < 9);
+	assert(_opl3Mode);
+
+	byte channel = g_operator1Offsets[chan];
+	adlibWriteSecondary(channel + 0x20, instr->modCharacteristic);
+	adlibWriteSecondary(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1);
+	adlibWriteSecondary(channel + 0x60, 0xff & (~instr->modAttackDecay));
+	adlibWriteSecondary(channel + 0x80, 0xff & (~instr->modSustainRelease));
+	adlibWriteSecondary(channel + 0xE0, instr->modWaveformSelect);
+
+	channel = g_operator2Offsets[chan];
+	adlibWriteSecondary(channel + 0x20, instr->carCharacteristic);
+	adlibWriteSecondary(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2);
+	adlibWriteSecondary(channel + 0x60, 0xff & (~instr->carAttackDecay));
+	adlibWriteSecondary(channel + 0x80, 0xff & (~instr->carSustainRelease));
+	adlibWriteSecondary(channel + 0xE0, instr->carWaveformSelect);
+
+	// The original uses the following (strange) behavior:
+#if 0
+	if (instr->feedback | (pan > 64)) {
+		adlibWriteSecondary((byte)chan + 0xC0, 0x20);
+	} else {
+		adlibWriteSecondary((byte)chan + 0xC0, 0x10);
+	}
+#else
+	adlibWriteSecondary((byte)chan + 0xC0, instr->feedback | ((pan > 64) ? 0x20 : 0x10));
+#endif
+}
+#endif
+
+void MidiDriver_ADLIB::mcInitStuff(AdLibVoice *voice, Struct10 *s10,
+									 Struct11 *s11, byte flags, const InstrumentExtra *ie) {
+	AdLibPart *part = voice->_part;
+	s11->modifyVal = 0;
+	s11->flag0x40 = flags & 0x40;
+	s10->loop = flags & 0x20;
+	s11->flag0x10 = flags & 0x10;
+	s11->param = g_paramTable1[flags & 0xF];
+	s10->maxValue = g_maxValTable[flags & 0xF];
+	s10->unk3 = 31;
+	if (s11->flag0x40) {
+		s10->modWheel = part->_modWheel >> 2;
+	} else {
+		s10->modWheel = 31;
+	}
+
+	switch (s11->param) {
+	case 0:
+		s10->startValue = voice->_vol2;
+		break;
+	case 13:
+		s10->startValue = voice->_vol1;
+		break;
+	case 30:
+		s10->startValue = 31;
+		s11->s10->modWheel = 0;
+		break;
+	case 31:
+		s10->startValue = 0;
+		s11->s10->unk3 = 0;
+		break;
+	default:
+		s10->startValue = adlibGetRegValueParam(voice->_channel, s11->param);
+	}
+
+	struct10Init(s10, ie);
+}
+
+void MidiDriver_ADLIB::struct10Init(Struct10 *s10, const InstrumentExtra *ie) {
+	s10->active = 1;
+	if (!_scummSmallHeader) {
+		s10->curVal = 0;
+	} else {
+		s10->curVal = s10->startValue;
+		s10->startValue = 0;
+	}
+	s10->modWheelLast = 31;
+	s10->count = ie->a;
+	if (s10->count)
+		s10->count *= 63;
+	s10->tableA[0] = ie->b;
+	s10->tableA[1] = ie->d;
+	s10->tableA[2] = ie->f;
+	s10->tableA[3] = ie->g;
+
+	s10->tableB[0] = ie->c;
+	s10->tableB[1] = ie->e;
+	s10->tableB[2] = 0;
+	s10->tableB[3] = ie->h;
+
+	struct10Setup(s10);
+}
+
+int MidiDriver_ADLIB::adlibGetRegValueParam(int chan, byte param) {
+	const AdLibSetParams *as;
+	byte val;
+	byte channel;
+
+	assert(chan >= 0 && chan < 9);
+
+	if (param <= 12) {
+		channel = g_operator2Offsets[chan];
+	} else if (param <= 25) {
+		param -= 13;
+		channel = g_operator1Offsets[chan];
+	} else if (param <= 27) {
+		param -= 13;
+		channel = chan;
+	} else if (param == 28) {
+		return 0xF;
+	} else if (param == 29) {
+		return 0x17F;
+	} else {
+		return 0;
+	}
+
+	as = &g_setParamTable[param];
+	val = adlibGetRegValue(channel + as->registerBase);
+	val &= as->mask;
+	val >>= as->shift;
+	if (as->inversion)
+		val = as->inversion - val;
+
+	return val;
+}
+
+void MidiDriver_ADLIB::adlibNoteOn(int chan, byte note, int mod) {
+#ifdef ENABLE_OPL3
+	if (_opl3Mode) {
+		adlibNoteOnEx(chan, note, mod);
+		return;
+	}
+#endif
+
+	assert(chan >= 0 && chan < 9);
+	int code = (note << 7) + mod;
+	_curNotTable[chan] = code;
+	adlibPlayNote(chan, (int16)_channelTable2[chan] + code);
+}
+
+void MidiDriver_ADLIB::adlibNoteOnEx(int chan, byte note, int mod) {
+	assert(chan >= 0 && chan < 9);
+
+#ifdef ENABLE_OPL3
+	if (_opl3Mode) {
+		const int noteAdjusted = note + (mod >> 8) - 7;
+		const int pitchAdjust = (mod >> 5) & 7;
+
+		adlibWrite(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]);
+		adlibWriteSecondary(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]);
+		adlibWrite(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20);
+		adlibWriteSecondary(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20);
+	} else {
+#endif
+		int code = (note << 7) + mod;
+		_curNotTable[chan] = code;
+		_channelTable2[chan] = 0;
+		adlibPlayNote(chan, code);
+#ifdef ENABLE_OPL3
+	}
+#endif
+}
+
+// Plugin interface
+
+class AdLibEmuMusicPlugin : public MusicPluginObject {
+public:
+	const char *getName() const {
+		return _s("AdLib Emulator");
+	}
+
+	const char *getId() const {
+		return "adlib";
+	}
+
+	MusicDevices getDevices() const;
+	Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const;
+};
+
+MusicDevices AdLibEmuMusicPlugin::getDevices() const {
+	MusicDevices devices;
+	devices.push_back(MusicDevice(this, "", MT_ADLIB));
+	return devices;
+}
+
+Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
+	*mididriver = new MidiDriver_ADLIB();
+
+	return Common::kNoError;
+}
+
+//#if PLUGIN_ENABLED_DYNAMIC(ADLIB)
+	//REGISTER_PLUGIN_DYNAMIC(ADLIB, PLUGIN_TYPE_MUSIC, AdLibEmuMusicPlugin);
+//#else
+	REGISTER_PLUGIN_STATIC(ADLIB, PLUGIN_TYPE_MUSIC, AdLibEmuMusicPlugin);
+//#endif
diff --git a/audio/module.mk b/audio/module.mk
index 1539163..7743c49 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -1,6 +1,7 @@
 MODULE := audio
 
 MODULE_OBJS := \
+	adlib.o \
 	audiostream.o \
 	fmopl.o \
 	mididrv.o \
@@ -39,7 +40,6 @@ MODULE_OBJS := \
 	mods/rjp1.o \
 	mods/soundfx.o \
 	mods/tfmx.o \
-	softsynth/adlib.o \
 	softsynth/cms.o \
 	softsynth/opl/dbopl.o \
 	softsynth/opl/dosbox.o \
diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp
deleted file mode 100644
index f609164..0000000
--- a/audio/softsynth/adlib.cpp
+++ /dev/null
@@ -1,2318 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "audio/softsynth/emumidi.h"
-#include "common/debug.h"
-#include "common/error.h"
-#include "common/scummsys.h"
-#include "common/system.h"
-#include "common/textconsole.h"
-#include "common/types.h"
-#include "common/util.h"
-#include "audio/fmopl.h"
-#include "audio/musicplugin.h"
-#include "common/translation.h"
-
-#ifdef DEBUG_ADLIB
-static int g_tick;
-#endif
-
-// Only include OPL3 when we actually have an AdLib emulator builtin, which
-// supports OPL3.
-#ifndef DISABLE_DOSBOX_OPL
-#define ENABLE_OPL3
-#endif
-
-class MidiDriver_ADLIB;
-struct AdLibVoice;
-
-// We use packing for the following two structs, because the code
-// does simply copy them over from byte streams, without any
-// serialization. Check AdLibPart::sysEx_customInstrument for an
-// example of this.
-//
-// It might be very well possible, that none of the compilers we support
-// add any padding bytes at all, since the structs contain only variables
-// of the type 'byte'. But better safe than sorry.
-#include "common/pack-start.h"
-struct InstrumentExtra {
-	byte a, b, c, d, e, f, g, h;
-} PACKED_STRUCT;
-
-struct AdLibInstrument {
-	byte modCharacteristic;
-	byte modScalingOutputLevel;
-	byte modAttackDecay;
-	byte modSustainRelease;
-	byte modWaveformSelect;
-	byte carCharacteristic;
-	byte carScalingOutputLevel;
-	byte carAttackDecay;
-	byte carSustainRelease;
-	byte carWaveformSelect;
-	byte feedback;
-	byte flagsA;
-	InstrumentExtra extraA;
-	byte flagsB;
-	InstrumentExtra extraB;
-	byte duration;
-} PACKED_STRUCT;
-#include "common/pack-end.h"
-
-class AdLibPart : public MidiChannel {
-	friend class MidiDriver_ADLIB;
-
-protected:
-//	AdLibPart *_prev, *_next;
-	AdLibVoice *_voice;
-	int16 _pitchBend;
-	byte _pitchBendFactor;
-	//int8 _transposeEff;
-	byte _volEff;
-	int8 _detuneEff;
-	byte _modWheel;
-	bool _pedal;
-	byte _program;
-	byte _priEff;
-	byte _pan;
-	AdLibInstrument _partInstr;
-#ifdef ENABLE_OPL3
-	AdLibInstrument _partInstrSecondary;
-#endif
-
-protected:
-	MidiDriver_ADLIB *_owner;
-	bool _allocated;
-	byte _channel;
-
-	void init(MidiDriver_ADLIB *owner, byte channel);
-	void allocate() { _allocated = true; }
-
-public:
-	AdLibPart() {
-		_voice = 0;
-		_pitchBend = 0;
-		_pitchBendFactor = 2;
-		//_transposeEff = 0;
-		_volEff = 0;
-		_detuneEff = 0;
-		_modWheel = 0;
-		_pedal = 0;
-		_program = 0;
-		_priEff = 0;
-		_pan = 64;
-
-		_owner = 0;
-		_allocated = false;
-		_channel = 0;
-
-		memset(&_partInstr, 0, sizeof(_partInstr));
-#ifdef ENABLE_OPL3
-		memset(&_partInstrSecondary, 0, sizeof(_partInstrSecondary));
-#endif
-	}
-
-	MidiDriver *device();
-	byte getNumber() { return _channel; }
-	void release() { _allocated = false; }
-
-	void send(uint32 b);
-
-	// Regular messages
-	void noteOff(byte note);
-	void noteOn(byte note, byte velocity);
-	void programChange(byte program);
-	void pitchBend(int16 bend);
-
-	// Control Change messages
-	void controlChange(byte control, byte value);
-	void modulationWheel(byte value);
-	void volume(byte value);
-	void panPosition(byte value);
-	void pitchBendFactor(byte value);
-	void detune(byte value);
-	void priority(byte value);
-	void sustain(bool value);
-	void effectLevel(byte value) { return; } // Not supported
-	void chorusLevel(byte value) { return; } // Not supported
-	void allNotesOff();
-
-	// SysEx messages
-	void sysEx_customInstrument(uint32 type, const byte *instr);
-};
-
-// FYI (Jamieson630)
-// It is assumed that any invocation to AdLibPercussionChannel
-// will be done through the MidiChannel base class as opposed to the
-// AdLibPart base class. If this were NOT the case, all the functions
-// listed below would need to be virtual in AdLibPart as well as MidiChannel.
-class AdLibPercussionChannel : public AdLibPart {
-	friend class MidiDriver_ADLIB;
-
-protected:
-	void init(MidiDriver_ADLIB *owner, byte channel);
-
-public:
-	~AdLibPercussionChannel();
-
-	void noteOff(byte note);
-	void noteOn(byte note, byte velocity);
-	void programChange(byte program) { }
-
-	// Control Change messages
-	void modulationWheel(byte value) { }
-	void pitchBendFactor(byte value) { }
-	void detune(byte value) { }
-	void priority(byte value) { }
-	void sustain(bool value) { }
-
-	// SysEx messages
-	void sysEx_customInstrument(uint32 type, const byte *instr);
-
-private:
-	byte _notes[256];
-	AdLibInstrument *_customInstruments[256];
-};
-
-struct Struct10 {
-	byte active;
-	int16 curVal;
-	int16 count;
-	uint16 maxValue;
-	int16 startValue;
-	byte loop;
-	byte tableA[4];
-	byte tableB[4];
-	int8 unk3;
-	int8 modWheel;
-	int8 modWheelLast;
-	uint16 speedLoMax;
-	uint16 numSteps;
-	int16 speedHi;
-	int8 direction;
-	uint16 speedLo;
-	uint16 speedLoCounter;
-};
-
-struct Struct11 {
-	int16 modifyVal;
-	byte param, flag0x40, flag0x10;
-	Struct10 *s10;
-};
-
-struct AdLibVoice {
-	AdLibPart *_part;
-	AdLibVoice *_next, *_prev;
-	byte _waitForPedal;
-	byte _note;
-	byte _channel;
-	byte _twoChan;
-	byte _vol1, _vol2;
-	int16 _duration;
-
-	Struct10 _s10a;
-	Struct11 _s11a;
-	Struct10 _s10b;
-	Struct11 _s11b;
-
-#ifdef ENABLE_OPL3
-	byte _secTwoChan;
-	byte _secVol1, _secVol2;
-#endif
-
-	AdLibVoice() { memset(this, 0, sizeof(AdLibVoice)); }
-};
-
-struct AdLibSetParams {
-	byte registerBase;
-	byte shift;
-	byte mask;
-	byte inversion;
-};
-
-static const byte g_operator1Offsets[9] = {
-	0, 1, 2, 8,
-	9, 10, 16, 17,
-	18
-};
-
-static const byte g_operator2Offsets[9] = {
-	3, 4, 5, 11,
-	12, 13, 19, 20,
-	21
-};
-
-static const AdLibSetParams g_setParamTable[] = {
-	{0x40, 0, 63, 63},  // level
-	{0xE0, 2, 0, 0},    // unused
-	{0x40, 6, 192, 0},  // level key scaling
-	{0x20, 0, 15, 0},   // modulator frequency multiple
-	{0x60, 4, 240, 15}, // attack rate
-	{0x60, 0, 15, 15},  // decay rate
-	{0x80, 4, 240, 15}, // sustain level
-	{0x80, 0, 15, 15},  // release rate
-	{0xE0, 0, 3, 0},    // waveformSelect select
-	{0x20, 7, 128, 0},  // amp mod
-	{0x20, 6, 64, 0},   // vib
-	{0x20, 5, 32, 0},   // eg typ
-	{0x20, 4, 16, 0},   // ksr
-	{0xC0, 0, 1, 0},    // decay alg
-	{0xC0, 1, 14, 0}    // feedback
-};
-
-static const byte g_paramTable1[16] = {
-	29, 28, 27, 0,
-	3, 4, 7, 8,
-	13, 16, 17, 20,
-	21, 30, 31, 0
-};
-
-static const uint16 g_maxValTable[16] = {
-	0x2FF, 0x1F, 0x7, 0x3F,
-	0x0F, 0x0F, 0x0F, 0x3,
-	0x3F, 0x0F, 0x0F, 0x0F,
-	0x3, 0x3E, 0x1F, 0
-};
-
-static const uint16 g_numStepsTable[] = {
-	1, 2, 4, 5,
-	6, 7, 8, 9,
-	10, 12, 14, 16,
-	18, 21, 24, 30,
-	36, 50, 64, 82,
-	100, 136, 160, 192,
-	240, 276, 340, 460,
-	600, 860, 1200, 1600
-};
-
-static const byte g_noteFrequencies[] = {
-	90, 91, 92, 92, 93, 94, 94, 95,
-	96, 96, 97, 98, 98, 99, 100, 101,
-	101, 102, 103, 104, 104, 105, 106, 107,
-	107, 108, 109, 110, 111, 111, 112, 113,
-	114, 115, 115, 116, 117, 118, 119, 120,
-	121, 121, 122, 123, 124, 125, 126, 127,
-	128, 129, 130, 131, 132, 132, 133, 134,
-	135, 136, 137, 138, 139, 140, 141, 142,
-	143, 145, 146, 147, 148, 149, 150, 151,
-	152, 153, 154, 155, 157, 158, 159, 160,
-	161, 162, 163, 165, 166, 167, 168, 169,
-	171, 172, 173, 174, 176, 177, 178, 180,
-	181, 182, 184, 185, 186, 188, 189, 190,
-	192, 193, 194, 196, 197, 199, 200, 202,
-	203, 205, 206, 208, 209, 211, 212, 214,
-	215, 217, 218, 220, 222, 223, 225, 226,
-	228, 230, 231, 233, 235, 236, 238, 240,
-	242, 243, 245, 247, 249, 251, 252, 254
-};
-
-static const AdLibInstrument g_gmInstruments[128] = {
-	// 0x00
-	{ 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
-	{ 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
-	{ 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A },
-	{ 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	// 0x10
-	{ 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
-	{ 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
-	{ 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 },
-	{ 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A },
-	{ 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B },
-	// 0x20
-	{ 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 },
-	{ 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
-	{ 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
-	{ 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	{ 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	// 0x30
-	{ 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	{ 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	// 0x40
-	{ 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	// 0x50
-	{ 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	// 0x60
-	{ 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C },
-	{ 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 },
-	{ 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
-	{ 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	{ 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
-	{ 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	{ 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	// 0x70
-	{ 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	{ 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	{ 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	{ 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	{ 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F },
-	{ 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	{ 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	{ 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 },
-	{ 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	{ 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	{ 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }
-};
-
-static AdLibInstrument g_gmPercussionInstruments[39] = {
-	{ 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	{ 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	{ 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 },
-	{ 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
-	{ 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
-	{ 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
-	{ 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	{ 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	{ 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
-	{ 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	{ 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	{ 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	{ 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	{ 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	{ 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
-	{ 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
-	{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
-	{ 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	{ 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	{ 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	{ 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }
-};
-
-#ifdef ENABLE_OPL3
-static const AdLibInstrument g_gmInstrumentsOPL3[128][2] = {
-	{ { 0xC2, 0xC2, 0x0A, 0x6B, 0xA0, 0xC2, 0x08, 0x0D, 0x88, 0xC8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
-	  { 0x02, 0x00, 0x0C, 0x78, 0x61, 0x04, 0x4C, 0x0B, 0x9A, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 } },
-	{ { 0x22, 0x53, 0x0E, 0x8A, 0x60, 0x14, 0x06, 0x1D, 0x7A, 0xB8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x22, 0x5A, 0x0E, 0x8A, 0x40, 0x14, 0x2F, 0x0E, 0x7A, 0x88, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x06, 0x00, 0x1C, 0x79, 0x70, 0x02, 0x00, 0x4B, 0x79, 0xA8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x06, 0x00, 0x1A, 0x79, 0x60, 0x02, 0x00, 0x4C, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC2, 0x80, 0x0B, 0x89, 0x90, 0xC2, 0x06, 0x1B, 0xA8, 0xB0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 },
-	  { 0x04, 0x28, 0x5D, 0xB8, 0x01, 0x02, 0x00, 0x3C, 0x70, 0x88, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0xD3, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x87, 0x40, 0x3A, 0x5A, 0x94, 0x82, 0x04, 0x3D, 0x59, 0xAC, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x8C, 0x80, 0x05, 0xEA, 0xA9, 0x82, 0x04, 0x3D, 0xAA, 0xB0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x8C, 0x80, 0x06, 0x98, 0xA9, 0x86, 0x10, 0x36, 0x7A, 0xFD, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x85, 0x40, 0x0D, 0xEC, 0xE1, 0x84, 0x58, 0x3E, 0xCB, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x84, 0x40, 0x0D, 0xEB, 0xE0, 0x84, 0x48, 0x3E, 0xCA, 0xC0, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC9, 0x40, 0x2B, 0x78, 0x8A, 0xC2, 0x0A, 0x4C, 0x8A, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A },
-	  { 0xCA, 0x40, 0x47, 0xCA, 0xB4, 0xC2, 0x00, 0x57, 0x8A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A } },
-	{ { 0x2A, 0x0E, 0x17, 0x89, 0x50, 0x22, 0x0C, 0x1B, 0x09, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x2A, 0x1A, 0x19, 0x8A, 0x00, 0x22, 0x38, 0x0B, 0x0A, 0x00, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x06, 0x0A, 0x08, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x2F, 0x0A, 0x08, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC5, 0x0A, 0x05, 0xDC, 0xB8, 0x84, 0x06, 0x00, 0xEC, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x09, 0x10, 0x04, 0x5B, 0xA5, 0x02, 0x08, 0x00, 0xEC, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xED, 0x0F, 0x5B, 0xC8, 0xC8, 0xE2, 0x9F, 0x4A, 0xE9, 0xF9, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE6, 0x40, 0x0A, 0xA7, 0x64, 0xE2, 0x8B, 0x6A, 0x79, 0xB1, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE8, 0x4F, 0x3A, 0xD7, 0xF8, 0xE2, 0x97, 0x49, 0xF9, 0xF9, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC9, 0x02, 0x16, 0x9A, 0xAB, 0xC4, 0x15, 0x46, 0xBA, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE1, 0x08, 0x2F, 0xF7, 0xE1, 0xF3, 0x42, 0x8F, 0xC7, 0xC2, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE3, 0x00, 0x2D, 0xF7, 0xC1, 0xE4, 0x40, 0x7F, 0xC7, 0xD2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x01, 0x8C, 0x9F, 0xDA, 0xE8, 0xE4, 0x50, 0x9F, 0xDA, 0xF2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x02, 0x80, 0x9F, 0xDA, 0x00, 0xE3, 0x50, 0x9F, 0xD9, 0xFA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE5, 0x0F, 0x7D, 0xB8, 0x5A, 0xA2, 0x0C, 0x7C, 0xC7, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x06, 0x4C, 0xAC, 0x56, 0x31, 0x02, 0x08, 0x8D, 0x46, 0xDC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0xC2, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xF2, 0x00, 0x9F, 0xDB, 0xA9, 0xE1, 0x00, 0x8F, 0xD7, 0xBA, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x88, 0x9C, 0x50, 0xC8, 0xE2, 0x18, 0x70, 0xC4, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE6, 0x00, 0x9C, 0x50, 0xB0, 0xE4, 0x00, 0x70, 0xC4, 0xA0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
-	  { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } },
-	{ { 0x42, 0x53, 0x3E, 0xEB, 0x48, 0xD4, 0x05, 0x1D, 0xA9, 0xC9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 },
-	  { 0x42, 0x54, 0x6F, 0xEB, 0x61, 0xD4, 0x02, 0x2E, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } },
-	{ { 0xC2, 0x00, 0x59, 0x17, 0xB1, 0xC2, 0x1E, 0x6D, 0x98, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 },
-	  { 0xC2, 0x00, 0x08, 0xB3, 0x99, 0xC2, 0x06, 0x2B, 0x58, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 } },
-	{ { 0xC6, 0x01, 0x2D, 0xA7, 0x88, 0xC2, 0x08, 0x0E, 0xA7, 0xC1, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC4, 0x00, 0x2D, 0xA7, 0x91, 0xC2, 0x02, 0x0E, 0xA7, 0xD1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC2, 0x0C, 0x06, 0x06, 0xA9, 0xC2, 0x3F, 0x08, 0xB8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A },
-	  { 0xC1, 0x00, 0x68, 0x50, 0xB8, 0xC2, 0x00, 0x48, 0x84, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A } },
-	{ { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0xB1, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC2, 0x2F, 0x6F, 0x79, 0x00, 0xC8, 0x0F, 0x5E, 0x98, 0xB9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B },
-	  { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B } },
-	{ { 0xC2, 0x41, 0x3D, 0x96, 0x88, 0xC4, 0xCA, 0x0E, 0xC7, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 },
-	  { 0xC2, 0x04, 0x58, 0xC9, 0x90, 0xC2, 0x94, 0x2C, 0xB9, 0xF0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 } },
-	{ { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
-	  { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } },
-	{ { 0xF2, 0x0A, 0x0D, 0xD7, 0x80, 0xE4, 0x1F, 0x5E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xD2, 0x06, 0x9A, 0xD7, 0xA0, 0xC2, 0x1F, 0x59, 0xB8, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 },
-	  { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } },
-	{ { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE1, 0x00, 0x4E, 0xDB, 0x92, 0xE3, 0x18, 0x6F, 0xE9, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x6F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x02, 0x0F, 0x66, 0xAA, 0xA1, 0x02, 0x64, 0x29, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	  { 0x02, 0x00, 0x65, 0xAA, 0xF1, 0x02, 0x4A, 0x28, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
-	{ { 0x16, 0x4A, 0x04, 0xBA, 0x71, 0xC2, 0x48, 0x2E, 0xCA, 0xF0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x14, 0xC0, 0x66, 0x08, 0x90, 0xC2, 0x48, 0x2C, 0x0A, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0x02, 0x0A, 0x01, 0x7A, 0xB1, 0x02, 0x12, 0x2A, 0xEA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x02, 0x06, 0x75, 0x05, 0xB1, 0x01, 0x3F, 0x28, 0xEA, 0xF9, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x16 } },
-	{ { 0x62, 0x53, 0x9C, 0xBA, 0x61, 0x62, 0x5A, 0xAD, 0xCA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xF2, 0x40, 0x9F, 0x8A, 0x98, 0xE2, 0x11, 0x7F, 0xB8, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE6, 0x80, 0x9C, 0x99, 0x82, 0xE2, 0x04, 0x8D, 0x78, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE0, 0x44, 0x8A, 0xA9, 0x5B, 0xE1, 0x06, 0x8D, 0x79, 0xBA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE8, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x06, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	  { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
-	{ { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xF2, 0x02, 0xAF, 0xFB, 0x90, 0xF6, 0x54, 0x8F, 0xE9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x00, 0x9F, 0xFA, 0xB0, 0xF2, 0x58, 0x7F, 0xEA, 0xF8, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xF2, 0x00, 0xAF, 0x88, 0xA8, 0xF2, 0x46, 0x6E, 0xC9, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xD2, 0x00, 0x7B, 0x88, 0xA8, 0xD2, 0x4C, 0x69, 0xE9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x8F, 0x4E, 0x78, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xD2, 0x02, 0x85, 0x89, 0xC8, 0xD2, 0x94, 0x77, 0x49, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x02, 0x9F, 0xB8, 0x90, 0x22, 0x8A, 0x9F, 0xE8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC2, 0x00, 0x86, 0xB8, 0x98, 0x02, 0x8F, 0x89, 0xE8, 0xF9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x1A, 0x90, 0xF8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xD2, 0x10, 0x69, 0x18, 0xCF, 0xD4, 0x14, 0x5B, 0x04, 0xFD, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x00, 0x5F, 0xF9, 0x88, 0xE4, 0x9E, 0x8F, 0xF8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC2, 0x00, 0x97, 0xF9, 0x90, 0xC9, 0x80, 0x69, 0x98, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x62, 0x0C, 0x6E, 0xD8, 0x79, 0x2A, 0x09, 0x7D, 0xD8, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x02, 0x04, 0x8A, 0xD8, 0x80, 0x0C, 0x12, 0x85, 0xD8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x00, 0x7E, 0x89, 0x70, 0xE6, 0x8F, 0x80, 0xF8, 0xF0, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC4, 0x00, 0x67, 0x59, 0x70, 0xC6, 0x8A, 0x77, 0xA8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x80, 0x88, 0x48, 0x98, 0xE2, 0x1E, 0x8E, 0xC9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xF2, 0x40, 0xA8, 0xB9, 0x80, 0xE2, 0x0C, 0x89, 0x09, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x80, 0x86, 0xB9, 0xA8, 0xE2, 0x14, 0x9F, 0xD7, 0xB0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC2, 0x80, 0x94, 0x09, 0x78, 0xC2, 0x00, 0x97, 0x97, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x00, 0x68, 0x68, 0xAA, 0xE2, 0x0A, 0x9B, 0xB3, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC2, 0x00, 0x86, 0x68, 0xA0, 0xC2, 0x00, 0x77, 0x47, 0xE0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x00, 0xA6, 0x87, 0x81, 0xE2, 0x0A, 0x7E, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x00, 0x89, 0x40, 0x79, 0xE2, 0x00, 0x7E, 0xC9, 0x90, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x80, 0xAA, 0xB8, 0x90, 0xE2, 0x00, 0x9E, 0xF9, 0xC0, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE6, 0x80, 0x9D, 0xB8, 0x51, 0xE2, 0x00, 0x9E, 0xF9, 0xA0, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE6, 0x00, 0x6F, 0xDA, 0xC9, 0xE2, 0x05, 0x2F, 0xD8, 0xAA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x00, 0x4F, 0xDA, 0xC8, 0xE2, 0x00, 0x0F, 0xD8, 0xD0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xEB, 0x80, 0x8C, 0xCB, 0x72, 0xE2, 0x86, 0xAF, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xEB, 0xC3, 0x9C, 0xCB, 0xA2, 0xE2, 0x4C, 0xAE, 0xCA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE5, 0x40, 0xDB, 0x3B, 0x78, 0xE2, 0x80, 0xBE, 0xCA, 0xE1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x80, 0x8E, 0xCB, 0xC0, 0xE2, 0x90, 0xAE, 0xCA, 0xFB, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE7, 0x40, 0xEB, 0xCA, 0x80, 0xE2, 0x03, 0xBF, 0xBA, 0xC2, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE3, 0x80, 0xDB, 0xCA, 0x40, 0xE2, 0x08, 0xDF, 0xBA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x61, 0x00, 0xBE, 0x99, 0xFA, 0xE3, 0x40, 0xCF, 0xCA, 0xF9, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x62, 0x00, 0xCE, 0x9A, 0xA8, 0xE2, 0x45, 0xCF, 0xCA, 0xA0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C },
-	  { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C } },
-	{ { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xEC, 0x00, 0x7D, 0xDA, 0x80, 0xE2, 0x00, 0x5E, 0x9B, 0xA8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE6, 0x0A, 0x4C, 0xC9, 0x60, 0xE2, 0x07, 0x0C, 0x7A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE9, 0xC0, 0xEE, 0xD8, 0x83, 0xE2, 0x05, 0xDD, 0xAA, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xED, 0x48, 0xDE, 0xD8, 0xB4, 0xE1, 0x00, 0xDD, 0xAA, 0xA9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xDA, 0x00, 0x8F, 0xAC, 0x92, 0x22, 0x05, 0x8D, 0x8A, 0xE9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xEF, 0x00, 0x8C, 0xAA, 0x67, 0x25, 0x00, 0x9D, 0xAB, 0xC1, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x62, 0x82, 0xCB, 0x7A, 0xD8, 0xE6, 0x56, 0xAF, 0xDB, 0xE0, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x62, 0x84, 0xBB, 0xAA, 0xCA, 0xCF, 0x41, 0xAC, 0xDA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xC2, 0x41, 0xAC, 0xBB, 0xBB, 0xC2, 0x85, 0x0E, 0xCB, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 },
-	  { 0xC2, 0x03, 0x6A, 0x5B, 0xA4, 0xC2, 0x0D, 0x2A, 0xBB, 0xFC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 } },
-	{ { 0x75, 0x00, 0x0E, 0xBB, 0xB2, 0xE2, 0x1E, 0x0A, 0xA9, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
-	  { 0x62, 0x00, 0x04, 0x9A, 0xE8, 0xE2, 0x00, 0x0A, 0x48, 0xFD, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } },
-	{ { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	  { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
-	{ { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
-	  { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } },
-	{ { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	  { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
-	{ { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0xC9, 0x0C, 0x88, 0xD9, 0x6A, 0xC2, 0x14, 0x3A, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0xC5, 0x00, 0x98, 0xD9, 0x92, 0xC1, 0x16, 0x6E, 0xF9, 0xE8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
-	{ { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x01, 0x0C, 0x44, 0xE6, 0xE8, 0x01, 0x3F, 0x0C, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	  { 0x02, 0x3F, 0x05, 0x08, 0xF8, 0x03, 0x3F, 0x3C, 0xF9, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
-	{ { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
-	{ { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
-	{ { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F },
-	  { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F } },
-	{ { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	  { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
-	{ { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	  { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
-	{ { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 },
-	  { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 } },
-	{ { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	  { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
-	{ { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
-	{ { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }
-};
-
-static const AdLibInstrument g_gmPercussionInstrumentsOPL3[39][2] = {
-	{ { 0x1A, 0x3F, 0x15, 0x05, 0xF8, 0x02, 0x21, 0x2B, 0xE4, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	  { 0x11, 0x18, 0x15, 0x00, 0xF8, 0x12, 0x00, 0x2B, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
-	{ { 0x11, 0x12, 0x04, 0x07, 0xF8, 0x02, 0x18, 0x0B, 0xE5, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0x11, 0x28, 0x06, 0x04, 0xF8, 0x02, 0x1E, 0x1B, 0x02, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
-	{ { 0x0A, 0x3F, 0x0B, 0x01, 0xF8, 0x1F, 0x13, 0x46, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 },
-	  { 0x04, 0x18, 0x06, 0x01, 0xB0, 0x10, 0x00, 0x07, 0x00, 0x90, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 } },
-	{ { 0x00, 0x3F, 0x0F, 0x00, 0xF8, 0x10, 0x0A, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x02, 0x14, 0x04, 0x00, 0xC0, 0x11, 0x08, 0x07, 0x00, 0xC6, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x0F, 0x3F, 0x0B, 0x00, 0xF8, 0x1F, 0x07, 0x19, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x0E, 0x32, 0x76, 0x03, 0xF8, 0x1F, 0x0F, 0x77, 0xD4, 0xFC, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x00, 0x3F, 0x1F, 0x00, 0xFA, 0x1F, 0x0C, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x07, 0x11, 0x13, 0x00, 0xA0, 0x13, 0x00, 0x07, 0x00, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x4A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x02, 0x22, 0x05, 0xB6, 0xF8, 0x04, 0x0A, 0x59, 0x03, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0xCF, 0x7F, 0x08, 0xFF, 0xFA, 0x00, 0xC0, 0x2D, 0xF7, 0xE3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0xD2, 0x7F, 0x04, 0x0F, 0xFA, 0x10, 0xCD, 0x24, 0x07, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x43, 0x17, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x12, 0x13, 0x09, 0x96, 0xF8, 0x44, 0x0A, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0xCF, 0x7F, 0x08, 0xCF, 0xFA, 0x00, 0x40, 0x2A, 0xF8, 0x8B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
-	  { 0xCF, 0x7F, 0x05, 0x07, 0xFA, 0x00, 0x40, 0x25, 0x08, 0xC3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } },
-	{ { 0x12, 0x3F, 0x06, 0x17, 0xF8, 0x03, 0x1D, 0x0B, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x12, 0x1A, 0x08, 0x96, 0xF8, 0x44, 0x00, 0x08, 0x03, 0xF8, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0xCF, 0x7F, 0x08, 0xCD, 0xFA, 0x00, 0x40, 0x1A, 0x69, 0xB3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C },
-	  { 0xCD, 0x3F, 0x36, 0x05, 0xFC, 0x0F, 0x47, 0x46, 0x06, 0xDF, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } },
-	{ { 0x13, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x0D, 0x0A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x12, 0x14, 0x09, 0x96, 0xF8, 0x44, 0x02, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0x15, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x12, 0x00, 0x07, 0x96, 0xE8, 0x44, 0x02, 0x08, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x16, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
-	  { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x1E, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } },
-	{ { 0x17, 0x3F, 0x04, 0x09, 0xF8, 0x03, 0x18, 0x0D, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x12, 0x00, 0x07, 0x96, 0xF8, 0x44, 0x02, 0x08, 0xF9, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0xCF, 0x3F, 0x0F, 0x5E, 0xF8, 0xC6, 0x0C, 0x00, 0xCA, 0xFB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0xCF, 0x3F, 0x04, 0x57, 0xF8, 0xC5, 0x13, 0x06, 0x05, 0xFF, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0xCF, 0x3F, 0x7E, 0x9D, 0xF8, 0xC8, 0xC0, 0x0A, 0xBA, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 },
-	  { 0xCF, 0x3F, 0x77, 0x09, 0xF8, 0xC2, 0xC0, 0x08, 0xB5, 0xEA, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } },
-	{ { 0xCF, 0x3F, 0x4D, 0x9F, 0xF8, 0xC6, 0x00, 0x08, 0xDA, 0xAB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	  { 0xCF, 0x3F, 0x47, 0x06, 0xF8, 0xCD, 0x00, 0x07, 0x05, 0xB3, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
-	{ { 0xCF, 0x3F, 0x5D, 0xAA, 0xF2, 0xC0, 0x8A, 0x67, 0x99, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0xCF, 0x3F, 0x9A, 0x69, 0xF8, 0xCF, 0x88, 0x88, 0x48, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0xCF, 0x3F, 0x4A, 0xFD, 0xF8, 0xCF, 0x00, 0x59, 0xEA, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0xCF, 0x3F, 0x48, 0x06, 0xF8, 0xCF, 0x00, 0x54, 0x04, 0xF9, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x0F, 0x18, 0x0A, 0xFA, 0xAB, 0x06, 0x06, 0x06, 0x39, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x03, 0x18, 0x04, 0x09, 0xAC, 0x05, 0x07, 0x08, 0x07, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0xCF, 0x3F, 0x2B, 0xFC, 0xF8, 0xCC, 0xC4, 0x0B, 0xEA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 },
-	  { 0xCF, 0x3F, 0x25, 0x06, 0xF8, 0xCC, 0xD7, 0x05, 0x02, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } },
-	{ { 0x05, 0x1A, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x0C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	  { 0x01, 0x00, 0x09, 0x08, 0x40, 0x13, 0x00, 0x2A, 0x0A, 0xD8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
-	{ { 0x04, 0x19, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x2C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 },
-	  { 0x04, 0x00, 0x07, 0x08, 0x40, 0x12, 0x00, 0x29, 0x08, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } },
-	{ { 0x04, 0x0A, 0x04, 0x00, 0xD8, 0x01, 0x02, 0x0D, 0xFA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 },
-	  { 0x04, 0x00, 0x03, 0x09, 0x93, 0x02, 0x00, 0x28, 0x09, 0xE8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } },
-	{ { 0x15, 0x14, 0x05, 0x00, 0xF9, 0x01, 0x03, 0x5C, 0xE9, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0x05, 0x00, 0x03, 0x03, 0x49, 0x02, 0x00, 0x58, 0x08, 0xE0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
-	{ { 0x10, 0x10, 0x05, 0x08, 0xF8, 0x01, 0x03, 0x0D, 0xEA, 0xE8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 },
-	  { 0x10, 0x00, 0x0C, 0x0C, 0x48, 0x02, 0x00, 0x08, 0xB9, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } },
-	{ { 0x11, 0x00, 0x06, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
-	  { 0x15, 0x00, 0x04, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } },
-	{ { 0x13, 0x26, 0x04, 0x6A, 0xFB, 0x01, 0x00, 0x08, 0x5A, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 },
-	  { 0x12, 0x26, 0x03, 0x6A, 0xFB, 0x02, 0x00, 0x06, 0x5A, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } },
-	{ { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC4, 0x00, 0x18, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0xCF, 0x4E, 0x05, 0xA6, 0xA0, 0xC6, 0x00, 0x16, 0xF8, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC3, 0x00, 0x18, 0xF8, 0x98, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0xCF, 0x4E, 0x06, 0xAA, 0xA0, 0xC5, 0x00, 0x19, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0xCB, 0x3F, 0x8F, 0x00, 0xFA, 0xC5, 0x06, 0x98, 0xD6, 0xBB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D },
-	  { 0xC0, 0x00, 0xF0, 0x00, 0x00, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } },
-	{ { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x19, 0x0B, 0x55, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x1B, 0x10, 0x57, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x05, 0x11, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xA8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x02, 0x11, 0x13, 0x00, 0xC8, 0x02, 0x00, 0x05, 0x00, 0x80, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0x04, 0x08, 0x15, 0x00, 0x90, 0x01, 0x00, 0x08, 0x00, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 },
-	  { 0x03, 0x08, 0x14, 0x00, 0x90, 0x02, 0x00, 0x07, 0x00, 0xA8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } },
-	{ { 0xDA, 0x00, 0x53, 0x30, 0xC0, 0x07, 0x10, 0x49, 0xC4, 0xDA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0xD2, 0x00, 0x56, 0x30, 0x90, 0x06, 0x00, 0x46, 0x56, 0x62, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } },
-	{ { 0x1C, 0x00, 0x07, 0xBC, 0xC8, 0x0C, 0x0A, 0x0B, 0x6A, 0xF2, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 },
-	  { 0x18, 0x00, 0x07, 0xBC, 0x88, 0x09, 0x00, 0x0B, 0x6A, 0xBA, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } },
-	{ { 0x0A, 0x0E, 0x7F, 0x00, 0xF9, 0x13, 0x16, 0x28, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 },
-	  { 0x01, 0x0E, 0x54, 0x00, 0xF9, 0x15, 0x03, 0x27, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }
-};
-#endif
-
-static const byte g_gmPercussionInstrumentMap[128] = {
-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-	0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
-	0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0xFF, 0xFF, 0x17, 0x18, 0x19, 0x1A,
-	0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x21, 0x22, 0x23, 0xFF, 0xFF,
-	0x24, 0x25, 0xFF, 0xFF, 0xFF, 0x26, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-};
-
-static byte g_volumeLookupTable[64][32];
-
-static const byte g_volumeTable[] = {
-	0, 4, 7, 11,
-	13, 16, 18, 20,
-	22, 24, 26, 27,
-	29, 30, 31, 33,
-	34, 35, 36, 37,
-	38, 39, 40, 41,
-	42, 43, 44, 44,
-	45, 46, 47, 47,
-	48, 49, 49, 50,
-	51, 51, 52, 53,
-	53, 54, 54, 55,
-	55, 56, 56, 57,
-	57, 58, 58, 59,
-	59, 60, 60, 60,
-	61, 61, 62, 62,
-	62, 63, 63, 63
-};
-
-static int lookupVolume(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 g_volumeLookupTable[-a][-b];
-		} else {
-			return -g_volumeLookupTable[a][-b];
-		}
-	} else {
-		if (a < 0) {
-			return -g_volumeLookupTable[-a][b];
-		} else {
-			return g_volumeLookupTable[a][b];
-		}
-	}
-}
-
-static void createLookupTable() {
-	int i, j;
-	int sum;
-
-	for (i = 0; i < 64; i++) {
-		sum = i;
-		for (j = 0; j < 32; j++) {
-			g_volumeLookupTable[i][j] = sum >> 5;
-			sum += i;
-		}
-	}
-	for (i = 0; i < 64; i++)
-		g_volumeLookupTable[i][0] = 0;
-}
-
-////////////////////////////////////////
-//
-// AdLib MIDI driver
-//
-////////////////////////////////////////
-
-class MidiDriver_ADLIB : public MidiDriver {
-	friend class AdLibPart;
-	friend class AdLibPercussionChannel;
-
-public:
-	MidiDriver_ADLIB();
-
-	int open();
-	void close();
-	void send(uint32 b);
-	void send(byte channel, uint32 b); // Supports higher than channel 15
-	uint32 property(int prop, uint32 param);
-	bool isOpen() const { return _isOpen; }
-	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
-
-	void setPitchBendRange(byte channel, uint range);
-	void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
-
-	MidiChannel *allocateChannel();
-	MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported
-
-	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
-
-private:
-	bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games
-#ifdef ENABLE_OPL3
-	bool _opl3Mode;
-#endif
-
-	OPL::OPL *_opl;
-	byte *_regCache;
-#ifdef ENABLE_OPL3
-	byte *_regCacheSecondary;
-#endif
-
-	Common::TimerManager::TimerProc _adlibTimerProc;
-	void *_adlibTimerParam;
-
-	int _timerCounter;
-
-	uint16 _channelTable2[9];
-	int _voiceIndex;
-	int _timerIncrease;
-	int _timerThreshold;
-	uint16 _curNotTable[9];
-	AdLibVoice _voices[9];
-	AdLibPart _parts[32];
-	AdLibPercussionChannel _percussion;
-
-	bool _isOpen;
-
-	void onTimer();
-	void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
-	void partKeyOff(AdLibPart *part, byte note);
-
-	void adlibKeyOff(int chan);
-	void adlibNoteOn(int chan, byte note, int mod);
-	void adlibNoteOnEx(int chan, byte note, int mod);
-	int adlibGetRegValueParam(int chan, byte data);
-	void adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2);
-#ifdef ENABLE_OPL3
-	void adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan);
-#endif
-	byte adlibGetRegValue(byte reg) {
-		return _regCache[reg];
-	}
-#ifdef ENABLE_OPL3
-	byte adlibGetRegValueSecondary(byte reg) {
-		return _regCacheSecondary[reg];
-	}
-#endif
-	void adlibSetParam(int channel, byte param, int value, bool primary = true);
-	void adlibKeyOnOff(int channel);
-	void adlibWrite(byte reg, byte value);
-#ifdef ENABLE_OPL3
-	void adlibWriteSecondary(byte reg, byte value);
-#endif
-	void adlibPlayNote(int channel, int note);
-
-	AdLibVoice *allocateVoice(byte pri);
-
-	void mcOff(AdLibVoice *voice);
-
-	static void linkMc(AdLibPart *part, AdLibVoice *voice);
-	void mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11);
-	void mcInitStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags,
-					   const InstrumentExtra *ie);
-
-	void struct10Init(Struct10 *s10, const InstrumentExtra *ie);
-	static byte struct10OnTimer(Struct10 *s10, Struct11 *s11);
-	static void struct10Setup(Struct10 *s10);
-	static int randomNr(int a);
-	void mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
-};
-
-// MidiChannel method implementations
-
-void AdLibPart::init(MidiDriver_ADLIB *owner, byte channel) {
-	_owner = owner;
-	_channel = channel;
-	_priEff = 127;
-	programChange(0);
-}
-
-MidiDriver *AdLibPart::device() {
-	return _owner;
-}
-
-void AdLibPart::send(uint32 b) {
-	_owner->send(_channel, b);
-}
-
-void AdLibPart::noteOff(byte note) {
-#ifdef DEBUG_ADLIB
-	debug(6, "%10d: noteOff(%d)", g_tick, note);
-#endif
-	_owner->partKeyOff(this, note);
-}
-
-void AdLibPart::noteOn(byte note, byte velocity) {
-#ifdef DEBUG_ADLIB
-	debug(6, "%10d: noteOn(%d,%d)", g_tick, note, velocity);
-#endif
-	_owner->partKeyOn(this, &_partInstr, note, velocity,
-#ifdef ENABLE_OPL3
-			&_partInstrSecondary,
-#else
-			NULL,
-#endif
-			_pan);
-}
-
-void AdLibPart::programChange(byte program) {
-	if (program > 127)
-		return;
-
-	/*
-	uint i;
-	uint count = 0;
-	for (i = 0; i < ARRAYSIZE(g_gmInstruments[0]); ++i)
-		count += g_gmInstruments[program][i];
-	if (!count)
-		warning("No AdLib instrument defined for GM program %d", (int)program);
-	*/
-	_program = program;
-#ifdef ENABLE_OPL3
-	if (!_owner->_opl3Mode) {
-#endif
-		memcpy(&_partInstr, &g_gmInstruments[program], sizeof(AdLibInstrument));
-#ifdef ENABLE_OPL3
-	} else {
-		memcpy(&_partInstr,          &g_gmInstrumentsOPL3[program][0], sizeof(AdLibInstrument));
-		memcpy(&_partInstrSecondary, &g_gmInstrumentsOPL3[program][1], sizeof(AdLibInstrument));
-	}
-#endif
-}
-
-void AdLibPart::pitchBend(int16 bend) {
-	AdLibVoice *voice;
-
-	_pitchBend = bend;
-	for (voice = _voice; voice; voice = voice->_next) {
-#ifdef ENABLE_OPL3
-		if (!_owner->_opl3Mode) {
-#endif
-			_owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/,
-								  (_pitchBend * _pitchBendFactor >> 6) + _detuneEff);
-#ifdef ENABLE_OPL3
-		} else {
-			_owner->adlibNoteOn(voice->_channel, voice->_note, _pitchBend >> 1);
-		}
-#endif
-	}
-}
-
-void AdLibPart::controlChange(byte control, byte value) {
-	switch (control) {
-	case 0:
-	case 32:
-		// Bank select. Not supported
-		break;
-	case 1:
-		modulationWheel(value);
-		break;
-	case 7:
-		volume(value);
-		break;
-	case 10:
-		panPosition(value);
-		break;
-	case 16:
-		pitchBendFactor(value);
-		break;
-	case 17:
-		detune(value);
-		break;
-	case 18:
-		priority(value);
-		break;
-	case 64:
-		sustain(value > 0);
-		break;
-	case 91:
-		// Effects level. Not supported.
-		break;
-	case 93:
-		// Chorus level. Not supported.
-		break;
-	case 119:
-		// Unknown, used in Simon the Sorcerer 2
-		break;
-	case 121:
-		// reset all controllers
-		modulationWheel(0);
-		pitchBendFactor(0);
-		detune(0);
-		sustain(0);
-		break;
-	case 123:
-		allNotesOff();
-		break;
-	default:
-		warning("AdLib: Unknown control change message %d (%d)", (int)control, (int)value);
-	}
-}
-
-void AdLibPart::modulationWheel(byte value) {
-	AdLibVoice *voice;
-
-	_modWheel = value;
-	for (voice = _voice; voice; voice = voice->_next) {
-		if (voice->_s10a.active && voice->_s11a.flag0x40)
-			voice->_s10a.modWheel = _modWheel >> 2;
-		if (voice->_s10b.active && voice->_s11b.flag0x40)
-			voice->_s10b.modWheel = _modWheel >> 2;
-	}
-}
-
-void AdLibPart::volume(byte value) {
-	AdLibVoice *voice;
-
-	_volEff = value;
-	for (voice = _voice; voice; voice = voice->_next) {
-#ifdef ENABLE_OPL3
-		if (!_owner->_opl3Mode) {
-#endif
-			_owner->adlibSetParam(voice->_channel, 0, g_volumeTable[g_volumeLookupTable[voice->_vol2][_volEff >> 2]]);
-			if (voice->_twoChan) {
-				_owner->adlibSetParam(voice->_channel, 13, g_volumeTable[g_volumeLookupTable[voice->_vol1][_volEff >> 2]]);
-			}
-#ifdef ENABLE_OPL3
-		} else {
-			_owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_vol2    + 1) * _volEff) >> 7], true);
-			_owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_secVol2 + 1) * _volEff) >> 7], false);
-			if (voice->_twoChan) {
-				_owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_vol1    + 1) * _volEff) >> 7], true);
-			}
-			if (voice->_secTwoChan) {
-				_owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_secVol1 + 1) * _volEff) >> 7], false);
-			}
-		}
-#endif
-	}
-}
-
-void AdLibPart::panPosition(byte value) {
-	_pan = value;
-}
-
-void AdLibPart::pitchBendFactor(byte value) {
-#ifdef ENABLE_OPL3
-	// Not supported in OPL3 mode.
-	if (_owner->_opl3Mode) {
-		return;
-	}
-#endif
-
-	AdLibVoice *voice;
-
-	_pitchBendFactor = value;
-	for (voice = _voice; voice; voice = voice->_next) {
-		_owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/,
-							  (_pitchBend * _pitchBendFactor >> 6) + _detuneEff);
-	}
-}
-
-void AdLibPart::detune(byte value) {
-	// Sam&Max's OPL3 driver uses this for a completly different purpose. It
-	// is related to voice allocation. We ignore this for now.
-	// TODO: We probably need to look how the interpreter side of Sam&Max's
-	// iMuse version handles all this too. Implementing the driver side here
-	// would be not that hard.
-#ifdef ENABLE_OPL3
-	if (_owner->_opl3Mode) {
-		//_maxNotes = value;
-		return;
-	}
-#endif
-
-	AdLibVoice *voice;
-
-	_detuneEff = value;
-	for (voice = _voice; voice; voice = voice->_next) {
-		_owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/,
-							  (_pitchBend * _pitchBendFactor >> 6) + _detuneEff);
-	}
-}
-
-void AdLibPart::priority(byte value) {
-	_priEff = value;
-}
-
-void AdLibPart::sustain(bool value) {
-	AdLibVoice *voice;
-
-	_pedal = value;
-	if (!value) {
-		for (voice = _voice; voice; voice = voice->_next) {
-			if (voice->_waitForPedal)
-				_owner->mcOff(voice);
-		}
-	}
-}
-
-void AdLibPart::allNotesOff() {
-	while (_voice)
-		_owner->mcOff(_voice);
-}
-
-void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr) {
-	// Sam&Max allows for instrument overwrites, but we will not support it
-	// until we can find any track actually using it.
-#ifdef ENABLE_OPL3
-	if (_owner->_opl3Mode) {
-		warning("AdLibPart::sysEx_customInstrument: Used in OPL3 mode");
-		return;
-	}
-#endif
-
-	if (type == 'ADL ') {
-		memcpy(&_partInstr, instr, sizeof(AdLibInstrument));
-	}
-}
-
-// MidiChannel method implementations for percussion
-
-AdLibPercussionChannel::~AdLibPercussionChannel() {
-	for (int i = 0; i < ARRAYSIZE(_customInstruments); ++i) {
-		delete _customInstruments[i];
-	}
-}
-
-void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) {
-	AdLibPart::init(owner, channel);
-	_priEff = 0;
-	_volEff = 127;
-
-	// Initialize the custom instruments data
-	memset(_notes, 0, sizeof(_notes));
-	memset(_customInstruments, 0, sizeof(_customInstruments));
-}
-
-void AdLibPercussionChannel::noteOff(byte note) {
-	if (_customInstruments[note]) {
-		note = _notes[note];
-	}
-
-	// This used to ignore note off events, since the builtin percussion
-	// instrument data has a duration value, which causes the percussion notes
-	// to stop automatically. This is not the case for (Groovie's) custom
-	// percussion instruments though. Also the OPL3 driver of Sam&Max actually
-	// does not handle the duration value, so we need it there too.
-	 _owner->partKeyOff(this, note);
-}
-
-void AdLibPercussionChannel::noteOn(byte note, byte velocity) {
-	const AdLibInstrument *inst = NULL;
-	const AdLibInstrument *sec  = NULL;
-
-	// The custom instruments have priority over the default mapping
-	// We do not support custom instruments in OPL3 mode though.
-#ifdef ENABLE_OPL3
-	if (!_owner->_opl3Mode) {
-#endif
-		inst = _customInstruments[note];
-		if (inst)
-			note = _notes[note];
-#ifdef ENABLE_OPL3
-	}
-#endif
-
-	if (!inst) {
-		// Use the default GM to FM mapping as a fallback
-		byte key = g_gmPercussionInstrumentMap[note];
-		if (key != 0xFF) {
-#ifdef ENABLE_OPL3
-			if (!_owner->_opl3Mode) {
-#endif
-				inst = &g_gmPercussionInstruments[key];
-#ifdef ENABLE_OPL3
-			} else {
-				inst = &g_gmPercussionInstrumentsOPL3[key][0];
-				sec  = &g_gmPercussionInstrumentsOPL3[key][1];
-			}
-#endif
-		}
-	}
-
-	if (!inst) {
-		debug(2, "No instrument FM definition for GM percussion key %d", (int)note);
-		return;
-	}
-
-	_owner->partKeyOn(this, inst, note, velocity, sec, _pan);
-}
-
-void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *instr) {
-	// We do not allow custom instruments in OPL3 mode right now.
-#ifdef ENABLE_OPL3
-	if (_owner->_opl3Mode) {
-		warning("AdLibPercussionChannel::sysEx_customInstrument: Used in OPL3 mode");
-		return;
-	}
-#endif
-
-	if (type == 'ADLP') {
-		byte note = instr[0];
-		_notes[note] = instr[1];
-
-		// Allocate memory for the new instruments
-		if (!_customInstruments[note]) {
-			_customInstruments[note] = new AdLibInstrument;
-			memset(_customInstruments[note], 0, sizeof(AdLibInstrument));
-		}
-
-		// Save the new instrument data
-		_customInstruments[note]->modCharacteristic     = instr[2];
-		_customInstruments[note]->modScalingOutputLevel = instr[3];
-		_customInstruments[note]->modAttackDecay        = instr[4];
-		_customInstruments[note]->modSustainRelease     = instr[5];
-		_customInstruments[note]->modWaveformSelect     = instr[6];
-		_customInstruments[note]->carCharacteristic     = instr[7];
-		_customInstruments[note]->carScalingOutputLevel = instr[8];
-		_customInstruments[note]->carAttackDecay        = instr[9];
-		_customInstruments[note]->carSustainRelease     = instr[10];
-		_customInstruments[note]->carWaveformSelect     = instr[11];
-		_customInstruments[note]->feedback               = instr[12];
-	}
-}
-
-// MidiDriver method implementations
-
-MidiDriver_ADLIB::MidiDriver_ADLIB() {
-	uint i;
-
-	_scummSmallHeader = false;
-#ifdef ENABLE_OPL3
-	_opl3Mode = false;
-#endif
-
-	_regCache = 0;
-#ifdef ENABLE_OPL3
-	_regCacheSecondary = 0;
-#endif
-
-	_timerCounter = 0;
-	_voiceIndex = -1;
-	for (i = 0; i < ARRAYSIZE(_curNotTable); ++i) {
-		_curNotTable[i] = 0;
-	}
-
-	for (i = 0; i < ARRAYSIZE(_parts); ++i) {
-		_parts[i].init(this, i + ((i >= 9) ? 1 : 0));
-	}
-	_percussion.init(this, 9);
-	_timerIncrease = 0xD69;
-	_timerThreshold = 0x411B;
-	_opl = 0;
-	_adlibTimerProc = 0;
-        _adlibTimerParam = 0;
-	_isOpen = false;
-}
-
-int MidiDriver_ADLIB::open() {
-	if (_isOpen)
-		return MERR_ALREADY_OPEN;
-
-	_isOpen = true;
-
-	int i;
-	AdLibVoice *voice;
-
-	for (i = 0, voice = _voices; i != ARRAYSIZE(_voices); i++, voice++) {
-		voice->_channel = i;
-		voice->_s11a.s10 = &voice->_s10b;
-		voice->_s11b.s10 = &voice->_s10a;
-	}
-
-	// Try to use OPL3 when requested.
-#ifdef ENABLE_OPL3
-	if (_opl3Mode) {
-		_opl = OPL::Config::create(OPL::Config::kOpl3);
-	}
-
-	// Initialize plain OPL2 when no OPL3 is intiailized already.
-	if (!_opl) {
-#endif
-		_opl = OPL::Config::create();
-#ifdef ENABLE_OPL3
-		_opl3Mode = false;
-	}
-#endif
-	_opl->init();
-
-	_regCache = (byte *)calloc(256, 1);
-
-	adlibWrite(8, 0x40);
-	adlibWrite(0xBD, 0x00);
-#ifdef ENABLE_OPL3
-	if (!_opl3Mode) {
-#endif
-		adlibWrite(1, 0x20);
-		createLookupTable();
-#ifdef ENABLE_OPL3
-	} else {
-		_regCacheSecondary = (byte *)calloc(256, 1);
-		adlibWriteSecondary(5, 1);
-	}
-#endif
-
-	_opl->start(new Common::Functor0Mem<void, MidiDriver_ADLIB>(this, &MidiDriver_ADLIB::onTimer));
-	return 0;
-}
-
-void MidiDriver_ADLIB::close() {
-	if (!_isOpen)
-		return;
-	_isOpen = false;
-
-	// Stop the OPL timer
-	_opl->stop();
-
-	uint i;
-	for (i = 0; i < ARRAYSIZE(_voices); ++i) {
-		if (_voices[i]._part)
-			mcOff(&_voices[i]);
-	}
-
-	// Turn off the OPL emulation
-	delete _opl;
-	_opl = 0;
-
-	free(_regCache);
-#ifdef ENABLE_OPL3
-	free(_regCacheSecondary);
-#endif
-}
-
-void MidiDriver_ADLIB::send(uint32 b) {
-	send(b & 0xF, b & 0xFFFFFFF0);
-}
-
-void MidiDriver_ADLIB::send(byte chan, uint32 b) {
-	//byte param3 = (byte) ((b >> 24) & 0xFF);
-	byte param2 = (byte)((b >> 16) & 0xFF);
-	byte param1 = (byte)((b >>  8) & 0xFF);
-	byte cmd    = (byte)(b & 0xF0);
-
-	AdLibPart *part;
-	if (chan == 9)
-		part = &_percussion;
-	else
-		part = &_parts[chan];
-
-	switch (cmd) {
-	case 0x80:// Note Off
-		part->noteOff(param1);
-		break;
-	case 0x90: // Note On
-		part->noteOn(param1, param2);
-		break;
-	case 0xA0: // Aftertouch
-		break; // Not supported.
-	case 0xB0: // Control Change
-		part->controlChange(param1, param2);
-		break;
-	case 0xC0: // Program Change
-		part->programChange(param1);
-		break;
-	case 0xD0: // Channel Pressure
-		break; // Not supported.
-	case 0xE0: // Pitch Bend
-		part->pitchBend((param1 | (param2 << 7)) - 0x2000);
-		break;
-	case 0xF0: // SysEx
-		// We should never get here! SysEx information has to be
-		// sent via high-level semantic methods.
-		warning("MidiDriver_ADLIB: Receiving SysEx command on a send() call");
-		break;
-
-	default:
-		warning("MidiDriver_ADLIB: Unknown send() command 0x%02X", cmd);
-	}
-}
-
-uint32 MidiDriver_ADLIB::property(int prop, uint32 param) {
-	switch (prop) {
-	case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm
-		_scummSmallHeader = (param > 0);
-		if (_scummSmallHeader) {
-			_timerIncrease = 473;
-			_timerThreshold = 1000;
-		} else {
-			_timerIncrease = 0xD69;
-			_timerThreshold = 0x411B;
-		}
-		return 1;
-
-	case PROP_SCUMM_OPL3: // Sam&Max OPL3 support.
-#ifdef ENABLE_OPL3
-		_opl3Mode = (param > 0);
-#endif
-		return 1;
-	}
-
-	return 0;
-}
-
-void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) {
-#ifdef ENABLE_OPL3
-	// Not supported in OPL3 mode.
-	if (_opl3Mode) {
-		return;
-	}
-#endif
-
-	AdLibVoice *voice;
-	AdLibPart *part = &_parts[channel];
-
-	part->_pitchBendFactor = range;
-	for (voice = part->_voice; voice; voice = voice->_next) {
-		adlibNoteOn(voice->_channel, voice->_note/* + part->_transposeEff*/,
-					  (part->_pitchBend * part->_pitchBendFactor >> 6) + part->_detuneEff);
-	}
-}
-
-void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
-	_parts[channel].sysEx_customInstrument(type, instr);
-}
-
-MidiChannel *MidiDriver_ADLIB::allocateChannel() {
-	AdLibPart *part;
-	uint i;
-
-	for (i = 0; i < ARRAYSIZE(_parts); ++i) {
-		part = &_parts[i];
-		if (!part->_allocated) {
-			part->allocate();
-			return part;
-		}
-	}
-	return NULL;
-}
-
-// All the code brought over from IMuseAdLib
-
-void MidiDriver_ADLIB::adlibWrite(byte reg, byte value) {
-	if (_regCache[reg] == value) {
-		return;
-	}
-#ifdef DEBUG_ADLIB
-	debug(6, "%10d: adlibWrite[%x] = %x", g_tick, reg, value);
-#endif
-	_regCache[reg] = value;
-
-	_opl->writeReg(reg, value);
-}
-
-#ifdef ENABLE_OPL3
-void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) {
-	assert(_opl3Mode);
-
-	if (_regCacheSecondary[reg] == value) {
-		return;
-	}
-#ifdef DEBUG_ADLIB
-	debug(6, "%10d: adlibWriteSecondary[%x] = %x", g_tick, reg, value);
-#endif
-	_regCacheSecondary[reg] = value;
-
-	_opl->writeReg(reg | 0x100, value);
-}
-#endif
-
-void MidiDriver_ADLIB::onTimer() {
-	if (_adlibTimerProc)
-		(*_adlibTimerProc)(_adlibTimerParam);
-
-	_timerCounter += _timerIncrease;
-	while (_timerCounter >= _timerThreshold) {
-		_timerCounter -= _timerThreshold;
-#ifdef DEBUG_ADLIB
-		g_tick++;
-#endif
-		// Sam&Max's OPL3 driver does not have any timer handling like this.
-#ifdef ENABLE_OPL3
-		if (!_opl3Mode) {
-#endif
-			AdLibVoice *voice = _voices;
-			for (int i = 0; i != ARRAYSIZE(_voices); i++, voice++) {
-				if (!voice->_part)
-					continue;
-				if (voice->_duration && (voice->_duration -= 0x11) <= 0) {
-					mcOff(voice);
-					return;
-				}
-				if (voice->_s10a.active) {
-					mcIncStuff(voice, &voice->_s10a, &voice->_s11a);
-				}
-				if (voice->_s10b.active) {
-					mcIncStuff(voice, &voice->_s10b, &voice->_s11b);
-				}
-			}
-#ifdef ENABLE_OPL3
-		}
-#endif
-	}
-}
-
-void MidiDriver_ADLIB::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
-	_adlibTimerProc = timerProc;
-	_adlibTimerParam = timerParam;
-}
-
-void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) {
-	AdLibVoice *tmp;
-
-	adlibKeyOff(voice->_channel);
-
-	tmp = voice->_prev;
-
-	if (voice->_next)
-		voice->_next->_prev = tmp;
-	if (tmp)
-		tmp->_next = voice->_next;
-	else
-		voice->_part->_voice = voice->_next;
-	voice->_part = NULL;
-}
-
-void MidiDriver_ADLIB::mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) {
-	byte code;
-	AdLibPart *part = voice->_part;
-
-	code = struct10OnTimer(s10, s11);
-
-	if (code & 1) {
-		switch (s11->param) {
-		case 0:
-			voice->_vol2 = s10->startValue + s11->modifyVal;
-			if (!_scummSmallHeader) {
-				adlibSetParam(voice->_channel, 0,
-								g_volumeTable[g_volumeLookupTable[voice->_vol2]
-											  [part->_volEff >> 2]]);
-			} else {
-				adlibSetParam(voice->_channel, 0, voice->_vol2);
-			}
-			break;
-		case 13:
-			voice->_vol1 = s10->startValue + s11->modifyVal;
-			if (voice->_twoChan && !_scummSmallHeader) {
-				adlibSetParam(voice->_channel, 13,
-								g_volumeTable[g_volumeLookupTable[voice->_vol1]
-											  [part->_volEff >> 2]]);
-			} else {
-				adlibSetParam(voice->_channel, 13, voice->_vol1);
-			}
-			break;
-		case 30:
-			s11->s10->modWheel = (char)s11->modifyVal;
-			break;
-		case 31:
-			s11->s10->unk3 = (char)s11->modifyVal;
-			break;
-		default:
-			adlibSetParam(voice->_channel, s11->param,
-							s10->startValue + s11->modifyVal);
-			break;
-		}
-	}
-
-	if (code & 2 && s11->flag0x10)
-		adlibKeyOnOff(voice->_channel);
-}
-
-void MidiDriver_ADLIB::adlibKeyOff(int chan) {
-	byte reg = chan + 0xB0;
-	adlibWrite(reg, adlibGetRegValue(reg) & ~0x20);
-#ifdef ENABLE_OPL3
-	if (_opl3Mode) {
-		adlibWriteSecondary(reg, adlibGetRegValueSecondary(reg) & ~0x20);
-	}
-#endif
-}
-
-byte MidiDriver_ADLIB::struct10OnTimer(Struct10 *s10, Struct11 *s11) {
-	byte result = 0;
-	int i;
-
-	if (s10->count && (s10->count -= 17) <= 0) {
-		s10->active = 0;
-		return 0;
-	}
-
-	i = s10->curVal + s10->speedHi;
-	s10->speedLoCounter += s10->speedLo;
-	if (s10->speedLoCounter >= s10->speedLoMax) {
-		s10->speedLoCounter -= s10->speedLoMax;
-		i += s10->direction;
-	}
-	if (s10->curVal != i || s10->modWheel != s10->modWheelLast) {
-		s10->curVal = i;
-		s10->modWheelLast = s10->modWheel;
-		i = lookupVolume(i, s10->modWheelLast);
-		if (i != s11->modifyVal) {
-			s11->modifyVal = i;
-			result = 1;
-		}
-	}
-
-	if (!--s10->numSteps) {
-		s10->active++;
-		if (s10->active > 4) {
-			if (s10->loop) {
-				s10->active = 1;
-				result |= 2;
-				struct10Setup(s10);
-			} else {
-				s10->active = 0;
-			}
-		} else {
-			struct10Setup(s10);
-		}
-	}
-
-	return result;
-}
-
-void MidiDriver_ADLIB::adlibSetParam(int channel, byte param, int value, bool primary) {
-	const AdLibSetParams *as;
-	byte reg;
-
-	assert(channel >= 0 && channel < 9);
-#ifdef ENABLE_OPL3
-	assert(!_opl3Mode || (param == 0 || param == 13));
-#endif
-
-	if (param <= 12) {
-		reg = g_operator2Offsets[channel];
-	} else if (param <= 25) {
-		param -= 13;
-		reg = g_operator1Offsets[channel];
-	} else if (param <= 27) {
-		param -= 13;
-		reg = channel;
-	} else if (param == 28 || param == 29) {
-		if (param == 28)
-			value -= 15;
-		else
-			value -= 383;
-		value <<= 4;
-		_channelTable2[channel] = value;
-		adlibPlayNote(channel, _curNotTable[channel] + value);
-		return;
-	} else {
-		return;
-	}
-
-	as = &g_setParamTable[param];
-	if (as->inversion)
-		value = as->inversion - value;
-	reg += as->registerBase;
-#ifdef ENABLE_OPL3
-	if (primary) {
-#endif
-		adlibWrite(reg, (adlibGetRegValue(reg) & ~as->mask) | (((byte)value) << as->shift));
-#ifdef ENABLE_OPL3
-	} else {
-		adlibWriteSecondary(reg, (adlibGetRegValueSecondary(reg) & ~as->mask) | (((byte)value) << as->shift));
-	}
-#endif
-}
-
-void MidiDriver_ADLIB::adlibKeyOnOff(int channel) {
-#ifdef ENABLE_OPL3
-	assert(!_opl3Mode);
-#endif
-
-	byte val;
-	byte reg = channel + 0xB0;
-	assert(channel >= 0 && channel < 9);
-
-	val = adlibGetRegValue(reg);
-	adlibWrite(reg, val & ~0x20);
-	adlibWrite(reg, val | 0x20);
-}
-
-void MidiDriver_ADLIB::struct10Setup(Struct10 *s10) {
-	int b, c, d, e, f, g, h;
-	byte t;
-
-	b = s10->unk3;
-	f = s10->active - 1;
-
-	t = s10->tableA[f];
-	e = g_numStepsTable[g_volumeLookupTable[t & 0x7F][b]];
-	if (t & 0x80) {
-		e = randomNr(e);
-	}
-	if (e == 0)
-		e++;
-
-	s10->numSteps = s10->speedLoMax = e;
-
-	if (f != 2) {
-		c = s10->maxValue;
-		g = s10->startValue;
-		t = s10->tableB[f];
-		d = lookupVolume(c, (t & 0x7F) - 31);
-		if (t & 0x80) {
-			d = randomNr(d);
-		}
-		if (d + g > c) {
-			h = c - g;
-		} else {
-			h = d;
-			if (d + g < 0)
-				h = -g;
-		}
-		h -= s10->curVal;
-	} else {
-		h = 0;
-	}
-
-	s10->speedHi = h / e;
-	if (h < 0) {
-		h = -h;
-		s10->direction = -1;
-	} else {
-		s10->direction = 1;
-	}
-
-	s10->speedLo = h % e;
-	s10->speedLoCounter = 0;
-}
-
-void MidiDriver_ADLIB::adlibPlayNote(int channel, int note) {
-	byte old, oct, notex;
-	int note2;
-	int i;
-
-	note2 = (note >> 7) - 4;
-	note2 = (note2 < 128) ? note2 : 0;
-
-	oct = (note2 / 12);
-	if (oct > 7)
-		oct = 7 << 2;
-	else
-		oct <<= 2;
-	notex = note2 % 12 + 3;
-
-	old = adlibGetRegValue(channel + 0xB0);
-	if (old & 0x20) {
-		old &= ~0x20;
-		if (oct > old) {
-			if (notex < 6) {
-				notex += 12;
-				oct -= 4;
-			}
-		} else if (oct < old) {
-			if (notex > 11) {
-				notex -= 12;
-				oct += 4;
-			}
-		}
-	}
-
-	i = (notex << 3) + ((note >> 4) & 0x7);
-	adlibWrite(channel + 0xA0, g_noteFrequencies[i]);
-	adlibWrite(channel + 0xB0, oct | 0x20);
-}
-
-int MidiDriver_ADLIB::randomNr(int a) {
-	static byte _randSeed = 1;
-	if (_randSeed & 1) {
-		_randSeed >>= 1;
-		_randSeed ^= 0xB8;
-	} else {
-		_randSeed >>= 1;
-	}
-	return _randSeed * a >> 8;
-}
-
-void MidiDriver_ADLIB::partKeyOff(AdLibPart *part, byte note) {
-	AdLibVoice *voice;
-
-	for (voice = part->_voice; voice; voice = voice->_next) {
-		if (voice->_note == note) {
-			if (part->_pedal)
-				voice->_waitForPedal = true;
-			else
-				mcOff(voice);
-		}
-	}
-}
-
-void MidiDriver_ADLIB::partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) {
-	AdLibVoice *voice;
-
-	voice = allocateVoice(part->_priEff);
-	if (!voice)
-		return;
-
-	linkMc(part, voice);
-	mcKeyOn(voice, instr, note, velocity, second, pan);
-}
-
-AdLibVoice *MidiDriver_ADLIB::allocateVoice(byte pri) {
-	AdLibVoice *ac, *best = NULL;
-	int i;
-
-	for (i = 0; i < 9; i++) {
-		if (++_voiceIndex >= 9)
-			_voiceIndex = 0;
-		ac = &_voices[_voiceIndex];
-		if (!ac->_part)
-			return ac;
-		if (!ac->_next) {
-			if (ac->_part->_priEff <= pri) {
-				pri = ac->_part->_priEff;
-				best = ac;
-			}
-		}
-	}
-
-	/* SCUMM V3 games don't have note priorities, first comes wins. */
-	if (_scummSmallHeader)
-		return NULL;
-
-	if (best)
-		mcOff(best);
-	return best;
-}
-
-void MidiDriver_ADLIB::linkMc(AdLibPart *part, AdLibVoice *voice) {
-	voice->_part = part;
-	voice->_next = (AdLibVoice *)part->_voice;
-	part->_voice = voice;
-	voice->_prev = NULL;
-
-	if (voice->_next)
-		voice->_next->_prev = voice;
-}
-
-void MidiDriver_ADLIB::mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) {
-	AdLibPart *part = voice->_part;
-	byte vol1, vol2;
-#ifdef ENABLE_OPL3
-	byte secVol1 = 0, secVol2 = 0;
-#endif
-
-	voice->_twoChan = instr->feedback & 1;
-	voice->_note = note;
-	voice->_waitForPedal = false;
-	voice->_duration = instr->duration;
-	if (voice->_duration != 0)
-		voice->_duration *= 63;
-
-	if (!_scummSmallHeader) {
-#ifdef ENABLE_OPL3
-		if (_opl3Mode)
-			vol1 = (instr->modScalingOutputLevel & 0x3F) + (velocity * ((instr->modWaveformSelect >> 3) + 1)) / 64;
-		else
-#endif
-		vol1 = (instr->modScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->modWaveformSelect >> 2];
-	} else {
-		vol1 = 0x3f - (instr->modScalingOutputLevel & 0x3F);
-	}
-	if (vol1 > 0x3F)
-		vol1 = 0x3F;
-	voice->_vol1 = vol1;
-
-	if (!_scummSmallHeader) {
-#ifdef ENABLE_OPL3
-		if (_opl3Mode)
-			vol2 = (instr->carScalingOutputLevel & 0x3F) + (velocity * ((instr->carWaveformSelect >> 3) + 1)) / 64;
-		else
-#endif
-		vol2 = (instr->carScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->carWaveformSelect >> 2];
-	} else {
-		vol2 = 0x3f - (instr->carScalingOutputLevel & 0x3F);
-	}
-	if (vol2 > 0x3F)
-		vol2 = 0x3F;
-	voice->_vol2 = vol2;
-
-#ifdef ENABLE_OPL3
-	if (_opl3Mode) {
-		voice->_secTwoChan = second->feedback & 1;
-		secVol1 = (second->modScalingOutputLevel & 0x3F) + (velocity * ((second->modWaveformSelect >> 3) + 1)) / 64;
-		if (secVol1 > 0x3F) {
-			secVol1 = 0x3F;
-		}
-		voice->_secVol1 = secVol1;
-		secVol2 = (second->carScalingOutputLevel & 0x3F) + (velocity * ((second->carWaveformSelect >> 3) + 1)) / 64;
-		if (secVol2 > 0x3F) {
-			secVol2 = 0x3F;
-		}
-		voice->_secVol2 = secVol2;
-	}
-#endif
-
-	if (!_scummSmallHeader) {
-#ifdef ENABLE_OPL3
-		if (!_opl3Mode) {
-#endif
-			int c = part->_volEff >> 2;
-			vol2 = g_volumeTable[g_volumeLookupTable[vol2][c]];
-			if (voice->_twoChan)
-				vol1 = g_volumeTable[g_volumeLookupTable[vol1][c]];
-#ifdef ENABLE_OPL3
-		} else {
-			vol2    = g_volumeTable[((vol2    + 1) * part->_volEff) >> 7];
-			secVol2 = g_volumeTable[((secVol2 + 1) * part->_volEff) >> 7];
-			if (voice->_twoChan)
-				vol1    = g_volumeTable[((vol1    + 1) * part->_volEff) >> 7];
-			if (voice->_secTwoChan)
-				secVol1 = g_volumeTable[((secVol1 + 1) * part->_volEff) >> 7];
-		}
-#endif
-	}
-
-	adlibSetupChannel(voice->_channel, instr, vol1, vol2);
-#ifdef ENABLE_OPL3
-	if (!_opl3Mode) {
-#endif
-		adlibNoteOnEx(voice->_channel, /*part->_transposeEff + */note, part->_detuneEff + (part->_pitchBend * part->_pitchBendFactor >> 6));
-
-		if (instr->flagsA & 0x80) {
-			mcInitStuff(voice, &voice->_s10a, &voice->_s11a, instr->flagsA, &instr->extraA);
-		} else {
-			voice->_s10a.active = 0;
-		}
-
-		if (instr->flagsB & 0x80) {
-			mcInitStuff(voice, &voice->_s10b, &voice->_s11b, instr->flagsB, &instr->extraB);
-		} else {
-			voice->_s10b.active = 0;
-		}
-#ifdef ENABLE_OPL3
-	} else {
-		adlibSetupChannelSecondary(voice->_channel, second, secVol1, secVol2, pan);
-		adlibNoteOnEx(voice->_channel, note, part->_pitchBend >> 1);
-	}
-#endif
-}
-
-void MidiDriver_ADLIB::adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2) {
-	assert(chan >= 0 && chan < 9);
-
-	byte channel = g_operator1Offsets[chan];
-	adlibWrite(channel + 0x20, instr->modCharacteristic);
-	adlibWrite(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1);
-	adlibWrite(channel + 0x60, 0xff & (~instr->modAttackDecay));
-	adlibWrite(channel + 0x80, 0xff & (~instr->modSustainRelease));
-	adlibWrite(channel + 0xE0, instr->modWaveformSelect);
-
-	channel = g_operator2Offsets[chan];
-	adlibWrite(channel + 0x20, instr->carCharacteristic);
-	adlibWrite(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2);
-	adlibWrite(channel + 0x60, 0xff & (~instr->carAttackDecay));
-	adlibWrite(channel + 0x80, 0xff & (~instr->carSustainRelease));
-	adlibWrite(channel + 0xE0, instr->carWaveformSelect);
-
-	adlibWrite((byte)chan + 0xC0, instr->feedback
-#ifdef ENABLE_OPL3
-			| (_opl3Mode ? 0x30 : 0)
-#endif
-			);
-}
-
-#ifdef ENABLE_OPL3
-void MidiDriver_ADLIB::adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan) {
-	assert(chan >= 0 && chan < 9);
-	assert(_opl3Mode);
-
-	byte channel = g_operator1Offsets[chan];
-	adlibWriteSecondary(channel + 0x20, instr->modCharacteristic);
-	adlibWriteSecondary(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1);
-	adlibWriteSecondary(channel + 0x60, 0xff & (~instr->modAttackDecay));
-	adlibWriteSecondary(channel + 0x80, 0xff & (~instr->modSustainRelease));
-	adlibWriteSecondary(channel + 0xE0, instr->modWaveformSelect);
-
-	channel = g_operator2Offsets[chan];
-	adlibWriteSecondary(channel + 0x20, instr->carCharacteristic);
-	adlibWriteSecondary(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2);
-	adlibWriteSecondary(channel + 0x60, 0xff & (~instr->carAttackDecay));
-	adlibWriteSecondary(channel + 0x80, 0xff & (~instr->carSustainRelease));
-	adlibWriteSecondary(channel + 0xE0, instr->carWaveformSelect);
-
-	// The original uses the following (strange) behavior:
-#if 0
-	if (instr->feedback | (pan > 64)) {
-		adlibWriteSecondary((byte)chan + 0xC0, 0x20);
-	} else {
-		adlibWriteSecondary((byte)chan + 0xC0, 0x10);
-	}
-#else
-	adlibWriteSecondary((byte)chan + 0xC0, instr->feedback | ((pan > 64) ? 0x20 : 0x10));
-#endif
-}
-#endif
-
-void MidiDriver_ADLIB::mcInitStuff(AdLibVoice *voice, Struct10 *s10,
-									 Struct11 *s11, byte flags, const InstrumentExtra *ie) {
-	AdLibPart *part = voice->_part;
-	s11->modifyVal = 0;
-	s11->flag0x40 = flags & 0x40;
-	s10->loop = flags & 0x20;
-	s11->flag0x10 = flags & 0x10;
-	s11->param = g_paramTable1[flags & 0xF];
-	s10->maxValue = g_maxValTable[flags & 0xF];
-	s10->unk3 = 31;
-	if (s11->flag0x40) {
-		s10->modWheel = part->_modWheel >> 2;
-	} else {
-		s10->modWheel = 31;
-	}
-
-	switch (s11->param) {
-	case 0:
-		s10->startValue = voice->_vol2;
-		break;
-	case 13:
-		s10->startValue = voice->_vol1;
-		break;
-	case 30:
-		s10->startValue = 31;
-		s11->s10->modWheel = 0;
-		break;
-	case 31:
-		s10->startValue = 0;
-		s11->s10->unk3 = 0;
-		break;
-	default:
-		s10->startValue = adlibGetRegValueParam(voice->_channel, s11->param);
-	}
-
-	struct10Init(s10, ie);
-}
-
-void MidiDriver_ADLIB::struct10Init(Struct10 *s10, const InstrumentExtra *ie) {
-	s10->active = 1;
-	if (!_scummSmallHeader) {
-		s10->curVal = 0;
-	} else {
-		s10->curVal = s10->startValue;
-		s10->startValue = 0;
-	}
-	s10->modWheelLast = 31;
-	s10->count = ie->a;
-	if (s10->count)
-		s10->count *= 63;
-	s10->tableA[0] = ie->b;
-	s10->tableA[1] = ie->d;
-	s10->tableA[2] = ie->f;
-	s10->tableA[3] = ie->g;
-
-	s10->tableB[0] = ie->c;
-	s10->tableB[1] = ie->e;
-	s10->tableB[2] = 0;
-	s10->tableB[3] = ie->h;
-
-	struct10Setup(s10);
-}
-
-int MidiDriver_ADLIB::adlibGetRegValueParam(int chan, byte param) {
-	const AdLibSetParams *as;
-	byte val;
-	byte channel;
-
-	assert(chan >= 0 && chan < 9);
-
-	if (param <= 12) {
-		channel = g_operator2Offsets[chan];
-	} else if (param <= 25) {
-		param -= 13;
-		channel = g_operator1Offsets[chan];
-	} else if (param <= 27) {
-		param -= 13;
-		channel = chan;
-	} else if (param == 28) {
-		return 0xF;
-	} else if (param == 29) {
-		return 0x17F;
-	} else {
-		return 0;
-	}
-
-	as = &g_setParamTable[param];
-	val = adlibGetRegValue(channel + as->registerBase);
-	val &= as->mask;
-	val >>= as->shift;
-	if (as->inversion)
-		val = as->inversion - val;
-
-	return val;
-}
-
-void MidiDriver_ADLIB::adlibNoteOn(int chan, byte note, int mod) {
-#ifdef ENABLE_OPL3
-	if (_opl3Mode) {
-		adlibNoteOnEx(chan, note, mod);
-		return;
-	}
-#endif
-
-	assert(chan >= 0 && chan < 9);
-	int code = (note << 7) + mod;
-	_curNotTable[chan] = code;
-	adlibPlayNote(chan, (int16)_channelTable2[chan] + code);
-}
-
-void MidiDriver_ADLIB::adlibNoteOnEx(int chan, byte note, int mod) {
-	assert(chan >= 0 && chan < 9);
-
-#ifdef ENABLE_OPL3
-	if (_opl3Mode) {
-		const int noteAdjusted = note + (mod >> 8) - 7;
-		const int pitchAdjust = (mod >> 5) & 7;
-
-		adlibWrite(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]);
-		adlibWriteSecondary(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]);
-		adlibWrite(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20);
-		adlibWriteSecondary(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20);
-	} else {
-#endif
-		int code = (note << 7) + mod;
-		_curNotTable[chan] = code;
-		_channelTable2[chan] = 0;
-		adlibPlayNote(chan, code);
-#ifdef ENABLE_OPL3
-	}
-#endif
-}
-
-// Plugin interface
-
-class AdLibEmuMusicPlugin : public MusicPluginObject {
-public:
-	const char *getName() const {
-		return _s("AdLib Emulator");
-	}
-
-	const char *getId() const {
-		return "adlib";
-	}
-
-	MusicDevices getDevices() const;
-	Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const;
-};
-
-MusicDevices AdLibEmuMusicPlugin::getDevices() const {
-	MusicDevices devices;
-	devices.push_back(MusicDevice(this, "", MT_ADLIB));
-	return devices;
-}
-
-Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
-	*mididriver = new MidiDriver_ADLIB();
-
-	return Common::kNoError;
-}
-
-//#if PLUGIN_ENABLED_DYNAMIC(ADLIB)
-	//REGISTER_PLUGIN_DYNAMIC(ADLIB, PLUGIN_TYPE_MUSIC, AdLibEmuMusicPlugin);
-//#else
-	REGISTER_PLUGIN_STATIC(ADLIB, PLUGIN_TYPE_MUSIC, AdLibEmuMusicPlugin);
-//#endif


Commit: bb8132beb82a4f5d5a3f8db0fb9dec08b2965b72
    https://github.com/scummvm/scummvm/commit/bb8132beb82a4f5d5a3f8db0fb9dec08b2965b72
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-07-07T20:41:30-04:00

Commit Message:
AUDIO: Move ALSA OPL 'driver' out of softsynth

Might eventually be worth moving to backends/

Changed paths:
  A audio/alsa_opl.cpp
  R audio/softsynth/opl/alsa.cpp
    audio/module.mk



diff --git a/audio/alsa_opl.cpp b/audio/alsa_opl.cpp
new file mode 100644
index 0000000..6b9e48e
--- /dev/null
+++ b/audio/alsa_opl.cpp
@@ -0,0 +1,349 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/* OPL implementation for hardware OPL using ALSA Direct FM API.
+ *
+ * Caveats and limitations:
+ * - Pretends to be a softsynth (emitting silence).
+ * - Dual OPL2 mode requires OPL3 hardware.
+ * - Every register write leads to a series of register writes on the hardware,
+ *   due to the lack of direct register access in the ALSA Direct FM API.
+ * - No timers
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/scummsys.h"
+
+#include "common/debug.h"
+#include "common/str.h"
+#include "audio/fmopl.h"
+
+#include <sys/ioctl.h>
+#include <alsa/asoundlib.h>
+#include <sound/asound_fm.h>
+
+namespace OPL {
+namespace ALSA {
+
+class OPL : public ::OPL::RealOPL {
+private:
+	enum {
+		kOpl2Voices = 9,
+		kVoices = 18,
+		kOpl2Operators = 18,
+		kOperators = 36
+	};
+
+	Config::OplType _type;
+	int _iface;
+	snd_hwdep_t *_opl;
+	snd_dm_fm_voice _oper[kOperators];
+	snd_dm_fm_note _voice[kVoices];
+	snd_dm_fm_params _params;
+	int index[2];
+	static const int voiceToOper0[kVoices];
+	static const int regOffsetToOper[0x20];
+
+	void writeOplReg(int c, int r, int v);
+	void clear();
+
+public:
+	OPL(Config::OplType type);
+	~OPL();
+
+	bool init();
+	void reset();
+
+	void write(int a, int v);
+	byte read(int a);
+
+	void writeReg(int r, int v);
+};
+
+const int OPL::voiceToOper0[OPL::kVoices] =
+	{ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
+
+const int OPL::regOffsetToOper[0x20] =
+	{ 0,  1,  2,  3,  4,  5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
+	 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
+}
+
+OPL::~OPL() {
+	stop();
+
+	if (_opl) {
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
+		snd_hwdep_close(_opl);
+	}
+}
+
+void OPL::clear() {
+	index[0] = index[1] = 0;
+
+	memset(_oper, 0, sizeof(_oper));
+	memset(_voice, 0, sizeof(_voice));
+	memset(&_params, 0, sizeof(_params));
+
+	for (int i = 0; i < kOperators; ++i) {
+		_oper[i].op = (i / 3) % 2;
+		_oper[i].voice = (i / 6) * 3 + (i % 3);
+	}
+
+	for (int i = 0; i < kVoices; ++i)
+		_voice[i].voice = i;
+
+	// For OPL3 hardware we need to set up the panning in OPL2 modes
+	if (_iface == SND_HWDEP_IFACE_OPL3) {
+		if (_type == Config::kDualOpl2) {
+			for (int i = 0; i < kOpl2Operators; ++i)
+				_oper[i].left = 1; // FIXME below
+			for (int i = kOpl2Operators; i < kOperators; ++i)
+				_oper[i].right = 1;
+		} else if (_type == Config::kOpl2) {
+			for (int i = 0; i < kOpl2Operators; ++i) {
+				_oper[i].left = 1;
+				_oper[i].right = 1;			
+			}
+		}
+	}
+}
+
+bool OPL::init() {
+	clear();
+
+	int card = -1;
+	snd_ctl_t *ctl;
+	snd_hwdep_info_t *info;
+	snd_hwdep_info_alloca(&info);
+
+	int iface = SND_HWDEP_IFACE_OPL3;
+	if (_type == Config::kOpl2)
+		iface = SND_HWDEP_IFACE_OPL2;
+
+	// Look for OPL hwdep interface
+	while (!snd_card_next(&card) && card >= 0) {
+		int dev = -1;
+		Common::String name = Common::String::format("hw:%d", card);
+
+		if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
+			continue;
+
+		while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
+			name = Common::String::format("hw:%d,%d", card, dev);
+
+			if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) 
+				continue;
+
+			if (!snd_hwdep_info(_opl, info)) {
+				int found = snd_hwdep_info_get_iface(info);
+				// OPL3 can be used for (Dual) OPL2 mode
+				if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
+					snd_ctl_close(ctl);
+					_iface = found;
+					reset();
+					return true;
+				}
+			}
+
+			// Wrong interface, try next device
+			snd_hwdep_close(_opl);
+			_opl = nullptr;
+		}
+
+		snd_ctl_close(ctl);
+	}
+
+	return false;
+}
+
+void OPL::reset() {
+	snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
+	if (_iface == SND_HWDEP_IFACE_OPL3)
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
+
+	clear();
+
+	// Sync up with the hardware
+	snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
+	for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
+	for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
+}
+
+void OPL::write(int port, int val) {
+	val &= 0xff;
+	int chip = (port & 2) >> 1;
+
+	if (port & 1) {
+		switch(_type) {
+		case Config::kOpl2:
+			writeOplReg(0, index[0], val);
+			break;
+		case Config::kDualOpl2:
+			if (port & 8) {
+				writeOplReg(0, index[0], val);
+				writeOplReg(1, index[1], val);
+			} else
+				writeOplReg(chip, index[chip], val);
+			break;
+		case Config::kOpl3:
+			writeOplReg(chip, index[chip], val);
+		}
+	} else {
+		switch(_type) {
+		case Config::kOpl2:
+			index[0] = val;
+			break;
+		case Config::kDualOpl2:
+			if (port & 8) {
+				index[0] = val;
+				index[1] = val;
+			} else
+				index[chip] = val;
+			break;
+		case Config::kOpl3:
+			index[chip] = val;
+		}
+	}
+}
+
+byte OPL::read(int port) {
+	return 0;
+}
+
+void OPL::writeReg(int r, int v) {
+	switch (_type) {
+	case Config::kOpl2:
+		writeOplReg(0, r, v);
+		break;
+	case Config::kDualOpl2:
+		writeOplReg(0, r, v);
+		writeOplReg(1, r, v);
+		break;
+	case Config::kOpl3:
+		writeOplReg(r >= 0x100, r & 0xff, v);
+	}
+}
+
+void OPL::writeOplReg(int c, int r, int v) {
+	if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
+	} else if (r == 0x08 && c == 0) {
+		_params.kbd_split = (v >> 6) & 0x1;
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
+	} else if (r == 0xbd && c == 0) {
+		_params.hihat = v & 0x1;
+		_params.cymbal = (v >> 1) & 0x1;
+		_params.tomtom = (v >> 2) & 0x1;
+		_params.snare = (v >> 3) & 0x1;
+		_params.bass = (v >> 4) & 0x1;
+		_params.rhythm = (v >> 5) & 0x1;
+		_params.vib_depth = (v >> 6) & 0x1;
+		_params.am_depth = (v >> 7) & 0x1;
+		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
+	} else if (r < 0xa0 || r >= 0xe0) {
+		// Operator
+		int idx = regOffsetToOper[r & 0x1f];
+
+		if (idx == -1)
+			return;
+
+		if (c == 1)
+			idx += kOpl2Operators;
+
+		switch (r & 0xf0) {
+		case 0x20:
+		case 0x30:
+			_oper[idx].harmonic = v & 0xf;
+			_oper[idx].kbd_scale = (v >> 4) & 0x1;
+			_oper[idx].do_sustain = (v >> 5) & 0x1;
+			_oper[idx].vibrato = (v >> 6) & 0x1;
+			_oper[idx].am = (v >> 7) & 0x1;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0x40:
+		case 0x50:
+			_oper[idx].volume = ~v & 0x3f;
+			_oper[idx].scale_level = (v >> 6) & 0x3;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0x60:
+		case 0x70:
+			_oper[idx].decay = v & 0xf;
+			_oper[idx].attack = (v >> 4) & 0xf;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0x80:
+		case 0x90:
+			_oper[idx].release = v & 0xf;
+			_oper[idx].sustain = (v >> 4) & 0xf;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+			break;
+		case 0xe0:
+		case 0xf0:
+			_oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
+		}
+	} else {
+		// Voice
+		int idx = r & 0xf;
+
+		if (idx >= kOpl2Voices)
+			return;
+
+		if (c == 1)
+			idx += kOpl2Voices;
+
+		int opIdx = voiceToOper0[idx];
+
+		switch (r & 0xf0) {
+		case 0xa0:
+			_voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
+			break;
+		case 0xb0:
+			_voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
+			_voice[idx].octave = (v >> 2) & 0x7;
+			_voice[idx].key_on = (v >> 5) & 0x1;
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
+			break;
+		case 0xc0:
+			_oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
+			_oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
+			if (_type == Config::kOpl3) {
+				_oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
+				_oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
+			}
+			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
+		}
+	}
+}
+
+OPL *create(Config::OplType type) {
+	return new OPL(type);
+}
+
+} // End of namespace ALSA
+} // End of namespace OPL
diff --git a/audio/module.mk b/audio/module.mk
index 7743c49..9e002d5 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -60,7 +60,7 @@ MODULE_OBJS := \
 
 ifdef USE_ALSA
 MODULE_OBJS += \
-	softsynth/opl/alsa.o
+	alsa_opl.o
 endif
 
 ifndef USE_ARM_SOUND_ASM
diff --git a/audio/softsynth/opl/alsa.cpp b/audio/softsynth/opl/alsa.cpp
deleted file mode 100644
index 6b9e48e..0000000
--- a/audio/softsynth/opl/alsa.cpp
+++ /dev/null
@@ -1,349 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-/* OPL implementation for hardware OPL using ALSA Direct FM API.
- *
- * Caveats and limitations:
- * - Pretends to be a softsynth (emitting silence).
- * - Dual OPL2 mode requires OPL3 hardware.
- * - Every register write leads to a series of register writes on the hardware,
- *   due to the lack of direct register access in the ALSA Direct FM API.
- * - No timers
- */
-
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-#include "common/scummsys.h"
-
-#include "common/debug.h"
-#include "common/str.h"
-#include "audio/fmopl.h"
-
-#include <sys/ioctl.h>
-#include <alsa/asoundlib.h>
-#include <sound/asound_fm.h>
-
-namespace OPL {
-namespace ALSA {
-
-class OPL : public ::OPL::RealOPL {
-private:
-	enum {
-		kOpl2Voices = 9,
-		kVoices = 18,
-		kOpl2Operators = 18,
-		kOperators = 36
-	};
-
-	Config::OplType _type;
-	int _iface;
-	snd_hwdep_t *_opl;
-	snd_dm_fm_voice _oper[kOperators];
-	snd_dm_fm_note _voice[kVoices];
-	snd_dm_fm_params _params;
-	int index[2];
-	static const int voiceToOper0[kVoices];
-	static const int regOffsetToOper[0x20];
-
-	void writeOplReg(int c, int r, int v);
-	void clear();
-
-public:
-	OPL(Config::OplType type);
-	~OPL();
-
-	bool init();
-	void reset();
-
-	void write(int a, int v);
-	byte read(int a);
-
-	void writeReg(int r, int v);
-};
-
-const int OPL::voiceToOper0[OPL::kVoices] =
-	{ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
-
-const int OPL::regOffsetToOper[0x20] =
-	{ 0,  1,  2,  3,  4,  5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
-	 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
-
-OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
-}
-
-OPL::~OPL() {
-	stop();
-
-	if (_opl) {
-		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
-		snd_hwdep_close(_opl);
-	}
-}
-
-void OPL::clear() {
-	index[0] = index[1] = 0;
-
-	memset(_oper, 0, sizeof(_oper));
-	memset(_voice, 0, sizeof(_voice));
-	memset(&_params, 0, sizeof(_params));
-
-	for (int i = 0; i < kOperators; ++i) {
-		_oper[i].op = (i / 3) % 2;
-		_oper[i].voice = (i / 6) * 3 + (i % 3);
-	}
-
-	for (int i = 0; i < kVoices; ++i)
-		_voice[i].voice = i;
-
-	// For OPL3 hardware we need to set up the panning in OPL2 modes
-	if (_iface == SND_HWDEP_IFACE_OPL3) {
-		if (_type == Config::kDualOpl2) {
-			for (int i = 0; i < kOpl2Operators; ++i)
-				_oper[i].left = 1; // FIXME below
-			for (int i = kOpl2Operators; i < kOperators; ++i)
-				_oper[i].right = 1;
-		} else if (_type == Config::kOpl2) {
-			for (int i = 0; i < kOpl2Operators; ++i) {
-				_oper[i].left = 1;
-				_oper[i].right = 1;			
-			}
-		}
-	}
-}
-
-bool OPL::init() {
-	clear();
-
-	int card = -1;
-	snd_ctl_t *ctl;
-	snd_hwdep_info_t *info;
-	snd_hwdep_info_alloca(&info);
-
-	int iface = SND_HWDEP_IFACE_OPL3;
-	if (_type == Config::kOpl2)
-		iface = SND_HWDEP_IFACE_OPL2;
-
-	// Look for OPL hwdep interface
-	while (!snd_card_next(&card) && card >= 0) {
-		int dev = -1;
-		Common::String name = Common::String::format("hw:%d", card);
-
-		if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
-			continue;
-
-		while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
-			name = Common::String::format("hw:%d,%d", card, dev);
-
-			if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) 
-				continue;
-
-			if (!snd_hwdep_info(_opl, info)) {
-				int found = snd_hwdep_info_get_iface(info);
-				// OPL3 can be used for (Dual) OPL2 mode
-				if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
-					snd_ctl_close(ctl);
-					_iface = found;
-					reset();
-					return true;
-				}
-			}
-
-			// Wrong interface, try next device
-			snd_hwdep_close(_opl);
-			_opl = nullptr;
-		}
-
-		snd_ctl_close(ctl);
-	}
-
-	return false;
-}
-
-void OPL::reset() {
-	snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
-	if (_iface == SND_HWDEP_IFACE_OPL3)
-		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
-
-	clear();
-
-	// Sync up with the hardware
-	snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
-	for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
-		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
-	for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
-		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
-}
-
-void OPL::write(int port, int val) {
-	val &= 0xff;
-	int chip = (port & 2) >> 1;
-
-	if (port & 1) {
-		switch(_type) {
-		case Config::kOpl2:
-			writeOplReg(0, index[0], val);
-			break;
-		case Config::kDualOpl2:
-			if (port & 8) {
-				writeOplReg(0, index[0], val);
-				writeOplReg(1, index[1], val);
-			} else
-				writeOplReg(chip, index[chip], val);
-			break;
-		case Config::kOpl3:
-			writeOplReg(chip, index[chip], val);
-		}
-	} else {
-		switch(_type) {
-		case Config::kOpl2:
-			index[0] = val;
-			break;
-		case Config::kDualOpl2:
-			if (port & 8) {
-				index[0] = val;
-				index[1] = val;
-			} else
-				index[chip] = val;
-			break;
-		case Config::kOpl3:
-			index[chip] = val;
-		}
-	}
-}
-
-byte OPL::read(int port) {
-	return 0;
-}
-
-void OPL::writeReg(int r, int v) {
-	switch (_type) {
-	case Config::kOpl2:
-		writeOplReg(0, r, v);
-		break;
-	case Config::kDualOpl2:
-		writeOplReg(0, r, v);
-		writeOplReg(1, r, v);
-		break;
-	case Config::kOpl3:
-		writeOplReg(r >= 0x100, r & 0xff, v);
-	}
-}
-
-void OPL::writeOplReg(int c, int r, int v) {
-	if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
-		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
-	} else if (r == 0x08 && c == 0) {
-		_params.kbd_split = (v >> 6) & 0x1;
-		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
-	} else if (r == 0xbd && c == 0) {
-		_params.hihat = v & 0x1;
-		_params.cymbal = (v >> 1) & 0x1;
-		_params.tomtom = (v >> 2) & 0x1;
-		_params.snare = (v >> 3) & 0x1;
-		_params.bass = (v >> 4) & 0x1;
-		_params.rhythm = (v >> 5) & 0x1;
-		_params.vib_depth = (v >> 6) & 0x1;
-		_params.am_depth = (v >> 7) & 0x1;
-		snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
-	} else if (r < 0xa0 || r >= 0xe0) {
-		// Operator
-		int idx = regOffsetToOper[r & 0x1f];
-
-		if (idx == -1)
-			return;
-
-		if (c == 1)
-			idx += kOpl2Operators;
-
-		switch (r & 0xf0) {
-		case 0x20:
-		case 0x30:
-			_oper[idx].harmonic = v & 0xf;
-			_oper[idx].kbd_scale = (v >> 4) & 0x1;
-			_oper[idx].do_sustain = (v >> 5) & 0x1;
-			_oper[idx].vibrato = (v >> 6) & 0x1;
-			_oper[idx].am = (v >> 7) & 0x1;
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
-			break;
-		case 0x40:
-		case 0x50:
-			_oper[idx].volume = ~v & 0x3f;
-			_oper[idx].scale_level = (v >> 6) & 0x3;
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
-			break;
-		case 0x60:
-		case 0x70:
-			_oper[idx].decay = v & 0xf;
-			_oper[idx].attack = (v >> 4) & 0xf;
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
-			break;
-		case 0x80:
-		case 0x90:
-			_oper[idx].release = v & 0xf;
-			_oper[idx].sustain = (v >> 4) & 0xf;
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
-			break;
-		case 0xe0:
-		case 0xf0:
-			_oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
-		}
-	} else {
-		// Voice
-		int idx = r & 0xf;
-
-		if (idx >= kOpl2Voices)
-			return;
-
-		if (c == 1)
-			idx += kOpl2Voices;
-
-		int opIdx = voiceToOper0[idx];
-
-		switch (r & 0xf0) {
-		case 0xa0:
-			_voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
-			break;
-		case 0xb0:
-			_voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
-			_voice[idx].octave = (v >> 2) & 0x7;
-			_voice[idx].key_on = (v >> 5) & 0x1;
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
-			break;
-		case 0xc0:
-			_oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
-			_oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
-			if (_type == Config::kOpl3) {
-				_oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
-				_oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
-			}
-			snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
-		}
-	}
-}
-
-OPL *create(Config::OplType type) {
-	return new OPL(type);
-}
-
-} // End of namespace ALSA
-} // End of namespace OPL


Commit: dbc8d3c530dd12cbe2cea4d9099f0ec05fedf6d0
    https://github.com/scummvm/scummvm/commit/dbc8d3c530dd12cbe2cea4d9099f0ec05fedf6d0
Author: clone2727 (clone2727 at gmail.com)
Date: 2015-07-09T01:32:53-04:00

Commit Message:
Merge pull request #600 from clone2727/opl_alsa

Add support for OPL output through ALSA

Changed paths:
  A audio/adlib.cpp
  A audio/alsa_opl.cpp
  A engines/queen/midiadlib.h
  R audio/softsynth/adlib.cpp
    audio/fmopl.cpp
    audio/fmopl.h
    audio/miles_adlib.cpp
    audio/module.mk
    audio/softsynth/opl/dosbox.cpp
    audio/softsynth/opl/dosbox.h
    audio/softsynth/opl/mame.cpp
    audio/softsynth/opl/mame.h
    configure
    engines/agos/drivers/accolade/adlib.cpp
    engines/cine/sound.cpp
    engines/cruise/sound.cpp
    engines/gob/gob.cpp
    engines/gob/sound/adlib.cpp
    engines/gob/sound/adlib.h
    engines/gob/sound/adlplayer.cpp
    engines/gob/sound/adlplayer.h
    engines/gob/sound/musplayer.cpp
    engines/gob/sound/musplayer.h
    engines/gob/sound/sound.cpp
    engines/gob/sound/sound.h
    engines/kyra/sound_adlib.cpp
    engines/mads/nebular/sound_nebular.cpp
    engines/mads/nebular/sound_nebular.h
    engines/mads/sound.cpp
    engines/mads/sound.h
    engines/parallaction/adlib.cpp
    engines/queen/midiadlib.cpp
    engines/queen/music.cpp
    engines/sci/sound/drivers/adlib.cpp
    engines/scumm/players/player_ad.cpp
    engines/scumm/players/player_ad.h
    engines/sherlock/scalpel/drivers/adlib.cpp
    engines/sky/music/adlibchannel.cpp
    engines/sky/music/adlibchannel.h
    engines/sky/music/adlibmusic.cpp
    engines/sky/music/adlibmusic.h
    engines/tsage/sound.cpp
    engines/tsage/sound.h
    gui/options.cpp









More information about the Scummvm-git-logs mailing list