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

waltervn at users.sourceforge.net waltervn at users.sourceforge.net
Tue Jan 26 20:25:34 CET 2010


Revision: 47571
          http://scummvm.svn.sourceforge.net/scummvm/?rev=47571&view=rev
Author:   waltervn
Date:     2010-01-26 19:25:33 +0000 (Tue, 26 Jan 2010)

Log Message:
-----------
SCI: Add driver for Yamaha FB-01. Cleanup.

Modified Paths:
--------------
    scummvm/trunk/engines/sci/module.mk
    scummvm/trunk/engines/sci/resource.cpp
    scummvm/trunk/engines/sci/resource.h
    scummvm/trunk/engines/sci/sci.cpp
    scummvm/trunk/engines/sci/sci.h
    scummvm/trunk/engines/sci/sound/drivers/adlib.cpp
    scummvm/trunk/engines/sci/sound/drivers/amiga.cpp
    scummvm/trunk/engines/sci/sound/drivers/midi.cpp
    scummvm/trunk/engines/sci/sound/drivers/mididriver.h
    scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp
    scummvm/trunk/engines/sci/sound/iterator/core.cpp
    scummvm/trunk/engines/sci/sound/midiparser_sci.cpp
    scummvm/trunk/engines/sci/sound/music.cpp
    scummvm/trunk/engines/sci/sound/music.h
    scummvm/trunk/engines/sci/sound/soundcmd.cpp

Added Paths:
-----------
    scummvm/trunk/engines/sci/sound/drivers/fb01.cpp

Modified: scummvm/trunk/engines/sci/module.mk
===================================================================
--- scummvm/trunk/engines/sci/module.mk	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/module.mk	2010-01-26 19:25:33 UTC (rev 47571)
@@ -61,8 +61,9 @@
 	sound/iterator/songlib.o \
 	sound/drivers/adlib.o \
 	sound/drivers/amiga.o \
+	sound/drivers/fb01.o \
+	sound/drivers/midi.o \
 	sound/drivers/pcjr.o \
-	sound/drivers/midi.o \
 	video/seq_decoder.o \
 	video/vmd_decoder.o
 	

Modified: scummvm/trunk/engines/sci/resource.cpp
===================================================================
--- scummvm/trunk/engines/sci/resource.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/resource.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -2152,4 +2152,18 @@
 	return channelMask;
 }
 
+byte SoundResource::getInitialVoiceCount(byte channel) {
+	byte *data = _innerResource->data;
+
+	if (_soundVersion > SCI_VERSION_0_LATE)
+		return 0; // TODO
+
+	data++; // Skip over digital sample flag
+
+	if (_soundVersion == SCI_VERSION_0_EARLY)
+		return data[channel] >> 4;
+	else
+		return data[channel * 2];
+}
+
 } // End of namespace Sci

Modified: scummvm/trunk/engines/sci/resource.h
===================================================================
--- scummvm/trunk/engines/sci/resource.h	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/resource.h	2010-01-26 19:25:33 UTC (rev 47571)
@@ -475,6 +475,7 @@
 	Track *getTrackByType(byte type);
 	Track *getDigitalTrack();
 	int getChannelFilterMask(int hardwareMask, bool wantsRhythm);
+	byte getInitialVoiceCount(byte channel);
 
 private:
 	SciVersion _soundVersion;

