/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001  Ludvig Strigeus
 * Copyright (C) 2001/2002 The ScummVM project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header$
 */

#include "stdafx.h"
#include "scumm.h"
#include "fmopl.h"
#include "mididrv.h"
#include "imuse.h"
#include "saveload.h"

/*
 * Some constants
 */
#define TICKS_PER_BEAT 480

#define SYSEX_ID 0x7D
#define SPECIAL_CHANNEL 9

#define TRIGGER_ID 0
#define COMMAND_ID 1

#define MDPG_TAG "MDpg"
#define MDHD_TAG "MDhd"


/* Roland to General Midi patch table. Still needs some work. */
static const byte mt32_to_gmidi[128] = {
//    0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
	0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21,	// 0x
	6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39,	// 1x
	88, 54, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80,	// 2x
	48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104,	// 3x
	32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65,	// 4x
	66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61,	// 5x
	61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76,	// 6x
	47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117	// 7x
};



/* Put IMUSE specific classes here, instead of in a .h file
 * they will only be used from this file, so it will reduce
 * compile time */

class IMuseDriver;

struct Part;
struct MidiChannelAdl;
struct MidiChannelGM;
struct Instrument;


struct HookDatas {
	byte _jump, _transpose;
	byte _part_onoff[16];
	byte _part_volume[16];
	byte _part_program[16];
	byte _part_transpose[16];

	int query_param(int param, byte chan);
	int set(byte cls, byte value, byte chan);
};


struct Player {
	IMuseInternal *_se;

	Part *_parts;
	bool _active;
	bool _scanning;
	int _id;
	byte _priority;
	byte _volume;
	int8 _pan;
	int8 _transpose;
	int8 _detune;
	uint _vol_chan;
	byte _vol_eff;

	uint _song_index;
	uint _track_index;
	uint _timer_counter;
	uint _loop_to_beat;
	uint _loop_from_beat;
	uint _loop_counter;
	uint _loop_to_tick;
	uint _loop_from_tick;
	uint32 _tempo;
	uint32 _tempo_eff;						/* NoSave */
	uint32 _cur_pos;
	uint32 _next_pos;
	uint32 _song_offset;
	uint32 _timer_speed;					/* NoSave */
	uint _tick_index;
	uint _beat_index;
	uint _ticks_per_beat;
	byte _speed;									/* NoSave */
	bool _abort;

	HookDatas _hook;

	bool _mt32emulate;

	/* Player part */
	void hook_clear();
	void clear();
	bool start_sound(int sound);
	void uninit_parts();
	byte *parse_midi(byte *s);
	void key_off(uint8 chan, byte data);
	void key_on(uint8 chan, byte data, byte velocity);
	void part_set_transpose(uint8 chan, byte relative, int8 b);
	void parse_sysex(byte *p, uint len);
	void maybe_jump(byte *data);
	void maybe_set_transpose(byte *data);
	void maybe_part_onoff(byte *data);
	void maybe_set_volume(byte *data);
	void maybe_set_program(byte *data);
	void maybe_set_transpose_part(byte *data);
	uint update_actives();
	Part *get_part(uint8 part);
	void turn_off_pedals();
	int set_vol(byte vol);
	int get_param(int param, byte chan);
	int query_part_param(int param, byte chan);
	int set_transpose(byte relative, int b);
	void set_priority(int pri);
	void set_pan(int pan);
	void set_detune(int detune);
	void turn_off_parts();
	void play_active_notes();
	void cancel_volume_fade();

	static void decode_sysex_bytes(byte *src, byte *dst, int len);

	void clear_active_note(int chan, byte note);
	void set_active_note(int chan, byte note);
	void clear_active_notes();

	/* Sequencer part */
	bool set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick);
	void clear_loop();
	void set_speed(byte speed);
	bool jump(uint track, uint beat, uint tick);
	void uninit_seq();
	void set_tempo(uint32 data);
	int start_seq_sound(int sound);
	void find_sustaining_notes(byte *a, byte *b, uint32 l);
	int scan(uint totrack, uint tobeat, uint totick);
	int query_param(int param);

	int fade_vol(byte vol, int time);
	void sequencer_timer();
};

struct VolumeFader {
	Player *player;
	bool active;
	byte curvol;
	uint16 speed_lo_max, num_steps;
	int8 speed_hi;
	int8 direction;
	int8 speed_lo;
	uint16 speed_lo_counter;

	void initialize() {
		active = false;
	} void on_timer();
};

struct SustainingNotes {
	SustainingNotes *next;
	SustainingNotes *prev;
	Player *player;
	byte note, chan;
	uint32 off_pos;
	uint32 pos;
	uint16 counter;
};

struct CommandQueue {
	uint16 array[8];
};


struct IsNoteCmdData {
	byte chan;
	byte note;
	byte vel;
};



struct MidiChannel {
	Part *_part;
	MidiChannelAdl *adl() {
		return (MidiChannelAdl *)this;
	} MidiChannelGM *gm() {
		return (MidiChannelGM *) this;
	}
};


struct MidiChannelGM:MidiChannel {
	byte _chan;
	uint16 _actives[8];
};


struct Part {
	int _slot;
	IMuseDriver *_drv;
	Part *_next, *_prev;
	MidiChannel *_mc;
	Player *_player;
	int16 _pitchbend;
	byte _pitchbend_factor;
	int8 _transpose, _transpose_eff;
	byte _vol, _vol_eff;
	int8 _detune, _detune_eff;
	int8 _pan, _pan_eff;
	bool _on;
	byte _modwheel;
	bool _pedal;
	byte _program;
	int8 _pri;
	byte _pri_eff;
	byte _chan;
	byte _effect_level;
	byte _chorus;
	byte _percussion;
	byte _bank;

	void key_on(byte note, byte velocity);
	void key_off(byte note);
	void set_param(byte param, int value);
	void init(IMuseDriver * _driver);
	void setup(Player *player);
	void uninit();
	void off();
	void silence();
	void set_instrument(uint b);
	void set_instrument(Instrument * data);

	void set_transpose(int8 transpose);
	void set_vol(uint8 volume);
	void set_detune(int8 detune);
	void set_pri(int8 pri);
	void set_pan(int8 pan);
	void set_modwheel(uint value);
	void set_pedal(bool value);
	void set_pitchbend(int value);
	void release_pedal();
	void set_program(byte program);
	void set_chorus(uint chorus);
	void set_effect_level(uint level);

	int update_actives(uint16 *active);
	void set_pitchbend_factor(uint8 value);
	void set_onoff(bool on);
	void fix_after_load();

	void update_pris();

	void changed(byte what);
};

/* Abstract IMuseInternal driver class */
class IMuseDriver {
public:
	enum {
		pcMod = 1,
		pcVolume = 2,
		pcPedal = 4,
		pcModwheel = 8,
		pcPan = 16,
		pcEffectLevel = 32,
		pcProgram = 64,
		pcChorus = 128,
		pcAll = 255,
	};

	virtual void on_timer() = 0;
	virtual uint32 get_base_tempo() = 0;
	virtual byte get_hardware_type() = 0;
	virtual void init(IMuseInternal *eng, OSystem *syst) = 0;
	virtual void update_pris() = 0;
	virtual void set_instrument(uint slot, byte *instr) = 0;
	virtual void part_set_instrument(Part *part, Instrument * instr) = 0;
	virtual void part_key_on(Part *part, byte note, byte velocity) = 0;
	virtual void part_key_off(Part *part, byte note) = 0;
	virtual void part_off(Part *part) = 0;
	virtual void part_changed(Part *part, byte what) = 0;
	virtual void part_set_param(Part *part, byte param, int value) = 0;
	virtual int part_update_active(Part *part, uint16 *active) = 0;
};


// WARNING: This is the internal variant of the IMUSE class.
// imuse.h contains a public version of the same class.
// the public version, only contains a set of methods.
class IMuseInternal {
	friend struct Player;
private:
	  IMuseDriver * _driver;

	byte **_base_sounds;

	byte _locked;
	byte _hardware_type;

private:


	  bool _paused;
	bool _active_volume_faders;
	bool _initialized;
	byte _volume_fader_counter;

	int _game_tempo;

	uint _queue_end, _queue_pos, _queue_sound;
	byte _queue_adding;

	SustainingNotes *_sustain_notes_used;
	SustainingNotes *_sustain_notes_free;
	SustainingNotes *_sustain_notes_head;

	byte _queue_marker;
	byte _queue_cleared;
	byte _master_volume;
	byte _music_volume;						/* Global music volume. Percantage */

	uint16 _trigger_count;

	uint16 _channel_volume[8];
	uint16 _channel_volume_eff[8];	/* NoSave */
	uint16 _volchan_table[8];

	Player _players[8];
	SustainingNotes _sustaining_notes[24];
	VolumeFader _volume_fader[8];
	Part _parts[32];

	uint16 _active_notes[128];
	CommandQueue _cmd_queue[64];

	byte *findTag(int sound, char *tag, int index);
	bool isMT32(int sound);
	int get_queue_sound_status(int sound);
	Player *allocate_player(byte priority);
	void handle_marker(uint id, byte data);
	int get_channel_volume(uint a);
	void init_players();
	void init_parts();
	void init_volume_fader();
	void init_sustaining_notes();
	void init_queue();

	void sequencer_timers();
	void expire_sustain_notes();
	void expire_volume_faders();

	Part *allocate_part(byte pri);

	int enqueue_command(int a, int b, int c, int d, int e, int f, int g);
	int enqueue_trigger(int sound, int marker);
	int query_queue(int param);
	Player *get_player_byid(int id);

	int get_volchan_entry(uint a);
	int set_volchan_entry(uint a, uint b);
	int set_channel_volume(uint chan, uint vol);
	void update_volumes();
	void reset_tick();
	VolumeFader *allocate_volume_fader();

	int set_volchan(int sound, int volchan);

	void fix_parts_after_load();
	void fix_players_after_load(Scumm *scumm);

	static int saveReference(IMuseInternal *me, byte type, void *ref);
	static void *loadReference(IMuseInternal *me, byte type, int ref);

	void lock();
	void unlock();

public:
	Part *parts_ptr() {
		return _parts;
	}
	IMuseDriver *driver() {
		return _driver;
	}

	int initialize(OSystem *syst, MidiDriver *midi, SoundMixer *mixer);

	/* Public interface */

	void on_timer();
	void pause(bool paused);
	int terminate();
	int save_or_load(Serializer *ser, Scumm *scumm);
	int set_music_volume(uint vol);
	int get_music_volume();
	int set_master_volume(uint vol);
	int get_master_volume();
	bool start_sound(int sound);
	int stop_sound(int sound);
	int stop_all_sounds();
	int get_sound_status(int sound);
	int32 do_command(int a, int b, int c, int d, int e, int f, int g, int h);
	int clear_queue();
	void setBase(byte **base);

	uint32 property(int prop, uint32 value);

	static IMuseInternal *create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer);
};


/* IMuseAdlib classes */

struct Struct10 {
	byte active;
	int16 cur_val;
	int16 count;
	uint16 param;
	int16 start_value;
	byte loop;
	byte table_a[4];
	byte table_b[4];
	int8 unk3;
	int8 modwheel;
	int8 modwheel_last;
	uint16 speed_lo_max;
	uint16 num_steps;
	int16 speed_hi;
	int8 direction;
	uint16 speed_lo;
	uint16 speed_lo_counter;
};

struct Struct11 {
	int16 modify_val;
	byte param, flag0x40, flag0x10;
	Struct10 *s10;
};

struct InstrumentExtra {
	byte a, b, c, d, e, f, g, h;
};

struct Instrument {
	byte flags_1;
	byte oplvl_1;
	byte atdec_1;
	byte sustrel_1;
	byte waveform_1;
	byte flags_2;
	byte oplvl_2;
	byte atdec_2;
	byte sustrel_2;
	byte waveform_2;
	byte feedback;
	byte flags_a;
	InstrumentExtra extra_a;
	byte flags_b;
	InstrumentExtra extra_b;
	byte duration;
};

struct MidiChannelAdl:MidiChannel {
	MidiChannelAdl *_next, *_prev;
	byte _waitforpedal;
	byte _note;
	byte _channel;
	byte _twochan;
	byte _vol_1, _vol_2;
	int16 _duration;

	Struct10 _s10a;
	Struct11 _s11a;
	Struct10 _s10b;
	Struct11 _s11b;
};

class IMuseAdlib:public IMuseDriver {
private:
	FM_OPL *_opl;
	byte *_adlib_reg_cache;
	IMuseInternal *_se;
	SoundMixer *_mixer;

	int _adlib_timer_counter;

	uint16 channel_table_2[9];
	int _midichan_index;
	int _next_tick;
	uint16 curnote_table[9];
	MidiChannelAdl _midi_channels[9];

	Instrument _part_instr[32];
	Instrument _glob_instr[32];

	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_read_param(int chan, byte data);
	void adlib_setup_channel(int chan, Instrument * instr, byte vol_1, byte vol_2);
	byte adlib_read(byte port) {
		return _adlib_reg_cache[port];
	}
	void adlib_set_param(int channel, byte param, int value);
	void adlib_key_onoff(int channel);
	void adlib_write(byte port, byte value);
	void adlib_playnote(int channel, int note);

	MidiChannelAdl *allocate_midichan(byte pri);

	void reset_tick();
	void mc_off(MidiChannel * mc);

	static void link_mc(Part *part, MidiChannelAdl *mc);
	static void mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11);
	static void mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11, byte flags,
														InstrumentExtra * ie);
	static 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(MidiChannel * mc, byte note, byte velocity);

	static void premix_proc(void *param, int16 *buf, uint len);

