[Scummvm-cvs-logs] SF.net SVN: scummvm:[47418] scummvm/trunk/engines/sci

waltervn at users.sourceforge.net waltervn at users.sourceforge.net
Thu Jan 21 17:27:29 CET 2010


Revision: 47418
          http://scummvm.svn.sourceforge.net/scummvm/?rev=47418&view=rev
Author:   waltervn
Date:     2010-01-21 16:27:29 +0000 (Thu, 21 Jan 2010)

Log Message:
-----------
SCI: Renamed softseq/ to drivers/

Modified Paths:
--------------
    scummvm/trunk/engines/sci/console.cpp
    scummvm/trunk/engines/sci/module.mk
    scummvm/trunk/engines/sci/sound/iterator/core.cpp
    scummvm/trunk/engines/sci/sound/iterator/iterator.h
    scummvm/trunk/engines/sci/sound/iterator/iterator_internal.h
    scummvm/trunk/engines/sci/sound/midiparser_sci.cpp
    scummvm/trunk/engines/sci/sound/music.h

Added Paths:
-----------
    scummvm/trunk/engines/sci/sound/drivers/
    scummvm/trunk/engines/sci/sound/drivers/adlib.cpp
    scummvm/trunk/engines/sci/sound/drivers/amiga.cpp
    scummvm/trunk/engines/sci/sound/drivers/map-mt32-to-gm.h
    scummvm/trunk/engines/sci/sound/drivers/midi.cpp
    scummvm/trunk/engines/sci/sound/drivers/mididriver.h
    scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp

Removed Paths:
-------------
    scummvm/trunk/engines/sci/sound/softseq/

Modified: scummvm/trunk/engines/sci/console.cpp
===================================================================
--- scummvm/trunk/engines/sci/console.cpp	2010-01-21 16:25:39 UTC (rev 47417)
+++ scummvm/trunk/engines/sci/console.cpp	2010-01-21 16:27:29 UTC (rev 47418)
@@ -41,7 +41,7 @@
 #else
 #include "sci/sound/music.h"
 #endif
-#include "sci/sound/softseq/mididriver.h"
+#include "sci/sound/drivers/mididriver.h"
 #include "sci/vocabulary.h"
 #include "sci/graphics/gui.h"
 #include "sci/graphics/cursor.h"

Modified: scummvm/trunk/engines/sci/module.mk
===================================================================
--- scummvm/trunk/engines/sci/module.mk	2010-01-21 16:25:39 UTC (rev 47417)
+++ scummvm/trunk/engines/sci/module.mk	2010-01-21 16:27:29 UTC (rev 47418)
@@ -58,10 +58,10 @@
 	sound/iterator/core.o \
 	sound/iterator/iterator.o \
 	sound/iterator/songlib.o \
-	sound/softseq/adlib.o \
-	sound/softseq/amiga.o \
-	sound/softseq/pcjr.o \
-	sound/softseq/midi.o \
+	sound/drivers/adlib.o \
+	sound/drivers/amiga.o \
+	sound/drivers/pcjr.o \
+	sound/drivers/midi.o \
 	video/seq_decoder.o \
 	video/vmd_decoder.o
 	

