aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorWalter van Niftrik2010-01-26 19:25:33 +0000
committerWalter van Niftrik2010-01-26 19:25:33 +0000
commit9861b04b935b84424b5b7950a2398dbfb44a99f9 (patch)
tree59a341fbe00b6cc9b4e51134ec2e746445183eff /engines
parentd6bb432a92c3600a5201217316aa7ed0321c898c (diff)
downloadscummvm-rg350-9861b04b935b84424b5b7950a2398dbfb44a99f9.tar.gz
scummvm-rg350-9861b04b935b84424b5b7950a2398dbfb44a99f9.tar.bz2
scummvm-rg350-9861b04b935b84424b5b7950a2398dbfb44a99f9.zip
SCI: Add driver for Yamaha FB-01. Cleanup.
svn-id: r47571
Diffstat (limited to 'engines')
-rw-r--r--engines/sci/module.mk3
-rw-r--r--engines/sci/resource.cpp14
-rw-r--r--engines/sci/resource.h1
-rw-r--r--engines/sci/sci.cpp6
-rw-r--r--engines/sci/sci.h2
-rw-r--r--engines/sci/sound/drivers/adlib.cpp14
-rw-r--r--engines/sci/sound/drivers/amiga.cpp12
-rw-r--r--engines/sci/sound/drivers/fb01.cpp648
-rw-r--r--engines/sci/sound/drivers/midi.cpp14
-rw-r--r--engines/sci/sound/drivers/mididriver.h20
-rw-r--r--engines/sci/sound/drivers/pcjr.cpp24
-rw-r--r--engines/sci/sound/iterator/core.cpp10
-rw-r--r--engines/sci/sound/midiparser_sci.cpp10
-rw-r--r--engines/sci/sound/music.cpp21
-rw-r--r--engines/sci/sound/music.h8
-rw-r--r--engines/sci/sound/soundcmd.cpp2
16 files changed, 743 insertions, 66 deletions
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 9435bc86ca..a230eb6565 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -61,8 +61,9 @@ MODULE_OBJS := \
sound/iterator/songlib.o \
sound/drivers/adlib.o \
sound/drivers/amiga.o \
- sound/drivers/pcjr.o \
+ sound/drivers/fb01.o \
sound/drivers/midi.o \
+ sound/drivers/pcjr.o \
video/seq_decoder.o \
video/vmd_decoder.o
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 862f9cc4f4..2f1110cc5a 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -2152,4 +2152,18 @@ int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
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
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 88ecbc1af1..247488f2e8 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -475,6 +475,7 @@ public:
Track *getTrackByType(byte type);
Track *getDigitalTrack();
int getChannelFilterMask(int hardwareMask, bool wantsRhythm);
+ byte getInitialVoiceCount(byte channel);
private:
SciVersion _soundVersion;
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 7b6147c28c..a2c355cb48 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -101,6 +101,10 @@ SciEngine::~SciEngine() {
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 @@ Common::Error SciEngine::run() {
_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
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index a12894306b..0d798ff2b1 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -41,8 +41,6 @@ struct ADGameDescription;
*/
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
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index bfcb114327..fa5e2c5697 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -160,7 +160,7 @@ private:
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 @@ public:
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 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
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 @@ void MidiPlayer_AdLib::close() {
}
}
-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 @@ byte MidiPlayer_AdLib::getPlayId(SciVersion soundVersion) {
}
}
-MidiPlayer *MidiPlayer_AdLib_create() {
- return new MidiPlayer_AdLib();
+MidiPlayer *MidiPlayer_AdLib_create(SciVersion _soundVersion) {
+ return new MidiPlayer_AdLib(_soundVersion);
}
} // End of namespace Sci
diff --git a/engines/sci/sound/drivers/amiga.cpp b/engines/sci/sound/drivers/amiga.cpp
index 1dca092cc2..b9fb17b84d 100644
--- a/engines/sci/sound/drivers/amiga.cpp
+++ b/engines/sci/sound/drivers/amiga.cpp
@@ -653,8 +653,8 @@ void MidiDriver_Amiga::generateSamples(int16 *data, int len) {
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 @@ public:
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;
diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp
new file mode 100644
index 0000000000..2a9f9b1239
--- /dev/null
+++ b/engines/sci/sound/drivers/fb01.cpp
@@ -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
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 76d1468580..9ce34aa9eb 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -43,7 +43,7 @@ public:
kMaxSysExSize = 264
};
- MidiPlayer_Midi();
+ MidiPlayer_Midi(SciVersion version);
virtual ~MidiPlayer_Midi();
int open(ResourceManager *resMan);
@@ -51,7 +51,7 @@ public:
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 @@ private:
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 @@ void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) {
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 @@ byte MidiPlayer_Midi::getPlayId(SciVersion soundVersion) {
}
}
-MidiPlayer *MidiPlayer_Midi_create() {
- return new MidiPlayer_Midi();
+MidiPlayer *MidiPlayer_Midi_create(SciVersion version) {
+ return new MidiPlayer_Midi(version);
}
} // End of namespace Sci
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index bc05a9aeba..ec67f8bb21 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -70,7 +70,7 @@ protected:
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 @@ public:
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 @@ public:
_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
diff --git a/engines/sci/sound/drivers/pcjr.cpp b/engines/sci/sound/drivers/pcjr.cpp
index eb264fb9dd..e9204d0c72 100644
--- a/engines/sci/sound/drivers/pcjr.cpp
+++ b/engines/sci/sound/drivers/pcjr.cpp
@@ -230,16 +230,16 @@ void MidiDriver_PCJr::close() {
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 @@ byte MidiPlayer_PCJr::getPlayId(SciVersion soundVersion) {
}
}
-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 @@ byte MidiPlayer_PCSpeaker::getPlayId(SciVersion soundVersion) {
}
}
-MidiPlayer *MidiPlayer_PCSpeaker_create() {
- return new MidiPlayer_PCSpeaker();
+MidiPlayer *MidiPlayer_PCSpeaker_create(SciVersion version) {
+ return new MidiPlayer_PCSpeaker(version);
}
} // End of namespace Sci
diff --git a/engines/sci/sound/iterator/core.cpp b/engines/sci/sound/iterator/core.cpp
index 95fc713ba1..daa92b70c5 100644
--- a/engines/sci/sound/iterator/core.cpp
+++ b/engines/sci/sound/iterator/core.cpp
@@ -229,15 +229,15 @@ Common::Error SfxPlayer::init(ResourceManager *resMan, int expected_latency) {
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::init(ResourceManager *resMan, int expected_latency) {
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) {
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index eab4d51489..79a3660931 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -87,6 +87,16 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
_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
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 8ef636c80f..1d8313f6f0 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -67,18 +67,21 @@ void SciMusic::init() {
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::sortPlayList() {
}
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 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
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 @@ void SciMusic::printSongInfo(reg_t obj, Console *con) {
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 @@ void SciMusic::printSongInfo(reg_t obj, Console *con) {
_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);
diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h
index 79eb72fc81..9f4bb1ce07 100644
--- a/engines/sci/sound/music.h
+++ b/engines/sci/sound/music.h
@@ -41,14 +41,6 @@
namespace Sci {
-enum TrackType {
- kTrackAdLib = 0,
- kTrackGameBlaster = 9,
- kTrackMT32 = 12,
- kTrackSpeaker = 18,
- kTrackTandy = 19
-};
-
enum SoundStatus {
kSoundStopped = 0,
kSoundInitialized = 1,
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index 74b44953fb..3f1f29711d 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -209,7 +209,9 @@ SoundCommandParser::~SoundCommandParser() {
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) {