public:
	IMuseAdlib(SoundMixer *mixer) {
		_mixer = mixer;
	}
	void uninit();
	void init(IMuseInternal *eng, OSystem *syst);
	void update_pris() {
	}
	void generate_samples(int16 *buf, int len);
	void on_timer();
	void set_instrument(uint slot, byte *instr);
	void part_set_instrument(Part *part, Instrument * instr);
	void part_key_on(Part *part, byte note, byte velocity);
	void part_key_off(Part *part, byte note);
	void part_set_param(Part *part, byte param, int value);
	void part_changed(Part *part, byte what);
	void part_off(Part *part);
	int part_update_active(Part *part, uint16 *active);
	void adjust_priorities() {
	}

	uint32 get_base_tempo() {
#ifdef _WIN32_WCE
		return 0x1F0000 * 2;				// Sampled down to 11 kHz
#else	//_WIN32_WCE
		return 0x1F0000;						// Was: 0x1924E0;
#endif //_WIN32_WCE
	}

	byte get_hardware_type() {
		return 1;
	}
};


/* IMuseGM classes */

class IMuseGM:public IMuseDriver {
	IMuseInternal *_se;
	OSystem *_system;
	MidiDriver *_md;
	MidiChannelGM _midi_channels[9];

	int16 _midi_pitchbend_last[16];
	uint8 _midi_volume_last[16];
	bool _midi_pedal_last[16];
	byte _midi_modwheel_last[16];
	byte _midi_effectlevel_last[16];
	byte _midi_chorus_last[16];
	int8 _midi_pan_last[16];


	void midiPitchBend(byte chan, int16 pitchbend);
	void midiVolume(byte chan, byte volume);
	void midiPedal(byte chan, bool pedal);
	void midiModWheel(byte chan, byte modwheel);
	void midiEffectLevel(byte chan, byte level);
	void midiChorus(byte chan, byte chorus);
	void midiControl0(byte chan, byte value);
	void midiProgram(byte chan, byte program, bool mt32emulate);
	void midiPan(byte chan, int8 pan);
	void midiNoteOn(byte chan, byte note, byte velocity);
	void midiNoteOff(byte chan, byte note);
	void midiSilence(byte chan);
	void midiInit();

public:
	  IMuseGM(MidiDriver *midi) {
		_md = midi;
	} void uninit();
	void init(IMuseInternal *eng, OSystem *os);
	void update_pris();
	void part_off(Part *part);
	int part_update_active(Part *part, uint16 *active);

	void on_timer() {
	}
	void set_instrument(uint slot, byte *instr) {
	}
	void part_set_instrument(Part *part, Instrument * instr) {
	}
	void part_set_param(Part *part, byte param, int value) {
	}
	void part_key_on(Part *part, byte note, byte velocity);
	void part_key_off(Part *part, byte note);
	void part_changed(Part *part, byte what);

	static int midi_driver_thread(void *param);

	uint32 get_base_tempo() {
		return 0x460000;
	}
	byte get_hardware_type() {
		return 5;
	}
};



//*********************************
//**** IMUSE helper functions ****
//*********************************


static int clamp(int val, int min, int max)
{
	if (val < min)
		return min;
	if (val > max)
		return max;
	return val;
}

static int transpose_clamp(int a, int b, int c)
{
	if (b > a)
		a += (b - a + 11) / 12 * 12;
	if (c < a)
		a -= (a - c + 11) / 12 * 12;
	return a;
}

static uint32 get_delta_time(byte **s)
{
	byte *d = *s, b;
	uint32 time = 0;
	do {
		b = *d++;
		time = (time << 7) | (b & 0x7F);
	} while (b & 0x80);
	*s = d;
	return time;
}

static uint read_word(byte *a)
{
	return (a[0] << 8) + a[1];
}

static void skip_midi_cmd(byte **song_ptr)
{
	byte *s, code;

	const byte num_skip[] = {
		2, 2, 2, 2, 1, 1, 2
	};

	s = *song_ptr;

	code = *s++;

	if (code < 0x80) {
		s = NULL;
	} else if (code < 0xF0) {
		s += num_skip[(code & 0x70) >> 4];
	} else {
		if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
			s += get_delta_time(&s);
		} else {
			s = NULL;
		}
	}
	*song_ptr = s;
}

static int is_note_cmd(byte **a, IsNoteCmdData * isnote)
{
	byte *s = *a;
	byte code;

	code = *s++;

	switch (code >> 4) {
	case 8:											/* key off */
		isnote->chan = code & 0xF;
		isnote->note = *s++;
		isnote->vel = *s++;
		*a = s;
		return 1;
	case 9:											/* key on */
		isnote->chan = code & 0xF;
		isnote->note = *s++;
		isnote->vel = *s++;
		*a = s;
		if (isnote->vel)
			return 2;
		return 1;
	case 0xA:
	case 0xB:
	case 0xE:
		s++;
	case 0xC:
	case 0xD:
		s++;
		break;
	case 0xF:
		if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
			s += get_delta_time(&s);
			break;
		}
		return -1;
	default:
		return -1;
	}
	*a = s;
	return 0;
}

/**********************************************************************/

void IMuseInternal::lock()
{
	_locked++;
}

void IMuseInternal::unlock()
{
	_locked--;
}

byte *IMuseInternal::findTag(int sound, char *tag, int index)
{
	byte *ptr = NULL;
	int32 size, pos;

	if (_base_sounds)
		ptr = _base_sounds[sound];

	if (ptr == NULL) {
		//  debug(1, "IMuseInternal::findTag completely failed finding sound %d",
		//        sound);
		return NULL;

	}

	ptr += 8;
	size = READ_BE_UINT32_UNALIGNED(ptr);
	ptr += 4;

	pos = 0;

	while (pos < size) {
		if (!memcmp(ptr + pos, tag, 4) && !index--)
			return ptr + pos + 8;
		pos += READ_BE_UINT32_UNALIGNED(ptr + pos + 4) + 8;
	}

	debug(1, "IMuseInternal::findTag failed finding sound %d", sound);
	return NULL;
}

bool IMuseInternal::isMT32(int sound)
{
	byte *ptr = NULL;
	uint32 tag;

	if (_base_sounds)
		ptr = _base_sounds[sound];

	if (ptr == NULL)
		return false;

	tag = *(((uint32 *)ptr) + 1);
	switch (tag) {
	case MKID('ADL '):
		return false;
	case MKID('ROL '):
		return true;
	case MKID('GMD '):
		return false;
	case MKID('MAC '):
		return true;
	case MKID('SPK '):
		return false;
	}

	return false;
}

bool IMuseInternal::start_sound(int sound)
{
	Player *player;
	void *mdhd;

	mdhd = findTag(sound, MDHD_TAG, 0);
	if (!mdhd) {
		mdhd = findTag(sound, MDPG_TAG, 0);
		if (!mdhd) {
			warning("SE::start_sound failed: Couldn't find %s", MDHD_TAG);
			return false;
		}
	}
	player = allocate_player(128);
	if (!player)
		return false;

	player->clear();
	return player->start_sound(sound);
}


Player *IMuseInternal::allocate_player(byte priority)
{
	Player *player = _players, *best = NULL;
	int i;
	byte bestpri = 255;

	for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
		if (!player->_active)
			return player;
		if (player->_priority < bestpri) {
			best = player;
			bestpri = player->_priority;
		}
	}

	if (bestpri < priority)
		return best;

	debug(1, "Denying player request");
	return NULL;
}

void IMuseInternal::init_players()
{
	Player *player = _players;
	int i;

	for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
		player->_active = false;
		player->_se = this;
	}
}

void IMuseInternal::init_sustaining_notes()
{
	SustainingNotes *next = NULL, *sn = _sustaining_notes;
	int i;

	_sustain_notes_used = NULL;
	_sustain_notes_head = NULL;

	for (i = ARRAYSIZE(_sustaining_notes); i != 0; i--, sn++) {
		sn->next = next;
		next = sn;
	}
	_sustain_notes_free = next;
}

void IMuseInternal::init_volume_fader()
{
	VolumeFader *vf = _volume_fader;
	int i;

	for (i = ARRAYSIZE(_volume_fader); i != 0; i--, vf++)
		vf->initialize();

	_active_volume_faders = false;
}

void IMuseInternal::init_parts()
{
	Part *part;
	int i;

	for (i = 0, part = _parts; i != ARRAYSIZE(_parts); i++, part++) {
		part->init(_driver);
		part->_slot = i;
	}
}

int IMuseInternal::stop_sound(int sound)
{
	Player *player = _players;
	int i;
	int r = -1;

	for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
		if (player->_active && player->_id == sound) {
			player->clear();
			r = 0;
		}
	}
	return r;
}

int IMuseInternal::stop_all_sounds()
{
	Player *player = _players;
	int i;

	for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
		if (player->_active)
			player->clear();
	}
	return 0;
}

void IMuseInternal::on_timer()
{
	if (_locked || _paused)
		return;

	lock();

	sequencer_timers();
	expire_sustain_notes();
	expire_volume_faders();
	_driver->on_timer();

	unlock();
}

void IMuseInternal::sequencer_timers()
{
	Player *player = _players;
	int i;

	for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
		if (player->_active)
			player->sequencer_timer();
	}
}

void IMuseInternal::handle_marker(uint id, byte data)
{
	uint16 *p;
	uint pos;

	pos = _queue_end;
	if (pos == _queue_pos)
		return;

	if (_queue_adding && _queue_sound == id && data == _queue_marker)
		return;

	p = _cmd_queue[pos].array;

	if (p[0] != TRIGGER_ID || p[1] != id || p[2] != data)
		return;

	_trigger_count--;
	_queue_cleared = false;
	do {
		pos = (pos + 1) & (ARRAYSIZE(_cmd_queue) - 1);
		if (_queue_pos == pos)
			break;
		p = _cmd_queue[pos].array;
		if (*p++ != COMMAND_ID)
			break;
		_queue_end = pos;

		do_command(p[0], p[1], p[2], p[3], p[4], p[5], p[6], 0);

		if (_queue_cleared)
			return;
		pos = _queue_end;
	} while (1);

	_queue_end = pos;
}

int IMuseInternal::get_channel_volume(uint a)
{
	if (a < 8)
		return _channel_volume_eff[a];
	return _master_volume;
}

Part *IMuseInternal::allocate_part(byte pri)
{
	Part *part, *best = NULL;
	int i;

	for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
		if (!part->_player)
			return part;
		if (pri >= part->_pri_eff) {
			pri = part->_pri_eff;
			best = part;
		}
	}

	if (best)
		best->uninit();
	else
		debug(1, "Denying part request");
	return best;
}

void IMuseInternal::expire_sustain_notes()
{
	SustainingNotes *sn, *next;
	Player *player;
	uint32 counter;

	for (sn = _sustain_notes_head; sn; sn = next) {
		next = sn->next;
		player = sn->player;

		counter = sn->counter + player->_timer_speed;
		sn->pos += counter >> 16;
		sn->counter = (unsigned short)counter & 0xFFFF;

		if (sn->pos >= sn->off_pos) {
			player->key_off(sn->chan, sn->note);

			/* Unlink the node */
			if (next)
				next->prev = sn->prev;
			if (sn->prev)
				sn->prev->next = next;
			else
				_sustain_notes_head = next;

			/* And put it in the free list */
			sn->next = _sustain_notes_free;
			_sustain_notes_free = sn;
		}
	}
}

void IMuseInternal::expire_volume_faders()
{
	VolumeFader *vf;
	int i;

	if (++_volume_fader_counter & 7)
		return;

	if (!_active_volume_faders)
		return;

	_active_volume_faders = false;
	vf = _volume_fader;
	for (i = ARRAYSIZE(_volume_fader); i != 0; i--, vf++) {
		if (vf->active) {
			_active_volume_faders = true;
			vf->on_timer();
		}
	}
}

void VolumeFader::on_timer()
{
	byte newvol;

	newvol = curvol + speed_hi;
	speed_lo_counter += speed_lo;

	if (speed_lo_counter >= speed_lo_max) {
		speed_lo_counter -= speed_lo_max;
		newvol += direction;
	}

	if (curvol != newvol) {
		if (!newvol) {
			player->clear();
			active = false;
			return;
		}
		curvol = newvol;
		player->set_vol(newvol);
	}

	if (!--num_steps) {
		active = false;
	}
}

int IMuseInternal::get_sound_status(int sound)
{
	int i;
	Player *player;

	for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
		if (player->_active && player->_id == (uint16)sound)
			return 1;
	}
	return get_queue_sound_status(sound);
}

int IMuseInternal::get_queue_sound_status(int sound)
{
	uint16 *a;
	int i, j;

	j = _queue_pos;
	i = _queue_end;

	while (i != j) {
		a = _cmd_queue[i].array;
		if (a[0] == COMMAND_ID && a[1] == 8 && a[2] == (uint16)sound)
			return 2;
		i = (i + 1) & (ARRAYSIZE(_cmd_queue) - 1);
	}
	return 0;
}

int IMuseInternal::set_volchan(int sound, int volchan)
{
	int r;
	int i;
	int num;
	Player *player, *best, *sameid;

	r = get_volchan_entry(volchan);
	if (r == -1)
		return -1;

	if (r >= 8) {
		for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
			if (player->_active && player->_id == (uint16)sound && player->_vol_chan != (uint16)volchan) {
				player->_vol_chan = volchan;
				player->set_vol(player->_volume);
				return 0;
			}
		}
		return -1;
	} else {
		best = NULL;
		num = 0;
		sameid = NULL;
		for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
			if (player->_active) {
				if (player->_vol_chan == (uint16)volchan) {
					num++;
					if (!best || player->_priority <= best->_priority)
						best = player;
				} else if (player->_id == (uint16)sound) {
					sameid = player;
				}
			}
		}
		if (sameid == NULL)
			return -1;
		if (num >= r)
			best->clear();
		player->_vol_chan = volchan;
		player->set_vol(player->_volume);
		return 0;
	}
}

int IMuseInternal::clear_queue()
{
	_queue_adding = false;
	_queue_cleared = true;
	_queue_pos = 0;
	_queue_end = 0;
	_trigger_count = 0;
	return 0;
}

int IMuseInternal::enqueue_command(int a, int b, int c, int d, int e, int f, int g)
{
	uint16 *p;
	uint i;

	i = _queue_pos;

	if (i == _queue_end)
		return -1;

	if (a == -1) {
		_queue_adding = false;
		_trigger_count++;
		return 0;
	}

	p = _cmd_queue[_queue_pos].array;
	p[0] = COMMAND_ID;
	p[1] = a;
	p[2] = b;
	p[3] = c;
	p[4] = d;
	p[5] = e;
	p[6] = f;
	p[7] = g;

	i = (i + 1) & (ARRAYSIZE(_cmd_queue) - 1);

	if (_queue_end != i) {
		_queue_pos = i;
		return 0;
	} else {
		_queue_pos = (i - 1) & (ARRAYSIZE(_cmd_queue) - 1);
		return -1;
	}
}