Modified: scummvm/trunk/engines/sci/sci.cpp
===================================================================
--- scummvm/trunk/engines/sci/sci.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sci.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -101,6 +101,10 @@
 Common::Error SciEngine::run() {
 	// FIXME/TODO: Move some of the stuff below to init()
 
+	// Assign default values to the config manager, in case settings are missing
+	ConfMan.registerDefault("undither", "true");
+	ConfMan.registerDefault("enable_fb01", "false");
+
 	_resMan = new ResourceManager();
 
 	if (!_resMan) {
@@ -168,8 +172,6 @@
 
 	_gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion);
 
-	// Assign default values to the config manager, in case settings are missing
-	ConfMan.registerDefault("undither", "true");
 	screen->unditherSetState(ConfMan.getBool("undither"));
 
 #ifdef USE_OLD_MUSIC_FUNCTIONS

Modified: scummvm/trunk/engines/sci/sci.h
===================================================================
--- scummvm/trunk/engines/sci/sci.h	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sci.h	2010-01-26 19:25:33 UTC (rev 47571)
@@ -41,8 +41,6 @@
  */
 namespace Sci {
 
-// Uncomment this to include old graphics code
-//#define INCLUDE_OLDGFX
 // Uncomment this to use old music functions
 //#define USE_OLD_MUSIC_FUNCTIONS
 // Uncomment this to use old pathfinding code

Modified: scummvm/trunk/engines/sci/sound/drivers/adlib.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/adlib.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/drivers/adlib.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -160,7 +160,7 @@
 
 class MidiPlayer_AdLib : public MidiPlayer {
 public:
-	MidiPlayer_AdLib() { _driver = new MidiDriver_AdLib(g_system->getMixer()); }
+	MidiPlayer_AdLib(SciVersion soundVersion) : MidiPlayer(soundVersion) { _driver = new MidiDriver_AdLib(g_system->getMixer()); }
 	~MidiPlayer_AdLib() {
 		delete _driver;
 		_driver = 0;
@@ -169,7 +169,7 @@
 	int open(ResourceManager *resMan);
 	void close();
 
-	byte getPlayId(SciVersion soundVersion);
+	byte getPlayId();
 	int getPolyphony() const { return MidiDriver_AdLib::kVoices; }
 	bool hasRhythmChannel() const { return false; }
 	void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); }
@@ -814,7 +814,7 @@
 		return -1;
 	}
 
-	return static_cast<MidiDriver_AdLib *>(_driver)->open(getSciVersion() <= SCI_VERSION_0_LATE);
+	return static_cast<MidiDriver_AdLib *>(_driver)->open(_version <= SCI_VERSION_0_LATE);
 }
 
 void MidiPlayer_AdLib::close() {
@@ -823,8 +823,8 @@
 	}
 }
 