Copied: scummvm/trunk/engines/sci/sound/drivers/adlib.cpp (from rev 47417, scummvm/trunk/engines/sci/sound/softseq/adlib.cpp)
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/adlib.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/adlib.cpp	2010-01-21 16:27:29 UTC (rev 47418)
@@ -0,0 +1,841 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/sci.h"
+
+#include "sound/fmopl.h"
+#include "sound/softsynth/emumidi.h"
+
+#include "sci/resource.h"
+#include "sci/sound/drivers/mididriver.h"
+
+namespace Sci {
+
+#ifdef __DC__
+#define STEREO false
+#else
+#define STEREO true
+#endif
+
+// FIXME: We don't seem to be sending the polyphony init data, so disable this for now
+#define ADLIB_DISABLE_VOICE_MAPPING
+
+class MidiDriver_AdLib : public MidiDriver_Emulated {
+public:
+	enum {
+		kVoices = 9,
+		kRhythmKeys = 62
+	};
+
+	MidiDriver_AdLib(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { }
+	virtual ~MidiDriver_AdLib() { }
+
+	// MidiDriver
+	int open(bool isSCI0);
+	void close();
+	void send(uint32 b);
+	MidiChannel *allocateChannel() { return NULL; }
+	MidiChannel *getPercussionChannel() { return NULL; }
+
+	// AudioStream
+	bool isStereo() const { return _stereo; }
+	int getRate() const { return _mixer->getOutputRate(); }
+
+	// MidiDriver_Emulated
+	void generateSamples(int16 *buf, int len);
+
+	void setVolume(byte volume);
+	void playSwitch(bool play);
+	bool loadResource(const byte *data, uint size);
+	virtual uint32 property(int prop, uint32 param);
+
+private:
+	enum ChannelID {
+		kLeftChannel = 1,
+		kRightChannel = 2
+	};
+
+	struct AdLibOperator {
+		bool amplitudeMod;
+		bool vibrato;
+		bool envelopeType;
+		bool kbScaleRate;
+		byte frequencyMult;		// (0-15)
+		byte kbScaleLevel;		// (0-3)
+		byte totalLevel;		// (0-63, 0=max, 63=min)
+		byte attackRate;		// (0-15)
+		byte decayRate;			// (0-15)
+		byte sustainLevel;		// (0-15)
+		byte releaseRate;		// (0-15)
+		byte waveForm;			// (0-3)
+	};
+
+	struct AdLibModulator {
+		byte feedback;			// (0-7)
+		bool algorithm;
+	};
+
+	struct AdLibPatch {
+		AdLibOperator op[2];
+		AdLibModulator mod;
+	};
+
+	struct Channel {
+		uint8 patch;			// Patch setting
+		uint8 volume;			// Channel volume (0-63)
+		uint8 pan;				// Pan setting (0-127, 64 is center)
+		uint8 holdPedal;		// Hold pedal setting (0 to 63 is off, 127 to 64 is on)
+		uint8 extraVoices;		// The number of additional voices this channel optimally needs
+		uint16 pitchWheel;		// Pitch wheel setting (0-16383, 8192 is center)
+		uint8 lastVoice;		// Last voice used for this MIDI channel
+		bool enableVelocity;	// Enable velocity control (SCI0)
+
+		Channel() : patch(0), volume(63), pan(64), holdPedal(0), extraVoices(0),
+					pitchWheel(8192), lastVoice(0), enableVelocity(false) { }
+	};
+
+	struct AdLibVoice {
+		int8 channel;			// MIDI channel that this voice is assigned to or -1
+		int8 note;				// Currently playing MIDI note or -1
+		int patch;				// Currently playing patch or -1
+		uint8 velocity;			// Note velocity
+		bool isSustained;		// Flag indicating a note that is being sustained by the hold pedal
+		uint16 age;				// Age of the current note
+
+		AdLibVoice() : channel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
+	};
+
+	bool _stereo;
+	bool _isSCI0;
+	OPL::OPL *_opl;
+	bool _playSwitch;
+	int _masterVolume;
+	Channel _channels[MIDI_CHANNELS];
+	AdLibVoice _voices[kVoices];
+	byte *_rhythmKeyMap;
+	Common::Array<AdLibPatch> _patches;
+
+	void loadInstrument(const byte *ins);
+	void voiceOn(int voice, int note, int velocity);
+	void voiceOff(int voice);
+	void setPatch(int voice, int patch);
+	void setNote(int voice, int note, bool key);
+	void setVelocity(int voice);
+	void setOperator(int oper, AdLibOperator &op);
+	void setRegister(int reg, int value, int channels = kLeftChannel | kRightChannel);
+	void renewNotes(int channel, bool key);
+	void noteOn(int channel, int note, int velocity);
+	void noteOff(int channel, int note);
+	int findVoice(int channel);
+	void voiceMapping(int channel, int voices);
+	void assignVoices(int channel, int voices);
+	void releaseVoices(int channel, int voices);
+	void donateVoices();
+	int findVoiceBasic(int channel);
+	void setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan);
+	int calcVelocity(int voice, int op);
+};
+
+class MidiPlayer_AdLib : public MidiPlayer {
+public:
+	MidiPlayer_AdLib() { _driver = new MidiDriver_AdLib(g_system->getMixer()); }
+	~MidiPlayer_AdLib() {
+		delete _driver;
+		_driver = 0;
+	}
+
+	int open(ResourceManager *resMan);
+	void close();
+
+	byte getPlayId(SciVersion soundVersion);
+	int getPolyphony() const { return MidiDriver_AdLib::kVoices; }
+	bool hasRhythmChannel() const { return false; }
+	void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); }
+	void playSwitch(bool play) { static_cast<MidiDriver_AdLib *>(_driver)->playSwitch(play); }
+	void loadInstrument(int idx, byte *data);
+};
+
+static const byte registerOffset[MidiDriver_AdLib::kVoices] = {
+	0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12
+};
+
+static const byte velocityMap1[64] = {
+	0x00, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x12, 0x13,
+	0x14, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d,
+	0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+	0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2d, 0x2d, 0x2e,
+	0x2f, 0x30, 0x31, 0x32, 0x32, 0x33, 0x34, 0x34,
+	0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a,
+	0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
+	0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f
+};
+
+static const byte velocityMap2[64] = {
+	0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
+	0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x21,
+	0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+	0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f, 0x30,
+	0x31, 0x32, 0x32, 0x33, 0x34, 0x34, 0x35, 0x36,
+	0x36, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a, 0x3a,
+	0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
+	0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f
+};
+
+static const int ym3812_note[13] = {
+	0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca,
+	0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287,
+	0x2ae
+};
+
+int MidiDriver_AdLib::open(bool isSCI0) {
+	int rate = _mixer->getOutputRate();
+
+	_stereo = STEREO;
+
+	debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1"));
+	_isSCI0 = isSCI0;
+
+	_opl = OPL::Config::create(isStereo() ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2);
+
+	// Try falling back to mono, thus plain OPL2 emualtor, when no Dual OPL2 is available.
+	if (!_opl && _stereo) {
+		_stereo = false;
+		_opl = OPL::Config::create(OPL::Config::kOpl2);
+	}
+
+	if (!_opl)
+		return -1;
+
+	_opl->init(rate);
+
+	setRegister(0xBD, 0);
+	setRegister(0x08, 0);
+	setRegister(0x01, 0x20);
+
+	MidiDriver_Emulated::open();
+
+	_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
+
+	return 0;
+}
+
+void MidiDriver_AdLib::close() {
+	_mixer->stopHandle(_mixerSoundHandle);
+
+	delete _opl;
+	delete[] _rhythmKeyMap;
+}
+
+void MidiDriver_AdLib::setVolume(byte volume) {
+	_masterVolume = volume;
+	renewNotes(-1, true);
+}
+
+// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php
+void MidiDriver_AdLib::send(uint32 b) {
+	byte command = b & 0xf0;
+	byte channel = b & 0xf;
+	byte op1 = (b >> 8) & 0xff;
+	byte op2 = (b >> 16) & 0xff;
+
+	switch (command) {
+	case 0x80:
+		noteOff(channel, op1);
+		break;
+	case 0x90:
+		noteOn(channel, op1, op2);
+		break;
+	case 0xe0:
+		_channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7);
+		renewNotes(channel, true);
+		break;
+	case 0xb0:
+		switch (op1) {
+		case 0x07:
+			_channels[channel].volume = op2 >> 1;
+			renewNotes(channel, true);
+			break;
+		case 0x0a:
+			_channels[channel].pan = op2;
+			renewNotes(channel, true);
+			break;
+		case 0x40:
+			_channels[channel].holdPedal = op2;
+			if (op2 == 0) {
+				for (int i = 0; i < kVoices; i++) {
+					if ((_voices[i].channel == channel) && _voices[i].isSustained)
+						voiceOff(i);
+				}
+			}
+			break;
+		case 0x4b:
+#ifndef ADLIB_DISABLE_VOICE_MAPPING
+			voiceMapping(channel, op2);
+#endif
+			break;
+		case 0x4e:
+			_channels[channel].enableVelocity = op2;
+			break;
+		case SCI_MIDI_CHANNEL_NOTES_OFF:
+			for (int i = 0; i < kVoices; i++)
+				if ((_voices[i].channel == channel) && (_voices[i].note != -1))
+					voiceOff(i);
+			break;
+		default:
+			//warning("ADLIB: ignoring MIDI command %02x %02x %02x", command | channel, op1, op2);
+			break;
+		}
+		break;
+	case 0xc0:
+		_channels[channel].patch = op1;
+		break;
+	// The original adlib driver from sierra ignores aftertouch completely, so should we
+	case 0xa0: // Polyphonic key pressure (aftertouch)
+	case 0xd0: // Channel pressure (aftertouch)
+		break;
+	case 0xf0:	// SysEx, ignore it
+		break;
+	default:
+		warning("ADLIB: Unknown event %02x", command);
+	}
+}
+
+void MidiDriver_AdLib::generateSamples(int16 *data, int len) {
+	if (isStereo())
+		len <<= 1;
+	_opl->readBuffer(data, len);
+
+	// Increase the age of the notes
+	for (int i = 0; i < kVoices; i++) {
+		if (_voices[i].note != -1)
+			_voices[i].age++;
+	}
+}
+
+void MidiDriver_AdLib::loadInstrument(const byte *ins) {
+	AdLibPatch patch;
+
+	// Set data for the operators
+	for (int i = 0; i < 2; i++) {
+		const byte *op = ins + i * 13;
+		patch.op[i].kbScaleLevel = op[0] & 0x3;
+		patch.op[i].frequencyMult = op[1] & 0xf;
+		patch.op[i].attackRate = op[3] & 0xf;
+		patch.op[i].sustainLevel = op[4] & 0xf;
+		patch.op[i].envelopeType = op[5];
+		patch.op[i].decayRate = op[6] & 0xf;
+		patch.op[i].releaseRate = op[7] & 0xf;
+		patch.op[i].totalLevel = op[8] & 0x3f;
+		patch.op[i].amplitudeMod = op[9];
+		patch.op[i].vibrato = op[10];
+		patch.op[i].kbScaleRate = op[11];
+	}
+	patch.op[0].waveForm = ins[26] & 0x3;
+	patch.op[1].waveForm = ins[27] & 0x3;
+
+	// Set data for the modulator
+	patch.mod.feedback = ins[2] & 0x7;
+	patch.mod.algorithm = !ins[12]; // Flag is inverted
+
+	_patches.push_back(patch);
+}
+
+void MidiDriver_AdLib::voiceMapping(int channel, int voices) {
+	int curVoices = 0;
+
+	for (int i = 0; i < kVoices; i++)
+		if (_voices[i].channel == channel)
+			curVoices++;
+
+	curVoices += _channels[channel].extraVoices;
+
+	if (curVoices < voices) {
+		debug(3, "ADLIB: assigning %i additional voices to channel %i", voices - curVoices, channel);
+		assignVoices(channel, voices - curVoices);
+	} else if (curVoices > voices) {
+		debug(3, "ADLIB: releasing %i voices from channel %i", curVoices - voices, channel);
+		releaseVoices(channel, curVoices - voices);
+		donateVoices();
+	}
+}
+
+void MidiDriver_AdLib::assignVoices(int channel, int voices) {
+	assert(voices > 0);
+
+	for (int i = 0; i < kVoices; i++)
+		if (_voices[i].channel == -1) {
+			_voices[i].channel = channel;
+			if (--voices == 0)
+				return;
+		}
+
+	_channels[channel].extraVoices += voices;
+}
+
+void MidiDriver_AdLib::releaseVoices(int channel, int voices) {
+	if (_channels[channel].extraVoices >= voices) {
+		_channels[channel].extraVoices -= voices;
+		return;
+	}
+
+	voices -= _channels[channel].extraVoices;
+	_channels[channel].extraVoices = 0;
+
+	for (int i = 0; i < kVoices; i++) {
+		if ((_voices[i].channel == channel) && (_voices[i].note == -1)) {
+			_voices[i].channel = -1;
+			if (--voices == 0)
+				return;
+		}
+	}
+
+	for (int i = 0; i < kVoices; i++) {
+		if (_voices[i].channel == channel) {
+			voiceOff(i);
+			_voices[i].channel = -1;
+			if (--voices == 0)
+				return;
+		}
+	}
+}
+
+void MidiDriver_AdLib::donateVoices() {
+	int freeVoices = 0;
+
+	for (int i = 0; i < kVoices; i++)
+		if (_voices[i].channel == -1)
+			freeVoices++;
+
+	if (freeVoices == 0)
+		return;
+
+	for (int i = 0; i < MIDI_CHANNELS; i++) {
+		if (_channels[i].extraVoices >= freeVoices) {
+			assignVoices(i, freeVoices);
+			_channels[i].extraVoices -= freeVoices;
+			return;
+		} else if (_channels[i].extraVoices > 0) {
+			assignVoices(i, _channels[i].extraVoices);
+			freeVoices -= _channels[i].extraVoices;
+			_channels[i].extraVoices = 0;
+		}
+	}
+}
+
+void MidiDriver_AdLib::renewNotes(int channel, bool key) {
+	for (int i = 0; i < kVoices; i++) {
+		// Update all notes playing this channel
+		if ((channel == -1) || (_voices[i].channel == channel)) {
+			if (_voices[i].note != -1)
+				setNote(i, _voices[i].note, key);
+		}
+	}
+}
+
+void MidiDriver_AdLib::noteOn(int channel, int note, int velocity) {
+	if (velocity == 0)
+		return noteOff(channel, note);
+
+	velocity >>= 1;
+
+	// Check for playable notes
+	if ((note < 12) || (note > 107))
+		return;
+
+	for (int i = 0; i < kVoices; i++) {
+		if ((_voices[i].channel == channel) && (_voices[i].note == note)) {
+			voiceOff(i);
+			voiceOn(i, note, velocity);
+			return;
+		}
+	}
+
+#ifdef ADLIB_DISABLE_VOICE_MAPPING
+	int voice = findVoiceBasic(channel);
+#else
+	int voice = findVoice(channel);
+#endif
+
+	if (voice == -1) {
+		debug(3, "ADLIB: failed to find free voice assigned to channel %i", channel);
+		return;
+	}
+
+	voiceOn(voice, note, velocity);
+}
+
+// FIXME: Temporary, see comment at top of file regarding ADLIB_DISABLE_VOICE_MAPPING
+int MidiDriver_AdLib::findVoiceBasic(int channel) {
+	int voice = -1;
+	int oldestVoice = -1;
+	int oldestAge = -1;
+
+	// Try to find a voice assigned to this channel that is free (round-robin)
+	for (int i = 0; i < kVoices; i++) {
+		int v = (_channels[channel].lastVoice + i + 1) % kVoices;
+
+		if (_voices[v].note == -1) {
+			voice = v;
+			break;
+		}
+
+		// We also keep track of the oldest note in case the search fails
+		if (_voices[v].age > oldestAge) {
+			oldestAge = _voices[v].age;
+			oldestVoice = v;
+		}
+	}
+
+	if (voice == -1) {
+		if (oldestVoice != -1) {
+			voiceOff(oldestVoice);
+			voice = oldestVoice;
+		} else {
+			return -1;
+		}
+	}
+
+	_voices[voice].channel = channel;
+	_channels[channel].lastVoice = voice;
+	return voice;
+}
+
+int MidiDriver_AdLib::findVoice(int channel) {
+	int voice = -1;
+	int oldestVoice = -1;
+	uint32 oldestAge = 0;
+
+	// Try to find a voice assigned to this channel that is free (round-robin)
+	for (int i = 0; i < kVoices; i++) {
+		int v = (_channels[channel].lastVoice + i + 1) % kVoices;
+
+		if (_voices[v].channel == channel) {
+			if (_voices[v].note == -1) {
+				voice = v;
+				break;
+			}
+
+			// We also keep track of the oldest note in case the search fails
+			// Notes started in the current time slice will not be selected
+			if (_voices[v].age > oldestAge) {
+				oldestAge = _voices[v].age;
+				oldestVoice = v;
+			}
+		}
+	}
+
+	if (voice == -1) {
+		if (oldestVoice != -1) {
+			voiceOff(oldestVoice);
+			voice = oldestVoice;
+		} else {
+			return -1;
+		}
+	}
+
+	_channels[channel].lastVoice = voice;
+	return voice;
+}
+
+void MidiDriver_AdLib::noteOff(int channel, int note) {
+	for (int i = 0; i < kVoices; i++) {
+		if ((_voices[i].channel == channel) && (_voices[i].note == note)) {
+			if (_channels[channel].holdPedal)
+				_voices[i].isSustained = true;
+			else
+				voiceOff(i);
+			return;
+		}
+	}
+}
+
+void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) {
+	int channel = _voices[voice].channel;
+	int patch;
+
+	_voices[voice].age = 0;
+
+	if ((channel == 9) && _rhythmKeyMap) {
+		patch = CLIP(note, 27, 88) + 101;
+	} else {
+		patch = _channels[channel].patch;
+	}
+
+	// Set patch if different from current patch
+	if ((patch != _voices[voice].patch) && _playSwitch)
+		setPatch(voice, patch);
+
+	_voices[voice].velocity = velocity;
+	setNote(voice, note, true);
+}
+
+void MidiDriver_AdLib::voiceOff(int voice) {
+	_voices[voice].isSustained = false;
+	setNote(voice, _voices[voice].note, 0);
+	_voices[voice].note = -1;
+	_voices[voice].age = 0;
+}
+
+void MidiDriver_AdLib::setNote(int voice, int note, bool key) {
+	int channel = _voices[voice].channel;
+	int n, fre, oct;
+	float delta;
+	int bend = _channels[channel].pitchWheel;
+
+	if ((channel == 9) && _rhythmKeyMap) {
+		note = _rhythmKeyMap[CLIP(note, 27, 88) - 27];
+	}
+
+	_voices[voice].note = note;
+
+	delta = 0;
+
+	n = note % 12;
+
+	if (bend < 8192)
+		bend = 8192 - bend;
+	delta = (float)pow(2.0, (bend % 8192) / 8192.0);
+
+	if (bend > 8192)
+		fre = (int)(ym3812_note[n] * delta);
+	else
+		fre = (int)(ym3812_note[n] / delta);
+
+	oct = note / 12 - 1;
+
+	if (oct < 0)
+		oct = 0;
+
+	if (oct > 7)
+		oct = 7;
+
+	setRegister(0xA0 + voice, fre & 0xff);
+	setRegister(0xB0 + voice, (key << 5) | (oct << 2) | (fre >> 8));
+
+	setVelocity(voice);
+}
+
+void MidiDriver_AdLib::setVelocity(int voice) {
+	AdLibPatch &patch = _patches[_voices[voice].patch];
+	int pan = _channels[_voices[voice].channel].pan;
+	setVelocityReg(registerOffset[voice] + 3, calcVelocity(voice, 1), patch.op[1].kbScaleLevel, pan);
+
+	// In AM mode we need to set the level for both operators
+	if (_patches[_voices[voice].patch].mod.algorithm == 1)
+		setVelocityReg(registerOffset[voice], calcVelocity(voice, 0), patch.op[0].kbScaleLevel, pan);
+}
+
+int MidiDriver_AdLib::calcVelocity(int voice, int op) {
+	if (_isSCI0) {
+		int velocity = _masterVolume;
+
+		if (velocity > 0)
+			velocity += 3;
+
+		if (velocity > 15)
+			velocity = 15;
+
+		int insVelocity;
+		if (_channels[_voices[voice].channel].enableVelocity)
+			insVelocity = _voices[voice].velocity;
+		else
+			insVelocity = 63 - _patches[_voices[voice].patch].op[op].totalLevel;
+
+		// Note: Later SCI0 has a static table that is close to this formula, but not exactly the same.
+		// Early SCI0 does (velocity * (insVelocity / 15))
+		return velocity * insVelocity / 15;
+	} else {
+		AdLibOperator &oper = _patches[_voices[voice].patch].op[op];
+		int velocity = _channels[_voices[voice].channel].volume + 1;
+		velocity = velocity * (velocityMap1[_voices[voice].velocity] + 1) / 64;
+		velocity = velocity * (_masterVolume + 1) / 16;
+
+		if (--velocity < 0)
+			velocity = 0;
+
+		return velocityMap2[velocity] * (63 - oper.totalLevel) / 63;
+	}
+}
+
+void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan) {
+	if (!_playSwitch)
+		velocity = 0;
+
+	if (isStereo()) {
+		int velLeft = velocity;
+		int velRight = velocity;
+
+		if (pan > 0x40)
+			velLeft = velLeft * (0x7f - pan) / 0x3f;
+		else if (pan < 0x40)
+			velRight = velRight * pan / 0x40;
+
+		setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velLeft), kLeftChannel);
+		setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velRight), kRightChannel);
+	} else {
+		setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velocity));
+	}
+}
+
+void MidiDriver_AdLib::setPatch(int voice, int patch) {
+	if ((patch < 0) || ((uint)patch >= _patches.size())) {
+		warning("ADLIB: Invalid patch %i requested", patch);
+		patch = 0;
+	}
+
+	_voices[voice].patch = patch;
+	AdLibModulator &mod = _patches[patch].mod;
+
+	// Set the common settings for both operators
+	setOperator(registerOffset[voice], _patches[patch].op[0]);
+	setOperator(registerOffset[voice] + 3, _patches[patch].op[1]);
+
+	// Set the additional settings for the modulator
+	byte algorithm = mod.algorithm ? 1 : 0;
+	setRegister(0xC0 + voice, (mod.feedback << 1) | algorithm);
+}
+
+void MidiDriver_AdLib::setOperator(int reg, AdLibOperator &op) {
+	setRegister(0x40 + reg, (op.kbScaleLevel << 6) | op.totalLevel);
+	setRegister(0x60 + reg, (op.attackRate << 4) | op.decayRate);
+	setRegister(0x80 + reg, (op.sustainLevel << 4) | op.releaseRate);
+	setRegister(0x20 + reg, (op.amplitudeMod << 7) | (op.vibrato << 6)
+				| (op.envelopeType << 5) | (op.kbScaleRate << 4) | op.frequencyMult);
+	setRegister(0xE0 + reg, op.waveForm);
+}
+
+void MidiDriver_AdLib::setRegister(int reg, int value, int channels) {
+	if (channels & kLeftChannel) {
+		_opl->write(0x220, reg);
+		_opl->write(0x221, value);
+	}
+
+	if (isStereo()) {
+		if (channels & kRightChannel) {
+			_opl->write(0x222, reg);
+			_opl->write(0x223, value);
+		}
+	}
+}
+
+void MidiDriver_AdLib::playSwitch(bool play) {
+	_playSwitch = play;
+	renewNotes(-1, play);
+}
+
+bool MidiDriver_AdLib::loadResource(const byte *data, uint size) {
+	if ((size != 1344) && (size != 2690) && (size != 5382)) {
+		warning("ADLIB: Unsupported patch format (%i bytes)", size);
+		return false;
+	}
+
+	for (int i = 0; i < 48; i++)
+		loadInstrument(data + (28 * i));
+
+	if (size == 2690) {
+		for (int i = 48; i < 96; i++)
+			loadInstrument(data + 2 + (28 * i));
+	} else if (size == 5382) {
+		for (int i = 48; i < 190; i++)
+			loadInstrument(data + (28 * i));
+		_rhythmKeyMap = new byte[kRhythmKeys];
+		memcpy(_rhythmKeyMap, data + 5320, kRhythmKeys);
+	}
+
+	return true;
+}
+
+uint32 MidiDriver_AdLib::property(int prop, uint32 param) {
+	switch(prop) {
+	case MIDI_PROP_MASTER_VOLUME:
+		if (param != 0xffff)
+			_masterVolume = param;
+		return _masterVolume;
+	default:
+		break;
+	}
+	return 0;
+}
+
+
+int MidiPlayer_AdLib::open(ResourceManager *resMan) {
+	assert(resMan != NULL);
+
+	// Load up the patch.003 file, parse out the instruments
+	Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 3), 0);
+	bool ok = false;
+
+	if (res) {
+		ok = static_cast<MidiDriver_AdLib *>(_driver)->loadResource(res->data, res->size);
+	} else {
+		// Early SCI0 games have the sound bank embedded in the adlib driver
+
+		Common::File f;
+
+		if (f.open("ADL.DRV")) {
+			int size = f.size();
+			const uint patchSize = 1344;
+
+			if ((size == 5684) || (size == 5720) || (size == 5727)) {
+				byte *buf = new byte[patchSize];
+
+				if (f.seek(0x45a) && (f.read(buf, patchSize) == patchSize))
+					ok = static_cast<MidiDriver_AdLib *>(_driver)->loadResource(buf, patchSize);
+
+				delete[] buf;
+			}
+		}
+	}
+
+	if (!ok) {
+		warning("ADLIB: Failed to load patch.003");
+		return -1;
+	}
+
+	return static_cast<MidiDriver_AdLib *>(_driver)->open(getSciVersion() <= SCI_VERSION_0_LATE);
+}
+
+void MidiPlayer_AdLib::close() {
+	if (_driver) {
+		_driver->close();
+	}
+}
+
+byte MidiPlayer_AdLib::getPlayId(SciVersion soundVersion) {
+	switch (soundVersion) {
+	case SCI_VERSION_0_EARLY:
+		return 0x01;
+	case SCI_VERSION_0_LATE:
+		return 0x04;
+	default:
+		return 0x00;
+	}
+}
+
+MidiPlayer *MidiPlayer_AdLib_create() {
+	return new MidiPlayer_AdLib();
+}
+
+} // End of namespace Sci