int IMuseInternal::query_queue(int param)
{
	switch (param) {
	case 0:											/* get trigger count */
		return _trigger_count;
	case 1:											/* get trigger type */
		if (_queue_end == _queue_pos)
			return -1;
		return _cmd_queue[_queue_end].array[1];
	case 2:											/* get trigger sound */
		if (_queue_end == _queue_pos)
			return 0xFF;
		return _cmd_queue[_queue_end].array[2];
	default:
		return -1;
	}
}

int IMuseInternal::get_music_volume()
{
	return _music_volume;
}

int IMuseInternal::set_music_volume(uint vol)
{
	if (vol > 100)
		vol = 100;

	if (vol < 1)
		vol = 1;

	_music_volume = vol;
	return 0;
}

int IMuseInternal::set_master_volume(uint vol)
{
	int i;
	if (vol > 127)
		return -1;

	if (_music_volume > 0)
		vol = vol / (100 / _music_volume);

	_master_volume = vol;
//  _s->_sound_volume_master = vol;
	for (i = 0; i != 8; i++)
		_channel_volume_eff[i] = (_channel_volume[i] + 1) * vol >> 7;
	update_volumes();
	return 0;
}

int IMuseInternal::get_master_volume()
{
	return _master_volume;
}

int IMuseInternal::terminate()
{
	return 0;
	/* not implemented */
}


int IMuseInternal::enqueue_trigger(int sound, int marker)
{
	uint16 *p;
	uint pos;

	pos = _queue_pos;

	p = _cmd_queue[pos].array;
	p[0] = TRIGGER_ID;
	p[1] = sound;
	p[2] = marker;

	pos = (pos + 1) & (ARRAYSIZE(_cmd_queue) - 1);
	if (_queue_end == pos) {
		_queue_pos = (pos - 1) & (ARRAYSIZE(_cmd_queue) - 1);
		return -1;
	}

	_queue_pos = pos;
	_queue_adding = true;
	_queue_sound = sound;
	_queue_marker = marker;
	return 0;
}

int32 IMuseInternal::do_command(int a, int b, int c, int d, int e, int f, int g, int h)
{
	byte cmd = a & 0xFF;
	byte param = a >> 8;
	Player *player = NULL;

	if (!_initialized && (cmd || param))
		return -1;

	if (param == 0) {
		switch (cmd) {
		case 6:
			return set_master_volume(b);
		case 7:
			return get_master_volume();
		case 8:
			return start_sound(b) ? 0 : -1;
		case 9:
			return stop_sound(b);
		case 10:
			return stop_all_sounds();	//FIXME: Sam and Max unknown
		case 11:
			return stop_all_sounds();
		case 13:
			return get_sound_status(b);
		case 14:{									// FIXME: Sam and Max command - Total guess
				int i;									//      Seems to work as a volume fader
				Player *player;

				for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
					if (player->_active && player->_id == (uint16)b) {
						player->fade_vol(e, f);
						return 0;
					}
				}
			}
		case 15:{									// FIXME: Sam and Max command - Total guess
				int i;									//      Something to do with position?
				Player *player;
				for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
					if (player->_active && player->_id == (uint16)b) {
						player->jump(player->_track_index + 1, 0, 0);
						return 0;
					}
				}
			}

		case 16:
			return set_volchan(b, c);
		case 17:
			return set_channel_volume(b, c);
		case 18:
			return set_volchan_entry(b, c);
		case 19:
			return stop_sound(b);			//FIXME: Sam and Max unknown
		case 20:
			return stop_sound(b);			//FIXME: Sam and Max unknown
		case 2:
		case 3:
			return 0;
		default:
			warning("IMuseInternal::do_command invalid command %d", cmd);
		}
	} else if (param == 1) {

		if ((1 << cmd) & (0x783FFF)) {
			player = get_player_byid(b);
			if (!player)
				return -1;
			if ((1 << cmd) & (1 << 11 | 1 << 22)) {
				assert(c >= 0 && c <= 15);
				player = (Player *)player->get_part(c);
				if (!player)
					return -1;
			}
		}

		switch (cmd) {
		case 0:
			return player->get_param(c, d);
		case 1:
			player->set_priority(c);
			return 0;
		case 2:
			return player->set_vol(c);
		case 3:
			player->set_pan(c);
			return 0;
		case 4:
			return player->set_transpose(c, d);
		case 5:
			player->set_detune(c);
			return 0;
		case 6:
			player->set_speed(c);
			return 0;
		case 7:
			return player->jump(c, d, e) ? 0 : -1;
		case 8:
			return player->scan(c, d, e);
		case 9:
			return player->set_loop(c, d, e, f, g) ? 0 : -1;
		case 10:
			player->clear_loop();
			return 0;
		case 11:
			((Part *)player)->set_onoff(d != 0);
			return 0;
		case 12:
			return player->_hook.set(c, d, e);
		case 13:
			return player->fade_vol(c, d);
		case 14:
			return enqueue_trigger(b, c);
		case 15:
			return enqueue_command(b, c, d, e, f, g, h);
		case 16:
			return clear_queue();
		case 19:
			return player->get_param(c, d);
		case 20:
			return player->_hook.set(c, d, e);
		case 21:
			return -1;
		case 22:
			((Part *)player)->set_vol(d);
			return 0;
		case 23:
			return query_queue(b);
		case 24:
			return 0;
		default:
			warning("IMuseInternal::do_command default midi command %d", cmd);
			return -1;
		}
	}

	return -1;
}

int IMuseInternal::set_channel_volume(uint chan, uint vol)
{
	if (chan >= 8 || vol > 127)
		return -1;

	_channel_volume[chan] = vol;
	_channel_volume_eff[chan] = _master_volume * (vol + 1) >> 7;
	update_volumes();
	return 0;
}

void IMuseInternal::update_volumes()
{
	Player *player;
	int i;

	for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
		if (player->_active)
			player->set_vol(player->_volume);
	}
}

int IMuseInternal::set_volchan_entry(uint a, uint b)
{
	if (a >= 8)
		return -1;
	_volchan_table[a] = b;
	return 0;
}

int HookDatas::query_param(int param, byte chan)
{
	switch (param) {
	case 18:
		return _jump;
	case 19:
		return _transpose;
	case 20:
		return _part_onoff[chan];
	case 21:
		return _part_volume[chan];
	case 22:
		return _part_program[chan];
	case 23:
		return _part_transpose[chan];
	default:
		return -1;
	}
}

int HookDatas::set(byte cls, byte value, byte chan)
{
	switch (cls) {
	case 0:
		_jump = value;
		break;
	case 1:
		_transpose = value;
		break;
	case 2:
		if (chan < 16)
			_part_onoff[chan] = value;
		else if (chan == 16)
			memset(_part_onoff, value, 16);
		break;
	case 3:
		if (chan < 16)
			_part_volume[chan] = value;
		else if (chan == 16)
			memset(_part_volume, value, 16);
		break;
	case 4:
		if (chan < 16)
			_part_program[chan] = value;
		else if (chan == 16)
			memset(_part_program, value, 16);
		break;
	case 5:
		if (chan < 16)
			_part_transpose[chan] = value;
		else if (chan == 16)
			memset(_part_transpose, value, 16);
		break;
	default:
		return -1;
	}
	return 0;
}


VolumeFader *IMuseInternal::allocate_volume_fader()
{
	VolumeFader *vf;
	int i;

	vf = _volume_fader;
	for (i = ARRAYSIZE(_volume_fader); vf->active;) {
		vf++;
		if (!--i)
			return NULL;
	}

	vf->active = true;
	_active_volume_faders = true;
	return vf;
}

Player *IMuseInternal::get_player_byid(int id)
{
	int i;
	Player *player, *found = NULL;

	for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
		if (player->_active && player->_id == (uint16)id) {
			if (found)
				return NULL;
			found = player;
		}
	}
	return found;
}

int IMuseInternal::get_volchan_entry(uint a)
{
	if (a < 8)
		return _volchan_table[a];
	return -1;
}

uint32 IMuseInternal::property(int prop, uint32 value)
{
	switch (prop) {
	case IMuse::PROP_TEMPO_BASE:
		_game_tempo = value;
		break;
	}
	return 0;
}

void IMuseInternal::setBase(byte **base)
{
	_base_sounds = base;
}


IMuseInternal *IMuseInternal::create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer)
{
	IMuseInternal *i = new IMuseInternal;
	i->initialize(syst, midi, mixer);
	return i;
}


int IMuseInternal::initialize(OSystem *syst, MidiDriver *midi, SoundMixer *mixer)
{
	int i;

	IMuseDriver *driv;

	if (midi == NULL) {
		driv = new IMuseAdlib(mixer);
	} else {
		driv = new IMuseGM(midi);
	}

	_driver = driv;
	_hardware_type = driv->get_hardware_type();
	_game_tempo = driv->get_base_tempo();

	driv->init(this, syst);

	_master_volume = 127;
	if (_music_volume < 1)
		_music_volume = 60;

	for (i = 0; i != 8; i++)
		_channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127;

	init_players();
	init_sustaining_notes();
	init_volume_fader();
	init_queue();
	init_parts();

	_initialized = true;

	return 0;
}

void IMuseInternal::init_queue()
{
	_queue_adding = false;
	_queue_pos = 0;
	_queue_end = 0;
	_trigger_count = 0;
}

void IMuseInternal::pause(bool paused)
{
	lock();

	int i;
	Part *part;

	for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
		if (part->_player) {
			if (paused) {
				part->_vol_eff = 0;
			} else {
				part->set_vol(part->_vol);
			}
			part->changed(IMuseDriver::pcVolume);
		}
	}

	_paused = paused;

	unlock();
}


/*************************************************************************/

int Player::fade_vol(byte vol, int time)
{
	VolumeFader *vf;
	int i;

	cancel_volume_fade();
	if (time == 0) {
		set_vol(vol);
		return 0;
	}

	vf = _se->allocate_volume_fader();
	if (vf == NULL)
		return -1;

	vf->player = this;
	vf->num_steps = vf->speed_lo_max = time;
	vf->curvol = _volume;
	i = (vol - vf->curvol);
	vf->speed_hi = i / time;
	if (i < 0) {
		i = -i;
		vf->direction = -1;
	} else {
		vf->direction = 1;
	}
	vf->speed_lo = i % time;
	vf->speed_lo_counter = 0;
	return 0;
}

void Player::clear()
{
	uninit_seq();
	cancel_volume_fade();
	uninit_parts();
	_active = false;
	_ticks_per_beat = TICKS_PER_BEAT;
}

bool Player::start_sound(int sound)
{
	void *mdhd;

	mdhd = _se->findTag(sound, MDHD_TAG, 0);
	if (mdhd == NULL) {
		mdhd = _se->findTag(sound, MDPG_TAG, 0);
		if (mdhd == NULL) {
			warning("P::start_sound failed: Couldn't find %s", MDHD_TAG);
			return false;
		}
	}
	_mt32emulate = _se->isMT32(sound);
	_parts = NULL;
	_active = true;
	_id = sound;
	_priority = 0x80;
	_volume = 0x7F;
	_vol_chan = 0xFFFF;

	_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;

	_pan = 0;
	_transpose = 0;
	_detune = 0;

	hook_clear();
	if (start_seq_sound(sound) != 0) {
		_active = false;
		return false;
	}
	return true;
}

void Player::hook_clear()
{
	memset(&_hook, 0, sizeof(_hook));
}

int Player::start_seq_sound(int sound)
{
	byte *ptr, *track_ptr;

	_song_index = sound;
	_timer_counter = 0;
	_loop_to_beat = 1;
	_loop_from_beat = 1;
	_track_index = 0;
	_loop_counter = 0;
	_loop_to_tick = 0;
	_loop_from_tick = 0;

	set_tempo(500000);
	set_speed(128);
	ptr = _se->findTag(sound, "MTrk", _track_index);
	if (ptr == NULL)
		return -1;

	track_ptr = ptr;
	_cur_pos = _next_pos = get_delta_time(&track_ptr);
	_song_offset = track_ptr - ptr;

	_tick_index = _cur_pos;
	_beat_index = 1;

	if (_tick_index >= _ticks_per_beat) {
		_beat_index += _tick_index / _ticks_per_beat;
		_tick_index %= _ticks_per_beat;
	}

	return 0;
}

void Player::set_tempo(uint32 b)
{
	uint32 i, j;

	i = _se->_game_tempo;

	j = _tempo = b;

	while (i & 0xFFFF0000 || j & 0xFFFF0000) {
		i >>= 1;
		j >>= 1;
	}

	_tempo_eff = (i << 16) / j;

	set_speed(_speed);
}

void Player::cancel_volume_fade()
{
	VolumeFader *vf = _se->_volume_fader;
	int i;

	for (i = 0; i < 8; i++, vf++) {
		if (vf->active && vf->player == this)
			vf->active = false;
	}
}

void Player::uninit_parts()
{
	if (_parts && _parts->_player != this)
		error("asd");
	while (_parts)
		_parts->uninit();
}

void Player::uninit_seq()
{
	_abort = true;
}

void Player::set_speed(byte speed)
{
	_speed = speed;
	_timer_speed = (_tempo_eff * speed >> 7);
}