-byte MidiPlayer_AdLib::getPlayId(SciVersion soundVersion) {
-	switch (soundVersion) {
+byte MidiPlayer_AdLib::getPlayId() {
+	switch (_version) {
 	case SCI_VERSION_0_EARLY:
 		return 0x01;
 	case SCI_VERSION_0_LATE:
@@ -834,8 +834,8 @@
 	}
 }
 
-MidiPlayer *MidiPlayer_AdLib_create() {
-	return new MidiPlayer_AdLib();
+MidiPlayer *MidiPlayer_AdLib_create(SciVersion _soundVersion) {
+	return new MidiPlayer_AdLib(_soundVersion);
 }
 
 } // End of namespace Sci

Modified: scummvm/trunk/engines/sci/sound/drivers/amiga.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/amiga.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/drivers/amiga.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -653,8 +653,8 @@
 
 class MidiPlayer_Amiga : public MidiPlayer {
 public:
-	MidiPlayer_Amiga() { _driver = new MidiDriver_Amiga(g_system->getMixer()); }
-	byte getPlayId(SciVersion soundVersion);
+	MidiPlayer_Amiga(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_Amiga(g_system->getMixer()); }
+	byte getPlayId();
 	int getPolyphony() const { return MidiDriver_Amiga::kVoices; }
 	bool hasRhythmChannel() const { return false; }
 	void setVolume(byte volume) { static_cast<MidiDriver_Amiga *>(_driver)->setVolume(volume); }
@@ -662,12 +662,12 @@
 	void loadInstrument(int idx, byte *data);
 };
 
-MidiPlayer *MidiPlayer_Amiga_create() {
-	return new MidiPlayer_Amiga();
+MidiPlayer *MidiPlayer_Amiga_create(SciVersion version) {
+	return new MidiPlayer_Amiga(version);
 }
 
-byte MidiPlayer_Amiga::getPlayId(SciVersion soundVersion) {
-	if (soundVersion != SCI_VERSION_0_LATE)
+byte MidiPlayer_Amiga::getPlayId() {
+	if (_version != SCI_VERSION_0_LATE)
 		error("Amiga sound support not available for this SCI version");
 
 	return 0x40;

Added: scummvm/trunk/engines/sci/sound/drivers/fb01.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/fb01.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/fb01.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -0,0 +1,648 @@
+/* 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 "sci/resource.h"
+#include "sci/sound/drivers/mididriver.h"
+
+namespace Sci {
+
+static byte volumeTable[64] = {
+	0x00, 0x10, 0x14, 0x18, 0x1f, 0x26, 0x2a, 0x2e,
+	0x2f, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36,
+	0x36, 0x37, 0x37, 0x38, 0x38, 0x38, 0x39, 0x39,
+	0x39, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b,
+	0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+	0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e,
+	0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+	0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f
+};
+
+class MidiPlayer_Fb01 : public MidiPlayer {
+public:
+	enum {
+		kVoices = 8,
+		kMaxSysExSize = 264
+	};
+
+	MidiPlayer_Fb01(SciVersion version);
+	virtual ~MidiPlayer_Fb01();
+
+	int open(ResourceManager *resMan);
+	void close();
+	void send(uint32 b);
+	void sysEx(const byte *msg, uint16 length);
+	bool hasRhythmChannel() const { return false; }
+	byte getPlayId();
+	int getPolyphony() const { return kVoices; } // 9 in SCI1?
+	void setVolume(byte volume);
+	int getVolume();
+	void playSwitch(bool play);
+
+private:
+	void noteOn(int channel, int note, int velocity);
+	void noteOff(int channel, int note);
+	void setPatch(int channel, int patch);
+	void controlChange(int channel, int control, int value);
+
+	void setVoiceParam(byte voice, byte param, byte value);
+	void setSystemParam(byte sysChan, byte param, byte value);
+	void sendVoiceData(byte instrument, const byte *data);
+	void sendBanks(const byte *data, int size);
+	void storeVoiceData(byte instrument, byte bank, byte index);
+	void initVoices();
+
+	void voiceOn(int voice, int note, int velocity);
+	void voiceOff(int voice);
+	int findVoice(int channel);
+	void voiceMapping(int channel, int voices);
+	void assignVoices(int channel, int voices);
+	void releaseVoices(int channel, int voices);
+	void donateVoices();
+	void sendToChannel(byte channel, byte command, byte op1, byte op2);
+
+	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(127), pan(64), holdPedal(0), extraVoices(0),
+					pitchWheel(8192), lastVoice(0), enableVelocity(false) { }
+	};
+
+	struct Voice {
+		int8 channel;			// MIDI channel that this voice is assigned to or -1
+		int8 note;				// Currently playing MIDI note or -1
+		int bank;				// Current bank setting 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
+
+		Voice() : channel(-1), note(-1), bank(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
+	};
+
+	bool _playSwitch;
+	int _masterVolume;
+
+	Channel _channels[16];
+	Voice _voices[kVoices];
+
+	Common::TimerManager::TimerProc _timerProc;
+	void *_timerParam;
+	static void midiTimerCallback(void *p);
+	void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
+
+	byte _sysExBuf[kMaxSysExSize];
+};
+
+MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL) {
+	MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
+	_driver = createMidi(midiType);
+
+	_sysExBuf[0] = 0x43;
+	_sysExBuf[1] = 0x75;
+}
+
+MidiPlayer_Fb01::~MidiPlayer_Fb01() {
+	delete _driver;
+}
+
+void MidiPlayer_Fb01::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, "FB-01: assigning %i additional voices to channel %i", voices - curVoices, channel);
+		assignVoices(channel, voices - curVoices);
+	} else if (curVoices > voices) {
+		debug(3, "FB-01: releasing %i voices from channel %i", curVoices - voices, channel);
+		releaseVoices(channel, curVoices - voices);
+		donateVoices();
+	}
+}
+
+void MidiPlayer_Fb01::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)
+				break;
+		}
+	}
+
+	_channels[channel].extraVoices += voices;
+	setPatch(channel, _channels[channel].patch);
+	sendToChannel(channel, 0xe0, _channels[channel].pitchWheel & 0x7f, _channels[channel].pitchWheel >> 7);
+	controlChange(channel, 0x07, _channels[channel].volume);
+	controlChange(channel, 0x0a, _channels[channel].pan);
+	controlChange(channel, 0x40, _channels[channel].holdPedal);
+}
+
+void MidiPlayer_Fb01::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 MidiPlayer_Fb01::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;
+		}
+	}
+}
+
+int MidiPlayer_Fb01::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 MidiPlayer_Fb01::sendToChannel(byte channel, byte command, byte op1, byte op2) {
+	for (int i = 0; i < kVoices; i++) {
+		// Send command to all voices assigned to this channel
+		if (_voices[i].channel == channel)
+			_driver->send(command | i, op1, op2);
+	}
+}
+
+void MidiPlayer_Fb01::setPatch(int channel, int patch) {
+	int bank = 0;
+
+	_channels[channel].patch = patch;
+
+	if (patch >= 48) {
+		patch -= 48;
+		bank = 1;
+	}
+
+	for (int voice = 0; voice < kVoices; voice++) {
+		if (_voices[voice].channel == channel) {
+			if (_voices[voice].bank != bank) {
+				_voices[voice].bank = bank;
+				setVoiceParam(voice, 4, bank);
+			}
+			_driver->send(0xc0 | voice, patch, 0);
+		}
+	}
+}
+
+void MidiPlayer_Fb01::voiceOn(int voice, int note, int velocity) {
+	if (_playSwitch) {
+		_voices[voice].note = note;
+		_voices[voice].age = 0;
+		_driver->send(0x90 | voice, note, velocity);
+	}
+}
+
+void MidiPlayer_Fb01::voiceOff(int voice) {
+	_voices[voice].note = -1;
+	_driver->send(0xb0 | voice, 0x7b, 0x00);
+}
+
+void MidiPlayer_Fb01::noteOff(int channel, int note) {
+	int voice;
+	for (voice = 0; voice < kVoices; voice++) {
+		if ((_voices[voice].channel == channel) && (_voices[voice].note == note)) {
+			voiceOff(voice);
+			return;
+		}
+	}
+}
+
+void MidiPlayer_Fb01::noteOn(int channel, int note, int velocity) {
+	if (velocity == 0)
+		return noteOff(channel, note);
+
+	if (_version > SCI_VERSION_0_LATE)
+		velocity = volumeTable[velocity >> 1] << 1;
+
+	int voice;
+	for (voice = 0; voice < kVoices; voice++) {
+		if ((_voices[voice].channel == channel) && (_voices[voice].note == note)) {
+			voiceOff(voice);
+			voiceOn(voice, note, velocity);
+			return;
+		}
+	}
+
+	voice = findVoice(channel);
+
+	if (voice == -1) {
+		debug(3, "FB-01: failed to find free voice assigned to channel %i", channel);
+		return;
+	}
+
+	voiceOn(voice, note, velocity);
+}
+
+void MidiPlayer_Fb01::controlChange(int channel, int control, int value) {
+	switch (control) {
+	case 0x07: {
+		_channels[channel].volume = value;
+
+		if (_version > SCI_VERSION_0_LATE)
+			value = volumeTable[value >> 1] << 1;
+
+		byte vol = _masterVolume;
+
+		if (vol > 0)
+			vol = CLIP<byte>(vol + 3, 0, 15);
+
+		sendToChannel(channel, 0xb0, control, (value * vol / 15) & 0x7f);
+		break;
+	}
+	case 0x0a:
+		_channels[channel].pan = value;
+		sendToChannel(channel, 0xb0, control, value);
+		break;
+	case 0x40:
+		_channels[channel].holdPedal = value;
+		sendToChannel(channel, 0xb0, control, value);
+		break;
+	case 0x4b:
+		// In early SCI0, voice count 15 signifies that the channel should be ignored
+		// for this song. Assuming that there are no embedded voice count commands in
+		// the MIDI stream, we should be able to get away with simply setting the voice
+		// count for this channel to 0.
+		voiceMapping(channel, (value != 15 ? value : 0));
+		break;
+	case 0x7b:
+		for (int i = 0; i < kVoices; i++)
+			if ((_voices[i].channel == channel) && (_voices[i].note != -1))
+				voiceOff(i);
+	}
+}
+
+void MidiPlayer_Fb01::send(uint32 b) {
+	byte command = b & 0xf0;
+	byte channel = b & 0xf;
+	byte op1 = (b >> 8) & 0x7f;
+	byte op2 = (b >> 16) & 0x7f;
+
+	switch (command) {
+	case 0x80:
+		noteOff(channel, op1);
+		break;
+	case 0x90:
+		noteOn(channel, op1, op2);
+		break;
+	case 0xb0:
+		controlChange(channel, op1, op2);
+		break;
+	case 0xc0:
+		setPatch(channel, op1);
+		break;
+	case 0xe0:
+		_channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7);
+		sendToChannel(channel, command, op1, op2);
+		break;
+	default:
+		warning("FB-01: Ignoring MIDI event %02x %02x %02x", command | channel, op1, op2);
+	}
+}
+
+void MidiPlayer_Fb01::setVolume(byte volume) {
+	_masterVolume = volume;
+
+	for (uint i = 0; i < MIDI_CHANNELS; i++)
+		controlChange(i, 0x07, _channels[i].volume & 0x7f);
+}
+
+int MidiPlayer_Fb01::getVolume() {
+	return _masterVolume;
+}
+
+void MidiPlayer_Fb01::playSwitch(bool play) {
+}
+
+void MidiPlayer_Fb01::midiTimerCallback(void *p) {
+	MidiPlayer_Fb01 *m = (MidiPlayer_Fb01 *)p;
+
+	// Increase the age of the notes
+	for (int i = 0; i < kVoices; i++) {
+		if (m->_voices[i].note != -1)
+			m->_voices[i].age++;
+	}
+
+	if (m->_timerProc)
+		m->_timerProc(m->_timerParam);
+}
+
+void MidiPlayer_Fb01::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
+	_driver->setTimerCallback(NULL, NULL);
+
+	_timerParam = timer_param;
+	_timerProc = timer_proc;
+
+	_driver->setTimerCallback(this, midiTimerCallback);
+}
+
+void MidiPlayer_Fb01::sendBanks(const byte *data, int size) {
+	if (size < 3072)
+		error("Failed to read FB-01 patch");
+
+	// SSCI sends bank dumps containing 48 instruments at once. We cannot do that
+	// due to the limited maximum SysEx length. Instead we send the instruments
+	// one by one and store them in the banks.
+	for (int i = 0; i < 48; i++) {
+		sendVoiceData(0, data + i * 64);
+		storeVoiceData(0, 0, i);
+	}
+
+	// Send second bank if available
+	if ((size >= 6146) && (READ_BE_UINT16(data + 3072) == 0xabcd)) {
+		for (int i = 0; i < 48; i++) {
+			sendVoiceData(0, data + 3074 + i * 64);
+			storeVoiceData(0, 1, i);
+		}
+	}
+}
+
+int MidiPlayer_Fb01::open(ResourceManager *resMan) {
+	assert(resMan != NULL);
+
+	int retval = _driver->open();
+	if (retval != 0) {
+		warning("Failed to open MIDI driver");
+		return retval;
+	}
+
+	// Set system channel to 0. We send this command over all 16 system channels
+	for (int i = 0; i < 16; i++)
+		setSystemParam(i, 0x20, 0);
+
+	// Turn off memory protection
+	setSystemParam(0, 0x21, 0);
+
+	Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 2), 0);
+
+	if (res) {
+		sendBanks(res->data, res->size);
+	} else {
+		warning("FB-01 patch file not found, attempting to load sound bank from IMF.DRV");
+		// Try to load sound bank from IMF.DRV
+		Common::File f;
+
+		if (f.open("IMF.DRV")) {
+			int size = f.size();
+			byte *buf = new byte[size];
+
+			f.read(buf, size);
+
+			// Search for start of sound bank
+			int offset;
+			for (offset = 0; offset < size; ++offset) {
+				if (!strncmp((char *)buf + offset, "SIERRA ", 7))
+					break;
+			}
+
+			// Skip to voice data
+			offset += 0x20;
+
+			if (offset >= size)
+				error("Failed to locate start of FB-01 sound bank");
+
+			sendBanks(buf + offset, size - offset);
+
+			delete[] buf;
+		} else
+			error("Failed to open IMF.DRV");
+	}
+
+	// Set up voices to use MIDI channels 0 - 7
+	for (int i = 0; i < kVoices; i++)
+		setVoiceParam(i, 1, i);
+
+	initVoices();
+
+	// Set master volume
+	setSystemParam(0, 0x24, 0x7f);
+
+	return 0;
+}
+
+void MidiPlayer_Fb01::close() {
+	_driver->close();
+}
+
+void MidiPlayer_Fb01::setVoiceParam(byte voice, byte param, byte value) {
+	_sysExBuf[2] = 0x00;
+	_sysExBuf[3] = 0x18 | voice;
+	_sysExBuf[4] = param;
+	_sysExBuf[5] = value;
+
+	_driver->sysEx(_sysExBuf, 6);
+}
+
+void MidiPlayer_Fb01::setSystemParam(byte sysChan, byte param, byte value) {
+	_sysExBuf[2] = sysChan;
+	_sysExBuf[3] = 0x10;
+	_sysExBuf[4] = param;
+	_sysExBuf[5] = value;
+
+	sysEx(_sysExBuf, 6);
+}
+
+void MidiPlayer_Fb01::sendVoiceData(byte instrument, const byte *data) {
+	_sysExBuf[2] = 0x00;
+	_sysExBuf[3] = 0x08 | instrument;
+	_sysExBuf[4] = 0x00;
+	_sysExBuf[5] = 0x00;
+	_sysExBuf[6] = 0x01;
+	_sysExBuf[7] = 0x00;
+
+	for (int i = 0; i < 64; i++) {
+		_sysExBuf[8 + i * 2] = data[i] & 0xf;
+		_sysExBuf[8 + i * 2 + 1] = data[i] >> 4;
+	}
+
+	byte checksum = 0;
+	for (int i = 8; i < 136; i++)
+		checksum += _sysExBuf[i];
+
+	_sysExBuf[136] = (-checksum) & 0x7f;
+
+	sysEx(_sysExBuf, 137);
+}
+
+void MidiPlayer_Fb01::storeVoiceData(byte instrument, byte bank, byte index) {
+	_sysExBuf[2] = 0x00;
+	_sysExBuf[3] = 0x28 | instrument;
+	_sysExBuf[4] = 0x40;
+	_sysExBuf[5] = (bank > 0 ? 48 : 0) + index;
+
+	sysEx(_sysExBuf, 6);
+}
+
+void MidiPlayer_Fb01::initVoices() {
+	int i = 2;
+	_sysExBuf[i++] = 0x70;
+
+	// Set all MIDI channels to 0 voices
+	for (int j = 0; j < MIDI_CHANNELS; j++) {
+		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x00;
+		_sysExBuf[i++] = 0x00;
+	}
+
+	// Set up the 8 MIDI channels we will be using
+	for (int j = 0; j < 8; j++) {
+		// One voice
+		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x00;
+		_sysExBuf[i++] = 0x01;
+
+		// Full range of keys
+		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x02;
+		_sysExBuf[i++] = 0x7f;
+		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x03;
+		_sysExBuf[i++] = 0x00;
+
+		// Voice bank 0
+		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x04;
+		_sysExBuf[i++] = 0x00;
+
+		// Voice 10
+		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x05;
+		_sysExBuf[i++] = 0x0a;
+	}
+
+	sysEx(_sysExBuf, i);
+}
+
+void MidiPlayer_Fb01::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;
+
+	delay += 10;
+
+	g_system->delayMillis(delay);
+	g_system->updateScreen();
+}
+
+byte MidiPlayer_Fb01::getPlayId() {
+	switch (_version) {
+	case SCI_VERSION_0_EARLY:
+		return 0x01;
+	case SCI_VERSION_0_LATE:
+		return 0x02;
+	default:
+		return 0x00;
+	}
+}
+
+MidiPlayer *MidiPlayer_Fb01_create(SciVersion version) {
+	return new MidiPlayer_Fb01(version);
+}
+
+} // End of namespace Sci


Property changes on: scummvm/trunk/engines/sci/sound/drivers/fb01.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/drivers/midi.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/midi.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/drivers/midi.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -43,7 +43,7 @@
 		kMaxSysExSize = 264
 	};
 
-	MidiPlayer_Midi();
+	MidiPlayer_Midi(SciVersion version);
 	virtual ~MidiPlayer_Midi();
 
 	int open(ResourceManager *resMan);
@@ -51,7 +51,7 @@
 	void send(uint32 b);
 	void sysEx(const byte *msg, uint16 length);
 	bool hasRhythmChannel() const { return true; }
-	byte getPlayId(SciVersion soundVersion);
+	byte getPlayId();
 	int getPolyphony() const { return kVoices; }
 	void setVolume(byte volume);
 	int getVolume();
@@ -116,7 +116,7 @@
 	byte _sysExBuf[kMaxSysExSize];
 };
 
-MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) {
+MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) {
 	MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
 	_driver = createMidi(midiType);
 
@@ -847,8 +847,8 @@
 	g_system->updateScreen();
 }
 
-byte MidiPlayer_Midi::getPlayId(SciVersion soundVersion) {
-	switch (soundVersion) {
+byte MidiPlayer_Midi::getPlayId() {
+	switch (_version) {
 	case SCI_VERSION_0_EARLY:
 	case SCI_VERSION_0_LATE:
 		return 0x01;
@@ -860,8 +860,8 @@
 	}
 }
 
-MidiPlayer *MidiPlayer_Midi_create() {
-	return new MidiPlayer_Midi();
+MidiPlayer *MidiPlayer_Midi_create(SciVersion version) {
+	return new MidiPlayer_Midi(version);
 }
 
 } // End of namespace Sci

Modified: scummvm/trunk/engines/sci/sound/drivers/mididriver.h
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/mididriver.h	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/drivers/mididriver.h	2010-01-26 19:25:33 UTC (rev 47571)
@@ -70,7 +70,7 @@
 	byte _reverb;
 
 public:
-	MidiPlayer() : _reverb(0) { }
+	MidiPlayer(SciVersion version) : _reverb(0), _version(version) { }
 
 	int open() {
 		ResourceManager *resMan = ((SciEngine *)g_engine)->getResourceManager();	// HACK
@@ -83,9 +83,9 @@
 	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 void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); }
 
-	virtual byte getPlayId(SciVersion soundVersion) = 0;
+	virtual byte getPlayId() = 0;
 	virtual int getPolyphony() const = 0;
 
 	virtual void setVolume(byte volume) {
@@ -107,13 +107,17 @@
 				_driver->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0);
 		}
 	}
+
+protected:
+	SciVersion _version;
 };
 
-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();
+extern MidiPlayer *MidiPlayer_AdLib_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_Amiga_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_PCJr_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_PCSpeaker_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_Midi_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_Fb01_create(SciVersion version);
 
 } // End of namespace Sci
 

Modified: scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/drivers/pcjr.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -230,16 +230,16 @@
 
 class MidiPlayer_PCJr : public MidiPlayer {
 public:
-	MidiPlayer_PCJr() { _driver = new MidiDriver_PCJr(g_system->getMixer()); }
+	MidiPlayer_PCJr(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_PCJr(g_system->getMixer()); }
 	int open(ResourceManager *resMan) { return static_cast<MidiDriver_PCJr *>(_driver)->open(getPolyphony()); }
-	byte getPlayId(SciVersion soundVersion);
+	byte getPlayId();
 	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) {
+byte MidiPlayer_PCJr::getPlayId() {
+	switch (_version) {
 	case SCI_VERSION_0_EARLY:
 		return 0x02;
 	case SCI_VERSION_0_LATE:
@@ -249,18 +249,20 @@
 	}
 }
 
-MidiPlayer *MidiPlayer_PCJr_create() {
-	return new MidiPlayer_PCJr();
+MidiPlayer *MidiPlayer_PCJr_create(SciVersion version) {
+	return new MidiPlayer_PCJr(version);
 }
 
 class MidiPlayer_PCSpeaker : public MidiPlayer_PCJr {
 public:
-	byte getPlayId(SciVersion soundVersion);
+	MidiPlayer_PCSpeaker(SciVersion version) : MidiPlayer_PCJr(version) { }
+
+	byte getPlayId();
 	int getPolyphony() const { return 1; }
 };
 
-byte MidiPlayer_PCSpeaker::getPlayId(SciVersion soundVersion) {
-	switch (soundVersion) {
+byte MidiPlayer_PCSpeaker::getPlayId() {
+	switch (_version) {
 	case SCI_VERSION_0_EARLY:
 		return 0x04;
 	case SCI_VERSION_0_LATE:
@@ -270,8 +272,8 @@
 	}
 }
 
-MidiPlayer *MidiPlayer_PCSpeaker_create() {
-	return new MidiPlayer_PCSpeaker();
+MidiPlayer *MidiPlayer_PCSpeaker_create(SciVersion version) {
+	return new MidiPlayer_PCSpeaker(version);
 }
 
 } // End of namespace Sci

Modified: scummvm/trunk/engines/sci/sound/iterator/core.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/iterator/core.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/iterator/core.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -229,15 +229,15 @@
 	case MD_ADLIB:
 		// FIXME: There's no Amiga sound option, so we hook it up to AdLib
 		if (((SciEngine *)g_engine)->getPlatform() == Common::kPlatformAmiga)
-			_mididrv = MidiPlayer_Amiga_create();
+			_mididrv = MidiPlayer_Amiga_create(_soundVersion);
 		else
-			_mididrv = MidiPlayer_AdLib_create();
+			_mididrv = MidiPlayer_AdLib_create(_soundVersion);
 		break;
 	case MD_PCJR:
-		_mididrv = MidiPlayer_PCJr_create();
+		_mididrv = MidiPlayer_PCJr_create(_soundVersion);
 		break;
 	case MD_PCSPK:
-		_mididrv = MidiPlayer_PCSpeaker_create();
+		_mididrv = MidiPlayer_PCSpeaker_create(_soundVersion);
 		break;
 	default:
 		break;
@@ -261,7 +261,7 @@
 
 Common::Error SfxPlayer::add_iterator(SongIterator *it, uint32 start_time) {
 	Common::StackLock lock(_mutex);
-	SIMSG_SEND(it, SIMSG_SET_PLAYMASK(_mididrv->getPlayId(_soundVersion)));
+	SIMSG_SEND(it, SIMSG_SET_PLAYMASK(_mididrv->getPlayId()));
 	SIMSG_SEND(it, SIMSG_SET_RHYTHM(_mididrv->hasRhythmChannel()));
 
 	if (_iterator == NULL) {

Modified: scummvm/trunk/engines/sci/sound/midiparser_sci.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/midiparser_sci.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/midiparser_sci.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -87,6 +87,16 @@
 	_loopTick = 0;
 	_channelsUsed = 0;
 
+	if (_soundVersion <= SCI_VERSION_0_LATE) {
+		// Set initial voice count
+		for (int i = 0; i < 16; ++i) {
+			byte voiceCount = 0;
+			if (channelFilterMask & (1 << i))
+				voiceCount = psnd->soundRes->getInitialVoiceCount(i);
+			_driver->send(0xB0 | i, 0x4B, voiceCount);
+		}
+	}
+
 	// Send a velocity off signal to all channels
 	for (int i = 0; i < 16; ++i) {
 		_driver->send(0xB0 | i, 0x4E, 0);	// Reset velocity

Modified: scummvm/trunk/engines/sci/sound/music.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/music.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/music.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -67,18 +67,21 @@
 	case MD_ADLIB:
 		// FIXME: There's no Amiga sound option, so we hook it up to AdLib
 		if (((SciEngine *)g_engine)->getPlatform() == Common::kPlatformAmiga)
-			_pMidiDrv = MidiPlayer_Amiga_create();
+			_pMidiDrv = MidiPlayer_Amiga_create(_soundVersion);
 		else
-			_pMidiDrv = MidiPlayer_AdLib_create();
+			_pMidiDrv = MidiPlayer_AdLib_create(_soundVersion);
 		break;
 	case MD_PCJR:
-		_pMidiDrv = MidiPlayer_PCJr_create();
+		_pMidiDrv = MidiPlayer_PCJr_create(_soundVersion);
 		break;
 	case MD_PCSPK:
-		_pMidiDrv = MidiPlayer_PCSpeaker_create();
+		_pMidiDrv = MidiPlayer_PCSpeaker_create(_soundVersion);
 		break;
 	default:
-		_pMidiDrv = MidiPlayer_Midi_create();
+		if (ConfMan.getBool("enable_fb01"))
+			_pMidiDrv = MidiPlayer_Fb01_create(_soundVersion);
+		else
+			_pMidiDrv = MidiPlayer_Midi_create(_soundVersion);
 	}
 
 	if (_pMidiDrv) {
@@ -165,7 +168,7 @@
 }
 void SciMusic::soundInitSnd(MusicEntry *pSnd) {
 	int channelFilterMask = 0;
-	SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId(_soundVersion));
+	SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId());
 
 	if (track) {
 		// If MIDI device is selected but there is no digital track in sound resource
@@ -202,7 +205,7 @@
 			pSnd->pauseCounter = 0;
 
 			// Find out what channels to filter for SCI0
-			channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(_soundVersion), _pMidiDrv->hasRhythmChannel());
+			channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel());
 			pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
 
 			// Fast forward to the last position and perform associated events when loading
@@ -394,7 +397,7 @@
 			if (song->pMidiParser) {
 				con->DebugPrintf("Type: MIDI\n");
 				if (song->soundRes) {
-					SoundResource::Track *track = song->soundRes->getTrackByType(_pMidiDrv->getPlayId(_soundVersion));
+					SoundResource::Track *track = song->soundRes->getTrackByType(_pMidiDrv->getPlayId());
 					con->DebugPrintf("Channels: %d\n", track->channelCount);
 				}
 			} else if (song->pStreamAud || song->pLoopStream) {
@@ -403,7 +406,7 @@
 					_pMixer->isSoundHandleActive(song->hCurrentAud) ? "yes" : "no");
 				if (song->soundRes) {
 					con->DebugPrintf("Sound resource information:\n");
-					SoundResource::Track *track = song->soundRes->getTrackByType(_pMidiDrv->getPlayId(_soundVersion));
+					SoundResource::Track *track = song->soundRes->getTrackByType(_pMidiDrv->getPlayId());
 					if (track && track->digitalChannelNr != -1) {
 						con->DebugPrintf("Sample size: %d, sample rate: %d, channels: %d, digital channel number: %d\n",
 							track->digitalSampleSize, track->digitalSampleRate, track->channelCount, track->digitalChannelNr);

Modified: scummvm/trunk/engines/sci/sound/music.h
===================================================================
--- scummvm/trunk/engines/sci/sound/music.h	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/music.h	2010-01-26 19:25:33 UTC (rev 47571)
@@ -41,14 +41,6 @@
 
 namespace Sci {
 
-enum TrackType {
-	kTrackAdLib = 0,
-	kTrackGameBlaster = 9,
-	kTrackMT32 = 12,
-	kTrackSpeaker = 18,
-	kTrackTandy = 19
-};
-
 enum SoundStatus {
 	kSoundStopped = 0,
 	kSoundInitialized = 1,

Modified: scummvm/trunk/engines/sci/sound/soundcmd.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/soundcmd.cpp	2010-01-26 15:41:24 UTC (rev 47570)
+++ scummvm/trunk/engines/sci/sound/soundcmd.cpp	2010-01-26 19:25:33 UTC (rev 47571)
@@ -209,7 +209,9 @@
 	for (SoundCommandContainer::iterator i = _soundCommands.begin(); i != _soundCommands.end(); ++i)
 		delete *i;
 
+#ifndef USE_OLD_MUSIC_FUNCTIONS
 	delete _music;
+#endif
 }
 
 reg_t SoundCommandParser::parseCommand(int argc, reg_t *argv, reg_t acc) {


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