diff options
Diffstat (limited to 'audio/softsynth')
34 files changed, 2858 insertions, 1319 deletions
diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp index 32a5f4a910..0cadea7f22 100644 --- a/audio/softsynth/adlib.cpp +++ b/audio/softsynth/adlib.cpp @@ -32,7 +32,13 @@  #include "common/translation.h"  #ifdef DEBUG_ADLIB -static int tick; +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; @@ -52,21 +58,21 @@ struct InstrumentExtra {  } PACKED_STRUCT;  struct AdLibInstrument { -	byte mod_characteristic; -	byte mod_scalingOutputLevel; -	byte mod_attackDecay; -	byte mod_sustainRelease; -	byte mod_waveformSelect; -	byte car_characteristic; -	byte car_scalingOutputLevel; -	byte car_attackDecay; -	byte car_sustainRelease; -	byte car_waveformSelect; +	byte modCharacteristic; +	byte modScalingOutputLevel; +	byte modAttackDecay; +	byte modSustainRelease; +	byte modWaveformSelect; +	byte carCharacteristic; +	byte carScalingOutputLevel; +	byte carAttackDecay; +	byte carSustainRelease; +	byte carWaveformSelect;  	byte feedback; -	byte flags_a; -	InstrumentExtra extra_a; -	byte flags_b; -	InstrumentExtra extra_b; +	byte flagsA; +	InstrumentExtra extraA; +	byte flagsB; +	InstrumentExtra extraB;  	byte duration;  } PACKED_STRUCT;  #include "common/pack-end.h" @@ -77,16 +83,20 @@ class AdLibPart : public MidiChannel {  protected:  //	AdLibPart *_prev, *_next;  	AdLibVoice *_voice; -	int16 _pitchbend; -	byte _pitchbend_factor; -	int8 _transpose_eff; -	byte _vol_eff; -	int8 _detune_eff; -	byte _modwheel; +	int16 _pitchBend; +	byte _pitchBendFactor; +	//int8 _transposeEff; +	byte _volEff; +	int8 _detuneEff; +	byte _modWheel;  	bool _pedal;  	byte _program; -	byte _pri_eff; -	AdLibInstrument _part_instr; +	byte _priEff; +	byte _pan; +	AdLibInstrument _partInstr; +#ifdef ENABLE_OPL3 +	AdLibInstrument _partInstrSecondary; +#endif  protected:  	MidiDriver_ADLIB *_owner; @@ -99,21 +109,25 @@ protected:  public:  	AdLibPart() {  		_voice = 0; -		_pitchbend = 0; -		_pitchbend_factor = 2; -		_transpose_eff = 0; -		_vol_eff = 0; -		_detune_eff = 0; -		_modwheel = 0; +		_pitchBend = 0; +		_pitchBendFactor = 2; +		//_transposeEff = 0; +		_volEff = 0; +		_detuneEff = 0; +		_modWheel = 0;  		_pedal = 0;  		_program = 0; -		_pri_eff = 0; +		_priEff = 0; +		_pan = 64;  		_owner = 0;  		_allocated = false;  		_channel = 0; -		memset(&_part_instr, 0, sizeof(_part_instr)); +		memset(&_partInstr, 0, sizeof(_partInstr)); +#ifdef ENABLE_OPL3 +		memset(&_partInstrSecondary, 0, sizeof(_partInstrSecondary)); +#endif  	}  	MidiDriver *device(); @@ -132,7 +146,7 @@ public:  	void controlChange(byte control, byte value);  	void modulationWheel(byte value);  	void volume(byte value); -	void panPosition(byte value) { return; } // Not supported +	void panPosition(byte value);  	void pitchBendFactor(byte value);  	void detune(byte value);  	void priority(byte value); @@ -162,7 +176,6 @@ public:  	void noteOff(byte note);  	void noteOn(byte note, byte velocity);  	void programChange(byte program) { } -	void pitchBend(int16 bend) { }  	// Control Change messages  	void modulationWheel(byte value) { } @@ -181,26 +194,26 @@ private:  struct Struct10 {  	byte active; -	int16 cur_val; +	int16 curVal;  	int16 count; -	uint16 max_value; -	int16 start_value; +	uint16 maxValue; +	int16 startValue;  	byte loop; -	byte table_a[4]; -	byte table_b[4]; +	byte tableA[4]; +	byte tableB[4];  	int8 unk3; -	int8 modwheel; -	int8 modwheel_last; -	uint16 speed_lo_max; -	uint16 num_steps; -	int16 speed_hi; +	int8 modWheel; +	int8 modWheelLast; +	uint16 speedLoMax; +	uint16 numSteps; +	int16 speedHi;  	int8 direction; -	uint16 speed_lo; -	uint16 speed_lo_counter; +	uint16 speedLo; +	uint16 speedLoCounter;  };  struct Struct11 { -	int16 modify_val; +	int16 modifyVal;  	byte param, flag0x40, flag0x10;  	Struct10 *s10;  }; @@ -208,11 +221,11 @@ struct Struct11 {  struct AdLibVoice {  	AdLibPart *_part;  	AdLibVoice *_next, *_prev; -	byte _waitforpedal; +	byte _waitForPedal;  	byte _note;  	byte _channel; -	byte _twochan; -	byte _vol_1, _vol_2; +	byte _twoChan; +	byte _vol1, _vol2;  	int16 _duration;  	Struct10 _s10a; @@ -220,26 +233,34 @@ struct AdLibVoice {  	Struct10 _s10b;  	Struct11 _s11b; +#ifdef ENABLE_OPL3 +	byte _secTwoChan; +	byte _secVol1, _secVol2; +#endif +  	AdLibVoice() { memset(this, 0, sizeof(AdLibVoice)); }  };  struct AdLibSetParams { -	byte a, b, c, d; +	byte registerBase; +	byte shift; +	byte mask; +	byte inversion;  }; -static const byte channel_mappings[9] = { +static const byte g_operator1Offsets[9] = {  	0, 1, 2, 8,  	9, 10, 16, 17,  	18  }; -static const byte channel_mappings_2[9] = { +static const byte g_operator2Offsets[9] = {  	3, 4, 5, 11,  	12, 13, 19, 20,  	21  }; -static const AdLibSetParams adlib_setparam_table[] = { +static const AdLibSetParams g_setParamTable[] = {  	{0x40, 0, 63, 63},  // level  	{0xE0, 2, 0, 0},    // unused  	{0x40, 6, 192, 0},  // level key scaling @@ -257,21 +278,21 @@ static const AdLibSetParams adlib_setparam_table[] = {  	{0xC0, 1, 14, 0}    // feedback  }; -static const byte param_table_1[16] = { +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 maxval_table[16] = { +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 num_steps_table[] = { +static const uint16 g_numStepsTable[] = {  	1, 2, 4, 5,  	6, 7, 8, 9,  	10, 12, 14, 16, @@ -282,7 +303,7 @@ static const uint16 num_steps_table[] = {  	600, 860, 1200, 1600  }; -static const byte note_to_f_num[] = { +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, @@ -303,188 +324,530 @@ static const byte note_to_f_num[] = {  	242, 243, 245, 247, 249, 251, 252, 254  }; -static const AdLibInstrument map_gm_to_fm[128] = { +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 }, +	{ 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 }, +	{ 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 }, +	{ 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 }, +	{ 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 }, +	{ 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 }, +	{ 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 }, +	{ 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 } +	{ 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 gm_percussion_to_fm[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 } +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 }  }; -static const byte gm_percussion_lookup[128] = { +#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, @@ -495,9 +858,9 @@ static const byte gm_percussion_lookup[128] = {  	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  }; -static byte lookup_table[64][32]; +static byte g_volumeLookupTable[64][32]; -static const byte volume_table[] = { +static const byte g_volumeTable[] = {  	0, 4, 7, 11,  	13, 16, 18, 20,  	22, 24, 26, 27, @@ -516,7 +879,7 @@ static const byte volume_table[] = {  	62, 63, 63, 63  }; -static int lookup_volume(int a, int b) { +static int lookupVolume(int a, int b) {  	if (b == 0)  		return 0; @@ -529,32 +892,32 @@ static int lookup_volume(int a, int b) {  	if (b < 0) {  		if (a < 0) { -			return lookup_table[-a][-b]; +			return g_volumeLookupTable[-a][-b];  		} else { -			return -lookup_table[a][-b]; +			return -g_volumeLookupTable[a][-b];  		}  	} else {  		if (a < 0) { -			return -lookup_table[-a][b]; +			return -g_volumeLookupTable[-a][b];  		} else { -			return lookup_table[a][b]; +			return g_volumeLookupTable[a][b];  		}  	}  } -static void create_lookup_table() { +static void createLookupTable() {  	int i, j;  	int sum;  	for (i = 0; i < 64; i++) {  		sum = i;  		for (j = 0; j < 32; j++) { -			lookup_table[i][j] = sum >> 5; +			g_volumeLookupTable[i][j] = sum >> 5;  			sum += i;  		}  	}  	for (i = 0; i < 64; i++) -		lookup_table[i][0] = 0; +		g_volumeLookupTable[i][0] = 0;  }  //////////////////////////////////////// @@ -584,58 +947,75 @@ public:  	// AudioStream API -	bool isStereo() const { return false; } +	bool isStereo() const { return _opl->isStereo(); }  	int getRate() const { return _mixer->getOutputRate(); }  private: -	bool _scummSmallHeader;	// FIXME: This flag controls a special mode for SCUMM V3 games +	bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games +#ifdef ENABLE_OPL3 +	bool _opl3Mode; +#endif -	FM_OPL *_opl; -	byte *_adlib_reg_cache; +	OPL::OPL *_opl; +	byte *_regCache; +#ifdef ENABLE_OPL3 +	byte *_regCacheSecondary; +#endif -	int _adlib_timer_counter; +	int _timerCounter; -	uint16 channel_table_2[9]; -	int _voice_index; -	int _timer_p; -	int _timer_q; -	uint16 curnote_table[9]; +	uint16 _channelTable2[9]; +	int _voiceIndex; +	int _timerIncrease; +	int _timerThreshold; +	uint16 _curNotTable[9];  	AdLibVoice _voices[9];  	AdLibPart _parts[32];  	AdLibPercussionChannel _percussion;  	void generateSamples(int16 *buf, int len);  	void onTimer(); -	void part_key_on(AdLibPart *part, AdLibInstrument *instr, byte note, byte velocity); -	void part_key_off(AdLibPart *part, byte note); - -	void adlib_key_off(int chan); -	void adlib_note_on(int chan, byte note, int mod); -	void adlib_note_on_ex(int chan, byte note, int mod); -	int adlib_get_reg_value_param(int chan, byte data); -	void adlib_setup_channel(int chan, AdLibInstrument *instr, byte vol_1, byte vol_2); -	byte adlib_get_reg_value(byte reg) { -		return _adlib_reg_cache[reg]; -	} -	void adlib_set_param(int channel, byte param, int value); -	void adlib_key_onoff(int channel); -	void adlib_write(byte reg, byte value); -	void adlib_playnote(int channel, int note); - -	AdLibVoice *allocate_voice(byte pri); - -	void mc_off(AdLibVoice *voice); - -	static void link_mc(AdLibPart *part, AdLibVoice *voice); -	void mc_inc_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11); -	void mc_init_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags, -						InstrumentExtra *ie); - -	void struct10_init(Struct10 *s10, InstrumentExtra *ie); -	static byte struct10_ontimer(Struct10 *s10, Struct11 *s11); -	static void struct10_setup(Struct10 *s10); -	static int random_nr(int a); -	void mc_key_on(AdLibVoice *voice, AdLibInstrument *instr, byte note, byte velocity); +	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 @@ -643,7 +1023,7 @@ private:  void AdLibPart::init(MidiDriver_ADLIB *owner, byte channel) {  	_owner = owner;  	_channel = channel; -	_pri_eff = 127; +	_priEff = 127;  	programChange(0);  } @@ -657,41 +1037,64 @@ void AdLibPart::send(uint32 b) {  void AdLibPart::noteOff(byte note) {  #ifdef DEBUG_ADLIB -	debug(6, "%10d: noteOff(%d)", tick, note); +	debug(6, "%10d: noteOff(%d)", g_tick, note);  #endif -	_owner->part_key_off(this, note); +	_owner->partKeyOff(this, note);  }  void AdLibPart::noteOn(byte note, byte velocity) {  #ifdef DEBUG_ADLIB -	debug(6, "%10d: noteOn(%d,%d)", tick, note, velocity); +	debug(6, "%10d: noteOn(%d,%d)", g_tick, note, velocity); +#endif +	_owner->partKeyOn(this, &_partInstr, note, velocity, +#ifdef ENABLE_OPL3 +			&_partInstrSecondary, +#else +			NULL,  #endif -	_owner->part_key_on(this, &_part_instr, note, velocity); +			_pan);  }  void AdLibPart::programChange(byte program) {  	if (program > 127)  		return; -/* +	/*  	uint i;  	uint count = 0; -	for (i = 0; i < ARRAYSIZE(map_gm_to_fm[0]); ++i) -		count += map_gm_to_fm[program][i]; +	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; -	memcpy(&_part_instr, &map_gm_to_fm[program], sizeof(AdLibInstrument)); +#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; +	_pitchBend = bend;  	for (voice = _voice; voice; voice = voice->_next) { -		_owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, -						(_pitchbend * _pitchbend_factor >> 6) + _detune_eff); +#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  	}  } @@ -699,75 +1102,137 @@ void AdLibPart::controlChange(byte control, byte value) {  	switch (control) {  	case 0:  	case 32: -		break; // Bank select. Not supported -	case 1:   modulationWheel(value); break; -	case 7:   volume(value); break; -	case 10:  break; // Pan position. Not supported. -	case 16:  pitchBendFactor(value); break; -	case 17:  detune(value); break; -	case 18:  priority(value); break; -	case 64:  sustain(value > 0); break; -	case 91:  break; // Effects level. Not supported. -	case 93:  break; // Chorus level. Not supported. -	case 119: break; // Unknown, used in Simon the Sorcerer 2 -	case 121: // reset all controllers +		// 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; +	case 123: +		allNotesOff(); +		break;  	default: -		warning("AdLib: Unknown control change message %d (%d)", (int) control, (int)value); +		warning("AdLib: Unknown control change message %d (%d)", (int)control, (int)value);  	}  }  void AdLibPart::modulationWheel(byte value) {  	AdLibVoice *voice; -	_modwheel = value; +	_modWheel = value;  	for (voice = _voice; voice; voice = voice->_next) {  		if (voice->_s10a.active && voice->_s11a.flag0x40) -			voice->_s10a.modwheel = _modwheel >> 2; +			voice->_s10a.modWheel = _modWheel >> 2;  		if (voice->_s10b.active && voice->_s11b.flag0x40) -			voice->_s10b.modwheel = _modwheel >> 2; +			voice->_s10b.modWheel = _modWheel >> 2;  	}  }  void AdLibPart::volume(byte value) {  	AdLibVoice *voice; -	_vol_eff = value; +	_volEff = value;  	for (voice = _voice; voice; voice = voice->_next) { -		_owner->adlib_set_param(voice->_channel, 0, volume_table[lookup_table[voice->_vol_2][_vol_eff >> 2]]); -		if (voice->_twochan) { -			_owner->adlib_set_param(voice->_channel, 13, volume_table[lookup_table[voice->_vol_1][_vol_eff >> 2]]); +#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; -	_pitchbend_factor = value; +	_pitchBendFactor = value;  	for (voice = _voice; voice; voice = voice->_next) { -		_owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, -							(_pitchbend * _pitchbend_factor >> 6) + _detune_eff); +		_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; -	_detune_eff = value; +	_detuneEff = value;  	for (voice = _voice; voice; voice = voice->_next) { -		_owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, -						(_pitchbend * _pitchbend_factor >> 6) + _detune_eff); +		_owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, +							  (_pitchBend * _pitchBendFactor >> 6) + _detuneEff);  	}  }  void AdLibPart::priority(byte value) { -	_pri_eff = value; +	_priEff = value;  }  void AdLibPart::sustain(bool value) { @@ -776,20 +1241,29 @@ void AdLibPart::sustain(bool value) {  	_pedal = value;  	if (!value) {  		for (voice = _voice; voice; voice = voice->_next) { -			if (voice->_waitforpedal) -				_owner->mc_off(voice); +			if (voice->_waitForPedal) +				_owner->mcOff(voice);  		}  	}  }  void AdLibPart::allNotesOff() {  	while (_voice) -		_owner->mc_off(_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(&_part_instr, instr, sizeof(AdLibInstrument)); +		memcpy(&_partInstr, instr, sizeof(AdLibInstrument));  	}  } @@ -803,8 +1277,8 @@ AdLibPercussionChannel::~AdLibPercussionChannel() {  void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) {  	AdLibPart::init(owner, channel); -	_pri_eff = 0; -	_vol_eff = 127; +	_priEff = 0; +	_volEff = 127;  	// Initialize the custom instruments data  	memset(_notes, 0, sizeof(_notes)); @@ -812,33 +1286,49 @@ void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) {  }  void AdLibPercussionChannel::noteOff(byte note) { -	// Jamieson630: Unless I run into a specific instrument that -	// may require a key off, I'm going to ignore this message. -	// The rationale is that a percussion instrument should -	// fade out of its own accord, and the AdLib instrument -	// definitions used should follow this rule. Since -	// percussion voices are allocated at the lowest priority -	// anyway, we know that "hanging" percussion sounds will -	// not prevent later musical instruments (or even other -	// percussion sounds) from playing. -/* -	_owner->part_key_off(this, 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) { -	AdLibInstrument *inst = NULL; +	const AdLibInstrument *inst = NULL; +	const AdLibInstrument *sec  = NULL;  	// The custom instruments have priority over the default mapping -	inst = _customInstruments[note]; -	if (inst) -		note = _notes[note]; +	// 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 as a fallback -		byte key = gm_percussion_lookup[note]; -		if (key != 0xFF) -			inst = &gm_percussion_to_fm[key]; +		// 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) { @@ -846,10 +1336,18 @@ void AdLibPercussionChannel::noteOn(byte note, byte velocity) {  		return;  	} -	_owner->part_key_on(this, inst, note, velocity); +	_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]; @@ -861,16 +1359,16 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins  		}  		// Save the new instrument data -		_customInstruments[note]->mod_characteristic     = instr[2]; -		_customInstruments[note]->mod_scalingOutputLevel = instr[3]; -		_customInstruments[note]->mod_attackDecay        = instr[4]; -		_customInstruments[note]->mod_sustainRelease     = instr[5]; -		_customInstruments[note]->mod_waveformSelect     = instr[6]; -		_customInstruments[note]->car_characteristic     = instr[7]; -		_customInstruments[note]->car_scalingOutputLevel = instr[8]; -		_customInstruments[note]->car_attackDecay        = instr[9]; -		_customInstruments[note]->car_sustainRelease     = instr[10]; -		_customInstruments[note]->car_waveformSelect     = instr[11]; +		_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];  	}  } @@ -882,21 +1380,28 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)  	uint i;  	_scummSmallHeader = false; +#ifdef ENABLE_OPL3 +	_opl3Mode = false; +#endif -	_adlib_reg_cache = 0; +	_regCache = 0; +#ifdef ENABLE_OPL3 +	_regCacheSecondary = 0; +#endif -	_adlib_timer_counter = 0; -	_voice_index = 0; -	for (i = 0; i < ARRAYSIZE(curnote_table); ++i) { -		curnote_table[i] = 0; +	_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); -	_timer_p = 0xD69; -	_timer_q = 0x411B; +	_timerIncrease = 0xD69; +	_timerThreshold = 0x411B; +	_opl = 0;  }  int MidiDriver_ADLIB::open() { @@ -914,14 +1419,37 @@ int MidiDriver_ADLIB::open() {  		voice->_s11b.s10 = &voice->_s10a;  	} -	_adlib_reg_cache = (byte *)calloc(256, 1); +	// Try to use OPL3 when requested. +#ifdef ENABLE_OPL3 +	if (_opl3Mode) { +		_opl = OPL::Config::create(OPL::Config::kOpl3); +	} -	_opl = makeAdLibOPL(getRate()); +	// Initialize plain OPL2 when no OPL3 is intiailized already. +	if (!_opl) { +#endif +		_opl = OPL::Config::create(); +#ifdef ENABLE_OPL3 +		_opl3Mode = false; +	} +#endif +	_opl->init(getRate()); -	adlib_write(1, 0x20); -	adlib_write(8, 0x40); -	adlib_write(0xBD, 0x00); -	create_lookup_table(); +	_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  	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); @@ -938,14 +1466,17 @@ void MidiDriver_ADLIB::close() {  	uint i;  	for (i = 0; i < ARRAYSIZE(_voices); ++i) {  		if (_voices[i]._part) -			mc_off(&_voices[i]); +			mcOff(&_voices[i]);  	}  	// Turn off the OPL emulation -	OPLDestroy(_opl); -//	YM3812Shutdown(); +	delete _opl; +	_opl = 0; -	free(_adlib_reg_cache); +	free(_regCache); +#ifdef ENABLE_OPL3 +	free(_regCacheSecondary); +#endif  }  void MidiDriver_ADLIB::send(uint32 b) { @@ -954,9 +1485,9 @@ void MidiDriver_ADLIB::send(uint32 b) {  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); +	byte param2 = (byte)((b >> 16) & 0xFF); +	byte param1 = (byte)((b >>  8) & 0xFF); +	byte cmd    = (byte)(b & 0xF0);  	AdLibPart *part;  	if (chan == 9) @@ -997,29 +1528,42 @@ void MidiDriver_ADLIB::send(byte chan, uint32 b) {  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) { -				_timer_p = 473; -				_timer_q = 1000; -			} else { -				_timer_p = 0xD69; -				_timer_q = 0x411B; -			} -			return 1; +	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->_pitchbend_factor = range; +	part->_pitchBendFactor = range;  	for (voice = part->_voice; voice; voice = voice->_next) { -		adlib_note_on(voice->_channel, voice->_note + part->_transpose_eff, -					(part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff); +		adlibNoteOn(voice->_channel, voice->_note/* + part->_transposeEff*/, +					  (part->_pitchBend * part->_pitchBendFactor >> 6) + part->_detuneEff);  	}  } @@ -1043,54 +1587,77 @@ MidiChannel *MidiDriver_ADLIB::allocateChannel() {  // All the code brought over from IMuseAdLib -void MidiDriver_ADLIB::adlib_write(byte reg, byte value) { -	if (_adlib_reg_cache[reg] == value) +void MidiDriver_ADLIB::adlibWrite(byte reg, byte value) { +	if (_regCache[reg] == value) {  		return; +	}  #ifdef DEBUG_ADLIB -	debug(6, "%10d: adlib_write[%x] = %x", tick, reg, value); +	debug(6, "%10d: adlibWrite[%x] = %x", g_tick, reg, value);  #endif -	_adlib_reg_cache[reg] = value; +	_regCache[reg] = value; -	OPLWriteReg(_opl, 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::generateSamples(int16 *data, int len) { -	memset(data, 0, sizeof(int16) * len); -	YM3812UpdateOne(_opl, data, len); +	if (_opl->isStereo()) { +		len *= 2; +	} +	_opl->readBuffer(data, len);  }  void MidiDriver_ADLIB::onTimer() { -	AdLibVoice *voice; -	int i; - -	_adlib_timer_counter += _timer_p; -	while (_adlib_timer_counter >= _timer_q) { -		_adlib_timer_counter -= _timer_q; +	_timerCounter += _timerIncrease; +	while (_timerCounter >= _timerThreshold) { +		_timerCounter -= _timerThreshold;  #ifdef DEBUG_ADLIB -		tick++; +		g_tick++;  #endif -		voice = _voices; -		for (i = 0; i != ARRAYSIZE(_voices); i++, voice++) { -			if (!voice->_part) -				continue; -			if (voice->_duration && (voice->_duration -= 0x11) <= 0) { -				mc_off(voice); -				return; -			} -			if (voice->_s10a.active) { -				mc_inc_stuff(voice, &voice->_s10a, &voice->_s11a); -			} -			if (voice->_s10b.active) { -				mc_inc_stuff(voice, &voice->_s10b, &voice->_s11b); +		// 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::mc_off(AdLibVoice *voice) { +void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) {  	AdLibVoice *tmp; -	adlib_key_off(voice->_channel); +	adlibKeyOff(voice->_channel);  	tmp = voice->_prev; @@ -1103,57 +1670,62 @@ void MidiDriver_ADLIB::mc_off(AdLibVoice *voice) {  	voice->_part = NULL;  } -void MidiDriver_ADLIB::mc_inc_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) { +void MidiDriver_ADLIB::mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) {  	byte code;  	AdLibPart *part = voice->_part; -	code = struct10_ontimer(s10, s11); +	code = struct10OnTimer(s10, s11);  	if (code & 1) {  		switch (s11->param) {  		case 0: -			voice->_vol_2 = s10->start_value + s11->modify_val; +			voice->_vol2 = s10->startValue + s11->modifyVal;  			if (!_scummSmallHeader) { -				adlib_set_param(voice->_channel, 0, -												volume_table[lookup_table[voice->_vol_2] -																		 [part->_vol_eff >> 2]]); +				adlibSetParam(voice->_channel, 0, +								g_volumeTable[g_volumeLookupTable[voice->_vol2] +											  [part->_volEff >> 2]]);  			} else { -				adlib_set_param(voice->_channel, 0, voice->_vol_2); +				adlibSetParam(voice->_channel, 0, voice->_vol2);  			}  			break;  		case 13: -			voice->_vol_1 = s10->start_value + s11->modify_val; -			if (voice->_twochan && !_scummSmallHeader) { -				adlib_set_param(voice->_channel, 13, -												volume_table[lookup_table[voice->_vol_1] -												[part->_vol_eff >> 2]]); +			voice->_vol1 = s10->startValue + s11->modifyVal; +			if (voice->_twoChan && !_scummSmallHeader) { +				adlibSetParam(voice->_channel, 13, +								g_volumeTable[g_volumeLookupTable[voice->_vol1] +											  [part->_volEff >> 2]]);  			} else { -				adlib_set_param(voice->_channel, 13, voice->_vol_1); +				adlibSetParam(voice->_channel, 13, voice->_vol1);  			}  			break;  		case 30: -			s11->s10->modwheel = (char)s11->modify_val; +			s11->s10->modWheel = (char)s11->modifyVal;  			break;  		case 31: -			s11->s10->unk3 = (char)s11->modify_val; +			s11->s10->unk3 = (char)s11->modifyVal;  			break;  		default: -			adlib_set_param(voice->_channel, s11->param, -							s10->start_value + s11->modify_val); +			adlibSetParam(voice->_channel, s11->param, +							s10->startValue + s11->modifyVal);  			break;  		}  	}  	if (code & 2 && s11->flag0x10) -		adlib_key_onoff(voice->_channel); +		adlibKeyOnOff(voice->_channel);  } -void MidiDriver_ADLIB::adlib_key_off(int chan){ +void MidiDriver_ADLIB::adlibKeyOff(int chan) {  	byte reg = chan + 0xB0; -	adlib_write(reg, adlib_get_reg_value(reg) & ~0x20); +	adlibWrite(reg, adlibGetRegValue(reg) & ~0x20); +#ifdef ENABLE_OPL3 +	if (_opl3Mode) { +		adlibWriteSecondary(reg, adlibGetRegValueSecondary(reg) & ~0x20); +	} +#endif  } -byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) { +byte MidiDriver_ADLIB::struct10OnTimer(Struct10 *s10, Struct11 *s11) {  	byte result = 0;  	int i; @@ -1162,51 +1734,54 @@ byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) {  		return 0;  	} -	i = s10->cur_val + s10->speed_hi; -	s10->speed_lo_counter += s10->speed_lo; -	if (s10->speed_lo_counter >= s10->speed_lo_max) { -		s10->speed_lo_counter -= s10->speed_lo_max; +	i = s10->curVal + s10->speedHi; +	s10->speedLoCounter += s10->speedLo; +	if (s10->speedLoCounter >= s10->speedLoMax) { +		s10->speedLoCounter -= s10->speedLoMax;  		i += s10->direction;  	} -	if (s10->cur_val != i || s10->modwheel != s10->modwheel_last) { -		s10->cur_val = i; -		s10->modwheel_last = s10->modwheel; -		i = lookup_volume(i, s10->modwheel_last); -		if (i != s11->modify_val) { -			s11->modify_val = i; +	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->num_steps) { +	if (!--s10->numSteps) {  		s10->active++;  		if (s10->active > 4) {  			if (s10->loop) {  				s10->active = 1;  				result |= 2; -				struct10_setup(s10); +				struct10Setup(s10);  			} else {  				s10->active = 0;  			}  		} else { -			struct10_setup(s10); +			struct10Setup(s10);  		}  	}  	return result;  } -void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) { +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 = channel_mappings_2[channel]; +		reg = g_operator2Offsets[channel];  	} else if (param <= 25) {  		param -= 13; -		reg = channel_mappings[channel]; +		reg = g_operator1Offsets[channel];  	} else if (param <= 27) {  		param -= 13;  		reg = channel; @@ -1216,54 +1791,66 @@ void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) {  		else  			value -= 383;  		value <<= 4; -		channel_table_2[channel] = value; -		adlib_playnote(channel, curnote_table[channel] + value); +		_channelTable2[channel] = value; +		adlibPlayNote(channel, _curNotTable[channel] + value);  		return;  	} else {  		return;  	} -	as = &adlib_setparam_table[param]; -	if (as->d) -		value = as->d - value; -	reg += as->a; -	adlib_write(reg, (adlib_get_reg_value(reg) & ~as->c) | (((byte)value) << as->b)); +	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::adlib_key_onoff(int channel) { +void MidiDriver_ADLIB::adlibKeyOnOff(int channel) { +#ifdef ENABLE_OPL3 +	assert(!_opl3Mode); +#endif +  	byte val;  	byte reg = channel + 0xB0;  	assert(channel >= 0 && channel < 9); -	val = adlib_get_reg_value(reg); -	adlib_write(reg, val & ~0x20); -	adlib_write(reg, val | 0x20); +	val = adlibGetRegValue(reg); +	adlibWrite(reg, val & ~0x20); +	adlibWrite(reg, val | 0x20);  } -void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { +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->table_a[f]; -	e = num_steps_table[lookup_table[t & 0x7F][b]]; +	t = s10->tableA[f]; +	e = g_numStepsTable[g_volumeLookupTable[t & 0x7F][b]];  	if (t & 0x80) { -		e = random_nr(e); +		e = randomNr(e);  	}  	if (e == 0)  		e++; -	s10->num_steps = s10->speed_lo_max = e; +	s10->numSteps = s10->speedLoMax = e;  	if (f != 2) { -		c = s10->max_value; -		g = s10->start_value; -		t = s10->table_b[f]; -		d = lookup_volume(c, (t & 0x7F) - 31); +		c = s10->maxValue; +		g = s10->startValue; +		t = s10->tableB[f]; +		d = lookupVolume(c, (t & 0x7F) - 31);  		if (t & 0x80) { -			d = random_nr(d); +			d = randomNr(d);  		}  		if (d + g > c) {  			h = c - g; @@ -1272,12 +1859,12 @@ void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) {  			if (d + g < 0)  				h = -g;  		} -		h -= s10->cur_val; +		h -= s10->curVal;  	} else {  		h = 0;  	} -	s10->speed_hi = h / e; +	s10->speedHi = h / e;  	if (h < 0) {  		h = -h;  		s10->direction = -1; @@ -1285,11 +1872,11 @@ void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) {  		s10->direction = 1;  	} -	s10->speed_lo = h % e; -	s10->speed_lo_counter = 0; +	s10->speedLo = h % e; +	s10->speedLoCounter = 0;  } -void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { +void MidiDriver_ADLIB::adlibPlayNote(int channel, int note) {  	byte old, oct, notex;  	int note2;  	int i; @@ -1304,7 +1891,7 @@ void MidiDriver_ADLIB::adlib_playnote(int channel, int note) {  		oct <<= 2;  	notex = note2 % 12 + 3; -	old = adlib_get_reg_value(channel + 0xB0); +	old = adlibGetRegValue(channel + 0xB0);  	if (old & 0x20) {  		old &= ~0x20;  		if (oct > old) { @@ -1321,58 +1908,58 @@ void MidiDriver_ADLIB::adlib_playnote(int channel, int note) {  	}  	i = (notex << 3) + ((note >> 4) & 0x7); -	adlib_write(channel + 0xA0, note_to_f_num[i]); -	adlib_write(channel + 0xB0, oct | 0x20); +	adlibWrite(channel + 0xA0, g_noteFrequencies[i]); +	adlibWrite(channel + 0xB0, oct | 0x20);  } -int MidiDriver_ADLIB::random_nr(int a) { -	static byte _rand_seed = 1; -	if (_rand_seed & 1) { -		_rand_seed >>= 1; -		_rand_seed ^= 0xB8; +int MidiDriver_ADLIB::randomNr(int a) { +	static byte _randSeed = 1; +	if (_randSeed & 1) { +		_randSeed >>= 1; +		_randSeed ^= 0xB8;  	} else { -		_rand_seed >>= 1; +		_randSeed >>= 1;  	} -	return _rand_seed * a >> 8; +	return _randSeed * a >> 8;  } -void MidiDriver_ADLIB::part_key_off(AdLibPart *part, byte note) { +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; +				voice->_waitForPedal = true;  			else -				mc_off(voice); +				mcOff(voice);  		}  	}  } -void MidiDriver_ADLIB::part_key_on(AdLibPart *part, AdLibInstrument *instr, byte note, byte velocity) { +void MidiDriver_ADLIB::partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) {  	AdLibVoice *voice; -	voice = allocate_voice(part->_pri_eff); +	voice = allocateVoice(part->_priEff);  	if (!voice)  		return; -	link_mc(part, voice); -	mc_key_on(voice, instr, note, velocity); +	linkMc(part, voice); +	mcKeyOn(voice, instr, note, velocity, second, pan);  } -AdLibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) { +AdLibVoice *MidiDriver_ADLIB::allocateVoice(byte pri) {  	AdLibVoice *ac, *best = NULL;  	int i;  	for (i = 0; i < 9; i++) { -		if (++_voice_index >= 9) -			_voice_index = 0; -		ac = &_voices[_voice_index]; +		if (++_voiceIndex >= 9) +			_voiceIndex = 0; +		ac = &_voices[_voiceIndex];  		if (!ac->_part)  			return ac;  		if (!ac->_next) { -			if (ac->_part->_pri_eff <= pri) { -				pri = ac->_part->_pri_eff; +			if (ac->_part->_priEff <= pri) { +				pri = ac->_part->_priEff;  				best = ac;  			}  		} @@ -1383,11 +1970,11 @@ AdLibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) {  		return NULL;  	if (best) -		mc_off(best); +		mcOff(best);  	return best;  } -void MidiDriver_ADLIB::link_mc(AdLibPart *part, AdLibVoice *voice) { +void MidiDriver_ADLIB::linkMc(AdLibPart *part, AdLibVoice *voice) {  	voice->_part = part;  	voice->_next = (AdLibVoice *)part->_voice;  	part->_voice = voice; @@ -1397,153 +1984,229 @@ void MidiDriver_ADLIB::link_mc(AdLibPart *part, AdLibVoice *voice) {  		voice->_next->_prev = voice;  } -void MidiDriver_ADLIB::mc_key_on(AdLibVoice *voice, AdLibInstrument *instr, byte note, byte velocity) { +void MidiDriver_ADLIB::mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) {  	AdLibPart *part = voice->_part; -	int c; -	byte vol_1, vol_2; +	byte vol1, vol2; +#ifdef ENABLE_OPL3 +	byte secVol1 = 0, secVol2 = 0; +#endif -	voice->_twochan = instr->feedback & 1; +	voice->_twoChan = instr->feedback & 1;  	voice->_note = note; -	voice->_waitforpedal = false; +	voice->_waitForPedal = false;  	voice->_duration = instr->duration;  	if (voice->_duration != 0)  		voice->_duration *= 63; -	if (!_scummSmallHeader) -		vol_1 = (instr->mod_scalingOutputLevel & 0x3F) + lookup_table[velocity >> 1][instr->mod_waveformSelect >> 2]; -	else -		vol_1 = 0x3f - (instr->mod_scalingOutputLevel & 0x3F); -	if (vol_1 > 0x3F) -		vol_1 = 0x3F; -	voice->_vol_1 = vol_1; - -	if (!_scummSmallHeader) -		vol_2 = (instr->car_scalingOutputLevel & 0x3F) + lookup_table[velocity >> 1][instr->car_waveformSelect >> 2]; -	else -		vol_2 = 0x3f - (instr->car_scalingOutputLevel & 0x3F); -	if (vol_2 > 0x3F) -		vol_2 = 0x3F; -	voice->_vol_2 = vol_2; +	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; -	c = part->_vol_eff >> 2; +	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) { -		vol_2 = volume_table[lookup_table[vol_2][c]]; -		if (voice->_twochan) -			vol_1 = volume_table[lookup_table[vol_1][c]]; +#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  	} -	adlib_setup_channel(voice->_channel, instr, vol_1, vol_2); -	adlib_note_on_ex(voice->_channel, part->_transpose_eff + note, part->_detune_eff + (part->_pitchbend * part->_pitchbend_factor >> 6)); +	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->flags_a & 0x80) { -		mc_init_stuff(voice, &voice->_s10a, &voice->_s11a, instr->flags_a, &instr->extra_a); -	} else { -		voice->_s10a.active = 0; -	} +		if (instr->flagsA & 0x80) { +			mcInitStuff(voice, &voice->_s10a, &voice->_s11a, instr->flagsA, &instr->extraA); +		} else { +			voice->_s10a.active = 0; +		} -	if (instr->flags_b & 0x80) { -		mc_init_stuff(voice, &voice->_s10b, &voice->_s11b, instr->flags_b, &instr->extra_b); +		if (instr->flagsB & 0x80) { +			mcInitStuff(voice, &voice->_s10b, &voice->_s11b, instr->flagsB, &instr->extraB); +		} else { +			voice->_s10b.active = 0; +		} +#ifdef ENABLE_OPL3  	} else { -		voice->_s10b.active = 0; +		adlibSetupChannelSecondary(voice->_channel, second, secVol1, secVol2, pan); +		adlibNoteOnEx(voice->_channel, note, part->_pitchBend >> 1);  	} +#endif  } -void MidiDriver_ADLIB::adlib_setup_channel(int chan, AdLibInstrument *instr, byte vol_1, byte vol_2) { -	byte channel; - +void MidiDriver_ADLIB::adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2) {  	assert(chan >= 0 && chan < 9); -	channel = channel_mappings[chan]; -	adlib_write(channel + 0x20, instr->mod_characteristic); -	adlib_write(channel + 0x40, (instr->mod_scalingOutputLevel | 0x3F) - vol_1 ); -	adlib_write(channel + 0x60, 0xff & (~instr->mod_attackDecay)); -	adlib_write(channel + 0x80, 0xff & (~instr->mod_sustainRelease)); -	adlib_write(channel + 0xE0, instr->mod_waveformSelect); - -	channel = channel_mappings_2[chan]; -	adlib_write(channel + 0x20, instr->car_characteristic); -	adlib_write(channel + 0x40, (instr->car_scalingOutputLevel | 0x3F) - vol_2 ); -	adlib_write(channel + 0x60, 0xff & (~instr->car_attackDecay)); -	adlib_write(channel + 0x80, 0xff & (~instr->car_sustainRelease)); -	adlib_write(channel + 0xE0, instr->car_waveformSelect); - -	adlib_write((byte)chan + 0xC0, instr->feedback); +	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 +			);  } -void MidiDriver_ADLIB::adlib_note_on_ex(int chan, byte note, int mod) { -	int code; +#ifdef ENABLE_OPL3 +void MidiDriver_ADLIB::adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan) {  	assert(chan >= 0 && chan < 9); -	code = (note << 7) + mod; -	curnote_table[chan] = code; -	channel_table_2[chan] = 0; -	adlib_playnote(chan, code); +	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::mc_init_stuff(AdLibVoice *voice, Struct10 *s10, -									Struct11 *s11, byte flags, InstrumentExtra *ie) { +void MidiDriver_ADLIB::mcInitStuff(AdLibVoice *voice, Struct10 *s10, +									 Struct11 *s11, byte flags, const InstrumentExtra *ie) {  	AdLibPart *part = voice->_part; -	s11->modify_val = 0; +	s11->modifyVal = 0;  	s11->flag0x40 = flags & 0x40;  	s10->loop = flags & 0x20;  	s11->flag0x10 = flags & 0x10; -	s11->param = param_table_1[flags & 0xF]; -	s10->max_value = maxval_table[flags & 0xF]; +	s11->param = g_paramTable1[flags & 0xF]; +	s10->maxValue = g_maxValTable[flags & 0xF];  	s10->unk3 = 31;  	if (s11->flag0x40) { -		s10->modwheel = part->_modwheel >> 2; +		s10->modWheel = part->_modWheel >> 2;  	} else { -		s10->modwheel = 31; +		s10->modWheel = 31;  	}  	switch (s11->param) {  	case 0: -		s10->start_value = voice->_vol_2; +		s10->startValue = voice->_vol2;  		break;  	case 13: -		s10->start_value = voice->_vol_1; +		s10->startValue = voice->_vol1;  		break;  	case 30: -		s10->start_value = 31; -		s11->s10->modwheel = 0; +		s10->startValue = 31; +		s11->s10->modWheel = 0;  		break;  	case 31: -		s10->start_value = 0; +		s10->startValue = 0;  		s11->s10->unk3 = 0;  		break;  	default: -		s10->start_value = adlib_get_reg_value_param(voice->_channel, s11->param); +		s10->startValue = adlibGetRegValueParam(voice->_channel, s11->param);  	} -	struct10_init(s10, ie); +	struct10Init(s10, ie);  } -void MidiDriver_ADLIB::struct10_init(Struct10 *s10, InstrumentExtra *ie) { +void MidiDriver_ADLIB::struct10Init(Struct10 *s10, const InstrumentExtra *ie) {  	s10->active = 1;  	if (!_scummSmallHeader) { -		s10->cur_val = 0; +		s10->curVal = 0;  	} else { -		s10->cur_val = s10->start_value; -		s10->start_value = 0; +		s10->curVal = s10->startValue; +		s10->startValue = 0;  	} -	s10->modwheel_last = 31; +	s10->modWheelLast = 31;  	s10->count = ie->a;  	if (s10->count)  		s10->count *= 63; -	s10->table_a[0] = ie->b; -	s10->table_a[1] = ie->d; -	s10->table_a[2] = ie->f; -	s10->table_a[3] = ie->g; +	s10->tableA[0] = ie->b; +	s10->tableA[1] = ie->d; +	s10->tableA[2] = ie->f; +	s10->tableA[3] = ie->g; -	s10->table_b[0] = ie->c; -	s10->table_b[1] = ie->e; -	s10->table_b[2] = 0; -	s10->table_b[3] = ie->h; +	s10->tableB[0] = ie->c; +	s10->tableB[1] = ie->e; +	s10->tableB[2] = 0; +	s10->tableB[3] = ie->h; -	struct10_setup(s10); +	struct10Setup(s10);  } -int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) { +int MidiDriver_ADLIB::adlibGetRegValueParam(int chan, byte param) {  	const AdLibSetParams *as;  	byte val;  	byte channel; @@ -1551,10 +2214,10 @@ int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) {  	assert(chan >= 0 && chan < 9);  	if (param <= 12) { -		channel = channel_mappings_2[chan]; +		channel = g_operator2Offsets[chan];  	} else if (param <= 25) {  		param -= 13; -		channel = channel_mappings[chan]; +		channel = g_operator1Offsets[chan];  	} else if (param <= 27) {  		param -= 13;  		channel = chan; @@ -1566,24 +2229,52 @@ int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) {  		return 0;  	} -	as = &adlib_setparam_table[param]; -	val = adlib_get_reg_value(channel + as->a); -	val &= as->c; -	val >>= as->b; -	if (as->d) -		val = as->d - val; +	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::adlib_note_on(int chan, byte note, int mod) { -	int code; +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); -	code = (note << 7) + mod; -	curnote_table[chan] = code; -	adlib_playnote(chan, (int16) channel_table_2[chan] + code); +	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 diff --git a/audio/softsynth/fluidsynth.cpp b/audio/softsynth/fluidsynth.cpp index 2451336784..518e260175 100644 --- a/audio/softsynth/fluidsynth.cpp +++ b/audio/softsynth/fluidsynth.cpp @@ -127,12 +127,54 @@ int MidiDriver_FluidSynth::open() {  	_synth = new_fluid_synth(_settings); -	// In theory, this ought to reduce CPU load... but it doesn't make any -	// noticeable difference for me, so disable it for now. +	if (ConfMan.getBool("fluidsynth_chorus_activate")) { +		fluid_synth_set_chorus_on(_synth, 1); + +		int chorusNr = ConfMan.getInt("fluidsynth_chorus_nr"); +		double chorusLevel = (double)ConfMan.getInt("fluidsynth_chorus_level") / 100.0; +		double chorusSpeed = (double)ConfMan.getInt("fluidsynth_chorus_speed") / 100.0; +		double chorusDepthMs = (double)ConfMan.getInt("fluidsynth_chorus_depth") / 10.0; + +		Common::String chorusWaveForm = ConfMan.get("fluidsynth_chorus_waveform"); +		int chorusType = FLUID_CHORUS_MOD_SINE; +		if (chorusWaveForm == "sine") { +			chorusType = FLUID_CHORUS_MOD_SINE; +		} else { +			chorusType = FLUID_CHORUS_MOD_TRIANGLE; +		} + +		fluid_synth_set_chorus(_synth, chorusNr, chorusLevel, chorusSpeed, chorusDepthMs, chorusType); +	} else { +		fluid_synth_set_chorus_on(_synth, 0); +	} + +	if (ConfMan.getBool("fluidsynth_reverb_activate")) { +		fluid_synth_set_reverb_on(_synth, 1); + +		double reverbRoomSize = (double)ConfMan.getInt("fluidsynth_reverb_roomsize") / 100.0; +		double reverbDamping = (double)ConfMan.getInt("fluidsynth_reverb_damping") / 100.0; +		int reverbWidth = ConfMan.getInt("fluidsynth_reverb_width"); +		double reverbLevel = (double)ConfMan.getInt("fluidsynth_reverb_level") / 100.0; + +		fluid_synth_set_reverb(_synth, reverbRoomSize, reverbDamping, reverbWidth, reverbLevel); +	} else { +		fluid_synth_set_reverb_on(_synth, 0); +	} + +	Common::String interpolation = ConfMan.get("fluidsynth_misc_interpolation"); +	int interpMethod = FLUID_INTERP_4THORDER; +	 +	if (interpolation == "none") { +		interpMethod = FLUID_INTERP_NONE; +	} else if (interpolation == "linear") { +		interpMethod = FLUID_INTERP_LINEAR; +	} else if (interpolation == "4th") { +		interpMethod = FLUID_INTERP_4THORDER; +	} else if (interpolation == "7th") { +		interpMethod = FLUID_INTERP_7THORDER; +	} -	// fluid_synth_set_interp_method(_synth, -1, FLUID_INTERP_LINEAR); -	// fluid_synth_set_reverb_on(_synth, 0); -	// fluid_synth_set_chorus_on(_synth, 0); +	fluid_synth_set_interp_method(_synth, -1, interpMethod);  	const char *soundfont = ConfMan.get("soundfont").c_str(); diff --git a/audio/softsynth/fmtowns_pc98/towns_midi.cpp b/audio/softsynth/fmtowns_pc98/towns_midi.cpp index b8203944c0..46b1a6406d 100644 --- a/audio/softsynth/fmtowns_pc98/towns_midi.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_midi.cpp @@ -147,17 +147,10 @@ private:  	TownsMidiOutputChannel *_out;  	uint8 *_instrument; -	uint8 _prg;  	uint8 _chanIndex; -	uint8 _effectLevel;  	uint8 _priority; -	uint8 _ctrlVolume;  	uint8 _tl; -	uint8 _pan; -	uint8 _panEff; -	uint8 _percS;  	int8 _transpose; -	uint8 _fld_1f;  	int8 _detune;  	int8 _modWheel;  	uint8 _sustain; @@ -659,9 +652,8 @@ const uint16 TownsMidiOutputChannel::_freqLSB[] = {  	0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B  }; -TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(0), _prg(0), _chanIndex(chanIndex), -	_effectLevel(0), _priority(0), _ctrlVolume(0), _tl(0), _pan(0), _panEff(0), _transpose(0), _percS(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), -	_fld_1f(0), _detune(0), _modWheel(0), _allocated(false) { +TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(0), _chanIndex(chanIndex), +	_priority(0), _tl(0), _transpose(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), _detune(0), _modWheel(0), _allocated(false) {  	_instrument = new uint8[30];  	memset(_instrument, 0, 30);  } diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 186118262f..d9744924aa 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -25,6 +25,7 @@  #ifdef USE_MT32EMU  #include "audio/softsynth/mt32/mt32emu.h" +#include "audio/softsynth/mt32/ROMInfo.h"  #include "audio/softsynth/emumidi.h"  #include "audio/musicplugin.h" @@ -47,6 +48,50 @@  #include "graphics/palette.h"  #include "graphics/font.h" +#include "gui/message.h" + +namespace MT32Emu { + +class ReportHandlerScummVM : public ReportHandler { +friend class Synth; + +public: +	virtual ~ReportHandlerScummVM() {} + +protected: + +	// Callback for debug messages, in vprintf() format +	void printDebug(const char *fmt, va_list list) { +		debug(4, fmt, list); +	} + +	// Callbacks for reporting various errors and information +	void onErrorControlROM() { +		GUI::MessageDialog dialog("MT32emu: Init Error - Missing or invalid Control ROM image", "OK"); +		dialog.runModal(); +		error("MT32emu: Init Error - Missing or invalid Control ROM image"); +	} +	void onErrorPCMROM() { +		GUI::MessageDialog dialog("MT32emu: Init Error - Missing PCM ROM image", "OK"); +		dialog.runModal(); +		error("MT32emu: Init Error - Missing PCM ROM image"); +	} +	void showLCDMessage(const char *message) { +		g_system->displayMessageOnOSD(message); +	} +	void onDeviceReset() {} +	void onDeviceReconfig() {} +	void onNewReverbMode(Bit8u /* mode */) {} +	void onNewReverbTime(Bit8u /* time */) {} +	void onNewReverbLevel(Bit8u /* level */) {} +	void onPartStateChanged(int /* partNum */, bool /* isActive */) {} +	void onPolyStateChanged(int /* partNum */) {} +	void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} +	void onProgramChanged(int /* partNum */, char * /* patchName */) {} +}; + +}	// end of namespace MT32Emu +  class MidiChannel_MT32 : public MidiChannel_MPU401 {  	void effectLevel(byte value) { }  	void chorusLevel(byte value) { } @@ -57,6 +102,10 @@ private:  	MidiChannel_MT32 _midiChannels[16];  	uint16 _channelMask;  	MT32Emu::Synth *_synth; +	MT32Emu::ReportHandlerScummVM *_reportHandler; +	const MT32Emu::ROMImage *_controlROM, *_pcmROM; +	Common::File *_controlFile, *_pcmFile; +	void deleteMuntStructures();  	int _outputRate; @@ -84,149 +133,6 @@ public:  	int getRate() const { return _outputRate; }  }; -static int eatSystemEvents() { -	Common::Event event; -	Common::EventManager *eventMan = g_system->getEventManager(); -	while (eventMan->pollEvent(event)) { -		switch (event.type) { -		case Common::EVENT_QUIT: -			return 1; -		default: -			break; -		} -	} -	return 0; -} - -static void drawProgress(float progress) { -	const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); -	Graphics::Surface *screen = g_system->lockScreen(); - -	assert(screen); -	assert(screen->pixels); - -	Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); - -	int16 w = g_system->getWidth() / 7 * 5; -	int16 h = font.getFontHeight(); -	int16 x = g_system->getWidth() / 7; -	int16 y = g_system->getHeight() / 2 - h / 2; - -	Common::Rect r(x, y, x + w, y + h); - -	uint32 col; - -	if (screenFormat.bytesPerPixel > 1) -		col = screenFormat.RGBToColor(0, 171, 0); -	else -		col = 1; - -	screen->frameRect(r, col); - -	r.grow(-1); -	r.setWidth(uint16(progress * w)); - -	if (screenFormat.bytesPerPixel > 1) -		col = screenFormat.RGBToColor(171, 0, 0); -	else -		col = 2; - -	screen->fillRect(r, col); - -	g_system->unlockScreen(); -	g_system->updateScreen(); -} - -static void drawMessage(int offset, const Common::String &text) { -	const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); -	Graphics::Surface *screen = g_system->lockScreen(); - -	assert(screen); -	assert(screen->pixels); - -	Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); - -	uint16 h = font.getFontHeight(); -	uint16 y = g_system->getHeight() / 2 - h / 2 + offset * (h + 1); - -	uint32 col; - -	if (screenFormat.bytesPerPixel > 1) -		col = screenFormat.RGBToColor(0, 0, 0); -	else -		col = 0; - -	Common::Rect r(0, y, screen->w, y + h); -	screen->fillRect(r, col); - -	if (screenFormat.bytesPerPixel > 1) -		col = screenFormat.RGBToColor(0, 171, 0); -	else -		col = 1; - -	font.drawString(screen, text, 0, y, screen->w, col, Graphics::kTextAlignCenter); - -	g_system->unlockScreen(); -	g_system->updateScreen(); -} - -static Common::File *MT32_OpenFile(void *userData, const char *filename) { -	Common::File *file = new Common::File(); -	if (!file->open(filename)) { -		delete file; -		return NULL; -	} -	return file; -} - -static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) { -	if (((MidiDriver_MT32 *)userData)->_initializing) { -		char buf[512]; - -		vsnprintf(buf, 512, fmt, list); -		buf[70] = 0; // Truncate to a reasonable length - -		drawMessage(1, buf); -	} - -	//vdebug(0, fmt, list); // FIXME: Use a higher debug level -} - -static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) { -	switch (type) { -	case MT32Emu::ReportType_lcdMessage: -		g_system->displayMessageOnOSD((const char *)reportData); -		break; -	case MT32Emu::ReportType_errorControlROM: -		error("Failed to load MT32_CONTROL.ROM"); -		break; -	case MT32Emu::ReportType_errorPCMROM: -		error("Failed to load MT32_PCM.ROM"); -		break; -	case MT32Emu::ReportType_progressInit: -		if (((MidiDriver_MT32 *)userData)->_initializing) { -			drawProgress(*((const float *)reportData)); -			return eatSystemEvents(); -		} -		break; -	case MT32Emu::ReportType_availableSSE: -		debug(1, "MT32emu: SSE is available"); -		break; -	case MT32Emu::ReportType_usingSSE: -		debug(1, "MT32emu: using SSE"); -		break; -	case MT32Emu::ReportType_available3DNow: -		debug(1, "MT32emu: 3DNow! is available"); -		break; -	case MT32Emu::ReportType_using3DNow: -		debug(1, "MT32emu: using 3DNow!"); -		break; -	default: -		break; -	} -	return 0; -} -  ////////////////////////////////////////  //  // MidiDriver_MT32 @@ -239,6 +145,7 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe  	for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {  		_midiChannels[i].init(this, i);  	} +	_reportHandler = NULL;  	_synth = NULL;  	// A higher baseFreq reduces the length used in generateSamples(),  	// and means that the timer callback will be called more often. @@ -252,30 +159,35 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe  }  MidiDriver_MT32::~MidiDriver_MT32() { +	deleteMuntStructures(); +} + +void MidiDriver_MT32::deleteMuntStructures() {  	delete _synth; +	_synth = NULL; +	delete _reportHandler; +	_reportHandler = NULL; + +	if (_controlROM) +		MT32Emu::ROMImage::freeROMImage(_controlROM); +	_controlROM = NULL; +	if (_pcmROM) +		MT32Emu::ROMImage::freeROMImage(_pcmROM); +	_pcmROM = NULL; + +	delete _controlFile; +	_controlFile = NULL; +	delete _pcmFile; +	_pcmFile = NULL;  }  int MidiDriver_MT32::open() { -	MT32Emu::SynthProperties prop; -  	if (_isOpen)  		return MERR_ALREADY_OPEN;  	MidiDriver_Emulated::open(); - -	memset(&prop, 0, sizeof(prop)); -	prop.sampleRate = getRate(); -	prop.useReverb = true; -	prop.useDefaultReverb = false; -	prop.reverbType = 0; -	prop.reverbTime = 5; -	prop.reverbLevel = 3; -	prop.userData = this; -	prop.printDebug = MT32_PrintDebug; -	prop.report = MT32_Report; -	prop.openFile = MT32_OpenFile; - -	_synth = new MT32Emu::Synth(); +	_reportHandler = new MT32Emu::ReportHandlerScummVM(); +	_synth = new MT32Emu::Synth(_reportHandler);  	Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); @@ -290,8 +202,16 @@ int MidiDriver_MT32::open() {  	}  	_initializing = true; -	drawMessage(-1, _s("Initializing MT-32 Emulator")); -	if (!_synth->open(prop)) +	debug(4, _s("Initializing MT-32 Emulator")); +	_controlFile = new Common::File(); +	if (!_controlFile->open("MT32_CONTROL.ROM") && !_controlFile->open("CM32L_CONTROL.ROM")) +		error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM"); +	_pcmFile = new Common::File(); +	if (!_pcmFile->open("MT32_PCM.ROM") && !_pcmFile->open("CM32L_PCM.ROM")) +		error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM"); +	_controlROM = MT32Emu::ROMImage::makeROMImage(_controlFile); +	_pcmROM = MT32Emu::ROMImage::makeROMImage(_pcmFile); +	if (!_synth->open(*_controlROM, *_pcmROM))  		return MERR_DEVICE_NOT_AVAILABLE;  	double gain = (double)ConfMan.getInt("midi_gain") / 100.0; @@ -352,8 +272,7 @@ void MidiDriver_MT32::close() {  	_mixer->stopHandle(_mixerSoundHandle);  	_synth->close(); -	delete _synth; -	_synth = NULL; +	deleteMuntStructures();  }  void MidiDriver_MT32::generateSamples(int16 *data, int len) { diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp index 4ee6c87943..595b286a31 100644 --- a/audio/softsynth/mt32/AReverbModel.cpp +++ b/audio/softsynth/mt32/AReverbModel.cpp @@ -1,5 +1,5 @@  /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU Lesser General Public License as published by @@ -16,64 +16,97 @@   */  #include "mt32emu.h" -#include "AReverbModel.h" - -using namespace MT32Emu; - -// Default reverb settings for modes 0-2 - -static const unsigned int NUM_ALLPASSES = 6; -static const unsigned int NUM_DELAYS = 5; -static const Bit32u MODE_0_ALLPASSES[] = {729, 78, 394, 994, 1250, 1889}; -static const Bit32u MODE_0_DELAYS[] = {846, 4, 1819, 778, 346}; -static const float MODE_0_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.9f}; -static const float MODE_0_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f}; +#if MT32EMU_USE_REVERBMODEL == 1 -static const Bit32u MODE_1_ALLPASSES[] = {176, 809, 1324, 1258}; -static const Bit32u MODE_1_DELAYS[] = {2262, 124, 974, 2516, 356}; -static const float MODE_1_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.95f}; -static const float MODE_1_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f}; - -static const Bit32u MODE_2_ALLPASSES[] = {78, 729, 994, 389}; -static const Bit32u MODE_2_DELAYS[] = {846, 4, 1819, 778, 346}; -static const float MODE_2_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f}; -static const float MODE_2_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f}; - -const AReverbSettings AReverbModel::REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_DELAYS, MODE_0_TIMES, MODE_0_LEVELS, 0.687770909f, 0.5f, 0.5f}; -const AReverbSettings AReverbModel::REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_DELAYS, MODE_1_TIMES, MODE_1_LEVELS, 0.712025098f, 0.375f, 0.625f}; -const AReverbSettings AReverbModel::REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_DELAYS, MODE_2_TIMES, MODE_2_LEVELS, 0.939522749f, 0.0f, 0.0f}; +#include "AReverbModel.h" -RingBuffer::RingBuffer(Bit32u newsize) { -	index = 0; -	size = newsize; +// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that +// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) +// and followed by three parallel comb filters + +namespace MT32Emu { + +// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay, +// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data. +// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level, +// so we can simply increase the input buffer size. +static const Bit32u PROCESS_DELAY = 1; + +// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. +// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). + +static const Bit32u NUM_ALLPASSES = 3; +static const Bit32u NUM_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be perfectly processed via a comb here. + +static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; +static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; +static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; +static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; +static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60}; +static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, +                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, +                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15}; +static const Bit32u MODE_0_LPF_AMP = 6; + +static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; +static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; +static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; +static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; +static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60}; +static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +											  0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, +											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, +											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15}; +static const Bit32u MODE_1_LPF_AMP = 6; + +static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; +static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; +static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; +static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; +static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; +static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, +                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, +                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; +static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15}; +static const Bit32u MODE_2_LPF_AMP = 8; + +static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP}; +static const AReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_LEVELS, MODE_1_LPF_AMP}; +static const AReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_LEVELS, MODE_2_LPF_AMP}; + +static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS}; + +RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) {  	buffer = new float[size];  }  RingBuffer::~RingBuffer() {  	delete[] buffer;  	buffer = NULL; -	size = 0;  }  float RingBuffer::next() { -	index++; -	if (index >= size) { +	if (++index >= size) {  		index = 0;  	}  	return buffer[index];  } -bool RingBuffer::isEmpty() { +bool RingBuffer::isEmpty() const {  	if (buffer == NULL) return true;  	float *buf = buffer; -	float total = 0; +	float max = 0.001f;  	for (Bit32u i = 0; i < size; i++) { -		total += (*buf < 0 ? -*buf : *buf); +		if ((*buf < -max) || (*buf > max)) return false;  		buf++;  	} -	return ((total / size) < .0002 ? true : false); +	return true;  }  void RingBuffer::mute() { @@ -83,59 +116,66 @@ void RingBuffer::mute() {  	}  } -AllpassFilter::AllpassFilter(Bit32u useSize) : RingBuffer(useSize) { -} - -Delay::Delay(Bit32u useSize) : RingBuffer(useSize) { -} +AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} -float AllpassFilter::process(float in) { -	// This model corresponds to the allpass filter implementation in the real CM-32L device +float AllpassFilter::process(const float in) { +	// This model corresponds to the allpass filter implementation of the real CM-32L device  	// found from sample analysis -	float out; - -	out = next(); +	const float bufferOut = next();  	// store input - feedback / 2 -	buffer[index] = in - 0.5f * out; +	buffer[index] = in - 0.5f * bufferOut;  	// return buffer output + feedforward / 2 -	return out + 0.5f * buffer[index]; +	return bufferOut + 0.5f * buffer[index];  } -float Delay::process(float in) { -	// Implements a very simple delay +CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize) {} -	float out; +void CombFilter::process(const float in) { +	// This model corresponds to the comb filter implementation of the real CM-32L device +	// found from sample analysis + +	// the previously stored value +	float last = buffer[index]; -	out = next(); +	// prepare input + feedback +	float filterIn = in + next() * feedbackFactor; -	// store input -	buffer[index] = in; +	// store input + feedback processed by a low-pass filter +	buffer[index] = filterFactor * last - filterIn; +} -	// return buffer output -	return out; +float CombFilter::getOutputAt(const Bit32u outIndex) const { +	return buffer[(size + index - outIndex) % size];  } -AReverbModel::AReverbModel(const AReverbSettings *useSettings) : allpasses(NULL), delays(NULL), currentSettings(useSettings) { +void CombFilter::setFeedbackFactor(const float useFeedbackFactor) { +	feedbackFactor = useFeedbackFactor;  } +void CombFilter::setFilterFactor(const float useFilterFactor) { +	filterFactor = useFilterFactor; +} + +AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]) {} +  AReverbModel::~AReverbModel() {  	close();  } -void AReverbModel::open(unsigned int /*sampleRate*/) { -	// FIXME: filter sizes must be multiplied by sample rate to 32000Hz ratio -	// IIR filter values depend on sample rate as well +void AReverbModel::open() {  	allpasses = new AllpassFilter*[NUM_ALLPASSES];  	for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { -		allpasses[i] = new AllpassFilter(currentSettings->allpassSizes[i]); +		allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]);  	} -	delays = new Delay*[NUM_DELAYS]; -	for (Bit32u i = 0; i < NUM_DELAYS; i++) { -		delays[i] = new Delay(currentSettings->delaySizes[i]); +	combs = new CombFilter*[NUM_COMBS]; +	for (Bit32u i = 0; i < NUM_COMBS; i++) { +		combs[i] = new CombFilter(currentSettings.combSizes[i]); +		combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f);  	} +	lpfAmp = currentSettings.lpfAmp / 16.0f;  	mute();  } @@ -150,84 +190,80 @@ void AReverbModel::close() {  		delete[] allpasses;  		allpasses = NULL;  	} -	if (delays != NULL) { -		for (Bit32u i = 0; i < NUM_DELAYS; i++) { -			if (delays[i] != NULL) { -				delete delays[i]; -				delays[i] = NULL; +	if (combs != NULL) { +		for (Bit32u i = 0; i < NUM_COMBS; i++) { +			if (combs[i] != NULL) { +				delete combs[i]; +				combs[i] = NULL;  			}  		} -		delete[] delays; -		delays = NULL; +		delete[] combs; +		combs = NULL;  	}  }  void AReverbModel::mute() { +	if (allpasses == NULL || combs == NULL) return;  	for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {  		allpasses[i]->mute();  	} -	for (Bit32u i = 0; i < NUM_DELAYS; i++) { -		delays[i]->mute(); +	for (Bit32u i = 0; i < NUM_COMBS; i++) { +		combs[i]->mute();  	} -	filterhist1 = 0; -	filterhist2 = 0; -	combhist = 0;  }  void AReverbModel::setParameters(Bit8u time, Bit8u level) {  // FIXME: wetLevel definitely needs ramping when changed  // Although, most games don't set reverb level during MIDI playback -	decayTime = currentSettings->decayTimes[time]; -	wetLevel = currentSettings->wetLevels[level]; +	if (combs == NULL) return; +	level &= 7; +	time &= 7; +	for (Bit32u i = 0; i < NUM_COMBS; i++) { +		combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f); +	} +	wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f;  }  bool AReverbModel::isActive() const { -	bool bActive = false;  	for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { -		bActive |= !allpasses[i]->isEmpty(); +		if (!allpasses[i]->isEmpty()) return true;  	} -	for (Bit32u i = 0; i < NUM_DELAYS; i++) { -		bActive |= !delays[i]->isEmpty(); +	for (Bit32u i = 0; i < NUM_COMBS; i++) { +		if (!combs[i]->isEmpty()) return true;  	} -	return bActive; +	return false;  }  void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { -// Three series allpass filters followed by a delay, fourth allpass filter and another delay -	float dry, link, outL1, outL2, outR1, outR2; +	float dry, link, outL1;  	for (unsigned long i = 0; i < numSamples; i++) { -		dry = *inLeft + *inRight; +		dry = wetLevel * (*inLeft + *inRight); -		// Implementation of 2-stage IIR single-pole low-pass filter -		// found at the entrance of reverb processing on real devices -		filterhist1 += (dry - filterhist1) * currentSettings->filtVal; -		filterhist2 += (filterhist1 - filterhist2) * currentSettings->filtVal; +		// Get the last stored sample before processing in order not to loose it +		link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); -		link = allpasses[0]->process(-filterhist2); -		link = allpasses[1]->process(link); +		combs[0]->process(-dry); -		// this implements a comb filter cross-linked with the fourth allpass filter -		link += combhist * decayTime; +		link = allpasses[0]->process(link); +		link = allpasses[1]->process(link);  		link = allpasses[2]->process(link); -		link = delays[0]->process(link); -		outL1 = link; -		link = allpasses[3]->process(link); -		link = delays[1]->process(link); -		outR1 = link; -		link = allpasses[4]->process(link); -		link = delays[2]->process(link); -		outL2 = link; -		link = allpasses[5]->process(link); -		link = delays[3]->process(link); -		outR2 = link; -		link = delays[4]->process(link); - -		// comb filter end point -		combhist = combhist * currentSettings->damp1 + link * currentSettings->damp2; - -		*outLeft = (outL1 + outL2) * wetLevel; -		*outRight = (outR1 + outR2) * wetLevel; + +		// If the output position is equal to the comb size, get it now in order not to loose it +		outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); + +		combs[1]->process(link); +		combs[2]->process(link); +		combs[3]->process(link); + +		link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]); +		link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); +		*outLeft = link; + +		link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]); +		link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]); +		link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); +		*outRight = link;  		inLeft++;  		inRight++; @@ -235,3 +271,7 @@ void AReverbModel::process(const float *inLeft, const float *inRight, float *out  		outRight++;  	}  } + +} + +#endif diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h index 3fae08c34c..be1ca4916b 100644 --- a/audio/softsynth/mt32/AReverbModel.h +++ b/audio/softsynth/mt32/AReverbModel.h @@ -1,5 +1,5 @@  /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU Lesser General Public License as published by @@ -21,66 +21,67 @@  namespace MT32Emu {  struct AReverbSettings { -	const Bit32u *allpassSizes; -	const Bit32u *delaySizes; -	const float *decayTimes; -	const float *wetLevels; -	float filtVal; -	float damp1; -	float damp2; +	const Bit32u * const allpassSizes; +	const Bit32u * const combSizes; +	const Bit32u * const outLPositions; +	const Bit32u * const outRPositions; +	const Bit32u * const filterFactor; +	const Bit32u * const decayTimes; +	const Bit32u * const wetLevels; +	const Bit32u lpfAmp;  };  class RingBuffer {  protected:  	float *buffer; -	Bit32u size; +	const Bit32u size;  	Bit32u index; +  public: -	RingBuffer(Bit32u size); +	RingBuffer(const Bit32u size);  	virtual ~RingBuffer();  	float next(); -	bool isEmpty(); +	bool isEmpty() const;  	void mute();  };  class AllpassFilter : public RingBuffer {  public: -	AllpassFilter(Bit32u size); -	float process(float in); +	AllpassFilter(const Bit32u size); +	float process(const float in);  }; -class Delay : public RingBuffer { +class CombFilter : public RingBuffer { +	float feedbackFactor; +	float filterFactor; +  public: -	Delay(Bit32u size); -	float process(float in); +	CombFilter(const Bit32u size); +	void process(const float in); +	float getOutputAt(const Bit32u outIndex) const; +	void setFeedbackFactor(const float useFeedbackFactor); +	void setFilterFactor(const float useFilterFactor);  };  class AReverbModel : public ReverbModel {  	AllpassFilter **allpasses; -	Delay **delays; +	CombFilter **combs; -	const AReverbSettings *currentSettings; -	float decayTime; +	const AReverbSettings ¤tSettings; +	float lpfAmp;  	float wetLevel; -	float filterhist1, filterhist2; -	float combhist;  	void mute(); +  public: -	AReverbModel(const AReverbSettings *newSettings); +	AReverbModel(const ReverbMode mode);  	~AReverbModel(); -	void open(unsigned int sampleRate); +	void open();  	void close();  	void setParameters(Bit8u time, Bit8u level);  	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);  	bool isActive() const; - -	static const AReverbSettings REVERB_MODE_0_SETTINGS; -	static const AReverbSettings REVERB_MODE_1_SETTINGS; -	static const AReverbSettings REVERB_MODE_2_SETTINGS;  }; -// Default reverb settings for modes 0-2 -  }  #endif diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp new file mode 100644 index 0000000000..2570424187 --- /dev/null +++ b/audio/softsynth/mt32/BReverbModel.cpp @@ -0,0 +1,393 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mt32emu.h" + +#if MT32EMU_USE_REVERBMODEL == 2 + +#include "BReverbModel.h" + +// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that +// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) +// and followed by three parallel comb filters + +namespace MT32Emu { + +// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay, +// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data. +// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level, +// so we can simply increase the input buffer size. +static const Bit32u PROCESS_DELAY = 1; + +static const Bit32u MODE_3_ADDITIONAL_DELAY = 1; +static const Bit32u MODE_3_FEEDBACK_DELAY = 1; + +// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. +// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). + +static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; +static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be processed via a hacked comb. +static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; +static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; +static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; +static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; +static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, +                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, +                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; +static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_0_LPF_AMP = 0x60; + +static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; +static const Bit32u MODE_1_NUMBER_OF_COMBS = 4; // Same as for mode 0 above +static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; +static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; +static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; +static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; +static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +											  0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, +											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, +											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; +static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_1_LPF_AMP = 0x60; + +static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; +static const Bit32u MODE_2_NUMBER_OF_COMBS = 4; // Same as for mode 0 above +static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; +static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; +static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; +static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; +static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, +                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, +                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; +static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; +static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_2_LPF_AMP = 0x80; + +static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; +static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; +static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; +static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; +static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; +static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; +static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; +static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; +static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + +static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; +static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; +static const BReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_NUMBER_OF_ALLPASSES, MODE_2_ALLPASSES, MODE_2_NUMBER_OF_COMBS, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_DRY_AMP, MODE_2_WET_AMP, MODE_2_LPF_AMP}; +static const BReverbSettings REVERB_MODE_3_SETTINGS = {MODE_3_NUMBER_OF_ALLPASSES, NULL, MODE_3_NUMBER_OF_COMBS, MODE_3_DELAY, MODE_3_OUTL, MODE_3_OUTR, MODE_3_COMB_FACTOR, MODE_3_COMB_FEEDBACK, MODE_3_DRY_AMP, MODE_3_WET_AMP, 0}; + +static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_3_SETTINGS}; + +// This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines). +// Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation. +static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) { +	Bit8u mask = 0x80; +	Bit32s res = 0; +	for (int i = 0; i < 8; i++) { +		Bit32s carry = (a < 0) && (mask & carryMask) > 0 ? a & 1 : 0; +		a >>= 1; +		res += (mask & addMask) > 0 ? a + carry : 0; +		mask >>= 1; +	} +	return res; +} + +RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) { +	buffer = new Bit16s[size]; +} + +RingBuffer::~RingBuffer() { +	delete[] buffer; +	buffer = NULL; +} + +Bit32s RingBuffer::next() { +	if (++index >= size) { +		index = 0; +	} +	return buffer[index]; +} + +bool RingBuffer::isEmpty() const { +	if (buffer == NULL) return true; + +	Bit16s *buf = buffer; +	for (Bit32u i = 0; i < size; i++) { +		if (*buf < -8 || *buf > 8) return false; +		buf++; +	} +	return true; +} + +void RingBuffer::mute() { +	Bit16s *buf = buffer; +	for (Bit32u i = 0; i < size; i++) { +		*buf++ = 0; +	} +} + +AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} + +Bit32s AllpassFilter::process(const Bit32s in) { +	// This model corresponds to the allpass filter implementation of the real CM-32L device +	// found from sample analysis + +	Bit16s bufferOut = next(); + +	// store input - feedback / 2 +	buffer[index] = in - (bufferOut >> 1); + +	// return buffer output + feedforward / 2 +	return bufferOut + (buffer[index] >> 1); +} + +CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} + +void CombFilter::process(const Bit32s in) { +	// This model corresponds to the comb filter implementation of the real CM-32L device + +	// the previously stored value +	Bit32s last = buffer[index]; + +	// prepare input + feedback +	Bit32s filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */); + +	// store input + feedback processed by a low-pass filter +	buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn; +} + +Bit32s CombFilter::getOutputAt(const Bit32u outIndex) const { +	return buffer[(size + index - outIndex) % size]; +} + +void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { +	feedbackFactor = useFeedbackFactor; +} + +DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp) +	: CombFilter(useSize, useFilterFactor), amp(useAmp) {} + +void DelayWithLowPassFilter::process(const Bit32s in) { +	// the previously stored value +	Bit32s last = buffer[index]; + +	// move to the next index +	next(); + +	// low-pass filter process +	Bit32s lpfOut = weirdMul(last, filterFactor, 0xFF) + in; + +	// store lpfOut multiplied by LPF amp factor +	buffer[index] = weirdMul(lpfOut, amp, 0xFF); +} + +TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} + +void TapDelayCombFilter::process(const Bit32s in) { +	// the previously stored value +	Bit32s last = buffer[index]; + +	// move to the next index +	next(); + +	// prepare input + feedback +	// Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output +	Bit32s filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0); + +	// store input + feedback processed by a low-pass filter +	buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn; +} + +Bit32s TapDelayCombFilter::getLeftOutput() const { +	return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); +} + +Bit32s TapDelayCombFilter::getRightOutput() const { +	return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); +} + +void TapDelayCombFilter::setOutputPositions(const Bit32u useOutL, const Bit32u useOutR) { +	outL = useOutL; +	outR = useOutR; +} + +BReverbModel::BReverbModel(const ReverbMode mode) +	: allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]), tapDelayMode(mode == REVERB_MODE_TAP_DELAY) {} + +BReverbModel::~BReverbModel() { +	close(); +} + +void BReverbModel::open() { +	if (currentSettings.numberOfAllpasses > 0) { +		allpasses = new AllpassFilter*[currentSettings.numberOfAllpasses]; +		for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { +			allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); +		} +	} +	combs = new CombFilter*[currentSettings.numberOfCombs]; +	if (tapDelayMode) { +		*combs = new TapDelayCombFilter(*currentSettings.combSizes, *currentSettings.filterFactors); +	} else { +		combs[0] = new DelayWithLowPassFilter(currentSettings.combSizes[0], currentSettings.filterFactors[0], currentSettings.lpfAmp); +		for (Bit32u i = 1; i < currentSettings.numberOfCombs; i++) { +			combs[i] = new CombFilter(currentSettings.combSizes[i], currentSettings.filterFactors[i]); +		} +	} +	mute(); +} + +void BReverbModel::close() { +	if (allpasses != NULL) { +		for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { +			if (allpasses[i] != NULL) { +				delete allpasses[i]; +				allpasses[i] = NULL; +			} +		} +		delete[] allpasses; +		allpasses = NULL; +	} +	if (combs != NULL) { +		for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { +			if (combs[i] != NULL) { +				delete combs[i]; +				combs[i] = NULL; +			} +		} +		delete[] combs; +		combs = NULL; +	} +} + +void BReverbModel::mute() { +	if (allpasses != NULL) { +		for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { +			allpasses[i]->mute(); +		} +	} +	if (combs != NULL) { +		for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { +			combs[i]->mute(); +		} +	} +} + +void BReverbModel::setParameters(Bit8u time, Bit8u level) { +	if (combs == NULL) return; +	level &= 7; +	time &= 7; +	if (tapDelayMode) { +		TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); +		comb->setOutputPositions(currentSettings.outLPositions[time], currentSettings.outRPositions[time & 7]); +		comb->setFeedbackFactor(currentSettings.feedbackFactors[((level < 3) || (time < 6)) ? 0 : 1]); +	} else { +		for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { +			combs[i]->setFeedbackFactor(currentSettings.feedbackFactors[(i << 3) + time]); +		} +	} +	if (time == 0 && level == 0) { +		dryAmp = wetLevel = 0; +	} else { +		dryAmp = currentSettings.dryAmps[level]; +		wetLevel = currentSettings.wetLevels[level]; +	} +} + +bool BReverbModel::isActive() const { +	for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { +		if (!allpasses[i]->isEmpty()) return true; +	} +	for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { +		if (!combs[i]->isEmpty()) return true; +	} +	return false; +} + +void BReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { +	Bit32s dry, link, outL1, outR1; + +	for (unsigned long i = 0; i < numSamples; i++) { +		if (tapDelayMode) { +			dry = Bit32s(*inLeft * 8192.0f) + Bit32s(*inRight * 8192.0f); +		} else { +			dry = Bit32s(*inLeft * 8192.0f) / 2 + Bit32s(*inRight * 8192.0f) / 2; +		} + +		// Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I +		dry = weirdMul(dry, dryAmp, 0xFF); + +		if (tapDelayMode) { +			TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); +			comb->process(dry); +			*outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF) / 8192.0f; +			*outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF) / 8192.0f; +		} else { +			// Get the last stored sample before processing in order not to loose it +			link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); + +			// Entrance LPF. Note, comb.process() differs a bit here. +			combs[0]->process(dry); + +			// This introduces reverb noise which actually makes output from the real Boss chip nondeterministic +			link = link - 1; +			link = allpasses[0]->process(link); +			link = allpasses[1]->process(link); +			link = allpasses[2]->process(link); + +			// If the output position is equal to the comb size, get it now in order not to loose it +			outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); +			outL1 += outL1 >> 1; + +			combs[1]->process(link); +			combs[2]->process(link); +			combs[3]->process(link); + +			link = combs[2]->getOutputAt(currentSettings.outLPositions[1]); +			link += link >> 1; +			link += outL1; +			link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); +			*outLeft = weirdMul(link, wetLevel, 0xFF) / 8192.0f; + +			outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); +			outR1 += outR1 >> 1; +			link = combs[2]->getOutputAt(currentSettings.outRPositions[1]); +			link += link >> 1; +			link += outR1; +			link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); +			*outRight = weirdMul(link, wetLevel, 0xFF) / 8192.0f; +		} + +		inLeft++; +		inRight++; +		outLeft++; +		outRight++; +	} +} + +} + +#endif diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h new file mode 100644 index 0000000000..f728d1a99c --- /dev/null +++ b/audio/softsynth/mt32/BReverbModel.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MT32EMU_B_REVERB_MODEL_H +#define MT32EMU_B_REVERB_MODEL_H + +namespace MT32Emu { + +struct BReverbSettings { +	const Bit32u numberOfAllpasses; +	const Bit32u * const allpassSizes; +	const Bit32u numberOfCombs; +	const Bit32u * const combSizes; +	const Bit32u * const outLPositions; +	const Bit32u * const outRPositions; +	const Bit32u * const filterFactors; +	const Bit32u * const feedbackFactors; +	const Bit32u * const dryAmps; +	const Bit32u * const wetLevels; +	const Bit32u lpfAmp; +}; + +class RingBuffer { +protected: +	Bit16s *buffer; +	const Bit32u size; +	Bit32u index; + +public: +	RingBuffer(const Bit32u size); +	virtual ~RingBuffer(); +	Bit32s next(); +	bool isEmpty() const; +	void mute(); +}; + +class AllpassFilter : public RingBuffer { +public: +	AllpassFilter(const Bit32u size); +	Bit32s process(const Bit32s in); +}; + +class CombFilter : public RingBuffer { +protected: +	const Bit32u filterFactor; +	Bit32u feedbackFactor; + +public: +	CombFilter(const Bit32u size, const Bit32u useFilterFactor); +	virtual void process(const Bit32s in); // Actually, no need to make it virtual, but for sure +	Bit32s getOutputAt(const Bit32u outIndex) const; +	void setFeedbackFactor(const Bit32u useFeedbackFactor); +}; + +class DelayWithLowPassFilter : public CombFilter { +	Bit32u amp; + +public: +	DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp); +	void process(const Bit32s in); +	void setFeedbackFactor(const Bit32u) {} +}; + +class TapDelayCombFilter : public CombFilter { +	Bit32u outL; +	Bit32u outR; + +public: +	TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor); +	void process(const Bit32s in); +	Bit32s getLeftOutput() const; +	Bit32s getRightOutput() const; +	void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR); +}; + +class BReverbModel : public ReverbModel { +	AllpassFilter **allpasses; +	CombFilter **combs; + +	const BReverbSettings ¤tSettings; +	const bool tapDelayMode; +	Bit32u dryAmp; +	Bit32u wetLevel; +	void mute(); + +public: +	BReverbModel(const ReverbMode mode); +	~BReverbModel(); +	void open(); +	void close(); +	void setParameters(Bit8u time, Bit8u level); +	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); +	bool isActive() const; +}; + +} + +#endif diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp index 89eebf0d79..bf925f8419 100644 --- a/audio/softsynth/mt32/DelayReverb.cpp +++ b/audio/softsynth/mt32/DelayReverb.cpp @@ -20,15 +20,14 @@  #include "mt32emu.h"  #include "DelayReverb.h" -using namespace MT32Emu; +namespace MT32Emu { - -// CONFIRMED: The values below are found via analysis of digital samples. Checked with all time and level combinations. +// CONFIRMED: The values below are found via analysis of digital samples and tracing reverb RAM address / data lines. Checked with all time and level combinations.  // Obviously:  // rightDelay = (leftDelay - 2) * 2 + 2  // echoDelay = rightDelay - 1  // Leaving these separate in case it's useful for work on other reverb modes... -const Bit32u REVERB_TIMINGS[8][3]= { +static const Bit32u REVERB_TIMINGS[8][3]= {  	// {leftDelay, rightDelay, feedbackDelay}  	{402, 802, 801},  	{626, 1250, 1249}, @@ -40,14 +39,16 @@ const Bit32u REVERB_TIMINGS[8][3]= {  	{8002, 16002, 16001}  }; -const float REVERB_FADE[8] = {0.0f, -0.049400051f, -0.08220577f, -0.131861118f, -0.197344907f, -0.262956344f, -0.345162114f, -0.509508615f}; -const float REVERB_FEEDBACK67 = -0.629960524947437f; // = -EXP2F(-2 / 3) -const float REVERB_FEEDBACK = -0.682034520443118f; // = -EXP2F(-53 / 96) -const float LPF_VALUE = 0.594603558f; // = EXP2F(-0.75f) +// Reverb amp is found as dryAmp * wetAmp +static const Bit32u REVERB_AMP[8] = {0x20*0x18, 0x50*0x18, 0x50*0x28, 0x50*0x40, 0x50*0x60, 0x50*0x80, 0x50*0xA8, 0x50*0xF8}; +static const Bit32u REVERB_FEEDBACK67 = 0x60; +static const Bit32u REVERB_FEEDBACK = 0x68; +static const float LPF_VALUE = 0x68 / 256.0f; + +static const Bit32u BUFFER_SIZE = 16384;  DelayReverb::DelayReverb() {  	buf = NULL; -	sampleRate = 0;  	setParameters(0, 0);  } @@ -55,27 +56,22 @@ DelayReverb::~DelayReverb() {  	delete[] buf;  } -void DelayReverb::open(unsigned int newSampleRate) { -	if (newSampleRate != sampleRate || buf == NULL) { -		sampleRate = newSampleRate; - +void DelayReverb::open() { +	if (buf == NULL) {  		delete[] buf; -		// If we ever need a speedup, set bufSize to EXP2F(ceil(log2(bufSize))) and use & instead of % to find buf indexes -		bufSize = 16384 * sampleRate / 32000; -		buf = new float[bufSize]; +		buf = new float[BUFFER_SIZE];  		recalcParameters();  		// mute buffer  		bufIx = 0;  		if (buf != NULL) { -			for (unsigned int i = 0; i < bufSize; i++) { +			for (unsigned int i = 0; i < BUFFER_SIZE; i++) {  				buf[i] = 0.0f;  			}  		}  	} -	// FIXME: IIR filter value depends on sample rate as well  }  void DelayReverb::close() { @@ -92,59 +88,55 @@ void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) {  void DelayReverb::recalcParameters() {  	// Number of samples between impulse and eventual appearance on the left channel -	delayLeft = REVERB_TIMINGS[time][0] * sampleRate / 32000; +	delayLeft = REVERB_TIMINGS[time][0];  	// Number of samples between impulse and eventual appearance on the right channel -	delayRight = REVERB_TIMINGS[time][1] * sampleRate / 32000; +	delayRight = REVERB_TIMINGS[time][1];  	// Number of samples between a response and that response feeding back/echoing -	delayFeedback = REVERB_TIMINGS[time][2] * sampleRate / 32000; +	delayFeedback = REVERB_TIMINGS[time][2]; -	if (time < 6) { -		feedback = REVERB_FEEDBACK; +	if (level < 3 || time < 6) { +		feedback = REVERB_FEEDBACK / 256.0f;  	} else { -		feedback = REVERB_FEEDBACK67; +		feedback = REVERB_FEEDBACK67 / 256.0f;  	} -	// Fading speed, i.e. amplitude ratio of neighbor responses -	fade = REVERB_FADE[level]; +	// Overall output amp +	amp = (level == 0 && time == 0) ? 0.0f : REVERB_AMP[level] / 65536.0f;  }  void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { -	if (buf == NULL) { -		return; -	} +	if (buf == NULL) return;  	for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) {  		// The ring buffer write index moves backwards; reads are all done with positive offsets. -		Bit32u bufIxPrev = (bufIx + 1) % bufSize; -		Bit32u bufIxLeft = (bufIx + delayLeft) % bufSize; -		Bit32u bufIxRight = (bufIx + delayRight) % bufSize; -		Bit32u bufIxFeedback = (bufIx + delayFeedback) % bufSize; +		Bit32u bufIxPrev = (bufIx + 1) % BUFFER_SIZE; +		Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE; +		Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE; +		Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE;  		// Attenuated input samples and feedback response are directly added to the current ring buffer location -		float sample = fade * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback]; +		float lpfIn = amp * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback];  		// Single-pole IIR filter found on real devices -		buf[bufIx] = buf[bufIxPrev] + (sample - buf[bufIxPrev]) * LPF_VALUE; +		buf[bufIx] = buf[bufIxPrev] * LPF_VALUE - lpfIn;  		outLeft[sampleIx] = buf[bufIxLeft];  		outRight[sampleIx] = buf[bufIxRight]; -		bufIx = (bufSize + bufIx - 1) % bufSize; +		bufIx = (BUFFER_SIZE + bufIx - 1) % BUFFER_SIZE;  	}  }  bool DelayReverb::isActive() const { -	// Quick hack: Return true iff all samples in the left buffer are the same and -	// all samples in the right buffers are the same (within the sample output threshold). -	if (buf == NULL) { -		return false; -	} -	float last = buf[0] * 8192.0f; -	for (unsigned int i = 1; i < bufSize; i++) { -		float s = (buf[i] * 8192.0f); -		if (fabs(s - last) > 1.0f) { -			return true; -		} +	if (buf == NULL) return false; + +	float *b = buf; +	float max = 0.001f; +	for (Bit32u i = 0; i < BUFFER_SIZE; i++) { +		if ((*b < -max) || (*b > max)) return true; +		b++;  	}  	return false;  } + +} diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h index 7c030fb839..1abb49f128 100644 --- a/audio/softsynth/mt32/DelayReverb.h +++ b/audio/softsynth/mt32/DelayReverb.h @@ -25,17 +25,14 @@ private:  	Bit8u time;  	Bit8u level; -	unsigned int sampleRate; -	Bit32u bufSize;  	Bit32u bufIx; -  	float *buf;  	Bit32u delayLeft;  	Bit32u delayRight;  	Bit32u delayFeedback; -	float fade; +	float amp;  	float feedback;  	void recalcParameters(); @@ -43,7 +40,7 @@ private:  public:  	DelayReverb();  	~DelayReverb(); -	void open(unsigned int sampleRate); +	void open();  	void close();  	void setParameters(Bit8u time, Bit8u level);  	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp index c11fa859d8..2ed302fddc 100644 --- a/audio/softsynth/mt32/FreeverbModel.cpp +++ b/audio/softsynth/mt32/FreeverbModel.cpp @@ -20,7 +20,7 @@  #include "freeverb.h" -using namespace MT32Emu; +namespace MT32Emu {  FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) {  	freeverb = NULL; @@ -35,9 +35,7 @@ FreeverbModel::~FreeverbModel() {  	delete freeverb;  } -void FreeverbModel::open(unsigned int /*sampleRate*/) { -	// FIXME: scaleTuning must be multiplied by sample rate to 32000Hz ratio -	// IIR filter values depend on sample rate as well +void FreeverbModel::open() {  	if (freeverb == NULL) {  		freeverb = new revmodel(scaleTuning);  	} @@ -76,3 +74,5 @@ bool FreeverbModel::isActive() const {  	// FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon...  	return false;  } + +} diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h index 925b2dbf96..bf1102b14a 100644 --- a/audio/softsynth/mt32/FreeverbModel.h +++ b/audio/softsynth/mt32/FreeverbModel.h @@ -32,7 +32,7 @@ class FreeverbModel : public ReverbModel {  public:  	FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp);  	~FreeverbModel(); -	void open(unsigned int sampleRate); +	void open();  	void close();  	void setParameters(Bit8u time, Bit8u level);  	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp index 9f1f01c3c2..4e4d6b4f30 100644 --- a/audio/softsynth/mt32/LA32Ramp.cpp +++ b/audio/softsynth/mt32/LA32Ramp.cpp @@ -79,11 +79,12 @@ LA32Ramp::LA32Ramp() :  void LA32Ramp::startRamp(Bit8u target, Bit8u increment) {  	// CONFIRMED: From sample analysis, this appears to be very accurate. -	// FIXME: We could use a table for this in future  	if (increment == 0) {  		largeIncrement = 0;  	} else { -		largeIncrement = (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f); +		// Using integer argument here, no precision loss: +		// (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f) +		largeIncrement = (unsigned int)(EXP2I(((increment & 0x7F) + 24) << 9) + 0.125f);  	}  	descending = (increment & 0x80) != 0;  	if (descending) { diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index 75912f38a8..cd385898e1 100644 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.cpp @@ -68,18 +68,16 @@ Part::Part(Synth *useSynth, unsigned int usePartNum) {  	activePartialCount = 0;  	memset(patchCache, 0, sizeof(patchCache));  	for (int i = 0; i < MT32EMU_MAX_POLY; i++) { -		freePolys.push_front(new Poly(this)); +		freePolys.prepend(new Poly(this));  	}  }  Part::~Part() { -	while (!activePolys.empty()) { -		delete activePolys.front(); -		activePolys.pop_front(); +	while (!activePolys.isEmpty()) { +		delete activePolys.takeFirst();  	} -	while (!freePolys.empty()) { -		delete freePolys.front(); -		freePolys.pop_front(); +	while (!freePolys.isEmpty()) { +		delete freePolys.takeFirst();  	}  } @@ -209,6 +207,7 @@ void RhythmPart::setTimbre(TimbreParam * /*timbre*/) {  void Part::setTimbre(TimbreParam *timbre) {  	*timbreTemp = *timbre; +	synth->newTimbreSet(partNum, timbre->common.name);  }  unsigned int RhythmPart::getAbsTimbreNum() const { @@ -245,8 +244,8 @@ void Part::backupCacheToPartials(PatchCache cache[4]) {  	// if so then duplicate the cached data from the part to the partial so that  	// we can change the part's cache without affecting the partial.  	// We delay this until now to avoid a copy operation with every note played -	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		(*polyIt)->backupCacheToPartials(cache); +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { +		poly->backupCacheToPartials(cache);  	}  } @@ -445,8 +444,7 @@ void Part::abortPoly(Poly *poly) {  }  bool Part::abortFirstPoly(unsigned int key) { -	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		Poly *poly = *polyIt; +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {  		if (poly->getKey() == key) {  			abortPoly(poly);  			return true; @@ -456,8 +454,7 @@ bool Part::abortFirstPoly(unsigned int key) {  }  bool Part::abortFirstPoly(PolyState polyState) { -	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		Poly *poly = *polyIt; +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {  		if (poly->getState() == polyState) {  			abortPoly(poly);  			return true; @@ -474,10 +471,10 @@ bool Part::abortFirstPolyPreferHeld() {  }  bool Part::abortFirstPoly() { -	if (activePolys.empty()) { +	if (activePolys.isEmpty()) {  		return false;  	} -	abortPoly(activePolys.front()); +	abortPoly(activePolys.getFirst());  	return true;  } @@ -502,17 +499,16 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt  		return;  	} -	if (freePolys.empty()) { +	if (freePolys.isEmpty()) {  		synth->printDebug("%s (%s): No free poly to play key %d (velocity %d)", name, currentInstr, midiKey, velocity);  		return;  	} -	Poly *poly = freePolys.front(); -	freePolys.pop_front(); +	Poly *poly = freePolys.takeFirst();  	if (patchTemp->patch.assignMode & 1) {  		// Priority to data first received -		activePolys.push_front(poly); +		activePolys.prepend(poly);  	} else { -		activePolys.push_back(poly); +		activePolys.append(poly);  	}  	Partial *partials[4]; @@ -537,16 +533,20 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt  #if MT32EMU_MONITOR_PARTIALS > 1  	synth->printPartialUsage();  #endif +	synth->partStateChanged(partNum, true); +	synth->polyStateChanged(partNum);  }  void Part::allNotesOff() {  	// The MIDI specification states - and Mok confirms - that all notes off (0x7B)  	// should treat the hold pedal as usual. -	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		Poly *poly = *polyIt; -		// FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed -		// applies to AllNotesOff. -		poly->noteOff(holdpedal); +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { +		// FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed applies to AllNotesOff. +		// if (poly->canSustain() || poly->getKey() == 0) { +		// FIXME: The real devices are found to be ignoring non-sustaining polys while processing AllNotesOff. Need to be confirmed. +		if (poly->canSustain()) { +			poly->noteOff(holdpedal); +		}  	}  } @@ -554,15 +554,13 @@ void Part::allSoundOff() {  	// MIDI "All sound off" (0x78) should release notes immediately regardless of the hold pedal.  	// This controller is not actually implemented by the synths, though (according to the docs and Mok) -  	// we're only using this method internally. -	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		Poly *poly = *polyIt; +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {  		poly->startDecay();  	}  }  void Part::stopPedalHold() { -	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		Poly *poly = *polyIt; +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {  		poly->stopPedalHold();  	}  } @@ -580,8 +578,7 @@ void Part::stopNote(unsigned int key) {  	synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key);  #endif -	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		Poly *poly = *polyIt; +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {  		// Generally, non-sustaining instruments ignore note off. They die away eventually anyway.  		// Key 0 (only used by special cases on rhythm part) reacts to note off even if non-sustaining or pedal held.  		if (poly->getKey() == key && (poly->canSustain() || key == 0)) { @@ -602,8 +599,7 @@ unsigned int Part::getActivePartialCount() const {  unsigned int Part::getActiveNonReleasingPartialCount() const {  	unsigned int activeNonReleasingPartialCount = 0; -	for (Common::List<Poly *>::const_iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { -		Poly *poly = *polyIt; +	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {  		if (poly->getState() != POLY_Releasing) {  			activeNonReleasingPartialCount += poly->getActivePartialCount();  		} @@ -615,7 +611,103 @@ void Part::partialDeactivated(Poly *poly) {  	activePartialCount--;  	if (!poly->isActive()) {  		activePolys.remove(poly); -		freePolys.push_front(poly); +		freePolys.prepend(poly); +		synth->polyStateChanged(partNum); +	} +	if (activePartialCount == 0) { +		synth->partStateChanged(partNum, false); +	} +} + +//#define POLY_LIST_DEBUG + +PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {} + +bool PolyList::isEmpty() const { +#ifdef POLY_LIST_DEBUG +	if ((firstPoly == NULL || lastPoly == NULL) && firstPoly != lastPoly) { +		printf("PolyList: desynchronised firstPoly & lastPoly pointers\n"); +	} +#endif +	return firstPoly == NULL && lastPoly == NULL; +} + +Poly *PolyList::getFirst() const { +	return firstPoly; +} + +Poly *PolyList::getLast() const { +	return lastPoly; +} + +void PolyList::prepend(Poly *poly) { +#ifdef POLY_LIST_DEBUG +	if (poly->getNext() != NULL) { +		printf("PolyList: Non-NULL next field in a Poly being prepended is ignored\n"); +	} +#endif +	poly->setNext(firstPoly); +	firstPoly = poly; +	if (lastPoly == NULL) { +		lastPoly = poly; +	} +} + +void PolyList::append(Poly *poly) { +#ifdef POLY_LIST_DEBUG +	if (poly->getNext() != NULL) { +		printf("PolyList: Non-NULL next field in a Poly being appended is ignored\n"); +	} +#endif +	poly->setNext(NULL); +	if (lastPoly != NULL) { +#ifdef POLY_LIST_DEBUG +		if (lastPoly->getNext() != NULL) { +			printf("PolyList: Non-NULL next field in the lastPoly\n"); +		} +#endif +		lastPoly->setNext(poly); +	} +	lastPoly = poly; +	if (firstPoly == NULL) { +		firstPoly = poly; +	} +} + +Poly *PolyList::takeFirst() { +	Poly *oldFirst = firstPoly; +	firstPoly = oldFirst->getNext(); +	if (firstPoly == NULL) { +#ifdef POLY_LIST_DEBUG +		if (lastPoly != oldFirst) { +			printf("PolyList: firstPoly != lastPoly in a list with a single Poly\n"); +		} +#endif +		lastPoly = NULL; +	} +	oldFirst->setNext(NULL); +	return oldFirst; +} + +void PolyList::remove(Poly * const polyToRemove) { +	if (polyToRemove == firstPoly) { +		takeFirst(); +		return; +	} +	for (Poly *poly = firstPoly; poly != NULL; poly = poly->getNext()) { +		if (poly->getNext() == polyToRemove) { +			if (polyToRemove == lastPoly) { +#ifdef POLY_LIST_DEBUG +				if (lastPoly->getNext() != NULL) { +					printf("PolyList: Non-NULL next field in the lastPoly\n"); +				} +#endif +				lastPoly = poly; +			} +			poly->setNext(polyToRemove->getNext()); +			polyToRemove->setNext(NULL); +			break; +		}  	}  } diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h index 5c59c6d61f..e5be41ff10 100644 --- a/audio/softsynth/mt32/Part.h +++ b/audio/softsynth/mt32/Part.h @@ -18,13 +18,27 @@  #ifndef MT32EMU_PART_H  #define MT32EMU_PART_H -#include <common/list.h> -  namespace MT32Emu {  class PartialManager;  class Synth; +class PolyList { +private: +	Poly *firstPoly; +	Poly *lastPoly; + +public: +	PolyList(); +	bool isEmpty() const; +	Poly *getFirst() const; +	Poly *getLast() const; +	void prepend(Poly *poly); +	void append(Poly *poly); +	Poly *takeFirst(); +	void remove(Poly * const poly); +}; +  class Part {  private:  	// Direct pointer to sysex-addressable memory dedicated to this part (valid for parts 1-8, NULL for rhythm) @@ -37,8 +51,8 @@ private:  	unsigned int activePartialCount;  	PatchCache patchCache[4]; -	Common::List<Poly *> freePolys; -	Common::List<Poly *> activePolys; +	PolyList freePolys; +	PolyList activePolys;  	void setPatch(const PatchParam *patch);  	unsigned int midiKeyToKey(unsigned int midiKey); diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index 03bec560b8..a4d1ab03fa 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -22,7 +22,7 @@  #include "mt32emu.h"  #include "mmath.h" -using namespace MT32Emu; +namespace MT32Emu {  #ifdef INACCURATE_SMOOTH_PAN  // Mok wanted an option for smoother panning, and we love Mok. @@ -85,6 +85,7 @@ void Partial::deactivate() {  			pair->pair = NULL;  		}  	} +	synth->partialStateChanged(this, tva->getPhase(), TVA_PHASE_DEAD);  #if MT32EMU_MONITOR_PARTIALS > 2  	synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum);  	synth->printPartialUsage(sampleNum); @@ -133,6 +134,25 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us  	stereoVolume.leftVol = panVal / 7.0f;  	stereoVolume.rightVol = 1.0f - stereoVolume.leftVol; +	// SEMI-CONFIRMED: From sample analysis: +	// Found that timbres with 3 or 4 partials (i.e. one using two partial pairs) are mixed in two different ways. +	// Either partial pairs are added or subtracted, it depends on how the partial pairs are allocated. +	// It seems that partials are grouped into quarters and if the partial pairs are allocated in different quarters the subtraction happens. +	// Though, this matters little for the majority of timbres, it becomes crucial for timbres which contain several partials that sound very close. +	// In this case that timbre can sound totally different depending of the way it is mixed up. +	// Most easily this effect can be displayed with the help of a special timbre consisting of several identical square wave partials (3 or 4). +	// Say, it is 3-partial timbre. Just play any two notes simultaneously and the polys very probably are mixed differently. +	// Moreover, the partial allocator retains the last partial assignment it did and all the subsequent notes will sound the same as the last released one. +	// The situation is better with 4-partial timbres since then a whole quarter is assigned for each poly. However, if a 3-partial timbre broke the normal +	// whole-quarter assignment or after some partials got aborted, even 4-partial timbres can be found sounding differently. +	// This behaviour is also confirmed with two more special timbres: one with identical sawtooth partials, and one with PCM wave 02. +	// For my personal taste, this behaviour rather enriches the sounding and should be emulated. +	// Also, the current partial allocator model probably needs to be refined. +	if (debugPartialNum & 8) { +		stereoVolume.leftVol = -stereoVolume.leftVol; +		stereoVolume.rightVol = -stereoVolume.rightVol; +	} +  	if (patchCache->PCMPartial) {  		pcmNum = patchCache->pcm;  		if (synth->controlROMMap->pcmCount > 128) { @@ -149,7 +169,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us  	}  	// CONFIRMED: pulseWidthVal calculation is based on information from Mok -	pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + synth->tables.pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth]; +	pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + Tables::getInstance().pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth];  	if (pulseWidthVal < 0) {  		pulseWidthVal = 0;  	} else if (pulseWidthVal > 255) { @@ -175,6 +195,7 @@ float Partial::getPCMSample(unsigned int position) {  }  unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) { +	const Tables &tables = Tables::getInstance();  	if (!isActive() || alreadyOutputed) {  		return 0;  	} @@ -197,6 +218,9 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  			deactivate();  			break;  		} + +		Bit16u pitch = tvp->nextPitch(); +  		// SEMI-CONFIRMED: From sample analysis:  		// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.  		// This gives results within +/- 2 at the output (before any DAC bitshifting) @@ -206,10 +230,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  		// positive amps, so negative still needs to be explored, as well as lower levels.  		//  		// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing. + +#if MT32EMU_ACCURATE_WG == 1  		float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f); +		float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE; +#else +		static const float ampFactor = EXP2F(32772 / -2048.0f); +		float amp = EXP2I(ampRampVal >> 10) * ampFactor; -		Bit16u pitch = tvp->nextPitch(); -		float freq = synth->tables.pitchToFreq[pitch]; +		static const float freqFactor = EXP2F(-16.0f) * SAMPLE_RATE; +		float freq = EXP2I(pitch) * freqFactor; +#endif  		if (patchCache->PCMPartial) {  			// Render PCM waveform @@ -221,12 +252,18 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  				break;  			}  			Bit32u pcmAddr = pcmWave->addr; -			float positionDelta = freq * 2048.0f / synth->myProp.sampleRate; +			float positionDelta = freq * 2048.0f / SAMPLE_RATE;  			// Linear interpolation  			float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition]; -			float nextSample = getPCMSample(intPCMPosition + 1); -			sample = firstSample + (nextSample - firstSample) * (pcmPosition - intPCMPosition); +			// We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial. +			// It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial +			// is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair). +			if (pair == NULL || mixType == 0 || structurePosition == 0) { +				sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition); +			} else { +				sample = firstSample; +			}  			float newPCMPosition = pcmPosition + positionDelta;  			if (pcmWave->loop) { @@ -247,8 +284,12 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  			// res corresponds to a value set in an LA32 register  			Bit8u res = patchCache->srcPartial.tvf.resonance + 1; -			// EXP2F(1.0f - (32 - res) / 4.0f); -			float resAmp = synth->tables.resAmpMax[res]; +			float resAmp; +			{ +				// resAmp = EXP2F(1.0f - (32 - res) / 4.0f); +				static const float resAmpFactor = EXP2F(-7); +				resAmp = EXP2I(res << 10) * resAmpFactor; +			}  			// The cutoffModifier may not be supposed to be directly added to the cutoff -  			// it may for example need to be multiplied in some way. @@ -260,7 +301,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  			}  			// Wave length in samples -			float waveLen = synth->myProp.sampleRate / freq; +			float waveLen = SAMPLE_RATE / freq;  			// Init cosineLen  			float cosineLen = 0.5f * waveLen; @@ -268,7 +309,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				cosineLen *= EXP2F((cutoffVal - 128.0f) / -16.0f); // found from sample analysis  #else -				cosineLen *= synth->tables.cutoffToCosineLen[Bit32u((cutoffVal - 128.0f) * 8.0f)]; +				static const float cosineLenFactor = EXP2F(128.0f / -16.0f); +				cosineLen *= EXP2I(Bit32u((256.0f - cutoffVal) * 256.0f)) * cosineLenFactor;  #endif  			} @@ -279,23 +321,26 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  				relWavePos -= waveLen;  			} +			// Ratio of positive segment to wave length  			float pulseLen = 0.5f;  			if (pulseWidthVal > 128) { -				pulseLen += synth->tables.pulseLenFactor[pulseWidthVal - 128]; +				// pulseLen = EXP2F((64 - pulseWidthVal) / 64); +				static const float pulseLenFactor = EXP2F(-192 / 64); +				pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor;  			}  			pulseLen *= waveLen; -			float lLen = pulseLen - cosineLen; +			float hLen = pulseLen - cosineLen;  			// Ignore pulsewidths too high for given freq -			if (lLen < 0.0f) { -				lLen = 0.0f; +			if (hLen < 0.0f) { +				hLen = 0.0f;  			}  			// Ignore pulsewidths too high for given freq and cutoff -			float hLen = waveLen - lLen - 2 * cosineLen; -			if (hLen < 0.0f) { -				hLen = 0.0f; +			float lLen = waveLen - hLen - 2 * cosineLen; +			if (lLen < 0.0f) { +				lLen = 0.0f;  			}  			// Correct resAmp for cutoff in range 50..66 @@ -303,7 +348,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f);  #else -				resAmp *= synth->tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))]; +				resAmp *= tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))];  #endif  			} @@ -314,7 +359,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample = -cosf(FLOAT_PI * relWavePos / cosineLen);  #else -				sample = -synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024]; +				sample = -tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024];  #endif  			} else @@ -328,7 +373,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);  #else -				sample = synth->tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024]; +				sample = tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024];  #endif  			} else { @@ -343,7 +388,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample *= EXP2F(-0.125f * (128.0f - cutoffVal));  #else -				sample *= synth->tables.cutoffToFilterAmp[Bit32u(cutoffVal * 8.0f)]; +				static const float cutoffAttenuationFactor = EXP2F(-0.125f * 128.0f); +				sample *= EXP2I(Bit32u(512.0f * cutoffVal)) * cutoffAttenuationFactor;  #endif  			} else { @@ -363,11 +409,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				resSample *= sinf(FLOAT_PI * relWavePos / cosineLen);  #else -				resSample *= synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095]; +				resSample *= tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095];  #endif  				// Resonance sine amp -				float resAmpFade = EXP2F(-synth->tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen));	// seems to be exact +				float resAmpFadeLog2 = -tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen); // seems to be exact +#if MT32EMU_ACCURATE_WG == 1 +				float resAmpFade = EXP2F(resAmpFadeLog2); +#else +				static const float resAmpFadeFactor = EXP2F(-30.0f); +				float resAmpFade = (resAmpFadeLog2 < -30.0f) ? 0.0f : EXP2I(Bit32u((30.0f + resAmpFadeLog2) * 4096.0f)) * resAmpFadeFactor; +#endif  				// Now relWavePos set negative to the left from center of any cosine  				relWavePos = wavePos; @@ -388,7 +440,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  					resAmpFade *= 0.5f * (1.0f - cosf(FLOAT_PI * relWavePos / (0.5f * cosineLen)));  #else -					resAmpFade *= 0.5f * (1.0f + synth->tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]); +					resAmpFade *= 0.5f * (1.0f + tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]);  #endif  				} @@ -400,7 +452,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample *= cosf(FLOAT_2PI * wavePos / waveLen);  #else -				sample *= synth->tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024]; +				sample *= tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024];  #endif  			} @@ -516,10 +568,9 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt  			}  		}  		if (numGenerated > pairNumGenerated) { -			if (mixType == 1) { -				mixBuffersRingMix(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated); -			} else { -				mixBuffersRing(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated); +			if (mixType == 2) { +				numGenerated = pairNumGenerated; +				deactivate();  			}  		}  	} @@ -555,3 +606,5 @@ void Partial::startDecayAll() {  	tvp->startDecay();  	tvf->startDecay();  } + +} diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp index 0a6be826d6..f8c2dbcd48 100644 --- a/audio/softsynth/mt32/PartialManager.cpp +++ b/audio/softsynth/mt32/PartialManager.cpp @@ -20,7 +20,7 @@  #include "mt32emu.h"  #include "PartialManager.h" -using namespace MT32Emu; +namespace MT32Emu {  PartialManager::PartialManager(Synth *useSynth, Part **useParts) {  	synth = useSynth; @@ -248,3 +248,5 @@ const Partial *PartialManager::getPartial(unsigned int partialNum) const {  	}  	return partialTable[partialNum];  } + +} diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp index a2f00db73c..46e30c0f02 100644 --- a/audio/softsynth/mt32/Poly.cpp +++ b/audio/softsynth/mt32/Poly.cpp @@ -29,6 +29,7 @@ Poly::Poly(Part *usePart) {  		partials[i] = NULL;  	}  	state = POLY_Inactive; +	next = NULL;  }  void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, Partial **newPartials) { @@ -58,6 +59,9 @@ bool Poly::noteOff(bool pedalHeld) {  		return false;  	}  	if (pedalHeld) { +		if (state == POLY_Held) { +			return false; +		}  		state = POLY_Held;  	} else {  		startDecay(); @@ -171,4 +175,12 @@ void Poly::partialDeactivated(Partial *partial) {  	part->partialDeactivated(this);  } +Poly *Poly::getNext() { +	return next; +} + +void Poly::setNext(Poly *poly) { +	next = poly; +} +  } diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h index cd15a776f5..e25b6d8993 100644 --- a/audio/softsynth/mt32/Poly.h +++ b/audio/softsynth/mt32/Poly.h @@ -41,6 +41,8 @@ private:  	Partial *partials[4]; +	Poly *next; +  public:  	Poly(Part *part);  	void reset(unsigned int key, unsigned int velocity, bool sustain, Partial **partials); @@ -60,6 +62,9 @@ public:  	bool isActive() const;  	void partialDeactivated(Partial *partial); + +	Poly *getNext(); +	void setNext(Poly *poly);  };  } diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp new file mode 100644 index 0000000000..514bc23496 --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.cpp @@ -0,0 +1,111 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +//#include <cstring> +#include "ROMInfo.h" + +namespace MT32Emu { + +// Known ROMs +static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL}; +static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo * const ROM_INFOS[] = { +		&CTRL_MT32_V1_04, +		&CTRL_MT32_V1_05, +		&CTRL_MT32_V1_06, +		&CTRL_MT32_V1_07, +		&CTRL_MT32_BLUER, +		&CTRL_CM32L_V1_00, +		&CTRL_CM32L_V1_02, +		&PCM_MT32, +		&PCM_CM32L, +		NULL}; + +const ROMInfo* ROMInfo::getROMInfo(Common::File *file) { +	size_t fileSize = file->size(); +	// We haven't added the SHA1 checksum code in ScummVM, as the file size +	// suffices for our needs for now. +	//const char *fileDigest = file->getSHA1(); +	for (int i = 0; ROM_INFOS[i] != NULL; i++) { +		const ROMInfo *romInfo = ROM_INFOS[i]; +		if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) { +			return romInfo; +		} +	} +	return NULL; +} + +void ROMInfo::freeROMInfo(const ROMInfo *romInfo) { +	(void) romInfo; +} + +static int getROMCount() { +	int count; +	for(count = 0; ROM_INFOS[count] != NULL; count++) { +	} +	return count; +} + +const ROMInfo** ROMInfo::getROMInfoList(unsigned int types, unsigned int pairTypes) { +	const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1]; +	const ROMInfo **currentROMInList = romInfoList; +	for(int i = 0; ROM_INFOS[i] != NULL; i++) { +		const ROMInfo *romInfo = ROM_INFOS[i]; +		if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { +			*currentROMInList++ = romInfo; +		} +	} +	*currentROMInList = NULL; +	return romInfoList; +} + +void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) { +	delete[] romInfoList; +} + +const ROMImage* ROMImage::makeROMImage(Common::File *file) { +	ROMImage *romImage = new ROMImage; +	romImage->file = file; +	romImage->romInfo = ROMInfo::getROMInfo(romImage->file); +	return romImage; +} + +void ROMImage::freeROMImage(const ROMImage *romImage) { +	ROMInfo::freeROMInfo(romImage->romInfo); +	delete romImage; +} + + +Common::File* ROMImage::getFile() const { +	return file; +} + +const ROMInfo* ROMImage::getROMInfo() const { +	return romInfo; +} + +} diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h new file mode 100644 index 0000000000..2ffd2b72c6 --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MT32EMU_ROMINFO_H +#define MT32EMU_ROMINFO_H + +//#include <cstddef> +#include "common/file.h" + +namespace MT32Emu { + +// Defines vital info about ROM file to be used by synth and applications + +struct ROMInfo { +public: +	size_t fileSize; +	const char *sha1Digest; +	enum Type {PCM, Control, Reverb} type; +	const char *shortName; +	const char *description; +	enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; +	ROMInfo *pairROMInfo; +	void *controlROMInfo; + +	// Returns a ROMInfo struct by inspecting the size and the SHA1 hash +	static const ROMInfo* getROMInfo(Common::File *file); + +	// Currently no-op +	static void freeROMInfo(const ROMInfo *romInfo); + +	// Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes +	// (specified by bitmasks) +	// Useful for GUI/console app to output information on what ROMs it supports +	static const ROMInfo** getROMInfoList(unsigned int types, unsigned int pairTypes); + +	// Frees the list of ROMInfos given +	static void freeROMInfoList(const ROMInfo **romInfos); +}; + +// Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work + +class ROMImage { +private: +	Common::File *file; +	const ROMInfo *romInfo; + +public: + +	// Creates a ROMImage object given a ROMInfo and a File. Keeps a reference +	// to the File and ROMInfo given, which must be freed separately by the user +	// after the ROMImage is freed +	static const ROMImage* makeROMImage(Common::File *file); + +	// Must only be done after all Synths using the ROMImage are deleted +	static void freeROMImage(const ROMImage *romImage); + +	Common::File *getFile() const; +	const ROMInfo *getROMInfo() const; +}; + +} + +#endif diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 7a1b5c2275..7f4ba44999 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -27,8 +27,10 @@  #include "mmath.h"  #include "PartialManager.h" -#if MT32EMU_USE_AREVERBMODEL == 1 +#if MT32EMU_USE_REVERBMODEL == 1  #include "AReverbModel.h" +#elif MT32EMU_USE_REVERBMODEL == 2 +#include "BReverbModel.h"  #else  #include "FreeverbModel.h"  #endif @@ -36,7 +38,7 @@  namespace MT32Emu { -const ControlROMMap ControlROMMaps[7] = { +static const ControlROMMap ControlROMMaps[7] = {  	// ID    IDc IDbytes                     PCMmap  PCMc  tmbrA   tmbrAO, tmbrAC tmbrB   tmbrBO, tmbrBC tmbrR   trC  rhythm  rhyC  rsrv    panpot  prog    rhyMax  patMax  sysMax  timMax  	{0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000,  128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200,  30, 0x73A6,  85,  0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},  	{0x4014, 22, "\000 ver1.05 06 Aug, 87 ", 0x3000,  128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200,  30, 0x7414,  85,  0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A}, @@ -140,22 +142,36 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {  	return checksum;  } -Synth::Synth() { +Synth::Synth(ReportHandler *useReportHandler) {  	isOpen = false;  	reverbEnabled = true;  	reverbOverridden = false; -#if MT32EMU_USE_AREVERBMODEL == 1 -	reverbModels[0] = new AReverbModel(&AReverbModel::REVERB_MODE_0_SETTINGS); -	reverbModels[1] = new AReverbModel(&AReverbModel::REVERB_MODE_1_SETTINGS); -	reverbModels[2] = new AReverbModel(&AReverbModel::REVERB_MODE_2_SETTINGS); +	if (useReportHandler == NULL) { +		reportHandler = new ReportHandler; +		isDefaultReportHandler = true; +	} else { +		reportHandler = useReportHandler; +		isDefaultReportHandler = false; +	} + +#if MT32EMU_USE_REVERBMODEL == 1 +	reverbModels[REVERB_MODE_ROOM] = new AReverbModel(REVERB_MODE_ROOM); +	reverbModels[REVERB_MODE_HALL] = new AReverbModel(REVERB_MODE_HALL); +	reverbModels[REVERB_MODE_PLATE] = new AReverbModel(REVERB_MODE_PLATE); +	reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb(); +#elif MT32EMU_USE_REVERBMODEL == 2 +	reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM); +	reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL); +	reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE); +	reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY);  #else -	reverbModels[0] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f); -	reverbModels[1] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f); -	reverbModels[2] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f); +	reverbModels[REVERB_MODE_ROOM] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f); +	reverbModels[REVERB_MODE_HALL] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f); +	reverbModels[REVERB_MODE_PLATE] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f); +	reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb();  #endif -	reverbModels[3] = new DelayReverb();  	reverbModel = NULL;  	setDACInputMode(DACInputMode_NICE);  	setOutputGain(1.0f); @@ -170,31 +186,49 @@ Synth::~Synth() {  	for (int i = 0; i < 4; i++) {  		delete reverbModels[i];  	} +	if (isDefaultReportHandler) { +		delete reportHandler; +	} +} + +void ReportHandler::showLCDMessage(const char *data) { +	printf("WRITE-LCD: %s", data); +	printf("\n"); +} + +void ReportHandler::printDebug(const char *fmt, va_list list) { +		vprintf(fmt, list); +		printf("\n");  } -int Synth::report(ReportType type, const void *data) { -	if (myProp.report != NULL) { -		return myProp.report(myProp.userData, type, data); +void Synth::partStateChanged(int partNum, bool isPartActive) { +	reportHandler->onPartStateChanged(partNum, isPartActive); +} + +void Synth::polyStateChanged(int partNum) { +	reportHandler->onPolyStateChanged(partNum); +} + +void Synth::partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase) { +	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { +		if (getPartial(i) == partial) { +			reportHandler->onPartialStateChanged(i, oldPartialPhase, newPartialPhase); +			break; +		}  	} -	return 0;  } -unsigned int Synth::getSampleRate() const { -	return myProp.sampleRate; +void Synth::newTimbreSet(int partNum, char patchName[]) { +	reportHandler->onProgramChanged(partNum, patchName);  }  void Synth::printDebug(const char *fmt, ...) {  	va_list ap;  	va_start(ap, fmt); -	if (myProp.printDebug != NULL) { -		myProp.printDebug(myProp.userData, fmt, ap); -	} else {  #if MT32EMU_DEBUG_SAMPLESTAMPS > 0 -		printf("[%u] ", renderedSampleCount); +	reportHandler->printDebug("[%u] ", renderedSampleCount);  #endif -		vprintf(fmt, ap); -		printf("\n"); -	} +	reportHandler->printDebug(fmt, ap);  	va_end(ap);  } @@ -244,80 +278,60 @@ void Synth::setReverbOutputGain(float newReverbOutputGain) {  	reverbOutputGain = newReverbOutputGain;  } -Common::File *Synth::openFile(const char *filename) { -	if (myProp.openFile != NULL) { -		return myProp.openFile(myProp.userData, filename); -	} -	char pathBuf[2048]; -	if (myProp.baseDir != NULL) { -		strcpy(&pathBuf[0], myProp.baseDir); -		strcat(&pathBuf[0], filename); -		filename = pathBuf; -	} -	Common::File *file = new Common::File(); -	if (!file->open(filename)) { -		delete file; -		return NULL; -	} -	return file; -} - -void Synth::closeFile(Common::File *file) { -	if (myProp.closeFile != NULL) { -		myProp.closeFile(myProp.userData, file); -	} else { -		file->close(); -		delete file; -	} -} - -LoadResult Synth::loadControlROM(const char *filename) { -	Common::File *file = openFile(filename); // ROM File -	if (file == NULL) { -		return LoadResult_NotFound; -	} -	size_t fileSize = file->size(); -	if (fileSize != CONTROL_ROM_SIZE) { -		printDebug("Control ROM file %s size mismatch: %i", filename, fileSize); +bool Synth::loadControlROM(const ROMImage &controlROMImage) { +	if (&controlROMImage == NULL) return false; +	Common::File *file = controlROMImage.getFile(); +	const ROMInfo *controlROMInfo = controlROMImage.getROMInfo(); +	if ((controlROMInfo == NULL) +			|| (controlROMInfo->type != ROMInfo::Control) +			|| (controlROMInfo->pairType != ROMInfo::Full)) { +		return false;  	} +#if MT32EMU_MONITOR_INIT +	printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description); +#endif  	file->read(controlROMData, CONTROL_ROM_SIZE); -	if (file->err()) { -		closeFile(file); -		return LoadResult_Unreadable; -	} -	closeFile(file);  	// Control ROM successfully loaded, now check whether it's a known type  	controlROMMap = NULL;  	for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) {  		if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {  			controlROMMap = &ControlROMMaps[i]; -			return LoadResult_OK; +			return true;  		}  	} -	printDebug("%s does not match a known control ROM type", filename); -	return LoadResult_Invalid; +#if MT32EMU_MONITOR_INIT +	printDebug("Control ROM failed to load"); +#endif +	return false;  } -LoadResult Synth::loadPCMROM(const char *filename) { -	Common::File *file = openFile(filename); // ROM File -	if (file == NULL) { -		return LoadResult_NotFound; +bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { +	if (&pcmROMImage == NULL) return false; +	Common::File *file = pcmROMImage.getFile(); +	const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo(); +	if ((pcmROMInfo == NULL) +			|| (pcmROMInfo->type != ROMInfo::PCM) +			|| (pcmROMInfo->pairType != ROMInfo::Full)) { +		return false;  	} +#if MT32EMU_MONITOR_INIT +	printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description); +#endif  	size_t fileSize = file->size(); -	if (fileSize < (size_t)(2 * pcmROMSize)) { -		printDebug("PCM ROM file is too short (expected %d, got %d)", 2 * pcmROMSize, fileSize); -		closeFile(file); -		return LoadResult_Invalid; -	} -	if (file->err()) { -		closeFile(file); -		return LoadResult_Unreadable; +	if (fileSize != (2 * pcmROMSize)) { +#if MT32EMU_MONITOR_INIT +		printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize); +#endif +		return false;  	} -	LoadResult rc = LoadResult_OK; -	for (int i = 0; i < pcmROMSize; i++) { -		Bit8u s = file->readByte(); -		Bit8u c = file->readByte(); + +	byte *buffer = new byte[file->size()]; +	file->read(buffer, file->size()); +	const byte *fileData = buffer; +	for (size_t i = 0; i < pcmROMSize; i++) { +		Bit8u s = *(fileData++); +		Bit8u c = *(fileData++);  		int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; @@ -343,16 +357,18 @@ LoadResult Synth::loadPCMROM(const char *filename) {  		pcmROMData[i] = lin;  	} -	closeFile(file); -	return rc; + +	delete[] buffer; + +	return true;  }  bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {  	ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];  	for (int i = 0; i < count; i++) { -		int rAddr = tps[i].pos * 0x800; -		int rLenExp = (tps[i].len & 0x70) >> 4; -		int rLen = 0x800 << rLenExp; +		size_t rAddr = tps[i].pos * 0x800; +		size_t rLenExp = (tps[i].len & 0x70) >> 4; +		size_t rLen = 0x800 << rLenExp;  		if (rAddr + rLen > pcmROMSize) {  			printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);  			return false; @@ -414,26 +430,19 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi  	return true;  } -bool Synth::open(SynthProperties &useProp) { +bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) {  	if (isOpen) {  		return false;  	}  	prerenderReadIx = prerenderWriteIx = 0; -	myProp = useProp;  #if MT32EMU_MONITOR_INIT  	printDebug("Initialising Constant Tables");  #endif -	tables.init();  #if !MT32EMU_REDUCE_REVERB_MEMORY  	for (int i = 0; i < 4; i++) {  		reverbModels[i]->open(useProp.sampleRate);  	}  #endif -	if (useProp.baseDir != NULL) { -		char *baseDirCopy = new char[strlen(useProp.baseDir) + 1]; -		strcpy(baseDirCopy, useProp.baseDir); -		myProp.baseDir = baseDirCopy; -	}  	// This is to help detect bugs  	memset(&mt32ram, '?', sizeof(mt32ram)); @@ -441,12 +450,10 @@ bool Synth::open(SynthProperties &useProp) {  #if MT32EMU_MONITOR_INIT  	printDebug("Loading Control ROM");  #endif -	if (loadControlROM("CM32L_CONTROL.ROM") != LoadResult_OK) { -		if (loadControlROM("MT32_CONTROL.ROM") != LoadResult_OK) { -			printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM"); -			//report(ReportType_errorControlROM, &errno); -			return false; -		} +	if (!loadControlROM(controlROMImage)) { +		printDebug("Init Error - Missing or invalid Control ROM image"); +		reportHandler->onErrorControlROM(); +		return false;  	}  	initMemoryRegions(); @@ -460,12 +467,10 @@ bool Synth::open(SynthProperties &useProp) {  #if MT32EMU_MONITOR_INIT  	printDebug("Loading PCM ROM");  #endif -	if (loadPCMROM("CM32L_PCM.ROM") != LoadResult_OK) { -		if (loadPCMROM("MT32_PCM.ROM") != LoadResult_OK) { -			printDebug("Init Error - Missing MT32_PCM.ROM"); -			//report(ReportType_errorPCMROM, &errno); -			return false; -		} +	if (!loadPCMROM(pcmROMImage)) { +		printDebug("Init Error - Missing PCM ROM image"); +		reportHandler->onErrorPCMROM(); +		return false;  	}  #if MT32EMU_MONITOR_INIT @@ -594,9 +599,6 @@ void Synth::close() {  		parts[i] = NULL;  	} -	delete[] myProp.baseDir; -	myProp.baseDir = NULL; -  	delete[] pcmWaves;  	delete[] pcmROMData; @@ -627,6 +629,11 @@ void Synth::playMsg(Bit32u msg) {  		return;  	}  	playMsgOnPart(part, code, note, velocity); + +	// This ensures minimum 1-sample delay between sequential MIDI events +	// Without this, a sequence of NoteOn and immediately succeeding NoteOff messages is always silent +	// Technically, it's also impossible to send events through the MIDI interface faster than about each millisecond +	prerender();  }  void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { @@ -1178,7 +1185,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le  	case MR_System:  		region->write(0, off, data, len); -		report(ReportType_devReconfig, NULL); +		reportHandler->onDeviceReconfig();  		// FIXME: We haven't properly confirmed any of this behaviour  		// In particular, we tend to reset things such as reverb even if the write contained  		// the same parameters as were already set, which may be wrong. @@ -1216,7 +1223,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le  #if MT32EMU_MONITOR_SYSEX > 0  		printDebug("WRITE-LCD: %s", buf);  #endif -		report(ReportType_lcdMessage, buf); +		reportHandler->showLCDMessage(buf);  		break;  	case MR_Reset:  		reset(); @@ -1244,9 +1251,9 @@ void Synth::refreshSystemReverbParameters() {  #endif  		return;  	} -	report(ReportType_newReverbMode,  &mt32ram.system.reverbMode); -	report(ReportType_newReverbTime,  &mt32ram.system.reverbTime); -	report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel); +	reportHandler->onNewReverbMode(mt32ram.system.reverbMode); +	reportHandler->onNewReverbTime(mt32ram.system.reverbTime); +	reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel);  	ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode];  #if MT32EMU_REDUCE_REVERB_MEMORY @@ -1254,7 +1261,7 @@ void Synth::refreshSystemReverbParameters() {  		if (reverbModel != NULL) {  			reverbModel->close();  		} -		newReverbModel->open(myProp.sampleRate); +		newReverbModel->open();  	}  #endif  	reverbModel = newReverbModel; @@ -1309,7 +1316,7 @@ void Synth::reset() {  #if MT32EMU_MONITOR_SYSEX > 0  	printDebug("RESET");  #endif -	report(ReportType_devReset, NULL); +	reportHandler->onDeviceReset();  	partialManager->deactivateAll();  	mt32ram = mt32default;  	for (int i = 0; i < 9; i++) { diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index ccabce7282..91375c0fc0 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -26,6 +26,7 @@ class TableInitialiser;  class Partial;  class PartialManager;  class Part; +class ROMImage;  /**   * Methods for emulating the connection between the LA32 and the DAC, which involves @@ -57,71 +58,6 @@ enum DACInputMode {  	DACInputMode_GENERATION2  }; -enum ReportType { -	// Errors -	ReportType_errorControlROM = 1, -	ReportType_errorPCMROM, -	ReportType_errorSampleRate, - -	// Progress -	ReportType_progressInit, - -	// HW spec -	ReportType_availableSSE, -	ReportType_available3DNow, -	ReportType_usingSSE, -	ReportType_using3DNow, - -	// General info -	ReportType_lcdMessage, -	ReportType_devReset, -	ReportType_devReconfig, -	ReportType_newReverbMode, -	ReportType_newReverbTime, -	ReportType_newReverbLevel -}; - -enum LoadResult { -	LoadResult_OK, -	LoadResult_NotFound, -	LoadResult_Unreadable, -	LoadResult_Invalid -}; - -struct SynthProperties { -	// Sample rate to use in mixing -	unsigned int sampleRate; - -	// Deprecated - ignored. Use Synth::setReverbEnabled() instead. -	bool useReverb; -	// Deprecated - ignored. Use Synth::setReverbOverridden() instead. -	bool useDefaultReverb; -	// Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. -	unsigned char reverbType; -	// Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. -	unsigned char reverbTime; -	// Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. -	unsigned char reverbLevel; -	// The name of the directory in which the ROM and data files are stored (with trailing slash/backslash) -	// Not used if "openFile" is set. May be NULL in any case. -	const char *baseDir; -	// This is used as the first argument to all callbacks -	void *userData; -	// Callback for reporting various errors and information. May be NULL -	int (*report)(void *userData, ReportType type, const void *reportData); -	// Callback for debug messages, in vprintf() format -	void (*printDebug)(void *userData, const char *fmt, va_list list); -	// Callback for providing an implementation of File, opened and ready for use -	// May be NULL, in which case a default implementation will be used. -	Common::File *(*openFile)(void *userData, const char *filename); -	// Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted. -	void (*closeFile)(void *userData, Common::File *file); -}; - -// This is the specification of the Callback routine used when calling the RecalcWaveforms -// function -typedef void (*recalcStatusCallback)(int percDone); -  typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain);  const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; @@ -179,6 +115,13 @@ enum MemoryRegionType {  	MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset  }; +enum ReverbMode { +	REVERB_MODE_ROOM, +	REVERB_MODE_HALL, +	REVERB_MODE_PLATE, +	REVERB_MODE_TAP_DELAY +}; +  class MemoryRegion {  private:  	Synth *synth; @@ -278,7 +221,7 @@ class ReverbModel {  public:  	virtual ~ReverbModel() {}  	// After construction or a close(), open() will be called at least once before any other call (with the exception of close()). -	virtual void open(unsigned int sampleRate) = 0; +	virtual void open() = 0;  	// May be called multiple times without an open() in between.  	virtual void close() = 0;  	virtual void setParameters(Bit8u time, Bit8u level) = 0; @@ -286,6 +229,32 @@ public:  	virtual bool isActive() const = 0;  }; +class ReportHandler { +friend class Synth; + +public: +	virtual ~ReportHandler() {} + +protected: + +	// Callback for debug messages, in vprintf() format +	virtual void printDebug(const char *fmt, va_list list); + +	// Callbacks for reporting various errors and information +	virtual void onErrorControlROM() {} +	virtual void onErrorPCMROM() {} +	virtual void showLCDMessage(const char *message); +	virtual void onDeviceReset() {} +	virtual void onDeviceReconfig() {} +	virtual void onNewReverbMode(Bit8u /* mode */) {} +	virtual void onNewReverbTime(Bit8u /* time */) {} +	virtual void onNewReverbLevel(Bit8u /* level */) {} +	virtual void onPartStateChanged(int /* partNum */, bool /* isActive */) {} +	virtual void onPolyStateChanged(int /* partNum */) {} +	virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} +	virtual void onProgramChanged(int /* partNum */, char * /* patchName */) {} +}; +  class Synth {  friend class Part;  friend class RhythmPart; @@ -315,13 +284,12 @@ private:  	const ControlROMMap *controlROMMap;  	Bit8u controlROMData[CONTROL_ROM_SIZE];  	float *pcmROMData; -	int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM +	size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM  	Bit8s chantable[32];  	Bit32u renderedSampleCount; -	Tables tables;  	MemParams mt32ram, mt32default; @@ -337,6 +305,9 @@ private:  	bool isOpen; +	bool isDefaultReportHandler; +	ReportHandler *reportHandler; +  	PartialManager *partialManager;  	Part *parts[9]; @@ -369,8 +340,6 @@ private:  	int prerenderReadIx;  	int prerenderWriteIx; -	SynthProperties myProp; -  	bool prerender();  	void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len);  	void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len); @@ -384,8 +353,8 @@ private:  	void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data);  	void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data); -	LoadResult loadControlROM(const char *filename); -	LoadResult loadPCMROM(const char *filename); +	bool loadControlROM(const ROMImage &controlROMImage); +	bool loadPCMROM(const ROMImage &pcmROMImage);  	bool initPCMList(Bit16u mapAddress, Bit16u count);  	bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed); @@ -399,24 +368,25 @@ private:  	void refreshSystem();  	void reset(); -	unsigned int getSampleRate() const; -  	void printPartialUsage(unsigned long sampleOffset = 0); -protected: -	int report(ReportType type, const void *reportData); -	Common::File *openFile(const char *filename); -	void closeFile(Common::File *file); + +	void partStateChanged(int partNum, bool isPartActive); +	void polyStateChanged(int partNum); +	void partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase); +	void newTimbreSet(int partNum, char patchName[]);  	void printDebug(const char *fmt, ...);  public:  	static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); -	Synth(); +	// Optionally sets callbacks for reporting various errors, information and debug messages +	Synth(ReportHandler *useReportHandler = NULL);  	~Synth();  	// Used to initialise the MT-32. Must be called before any other function.  	// Returns true if initialization was sucessful, otherwise returns false. -	bool open(SynthProperties &useProp); +	// controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. +	bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage);  	// Closes the MT-32 and deallocates any memory used by the synthesizer  	void close(void); diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp index f3e3f7bbc7..19a01cfc73 100644 --- a/audio/softsynth/mt32/TVA.cpp +++ b/audio/softsynth/mt32/TVA.cpp @@ -30,10 +30,13 @@ namespace MT32Emu {  static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0};  TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) : -	partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system) { +	partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) {  }  void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { +	if (newPhase != phase) { +		partial->getSynth()->partialStateChanged(partial, phase, newPhase); +	}  	target = newTarget;  	phase = newPhase;  	ampRamp->startRamp(newTarget, newIncrement); @@ -43,6 +46,9 @@ void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {  }  void TVA::end(int newPhase) { +	if (newPhase != phase) { +		partial->getSynth()->partialStateChanged(partial, phase, newPhase); +	}  	phase = newPhase;  	playing = false;  #if MT32EMU_MONITOR_TVA >= 1 @@ -154,7 +160,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial  	playing = true; -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	int key = partial->getPoly()->getKey();  	int velocity = partial->getPoly()->getVelocity(); @@ -215,7 +221,7 @@ void TVA::recalcSustain() {  		return;  	}  	// We're sustaining. Recalculate all the values -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());  	newTarget += partialParam->tva.envLevel[3];  	// Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp. @@ -241,7 +247,7 @@ int TVA::getPhase() const {  }  void TVA::nextPhase() { -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	if (phase >= TVA_PHASE_DEAD || !playing) {  		partial->getSynth()->printDebug("TVA::nextPhase(): Shouldn't have got here with phase %d, playing=%s", phase, playing ? "true" : "false"); @@ -274,7 +280,7 @@ void TVA::nextPhase() {  	}  	int newTarget; -	int newIncrement = 0; +	int newIncrement = 0; // Initialised to please compilers  	int envPointIndex = phase;  	if (!allLevelsZeroFromNowOn) { diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp index 80b592ea67..47c4917632 100644 --- a/audio/softsynth/mt32/TVF.cpp +++ b/audio/softsynth/mt32/TVF.cpp @@ -117,7 +117,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b  	unsigned int key = partial->getPoly()->getKey();  	unsigned int velocity = partial->getPoly()->getVelocity(); -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key);  #if MT32EMU_MONITOR_TVF >= 1 @@ -179,7 +179,7 @@ void TVF::startDecay() {  }  void TVF::nextPhase() { -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	int newPhase = phase + 1;  	switch (newPhase) { diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index 0b339e8d71..5dc4ca6b66 100644 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.cpp @@ -47,12 +47,11 @@ static Bit16u keyToPitchTable[] = {  TVP::TVP(const Partial *usePartial) :  	partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) { -	unsigned int sampleRate = usePartial->getSynth()->myProp.sampleRate;  	// We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. -	maxCounter = sampleRate / 4000; +	maxCounter = SAMPLE_RATE / 4000;  	// The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing.  	// This is how much to increment it by every maxCounter samples. -	processTimerIncrement = 500000 * maxCounter / sampleRate; +	processTimerIncrement = 500000 * maxCounter / SAMPLE_RATE;  }  static Bit16s keyToPitch(unsigned int key) { @@ -171,9 +170,14 @@ void TVP::updatePitch() {  	if (newPitch < 0) {  		newPitch = 0;  	} + +// Note: Temporary #ifdef until we have proper "quirk" configuration +// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" +#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32  	if (newPitch > 59392) {  		newPitch = 59392;  	} +#endif  	pitch = (Bit16u)newPitch;  	// FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future. diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp index c9bd40b7a4..5353a74079 100644 --- a/audio/softsynth/mt32/Tables.cpp +++ b/audio/softsynth/mt32/Tables.cpp @@ -22,18 +22,14 @@  #include "mt32emu.h"  #include "mmath.h" -using namespace MT32Emu; +namespace MT32Emu { -Tables::Tables() { -	initialised = false; +const Tables &Tables::getInstance() { +	static const Tables instance; +	return instance;  } -void Tables::init() { -	if (initialised) { -		return; -	} -	initialised = true; - +Tables::Tables() {  	int lf;  	for (lf = 0; lf <= 100; lf++) {  		// CONFIRMED:KG: This matches a ROM table found by Mok @@ -76,31 +72,9 @@ void Tables::init() {  		//synth->printDebug("%d: %d", i, pulseWidth100To255[i]);  	} -	// Ratio of negative segment to wave length -	for (int i = 0; i < 128; i++) { -		// Formula determined from sample analysis. -		float pt = 0.5f / 127.0f * i; -		pulseLenFactor[i] = (1.241857812f - pt) * pt;    // seems to be 2 ^ (5 / 16) = 1.241857812f -	} - -	for (int i = 0; i < 65536; i++) { -		// Aka (slightly slower): EXP2F(pitchVal / 4096.0f - 16.0f) * 32000.0f -		pitchToFreq[i] = EXP2F(i / 4096.0f - 1.034215715f); -	} - -	// found from sample analysis -	for (int i = 0; i < 1024; i++) { -		cutoffToCosineLen[i] = EXP2F(i / -128.0f); -	} - -	// found from sample analysis -	for (int i = 0; i < 1024; i++) { -		cutoffToFilterAmp[i] = EXP2F(-0.125f * (128.0f - i / 8.0f)); -	} - -	// found from sample analysis -	for (int i = 0; i < 32; i++) { -		resAmpMax[i] = EXP2F(1.0f - (32 - i) / 4.0f); +	// The LA32 chip presumably has such a table inside as the internal computaions seem to be performed using fixed point math with 12-bit fractions +	for (int i = 0; i < 4096; i++) { +		exp2[i] = EXP2F(i / 4096.0f);  	}  	// found from sample analysis @@ -117,3 +91,5 @@ void Tables::init() {  		sinf10[i] = sin(FLOAT_PI * i / 2048.0f);  	}  } + +} diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index a2b5ff5d56..c3e80e7e9b 100644 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -20,14 +20,23 @@  namespace MT32Emu { +// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. +// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator. +// The output from the synth is supposed to be resampled to convert the sample rate. +const unsigned int SAMPLE_RATE = 32000; +  const int MIDDLEC = 60;  class Synth;  class Tables { -	bool initialised; +private: +	Tables(); +	Tables(Tables &);  public: +	static const Tables &getInstance(); +  	// Constant LUTs  	// CONFIRMED: This is used to convert several parameters to amp-modifying values in the TVA envelope: @@ -47,16 +56,9 @@ public:  	// CONFIRMED:  	Bit8u pulseWidth100To255[101]; -	float pulseLenFactor[128]; -	float pitchToFreq[65536]; -	float cutoffToCosineLen[1024]; -	float cutoffToFilterAmp[1024]; -	float resAmpMax[32]; +	float exp2[4096];  	float resAmpFadeFactor[8];  	float sinf10[5120]; - -	Tables(); -	void init();  };  } diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h index 226d73e27e..25c79d57a9 100644 --- a/audio/softsynth/mt32/mmath.h +++ b/audio/softsynth/mt32/mmath.h @@ -52,6 +52,10 @@ static inline float EXP2F(float x) {  #endif  } +static inline float EXP2I(unsigned int i) { +	return float(1 << (i >> 12)) * Tables::getInstance().exp2[i & 0x0FFF]; +} +  static inline float EXP10F(float x) {  	return exp(FLOAT_LN_10 * x);  } diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk index 995e450076..ed6b0d33ed 100644 --- a/audio/softsynth/mt32/module.mk +++ b/audio/softsynth/mt32/module.mk @@ -2,6 +2,7 @@ MODULE := audio/softsynth/mt32  MODULE_OBJS := \  	AReverbModel.o \ +	BReverbModel.o \  	DelayReverb.o \  	FreeverbModel.o \  	LA32Ramp.o \ @@ -9,6 +10,7 @@ MODULE_OBJS := \  	Partial.o \  	PartialManager.o \  	Poly.o \ +	ROMInfo.o \  	Synth.o \  	TVA.o \  	TVF.o \ diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index 091819b95c..ae5f4955b1 100644 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -59,13 +59,18 @@  #define MT32EMU_MONITOR_TVA 0  #define MT32EMU_MONITOR_TVF 0 - -// 0: Use LUTs to speedup WG -// 1: Use precise float math +// The WG algorithm involves dozens of transcendent maths, e.g. exponents and trigonometry. +// Unfortunately, the majority of systems perform such computations inefficiently, +// standard math libs and FPUs make no optimisations for single precision floats, +// and use no LUTs to speedup computing internal taylor series. Though, there're rare exceptions, +// and there's a hope it will become common soon. +// So, this is the crucial point of speed optimisations. We have now eliminated all the transcendent maths +// within the critical path and use LUTs instead. +// Besides, since the LA32 chip is assumed to use similar LUTs inside, the overall emulation accuracy should be better. +// 0: Use LUTs to speedup WG. Most common setting. You can expect about 50% performance boost. +// 1: Use precise float math. Use this setting to achieve more accurate wave generator. If your system performs better with this setting, it is really notable. :)  #define MT32EMU_ACCURATE_WG 0 -#define MT32EMU_USE_EXTINT 0 -  // Configuration  // The maximum number of partials playing simultaneously  #define MT32EMU_MAX_PARTIALS 32 @@ -77,9 +82,10 @@  // If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.  #define MT32EMU_REDUCE_REVERB_MEMORY 1 -// 0: Use standard Freeverb -// 1: Use AReverb (currently not properly tuned) -#define MT32EMU_USE_AREVERBMODEL 0 +// 0: Use legacy Freeverb +// 1: Use Accurate Reverb model aka AReverb +// 2: Use Bit-perfect Boss Reverb model aka BReverb (for developers, not much practical use) +#define MT32EMU_USE_REVERBMODEL 1  namespace MT32Emu  { @@ -109,6 +115,7 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024;  #include "TVF.h"  #include "Partial.h"  #include "Part.h" +#include "ROMInfo.h"  #include "Synth.h"  #endif diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp index e039845b8f..a1a736f9de 100644 --- a/audio/softsynth/opl/dosbox.cpp +++ b/audio/softsynth/opl/dosbox.cpp @@ -247,7 +247,7 @@ byte OPL::read(int port) {  }  void OPL::writeReg(int r, int v) { -	byte tempReg = 0; +	int tempReg = 0;  	switch (_type) {  	case Config::kOpl2:  	case Config::kDualOpl2: @@ -257,12 +257,27 @@ void OPL::writeReg(int r, int v) {  		// Backup old setup register  		tempReg = _reg.normal; -		// We need to set the register we want to write to via port 0x388 -		write(0x388, r); -		// Do the real writing to the register -		write(0x389, v); +		// We directly allow writing to secondary OPL3 registers by using +		// register values >= 0x100. +		if (_type == Config::kOpl3 && r >= 0x100) { +			// We need to set the register we want to write to via port 0x222, +			// since we want to write to the secondary register set. +			write(0x222, r); +			// Do the real writing to the register +			write(0x223, v); +		} else { +			// We need to set the register we want to write to via port 0x388 +			write(0x388, r); +			// Do the real writing to the register +			write(0x389, v); +		} +  		// Restore the old register -		write(0x388, tempReg); +		if (_type == Config::kOpl3 && tempReg >= 0x100) { +			write(0x222, tempReg & ~0x100); +		} else { +			write(0x388, tempReg); +		}  		break;  	};  } diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index c54f620a10..2db7d421b6 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -223,7 +223,7 @@ static int *ENV_CURVE;  /* multiple table */ -#define ML(a) (int)(a * 2) +#define ML(a) (uint)(a * 2)  static const uint MUL_TABLE[16]= {  /* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */  	ML(0.50), ML(1.00), ML(2.00),  ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00), diff --git a/audio/softsynth/sid.cpp b/audio/softsynth/sid.cpp index 1ad822b86a..b6f1c87c4b 100644 --- a/audio/softsynth/sid.cpp +++ b/audio/softsynth/sid.cpp @@ -512,7 +512,7 @@ void Filter::enable_filter(bool enable) {  	enabled = enable;  } -void Filter::reset(){ +void Filter::reset() {  	fc = 0;  	res = 0;  | 