byte *Player::parse_midi(byte *s)
{
	byte cmd, chan, note, velocity, control;
	uint value;
	Part *part;

	cmd = *s++;

	chan = cmd & 0xF;

	switch (cmd >> 4) {
	case 0x8:										/* key off */
		note = *s++;
		if (!_scanning) {
			key_off(chan, note);
		} else {
			clear_active_note(chan, note);
		}
		s++;												/* skip velocity */
		break;

	case 0x9:										/* key on */
		note = *s++;
		velocity = *s++;
		if (velocity) {
			if (!_scanning)
				key_on(chan, note, velocity);
			else
				set_active_note(chan, note);
		} else {
			if (!_scanning)
				key_off(chan, note);
			else
				clear_active_note(chan, note);
		}
		break;

	case 0xA:										/* aftertouch */
		s += 2;
		break;

	case 0xB:										/* control change */
		control = *s++;
		value = *s++;
		part = get_part(chan);
		if (!part)
			break;

		switch (control) {
		case 1:										/* modulation wheel */
			part->set_modwheel(value);
			break;
		case 7:										/* volume */
			part->set_vol(value);
			break;
		case 10:										/* pan position */
			part->set_pan(value - 0x40);
			break;
		case 16:										/* pitchbend factor */
			part->set_pitchbend_factor(value);
			break;
		case 17:										/* gp slider 2 */
			part->set_detune(value - 0x40);
			break;
		case 18:										/* gp slider 3 */
			part->set_pri(value - 0x40);
			_se->_driver->update_pris();
			break;
		case 64:										/* hold pedal */
			part->set_pedal(value != 0);
			break;
		case 91:										/* effects level */
			part->set_effect_level(value);
			break;
		case 93:										/* chorus */
			part->set_chorus(value);
			break;
		default:
			warning("parse_midi: invalid control %d", control);
		}
		break;

	case 0xC:										/* program change */
		value = *s++;
		part = get_part(chan);
		if (part)
			part->set_program(value);
		break;

	case 0xD:										/* channel pressure */
		s++;
		break;

	case 0xE:										/* pitch bend */
		part = get_part(chan);
		if (part)
			part->set_pitchbend(((s[1] - 0x40) << 7) | s[0]);
		s += 2;
		break;

	case 0xF:
		if (chan == 0) {
			uint size = get_delta_time(&s);
			if (*s == SYSEX_ID)
				parse_sysex(s, size);
			s += size;
		} else if (chan == 0xF) {
			cmd = *s++;
			if (cmd == 47)
				goto Error;							/* end of song */
			if (cmd == 81) {
				set_tempo((s[1] << 16) | (s[2] << 8) | s[3]);
				s += 4;
				break;
			}
			s += get_delta_time(&s);
		} else if (chan == 0x7) {
			s += get_delta_time(&s);
		} else {
			goto Error;
		}
		break;

	default:
	Error:;
		if (!_scanning)
			clear();
		return NULL;
	}
	return s;
}

void Player::parse_sysex(byte *p, uint len)
{
	byte code;
	byte a;
	uint b;
	byte buf[128];
	Part *part;

	/* too big? */
	if (len >= sizeof(buf) * 2)
		return;

	/* skip sysex manufacturer */
	p++;
	len -= 2;

	switch (code = *p++) {
	case 16:											/* set instrument in part */
		a = *p++ & 0x0F;
		if (_se->_hardware_type != *p++)
			break;
		decode_sysex_bytes(p, buf, len - 3);
		part = get_part(a);
		if (part)
			part->set_instrument((Instrument *) buf);
		break;

	case 17:											/* set global instrument */
		p++;
		if (_se->_hardware_type != *p++)
			break;
		a = *p++;
		decode_sysex_bytes(p, buf, len - 4);
		_se->_driver->set_instrument(a, buf);
		break;

	case 33:											/* param adjust */
		a = *p++ & 0x0F;
		if (_se->_hardware_type != *p++)
			break;
		decode_sysex_bytes(p, buf, len - 3);
		part = get_part(a);
		if (part)
			part->set_param(read_word(buf), read_word(buf + 2));
		break;

	case 48:											/* hook - jump */
		if (_scanning)
			break;
		decode_sysex_bytes(p + 1, buf, len - 2);
		maybe_jump(buf);
		break;

	case 49:											/* hook - global transpose */
		decode_sysex_bytes(p + 1, buf, len - 2);
		maybe_set_transpose(buf);
		break;

	case 50:											/* hook - part on/off */
		buf[0] = *p++ & 0x0F;
		decode_sysex_bytes(p, buf + 1, len - 2);
		maybe_part_onoff(buf);
		break;

	case 51:											/* hook - set volume */
		buf[0] = *p++ & 0x0F;
		decode_sysex_bytes(p, buf + 1, len - 2);
		maybe_set_volume(buf);
		break;

	case 52:											/* hook - set program */
		buf[0] = *p++ & 0x0F;
		decode_sysex_bytes(p, buf + 1, len - 2);
		maybe_set_program(buf);
		break;

	case 53:											/* hook - set transpose */
		buf[0] = *p++ & 0x0F;
		decode_sysex_bytes(p, buf + 1, len - 2);
		maybe_set_transpose_part(buf);
		break;

	case 64:											/* marker */
		p++;
		len -= 2;
		while (len--) {
			_se->handle_marker(_id, *p++);
		}
		break;

	case 80:											/* loop */
		decode_sysex_bytes(p + 1, buf, len - 2);
		set_loop(read_word(buf),
						 read_word(buf + 2), read_word(buf + 4), read_word(buf + 6), read_word(buf + 8)
			);
		break;

	case 81:											/* end loop */
		clear_loop();
		break;

	case 96:											/* set instrument */
		part = get_part(p[0] & 0x0F);
		b = (p[1] & 0x0F) << 12 | (p[2] & 0x0F) << 8 | (p[4] & 0x0F) << 4 | (p[4] & 0x0F);
		if (part)
			part->set_instrument(b);
		break;

	default:
		debug(6, "unknown sysex %d", code);
	}
}

void Player::decode_sysex_bytes(byte *src, byte *dst, int len)
{
	while (len >= 0) {
		*dst++ = (src[0] << 4) | (src[1] & 0xF);
		src += 2;
		len -= 2;
	}
}

void Player::maybe_jump(byte *data)
{
	byte cmd;

	cmd = data[0];

	/* is this the hook i'm waiting for? */
	if (cmd && _hook._jump != cmd)
		return;

	/* reset hook? */
	if (cmd != 0 && cmd < 0x80)
		_hook._jump = 0;

	jump(read_word(data + 1), read_word(data + 3), read_word(data + 5));
}

void Player::maybe_set_transpose(byte *data)
{
	byte cmd;

	cmd = data[0];

	/* is this the hook i'm waiting for? */
	if (cmd && _hook._transpose != cmd)
		return;

	/* reset hook? */
	if (cmd != 0 && cmd < 0x80)
		_hook._transpose = 0;

	set_transpose(data[1], (int8)data[2]);
}

void Player::maybe_part_onoff(byte *data)
{
	byte cmd, *p;
	uint chan;
	Part *part;

	cmd = data[1];
	chan = data[0];

	p = &_hook._part_onoff[chan];

	/* is this the hook i'm waiting for? */
	if (cmd && *p != cmd)
		return;

	if (cmd != 0 && cmd < 0x80)
		*p = 0;

	part = get_part(chan);
	if (part)
		part->set_onoff(data[2] != 0);
}

void Player::maybe_set_volume(byte *data)
{
	byte cmd;
	byte *p;
	uint chan;
	Part *part;

	cmd = data[1];
	chan = data[0];

	p = &_hook._part_volume[chan];

	/* is this the hook i'm waiting for? */
	if (cmd && *p != cmd)
		return;

	/* reset hook? */
	if (cmd != 0 && cmd < 0x80)
		*p = 0;

	part = get_part(chan);
	if (part)
		part->set_vol(data[2]);
}

void Player::maybe_set_program(byte *data)
{
	byte cmd;
	byte *p;
	uint chan;
	Part *part;

	cmd = data[1];
	chan = data[0];

	/* is this the hook i'm waiting for? */
	p = &_hook._part_program[chan];

	if (cmd && *p != cmd)
		return;

	if (cmd != 0 && cmd < 0x80)
		*p = 0;

	part = get_part(chan);
	if (part)
		part->set_program(data[2]);
}

void Player::maybe_set_transpose_part(byte *data)
{
	byte cmd;
	byte *p;
	uint chan;

	cmd = data[1];
	chan = data[0];

	/* is this the hook i'm waiting for? */
	p = &_hook._part_transpose[chan];

	if (cmd && *p != cmd)
		return;

	/* reset hook? */
	if (cmd != 0 && cmd < 0x80)
		*p = 0;

	part_set_transpose(chan, data[2], (int8)data[3]);
}

int Player::set_transpose(byte relative, int b)
{
	Part *part;

	if (b > 24 || b < -24 || relative > 1)
		return -1;
	if (relative)
		b = transpose_clamp(_transpose + b, -7, 7);

	_transpose = b;

	for (part = _parts; part; part = part->_next) {
		part->set_transpose(part->_transpose);
	}

	return 0;
}

void Player::clear_active_notes()
{
	memset(_se->_active_notes, 0, sizeof(_se->_active_notes));
}

void Player::clear_active_note(int chan, byte note)
{
	_se->_active_notes[note] &= ~(1 << chan);
}

void Player::set_active_note(int chan, byte note)
{
	_se->_active_notes[note] |= (1 << chan);
}

void Player::part_set_transpose(uint8 chan, byte relative, int8 b)
{
	Part *part;

	if (b > 24 || b < -24)
		return;

	part = get_part(chan);
	if (!part)
		return;
	if (relative)
		b = transpose_clamp(b + part->_transpose, -7, 7);
	part->set_transpose(b);
}

void Player::key_on(uint8 chan, uint8 note, uint8 velocity)
{
	Part *part;

	part = get_part(chan);
	if (!part || !part->_on)
		return;

	part->key_on(note, velocity);
}

void Player::key_off(uint8 chan, uint8 note)
{
	Part *part;

	for (part = _parts; part; part = part->_next) {
		if (part->_chan == (byte)chan && part->_on)
			part->key_off(note);
	}
}

bool Player::jump(uint track, uint beat, uint tick)
{
	byte *mtrk, *cur_mtrk, *scanpos;
	uint32 topos, curpos, track_offs;

	if (!_active)
		return false;

	mtrk = _se->findTag(_song_index, "MTrk", track);
	if (!mtrk)
		return false;

	cur_mtrk = _se->findTag(_song_index, "MTrk", _track_index);
	if (!cur_mtrk)
		return false;

	_se->lock();

	if (beat == 0)
		beat = 1;

	topos = (beat - 1) * _ticks_per_beat + tick;

	if (track == _track_index && topos >= _cur_pos) {
		scanpos = _song_offset + mtrk;
		curpos = _next_pos;
	} else {
		scanpos = mtrk;
		curpos = get_delta_time(&scanpos);
	}

	while (curpos < topos) {
		skip_midi_cmd(&scanpos);
		if (!scanpos) {
			_se->unlock();
			return false;
		}
		curpos += get_delta_time(&scanpos);
	}

	track_offs = scanpos - mtrk;

	turn_off_pedals();

	find_sustaining_notes(cur_mtrk + _song_offset, mtrk + track_offs, curpos - topos);

	_beat_index = beat;
	_tick_index = tick;
	_cur_pos = topos;
	_next_pos = curpos;
	_timer_counter = 0;
	_song_offset = track_offs;
	if (track != _track_index) {
		_track_index = track;
		_loop_counter = 0;
	}
	_abort = true;
	_se->unlock();
	return true;
}

bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick)
{
	if (tobeat + 1 >= frombeat)
		return false;

	if (tobeat == 0)
		tobeat = 1;

	_loop_counter = 0;						/* because of possible interrupts */
	_loop_to_beat = tobeat;
	_loop_to_tick = totick;
	_loop_from_beat = frombeat;
	_loop_from_tick = fromtick;
	_loop_counter = count;

	return true;
}

void Player::clear_loop()
{
	_loop_counter = 0;
}

void Player::turn_off_pedals()
{
	Part *part;

	for (part = _parts; part; part = part->_next) {
		if (part->_pedal)
			part->set_pedal(false);
	}
}

void Player::find_sustaining_notes(byte *a, byte *b, uint32 l)
{
	uint32 pos;
	uint16 mask;
	uint16 *bitlist_ptr;
	SustainingNotes *sn, *next;
	IsNoteCmdData isnote;
	int j;
	uint num_active;
	uint max_off_pos;

	num_active = update_actives();

	/* pos contains number of ticks since current position */
	pos = _next_pos - _cur_pos;
	if ((int32)pos < 0)
		pos = 0;

	/* locate the positions where the notes are turned off.
	 * remember each note that was turned off
	 */
	while (num_active != 0) {
		/* is note off? */
		j = is_note_cmd(&a, &isnote);
		if (j == -1)
			break;
		if (j == 1) {
			mask = 1 << isnote.chan;
			bitlist_ptr = _se->_active_notes + isnote.note;
			if (*bitlist_ptr & mask) {
				*bitlist_ptr &= ~mask;
				num_active--;
				/* Get a node from the free list */
				if ((sn = _se->_sustain_notes_free) == NULL)
					return;
				_se->_sustain_notes_free = sn->next;

				/* Insert it in the beginning of the used list */
				sn->next = _se->_sustain_notes_used;
				_se->_sustain_notes_used = sn;
				sn->prev = NULL;
				if (sn->next)
					sn->next->prev = sn;

				sn->note = isnote.note;
				sn->chan = isnote.chan;
				sn->player = this;
				sn->off_pos = pos;
				sn->pos = 0;
				sn->counter = 0;
			}
		}
		pos += get_delta_time(&a);
	}

	/* find the maximum position where a note was turned off */
	max_off_pos = 0;
	for (sn = _se->_sustain_notes_used; sn; sn = sn->next) {
		_se->_active_notes[sn->note] |= (1 << sn->chan);
		if (sn->off_pos > max_off_pos) {
			max_off_pos = sn->off_pos;
		}
	}

	/* locate positions where notes are turned on */
	pos = l;
	while (pos < max_off_pos) {
		j = is_note_cmd(&b, &isnote);
		if (j == -1)
			break;
		if (j == 2) {
			mask = 1 << isnote.chan;
			bitlist_ptr = _se->_active_notes + isnote.note;

			if (*bitlist_ptr & mask) {
				sn = _se->_sustain_notes_used;
				while (sn) {
					next = sn->next;
					if (sn->note == isnote.note && sn->chan == isnote.chan && pos < sn->off_pos) {
						*bitlist_ptr &= ~mask;
						/* Unlink from the sustain list */
						if (next)
							next->prev = sn->prev;
						if (sn->prev)
							sn->prev->next = next;
						else
							_se->_sustain_notes_used = next;
						/* Insert into the free list */
						sn->next = _se->_sustain_notes_free;
						_se->_sustain_notes_free = sn;
					}
					sn = next;
				}
			}
		}
		pos += get_delta_time(&b);
	}

	/* Concatenate head and used list */
	if (!_se->_sustain_notes_head) {
		_se->_sustain_notes_head = _se->_sustain_notes_used;
		_se->_sustain_notes_used = NULL;
		return;
	}
	sn = _se->_sustain_notes_head;
	while (sn->next)
		sn = sn->next;
	sn->next = _se->_sustain_notes_used;
	_se->_sustain_notes_used = NULL;
	if (sn->next)
		sn->next->prev = sn;
}

