diff options
author | Walter van Niftrik | 2010-01-26 19:25:33 +0000 |
---|---|---|
committer | Walter van Niftrik | 2010-01-26 19:25:33 +0000 |
commit | 9861b04b935b84424b5b7950a2398dbfb44a99f9 (patch) | |
tree | 59a341fbe00b6cc9b4e51134ec2e746445183eff | |
parent | d6bb432a92c3600a5201217316aa7ed0321c898c (diff) | |
download | scummvm-rg350-9861b04b935b84424b5b7950a2398dbfb44a99f9.tar.gz scummvm-rg350-9861b04b935b84424b5b7950a2398dbfb44a99f9.tar.bz2 scummvm-rg350-9861b04b935b84424b5b7950a2398dbfb44a99f9.zip |
SCI: Add driver for Yamaha FB-01. Cleanup.
svn-id: r47571
-rw-r--r-- | engines/sci/module.mk | 3 | ||||
-rw-r--r-- | engines/sci/resource.cpp | 14 | ||||
-rw-r--r-- | engines/sci/resource.h | 1 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 6 | ||||
-rw-r--r-- | engines/sci/sci.h | 2 | ||||
-rw-r--r-- | engines/sci/sound/drivers/adlib.cpp | 14 | ||||
-rw-r--r-- | engines/sci/sound/drivers/amiga.cpp | 12 | ||||
-rw-r--r-- | engines/sci/sound/drivers/fb01.cpp | 648 | ||||
-rw-r--r-- | engines/sci/sound/drivers/midi.cpp | 14 | ||||
-rw-r--r-- | engines/sci/sound/drivers/mididriver.h | 20 | ||||
-rw-r--r-- | engines/sci/sound/drivers/pcjr.cpp | 24 | ||||
-rw-r--r-- | engines/sci/sound/iterator/core.cpp | 10 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.cpp | 10 | ||||
-rw-r--r-- | engines/sci/sound/music.cpp | 21 | ||||
-rw-r--r-- | engines/sci/sound/music.h | 8 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.cpp | 2 |
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) { |