Property changes on: scummvm/trunk/engines/sci/sound/drivers/adlib.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Copied: scummvm/trunk/engines/sci/sound/drivers/amiga.cpp (from rev 47417, scummvm/trunk/engines/sci/sound/softseq/amiga.cpp)
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/amiga.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/amiga.cpp	2010-01-21 16:27:29 UTC (rev 47418)
@@ -0,0 +1,676 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sound/softsynth/emumidi.h"
+#include "sci/sound/drivers/mididriver.h"
+
+#include "common/file.h"
+#include "common/frac.h"
+#include "common/util.h"
+
+namespace Sci {
+
+/* #define DEBUG */
+
+// Frequencies for every note
+// FIXME Store only one octave
+static const int freq_table[] = {
+	58, 62, 65, 69, 73, 78, 82, 87,
+	92, 98, 104, 110, 117, 124, 131, 139,
+	147, 156, 165, 175, 185, 196, 208, 220,
+	234, 248, 262, 278, 294, 312, 331, 350,
+	371, 393, 417, 441, 468, 496, 525, 556,
+	589, 625, 662, 701, 743, 787, 834, 883,
+	936, 992, 1051, 1113, 1179, 1250, 1324, 1403,
+	1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227,
+	2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535,
+	3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612,
+	5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908,
+	9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142,
+	14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449,
+	23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635,
+	37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568,
+	59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796
+};
+
+class MidiDriver_Amiga : public MidiDriver_Emulated {
+public:
+	enum {
+		kVoices = 4
+	};
+
+	MidiDriver_Amiga(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { }
+	virtual ~MidiDriver_Amiga() { }
+
+	// MidiDriver
+	int open();
+	void close();
+	void send(uint32 b);
+	MidiChannel *allocateChannel() { return NULL; }
+	MidiChannel *getPercussionChannel() { return NULL; }
+
+	// AudioStream
+	bool isStereo() const { return true; }
+	int getRate() const { return _mixer->getOutputRate(); }
+
+	// MidiDriver_Emulated
+	void generateSamples(int16 *buf, int len);
+
+	void setVolume(byte volume);
+	void playSwitch(bool play);
+	virtual uint32 property(int prop, uint32 param);
+
+private:
+	enum {
+		kModeLoop = 1 << 0, // Instrument looping flag
+		kModePitch = 1 << 1 // Instrument pitch changes flag
+	};
+
+	enum {
+		kChannels = 10,
+		kBaseFreq = 20000, // Samplerate of the instrument bank
+		kPanLeft = 91,
+		kPanRight = 164
+	};
+
+	struct Channel {
+		int instrument;
+		int volume;
+		int pan;
+	};
+
+	struct Envelope {
+		int length; // Phase period length in samples
+		int delta; // Velocity delta per period
+		int target; // Target velocity
+	};
+
+	struct Voice {
+		int instrument;
+		int note;
+		int note_velocity;
+		int velocity;
+		int envelope;
+		int envelope_samples; // Number of samples till next envelope event
+		int decay;
+		int looping;
+		int hw_channel;
+		frac_t offset;
+		frac_t rate;
+	};
+
+	struct Instrument {
+		char name[30];
+		int mode;
+		int size; // Size of non-looping part in bytes
+		int loop_size; // Starting offset and size of loop in bytes
+		int transpose; // Transpose value in semitones
+		Envelope envelope[4]; // Envelope
+		int8 *samples;
+		int8 *loop;
+	};
+
+	struct Bank {
+		char name[30];
+		uint size;
+		Instrument *instruments[256];
+	};
+
+	bool _playSwitch;
+	int _masterVolume;
+	int _frequency;
+	Envelope _envDecay;
+	Bank _bank; // Instrument bank
+
+	Channel _channels[MIDI_CHANNELS];
+	/* Internal channels */
+	Voice _voices[kChannels];
+
+	void setEnvelope(Voice *channel, Envelope *envelope, int phase);
+	int interpolate(int8 *samples, frac_t offset);
+	void playInstrument(int16 *dest, Voice *channel, int count);
+	void changeInstrument(int channel, int instrument);
+	void stopChannel(int ch);
+	void stopNote(int ch, int note);
+	void startNote(int ch, int note, int velocity);
+	Instrument *readInstrument(Common::File &file, int *id);
+};
+
+void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase) {
+	channel->envelope = phase;
+	channel->envelope_samples = envelope[phase].length;
+
+	if (phase == 0)
+		channel->velocity = channel->note_velocity / 2;
+	else
+		channel->velocity = envelope[phase - 1].target;
+}
+
+int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset) {
+	int x = fracToInt(offset);
+	int diff = (samples[x + 1] - samples[x]) << 8;
+
+	return (samples[x] << 8) + fracToInt(diff * (offset & FRAC_LO_MASK));
+}
+
+void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
+	int index = 0;
+	int vol = _channels[channel->hw_channel].volume;
+	Instrument *instrument = _bank.instruments[channel->instrument];
+
+	while (1) {
+		/* Available source samples until end of segment */
+		frac_t lin_avail;
+		int seg_end, rem, i, amount;
+		int8 *samples;
+
+		if (channel->looping) {
+			samples = instrument->loop;
+			seg_end = instrument->loop_size;
+		} else {
+			samples = instrument->samples;
+			seg_end = instrument->size;
+		}
+
+		lin_avail = intToFrac(seg_end) - channel->offset;
+
+		rem = count - index;
+
+		/* Amount of destination samples that we will compute this iteration */
+		amount = lin_avail / channel->rate;
+
+		if (lin_avail % channel->rate)
+			amount++;
+
+		if (amount > rem)
+			amount = rem;
+
+		/* Stop at next envelope event */
+		if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples))
+			amount = channel->envelope_samples;
+
+		for (i = 0; i < amount; i++) {
+			dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
+			channel->offset += channel->rate;
+		}
+
+		if (channel->envelope_samples != -1)
+			channel->envelope_samples -= amount;
+
+		if (channel->envelope_samples == 0) {
+			Envelope *envelope;
+			int delta, target, velocity;
+
+			if (channel->decay)
+				envelope = &_envDecay;
+			else
+				envelope = &instrument->envelope[channel->envelope];
+
+			delta = envelope->delta;
+			target = envelope->target;
+			velocity = channel->velocity - envelope->delta;
+
+			/* Check whether we have reached the velocity target for the current phase */
+			if ((delta >= 0 && velocity <= target) || (delta < 0 && velocity >= target)) {
+				channel->velocity = target;
+
+				/* Stop note after velocity has dropped to 0 */
+				if (target == 0) {
+					channel->note = -1;
+					break;
+				} else
+					switch (channel->envelope) {
+					case 0:
+					case 2:
+						/* Go to next phase */
+						setEnvelope(channel, instrument->envelope, channel->envelope + 1);
+						break;
+					case 1:
+					case 3:
+						/* Stop envelope */
+						channel->envelope_samples = -1;
+						break;
+					}
+			} else {
+				/* We haven't reached the target yet */
+				channel->envelope_samples = envelope->length;
+				channel->velocity = velocity;
+			}
+		}
+
+		if (index == count)
+			break;
+
+		if (fracToInt(channel->offset) >= seg_end) {
+			if (instrument->mode & kModeLoop) {
+				/* Loop the samples */
+				channel->offset -= intToFrac(seg_end);
+				channel->looping = 1;
+			} else {
+				/* All samples have been played */
+				channel->note = -1;
+				break;
+			}
+		}
+	}
+}
+
+void MidiDriver_Amiga::changeInstrument(int channel, int instrument) {
+#ifdef DEBUG
+	if (_bank.instruments[instrument])
+		printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument);
+	else
+		warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel);
+#endif
+	_channels[channel].instrument = instrument;
+}
+
+void MidiDriver_Amiga::stopChannel(int ch) {
+	int i;
+
+	/* Start decay phase for note on this hw channel, if any */
+	for (i = 0; i < kChannels; i++)
+		if (_voices[i].note != -1 && _voices[i].hw_channel == ch && !_voices[i].decay) {
+			/* Trigger fast decay envelope */
+			_voices[i].decay = 1;
+			_voices[i].envelope_samples = _envDecay.length;
+			break;
+		}
+}
+
+void MidiDriver_Amiga::stopNote(int ch, int note) {
+	int channel;
+	Instrument *instrument;
+
+	for (channel = 0; channel < kChannels; channel++)
+		if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay)
+			break;
+
+	if (channel == kChannels) {
+#ifdef DEBUG
+		warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch);
+#endif
+		return;
+	}
+
+	instrument = _bank.instruments[_voices[channel].instrument];
+
+	/* Start the envelope phases for note-off if looping is on and envelope is enabled */
+	if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
+		setEnvelope(&_voices[channel], instrument->envelope, 2);
+}
+
+void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
+	Instrument *instrument;
+	int channel;
+
+	if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) {
+		warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch);
+		return;
+	}
+
+	instrument = _bank.instruments[_channels[ch].instrument];
+
+	if (!instrument) {
+		warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument);
+		return;
+	}
+
+	for (channel = 0; channel < kChannels; channel++)
+		if (_voices[channel].note == -1)
+			break;
+
+	if (channel == kChannels) {
+		warning("[sfx:seq:amiga] could not find a free channel");
+		return;
+	}
+
+	stopChannel(ch);
+
+	if (instrument->mode & kModePitch) {
+		int fnote = note + instrument->transpose;
+
+		if (fnote < 0 || fnote > 127) {
+			warning("[sfx:seq:amiga] illegal note %i\n", fnote);
+			return;
+		}
+
+		/* Compute rate for note */
+		_voices[channel].rate = doubleToFrac(freq_table[fnote] / (double) _frequency);
+	} else
+		_voices[channel].rate = doubleToFrac(kBaseFreq / (double) _frequency);
+
+	_voices[channel].instrument = _channels[ch].instrument;
+	_voices[channel].note = note;
+	_voices[channel].note_velocity = velocity;
+
+	if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
+		setEnvelope(&_voices[channel], instrument->envelope, 0);
+	else {
+		_voices[channel].velocity = 64;
+		_voices[channel].envelope_samples = -1;
+	}
+
+	_voices[channel].offset = 0;
+	_voices[channel].hw_channel = ch;
+	_voices[channel].decay = 0;
+	_voices[channel].looping = 0;
+}
+
+MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &file, int *id) {
+	Instrument *instrument;
+	byte header[61];
+	int size;
+	int seg_size[3];
+	int loop_offset;
+	int i;
+
+	if (file.read(header, 61) < 61) {
+		warning("[sfx:seq:amiga] failed to read instrument header");
+		return NULL;
+	}
+
+	instrument = new Instrument;
+
+	seg_size[0] = READ_BE_UINT16(header + 35) * 2;
+	seg_size[1] = READ_BE_UINT16(header + 41) * 2;
+	seg_size[2] = READ_BE_UINT16(header + 47) * 2;
+
+	instrument->mode = header[33];
+	instrument->transpose = (int8) header[34];
+	for (i = 0; i < 4; i++) {
+		int length = (int8) header[49 + i];
+
+		if (length == 0 && i > 0)
+			length = 256;
+
+		instrument->envelope[i].length = length * _frequency / 60;
+		instrument->envelope[i].delta = (int8)header[53 + i];
+		instrument->envelope[i].target = header[57 + i];
+	}
+	/* Final target must be 0 */
+	instrument->envelope[3].target = 0;
+
+	loop_offset = READ_BE_UINT32(header + 37) & ~1;
+	size = seg_size[0] + seg_size[1] + seg_size[2];
+
+	*id = READ_BE_UINT16(header);
+
+	strncpy(instrument->name, (char *) header + 2, 29);
+	instrument->name[29] = 0;
+#ifdef DEBUG
+	printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
+	          *id, instrument->name, size);
+	printf("                Mode: %02x\n", instrument->mode);
+	printf("                Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off");
+	printf("                Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off");
+	printf("                Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
+	printf("                Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
+#endif
+	instrument->samples = (int8 *) malloc(size + 1);
+	if (file.read(instrument->samples, size) < (unsigned int)size) {
+		warning("[sfx:seq:amiga] failed to read instrument samples");
+		free(instrument->samples);
+		delete instrument;
+		return NULL;
+	}
+
+	if (instrument->mode & kModeLoop) {
+		if (loop_offset + seg_size[1] > size) {
+#ifdef DEBUG
+			warning("[sfx:seq:amiga] looping samples extend %i bytes past end of sample block",
+			          loop_offset + seg_size[1] - size);
+#endif
+			seg_size[1] = size - loop_offset;
+		}
+
+		if (seg_size[1] < 0) {
+			warning("[sfx:seq:amiga] invalid looping point");
+			free(instrument->samples);
+			delete instrument;
+			return NULL;
+		}
+
+		instrument->size = seg_size[0];
+		instrument->loop_size = seg_size[1];
+
+		instrument->loop = (int8*)malloc(instrument->loop_size + 1);
+		memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size);
+
+		instrument->samples[instrument->size] = instrument->loop[0];
+		instrument->loop[instrument->loop_size] = instrument->loop[0];
+	} else {
+		instrument->loop = NULL;
+		instrument->size = size;
+		instrument->samples[instrument->size] = 0;
+	}
+
+	return instrument;
+}
+
+uint32 MidiDriver_Amiga::property(int prop, uint32 param) {
+	switch(prop) {
+	case MIDI_PROP_MASTER_VOLUME:
+		if (param != 0xffff)
+			_masterVolume = param;
+		return _masterVolume;
+	default:
+		break;
+	}
+	return 0;
+}
+
+int MidiDriver_Amiga::open() {
+	_frequency = _mixer->getOutputRate();
+	_envDecay.length = _frequency / (32 * 64);
+	_envDecay.delta = 1;
+	_envDecay.target = 0;
+
+	Common::File file;
+	byte header[40];
+
+	if (!file.open("bank.001")) {
+		warning("[sfx:seq:amiga] file bank.001 not found");
+		return Common::kUnknownError;
+	}
+
+	if (file.read(header, 40) < 40) {
+		warning("[sfx:seq:amiga] failed to read header of file bank.001");
+		return Common::kUnknownError;
+	}
+
+	for (uint i = 0; i < 256; i++)
+		_bank.instruments[i] = NULL;
+
+	for (uint i = 0; i < kChannels; i++) {
+		_voices[i].note = -1;
+		_voices[i].hw_channel = 0;
+	}
+
+	for (uint i = 0; i < MIDI_CHANNELS; i++) {
+		_channels[i].instrument = -1;
+		_channels[i].volume = 127;
+		_channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? kPanLeft : kPanRight);
+	}
+
+	_bank.size = READ_BE_UINT16(header + 38);
+	strncpy(_bank.name, (char *) header + 8, 29);
+	_bank.name[29] = 0;
+#ifdef DEBUG
+	printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
+#endif
+
+	for (uint i = 0; i < _bank.size; i++) {
+		int id;
+		Instrument *instrument = readInstrument(file, &id);
+
+		if (!instrument) {
+			warning("[sfx:seq:amiga] failed to read bank.001");
+			return Common::kUnknownError;
+		}
+
+		if (id < 0 || id > 255) {
+			warning("[sfx:seq:amiga] Error: instrument ID out of bounds");
+			return Common::kUnknownError;
+		}
+
+		_bank.instruments[id] = instrument;
+	}
+
+	MidiDriver_Emulated::open();
+
+	_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
+
+	return Common::kNoError;
+}
+
+void MidiDriver_Amiga::close() {
+	_mixer->stopHandle(_mixerSoundHandle);
+
+	for (uint i = 0; i < _bank.size; i++) {
+		if (_bank.instruments[i]) {
+			if (_bank.instruments[i]->loop)
+				free(_bank.instruments[i]->loop);
+			free(_bank.instruments[i]->samples);
+			delete _bank.instruments[i];
+		}
+	}
+}
+
+void MidiDriver_Amiga::playSwitch(bool play) {
+	_playSwitch = play;
+}
+
+void MidiDriver_Amiga::setVolume(byte volume_) {
+	_masterVolume = volume_;
+}
+
+void MidiDriver_Amiga::send(uint32 b) {
+	byte command = b & 0xf0;
+	byte channel = b & 0xf;
+	byte op1 = (b >> 8) & 0xff;
+	byte op2 = (b >> 16) & 0xff;
+
+	switch (command) {
+	case 0x80:
+		stopNote(channel, op1);
+		break;
+	case 0x90:
+		if (op2 > 0)
+			startNote(channel, op1, op2);
+		else
+			stopNote(channel, op1);
+		break;
+	case 0xb0:
+		switch (op1) {
+		case 0x07:
+			_channels[channel].volume = op2;
+			break;
+		case 0x0a:
+#ifdef DEBUG
+			warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel);
+#endif
+			break;
+		case 0x7b:
+			stopChannel(channel);
+			break;
+		default:
+			warning("[sfx:seq:amiga] unknown control event 0x%02x", op1);
+		}
+		break;
+	case 0xc0:
+		changeInstrument(channel, op1);
+		break;
+	default:
+		warning("[sfx:seq:amiga] unknown event %02x", command);
+	}
+}
+
+void MidiDriver_Amiga::generateSamples(int16 *data, int len) {
+	if (len == 0)
+		return;
+
+	int16 *buffers = (int16*)malloc(len * 2 * kChannels);
+
+	memset(buffers, 0, len * 2 * kChannels);
+
+	/* Generate samples for all notes */
+	for (int i = 0; i < kChannels; i++)
+		if (_voices[i].note >= 0)
+			playInstrument(buffers + i * len, &_voices[i], len);
+
+	if (isStereo()) {
+		for (int j = 0; j < len; j++) {
+			int mixedl = 0, mixedr = 0;
+
+			/* Mix and pan */
+			for (int i = 0; i < kChannels; i++) {
+				mixedl += buffers[i * len + j] * (256 - _channels[_voices[i].hw_channel].pan);
+				mixedr += buffers[i * len + j] * _channels[_voices[i].hw_channel].pan;
+			}
+
+			/* Adjust volume */
+			data[2 * j] = mixedl * _masterVolume >> 13;
+			data[2 * j + 1] = mixedr * _masterVolume >> 13;
+		}
+	} else {
+		for (int j = 0; j < len; j++) {
+			int mixed = 0;
+
+			/* Mix */
+			for (int i = 0; i < kChannels; i++)
+				mixed += buffers[i * len + j];
+
+			/* Adjust volume */
+			data[j] = mixed * _masterVolume >> 6;
+		}
+	}
+
+	free(buffers);
+}
+
+class MidiPlayer_Amiga : public MidiPlayer {
+public:
+	MidiPlayer_Amiga() { _driver = new MidiDriver_Amiga(g_system->getMixer()); }
+	byte getPlayId(SciVersion soundVersion);
+	int getPolyphony() const { return MidiDriver_Amiga::kVoices; }
+	bool hasRhythmChannel() const { return false; }
+	void setVolume(byte volume) { static_cast<MidiDriver_Amiga *>(_driver)->setVolume(volume); }
+	void playSwitch(bool play) { static_cast<MidiDriver_Amiga *>(_driver)->playSwitch(play); }
+	void loadInstrument(int idx, byte *data);
+};
+
+MidiPlayer *MidiPlayer_Amiga_create() {
+	return new MidiPlayer_Amiga();
+}
+
+byte MidiPlayer_Amiga::getPlayId(SciVersion soundVersion) {
+	if (soundVersion != SCI_VERSION_0_LATE)
+		error("Amiga sound support not available for this SCI version");
+
+	return 0x40;
+}
+
+} // End of namespace Sci