Part *Player::get_part(uint8 chan)
{
	Part *part;

	part = _parts;
	while (part) {
		if (part->_chan == chan)
			return part;
		part = part->_next;
	}

	part = _se->allocate_part(_priority);
	if (!part) {
		warning("no parts available");
		return NULL;
	}

	part->_chan = chan;
	part->setup(this);

	return part;
}

uint Player::update_actives()
{
	Part *part;
	uint16 *active;
	int count = 0;

	clear_active_notes();
	active = _se->_active_notes;
	for (part = _parts; part; part = part->_next) {
		if (part->_mc)
			count += part->update_actives(active);
	}
	return count;
}

void Player::set_priority(int pri)
{
	Part *part;

	_priority = pri;
	for (part = _parts; part; part = part->_next) {
		part->set_pri(part->_pri);
	}
	_se->_driver->update_pris();
}

void Player::set_pan(int pan)
{
	Part *part;

	_pan = pan;
	for (part = _parts; part; part = part->_next) {
		part->set_pan(part->_pan);
	}
}

void Player::set_detune(int detune)
{
	Part *part;

	_detune = detune;
	for (part = _parts; part; part = part->_next) {
		part->set_detune(part->_detune);
	}
}

int Player::scan(uint totrack, uint tobeat, uint totick)
{
	byte *mtrk, *scanptr;
	uint32 curpos, topos;
	uint32 pos;

	assert(totrack >= 0 && tobeat >= 0 && totick >= 0);

	if (!_active)
		return -1;

	mtrk = _se->findTag(_song_index, "MTrk", totrack);
	if (!mtrk)
		return -1;

	_se->lock();
	if (tobeat == 0)
		tobeat++;

	turn_off_parts();
	clear_active_notes();
	scanptr = mtrk;
	curpos = get_delta_time(&scanptr);
	_scanning = true;

	topos = (tobeat - 1) * _ticks_per_beat + totick;

	while (curpos < topos) {
		scanptr = parse_midi(scanptr);
		if (!scanptr) {
			_scanning = false;
			_se->unlock();
			return -1;
		}
		curpos += get_delta_time(&scanptr);
	}
	pos = scanptr - mtrk;

	_scanning = false;
	_se->driver()->update_pris();
	play_active_notes();
	_beat_index = tobeat;
	_tick_index = totick;
	_cur_pos = topos;
	_next_pos = curpos;
	_timer_counter = 0;
	_song_offset = pos;
	if (_track_index != totrack) {
		_track_index = totrack;
		_loop_counter = 0;
	}
	_se->unlock();
	return 0;
}

void Player::turn_off_parts()
{
	Part *part;

	for (part = _parts; part; part = part->_next)
		part->off();
}

void Player::play_active_notes()
{
	int i, j;
	uint mask;

	for (i = 0; i != 128; i++) {
		mask = _se->_active_notes[i];
		for (j = 0; j != 16; j++, mask >>= 1) {
			if (mask & 1) {
				key_on(j, i, 80);
			}
		}
	}
}

int Player::set_vol(byte vol)
{
	Part *part;

	if (vol > 127)
		return -1;

	_volume = vol;
	_vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7;

	for (part = _parts; part; part = part->_next) {
		part->set_vol(part->_vol);
	}

	return 0;
}

int Player::get_param(int param, byte chan)
{
	switch (param) {
	case 0:
		return (byte)_priority;
	case 1:
		return (byte)_volume;
	case 2:
		return (byte)_pan;
	case 3:
		return (byte)_transpose;
	case 4:
		return (byte)_detune;
	case 5:
		return _speed;
	case 6:
		return _track_index;
	case 7:
		return _beat_index;
	case 8:
		return _tick_index;
	case 9:
		return _loop_counter;
	case 10:
		return _loop_to_beat;
	case 11:
		return _loop_to_tick;
	case 12:
		return _loop_from_beat;
	case 13:
		return _loop_from_tick;
	case 14:
	case 15:
	case 16:
	case 17:
		return query_part_param(param, chan);
	case 18:
	case 19:
	case 20:
	case 21:
	case 22:
	case 23:
		return _hook.query_param(param, chan);
	default:
		return -1;
	}
}

int Player::query_part_param(int param, byte chan)
{
	Part *part;

	part = _parts;
	while (part) {
		if (part->_chan == chan) {
			switch (param) {
			case 14:
				return part->_on;
			case 15:
				return part->_vol;
			case 16:
				return part->_program;
			case 17:
				return part->_transpose;
			default:
				return -1;
			}
		}
		part = part->_next;
	}
	return 129;
}

void Player::sequencer_timer()
{
	byte *mtrk;
	uint32 counter;
	byte *song_ptr;

	counter = _timer_counter + _timer_speed;
	_timer_counter = counter & 0xFFFF;
	_cur_pos += counter >> 16;
	_tick_index += counter >> 16;

	if (_tick_index >= _ticks_per_beat) {
		_beat_index += _tick_index / _ticks_per_beat;
		_tick_index %= _ticks_per_beat;
	}
	if (_loop_counter && _beat_index >= _loop_from_beat && _tick_index >= _loop_from_tick) {
		_loop_counter--;
		jump(_track_index, _loop_to_beat, _loop_to_tick);
	}
	if (_next_pos <= _cur_pos) {
		mtrk = _se->findTag(_song_index, "MTrk", _track_index);
		if (!mtrk) {
			warning("Sound %d was unloaded while active", _song_index);
			clear();
		} else {
			song_ptr = mtrk + _song_offset;
			_abort = false;

			while (_next_pos <= _cur_pos) {
				song_ptr = parse_midi(song_ptr);
				if (!song_ptr || _abort)
					return;
				_next_pos += get_delta_time(&song_ptr);
			}
			_song_offset = song_ptr - mtrk;
		}
	}
}

/*******************************************************************/

#define OFFS(type,item) ((int)(&((type*)0)->item))
#define SIZE(type,item) sizeof(((type*)0)->item)
#define MKLINE(type,item,saveas) {OFFS(type,item),saveas,SIZE(type,item)}
#define MKARRAY(type,item,saveas,num) {OFFS(type,item),128|saveas,SIZE(type,item)}, {num,0,0}
#define MKEND() {0xFFFF,0xFF,0xFF}

#define MKREF(type,item,refid) {OFFS(type,item),refid,0xFF}

enum {
	TYPE_PART = 1,
	TYPE_PLAYER = 2,
};

int IMuseInternal::saveReference(IMuseInternal *me, byte type, void *ref)
{
	switch (type) {
	case TYPE_PART:
		return (Part *)ref - me->_parts;
	case TYPE_PLAYER:
		return (Player *)ref - me->_players;
	default:
		error("saveReference: invalid type");
	}
}

void *IMuseInternal::loadReference(IMuseInternal *me, byte type, int ref)
{
	switch (type) {
	case TYPE_PART:
		return &me->_parts[ref];
	case TYPE_PLAYER:
		return &me->_players[ref];
	default:
		error("loadReference: invalid type");
	}
}

int IMuseInternal::save_or_load(Serializer *ser, Scumm *scumm)
{
	const SaveLoadEntry mainEntries[] = {
		MKLINE(IMuseInternal, _queue_end, sleUint8),
		MKLINE(IMuseInternal, _queue_pos, sleUint8),
		MKLINE(IMuseInternal, _queue_sound, sleUint16),
		MKLINE(IMuseInternal, _queue_adding, sleByte),
		MKLINE(IMuseInternal, _queue_marker, sleByte),
		MKLINE(IMuseInternal, _queue_cleared, sleByte),
		MKLINE(IMuseInternal, _master_volume, sleByte),
		MKLINE(IMuseInternal, _trigger_count, sleUint16),
		MKARRAY(IMuseInternal, _channel_volume[0], sleUint16, 8),
		MKARRAY(IMuseInternal, _volchan_table[0], sleUint16, 8),
		MKEND()
	};

	const SaveLoadEntry playerEntries[] = {
		MKREF(Player, _parts, TYPE_PART),
		MKLINE(Player, _active, sleByte),
		MKLINE(Player, _id, sleUint16),
		MKLINE(Player, _priority, sleByte),
		MKLINE(Player, _volume, sleByte),
		MKLINE(Player, _pan, sleInt8),
		MKLINE(Player, _transpose, sleByte),
		MKLINE(Player, _detune, sleInt8),
		MKLINE(Player, _vol_chan, sleUint16),
		MKLINE(Player, _vol_eff, sleByte),
		MKLINE(Player, _speed, sleByte),
		MKLINE(Player, _song_index, sleUint16),
		MKLINE(Player, _track_index, sleUint16),
		MKLINE(Player, _timer_counter, sleUint16),
		MKLINE(Player, _loop_to_beat, sleUint16),
		MKLINE(Player, _loop_from_beat, sleUint16),
		MKLINE(Player, _loop_counter, sleUint16),
		MKLINE(Player, _loop_to_tick, sleUint16),
		MKLINE(Player, _loop_from_tick, sleUint16),
		MKLINE(Player, _tempo, sleUint32),
		MKLINE(Player, _cur_pos, sleUint32),
		MKLINE(Player, _next_pos, sleUint32),
		MKLINE(Player, _song_offset, sleUint32),
		MKLINE(Player, _tick_index, sleUint16),
		MKLINE(Player, _beat_index, sleUint16),
		MKLINE(Player, _ticks_per_beat, sleUint16),
		MKLINE(Player, _hook._jump, sleByte),
		MKLINE(Player, _hook._transpose, sleByte),
		MKARRAY(Player, _hook._part_onoff[0], sleByte, 16),
		MKARRAY(Player, _hook._part_volume[0], sleByte, 16),
		MKARRAY(Player, _hook._part_program[0], sleByte, 16),
		MKARRAY(Player, _hook._part_transpose[0], sleByte, 16),
		MKEND()
	};

	const SaveLoadEntry volumeFaderEntries[] = {
		MKREF(VolumeFader, player, TYPE_PLAYER),
		MKLINE(VolumeFader, active, sleUint8),
		MKLINE(VolumeFader, curvol, sleUint8),
		MKLINE(VolumeFader, speed_lo_max, sleUint16),
		MKLINE(VolumeFader, num_steps, sleUint16),
		MKLINE(VolumeFader, speed_hi, sleInt8),
		MKLINE(VolumeFader, direction, sleInt8),
		MKLINE(VolumeFader, speed_lo, sleInt8),
		MKLINE(VolumeFader, speed_lo_counter, sleUint16),
		MKEND()
	};

	const SaveLoadEntry partEntries[] = {
		MKREF(Part, _next, TYPE_PART),
		MKREF(Part, _prev, TYPE_PART),
		MKREF(Part, _player, TYPE_PLAYER),
		MKLINE(Part, _pitchbend, sleInt16),
		MKLINE(Part, _pitchbend_factor, sleUint8),
		MKLINE(Part, _transpose, sleInt8),
		MKLINE(Part, _vol, sleUint8),
		MKLINE(Part, _detune, sleInt8),
		MKLINE(Part, _pan, sleInt8),
		MKLINE(Part, _on, sleUint8),
		MKLINE(Part, _modwheel, sleUint8),
		MKLINE(Part, _pedal, sleUint8),
		MKLINE(Part, _program, sleUint8),
		MKLINE(Part, _pri, sleUint8),
		MKLINE(Part, _chan, sleUint8),
		MKLINE(Part, _effect_level, sleUint8),
		MKLINE(Part, _chorus, sleUint8),
		MKLINE(Part, _percussion, sleUint8),
		MKLINE(Part, _bank, sleUint8),
		MKEND()
	};

	if (!ser->isSaving()) {
		stop_all_sounds();
	}
#ifdef _WIN32_WCE								// Don't break savegames made with andys' build
	if (!ser->isSaving() && ser->checkEOFLoadStream())
		return 0;
#endif

	ser->_ref_me = this;
	ser->_saveload_ref = ser->isSaving()? ((void *)&saveReference) : ((void *)&loadReference);

	ser->saveLoadEntries(this, mainEntries);
	ser->saveLoadArrayOf(_players, ARRAYSIZE(_players), sizeof(_players[0]), playerEntries);
	ser->saveLoadArrayOf(_parts, ARRAYSIZE(_parts), sizeof(_parts[0]), partEntries);
	ser->saveLoadArrayOf(_volume_fader, ARRAYSIZE(_volume_fader),
											 sizeof(_volume_fader[0]), volumeFaderEntries);

	if (!ser->isSaving()) {
		/* Load all sounds that we need */
		fix_players_after_load(scumm);
		init_sustaining_notes();
		_active_volume_faders = true;
		fix_parts_after_load();
		_driver->update_pris();
	}

	return 0;
}

#undef MKLINE
#undef MKEND

void IMuseInternal::fix_parts_after_load()
{
	Part *part;
	int i;

	for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
		if (part->_player)
			part->fix_after_load();
	}
}

/* Only call this routine from the main thread,
 * since it uses getResourceAddress */
void IMuseInternal::fix_players_after_load(Scumm *scumm)
{
	Player *player = _players;
	int i;

	for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
		if (player->_active) {
			player->set_tempo(player->_tempo);
			scumm->getResourceAddress(rtSound, player->_id);
			player->_mt32emulate = isMT32(player->_id);
		}
	}
}

