aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sound/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/sound/drivers')
-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
6 files changed, 693 insertions, 39 deletions
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