diff options
Diffstat (limited to 'engines/sci/sound/drivers')
-rw-r--r-- | engines/sci/sound/drivers/adlib.cpp | 10 | ||||
-rw-r--r-- | engines/sci/sound/drivers/amigamac.cpp | 4 | ||||
-rw-r--r-- | engines/sci/sound/drivers/cms.cpp | 817 | ||||
-rw-r--r-- | engines/sci/sound/drivers/fb01.cpp | 4 | ||||
-rw-r--r-- | engines/sci/sound/drivers/map-mt32-to-gm.h | 170 | ||||
-rw-r--r-- | engines/sci/sound/drivers/midi.cpp | 58 | ||||
-rw-r--r-- | engines/sci/sound/drivers/mididriver.h | 25 | ||||
-rw-r--r-- | engines/sci/sound/drivers/pcjr.cpp | 8 |
8 files changed, 985 insertions, 111 deletions
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp index 55c3640c9d..20bac4a2c0 100644 --- a/engines/sci/sound/drivers/adlib.cpp +++ b/engines/sci/sound/drivers/adlib.cpp @@ -73,6 +73,8 @@ public: bool loadResource(const byte *data, uint size); virtual uint32 property(int prop, uint32 param); + bool useRhythmChannel() const { return _rhythmKeyMap != NULL; } + private: enum ChannelID { kLeftChannel = 1, @@ -171,12 +173,14 @@ public: int open(ResourceManager *resMan); void close(); - byte getPlayId(); + byte getPlayId() const; int getPolyphony() const { return MidiDriver_AdLib::kVoices; } bool hasRhythmChannel() const { return false; } void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); } void playSwitch(bool play) { static_cast<MidiDriver_AdLib *>(_driver)->playSwitch(play); } void loadInstrument(int idx, byte *data); + + int getLastChannel() const { return (static_cast<const MidiDriver_AdLib *>(_driver)->useRhythmChannel() ? 8 : 15); } }; static const byte registerOffset[MidiDriver_AdLib::kVoices] = { @@ -586,7 +590,7 @@ void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) { } // Set patch if different from current patch - if ((patch != _voices[voice].patch) && _playSwitch) + if (patch != _voices[voice].patch) setPatch(voice, patch); _voices[voice].velocity = velocity; @@ -833,7 +837,7 @@ void MidiPlayer_AdLib::close() { } } -byte MidiPlayer_AdLib::getPlayId() { +byte MidiPlayer_AdLib::getPlayId() const { switch (_version) { case SCI_VERSION_0_EARLY: return 0x01; diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp index 4fb9146b53..4b591eb609 100644 --- a/engines/sci/sound/drivers/amigamac.cpp +++ b/engines/sci/sound/drivers/amigamac.cpp @@ -966,7 +966,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI1(Common::SeekableReadStream &file) class MidiPlayer_AmigaMac : public MidiPlayer { public: MidiPlayer_AmigaMac(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_AmigaMac(g_system->getMixer()); } - byte getPlayId(); + byte getPlayId() const; int getPolyphony() const { return MidiDriver_AmigaMac::kVoices; } bool hasRhythmChannel() const { return false; } void setVolume(byte volume) { static_cast<MidiDriver_AmigaMac *>(_driver)->setVolume(volume); } @@ -978,7 +978,7 @@ MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version) { return new MidiPlayer_AmigaMac(version); } -byte MidiPlayer_AmigaMac::getPlayId() { +byte MidiPlayer_AmigaMac::getPlayId() const { if (_version > SCI_VERSION_0_LATE) return 0x06; diff --git a/engines/sci/sound/drivers/cms.cpp b/engines/sci/sound/drivers/cms.cpp new file mode 100644 index 0000000000..cd7b101f03 --- /dev/null +++ b/engines/sci/sound/drivers/cms.cpp @@ -0,0 +1,817 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "sci/sound/drivers/mididriver.h" + +#include "sound/softsynth/emumidi.h" +#include "sound/softsynth/cms.h" +#include "sound/mixer.h" + +#include "sci/resource.h" + +namespace Sci { + +// FIXME: We don't seem to be sending the polyphony init data, so disable this for now +#define CMS_DISABLE_VOICE_MAPPING + +class MidiDriver_CMS : public MidiDriver_Emulated { +public: + MidiDriver_CMS(Audio::Mixer *mixer, ResourceManager *resMan) + : MidiDriver_Emulated(mixer), _resMan(resMan), _cms(0), _rate(0), _playSwitch(true), _masterVolume(0) { + } + + int open(); + void close(); + + void send(uint32 b); + uint32 property(int prop, uint32 param); + + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + + bool isStereo() const { return true; } + int getRate() const { return _rate; } + + void playSwitch(bool play); +private: + void generateSamples(int16 *buffer, int len); + + ResourceManager *_resMan; + CMSEmulator *_cms; + + void writeToChip1(int address, int data); + void writeToChip2(int address, int data); + + int32 _samplesPerCallback; + int32 _samplesPerCallbackRemainder; + int32 _samplesTillCallback; + int32 _samplesTillCallbackRemainder; + + int _rate; + bool _playSwitch; + uint16 _masterVolume; + + uint8 *_patchData; + + struct Channel { + Channel() + : patch(0), volume(0), pan(0x40), hold(0), extraVoices(0), + pitchWheel(0x2000), pitchModifier(0), pitchAdditive(false), + lastVoiceUsed(0) { + } + + uint8 patch; + uint8 volume; + uint8 pan; + uint8 hold; + uint8 extraVoices; + uint16 pitchWheel; + uint8 pitchModifier; + bool pitchAdditive; + uint8 lastVoiceUsed; + }; + + Channel _channel[16]; + + struct Voice { + Voice() : channel(0xFF), note(0xFF), sustained(0xFF), ticks(0), + turnOffTicks(0), patchDataPtr(0), patchDataIndex(0), + amplitudeTimer(0), amplitudeModifier(0), turnOff(false), + velocity(0) { + } + + uint8 channel; + uint8 note; + uint8 sustained; + uint16 ticks; + uint16 turnOffTicks; + const uint8 *patchDataPtr; + uint8 patchDataIndex; + uint8 amplitudeTimer; + uint8 amplitudeModifier; + bool turnOff; + uint8 velocity; + }; + + Voice _voice[12]; + + void voiceOn(int voice, int note, int velocity); + void voiceOff(int voice); + + void noteSend(int voice); + + void noteOn(int channel, int note, int velocity); + void noteOff(int channel, int note); + void controlChange(int channel, int control, int value); + void pitchWheel(int channel, int value); + + void voiceMapping(int channel, int value); + void bindVoices(int channel, int voices); + void unbindVoices(int channel, int voices); + void donateVoices(); + int findVoice(int channel); + + int findVoiceBasic(int channel); + + void updateVoiceAmplitude(int voice); + void setupVoiceAmplitude(int voice); + + uint8 _octaveRegs[2][3]; + + static const int _timerFreq = 60; + + static const int _frequencyTable[]; + static const int _velocityTable[]; +}; + +const int MidiDriver_CMS::_frequencyTable[] = { + 3, 10, 17, 24, + 31, 38, 46, 51, + 58, 64, 71, 77, + 83, 89, 95, 101, + 107, 113, 119, 124, + 130, 135, 141, 146, + 151, 156, 162, 167, + 172, 177, 182, 186, + 191, 196, 200, 205, + 209, 213, 217, 222, + 226, 230, 234, 238, + 242, 246, 250, 253 +}; + +const int MidiDriver_CMS::_velocityTable[] = { + 1, 3, 6, 8, 9, 10, 11, 12, + 12, 13, 13, 14, 14, 14, 15, 15, + 0, 1, 2, 2, 3, 4, 4, 5, + 6, 6, 7, 8, 8, 9, 10, 10 +}; + +int MidiDriver_CMS::open() { + if (_cms) + return MERR_ALREADY_OPEN; + + assert(_resMan); + Resource *res = _resMan->findResource(ResourceId(kResourceTypePatch, 101), 0); + if (!res) + return -1; + + _patchData = new uint8[res->size]; + memcpy(_patchData, res->data, res->size); + + for (uint i = 0; i < ARRAYSIZE(_channel); ++i) + _channel[i] = Channel(); + + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) + _voice[i] = Voice(); + + _rate = _mixer->getOutputRate(); + _cms = new CMSEmulator(_rate); + assert(_cms); + _playSwitch = true; + _masterVolume = 0; + + for (int i = 0; i < 31; ++i) { + writeToChip1(i, 0); + writeToChip2(i, 0); + } + + writeToChip1(0x14, 0xFF); + writeToChip2(0x14, 0xFF); + + writeToChip1(0x1C, 1); + writeToChip2(0x1C, 1); + + _samplesPerCallback = getRate() / _timerFreq; + _samplesPerCallbackRemainder = getRate() % _timerFreq; + _samplesTillCallback = 0; + _samplesTillCallbackRemainder = 0; + + int retVal = MidiDriver_Emulated::open(); + if (retVal != 0) + return retVal; + + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + return 0; +} + +void MidiDriver_CMS::close() { + _mixer->stopHandle(_mixerSoundHandle); + + delete[] _patchData; + delete _cms; + _cms = 0; +} + +void MidiDriver_CMS::send(uint32 b) { + const uint8 command = b & 0xf0; + const uint8 channel = b & 0xf; + const uint8 op1 = (b >> 8) & 0xff; + const uint8 op2 = (b >> 16) & 0xff; + + switch (command) { + case 0x80: + noteOff(channel, op1); + break; + + case 0x90: + noteOn(channel, op1, op2); + break; + + case 0xB0: + controlChange(channel, op1, op2); + break; + + case 0xC0: + _channel[channel].patch = op1; + break; + + case 0xE0: + pitchWheel(channel, (op1 & 0x7f) | ((op2 & 0x7f) << 7)); + break; + + default: + break; + } +} + +uint32 MidiDriver_CMS::property(int prop, uint32 param) { + switch (prop) { + case MIDI_PROP_MASTER_VOLUME: + if (param != 0xffff) + _masterVolume = param; + return _masterVolume; + + default: + return MidiDriver_Emulated::property(prop, param); + } +} + +void MidiDriver_CMS::playSwitch(bool play) { + _playSwitch = play; +} + +void MidiDriver_CMS::writeToChip1(int address, int data) { + _cms->portWrite(0x221, address); + _cms->portWrite(0x220, data); + + if (address >= 16 && address <= 18) + _octaveRegs[0][address - 16] = data; +} + +void MidiDriver_CMS::writeToChip2(int address, int data) { + _cms->portWrite(0x223, address); + _cms->portWrite(0x222, data); + + if (address >= 16 && address <= 18) + _octaveRegs[1][address - 16] = data; +} + +void MidiDriver_CMS::voiceOn(int voiceNr, int note, int velocity) { + Voice &voice = _voice[voiceNr]; + voice.note = note; + voice.turnOff = false; + voice.patchDataIndex = 0; + voice.amplitudeTimer = 0; + voice.ticks = 0; + voice.turnOffTicks = 0; + voice.patchDataPtr = _patchData + READ_LE_UINT16(&_patchData[_channel[voice.channel].patch * 2]); + if (velocity) + velocity = _velocityTable[(velocity >> 3)]; + voice.velocity = velocity; + noteSend(voiceNr); +} + +void MidiDriver_CMS::voiceOff(int voiceNr) { + Voice &voice = _voice[voiceNr]; + voice.velocity = 0; + voice.note = 0xFF; + voice.sustained = 0; + voice.turnOff = false; + voice.patchDataIndex = 0; + voice.amplitudeTimer = 0; + voice.amplitudeModifier = 0; + voice.ticks = 0; + voice.turnOffTicks = 0; + + setupVoiceAmplitude(voiceNr); +} + +void MidiDriver_CMS::noteSend(int voiceNr) { + Voice &voice = _voice[voiceNr]; + + int frequency = (CLIP<int>(voice.note, 21, 116) - 21) * 4; + if (_channel[voice.channel].pitchModifier) { + int modifier = _channel[voice.channel].pitchModifier; + + if (!_channel[voice.channel].pitchAdditive) { + if (frequency > modifier) + frequency -= modifier; + else + frequency = 0; + } else { + int tempFrequency = 384 - frequency; + if (modifier < tempFrequency) + frequency += modifier; + else + frequency = 383; + } + } + + int chipNumber = 0; + if (voiceNr >= 6) { + voiceNr -= 6; + chipNumber = 1; + } + + int octave = 0; + while (frequency >= 48) { + frequency -= 48; + ++octave; + } + + frequency = _frequencyTable[frequency]; + + if (chipNumber == 1) + writeToChip2(8 + voiceNr, frequency); + else + writeToChip1(8 + voiceNr, frequency); + + uint8 octaveData = _octaveRegs[chipNumber][voiceNr >> 1]; + + if (voiceNr & 1) { + octaveData &= 0x0F; + octaveData |= (octave << 4); + } else { + octaveData &= 0xF0; + octaveData |= octave; + } + + if (chipNumber == 1) + writeToChip2(0x10 + (voiceNr >> 1), octaveData); + else + writeToChip1(0x10 + (voiceNr >> 1), octaveData); +} + +void MidiDriver_CMS::noteOn(int channel, int note, int velocity) { + if (note < 21 || note > 116) + return; + + if (velocity == 0) { + noteOff(channel, note); + return; + } + + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == channel && _voice[i].note == note) { + _voice[i].sustained = 0; + voiceOff(i); + voiceOn(i, note, velocity); + return; + } + } + +#ifdef CMS_DISABLE_VOICE_MAPPING + int voice = findVoiceBasic(channel); +#else + int voice = findVoice(channel); +#endif + if (voice != -1) + voiceOn(voice, note, velocity); +} + +int MidiDriver_CMS::findVoiceBasic(int channel) { + int voice = -1; + int oldestVoice = -1; + int oldestAge = -1; + + // Try to find a voice assigned to this channel that is free (round-robin) + for (int i = 0; i < ARRAYSIZE(_voice); i++) { + int v = (_channel[channel].lastVoiceUsed + i + 1) % ARRAYSIZE(_voice); + + if (_voice[v].note == 0xFF) { + voice = v; + break; + } + + // We also keep track of the oldest note in case the search fails + if (_voice[v].ticks > oldestAge) { + oldestAge = _voice[v].ticks; + oldestVoice = v; + } + } + + if (voice == -1) { + if (oldestVoice != -1) { + voiceOff(oldestVoice); + voice = oldestVoice; + } else { + return -1; + } + } + + _voice[voice].channel = channel; + _channel[channel].lastVoiceUsed = voice; + return voice; +} + +void MidiDriver_CMS::noteOff(int channel, int note) { + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == channel && _voice[i].note == note) { + if (_channel[channel].hold != 0) + _voice[i].sustained = true; + else + _voice[i].turnOff = true; + } + } +} + +void MidiDriver_CMS::controlChange(int channel, int control, int value) { + switch (control) { + case 7: + if (value) { + value >>= 3; + if (!value) + ++value; + } + + _channel[channel].volume = value; + break; + + case 10: + _channel[channel].pan = value; + break; + + case 64: + _channel[channel].hold = value; + + if (!value) { + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == channel && _voice[i].sustained) { + _voice[i].sustained = 0; + _voice[i].turnOff = true; + } + } + } + break; + + case 75: +#ifndef CMS_DISABLE_VOICE_MAPPING + voiceMapping(channel, value); +#endif + break; + + case 123: + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == channel && _voice[i].note != 0xFF) + voiceOff(i); + } + break; + + default: + return; + } +} + +void MidiDriver_CMS::pitchWheel(int channelNr, int value) { + Channel &channel = _channel[channelNr]; + channel.pitchWheel = value; + channel.pitchAdditive = false; + channel.pitchModifier = 0; + + if (value < 0x2000) { + channel.pitchModifier = (0x2000 - value) / 170; + } else if (value > 0x2000) { + channel.pitchModifier = (value - 0x2000) / 170; + channel.pitchAdditive = true; + } + + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == channelNr && _voice[i].note != 0xFF) + noteSend(i); + } +} + +void MidiDriver_CMS::voiceMapping(int channelNr, int value) { + int curVoices = 0; + + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == channelNr) + ++curVoices; + } + + curVoices += _channel[channelNr].extraVoices; + + if (curVoices == value) { + return; + } else if (curVoices < value) { + bindVoices(channelNr, value - curVoices); + } else { + unbindVoices(channelNr, curVoices - value); + donateVoices(); + } +} + +void MidiDriver_CMS::bindVoices(int channel, int voices) { + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == 0xFF) + continue; + + Voice &voice = _voice[i]; + voice.channel = channel; + + if (voice.note != 0xFF) + voiceOff(i); + + --voices; + if (voices == 0) + break; + } + + _channel[channel].extraVoices += voices; + + // The original called "PatchChange" here, since this just + // copies the value of _channel[channel].patch to itself + // it is left out here though. +} + +void MidiDriver_CMS::unbindVoices(int channelNr, int voices) { + Channel &channel = _channel[channelNr]; + + if (channel.extraVoices >= voices) { + channel.extraVoices -= voices; + } else { + voices -= channel.extraVoices; + channel.extraVoices = 0; + + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == channelNr + && _voice[i].note == 0xFF) { + --voices; + if (voices == 0) + return; + } + } + + do { + uint16 voiceTime = 0; + uint voiceNr = 0; + + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel != channelNr) + continue; + + uint16 curTime = _voice[i].turnOffTicks; + if (curTime) + curTime += 0x8000; + else + curTime = _voice[i].ticks; + + if (curTime >= voiceTime) { + voiceNr = i; + voiceTime = curTime; + } + } + + _voice[voiceNr].sustained = 0; + voiceOff(voiceNr); + _voice[voiceNr].channel = 0xFF; + --voices; + } while (voices != 0); + } +} + +void MidiDriver_CMS::donateVoices() { + int freeVoices = 0; + + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].channel == 0xFF) + ++freeVoices; + } + + if (!freeVoices) + return; + + for (uint i = 0; i < ARRAYSIZE(_channel); ++i) { + Channel &channel = _channel[i]; + + if (!channel.extraVoices) { + continue; + } else if (channel.extraVoices < freeVoices) { + freeVoices -= channel.extraVoices; + channel.extraVoices = 0; + bindVoices(i, channel.extraVoices); + } else { + channel.extraVoices -= freeVoices; + bindVoices(i, freeVoices); + return; + } + } +} + +int MidiDriver_CMS::findVoice(int channelNr) { + Channel &channel = _channel[channelNr]; + int voiceNr = channel.lastVoiceUsed; + + int newVoice = 0; + uint16 newVoiceTime = 0; + + bool loopDone = false; + do { + ++voiceNr; + + if (voiceNr == 12) + voiceNr = 0; + + Voice &voice = _voice[voiceNr]; + + if (voiceNr == channel.lastVoiceUsed) + loopDone = true; + + if (voice.channel == channelNr) { + if (voice.note == 0xFF) { + channel.lastVoiceUsed = voiceNr; + return voiceNr; + } + + uint16 curTime = voice.turnOffTicks; + if (curTime) + curTime += 0x8000; + else + curTime = voice.ticks; + + if (curTime >= newVoiceTime) { + newVoice = voiceNr; + newVoiceTime = curTime; + } + } + } while (!loopDone); + + if (newVoiceTime > 0) { + voiceNr = newVoice; + _voice[voiceNr].sustained = 0; + voiceOff(voiceNr); + channel.lastVoiceUsed = voiceNr; + return voiceNr; + } else { + return -1; + } +} + +void MidiDriver_CMS::updateVoiceAmplitude(int voiceNr) { + Voice &voice = _voice[voiceNr]; + + if (voice.amplitudeTimer != 0 && voice.amplitudeTimer != 254) { + --voice.amplitudeTimer; + return; + } else if (voice.amplitudeTimer == 254) { + if (!voice.turnOff) + return; + + voice.amplitudeTimer = 0; + } + + int nextDataIndex = voice.patchDataIndex; + uint8 timerData = 0; + uint8 amplitudeData = voice.patchDataPtr[nextDataIndex]; + + if (amplitudeData == 255) { + timerData = amplitudeData = 0; + voiceOff(voiceNr); + } else { + timerData = voice.patchDataPtr[nextDataIndex + 1]; + nextDataIndex += 2; + } + + voice.patchDataIndex = nextDataIndex; + voice.amplitudeTimer = timerData; + voice.amplitudeModifier = amplitudeData; +} + +void MidiDriver_CMS::setupVoiceAmplitude(int voiceNr) { + Voice &voice = _voice[voiceNr]; + uint amplitude = 0; + + if (_channel[voice.channel].volume && voice.velocity + && voice.amplitudeModifier && _masterVolume) { + amplitude = _channel[voice.channel].volume * voice.velocity; + amplitude /= 0x0F; + amplitude *= voice.amplitudeModifier; + amplitude /= 0x0F; + amplitude *= _masterVolume; + amplitude /= 0x0F; + + if (!amplitude) + ++amplitude; + } + + uint8 amplitudeData = 0; + int pan = _channel[voice.channel].pan >> 2; + if (pan >= 16) { + amplitudeData = (amplitude * (31 - pan) / 0x0F) & 0x0F; + amplitudeData |= (amplitude << 4); + } else { + amplitudeData = (amplitude * pan / 0x0F) & 0x0F; + amplitudeData <<= 4; + amplitudeData |= amplitude; + } + + if (!_playSwitch) + amplitudeData = 0; + + if (voiceNr >= 6) + writeToChip2(voiceNr - 6, amplitudeData); + else + writeToChip1(voiceNr, amplitudeData); +} + +void MidiDriver_CMS::generateSamples(int16 *buffer, int len) { + while (len) { + if (!_samplesTillCallback) { + for (uint i = 0; i < ARRAYSIZE(_voice); ++i) { + if (_voice[i].note == 0xFF) + continue; + + ++_voice[i].ticks; + if (_voice[i].turnOff) + ++_voice[i].turnOffTicks; + + updateVoiceAmplitude(i); + setupVoiceAmplitude(i); + } + + _samplesTillCallback = _samplesPerCallback; + _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; + if (_samplesTillCallbackRemainder >= _timerFreq) { + _samplesTillCallback++; + _samplesTillCallbackRemainder -= _timerFreq; + } + } + + int32 render = MIN<int32>(len, _samplesTillCallback); + len -= render; + _samplesTillCallback -= render; + _cms->readBuffer(buffer, render); + buffer += render * 2; + } +} + + +class MidiPlayer_CMS : public MidiPlayer { +public: + MidiPlayer_CMS(SciVersion version) : MidiPlayer(version) { + } + + int open(ResourceManager *resMan) { + if (_driver) + return MERR_ALREADY_OPEN; + + _driver = new MidiDriver_CMS(g_system->getMixer(), resMan); + int driverRetVal = _driver->open(); + if (driverRetVal != 0) + return driverRetVal; + + return 0; + } + + void close() { + _driver->setTimerCallback(0, 0); + _driver->close(); + delete _driver; + _driver = 0; + } + + bool hasRhythmChannel() const { return false; } + byte getPlayId() const { return 9; } + int getPolyphony() const { return 12; } + + void playSwitch(bool play) { static_cast<MidiDriver_CMS *>(_driver)->playSwitch(play); } +}; + +MidiPlayer *MidiPlayer_CMS_create(SciVersion version) { + return new MidiPlayer_CMS(version); +} + +} // End of namespace SCI + diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp index ab9b2e3df5..7560c62f4f 100644 --- a/engines/sci/sound/drivers/fb01.cpp +++ b/engines/sci/sound/drivers/fb01.cpp @@ -59,7 +59,7 @@ public: void send(uint32 b); void sysEx(const byte *msg, uint16 length); bool hasRhythmChannel() const { return false; } - byte getPlayId(); + byte getPlayId() const; int getPolyphony() const { return kVoices; } // 9 in SCI1? void setVolume(byte volume); int getVolume(); @@ -633,7 +633,7 @@ void MidiPlayer_Fb01::sysEx(const byte *msg, uint16 length) { g_system->updateScreen(); } -byte MidiPlayer_Fb01::getPlayId() { +byte MidiPlayer_Fb01::getPlayId() const { switch (_version) { case SCI_VERSION_0_EARLY: return 0x01; diff --git a/engines/sci/sound/drivers/map-mt32-to-gm.h b/engines/sci/sound/drivers/map-mt32-to-gm.h index a552ef0608..05d1aeba24 100644 --- a/engines/sci/sound/drivers/map-mt32-to-gm.h +++ b/engines/sci/sound/drivers/map-mt32-to-gm.h @@ -421,131 +421,131 @@ static const Mt32ToGmMap Mt32MemoryTimbreMaps[] = { {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (PQ2) */ {"AcouPnoKA ", 0, MIDI_UNMAPPED}, /* ++ (KQ1) */ {"BASS ", 32, MIDI_UNMAPPED}, /* + (LSL3) */ - {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (CB) */ + {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (LB1) */ {"BEACH WAVE", 122, MIDI_UNMAPPED}, /* + (LSL3) */ {"BagPipes ", 109, MIDI_UNMAPPED}, - {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (HQ) */ + {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (QFG1) */ {"BassoonKA ", 70, MIDI_UNMAPPED}, /* ++ (KQ1) */ - {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (iceMan) */ - {"Bells MS", 112, MIDI_UNMAPPED}, /* + (HQ) */ - {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (CB) */ + {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (Iceman) */ + {"Bells MS", 112, MIDI_UNMAPPED}, /* + (QFG1) */ + {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (LB1) */ {"Bird Tweet", 123, MIDI_UNMAPPED}, - {"BrsSect MS", 61, MIDI_UNMAPPED}, /* +++ (iceMan) */ + {"BrsSect MS", 61, MIDI_UNMAPPED}, /* +++ (Iceman) */ {"CLAPPING ", 126, MIDI_UNMAPPED}, /* ++ (LSL3) */ - {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 69}, /* R (HBoG) */ - {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (CoC) */ - {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 69}, /* R (Hoyle) */ + {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (QFG1) */ + {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (Camelot) */ + {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (Iceman) */ {"Chop ", 117, MIDI_UNMAPPED}, - {"Chorale MS", 52, MIDI_UNMAPPED}, /* + (CoC) */ + {"Chorale MS", 52, MIDI_UNMAPPED}, /* + (Camelot) */ {"ClarinetMS", 71, MIDI_UNMAPPED}, {"Claves ", MIDI_MAPPED_TO_RHYTHM, 75}, /* R (PQ2) */ - {"Claw MS", 118, MIDI_UNMAPPED}, /* + (HQ) */ - {"ClockBell ", 14, MIDI_UNMAPPED}, /* + (CB) */ + {"Claw MS", 118, MIDI_UNMAPPED}, /* + (QFG1) */ + {"ClockBell ", 14, MIDI_UNMAPPED}, /* + (LB1) */ {"ConcertCym", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (KQ1) */ - {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 64}, /* R (HQ) */ + {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 64}, /* R (QFG1) */ {"CoolPhone ", 124, MIDI_UNMAPPED}, /* ++ (LSL3) */ - {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ + {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */ {"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */ - {"Cricket ", 120, MIDI_UNMAPPED}, /* ? (CB) */ - {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 57}, /* R +++ (iceMan) */ - {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */ - {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (CoC, HQ) */ + {"Cricket ", 120, MIDI_UNMAPPED}, /* ? (LB1) */ + {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 57}, /* R +++ (Iceman) */ + {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1) */ + {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (Camelot, QFG1) */ {"CymbRollKA", MIDI_MAPPED_TO_RHYTHM, 57}, /* R ? (KQ1) */ {"Cymbal Lo ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* R ? (LSL3) */ - {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HBoG) */ - {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (iceMan) */ - {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (iceMan) */ + {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Hoyle) */ + {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (Iceman) */ + {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (Iceman) */ {"E Bass MS", 33, MIDI_UNMAPPED}, /* + (SQ3) */ {"ElecBassMS", 33, MIDI_UNMAPPED}, - {"ElecGtr MS", 27, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"ElecGtr MS", 27, MIDI_UNMAPPED}, /* ++ (Iceman) */ {"EnglHornMS", 69, MIDI_UNMAPPED}, {"FantasiaKA", 88, MIDI_UNMAPPED}, {"Fantasy ", 99, MIDI_UNMAPPED}, /* + (PQ2) */ - {"Fantasy2MS", 99, MIDI_UNMAPPED}, /* ++ (CoC, HQ) */ - {"Filter MS", 95, MIDI_UNMAPPED}, /* +++ (iceMan) */ - {"Filter2 MS", 95, MIDI_UNMAPPED}, /* ++ (iceMan) */ - {"Flame2 MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */ - {"Flames MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */ - {"Flute MS", 73, MIDI_UNMAPPED}, /* +++ (HQ) */ + {"Fantasy2MS", 99, MIDI_UNMAPPED}, /* ++ (Camelot, QFG1) */ + {"Filter MS", 95, MIDI_UNMAPPED}, /* +++ (Iceman) */ + {"Filter2 MS", 95, MIDI_UNMAPPED}, /* ++ (Iceman) */ + {"Flame2 MS", 121, MIDI_UNMAPPED}, /* ? (QFG1) */ + {"Flames MS", 121, MIDI_UNMAPPED}, /* ? (QFG1) */ + {"Flute MS", 73, MIDI_UNMAPPED}, /* +++ (QFG1) */ {"FogHorn MS", 58, MIDI_UNMAPPED}, - {"FrHorn1 MS", 60, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (CB) */ + {"FrHorn1 MS", 60, MIDI_UNMAPPED}, /* +++ (QFG1) */ + {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (LB1) */ {"GameSnd MS", 80, MIDI_UNMAPPED}, - {"Glock MS", 9, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"Gunshot ", 127, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */ - {"Harmonica2", 22, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Harpsi 1 ", 6, MIDI_UNMAPPED}, /* + (HBoG) */ - {"Harpsi 2 ", 6, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Heart MS", 116, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"Horse1 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ - {"Horse2 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ - {"InHale MS", 121, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"Glock MS", 9, MIDI_UNMAPPED}, /* +++ (QFG1) */ + {"Gunshot ", 127, MIDI_UNMAPPED}, /* +++ (LB1) */ + {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1) */ + {"Harmonica2", 22, MIDI_UNMAPPED}, /* +++ (LB1) */ + {"Harpsi 1 ", 6, MIDI_UNMAPPED}, /* + (Hoyle) */ + {"Harpsi 2 ", 6, MIDI_UNMAPPED}, /* +++ (LB1) */ + {"Heart MS", 116, MIDI_UNMAPPED}, /* ? (Iceman) */ + {"Horse1 MS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */ + {"Horse2 MS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */ + {"InHale MS", 121, MIDI_UNMAPPED}, /* ++ (Iceman) */ {"KNIFE ", 120, MIDI_UNMAPPED}, /* ? (LSL3) */ - {"KenBanjo ", 105, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (HQ) */ + {"KenBanjo ", 105, MIDI_UNMAPPED}, /* +++ (LB1) */ + {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (QFG1) */ {"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */ {"Koto ", 107, MIDI_UNMAPPED}, /* +++ (PQ2) */ - {"Laser MS", 81, MIDI_UNMAPPED}, /* ?? (HQ) */ - {"Meeps MS", 62, MIDI_UNMAPPED}, /* ? (HQ) */ - {"MTrak MS", 62, MIDI_UNMAPPED}, /* ?? (iceMan) */ - {"MachGun MS", 127, MIDI_UNMAPPED}, /* ? (iceMan) */ + {"Laser MS", 81, MIDI_UNMAPPED}, /* ?? (QFG1) */ + {"Meeps MS", 62, MIDI_UNMAPPED}, /* ? (QFG1) */ + {"MTrak MS", 62, MIDI_UNMAPPED}, /* ?? (Iceman) */ + {"MachGun MS", 127, MIDI_UNMAPPED}, /* ? (Iceman) */ {"OCEANSOUND", 122, MIDI_UNMAPPED}, /* + (LSL3) */ {"Oboe 2001 ", 68, MIDI_UNMAPPED}, /* + (PQ2) */ - {"Ocean MS", 122, MIDI_UNMAPPED}, /* + (iceMan) */ - {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */ - {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, /* R ? (iceMan) */ + {"Ocean MS", 122, MIDI_UNMAPPED}, /* + (Iceman) */ + {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, /* ? (Iceman) */ + {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (LB1) */ + {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, /* R ? (Iceman) */ {"PiccoloKA ", 72, MIDI_UNMAPPED}, /* +++ (KQ1) */ {"PinkBassMS", 39, MIDI_UNMAPPED}, - {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (CB) */ + {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (LB1) */ {"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */ - {"Raspbry MS", 81, MIDI_UNMAPPED}, /* ? (HQ) */ - {"RatSqueek ", 72, MIDI_UNMAPPED}, /* ? (CB, CoC) */ - {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (CB) */ - {"RecorderMS", 74, MIDI_UNMAPPED}, /* +++ (CoC) */ - {"Red Baron ", 125, MIDI_UNMAPPED}, /* ? (CB) */ - {"ReedPipMS ", 20, MIDI_UNMAPPED}, /* +++ (Coc) */ + {"Raspbry MS", 81, MIDI_UNMAPPED}, /* ? (QFG1) */ + {"RatSqueek ", 72, MIDI_UNMAPPED}, /* ? (LauraBow1, Camelot) */ + {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (LB1) */ + {"RecorderMS", 74, MIDI_UNMAPPED}, /* +++ (Camelot) */ + {"Red Baron ", 125, MIDI_UNMAPPED}, /* ? (LB1) */ + {"ReedPipMS ", 20, MIDI_UNMAPPED}, /* +++ (Camelot) */ {"RevCymb MS", 119, MIDI_UNMAPPED}, - {"RifleShot ", 127, MIDI_UNMAPPED}, /* + (CB) */ + {"RifleShot ", 127, MIDI_UNMAPPED}, /* + (LB1) */ {"RimShot MS", MIDI_MAPPED_TO_RHYTHM, 37}, /* R */ {"SHOWER ", 52, MIDI_UNMAPPED}, /* ? (LSL3) */ {"SQ Bass MS", 32, MIDI_UNMAPPED}, /* + (SQ3) */ - {"ShakuVibMS", 79, MIDI_UNMAPPED}, /* + (iceMan) */ - {"SlapBassMS", 36, MIDI_UNMAPPED}, /* +++ (iceMan) */ - {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (HQ) */ - {"Some Birds", 123, MIDI_UNMAPPED}, /* + (CB) */ - {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (CoC) */ + {"ShakuVibMS", 79, MIDI_UNMAPPED}, /* + (Iceman) */ + {"SlapBassMS", 36, MIDI_UNMAPPED}, /* +++ (Iceman) */ + {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (QFG1) */ + {"Some Birds", 123, MIDI_UNMAPPED}, /* + (LB1) */ + {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (Iceman) */ + {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (LB1) */ + {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (Camelot) */ {"SqurWaveMS", 80, MIDI_UNMAPPED}, - {"StabBassMS", 34, MIDI_UNMAPPED}, /* + (iceMan) */ - {"SteelDrmMS", 114, MIDI_UNMAPPED}, /* +++ (iceMan) */ - {"StrSect1MS", 48, MIDI_UNMAPPED}, /* ++ (HQ) */ - {"String MS", 45, MIDI_UNMAPPED}, /* + (CoC) */ + {"StabBassMS", 34, MIDI_UNMAPPED}, /* + (Iceman) */ + {"SteelDrmMS", 114, MIDI_UNMAPPED}, /* +++ (Iceman) */ + {"StrSect1MS", 48, MIDI_UNMAPPED}, /* ++ (QFG1) */ + {"String MS", 45, MIDI_UNMAPPED}, /* + (Camelot) */ {"Syn-Choir ", 91, MIDI_UNMAPPED}, {"Syn Brass4", 63, MIDI_UNMAPPED}, /* ++ (PQ2) */ {"SynBass MS", 38, MIDI_UNMAPPED}, - {"SwmpBackgr", 120, MIDI_UNMAPPED}, /* ?? (CB,HQ) */ - {"T-Bone2 MS", 57, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"Taiko ", 116, 35}, /* +++ (Coc) */ + {"SwmpBackgr", 120, MIDI_UNMAPPED}, /* ?? (LB1, QFG1) */ + {"T-Bone2 MS", 57, MIDI_UNMAPPED}, /* +++ (QFG1) */ + {"Taiko ", 116, 35}, /* +++ (Camelot) */ {"Taiko Rim ", 118, 37}, /* +++ (LSL3) */ - {"Timpani1 ", 47, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Tom MS", 117, 48}, /* +++ (iceMan) */ - {"Toms MS", 117, 48}, /* +++ (CoC, HQ) */ + {"Timpani1 ", 47, MIDI_UNMAPPED}, /* +++ (LB1) */ + {"Tom MS", 117, 48}, /* +++ (Iceman) */ + {"Toms MS", 117, 48}, /* +++ (Camelot, QFG1) */ {"Tpt1prtl ", 56, MIDI_UNMAPPED}, /* +++ (KQ1) */ - {"TriangleMS", 112, 81}, /* R (CoC) */ - {"Trumpet 1 ", 56, MIDI_UNMAPPED}, /* +++ (CoC) */ - {"Type MS", MIDI_MAPPED_TO_RHYTHM, 39}, /* + (iceMan) */ + {"TriangleMS", 112, 81}, /* R (Camelot) */ + {"Trumpet 1 ", 56, MIDI_UNMAPPED}, /* +++ (Camelot) */ + {"Type MS", MIDI_MAPPED_TO_RHYTHM, 39}, /* + (Iceman) */ {"WaterBells", 98, MIDI_UNMAPPED}, /* + (PQ2) */ {"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */ - {"Whiporill ", 123, MIDI_UNMAPPED}, /* + (CB) */ - {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */ - {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ, iceMan) */ - {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC) */ - {"Woodpecker", 115, MIDI_UNMAPPED}, /* ? (CB) */ - {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC, HQ, iceMan) */ + {"Whiporill ", 123, MIDI_UNMAPPED}, /* + (LB1) */ + {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (LB1) */ + {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1, Iceman) */ + {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Camelot) */ + {"Woodpecker", 115, MIDI_UNMAPPED}, /* ? (LB1) */ + {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Camelot, QFG1, Iceman) */ {0, 0, 0} }; diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp index 1ef0781906..8ba7a6a352 100644 --- a/engines/sci/sound/drivers/midi.cpp +++ b/engines/sci/sound/drivers/midi.cpp @@ -53,9 +53,10 @@ public: void send(uint32 b); void sysEx(const byte *msg, uint16 length); bool hasRhythmChannel() const { return true; } - byte getPlayId(); + byte getPlayId() const; int getPolyphony() const { return kVoices; } - int getFirstChannel(); + int getFirstChannel() const; + int getLastChannel() const; void setVolume(byte volume); int getVolume(); void setReverb(byte reverb); @@ -97,7 +98,7 @@ private: }; bool _isMt32; - bool _isOldPatchFormat; + bool _useMT32Track; bool _hasReverb; bool _playSwitch; int _masterVolume; @@ -119,7 +120,7 @@ private: byte _sysExBuf[kMaxSysExSize]; }; -MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _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), _useMT32Track(true) { MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI); _driver = createMidi(dev); @@ -139,6 +140,10 @@ MidiPlayer_Midi::~MidiPlayer_Midi() { void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) { uint8 patch = _channels[channel].mappedPatch; + assert(channel <= 15); + assert(note <= 127); + assert(velocity <= 127); + if (channel == MIDI_RHYTHM_CHANNEL) { if (_percussionMap[note] == MIDI_UNMAPPED) { debugC(kDebugLevelSound, "[Midi] Percussion instrument %i is unmapped", note); @@ -175,6 +180,7 @@ void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) { // We assume that velocity 0 maps to 0 (for note off) int mapIndex = _channels[channel].velocityMapIdx; + assert(velocity <= 127); velocity = _velocityMap[mapIndex][velocity]; } @@ -183,6 +189,8 @@ void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) { } void MidiPlayer_Midi::controlChange(int channel, int control, int value) { + assert(channel <= 15); + switch (control) { case 0x07: _channels[channel].volume = value; @@ -232,6 +240,8 @@ void MidiPlayer_Midi::controlChange(int channel, int control, int value) { void MidiPlayer_Midi::setPatch(int channel, int patch) { bool resetVol = false; + assert(channel <= 15); + if ((channel == MIDI_RHYTHM_CHANNEL) || (_channels[channel].patch == patch)) return; @@ -319,12 +329,19 @@ void MidiPlayer_Midi::send(uint32 b) { } // We return 1 for mt32, because if we remap channels to 0 for mt32, those won't get played at all -int MidiPlayer_Midi::getFirstChannel() { +// NOTE: SSCI uses channels 1 through 8 for General MIDI as well, in the drivers I checked +int MidiPlayer_Midi::getFirstChannel() const { if (_isMt32) return 1; return 0; } +int MidiPlayer_Midi::getLastChannel() const { + if (_isMt32) + return 8; + return 15; +} + void MidiPlayer_Midi::setVolume(byte volume) { _masterVolume = volume; @@ -772,6 +789,9 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { _percussionMap[i] = i; _patchMap[i] = i; _velocityMap[0][i] = i; + _velocityMap[1][i] = i; + _velocityMap[2][i] = i; + _velocityMap[3][i] = i; _keyShift[i] = 0; _volAdjust[i] = 0; _velocityMapIdx[i] = 0; @@ -808,17 +828,35 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { // Detect the format of patch 1, so that we know what play mask to use res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); if (!res) - _isOldPatchFormat = false; + _useMT32Track = false; else - _isOldPatchFormat = !isMt32GmPatch(res->data, res->size); + _useMT32Track = !isMt32GmPatch(res->data, res->size); + + // Check if the songs themselves have a GM track + if (!_useMT32Track) { + if (!resMan->isGMTrackIncluded()) + _useMT32Track = true; + } } else { // No GM patch found, map instruments using MT-32 patch warning("Game has no native support for General MIDI, applying auto-mapping"); + // TODO: The MT-32 <-> GM mapping hasn't been worked on for SCI1 games. Throw + // a warning to the user + if (getSciVersion() >= SCI_VERSION_1_EGA) + warning("The automatic mapping for General MIDI hasn't been worked on for " + "SCI1 games. Music might sound wrong or broken. Please choose another " + "music driver for this game (e.g. Adlib or MT-32) if you are " + "experiencing issues with music"); + // Modify velocity map to make low velocity notes a little louder - for (uint i = 1; i < 0x40; i++) + for (uint i = 1; i < 0x40; i++) { _velocityMap[0][i] = 0x20 + (i - 1) / 2; + _velocityMap[1][i] = 0x20 + (i - 1) / 2; + _velocityMap[2][i] = 0x20 + (i - 1) / 2; + _velocityMap[3][i] = 0x20 + (i - 1) / 2; + } res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); @@ -872,7 +910,7 @@ void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) { g_system->updateScreen(); } -byte MidiPlayer_Midi::getPlayId() { +byte MidiPlayer_Midi::getPlayId() const { switch (_version) { case SCI_VERSION_0_EARLY: case SCI_VERSION_0_LATE: @@ -881,7 +919,7 @@ byte MidiPlayer_Midi::getPlayId() { if (_isMt32) return 0x0c; else - return _isOldPatchFormat ? 0x0c : 0x07; + return _useMT32Track ? 0x0c : 0x07; } } diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h index 2db6f25c70..129159ecdc 100644 --- a/engines/sci/sound/drivers/mididriver.h +++ b/engines/sci/sound/drivers/mididriver.h @@ -32,6 +32,20 @@ namespace Sci { +// Music patches in SCI games: +// =========================== +// 1.pat - MT-32 driver music patch +// 2.pat - Yamaha FB01 driver music patch +// 3.pat - Adlib driver music patch +// 4.pat - Casio MT-540 (in earlier SCI0 games) +// 4.pat - GM driver music patch (in later games that support GM) +// 7.pat (newer) / patch.200 (older) - Mac driver music patch / Casio CSM-1 +// 9.pat (newer) / patch.005 (older) - Amiga driver music patch +// 98.pat - Unknown, found in later SCI1.1 games. A MIDI format patch +// 101.pat - CMS/PCjr driver music patch. +// Only later PCjr drivers use this patch, earlier ones don't use a patch +// bank.001 - older SCI0 Amiga instruments + class ResourceManager; enum { @@ -39,7 +53,6 @@ enum { MIDI_PROP_MASTER_VOLUME = 0 }; - #define MIDI_RHYTHM_CHANNEL 9 /* Special SCI sound stuff */ @@ -69,7 +82,7 @@ protected: byte _reverb; public: - MidiPlayer(SciVersion version) : _reverb(0), _version(version) { } + MidiPlayer(SciVersion version) : _driver(0), _reverb(0), _version(version) { } int open() { ResourceManager *resMan = g_sci->getResMan(); // HACK @@ -84,9 +97,10 @@ public: MidiChannel *getPercussionChannel() { return _driver->getPercussionChannel(); } virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); } - virtual byte getPlayId() = 0; + virtual byte getPlayId() const = 0; virtual int getPolyphony() const = 0; - virtual int getFirstChannel() { return 0; } + virtual int getFirstChannel() const { return 0; } + virtual int getLastChannel() const { return 15; } virtual void setVolume(byte volume) { if(_driver) @@ -97,7 +111,7 @@ public: return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0; } - virtual byte getReverb() { return _reverb; } + virtual byte getReverb() const { return _reverb; } virtual void setReverb(byte reverb) { _reverb = reverb; } virtual void playSwitch(bool play) { @@ -116,6 +130,7 @@ extern MidiPlayer *MidiPlayer_AdLib_create(SciVersion version); extern MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version); extern MidiPlayer *MidiPlayer_PCJr_create(SciVersion version); extern MidiPlayer *MidiPlayer_PCSpeaker_create(SciVersion version); +extern MidiPlayer *MidiPlayer_CMS_create(SciVersion version); extern MidiPlayer *MidiPlayer_Midi_create(SciVersion version); extern MidiPlayer *MidiPlayer_Fb01_create(SciVersion version); diff --git a/engines/sci/sound/drivers/pcjr.cpp b/engines/sci/sound/drivers/pcjr.cpp index bdf90eff5c..93de072865 100644 --- a/engines/sci/sound/drivers/pcjr.cpp +++ b/engines/sci/sound/drivers/pcjr.cpp @@ -234,13 +234,13 @@ class MidiPlayer_PCJr : public MidiPlayer { public: 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(); + byte getPlayId() const; 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() { +byte MidiPlayer_PCJr::getPlayId() const { switch (_version) { case SCI_VERSION_0_EARLY: return 0x02; @@ -259,11 +259,11 @@ class MidiPlayer_PCSpeaker : public MidiPlayer_PCJr { public: MidiPlayer_PCSpeaker(SciVersion version) : MidiPlayer_PCJr(version) { } - byte getPlayId(); + byte getPlayId() const; int getPolyphony() const { return 1; } }; -byte MidiPlayer_PCSpeaker::getPlayId() { +byte MidiPlayer_PCSpeaker::getPlayId() const { switch (_version) { case SCI_VERSION_0_EARLY: return 0x04; |