void Part::set_detune(int8 detune)
{
	_detune_eff = clamp((_detune = detune) + _player->_detune, -128, 127);
	changed(IMuseDriver::pcMod);
}

void Part::set_pitchbend(int value)
{
	_pitchbend = value * _pitchbend_factor >> 6;
	changed(IMuseDriver::pcMod);
}

void Part::set_vol(uint8 vol)
{
	_vol_eff = ((_vol = vol) + 1) * _player->_vol_eff >> 7;
	changed(IMuseDriver::pcVolume);
}

void Part::set_pri(int8 pri)
{
	_pri_eff = clamp((_pri = pri) + _player->_priority, 0, 255);
}

void Part::set_pan(int8 pan)
{
	_pan_eff = clamp((_pan = pan) + _player->_pan, -64, 63);
	changed(IMuseDriver::pcPan);
}

void Part::set_transpose(int8 transpose)
{
	_transpose_eff = transpose_clamp((_transpose = transpose) + _player->_transpose, -12, 12);
	changed(IMuseDriver::pcMod);
}

void Part::set_pedal(bool value)
{
	_pedal = value;
	changed(IMuseDriver::pcPedal);
}

void Part::set_modwheel(uint value)
{
	_modwheel = value;
	changed(IMuseDriver::pcModwheel);
}

void Part::set_chorus(uint chorus)
{
	_chorus = chorus;
	changed(IMuseDriver::pcChorus);
}

void Part::set_effect_level(uint level)
{
	_effect_level = level;
	changed(IMuseDriver::pcEffectLevel);
}

void Part::fix_after_load()
{
	set_transpose(_transpose);
	set_vol(_vol);
	set_detune(_detune);
	set_pri(_pri);
	set_pan(_pan);
}

void Part::set_pitchbend_factor(uint8 value)
{
	if (value > 12)
		return;
	set_pitchbend(0);
	_pitchbend_factor = value;
}

void Part::set_onoff(bool on)
{
	if (_on != on) {
		_on = on;
		if (!on)
			off();
		if (!_percussion)
			update_pris();
	}
}

void Part::set_instrument(Instrument * data)
{
	_drv->part_set_instrument(this, data);
}

void Part::key_on(byte note, byte velocity)
{
	_drv->part_key_on(this, note, velocity);
}

void Part::key_off(byte note)
{
	_drv->part_key_off(this, note);
}

void Part::init(IMuseDriver * driver)
{
	_drv = driver;
	_player = NULL;
	_next = NULL;
	_prev = NULL;
	_mc = NULL;
}

void Part::setup(Player *player)
{
	_player = player;

	/* Insert first into player's list */
	_prev = NULL;
	_next = player->_parts;
	if (player->_parts)
		player->_parts->_prev = this;
	player->_parts = this;

	_percussion = true;
	_on = true;
	_pri_eff = player->_priority;
	_pri = 0;
	_vol = 127;
	_vol_eff = player->_vol_eff;
	_pan = clamp(player->_pan, -64, 63);
	_transpose_eff = player->_transpose;
	_transpose = 0;
	_detune = 0;
	_detune_eff = player->_detune;
	_pitchbend_factor = 2;
	_pitchbend = 0;
	_effect_level = 64;
	_program = 255;
	_chorus = 0;
	_modwheel = 0;
	_bank = 0;
	_pedal = false;
	_mc = NULL;
}

void Part::uninit()
{
	if (!_player)
		return;
	off();

	/* unlink */
	if (_next)
		_next->_prev = _prev;
	if (_prev)
		_prev->_next = _next;
	else
		_player->_parts = _next;
	_player = NULL;
	_next = NULL;
	_prev = NULL;
}

void Part::off()
{
	_drv->part_off(this);
}

void Part::changed(byte what)
{
	_drv->part_changed(this, what);
}

void Part::set_param(byte param, int value)
{
	_drv->part_set_param(this, param, value);
}

void Part::update_pris()
{
	_drv->update_pris();
}

int Part::update_actives(uint16 *active)
{
	return _drv->part_update_active(this, active);
}

void Part::set_program(byte program)
{
	if (_program != program || _bank != 0) {
		_program = program;
		_bank = 0;
		changed(IMuseDriver::pcProgram);
	}
}

void Part::set_instrument(uint b)
{
	_bank = (byte)(b >> 8);
	_program = (byte)b;
	changed(IMuseDriver::pcProgram);
}


//********************************************
//***** ADLIB PART OF IMUSE STARTS HERE ******
//********************************************


static byte lookup_table[64][32];
const byte volume_table[] = {
	0, 4, 7, 11,
	13, 16, 18, 20,
	22, 24, 26, 27,
	29, 30, 31, 33,
	34, 35, 36, 37,
	38, 39, 40, 41,
	42, 43, 44, 44,
	45, 46, 47, 47,
	48, 49, 49, 50,
	51, 51, 52, 53,
	53, 54, 54, 55,
	55, 56, 56, 57,
	57, 58, 58, 59,
	59, 60, 60, 60,
	61, 61, 62, 62,
	62, 63, 63, 63
};

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

	if (b == 31)
		return a;

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

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

void create_lookup_table()
{
	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;
			sum += i;
		}
	}
	for (i = 0; i < 64; i++)
		lookup_table[i][0] = 0;
}

MidiChannelAdl *IMuseAdlib::allocate_midichan(byte pri)
{
	MidiChannelAdl *ac, *best = NULL;
	int i;

	for (i = 0; i < 9; i++) {
		if (++_midichan_index >= 9)
			_midichan_index = 0;
		ac = &_midi_channels[_midichan_index];
		if (!ac->_part)
			return ac;
		if (!ac->_next) {
			if (ac->_part->_pri_eff <= pri) {
				pri = ac->_part->_pri_eff;
				best = ac;
			}
		}
	}

	if (best)
		mc_off(best);
	else;													//debug(1, "Denying adlib channel request");
	return best;
}

void IMuseAdlib::premix_proc(void *param, int16 *buf, uint len)
{
	((IMuseAdlib *) param)->generate_samples(buf, len);
}

void IMuseAdlib::init(IMuseInternal *eng, OSystem *syst)
{
	int i;
	MidiChannelAdl *mc;

	_se = eng;

	for (i = 0, mc = _midi_channels; i != ARRAYSIZE(_midi_channels); i++, mc++) {
		mc->_channel = i;
		mc->_s11a.s10 = &mc->_s10b;
		mc->_s11b.s10 = &mc->_s10a;
	}

	_adlib_reg_cache = (byte *)calloc(256, 1);

	_opl = OPLCreate(OPL_TYPE_YM3812, 3579545, syst->property(OSystem::PROP_GET_SAMPLE_RATE, 0));

	adlib_write(1, 0x20);
	adlib_write(8, 0x40);
	adlib_write(0xBD, 0x00);
	create_lookup_table();

	_mixer->setup_premix(this, premix_proc);
}

void IMuseAdlib::adlib_write(byte port, byte value)
{
	if (_adlib_reg_cache[port] == value)
		return;
	_adlib_reg_cache[port] = value;

	OPLWriteReg(_opl, port, value);
}

void IMuseAdlib::adlib_key_off(int chan)
{
	byte port = chan + 0xB0;
	adlib_write(port, adlib_read(port) & ~0x20);
}

struct AdlibSetParams {
	byte a, b, c, d;
};

static const byte channel_mappings[9] = {
	0, 1, 2, 8,
	9, 10, 16, 17,
	18
};

static const byte channel_mappings_2[9] = {
	3, 4, 5, 11,
	12, 13, 19, 20,
	21
};

static const AdlibSetParams adlib_setparam_table[] = {
	{0x40, 0, 63, 63},						/* level */
	{0xE0, 2, 0, 0},							/* unused */
	{0x40, 6, 192, 0},						/* level key scaling */
	{0x20, 0, 15, 0},							/* modulator frequency multiple */
	{0x60, 4, 240, 15},						/* attack rate */
	{0x60, 0, 15, 15},						/* decay rate */
	{0x80, 4, 240, 15},						/* sustain level */
	{0x80, 0, 15, 15},						/* release rate */
	{0xE0, 0, 3, 0},							/* waveform select */
	{0x20, 7, 128, 0},						/* amp mod */
	{0x20, 6, 64, 0},							/* vib */
	{0x20, 5, 32, 0},							/* eg typ */
	{0x20, 4, 16, 0},							/* ksr */
	{0xC0, 0, 1, 0},							/* decay alg */
	{0xC0, 1, 14, 0}							/* feedback */
};

void IMuseAdlib::adlib_set_param(int channel, byte param, int value)
{
	const AdlibSetParams *as;
	byte port;

	assert(channel >= 0 && channel < 9);

	if (param <= 12) {
		port = channel_mappings_2[channel];
	} else if (param <= 25) {
		param -= 13;
		port = channel_mappings[channel];
	} else if (param <= 27) {
		param -= 13;
		port = channel;
	} else if (param == 28 || param == 29) {
		if (param == 28)
			value -= 15;
		else
			value -= 383;
		value <<= 4;
		channel_table_2[channel] = value;
		adlib_playnote(channel, curnote_table[channel] + value);
		return;
	} else {
		return;
	}

	as = &adlib_setparam_table[param];
	if (as->d)
		value = as->d - value;
	port += as->a;
	adlib_write(port, (adlib_read(port) & ~as->c) | (((byte)value) << as->b));
}

static const byte octave_numbers[] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3,
	4, 4, 4, 4, 4, 4, 4, 4,
	4, 4, 4, 4, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 5,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7
};

static const byte note_numbers[] = {
	3, 4, 5, 6, 7, 8, 9, 10,
	11, 12, 13, 14, 3, 4, 5, 6,
	7, 8, 9, 10, 11, 12, 13, 14,
	3, 4, 5, 6, 7, 8, 9, 10,
	11, 12, 13, 14, 3, 4, 5, 6,
	7, 8, 9, 10, 11, 12, 13, 14,
	3, 4, 5, 6, 7, 8, 9, 10,
	11, 12, 13, 14, 3, 4, 5, 6,
	7, 8, 9, 10, 11, 12, 13, 14,
	3, 4, 5, 6, 7, 8, 9, 10,
	11, 12, 13, 14, 3, 4, 5, 6,
	7, 8, 9, 10, 11, 12, 13, 14,
	3, 4, 5, 6, 7, 8, 9, 10,
	11, 12, 13, 14, 3, 4, 5, 6,
	7, 8, 9, 10, 11, 12, 13, 14,
	3, 4, 5, 6, 7, 8, 9, 10
};

static const byte note_to_f_num[] = {
	90, 91, 92, 92, 93, 94, 94, 95,
	96, 96, 97, 98, 98, 99, 100, 101,
	101, 102, 103, 104, 104, 105, 106, 107,
	107, 108, 109, 110, 111, 111, 112, 113,
	114, 115, 115, 116, 117, 118, 119, 120,
	121, 121, 122, 123, 124, 125, 126, 127,
	128, 129, 130, 131, 132, 132, 133, 134,
	135, 136, 137, 138, 139, 140, 141, 142,
	143, 145, 146, 147, 148, 149, 150, 151,
	152, 153, 154, 155, 157, 158, 159, 160,
	161, 162, 163, 165, 166, 167, 168, 169,
	171, 172, 173, 174, 176, 177, 178, 180,
	181, 182, 184, 185, 186, 188, 189, 190,
	192, 193, 194, 196, 197, 199, 200, 202,
	203, 205, 206, 208, 209, 211, 212, 214,
	215, 217, 218, 220, 222, 223, 225, 226,
	228, 230, 231, 233, 235, 236, 238, 240,
	242, 243, 245, 247, 249, 251, 252, 254,
};

void IMuseAdlib::adlib_playnote(int channel, int note)
{
	byte old, oct, notex;
	int note2;
	int i;

	note2 = (note >> 7) - 4;

	oct = octave_numbers[note2] << 2;
	notex = note_numbers[note2];

	old = adlib_read(channel + 0xB0);
	if (old & 0x20) {
		old &= ~0x20;
		if (oct > old) {
			if (notex < 6) {
				notex += 12;
				oct -= 4;
			}
		} else if (oct < old) {
			if (notex > 11) {
				notex -= 12;
				oct += 4;
			}
		}
	}

	i = (notex << 3) + ((note >> 4) & 0x7);
	adlib_write(channel + 0xA0, note_to_f_num[i]);
	adlib_write(channel + 0xB0, oct | 0x20);
}

void IMuseAdlib::adlib_note_on(int chan, byte note, int mod)
{
	int code;
	assert(chan >= 0 && chan < 9);
	code = (note << 7) + mod;
	curnote_table[chan] = code;
	adlib_playnote(chan, channel_table_2[chan] + code);
}

void IMuseAdlib::adlib_note_on_ex(int chan, byte note, int mod)
{
	int code;
	assert(chan >= 0 && chan < 9);
	code = (note << 7) + mod;
	curnote_table[chan] = code;
	channel_table_2[chan] = 0;
	adlib_playnote(chan, code);
}

void IMuseAdlib::adlib_key_onoff(int channel)
{
	byte val;
	byte port = channel + 0xB0;
	assert(channel >= 0 && channel < 9);

	val = adlib_read(port);
	adlib_write(port, val & ~0x20);
	adlib_write(port, val | 0x20);
}

void IMuseAdlib::adlib_setup_channel(int chan, Instrument * instr, byte vol_1, byte vol_2)
{
	byte port;

	assert(chan >= 0 && chan < 9);

	port = channel_mappings[chan];
	adlib_write(port + 0x20, instr->flags_1);
	adlib_write(port + 0x40, (instr->oplvl_1 | 0x3F) - vol_1);
	adlib_write(port + 0x60, ~instr->atdec_1);
	adlib_write(port + 0x80, ~instr->sustrel_1);
	adlib_write(port + 0xE0, instr->waveform_1);

	port = channel_mappings_2[chan];
	adlib_write(port + 0x20, instr->flags_2);
	adlib_write(port + 0x40, (instr->oplvl_2 | 0x3F) - vol_2);
	adlib_write(port + 0x60, ~instr->atdec_2);
	adlib_write(port + 0x80, ~instr->sustrel_2);
	adlib_write(port + 0xE0, instr->waveform_2);

	adlib_write((byte)chan + 0xC0, instr->feedback);
}