Property changes on: scummvm/trunk/engines/sci/sound/drivers/amiga.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Copied: scummvm/trunk/engines/sci/sound/drivers/map-mt32-to-gm.h (from rev 47417, scummvm/trunk/engines/sci/sound/softseq/map-mt32-to-gm.h)
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/map-mt32-to-gm.h	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/map-mt32-to-gm.h	2010-01-21 16:27:29 UTC (rev 47418)
@@ -0,0 +1,552 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+namespace Sci {
+
+/* Patch not mapped */
+#define MIDI_UNMAPPED 0xff
+/* Patch mapped to rhythm key */
+#define MIDI_MAPPED_TO_RHYTHM 0xfe
+
+struct Mt32ToGmMap {
+	const char *name;
+	uint8 gmInstr;
+	uint8 gmRhythmKey;
+};
+
+static const char *GmInstrumentNames[] = {
+	/*000*/  "Acoustic Grand Piano",
+	/*001*/  "Bright Acoustic Piano",
+	/*002*/  "Electric Grand Piano",
+	/*003*/  "Honky-tonk Piano",
+	/*004*/  "Electric Piano 1",
+	/*005*/  "Electric Piano 2",
+	/*006*/  "Harpsichord",
+	/*007*/  "Clavinet",
+	/*008*/  "Celesta",
+	/*009*/  "Glockenspiel",
+	/*010*/  "Music Box",
+	/*011*/  "Vibraphone",
+	/*012*/  "Marimba",
+	/*013*/  "Xylophone",
+	/*014*/  "Tubular Bells",
+	/*015*/  "Dulcimer",
+	/*016*/  "Drawbar Organ",
+	/*017*/  "Percussive Organ",
+	/*018*/  "Rock Organ",
+	/*019*/  "Church Organ",
+	/*020*/  "Reed Organ",
+	/*021*/  "Accordion",
+	/*022*/  "Harmonica",
+	/*023*/  "Tango Accordion",
+	/*024*/  "Acoustic Guitar (nylon)",
+	/*025*/  "Acoustic Guitar (steel)",
+	/*026*/  "Electric Guitar (jazz)",
+	/*027*/  "Electric Guitar (clean)",
+	/*028*/  "Electric Guitar (muted)",
+	/*029*/  "Overdriven Guitar",
+	/*030*/  "Distortion Guitar",
+	/*031*/  "Guitar Harmonics",
+	/*032*/  "Acoustic Bass",
+	/*033*/  "Electric Bass (finger)",
+	/*034*/  "Electric Bass (pick)",
+	/*035*/  "Fretless Bass",
+	/*036*/  "Slap Bass 1",
+	/*037*/  "Slap Bass 2",
+	/*038*/  "Synth Bass 1",
+	/*039*/  "Synth Bass 2",
+	/*040*/  "Violin",
+	/*041*/  "Viola",
+	/*042*/  "Cello",
+	/*043*/  "Contrabass",
+	/*044*/  "Tremolo Strings",
+	/*045*/  "Pizzicato Strings",
+	/*046*/  "Orchestral Harp",
+	/*047*/  "Timpani",
+	/*048*/  "String Ensemble 1",
+	/*049*/  "String Ensemble 2",
+	/*050*/  "SynthStrings 1",
+	/*051*/  "SynthStrings 2",
+	/*052*/  "Choir Aahs",
+	/*053*/  "Voice Oohs",
+	/*054*/  "Synth Voice",
+	/*055*/  "Orchestra Hit",
+	/*056*/  "Trumpet",
+	/*057*/  "Trombone",
+	/*058*/  "Tuba",
+	/*059*/  "Muted Trumpet",
+	/*060*/  "French Horn",
+	/*061*/  "Brass Section",
+	/*062*/  "SynthBrass 1",
+	/*063*/  "SynthBrass 2",
+	/*064*/  "Soprano Sax",
+	/*065*/  "Alto Sax",
+	/*066*/  "Tenor Sax",
+	/*067*/  "Baritone Sax",
+	/*068*/  "Oboe",
+	/*069*/  "English Horn",
+	/*070*/  "Bassoon",
+	/*071*/  "Clarinet",
+	/*072*/  "Piccolo",
+	/*073*/  "Flute",
+	/*074*/  "Recorder",
+	/*075*/  "Pan Flute",
+	/*076*/  "Blown Bottle",
+	/*077*/  "Shakuhachi",
+	/*078*/  "Whistle",
+	/*079*/  "Ocarina",
+	/*080*/  "Lead 1 (square)",
+	/*081*/  "Lead 2 (sawtooth)",
+	/*082*/  "Lead 3 (calliope)",
+	/*083*/  "Lead 4 (chiff)",
+	/*084*/  "Lead 5 (charang)",
+	/*085*/  "Lead 6 (voice)",
+	/*086*/  "Lead 7 (fifths)",
+	/*087*/  "Lead 8 (bass+lead)",
+	/*088*/  "Pad 1 (new age)",
+	/*089*/  "Pad 2 (warm)",
+	/*090*/  "Pad 3 (polysynth)",
+	/*091*/  "Pad 4 (choir)",
+	/*092*/  "Pad 5 (bowed)",
+	/*093*/  "Pad 6 (metallic)",
+	/*094*/  "Pad 7 (halo)",
+	/*095*/  "Pad 8 (sweep)",
+	/*096*/  "FX 1 (rain)",
+	/*097*/  "FX 2 (soundtrack)",
+	/*098*/  "FX 3 (crystal)",
+	/*099*/  "FX 4 (atmosphere)",
+	/*100*/  "FX 5 (brightness)",
+	/*101*/  "FX 6 (goblins)",
+	/*102*/  "FX 7 (echoes)",
+	/*103*/  "FX 8 (sci-fi)",
+	/*104*/  "Sitar",
+	/*105*/  "Banjo",
+	/*106*/  "Shamisen",
+	/*107*/  "Koto",
+	/*108*/  "Kalimba",
+	/*109*/  "Bag pipe",
+	/*110*/  "Fiddle",
+	/*111*/  "Shannai",
+	/*112*/  "Tinkle Bell",
+	/*113*/  "Agogo",
+	/*114*/  "Steel Drums",
+	/*115*/  "Woodblock",
+	/*116*/  "Taiko Drum",
+	/*117*/  "Melodic Tom",
+	/*118*/  "Synth Drum",
+	/*119*/  "Reverse Cymbal",
+	/*120*/  "Guitar Fret Noise",
+	/*121*/  "Breath Noise",
+	/*122*/  "Seashore",
+	/*123*/  "Bird Tweet",
+	/*124*/  "Telephone Ring",
+	/*125*/  "Helicopter",
+	/*126*/  "Applause",
+	/*127*/  "Gunshot"
+};
+
+/* The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI */
+static const char *GmPercussionNames[] = {
+	/*00*/  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	/*10*/  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	/*20*/  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	/*30*/  0, 0, 0, 0,
+	/* The preceeding percussions are not covered by the GM standard */
+	/*34*/  "Acoustic Bass Drum",
+	/*35*/  "Bass Drum 1",
+	/*36*/  "Side Stick",
+	/*37*/  "Acoustic Snare",
+	/*38*/  "Hand Clap",
+	/*39*/  "Electric Snare",
+	/*40*/  "Low Floor Tom",
+	/*41*/  "Closed Hi-Hat",
+	/*42*/  "High Floor Tom",
+	/*43*/  "Pedal Hi-Hat",
+	/*44*/  "Low Tom",
+	/*45*/  "Open Hi-Hat",
+	/*46*/  "Low-Mid Tom",
+	/*47*/  "Hi-Mid Tom",
+	/*48*/  "Crash Cymbal 1",
+	/*49*/  "High Tom",
+	/*50*/  "Ride Cymbal 1",
+	/*51*/  "Chinese Cymbal",
+	/*52*/  "Ride Bell",
+	/*53*/  "Tambourine",
+	/*54*/  "Splash Cymbal",
+	/*55*/  "Cowbell",
+	/*56*/  "Crash Cymbal 2",
+	/*57*/  "Vibraslap",
+	/*58*/  "Ride Cymbal 2",
+	/*59*/  "Hi Bongo",
+	/*60*/  "Low Bongo",
+	/*61*/  "Mute Hi Conga",
+	/*62*/  "Open Hi Conga",
+	/*63*/  "Low Conga",
+	/*64*/  "High Timbale",
+	/*65*/  "Low Timbale",
+	/*66*/  "High Agogo",
+	/*67*/  "Low Agogo",
+	/*68*/  "Cabasa",
+	/*69*/  "Maracas",
+	/*70*/  "Short Whistle",
+	/*71*/  "Long Whistle",
+	/*72*/  "Short Guiro",
+	/*73*/  "Long Guiro",
+	/*74*/  "Claves",
+	/*75*/  "Hi Wood Block",
+	/*76*/  "Low Wood Block",
+	/*77*/  "Mute Cuica",
+	/*78*/  "Open Cuica",
+	/*79*/  "Mute Triangle",
+	/*80*/  "Open Triangle"
+};
+
+/*******************************************
+ * Fancy instrument mappings begin here... *
+ *******************************************/
+
+
+static const Mt32ToGmMap Mt32PresetTimbreMaps[] = {
+	/*000*/  {"AcouPiano1", 0, MIDI_UNMAPPED},
+	/*001*/  {"AcouPiano2", 1, MIDI_UNMAPPED},
+	/*002*/  {"AcouPiano3", 0, MIDI_UNMAPPED},
+	/*003*/  {"ElecPiano1", 4, MIDI_UNMAPPED},
+	/*004*/  {"ElecPiano2", 5, MIDI_UNMAPPED},
+	/*005*/  {"ElecPiano3", 4, MIDI_UNMAPPED},
+	/*006*/  {"ElecPiano4", 5, MIDI_UNMAPPED},
+	/*007*/  {"Honkytonk ", 3, MIDI_UNMAPPED},
+	/*008*/  {"Elec Org 1", 16, MIDI_UNMAPPED},
+	/*009*/  {"Elec Org 2", 17, MIDI_UNMAPPED},
+	/*010*/  {"Elec Org 3", 18, MIDI_UNMAPPED},
+	/*011*/  {"Elec Org 4", 18, MIDI_UNMAPPED},
+	/*012*/  {"Pipe Org 1", 19, MIDI_UNMAPPED},
+	/*013*/  {"Pipe Org 2", 19, MIDI_UNMAPPED},
+	/*014*/  {"Pipe Org 3", 20, MIDI_UNMAPPED},
+	/*015*/  {"Accordion ", 21, MIDI_UNMAPPED},
+	/*016*/  {"Harpsi 1  ", 6, MIDI_UNMAPPED},
+	/*017*/  {"Harpsi 2  ", 6, MIDI_UNMAPPED},
+	/*018*/  {"Harpsi 3  ", 6, MIDI_UNMAPPED},
+	/*019*/  {"Clavi 1   ", 7, MIDI_UNMAPPED},
+	/*020*/  {"Clavi 2   ", 7, MIDI_UNMAPPED},
+	/*021*/  {"Clavi 3   ", 7, MIDI_UNMAPPED},
+	/*022*/  {"Celesta 1 ", 8, MIDI_UNMAPPED},
+	/*023*/  {"Celesta 2 ", 8, MIDI_UNMAPPED},
+	/*024*/  {"Syn Brass1", 62, MIDI_UNMAPPED},
+	/*025*/  {"Syn Brass2", 63, MIDI_UNMAPPED},
+	/*026*/  {"Syn Brass3", 62, MIDI_UNMAPPED},
+	/*027*/  {"Syn Brass4", 63, MIDI_UNMAPPED},
+	/*028*/  {"Syn Bass 1", 38, MIDI_UNMAPPED},
+	/*029*/  {"Syn Bass 2", 39, MIDI_UNMAPPED},
+	/*030*/  {"Syn Bass 3", 38, MIDI_UNMAPPED},
+	/*031*/  {"Syn Bass 4", 39, MIDI_UNMAPPED},
+	/*032*/  {"Fantasy   ", 88, MIDI_UNMAPPED},
+	/*033*/  {"Harmo Pan ", 89, MIDI_UNMAPPED},
+	/*034*/  {"Chorale   ", 52, MIDI_UNMAPPED},
+	/*035*/  {"Glasses   ", 98, MIDI_UNMAPPED},
+	/*036*/  {"Soundtrack", 97, MIDI_UNMAPPED},
+	/*037*/  {"Atmosphere", 99, MIDI_UNMAPPED},
+	/*038*/  {"Warm Bell ", 89, MIDI_UNMAPPED},
+	/*039*/  {"Funny Vox ", 85, MIDI_UNMAPPED},
+	/*040*/  {"Echo Bell ", 39, MIDI_UNMAPPED},
+	/*041*/  {"Ice Rain  ", 101, MIDI_UNMAPPED},
+	/*042*/  {"Oboe 2001 ", 68, MIDI_UNMAPPED},
+	/*043*/  {"Echo Pan  ", 87, MIDI_UNMAPPED},
+	/*044*/  {"DoctorSolo", 86, MIDI_UNMAPPED},
+	/*045*/  {"Schooldaze", 103, MIDI_UNMAPPED},
+	/*046*/  {"BellSinger", 88, MIDI_UNMAPPED},
+	/*047*/  {"SquareWave", 80, MIDI_UNMAPPED},
+	/*048*/  {"Str Sect 1", 48, MIDI_UNMAPPED},
+	/*049*/  {"Str Sect 2", 48, MIDI_UNMAPPED},
+	/*050*/  {"Str Sect 3", 49, MIDI_UNMAPPED},
+	/*051*/  {"Pizzicato ", 45, MIDI_UNMAPPED},
+	/*052*/  {"Violin 1  ", 40, MIDI_UNMAPPED},
+	/*053*/  {"Violin 2  ", 40, MIDI_UNMAPPED},
+	/*054*/  {"Cello 1   ", 42, MIDI_UNMAPPED},
+	/*055*/  {"Cello 2   ", 42, MIDI_UNMAPPED},
+	/*056*/  {"Contrabass", 43, MIDI_UNMAPPED},
+	/*057*/  {"Harp 1    ", 46, MIDI_UNMAPPED},
+	/*058*/  {"Harp 2    ", 46, MIDI_UNMAPPED},
+	/*059*/  {"Guitar 1  ", 24, MIDI_UNMAPPED},
+	/*060*/  {"Guitar 2  ", 25, MIDI_UNMAPPED},
+	/*061*/  {"Elec Gtr 1", 26, MIDI_UNMAPPED},
+	/*062*/  {"Elec Gtr 2", 27, MIDI_UNMAPPED},
+	/*063*/  {"Sitar     ", 104, MIDI_UNMAPPED},
+	/*064*/  {"Acou Bass1", 32, MIDI_UNMAPPED},
+	/*065*/  {"Acou Bass2", 33, MIDI_UNMAPPED},
+	/*066*/  {"Elec Bass1", 34, MIDI_UNMAPPED},
+	/*067*/  {"Elec Bass2", 39, MIDI_UNMAPPED},
+	/*068*/  {"Slap Bass1", 36, MIDI_UNMAPPED},
+	/*069*/  {"Slap Bass2", 37, MIDI_UNMAPPED},
+	/*070*/  {"Fretless 1", 35, MIDI_UNMAPPED},
+	/*071*/  {"Fretless 2", 35, MIDI_UNMAPPED},
+	/*072*/  {"Flute 1   ", 73, MIDI_UNMAPPED},
+	/*073*/  {"Flute 2   ", 73, MIDI_UNMAPPED},
+	/*074*/  {"Piccolo 1 ", 72, MIDI_UNMAPPED},
+	/*075*/  {"Piccolo 2 ", 72, MIDI_UNMAPPED},
+	/*076*/  {"Recorder  ", 74, MIDI_UNMAPPED},
+	/*077*/  {"Panpipes  ", 75, MIDI_UNMAPPED},
+	/*078*/  {"Sax 1     ", 64, MIDI_UNMAPPED},
+	/*079*/  {"Sax 2     ", 65, MIDI_UNMAPPED},
+	/*080*/  {"Sax 3     ", 66, MIDI_UNMAPPED},
+	/*081*/  {"Sax 4     ", 67, MIDI_UNMAPPED},
+	/*082*/  {"Clarinet 1", 71, MIDI_UNMAPPED},
+	/*083*/  {"Clarinet 2", 71, MIDI_UNMAPPED},
+	/*084*/  {"Oboe      ", 68, MIDI_UNMAPPED},
+	/*085*/  {"Engl Horn ", 69, MIDI_UNMAPPED},
+	/*086*/  {"Bassoon   ", 70, MIDI_UNMAPPED},
+	/*087*/  {"Harmonica ", 22, MIDI_UNMAPPED},
+	/*088*/  {"Trumpet 1 ", 56, MIDI_UNMAPPED},
+	/*089*/  {"Trumpet 2 ", 56, MIDI_UNMAPPED},
+	/*090*/  {"Trombone 1", 57, MIDI_UNMAPPED},
+	/*091*/  {"Trombone 2", 57, MIDI_UNMAPPED},
+	/*092*/  {"Fr Horn 1 ", 60, MIDI_UNMAPPED},
+	/*093*/  {"Fr Horn 2 ", 60, MIDI_UNMAPPED},
+	/*094*/  {"Tuba      ", 58, MIDI_UNMAPPED},
+	/*095*/  {"Brs Sect 1", 61, MIDI_UNMAPPED},
+	/*096*/  {"Brs Sect 2", 61, MIDI_UNMAPPED},
+	/*097*/  {"Vibe 1    ", 11, MIDI_UNMAPPED},
+	/*098*/  {"Vibe 2    ", 11, MIDI_UNMAPPED},
+	/*099*/  {"Syn Mallet", 15, MIDI_UNMAPPED},
+	/*100*/  {"Wind Bell ", 88, MIDI_UNMAPPED},
+	/*101*/  {"Glock     ", 9, MIDI_UNMAPPED},
+	/*102*/  {"Tube Bell ", 14, MIDI_UNMAPPED},
+	/*103*/  {"Xylophone ", 13, MIDI_UNMAPPED},
+	/*104*/  {"Marimba   ", 12, MIDI_UNMAPPED},
+	/*105*/  {"Koto      ", 107, MIDI_UNMAPPED},
+	/*106*/  {"Sho       ", 111, MIDI_UNMAPPED},
+	/*107*/  {"Shakuhachi", 77, MIDI_UNMAPPED},
+	/*108*/  {"Whistle 1 ", 78, MIDI_UNMAPPED},
+	/*109*/  {"Whistle 2 ", 78, MIDI_UNMAPPED},
+	/*110*/  {"BottleBlow", 76, MIDI_UNMAPPED},
+	/*111*/  {"BreathPipe", 121, MIDI_UNMAPPED},
+	/*112*/  {"Timpani   ", 47, MIDI_UNMAPPED},
+	/*113*/  {"MelodicTom", 117, MIDI_UNMAPPED},
+	/*114*/  {"Deep Snare", MIDI_MAPPED_TO_RHYTHM, 37},
+	/*115*/  {"Elec Perc1", 115, MIDI_UNMAPPED}, /* ? */
+	/*116*/  {"Elec Perc2", 118, MIDI_UNMAPPED}, /* ? */
+	/*117*/  {"Taiko     ", 116, MIDI_UNMAPPED},
+	/*118*/  {"Taiko Rim ", 118, MIDI_UNMAPPED},
+	/*119*/  {"Cymbal    ", MIDI_MAPPED_TO_RHYTHM, 50},
+	/*120*/  {"Castanets ", MIDI_UNMAPPED, MIDI_UNMAPPED},
+	/*121*/  {"Triangle  ", 112, MIDI_UNMAPPED},
+	/*122*/  {"Orche Hit ", 55, MIDI_UNMAPPED},
+	/*123*/  {"Telephone ", 124, MIDI_UNMAPPED},
+	/*124*/  {"Bird Tweet", 123, MIDI_UNMAPPED},
+	/*125*/  {"OneNoteJam", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? */
+	/*126*/  {"WaterBells", 98, MIDI_UNMAPPED},
+	/*127*/  {"JungleTune", MIDI_UNMAPPED, MIDI_UNMAPPED} /* ? */
+};
+
+static const Mt32ToGmMap Mt32RhythmTimbreMaps[] = {
+	/*00*/  {"Acou BD   ", MIDI_MAPPED_TO_RHYTHM, 34},
+	/*01*/  {"Acou SD   ", MIDI_MAPPED_TO_RHYTHM, 37},
+	/*02*/  {"Acou HiTom", 117, 49},
+	/*03*/  {"AcouMidTom", 117, 46},
+	/*04*/  {"AcouLowTom", 117, 40},
+	/*05*/  {"Elec SD   ", MIDI_MAPPED_TO_RHYTHM, 39},
+	/*06*/  {"Clsd HiHat", MIDI_MAPPED_TO_RHYTHM, 41},
+	/*07*/  {"OpenHiHat1", MIDI_MAPPED_TO_RHYTHM, 45},
+	/*08*/  {"Crash Cym ", MIDI_MAPPED_TO_RHYTHM, 48},
+	/*09*/  {"Ride Cym  ", MIDI_MAPPED_TO_RHYTHM, 50},
+	/*10*/  {"Rim Shot  ", MIDI_MAPPED_TO_RHYTHM, 36},
+	/*11*/  {"Hand Clap ", MIDI_MAPPED_TO_RHYTHM, 38},
+	/*12*/  {"Cowbell   ", MIDI_MAPPED_TO_RHYTHM, 55},
+	/*13*/  {"Mt HiConga", MIDI_MAPPED_TO_RHYTHM, 61},
+	/*14*/  {"High Conga", MIDI_MAPPED_TO_RHYTHM, 62},
+	/*15*/  {"Low Conga ", MIDI_MAPPED_TO_RHYTHM, 63},
+	/*16*/  {"Hi Timbale", MIDI_MAPPED_TO_RHYTHM, 64},
+	/*17*/  {"LowTimbale", MIDI_MAPPED_TO_RHYTHM, 65},
+	/*18*/  {"High Bongo", MIDI_MAPPED_TO_RHYTHM, 59},
+	/*19*/  {"Low Bongo ", MIDI_MAPPED_TO_RHYTHM, 60},
+	/*20*/  {"High Agogo", 113, 66},
+	/*21*/  {"Low Agogo ", 113, 67},
+	/*22*/  {"Tambourine", MIDI_MAPPED_TO_RHYTHM, 53},
+	/*23*/  {"Claves    ", MIDI_MAPPED_TO_RHYTHM, 74},
+	/*24*/  {"Maracas   ", MIDI_MAPPED_TO_RHYTHM, 69},
+	/*25*/  {"SmbaWhis L", 78, 71},
+	/*26*/  {"SmbaWhis S", 78, 70},
+	/*27*/  {"Cabasa    ", MIDI_MAPPED_TO_RHYTHM, 68},
+	/*28*/  {"Quijada   ", MIDI_MAPPED_TO_RHYTHM, 72},
+	/*29*/  {"OpenHiHat2", MIDI_MAPPED_TO_RHYTHM, 43}
+};
+
+static const uint8 Mt32PresetRhythmKeymap[] = {
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, 34, 34, 36, 37, 38, 39,
+	40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+	50, MIDI_UNMAPPED, MIDI_UNMAPPED, 53, MIDI_UNMAPPED, 55, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, 59,
+	60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+	70, 71, 72, MIDI_UNMAPPED, 74, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED,
+	MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED
+};
+
+/* +++ - Don't change unless you've got a good reason
+   ++  - Looks good, sounds ok
+   +   - Not too bad, but is it right?
+   ?   - Where do I map this one?
+   ??  - Any good ideas?
+   ??? - I'm clueless?
+   R   - Rhythm... */
+static const Mt32ToGmMap Mt32MemoryTimbreMaps[] = {
+	{"AccPnoKA2 ", 1, MIDI_UNMAPPED},     /* ++ (KQ1) */
+	{"Acou BD   ", MIDI_MAPPED_TO_RHYTHM, 34},   /* R (PQ2) */
+	{"Acou SD   ", MIDI_MAPPED_TO_RHYTHM, 37},   /* R (PQ2) */
+	{"AcouPnoKA ", 0, MIDI_UNMAPPED},     /* ++ (KQ1) */
+	{"BASS      ", 32, MIDI_UNMAPPED},    /* + (LSL3) */
+	{"BASSOONPCM", 70, MIDI_UNMAPPED},    /* + (CB) */
+	{"BEACH WAVE", 122, MIDI_UNMAPPED},   /* + (LSL3) */
+	{"BagPipes  ", 109, MIDI_UNMAPPED},
+	{"BassPizzMS", 45, MIDI_UNMAPPED},    /* ++ (HQ) */
+	{"BassoonKA ", 70, MIDI_UNMAPPED},    /* ++ (KQ1) */
+	{"Bell    MS", 112, MIDI_UNMAPPED},   /* ++ (iceMan) */
+	{"Bells   MS", 112, MIDI_UNMAPPED},   /* + (HQ) */
+	{"Big Bell  ", 14, MIDI_UNMAPPED},    /* + (CB) */
+	{"Bird Tweet", 123, MIDI_UNMAPPED},
+	{"BrsSect MS", 61, MIDI_UNMAPPED},    /* +++ (iceMan) */
+	{"CLAPPING  ", 126, MIDI_UNMAPPED},   /* ++ (LSL3) */
+	{"Cabasa    ", MIDI_MAPPED_TO_RHYTHM, 68},   /* R (HBoG) */
+	{"Calliope  ", 82, MIDI_UNMAPPED},    /* +++ (HQ) */
+	{"CelticHarp", 46, MIDI_UNMAPPED},    /* ++ (CoC) */
+	{"Chicago MS", 1, MIDI_UNMAPPED},     /* ++ (iceMan) */
+	{"Chop      ", 117, MIDI_UNMAPPED},
+	{"Chorale MS", 52, MIDI_UNMAPPED},    /* + (CoC) */
+	{"ClarinetMS", 71, MIDI_UNMAPPED},
+	{"Claves    ", MIDI_MAPPED_TO_RHYTHM, 74},   /* R (PQ2) */
+	{"Claw    MS", 118, MIDI_UNMAPPED},    /* + (HQ) */
+	{"ClockBell ", 14, MIDI_UNMAPPED},    /* + (CB) */
+	{"ConcertCym", MIDI_MAPPED_TO_RHYTHM, 54},   /* R ? (KQ1) */
+	{"Conga   MS", MIDI_MAPPED_TO_RHYTHM, 63},   /* R (HQ) */
+	{"CoolPhone ", 124, MIDI_UNMAPPED},   /* ++ (LSL3) */
+	{"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */
+	{"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
+	{"Cricket   ", 120, MIDI_UNMAPPED}, /* ? (CB) */
+	{"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 56},   /* R +++ (iceMan) */
+	{"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */
+	{"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 54},   /* R ? (CoC, HQ) */
+	{"CymbRollKA", MIDI_MAPPED_TO_RHYTHM, 56},   /* R ? (KQ1) */
+	{"Cymbal Lo ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* R ? (LSL3) */
+	{"card      ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HBoG) */
+	{"DirtGtr MS", 30, MIDI_UNMAPPED},    /* + (iceMan) */
+	{"DirtGtr2MS", 29, MIDI_UNMAPPED},    /* + (iceMan) */
+	{"E Bass  MS", 33, MIDI_UNMAPPED},    /* + (SQ3) */
+	{"ElecBassMS", 33, MIDI_UNMAPPED},
+	{"ElecGtr MS", 27, MIDI_UNMAPPED},    /* ++ (iceMan) */
+	{"EnglHornMS", 69, MIDI_UNMAPPED},
+	{"FantasiaKA", 88, MIDI_UNMAPPED},
+	{"Fantasy   ", 99, MIDI_UNMAPPED},    /* + (PQ2) */
+	{"Fantasy2MS", 99, MIDI_UNMAPPED},    /* ++ (CoC, HQ) */
+	{"Filter  MS", 95, MIDI_UNMAPPED},    /* +++ (iceMan) */
+	{"Filter2 MS", 95, MIDI_UNMAPPED},    /* ++ (iceMan) */
+	{"Flame2  MS", 121, MIDI_UNMAPPED},   /* ? (HQ) */
+	{"Flames  MS", 121, MIDI_UNMAPPED},   /* ? (HQ) */
+	{"Flute   MS", 73, MIDI_UNMAPPED},    /* +++ (HQ) */
+	{"FogHorn MS", 58, MIDI_UNMAPPED},
+	{"FrHorn1 MS", 60, MIDI_UNMAPPED},    /* +++ (HQ) */
+	{"FunnyTrmp ", 56, MIDI_UNMAPPED},    /* ++ (CB) */
+	{"GameSnd MS", 80, MIDI_UNMAPPED},
+	{"Glock   MS", 9, MIDI_UNMAPPED},     /* +++ (HQ) */
+	{"Gunshot   ", 127, MIDI_UNMAPPED},   /* +++ (CB) */
+	{"Hammer  MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */
+	{"Harmonica2", 22, MIDI_UNMAPPED},    /* +++ (CB) */
+	{"Harpsi 1  ", 6, MIDI_UNMAPPED},     /* + (HBoG) */
+	{"Harpsi 2  ", 6, MIDI_UNMAPPED},     /* +++ (CB) */
+	{"Heart   MS", 116, MIDI_UNMAPPED},   /* ? (iceMan) */
+	{"Horse1  MS", 115, MIDI_UNMAPPED},   /* ? (CoC, HQ) */
+	{"Horse2  MS", 115, MIDI_UNMAPPED},   /* ? (CoC, HQ) */
+	{"InHale  MS", 121, MIDI_UNMAPPED},   /* ++ (iceMan) */
+	{"KNIFE     ", 120, MIDI_UNMAPPED},   /* ? (LSL3) */
+	{"KenBanjo  ", 105, MIDI_UNMAPPED},   /* +++ (CB) */
+	{"Kiss    MS", 25, MIDI_UNMAPPED},    /* ++ (HQ) */
+	{"KongHit   ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
+	{"Koto      ", 107, MIDI_UNMAPPED},   /* +++ (PQ2) */
+	{"Laser   MS", 81, MIDI_UNMAPPED},    /* ?? (HQ) */
+	{"Meeps   MS", 62, MIDI_UNMAPPED},    /* ? (HQ) */
+	{"MTrak   MS", 62, MIDI_UNMAPPED},    /* ?? (iceMan) */
+	{"MachGun MS", 127, MIDI_UNMAPPED},   /* ? (iceMan) */
+	{"OCEANSOUND", 122, MIDI_UNMAPPED},   /* + (LSL3) */
+	{"Oboe 2001 ", 68, MIDI_UNMAPPED},    /* + (PQ2) */
+	{"Ocean   MS", 122, MIDI_UNMAPPED},   /* + (iceMan) */
+	{"PPG 2.3 MS", 75, MIDI_UNMAPPED},    /* ? (iceMan) */
+	{"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */
+	{"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 39},   /* R ? (iceMan) */
+	{"PiccoloKA ", 72, MIDI_UNMAPPED},    /* +++ (KQ1) */
+	{"PinkBassMS", 39, MIDI_UNMAPPED},
+	{"Pizz2     ", 45, MIDI_UNMAPPED},    /* ++ (CB) */
+	{"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */
+	{"Raspbry MS", 81, MIDI_UNMAPPED},    /* ? (HQ) */
+	{"RatSqueek ", 72, MIDI_UNMAPPED},    /* ? (CB, CoC) */
+	{"Record78  ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (CB) */
+	{"RecorderMS", 74, MIDI_UNMAPPED},    /* +++ (CoC) */
+	{"Red Baron ", 125, MIDI_UNMAPPED},   /* ? (CB) */
+	{"ReedPipMS ", 20, MIDI_UNMAPPED},    /* +++ (Coc) */
+	{"RevCymb MS", 119, MIDI_UNMAPPED},
+	{"RifleShot ", 127, MIDI_UNMAPPED},   /* + (CB) */
+	{"RimShot MS", MIDI_MAPPED_TO_RHYTHM, 36},   /* R */
+	{"SHOWER    ", 52, MIDI_UNMAPPED},    /* ? (LSL3) */
+	{"SQ Bass MS", 32, MIDI_UNMAPPED},    /* + (SQ3) */
+	{"ShakuVibMS", 79, MIDI_UNMAPPED},    /* + (iceMan) */
+	{"SlapBassMS", 36, MIDI_UNMAPPED},    /* +++ (iceMan) */
+	{"Snare   MS", MIDI_MAPPED_TO_RHYTHM, 37},   /* R (HQ) */
+	{"Some Birds", 123, MIDI_UNMAPPED},   /* + (CB) */
+	{"Sonar   MS", 78, MIDI_UNMAPPED},    /* ? (iceMan) */
+	{"Soundtrk2 ", 97, MIDI_UNMAPPED},    /* +++ (CB) */
+	{"Soundtrack", 97, MIDI_UNMAPPED},    /* ++ (CoC) */
+	{"SqurWaveMS", 80, MIDI_UNMAPPED},
+	{"StabBassMS", 34, MIDI_UNMAPPED},    /* + (iceMan) */
+	{"SteelDrmMS", 114, MIDI_UNMAPPED},   /* +++ (iceMan) */
+	{"StrSect1MS", 48, MIDI_UNMAPPED},    /* ++ (HQ) */
+	{"String  MS", 45, MIDI_UNMAPPED},    /* + (CoC) */
+	{"Syn-Choir ", 91, MIDI_UNMAPPED},
+	{"Syn Brass4", 63, MIDI_UNMAPPED},    /* ++ (PQ2) */
+	{"SynBass MS", 38, MIDI_UNMAPPED},
+	{"SwmpBackgr", 120, MIDI_UNMAPPED},    /* ?? (CB,HQ) */
+	{"T-Bone2 MS", 57, MIDI_UNMAPPED},    /* +++ (HQ) */
+	{"Taiko     ", 116, 34},      /* +++ (Coc) */
+	{"Taiko Rim ", 118, 36},      /* +++ (LSL3) */
+	{"Timpani1  ", 47, MIDI_UNMAPPED},    /* +++ (CB) */
+	{"Tom     MS", 117, 47},      /* +++ (iceMan) */
+	{"Toms    MS", 117, 47},      /* +++ (CoC, HQ) */
+	{"Tpt1prtl  ", 56, MIDI_UNMAPPED},    /* +++ (KQ1) */
+	{"TriangleMS", 112, 80},      /* R (CoC) */
+	{"Trumpet 1 ", 56, MIDI_UNMAPPED},    /* +++ (CoC) */
+	{"Type    MS", 114, MIDI_UNMAPPED},   /* ? (iceMan) */
+	{"WaterBells", 98, MIDI_UNMAPPED},    /* + (PQ2) */
+	{"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */
+	{"Whiporill ", 123, MIDI_UNMAPPED},   /* + (CB) */
+	{"Wind      ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */
+	{"Wind    MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ, iceMan) */
+	{"Wind2   MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC) */
+	{"Woodpecker", 115, MIDI_UNMAPPED},   /* ? (CB) */
+	{"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC, HQ, iceMan) */
+	{0, 0, 0}
+};
+
+} // End of namespace Sci


Property changes on: scummvm/trunk/engines/sci/sound/drivers/map-mt32-to-gm.h
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Copied: scummvm/trunk/engines/sci/sound/drivers/midi.cpp (from rev 47417, scummvm/trunk/engines/sci/sound/softseq/midi.cpp)
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/midi.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/midi.cpp	2010-01-21 16:27:29 UTC (rev 47418)
@@ -0,0 +1,859 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/sci.h"
+
+#include "common/config-manager.h"
+#include "sound/fmopl.h"
+#include "sound/softsynth/emumidi.h"
+
+#include "sci/resource.h"
+#include "sci/sound/drivers/mididriver.h"
+#include "sci/sound/drivers/map-mt32-to-gm.h"
+
+namespace Sci {
+
+class MidiPlayer_Midi : public MidiPlayer {
+public:
+	enum {
+		kVoices = 32,
+		kReverbConfigNr = 11,
+		kMaxSysExSize = 264
+	};
+
+	MidiPlayer_Midi();
+	virtual ~MidiPlayer_Midi();
+
+	int open(ResourceManager *resMan);
+	void close();
+	void send(uint32 b);
+	void sysEx(const byte *msg, uint16 length);
+	bool hasRhythmChannel() const { return true; }
+	byte getPlayId(SciVersion soundVersion);
+	int getPolyphony() const { return kVoices; }
+	void setVolume(byte volume);
+	int getVolume();
+	void setReverb(byte reverb);
+	void playSwitch(bool play);
+
+private:
+	bool isMt32GmPatch(const byte *data, int size);
+	void readMt32GmPatch(const byte *data, int size);
+	void readMt32Patch(const byte *data, int size);
+	void readMt32DrvData();
+
+	void mapMt32ToGm(byte *data, size_t size);
+	uint8 lookupGmInstrument(const char *iname);
+	uint8 lookupGmRhythmKey(const char *iname);
+	uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins);
+
+	void sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay);
+	void sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay);
+	void setMt32Volume(byte volume);
+	void resetMt32();
+
+	void noteOn(int channel, int note, int velocity);
+	void setPatch(int channel, int patch);
+	void controlChange(int channel, int control, int value);
+
+	struct Channel {
+		byte mappedPatch;
+		byte patch;
+		int velocityMapIdx;
+		bool playing;
+		int8 keyShift;
+		int8 volAdjust;
+		uint8 pan;
+		uint8 hold;
+		uint8 volume;
+
+		Channel() : mappedPatch(MIDI_UNMAPPED), patch(MIDI_UNMAPPED), velocityMapIdx(0), playing(false),
+			keyShift(0), volAdjust(0), pan(0x80), hold(0), volume(0x7f) { }
+	};
+
+	bool _isMt32;
+	bool _isOldPatchFormat;
+	bool _hasReverb;
+	bool _playSwitch;
+	int _masterVolume;
+
+	byte _reverbConfig[kReverbConfigNr][3];
+	Channel _channels[16];	
+	uint8 _percussionMap[128];
+	int8 _keyShift[128];
+	int8 _volAdjust[128];
+	uint8 _patchMap[128];
+	uint8 _velocityMapIdx[128];
+	uint8 _velocityMap[4][128];
+
+	// These are extensions used for our own MT-32 to GM mapping
+	uint8 _pitchBendRange[128];
+	uint8 _percussionVelocityScale[128];
+
+	byte _goodbyeMsg[20];
+	byte _sysExBuf[kMaxSysExSize];
+};
+
+MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) {
+	MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
+	_driver = createMidi(midiType);
+
+	if (midiType == MD_MT32 || ConfMan.getBool("native_mt32"))
+		_isMt32 = true;
+
+	_sysExBuf[0] = 0x41;
+	_sysExBuf[1] = 0x10;
+	_sysExBuf[2] = 0x16;
+	_sysExBuf[3] = 0x12;
+}
+
+MidiPlayer_Midi::~MidiPlayer_Midi() {
+	delete _driver;
+}
+
+void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) {
+	uint8 patch = _channels[channel].mappedPatch;
+
+	if (channel == MIDI_RHYTHM_CHANNEL) {
+		if (_percussionMap[note] == MIDI_UNMAPPED) {
+			debugC(kDebugLevelSound, "[Midi] Percussion instrument %i is unmapped", note);
+			return;
+		}
+
+		note = _percussionMap[note];
+		// Scale velocity;
+		velocity = velocity * _percussionVelocityScale[note] / 127;
+	} else if (patch >= 128) {
+		if (patch == MIDI_UNMAPPED)
+			return;
+
+		// Map to rhythm
+		channel = MIDI_RHYTHM_CHANNEL;
+		note = patch - 128;
+
+		// Scale velocity;
+		velocity = velocity * _percussionVelocityScale[note] / 127;
+	} else {
+		int8 keyshift = _keyShift[channel];
+
+		int shiftNote = note + keyshift;
+
+		if (keyshift > 0) {
+			while (shiftNote > 127)
+				shiftNote -= 12;
+		} else {
+			while (shiftNote < 0)
+				shiftNote += 12;
+		}
+
+		note = shiftNote;
+
+		// We assume that velocity 0 maps to 0 (for note off)
+		int mapIndex = _channels[channel].velocityMapIdx;
+		velocity = _velocityMap[mapIndex][velocity];
+	}
+
+	_channels[channel].playing = true;
+	_driver->send(0x90 | channel, note, velocity);
+}
+
+void MidiPlayer_Midi::controlChange(int channel, int control, int value) {
+	switch (control) {
+	case 0x07:
+		_channels[channel].volume = value;
+
+		if (!_playSwitch)
+			return;
+
+		value += _channels[channel].volAdjust;
+
+		if (value > 0x7f)
+			value = 0x7f;
+
+		if (value < 0)
+			value = 1;
+
+		value *= _masterVolume;
+
+		if (value != 0) {
+			value /= 15;
+
+			if (value == 0)
+				value = 1;
+		}
+		break;
+	case 0x0a:
+		if (_channels[channel].pan == value)
+			return;
+
+		_channels[channel].pan = value;
+		break;
+	case 0x40:
+		if (_channels[channel].hold == value)
+			return;
+
+		_channels[channel].hold = value;
+		break;
+	case 0x7b:
+		if (!_channels[channel].playing)
+			return;
+
+		_channels[channel].playing = false;
+	}
+
+	_driver->send(0xb0 | channel, control, value);
+}
+
+void MidiPlayer_Midi::setPatch(int channel, int patch) {
+	bool resetVol = false;
+
+	if ((channel == MIDI_RHYTHM_CHANNEL) || (_channels[channel].patch == patch))
+		return;
+
+	_channels[channel].patch = patch;
+	_channels[channel].velocityMapIdx = _velocityMapIdx[patch];
+
+	if (_channels[channel].mappedPatch == MIDI_UNMAPPED)
+		resetVol = true;
+
+	_channels[channel].mappedPatch = _patchMap[patch];
+
+	if (_patchMap[patch] == MIDI_UNMAPPED) {
+		debugC(kDebugLevelSound, "[Midi] Channel %i set to unmapped patch %i", channel, patch);
+		_driver->send(0xb0 | channel, 0x7b, 0);
+		_driver->send(0xb0 | channel, 0x40, 0);
+		return;
+	}
+
+	if (_channels[channel].keyShift != _keyShift[patch]) {
+		_channels[channel].keyShift = _keyShift[patch];
+		_driver->send(0xb0 | channel, 0x7b, 0);
+		_driver->send(0xb0 | channel, 0x40, 0);
+		resetVol = true;
+	}
+
+	if (resetVol || (_channels[channel].volAdjust != _volAdjust[patch])) {
+		_channels[channel].volAdjust = _volAdjust[patch];
+		controlChange(channel, 0x07, _channels[channel].volume);
+	}
+
+	uint8 bendRange = _pitchBendRange[patch];
+	if (bendRange != MIDI_UNMAPPED)
+		_driver->setPitchBendRange(channel, bendRange);
+
+	_driver->send(0xc0 | channel, _patchMap[patch], 0);
+}
+
+void MidiPlayer_Midi::send(uint32 b) {
+	byte command = b & 0xf0;
+	byte channel = b & 0xf;
+	byte op1 = (b >> 8) & 0x7f;
+	byte op2 = (b >> 16) & 0x7f;
+
+	// In early SCI0, we may also get events for AdLib rhythm channels.
+	// While an MT-32 would ignore those with the default channel mapping,
+	// we filter these out for the benefit of other MIDI devices.
+	if (channel < 1 || channel > 9)
+		return;
+
+	switch (command) {
+	case 0x80:
+		noteOn(channel, op1, 0);
+		break;
+	case 0x90:
+		noteOn(channel, op1, op2);
+		break;
+	case 0xb0:
+		controlChange(channel, op1, op2);
+		break;
+	case 0xc0:
+		setPatch(channel, op1);
+		break;
+	case 0xe0:
+		_driver->send(b);
+		break;
+	default:
+		warning("Ignoring MIDI event %02x", command);
+	}
+}
+
+void MidiPlayer_Midi::setVolume(byte volume) {
+	_masterVolume = volume;
+
+	if (!_playSwitch)
+		return;
+
+	for (uint i = 1; i < 10; i++) {
+		if (_channels[i].volume != 0xff)
+			controlChange(i, 0x07, _channels[i].volume & 0x7f);
+	}
+}
+
+int MidiPlayer_Midi::getVolume() {
+	return _masterVolume;
+}
+
+void MidiPlayer_Midi::setReverb(byte reverb) {
+	_reverb = CLIP<byte>(reverb, 0, kReverbConfigNr - 1);
+	if (_hasReverb)
+		sendMt32SysEx(0x100001, _reverbConfig[_reverb], 3, true);
+}
+
+void MidiPlayer_Midi::playSwitch(bool play) {
+	_playSwitch = play;
+	if (play)
+		setVolume(_masterVolume);
+	else {
+		for (uint i = 1; i < 10; i++)
+			_driver->send(0xb0 | i, 7, 0);
+	}
+}
+
+bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size)
+{
+	if (size < 1155)
+		return false;
+	if (size > 16889)
+		return true;
+
+	bool isMt32 = false;
+	bool isMt32Gm = false;
+
+	if (READ_LE_UINT16(data + 1153) + 1155 == size)
+		isMt32Gm = true;
+
+	int pos = 492 + 246 * data[491];
+
+	if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd))
+		pos += 386;
+
+	if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba))
+		pos += 267;
+
+	if (size == pos)
+		isMt32 = true;
+
+	if (isMt32 == isMt32Gm)
+		error("Failed to detect MT-32 patch format");
+
+	return isMt32Gm;
+}
+
+void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay = false) {
+	if (len + 8 > kMaxSysExSize) {
+		warning("SysEx message exceed maximum size; ignoring");
+		return;
+	}
+
+	uint16 chk = 0;
+
+	_sysExBuf[4] = (addr >> 16) & 0xff;
+	_sysExBuf[5] = (addr >> 8) & 0xff;
+	_sysExBuf[6] = addr & 0xff;
+
+	for (int i = 0; i < len; i++)
+		_sysExBuf[7 + i] = str->readByte();
+
+	for (int i = 4; i < 7 + len; i++)
+		chk += _sysExBuf[i];
+
+	_sysExBuf[7 + len] = 128 - chk % 128;
+
+	if (noDelay)
+		_driver->sysEx(_sysExBuf, len + 8);
+	else
+		sysEx(_sysExBuf, len + 8);
+}
+
+void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay = false) {
+	Common::MemoryReadStream *str = new Common::MemoryReadStream(buf, len);
+	sendMt32SysEx(addr, str, len, noDelay);
+	delete str;
+}
+
+void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) {
+	Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size);
+
+	// Send before-SysEx text
+	str->seek(0x14);
+	sendMt32SysEx(0x200000, str, 20);
+
+	// Save goodbye message
+	str->read(_goodbyeMsg, 20);
+
+	byte volume = CLIP<uint16>(str->readUint16LE(), 0, 100);
+	setMt32Volume(volume);
+
+	// Reverb default only used in (roughly) SCI0/SCI01
+	_reverb = str->readByte();
+	_hasReverb = true;
+
+	// Skip reverb SysEx message
+	str->seek(11, SEEK_CUR);
+
+	// Read reverb data
+	for (int i = 0; i < kReverbConfigNr; i++) {
+		_reverbConfig[i][0] = str->readByte();
+		_reverbConfig[i][1] = str->readByte();
+		_reverbConfig[i][2] = str->readByte();
+	}
+
+	// Patches 1-48
+	sendMt32SysEx(0x50000, str, 256);
+	sendMt32SysEx(0x50200, str, 128);
+
+	// Timbres
+	byte timbresNr = str->readByte();
+	for (int i = 0; i < timbresNr; i++)
+		sendMt32SysEx(0x80000 + (i << 9), str, 246);
+
+	uint16 flag = str->readUint16BE();
+
+	if (!str->eos() && (flag == 0xabcd)) {
+		// Patches 49-96
+		sendMt32SysEx(0x50300, str, 256);
+		sendMt32SysEx(0x50500, str, 128);
+		flag = str->readUint16BE();
+	}
+
+	if (!str->eos() && (flag == 0xdcba)) {
+		// Rhythm key map
+		sendMt32SysEx(0x30110, str, 256);
+		// Partial reserve
+		sendMt32SysEx(0x100004, str, 9);
+	}
+
+	// Send after-SysEx text
+	str->seek(0);
+	sendMt32SysEx(0x200000, str, 20);
+
+	// Send the mystery SysEx
+	sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6);
+
+	delete str;
+}
+
+void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {
+	memcpy(_patchMap, data, 0x80);
+	memcpy(_keyShift, data + 0x80, 0x80);
+	memcpy(_volAdjust, data + 0x100, 0x80);
+	memcpy(_percussionMap, data + 0x180, 0x80);
+	_channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[0x200];
+	memcpy(_velocityMapIdx, data + 0x201, 0x80);
+	memcpy(_velocityMap, data + 0x281, 0x200);
+
+	uint16 midiSize = READ_LE_UINT16(data + 0x481);
+
+	if (midiSize > 0) {
+		if (size < midiSize + 1155)
+			error("Failed to read MIDI data");
+
+		const byte *midi = data + 1155;
+		byte command = 0;
+		uint i = 0;
+
+		while (i < midiSize) {
+			byte op1, op2;
+
+			if (midi[i] & 0x80)
+				command = midi[i++];
+
+			switch (command & 0xf0) {
+			case 0xf0: {
+				byte *sysExEnd = (byte *)memchr(midi + i, 0xf7, midiSize - i);
+
+				if (!sysExEnd)
+					error("Failed to find end of sysEx");
+
+				int len = sysExEnd - (midi + i);
+				sysEx(midi + i, len);
+
+				i += len + 1; // One more for the 0x7f
+				break;
+			}
+			case 0x80:
+			case 0x90:
+			case 0xa0:
+			case 0xb0:
+			case 0xe0:
+				if (i + 1 >= midiSize)
+					error("MIDI command exceeds data size");
+
+				op1 = midi[i++];
+				op2 = midi[i++];
+				_driver->send(command, op1, op2);
+				break;
+			case 0xc0:
+			case 0xd0:
+				if (i >= midiSize)
+					error("MIDI command exceeds data size");
+
+				op1 = midi[i++];
+				_driver->send(command, op1, 0);
+				break;
+			default:
+				error("Failed to find MIDI command byte");
+			}
+		}
+	}
+}
+
+void MidiPlayer_Midi::readMt32DrvData() {
+	Common::File f;
+
+	if (f.open("MT32.DRV")) {
+		int size = f.size();
+
+		assert(size >= 166);
+
+		// Send before-SysEx text
+		f.seek(0x59);
+
+		// Skip 2 extra 0 bytes in some drivers
+		if (f.readUint16LE() != 0)
+			f.seek(-2, SEEK_CUR);
+
+		sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);
+
+		// Send after-SysEx text (SSCI sends this before every song)
+		sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);
+
+		// Save goodbye message
+		f.read(_goodbyeMsg, 20);
+
+		// Set volume
+		byte volume = CLIP<uint16>(f.readUint16LE(), 0, 100);
+		setMt32Volume(volume);
+
+		byte reverbSysEx[13];
+		// This old driver should have a full reverb SysEx
+		if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7))
+			error("Error reading MT32.DRV");
+
+		// Send reverb SysEx
+		sysEx(reverbSysEx + 1, 11);
+		_hasReverb = false;
+
+		f.seek(0x29);
+
+		// Read AdLib->MT-32 patch map
+		for (int i = 0; i < 48; i++) {
+			_patchMap[i] = f.readByte();
+		}
+
+		f.close();
+	} else {
+		error("Failed to open MT32.DRV");
+	}
+}
+
+byte MidiPlayer_Midi::lookupGmInstrument(const char *iname) {
+	int i = 0;
+
+	while (Mt32MemoryTimbreMaps[i].name) {
+		if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
+			return getGmInstrument(Mt32MemoryTimbreMaps[i]);
+		i++;
+	}
+	return MIDI_UNMAPPED;
+}
+
+byte MidiPlayer_Midi::lookupGmRhythmKey(const char *iname) {
+	int i = 0;
+
+	while (Mt32MemoryTimbreMaps[i].name) {
+		if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
+			return Mt32MemoryTimbreMaps[i].gmRhythmKey;
+		i++;
+	}
+	return MIDI_UNMAPPED;
+}
+
+uint8 MidiPlayer_Midi::getGmInstrument(const Mt32ToGmMap &Mt32Ins) {
+	if (Mt32Ins.gmInstr == MIDI_MAPPED_TO_RHYTHM)
+		return Mt32Ins.gmRhythmKey + 0x80;
+	else
+		return Mt32Ins.gmInstr;
+}
+
+void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
+	// FIXME: Clean this up
+	int memtimbres, patches;
+	uint8 group, number, keyshift, finetune, bender_range;
+	uint8 *patchpointer;
+	uint32 pos;
+	int i;
+
+	for (i = 0; i < 128; i++) {
+		_patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[i]);
+		_pitchBendRange[i] = 12;
+	}
+
+	for (i = 0; i < 128; i++)
+		_percussionMap[i] = Mt32PresetRhythmKeymap[i];
+
+	memtimbres = *(data + 0x1eb);
+	pos = 0x1ec + memtimbres * 0xf6;
+
+	if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xabcd)) {
+		patches = 96;
+		pos += 2 + 8 * 48;
+	} else
+		patches = 48;
+
+	debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Patches detected", patches);
+	debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Memory Timbres", memtimbres);
+
+	debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping patches..");
+
+	for (i = 0; i < patches; i++) {
+		char name[11];
+
+		if (i < 48)
+			patchpointer = data + 0x6b + 8 * i;
+		else
+			patchpointer = data + 0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2;
+
+		group = *patchpointer;
+		number = *(patchpointer + 1);
+		keyshift = *(patchpointer + 2);
+		finetune = *(patchpointer + 3);
+		bender_range = *(patchpointer + 4);
+
+		debugCN(kDebugLevelSound, "  [%03d] ", i);
+
+		switch (group) {
+		case 1:
+			number += 64;
+			// Fall through
+		case 0:
+			_patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[number]);
+			debugCN(kDebugLevelSound, "%s -> ", Mt32PresetTimbreMaps[number].name);
+			break;
+		case 2:
+			strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10);
+			name[10] = 0;
+			_patchMap[i] = lookupGmInstrument(name);
+			debugCN(kDebugLevelSound, "%s -> ", name);
+			break;
+		case 3:
+			_patchMap[i] = getGmInstrument(Mt32RhythmTimbreMaps[number]);
+			debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number].name);
+			break;
+		default:
+			break;
+		}
+
+		if (_patchMap[i] == MIDI_UNMAPPED) {
+			debugC(kDebugLevelSound, "[Unmapped]");
+		} else {
+			if (_patchMap[i] >= 128) {
+				debugC(kDebugLevelSound, "%s [Rhythm]", GmPercussionNames[_patchMap[i] - 128]);
+			} else {
+				debugC(kDebugLevelSound, "%s", GmInstrumentNames[_patchMap[i]]);
+			}
+		}
+
+		_keyShift[i] = CLIP<uint8>(keyshift, 0, 48) - 24;
+		_pitchBendRange[i] = CLIP<uint8>(bender_range, 0, 24);
+	}
+
+	if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xdcba)) {
+		debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping percussion..");
+
+		for (i = 0; i < 64 ; i++) {
+			number = *(data + pos + 4 * i + 2);
+
+			debugCN(kDebugLevelSound, "  [%03d] ", i + 23);
+
+			if (number < 64) {
+				char name[11];
+				strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10);
+				name[10] = 0;
+				debugCN(kDebugLevelSound, "%s -> ", name);
+				_percussionMap[i + 23] = lookupGmRhythmKey(name);
+			} else {
+				if (number < 94) {
+					debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number - 64].name);
+					_percussionMap[i + 23] = Mt32RhythmTimbreMaps[number - 64].gmRhythmKey;
+				} else {
+					debugCN(kDebugLevelSound, "[Key  %03i] -> ", number);
+					_percussionMap[i + 23] = MIDI_UNMAPPED;
+				}
+			}
+
+			if (_percussionMap[i + 23] == MIDI_UNMAPPED)
+				debugC(kDebugLevelSound, "[Unmapped]");
+			else
+				debugC(kDebugLevelSound, "%s", GmPercussionNames[_percussionMap[i + 23]]);
+
+			_percussionVelocityScale[i + 23] = *(data + pos + 4 * i + 3) * 127 / 100;
+		}
+	}
+}
+
+void MidiPlayer_Midi::setMt32Volume(byte volume) {
+	sendMt32SysEx(0x100016, &volume, 1);
+}
+
+void MidiPlayer_Midi::resetMt32() {
+	sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true);
+
+	// This seems to require a longer delay than usual
+	g_system->delayMillis(150);
+}
+
+int MidiPlayer_Midi::open(ResourceManager *resMan) {
+	assert(resMan != NULL);
+
+	int retval = _driver->open();
+	if (retval != 0) {
+		warning("Failed to open MIDI driver");
+		return retval;
+	}
+
+	// By default use no mapping
+	for (uint i = 0; i < 128; i++) {
+		_percussionMap[i] = i;
+		_patchMap[i] = i;
+		_velocityMap[0][i] = i;
+		_keyShift[i] = 0;
+		_volAdjust[i] = 0;
+		_velocityMapIdx[i] = 0;
+		_pitchBendRange[i] = MIDI_UNMAPPED;
+		_percussionVelocityScale[i] = 127;
+	}
+
+	Resource *res = NULL;
+
+	if (_isMt32) {
+		// MT-32
+		resetMt32();
+
+		res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
+
+		if (res) {
+			if (isMt32GmPatch(res->data, res->size)) {
+				readMt32GmPatch(res->data, res->size);
+				strncpy((char *)_goodbyeMsg, "      ScummVM       ", 20);
+			} else {
+				readMt32Patch(res->data, res->size);
+			}
+		} else {
+			readMt32DrvData();
+		}
+	} else {
+		// General MIDI
+		res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0);
+
+		if (res && isMt32GmPatch(res->data, res->size)) {
+			// There is a GM patch
+			readMt32GmPatch(res->data, res->size);
+
+			// Detect the format of patch 1, so that we know what play mask to use
+			res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
+			if (!res)
+				_isOldPatchFormat = false;
+			else
+				_isOldPatchFormat = !isMt32GmPatch(res->data, res->size);
+		} else {
+			// No GM patch found, map instruments using MT-32 patch
+
+			warning("Game has no native support for General MIDI, applying auto-mapping");
+
+			// Modify velocity map to make low velocity notes a little louder
+			for (uint i = 1; i < 0x40; i++)
+				_velocityMap[0][i] = 0x20 + (i - 1) / 2;
+
+			res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
+
+			if (res) {
+				if (!isMt32GmPatch(res->data, res->size))
+					mapMt32ToGm(res->data, res->size);
+				else
+					error("MT-32 patch has wrong type");
+			} else {
+				// No MT-32 patch present, try to read from MT32.DRV
+				Common::File f;
+
+				if (f.open("MT32.DRV")) {
+					int size = f.size();
+
+					assert(size >= 70);
+
+					f.seek(0x29);
+
+					// Read AdLib->MT-32 patch map
+					for (int i = 0; i < 48; i++)
+						_patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[f.readByte() & 0x7f]);
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+void MidiPlayer_Midi::close() {
+	if (_isMt32) {
+		// Send goodbye message
+		sendMt32SysEx(0x200000, _goodbyeMsg, 20);
+	}
+
+	_driver->close();
+}
+
+void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) {
+	_driver->sysEx(msg, length);
+
+	// Wait the time it takes to send the SysEx data
+	uint32 delay = (length + 2) * 1000 / 3125;
+
+	// Plus an additional delay for the MT-32 rev00
+	if (_isMt32)
+		delay += 40;
+
+	g_system->delayMillis(delay);
+	g_system->updateScreen();
+}
+
+byte MidiPlayer_Midi::getPlayId(SciVersion soundVersion) {
+	switch (soundVersion) {
+	case SCI_VERSION_0_EARLY:
+	case SCI_VERSION_0_LATE:
+		return 0x01;
+	default:
+		if (_isMt32)
+			return 0x0c;
+		else
+			return _isOldPatchFormat ? 0x0c : 0x07;
+	}
+}
+
+MidiPlayer *MidiPlayer_Midi_create() {
+	return new MidiPlayer_Midi();
+}
+
+} // End of namespace Sci


Property changes on: scummvm/trunk/engines/sci/sound/drivers/midi.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Copied: scummvm/trunk/engines/sci/sound/drivers/mididriver.h (from rev 47417, scummvm/trunk/engines/sci/sound/softseq/mididriver.h)
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/mididriver.h	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/mididriver.h	2010-01-21 16:27:29 UTC (rev 47418)
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_SFX_SOFTSEQ_MIDIDRIVER_H
+#define SCI_SFX_SOFTSEQ_MIDIDRIVER_H
+
+#include "sci/sci.h"
+#include "sound/mididrv.h"
+#include "sound/softsynth/emumidi.h"
+#include "common/error.h"
+
+namespace Sci {
+
+class ResourceManager;
+
+enum {
+	MIDI_CHANNELS = 16,
+	MIDI_PROP_MASTER_VOLUME = 0
+};
+
+
+#define MIDI_RHYTHM_CHANNEL 9
+
+/* Special SCI sound stuff */
+
+#define SCI_MIDI_TIME_EXPANSION_PREFIX 0xF8
+#define SCI_MIDI_TIME_EXPANSION_LENGTH 240
+
+#define SCI_MIDI_EOT 0xFC
+#define SCI_MIDI_SET_SIGNAL 0xCF
+#define SCI_MIDI_SET_POLYPHONY 0x4B
+#define SCI_MIDI_RESET_ON_SUSPEND 0x4C
+#define SCI_MIDI_CHANNEL_MUTE 0x4E
+#define SCI_MIDI_SET_REVERB 0x50
+#define SCI_MIDI_HOLD 0x52
+#define SCI_MIDI_CUMULATIVE_CUE 0x60
+#define SCI_MIDI_CHANNEL_SOUND_OFF 0x78 /* all-sound-off for Bn */
+#define SCI_MIDI_CHANNEL_NOTES_OFF 0x7B /* all-notes-off for Bn */
+
+#define SCI_MIDI_SET_SIGNAL_LOOP 0x7F
+/* If this is the parameter of 0xCF, the loop point is set here */
+
+#define SCI_MIDI_CONTROLLER(status) ((status & 0xF0) == 0xB0)
+
+class MidiPlayer : public MidiDriver {
+protected:
+	MidiDriver *_driver;
+	byte _reverb;
+
+public:
+	MidiPlayer() : _reverb(0) { }
+
+	int open() {
+		ResourceManager *resMan = ((SciEngine *)g_engine)->getResourceManager();	// HACK
+		return open(resMan);
+	}
+	virtual int open(ResourceManager *resMan) { return _driver->open(); }
+	virtual void close() { _driver->close(); }
+	virtual void send(uint32 b) { _driver->send(b); }
+	uint32 getBaseTempo() { return _driver->getBaseTempo(); }
+	virtual bool hasRhythmChannel() const = 0;
+	MidiChannel *allocateChannel() { return _driver->allocateChannel(); }
+	MidiChannel *getPercussionChannel() { return _driver->getPercussionChannel(); }
+	void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); }
+
+	virtual byte getPlayId(SciVersion soundVersion) = 0;
+	virtual int getPolyphony() const = 0;
+
+	virtual void setVolume(byte volume) {
+		if(_driver)
+			_driver->property(MIDI_PROP_MASTER_VOLUME, volume);
+	}
+
+	virtual int getVolume() {
+		return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0;
+ 	}
+
+	virtual byte getReverb() { return _reverb; }
+	virtual void setReverb(byte reverb) { _reverb = reverb; }
+
+	virtual void playSwitch(bool play) {
+		if (!play) {
+			// Send "All Sound Off" on all channels
+			for (int i = 0; i < MIDI_CHANNELS; ++i)
+				_driver->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0);
+		}
+	}
+};
+
+extern MidiPlayer *MidiPlayer_AdLib_create();
+extern MidiPlayer *MidiPlayer_Amiga_create();
+extern MidiPlayer *MidiPlayer_PCJr_create();
+extern MidiPlayer *MidiPlayer_PCSpeaker_create();
+extern MidiPlayer *MidiPlayer_Midi_create();
+
+} // End of namespace Sci
+
+#endif // SCI_SFX_SOFTSEQ_MIDIDRIVER_H