int IMuseAdlib::adlib_read_param(int chan, byte param)
{
	const AdlibSetParams *as;
	byte val;
	byte port;

	assert(chan >= 0 && chan < 9);

	if (param <= 12) {
		port = channel_mappings_2[chan];
	} else if (param <= 25) {
		param -= 13;
		port = channel_mappings[chan];
	} else if (param <= 27) {
		param -= 13;
		port = chan;
	} else if (param == 28) {
		return 0xF;
	} else if (param == 29) {
		return 0x17F;
	} else {
		return 0;
	}

	as = &adlib_setparam_table[param];
	val = adlib_read(port + as->a);
	val &= as->c;
	val >>= as->b;
	if (as->d)
		val = as->d - val;

	return val;
}

void IMuseAdlib::generate_samples(int16 *data, int len)
{
	int step;

	if (!_opl) {
		memset(data, 0, len * sizeof(int16));
		return;
	}

	do {
		step = len;
		if (step > _next_tick)
			step = _next_tick;
		YM3812UpdateOne(_opl, data, step);

		if (!(_next_tick -= step)) {
			_se->on_timer();
			reset_tick();
		}
		data += step;
	} while (len -= step);
}


void IMuseAdlib::reset_tick()
{
	_next_tick = 88;
}

void IMuseAdlib::on_timer()
{
	MidiChannelAdl *mc;
	int i;

	_adlib_timer_counter += 0xD69;
	while (_adlib_timer_counter >= 0x411B) {
		_adlib_timer_counter -= 0x411B;
		mc = _midi_channels;
		for (i = 0; i != ARRAYSIZE(_midi_channels); i++, mc++) {
			if (!mc->_part)
				continue;
			if (mc->_duration && (mc->_duration -= 0x11) <= 0) {
				mc_off(mc);
				return;
			}
			if (mc->_s10a.active) {
				mc_inc_stuff(mc, &mc->_s10a, &mc->_s11a);
			}
			if (mc->_s10b.active) {
				mc_inc_stuff(mc, &mc->_s10b, &mc->_s11b);
			}
		}
	}
}

const byte param_table_1[16] = {
	29, 28, 27, 0,
	3, 4, 7, 8,
	13, 16, 17, 20,
	21, 30, 31, 0
};

const uint16 param_table_2[16] = {
	0x2FF, 0x1F, 0x7, 0x3F,
	0x0F, 0x0F, 0x0F, 0x3,
	0x3F, 0x0F, 0x0F, 0x0F,
	0x3, 0x3E, 0x1F, 0
};

static const uint16 num_steps_table[] = {
	1, 2, 4, 5,
	6, 7, 8, 9,
	10, 12, 14, 16,
	18, 21, 24, 30,
	36, 50, 64, 82,
	100, 136, 160, 192,
	240, 276, 340, 460,
	600, 860, 1200, 1600
};

int IMuseAdlib::random_nr(int a)
{
	static byte _rand_seed = 1;
	if (_rand_seed & 1) {
		_rand_seed >>= 1;
		_rand_seed ^= 0xB8;
	} else {
		_rand_seed >>= 1;
	}
	return _rand_seed * a >> 8;
}

void IMuseAdlib::struct10_setup(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]];
	if (t & 0x80) {
		e = random_nr(e);
	}
	if (e == 0)
		e++;

	s10->num_steps = s10->speed_lo_max = e;

	if (f != 2) {
		c = s10->param;
		g = s10->start_value;
		t = s10->table_b[f];
		d = lookup_volume(c, (t & 0x7F) - 31);
		if (t & 0x80) {
			d = random_nr(d);
		}
		if (d + g > c) {
			h = c - g;
		} else {
			h = d;
			if (d + g < 0)
				h = -g;
		}
		h -= s10->cur_val;
	} else {
		h = 0;
	}

	s10->speed_hi = h / e;
	if (h < 0) {
		h = -h;
		s10->direction = -1;
	} else {
		s10->direction = 1;
	}

	s10->speed_lo = h % e;
	s10->speed_lo_counter = 0;
}

byte IMuseAdlib::struct10_ontimer(Struct10 * s10, Struct11 * s11)
{
	byte result = 0;
	int i;

	if (s10->count && (s10->count -= 17) <= 0) {
		s10->active = 0;
		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->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;
			result = 1;
		}
	}

	if (!--s10->num_steps) {
		s10->active++;
		if (s10->active > 4) {
			if (s10->loop) {
				s10->active = 1;
				result |= 2;
				struct10_setup(s10);
			} else {
				s10->active = 0;
			}
		} else {
			struct10_setup(s10);
		}
	}

	return result;
}

void IMuseAdlib::struct10_init(Struct10 * s10, InstrumentExtra * ie)
{
	s10->active = 1;
	s10->cur_val = 0;
	s10->modwheel_last = 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->table_b[0] = ie->c;
	s10->table_b[1] = ie->e;
	s10->table_b[2] = 0;
	s10->table_b[3] = ie->h;

	struct10_setup(s10);
}

void IMuseAdlib::mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10,
															 Struct11 * s11, byte flags, InstrumentExtra * ie)
{
	Part *part = mc->_part;

	s11->modify_val = 0;
	s11->flag0x40 = flags & 0x40;
	s10->loop = flags & 0x20;
	s11->flag0x10 = flags & 0x10;
	s11->param = param_table_1[flags & 0xF];
	s10->param = param_table_2[flags & 0xF];
	s10->unk3 = 31;
	if (s11->flag0x40) {
		s10->modwheel = part->_modwheel >> 2;
	} else {
		s10->modwheel = 31;
	}

	switch (s11->param) {
	case 0:
		s10->start_value = mc->_vol_2;
		break;
	case 13:
		s10->start_value = mc->_vol_1;
		break;
	case 30:
		s10->start_value = 31;
		s11->s10->modwheel = 0;
		break;
	case 31:
		s10->start_value = 0;
		s11->s10->unk3 = 0;
		break;
	default:
		s10->start_value = ((IMuseAdlib *) part->_drv)->adlib_read_param(mc->_channel, s11->param);
	}

	struct10_init(s10, ie);
}

void IMuseAdlib::mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11)
{
	byte code;
	Part *part = mc->_part;

	code = struct10_ontimer(s10, s11);

	if (code & 1) {
		switch (s11->param) {
		case 0:
			mc->_vol_2 = s10->start_value + s11->modify_val;
			((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, 0,
																									 volume_table[lookup_table[mc->_vol_2]
																																[part->_vol_eff >> 2]]);
			break;
		case 13:
			mc->_vol_1 = s10->start_value + s11->modify_val;
			if (mc->_twochan) {
				((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, 13,
																										 volume_table[lookup_table[mc->_vol_1]
																																	[part->_vol_eff >> 2]]);
			} else {
				((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, 13, mc->_vol_1);
			}
			break;
		case 30:
			s11->s10->modwheel = (char)s11->modify_val;
			break;
		case 31:
			s11->s10->unk3 = (char)s11->modify_val;
			break;
		default:
			((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, s11->param,
																									 s10->start_value + s11->modify_val);
			break;
		}
	}

	if (code & 2 && s11->flag0x10)
		((IMuseAdlib *) part->_drv)->adlib_key_onoff(mc->_channel);
}

void IMuseAdlib::part_changed(Part *part, byte what)
{
	MidiChannelAdl *mc;

	if (what & pcProgram) {
		if (part->_program < 32) {
			part_set_instrument(part, &_glob_instr[part->_program]);
		}
	}

	if (what & pcMod) {
		for (mc = part->_mc->adl(); mc; mc = mc->_next) {
			adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
										part->_pitchbend + part->_detune_eff);
		}
	}

	if (what & pcVolume) {
		for (mc = part->_mc->adl(); mc; mc = mc->_next) {
			adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2]
																										[part->_vol_eff >> 2]]);
			if (mc->_twochan) {
				adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1]
																											 [part->_vol_eff >> 2]]);
			}
		}
	}

	if (what & pcPedal) {
		if (!part->_pedal) {
			for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
				if (mc->_waitforpedal)
					mc_off(mc);
			}
		}
	}

	if (what & pcModwheel) {
		for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
			if (mc->_s10a.active && mc->_s11a.flag0x40)
				mc->_s10a.modwheel = part->_modwheel >> 2;
			if (mc->_s10b.active && mc->_s11b.flag0x40)
				mc->_s10b.modwheel = part->_modwheel >> 2;
		}
	}
}

void IMuseAdlib::mc_key_on(MidiChannel * mc2, byte note, byte velocity)
{
	MidiChannelAdl *mc = (MidiChannelAdl *)mc2;
	Part *part = mc->_part;
	Instrument *instr = &_part_instr[part->_slot];
	int c;
	byte vol_1, vol_2;

	mc->_twochan = instr->feedback & 1;
	mc->_note = note;
	mc->_waitforpedal = false;
	mc->_duration = instr->duration;
	if (mc->_duration != 0)
		mc->_duration *= 63;

	vol_1 = (instr->oplvl_1 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_1 >> 2];
	if (vol_1 > 0x3F)
		vol_1 = 0x3F;
	mc->_vol_1 = vol_1;

	vol_2 = (instr->oplvl_2 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_2 >> 2];
	if (vol_2 > 0x3F)
		vol_2 = 0x3F;
	mc->_vol_2 = vol_2;

	c = part->_vol_eff >> 2;

	vol_2 = volume_table[lookup_table[vol_2][c]];
	if (mc->_twochan)
		vol_1 = volume_table[lookup_table[vol_1][c]];

	adlib_setup_channel(mc->_channel, instr, vol_1, vol_2);
	adlib_note_on_ex(mc->_channel, part->_transpose_eff + note, part->_detune_eff + part->_pitchbend);

	if (instr->flags_a & 0x80) {
		mc_init_stuff(mc, &mc->_s10a, &mc->_s11a, instr->flags_a, &instr->extra_a);
	} else {
		mc->_s10a.active = 0;
	}

	if (instr->flags_b & 0x80) {
		mc_init_stuff(mc, &mc->_s10b, &mc->_s11b, instr->flags_b, &instr->extra_b);
	} else {
		mc->_s10b.active = 0;
	}
}

void IMuseAdlib::set_instrument(uint slot, byte *data)
{
	if (slot < 32) {
		memcpy(&_glob_instr[slot], data, sizeof(Instrument));
	}
}


void IMuseAdlib::link_mc(Part *part, MidiChannelAdl *mc)
{
	mc->_part = part;
	mc->_next = (MidiChannelAdl *)part->_mc;
	part->_mc = mc;
	mc->_prev = NULL;

	if (mc->_next)
		mc->_next->_prev = mc;
}

void IMuseAdlib::part_key_on(Part *part, byte note, byte velocity)
{
	MidiChannelAdl *mc;

	mc = allocate_midichan(part->_pri_eff);
	if (!mc)
		return;

	link_mc(part, mc);
	mc_key_on(mc, note, velocity);
}

void IMuseAdlib::part_key_off(Part *part, byte note)
{
	MidiChannelAdl *mc;

	for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
		if (mc->_note == note) {
			if (part->_pedal)
				mc->_waitforpedal = true;
			else
				mc_off(mc);
		}
	}
}

struct AdlibInstrSetParams {
	byte param;
	byte shl;
	byte mask;
};

#define MKLINE(_a_,_b_,_c_) { (int)&((Instrument*)0)->_a_, _b_, ((1<<(_c_))-1)<<(_b_) }
static const AdlibInstrSetParams adlib_instr_params[69] = {
	MKLINE(oplvl_2, 0, 6),
	MKLINE(waveform_2, 2, 5),
	MKLINE(oplvl_2, 6, 2),
	MKLINE(flags_2, 0, 4),
	MKLINE(atdec_2, 4, 4),
	MKLINE(atdec_2, 0, 4),
	MKLINE(sustrel_2, 4, 4),
	MKLINE(sustrel_2, 0, 4),
	MKLINE(waveform_2, 0, 2),
	MKLINE(flags_2, 7, 1),
	MKLINE(flags_2, 6, 1),
	MKLINE(flags_2, 5, 1),
	MKLINE(flags_2, 4, 1),

	MKLINE(oplvl_1, 0, 6),
	MKLINE(waveform_1, 2, 5),
	MKLINE(oplvl_1, 6, 2),
	MKLINE(flags_1, 0, 4),
	MKLINE(atdec_1, 4, 4),
	MKLINE(atdec_1, 0, 4),
	MKLINE(sustrel_1, 4, 4),
	MKLINE(sustrel_1, 0, 4),
	MKLINE(waveform_1, 0, 2),
	MKLINE(flags_1, 7, 1),
	MKLINE(flags_1, 6, 1),
	MKLINE(flags_1, 5, 1),
	MKLINE(flags_1, 4, 1),

	MKLINE(feedback, 0, 1),
	MKLINE(feedback, 1, 3),

	MKLINE(flags_a, 7, 1),
	MKLINE(flags_a, 6, 1),
	MKLINE(flags_a, 5, 1),
	MKLINE(flags_a, 4, 1),
	MKLINE(flags_a, 0, 4),
	MKLINE(extra_a.a, 0, 8),
	MKLINE(extra_a.b, 0, 7),
	MKLINE(extra_a.c, 0, 7),
	MKLINE(extra_a.d, 0, 7),
	MKLINE(extra_a.e, 0, 7),
	MKLINE(extra_a.f, 0, 7),
	MKLINE(extra_a.g, 0, 7),
	MKLINE(extra_a.h, 0, 7),
	MKLINE(extra_a.b, 7, 1),
	MKLINE(extra_a.c, 7, 1),
	MKLINE(extra_a.d, 7, 1),
	MKLINE(extra_a.e, 7, 1),
	MKLINE(extra_a.f, 7, 1),
	MKLINE(extra_a.g, 7, 1),
	MKLINE(extra_a.h, 7, 1),

	MKLINE(flags_b, 7, 1),
	MKLINE(flags_b, 6, 1),
	MKLINE(flags_b, 5, 1),
	MKLINE(flags_b, 4, 1),
	MKLINE(flags_b, 0, 4),
	MKLINE(extra_b.a, 0, 8),
	MKLINE(extra_b.b, 0, 7),
	MKLINE(extra_b.c, 0, 7),
	MKLINE(extra_b.d, 0, 7),
	MKLINE(extra_b.e, 0, 7),
	MKLINE(extra_b.f, 0, 7),
	MKLINE(extra_b.g, 0, 7),
	MKLINE(extra_b.h, 0, 7),
	MKLINE(extra_b.b, 7, 1),
	MKLINE(extra_b.c, 7, 1),
	MKLINE(extra_b.d, 7, 1),
	MKLINE(extra_b.e, 7, 1),
	MKLINE(extra_b.f, 7, 1),
	MKLINE(extra_b.g, 7, 1),
	MKLINE(extra_b.h, 7, 1),

	MKLINE(duration, 0, 8),
};
#undef MKLINE