Property changes on: scummvm/trunk/engines/sci/sound/drivers/mididriver.h
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Copied: scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp (from rev 47417, scummvm/trunk/engines/sci/sound/softseq/pcjr.cpp)
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp	2010-01-21 16:27:29 UTC (rev 47418)
@@ -0,0 +1,277 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/sound/drivers/mididriver.h"
+
+namespace Sci {
+
+#define VOLUME_SHIFT 3
+
+#define BASE_NOTE 129	// A10
+#define BASE_OCTAVE 10	// A10, as I said
+
+const static int freq_table[12] = { // A4 is 440Hz, halftone map is x |-> ** 2^(x/12)
+	28160, // A10
+	29834,
+	31608,
+	33488,
+	35479,
+	37589,
+	39824,
+	42192,
+	44701,
+	47359,
+	50175,
+	53159
+};
+
+static inline int get_freq(int note) {
+	int halftone_delta = note - BASE_NOTE;
+	int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE;
+	int halftone_index = (halftone_delta + (12 * 100)) % 12 ;
+	int freq = (!note) ? 0 : freq_table[halftone_index] / (1 << (-oct_diff));
+
+	return freq;
+}
+
+class MidiDriver_PCJr : public MidiDriver_Emulated {
+public:
+	friend class MidiPlayer_PCJr;
+
+	enum {
+		kMaxChannels = 3
+	};
+
+	MidiDriver_PCJr(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { }
+	~MidiDriver_PCJr() { }
+
+	// MidiDriver
+	int open() { return open(kMaxChannels); }
+	void close();
+	void send(uint32 b);
+	MidiChannel *allocateChannel() { return NULL; }
+	MidiChannel *getPercussionChannel() { return NULL; }
+
+	// AudioStream
+	bool isStereo() const { return false; }
+	int getRate() const { return _mixer->getOutputRate(); }
+
+	// MidiDriver_Emulated
+	void generateSamples(int16 *buf, int len);
+
+	int open(int channels);
+private:
+	int _channels_nr;
+	int _global_volume; // Base volume
+	int _volumes[kMaxChannels];
+	int _notes[kMaxChannels]; // Current halftone, or 0 if off
+	int _freq_count[kMaxChannels];
+	int _channel_assigner;
+	int _channels_assigned;
+	int _chan_nrs[kMaxChannels];
+};
+
+void MidiDriver_PCJr::send(uint32 b) {
+	byte command = b & 0xff;
+	byte op1 = (b >> 8) & 0xff;
+	byte op2 = (b >> 16) & 0xff;
+	int i;
+	int mapped_chan = -1;
+	int chan_nr = command & 0xf;
+
+	// First, test for channel having been assigned already
+	if (_channels_assigned & (1 << chan_nr)) {
+		// Already assigned this channel number:
+		for (i = 0; i < _channels_nr; i++)
+			if (_chan_nrs[i] == chan_nr) {
+				mapped_chan = i;
+				break;
+			}
+	} else if ((command & 0xe0) == 0x80) {
+		// Assign new channel round-robin
+
+		// Mark channel as unused:
+		if (_chan_nrs[_channel_assigner] >= 0)
+			_channels_assigned &= ~(1 << _chan_nrs[_channel_assigner]);
+
+		// Remember channel:
+		_chan_nrs[_channel_assigner] = chan_nr;
+		// Mark channel as used
+		_channels_assigned |= (1 << _chan_nrs[_channel_assigner]);
+
+		// Save channel for use later in this call:
+		mapped_chan = _channel_assigner;
+		// Round-ropin iterate channel assigner:
+		_channel_assigner = (_channel_assigner + 1) % _channels_nr;
+	}
+
+	if (mapped_chan == -1)
+		return;
+
+	switch (command & 0xf0) {
+
+	case 0x80:
+		if (op1 == _notes[mapped_chan])
+			_notes[mapped_chan] = 0;
+		break;
+
+	case 0x90:
+		if (!op2) {
+			if (op1 == _notes[mapped_chan])
+				_notes[mapped_chan] = 0;
+		} else {
+			_notes[mapped_chan] = op1;
+			_volumes[mapped_chan] = op2;
+		}
+		break;
+
+	case 0xb0:
+		if ((op1 == SCI_MIDI_CHANNEL_NOTES_OFF) || (op1 == SCI_MIDI_CHANNEL_SOUND_OFF))
+			_notes[mapped_chan] = 0;
+		break;
+
+	default:
+		debug(2, "Unused MIDI command %02x %02x %02x", command, op1, op2);
+		break; /* ignore */
+	}
+}
+
+void MidiDriver_PCJr::generateSamples(int16 *data, int len) {
+	int i;
+	int chan;
+	int freq[kMaxChannels];
+	int frequency = getRate();
+
+	for (chan = 0; chan < _channels_nr; chan++)
+		freq[chan] = get_freq(_notes[chan]);
+
+	for (i = 0; i < len; i++) {
+		int16 result = 0;
+
+		for (chan = 0; chan < _channels_nr; chan++)
+			if (_notes[chan]) {
+				int volume = (_global_volume * _volumes[chan])
+				             >> VOLUME_SHIFT;
+
+				_freq_count[chan] += freq[chan];
+				while (_freq_count[chan] >= (frequency << 1))
+					_freq_count[chan] -= (frequency << 1);
+
+				if (_freq_count[chan] - freq[chan] < 0) {
+					/* Unclean rising edge */
+					int l = volume << 1;
+					result += -volume + (l * _freq_count[chan]) / freq[chan];
+				} else if (_freq_count[chan] >= frequency
+				           && _freq_count[chan] - freq[chan] < frequency) {
+					/* Unclean falling edge */
+					int l = volume << 1;
+					result += volume - (l * (_freq_count[chan] - frequency)) / freq[chan];
+				} else {
+					if (_freq_count[chan] < frequency)
+						result += volume;
+					else
+						result += -volume;
+				}
+			}
+		data[i] = result;
+	}
+}
+
+int MidiDriver_PCJr::open(int channels) {
+	if (_isOpen)
+		return MERR_ALREADY_OPEN;
+
+	if (channels > kMaxChannels)
+		return -1;
+
+	_channels_nr = channels;
+	_global_volume = 100;
+	for (int i = 0; i < _channels_nr; i++) {
+		_volumes[i] = 100;
+		_notes[i] = 0;
+		_freq_count[i] = 0;
+		_chan_nrs[i] = -1;
+	}
+	_channel_assigner = 0;
+	_channels_assigned = 0;
+
+	MidiDriver_Emulated::open();
+
+	_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
+
+	return 0;
+}
+
+void MidiDriver_PCJr::close() {
+	_mixer->stopHandle(_mixerSoundHandle);
+}
+
+class MidiPlayer_PCJr : public MidiPlayer {
+public:
+	MidiPlayer_PCJr() { _driver = new MidiDriver_PCJr(g_system->getMixer()); }
+	int open(ResourceManager *resMan) { return static_cast<MidiDriver_PCJr *>(_driver)->open(getPolyphony()); }
+	byte getPlayId(SciVersion soundVersion);
+	int getPolyphony() const { return 3; }
+	bool hasRhythmChannel() const { return false; }
+	void setVolume(byte volume) { static_cast<MidiDriver_PCJr *>(_driver)->_global_volume = volume; }
+};
+
+byte MidiPlayer_PCJr::getPlayId(SciVersion soundVersion) {
+	switch (soundVersion) {
+	case SCI_VERSION_0_EARLY:
+		return 0x02;
+	case SCI_VERSION_0_LATE:
+		return 0x10;
+	default:
+		return 0x13;
+	}
+}
+
+MidiPlayer *MidiPlayer_PCJr_create() {
+	return new MidiPlayer_PCJr();
+}
+
+class MidiPlayer_PCSpeaker : public MidiPlayer_PCJr {
+public:
+	byte getPlayId(SciVersion soundVersion);
+	int getPolyphony() const { return 1; }
+};
+
+byte MidiPlayer_PCSpeaker::getPlayId(SciVersion soundVersion) {
+	switch (soundVersion) {
+	case SCI_VERSION_0_EARLY:
+		return 0x04;
+	case SCI_VERSION_0_LATE:
+		return 0x20;
+	default:
+		return 0x12;
+	}
+}
+
+MidiPlayer *MidiPlayer_PCSpeaker_create() {
+	return new MidiPlayer_PCSpeaker();
+}
+
+} // End of namespace Sci


Property changes on: scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Modified: scummvm/trunk/engines/sci/sound/iterator/core.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/iterator/core.cpp	2010-01-21 16:25:39 UTC (rev 47417)

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list