void IMuseAdlib::part_set_param(Part *part, byte param, int value)
{
	const AdlibInstrSetParams *sp = &adlib_instr_params[param];
	byte *p = (byte *)&_part_instr[part->_slot] + sp->param;
	*p = (*p & ~sp->mask) | (value << sp->shl);

	if (param < 28) {
		MidiChannelAdl *mc;

		for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
			adlib_set_param(mc->_channel, param, value);
		}
	}
}

void IMuseAdlib::part_off(Part *part)
{
	MidiChannelAdl *mc = (MidiChannelAdl *)part->_mc;
	part->_mc = NULL;
	for (; mc; mc = mc->_next) {
		mc_off(mc);
	}
}

void IMuseAdlib::mc_off(MidiChannel * mc2)
{
	MidiChannelAdl *mc = (MidiChannelAdl *)mc2, *tmp;

	adlib_key_off(mc->_channel);

	tmp = mc->_prev;

	if (mc->_next)
		mc->_next->_prev = tmp;
	if (tmp)
		tmp->_next = mc->_next;
	else
		mc->_part->_mc = mc->_next;
	mc->_part = NULL;
}

void IMuseAdlib::part_set_instrument(Part *part, Instrument * instr)
{
	Instrument *i = &_part_instr[part->_slot];
	memcpy(i, instr, sizeof(Instrument));
}

int IMuseAdlib::part_update_active(Part *part, uint16 *active)
{
	uint16 bits;
	int count = 0;
	MidiChannelAdl *mc;

	bits = 1 << part->_chan;

	for (mc = part->_mc->adl(); mc; mc = mc->_next) {
		if (!(active[mc->_note] & bits)) {
			active[mc->_note] |= bits;
			count++;
		}
	}
	return count;
}

//********************************************
//** GENERAL MIDI PART OF IMUSE STARTS HERE **
//********************************************

void IMuseGM::midiPitchBend(byte chan, int16 pitchbend)
{
	uint16 tmp;

	if (_midi_pitchbend_last[chan] != pitchbend) {
		_midi_pitchbend_last[chan] = pitchbend;
		tmp = (pitchbend << 2) + 0x2000;
		_md->send(((tmp >> 7) & 0x7F) << 16 | (tmp & 0x7F) << 8 | 0xE0 | chan);
	}
}

void IMuseGM::midiVolume(byte chan, byte volume)
{
	if (_midi_volume_last[chan] != volume) {
		_midi_volume_last[chan] = volume;
		_md->send(volume << 16 | 7 << 8 | 0xB0 | chan);
	}
}
void IMuseGM::midiPedal(byte chan, bool pedal)
{
	if (_midi_pedal_last[chan] != pedal) {
		_midi_pedal_last[chan] = pedal;
		_md->send(pedal << 16 | 64 << 8 | 0xB0 | chan);
	}
}

void IMuseGM::midiModWheel(byte chan, byte modwheel)
{
	if (_midi_modwheel_last[chan] != modwheel) {
		_midi_modwheel_last[chan] = modwheel;
		_md->send(modwheel << 16 | 1 << 8 | 0xB0 | chan);
	}
}

void IMuseGM::midiEffectLevel(byte chan, byte level)
{
	if (_midi_effectlevel_last[chan] != level) {
		_midi_effectlevel_last[chan] = level;
		_md->send(level << 16 | 91 << 8 | 0xB0 | chan);
	}
}

void IMuseGM::midiChorus(byte chan, byte chorus)
{
	if (_midi_chorus_last[chan] != chorus) {
		_midi_chorus_last[chan] = chorus;
		_md->send(chorus << 16 | 93 << 8 | 0xB0 | chan);
	}
}

void IMuseGM::midiControl0(byte chan, byte value)
{
	_md->send(value << 16 | 0 << 8 | 0xB0 | chan);
}


void IMuseGM::midiProgram(byte chan, byte program, bool mt32emulate)
{
//  if ((chan + 1) != 10) {       /* Ignore percussion prededed by patch change */
	if (mt32emulate)
		program = mt32_to_gmidi[program];

	_md->send(program << 8 | 0xC0 | chan);
//  }
}

void IMuseGM::midiPan(byte chan, int8 pan)
{
	if (_midi_pan_last[chan] != pan) {
		_midi_pan_last[chan] = pan;
		_md->send(((pan - 64) & 0x7F) << 16 | 10 << 8 | 0xB0 | chan);
	}
}

void IMuseGM::midiNoteOn(byte chan, byte note, byte velocity)
{
	_md->send(velocity << 16 | note << 8 | 0x90 | chan);
}

void IMuseGM::midiNoteOff(byte chan, byte note)
{
	_md->send(note << 8 | 0x80 | chan);
}

void IMuseGM::midiSilence(byte chan)
{
	_md->send((64 << 8) | 0xB0 | chan);
	_md->send((123 << 8) | 0xB0 | chan);
}


void IMuseGM::part_key_on(Part *part, byte note, byte velocity)
{
	MidiChannelGM *mc = part->_mc->gm();

	if (mc) {
		mc->_actives[note >> 4] |= (1 << (note & 0xF));
		midiNoteOn(mc->_chan, note, velocity);
	} else if (part->_percussion) {
		midiVolume(SPECIAL_CHANNEL, part->_vol_eff);
		midiProgram(SPECIAL_CHANNEL, part->_bank, part->_player->_mt32emulate);
		midiNoteOn(SPECIAL_CHANNEL, note, velocity);
	}
}

void IMuseGM::part_key_off(Part *part, byte note)
{
	MidiChannelGM *mc = part->_mc->gm();

	if (mc) {
		mc->_actives[note >> 4] &= ~(1 << (note & 0xF));
		midiNoteOff(mc->_chan, note);
	} else if (part->_percussion) {
		midiNoteOff(SPECIAL_CHANNEL, note);
	}
}

#if !defined(__MORPHOS__)
int IMuseGM::midi_driver_thread(void *param)
{
	IMuseGM *mid = (IMuseGM *) param;
	int old_time, cur_time;

	old_time = mid->_system->get_msecs();

	for (;;) {
		mid->_system->delay_msecs(10);

		cur_time = mid->_system->get_msecs();
		while (old_time < cur_time) {
			old_time += 10;
			mid->_se->on_timer();
		}
	}
}
#else
#include <exec/semaphores.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include "../morphos/morphos.h"
int IMuseGM::midi_driver_thread(void *param)
{
	IMuseGM *mid = (IMuseGM *) param;
	int old_time, cur_time;
	bool initialized;

	ObtainSemaphore(&ScummMusicThreadRunning);

	initialized = init_morphos_music(0);

	old_time = mid->_system->get_msecs();

	if (!initialized)
		Wait(SIGBREAKF_CTRL_C);
	else {
		for (;;) {
			MusicTimerIORequest->tr_time.tv_micro = 10000;
			MusicTimerIORequest->tr_node.io_Command = TR_ADDREQUEST;
			MusicTimerIORequest->tr_time.tv_secs = 0;
			DoIO((struct IORequest *)MusicTimerIORequest);

			if (CheckSignal(SIGBREAKF_CTRL_C))
				break;

			cur_time = mid->_system->get_msecs();
			while (old_time < cur_time) {
				old_time += 10;
				mid->_se->on_timer();
			}
		}
	}

	exit_morphos_music();

	ReleaseSemaphore(&ScummMusicThreadRunning);
	RemTask(NULL);
	return 0;
}
#endif

void IMuseGM::init(IMuseInternal *eng, OSystem *syst)
{
	int i;
	MidiChannelGM *mc;

	_system = syst;

	/* open midi driver */
	int result = _md->open(MidiDriver::MO_SIMPLE);
	if (result)
		error("IMuseGM::error = %s", MidiDriver::get_error_name(result));

	/* Install the on_timer thread */
	_se = eng;
	syst->create_thread(midi_driver_thread, this);

	for (i = 0, mc = _midi_channels; i != ARRAYSIZE(_midi_channels); i++, mc++)
		mc->_chan = i;
}

void IMuseGM::update_pris()
{
	Part *part, *hipart;
	int i;
	byte hipri, lopri;
	MidiChannelGM *mc, *lomc;

	while (true) {
		hipri = 0;
		hipart = NULL;
		for (i = 32, part = _se->parts_ptr(); i; i--, part++) {
			if (part->_player && !part->_percussion && part->_on && !part->_mc && part->_pri_eff >= hipri) {
				hipri = part->_pri_eff;
				hipart = part;
			}
		}

		if (!hipart)
			return;

		lopri = 255;
		lomc = NULL;
		for (i = ARRAYSIZE(_midi_channels), mc = _midi_channels;; mc++) {
			if (!mc->_part) {
				lomc = mc;
				break;
			}
			if (mc->_part->_pri_eff <= lopri) {
				lopri = mc->_part->_pri_eff;
				lomc = mc;
			}

			if (!--i) {
				if (lopri >= hipri)
					return;
				lomc->_part->off();
				break;
			}
		}

		hipart->_mc = lomc;
		lomc->_part = hipart;
		hipart->changed(pcAll);
	}
}

int IMuseGM::part_update_active(Part *part, uint16 *active)
{
	int i, j;
	uint16 *act, mask, bits;
	int count = 0;

	bits = 1 << part->_chan;

	act = part->_mc->gm()->_actives;

	for (i = 8; i; i--) {
		mask = *act++;
		if (mask) {
			for (j = 16; j; j--, mask >>= 1, active++) {
				if (mask & 1 && !(*active & bits)) {
					*active |= bits;
					count++;
				}
			}
		} else {
			active += 16;
		}
	}
	return count;
}

void IMuseGM::part_changed(Part *part, byte what)
{
	MidiChannelGM *mc;

	/* Mark for re-schedule if program changed when in pre-state */
	if (what & pcProgram && part->_percussion) {
		part->_percussion = false;
		update_pris();
	}

	if (!(mc = part->_mc->gm()))
		return;

	if (part->_player == NULL) {	/* No player, so dump phantom channel */
		part->_mc = NULL;
		mc->_part = NULL;
		memset(mc->_actives, 0, sizeof(mc->_actives));
		return;
	}

	if (what & pcMod)
		midiPitchBend(mc->_chan,
									clamp(part->_pitchbend + part->_detune_eff +
												(part->_transpose_eff << 7), -2048, 2047));

	if (what & pcVolume)
		midiVolume(mc->_chan, part->_vol_eff);

	if (what & pcPedal)
		midiPedal(mc->_chan, part->_pedal);

	if (what & pcModwheel)
		midiModWheel(mc->_chan, part->_modwheel);

	if (what & pcPan)
		midiPan(mc->_chan, part->_pan_eff);

	if (what & pcEffectLevel)
		midiEffectLevel(mc->_chan, part->_effect_level);

	if (what & pcProgram) {
		if (part->_bank) {
			midiControl0(mc->_chan, part->_bank);
			midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
			midiControl0(mc->_chan, 0);
		} else {
			midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
		}
	}

	if (what & pcChorus)
		midiChorus(mc->_chan, part->_effect_level);
}


void IMuseGM::part_off(Part *part)
{
	MidiChannelGM *mc = part->_mc->gm();
	if (mc) {
		part->_mc = NULL;
		mc->_part = NULL;
		memset(mc->_actives, 0, sizeof(mc->_actives));
		midiSilence(mc->_chan);
	}
}



/*
 * Implementation of the dummy IMuse class that acts as a proxy for
 * our real IMuseInternal class. This way we reduce the compile time
 * and inter source dependencies.
 */
IMuse::IMuse():_imuse(NULL)
{
}

IMuse::~IMuse()
{
	if (_imuse)
		delete _imuse;
}

void IMuse::on_timer()
{
	_imuse->on_timer();
}

void IMuse::pause(bool paused)
{
	_imuse->pause(paused);
}

int IMuse::save_or_load(Serializer *ser, Scumm *scumm)
{
	return _imuse->save_or_load(ser, scumm);
}

int IMuse::set_music_volume(uint vol)
{
	return _imuse->set_music_volume(vol);
}

int IMuse::get_music_volume()
{
	return _imuse->get_music_volume();
}

int IMuse::set_master_volume(uint vol)
{
	return _imuse->set_master_volume(vol);
}

int IMuse::get_master_volume()
{
	return _imuse->get_master_volume();
}

bool IMuse::start_sound(int sound)
{
	return _imuse->start_sound(sound);
}

int IMuse::stop_sound(int sound)
{
	return _imuse->stop_sound(sound);
}

int IMuse::stop_all_sounds()
{
	return _imuse->stop_all_sounds();
}

int IMuse::get_sound_status(int sound)
{
	return _imuse->get_sound_status(sound);
}

int32 IMuse::do_command(int a, int b, int c, int d, int e, int f, int g, int h)
{
	return _imuse->do_command(a, b, c, d, e, f, g, h);
}

int IMuse::clear_queue()
{
	return _imuse->clear_queue();
}

void IMuse::setBase(byte **base)
{
	_imuse->setBase(base);
}

uint32 IMuse::property(int prop, uint32 value)
{
	return _imuse->property(prop, value);
}

IMuse *IMuse::create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer)
{
	IMuse *i = new IMuse;
	i->_imuse = IMuseInternal::create(syst, midi, mixer);
	return i;
}