diff options
author | Johannes Schickel | 2010-10-13 03:57:44 +0000 |
---|---|---|
committer | Johannes Schickel | 2010-10-13 03:57:44 +0000 |
commit | 75e8452b6e6a2bf4fb2f588aa00b428a60d873b5 (patch) | |
tree | f29541d55309487a94bd1d38e8b53bb3dde9aec6 /engines/sci/sound | |
parent | 48ee83b88957dab86bc763e9ef21a70179fa8679 (diff) | |
parent | e9f50882ea5b6beeefa994040be9d3bab6a1f107 (diff) | |
download | scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.tar.gz scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.tar.bz2 scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.zip |
OPENGL: Merged from trunk, from rev 52105 to 53396.
This includes an rather hacky attempt to merge all the recent gp2x backend
changes into the branch. I suppose the gp2x backend and probably all new
backends, i.e. gph, dingux etc., might not compile anymore.
Since I have no way of testing those it would be nice if porters could look
into getting those up to speed in this branch.
svn-id: r53399
Diffstat (limited to 'engines/sci/sound')
-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 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.cpp | 43 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.h | 4 | ||||
-rw-r--r-- | engines/sci/sound/music.cpp | 100 | ||||
-rw-r--r-- | engines/sci/sound/music.h | 7 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.cpp | 21 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.h | 7 |
14 files changed, 1122 insertions, 156 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; diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 769df73365..d53f919f8f 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -53,6 +53,7 @@ MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion, SciMusic *music) : _ppqn = 1; setTempo(16667); + _masterVolume = 15; _volume = 127; _signalSet = false; @@ -418,7 +419,7 @@ void MidiParser_SCI::sendToDriver(uint32 midi) { int channelVolume = (midi >> 16) & 0xFF; // Remember, if we need to set it ourselves _channelVolume[midiChannel] = channelVolume; - // Adjust volume accordingly to current "global" volume + // Adjust volume accordingly to current local volume channelVolume = channelVolume * _volume / 127; midi = (midi & 0xFFF0) | ((channelVolume & 0xFF) << 16); } @@ -445,12 +446,8 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { } if (_signalSet) { _signalSet = false; - if (!_pSnd->signal) { - _pSnd->signal = _signalToSet; - } else { - // signal already set and waiting for getting to scripts, queue new one - _pSnd->signalQueue.push_back(_signalToSet); - } + _pSnd->setSignal(_signalToSet); + debugC(4, kDebugLevelSound, "signal %04x", _signalToSet); } @@ -613,12 +610,7 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { jumpToTick(_loopTick); } else { _pSnd->status = kSoundStopped; - if (!_pSnd->signal) { - _pSnd->signal = SIGNAL_OFFSET; - } else { - // signal already set and waiting for getting to scripts, queue new one - _pSnd->signalQueue.push_back(SIGNAL_OFFSET); - } + _pSnd->setSignal(SIGNAL_OFFSET); debugC(4, kDebugLevelSound, "signal EOT"); } @@ -668,6 +660,28 @@ void MidiParser_SCI::allNotesOff() { memset(_active_notes, 0, sizeof(_active_notes)); } +void MidiParser_SCI::setMasterVolume(byte masterVolume) { + assert(masterVolume <= MUSIC_MASTERVOLUME_MAX); + _masterVolume = masterVolume; + switch (_soundVersion) { + case SCI_VERSION_0_EARLY: + case SCI_VERSION_0_LATE: + // update driver master volume + setVolume(_volume); + break; + + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + case SCI_VERSION_2_1: + // directly set master volume (global volume is merged with channel volumes) + ((MidiPlayer *)_driver)->setVolume(masterVolume); + break; + + default: + error("MidiParser_SCI::setVolume: Unsupported soundVersion"); + } +} + void MidiParser_SCI::setVolume(byte volume) { assert(volume <= MUSIC_VOLUME_MAX); _volume = volume; @@ -676,8 +690,7 @@ void MidiParser_SCI::setVolume(byte volume) { case SCI_VERSION_0_EARLY: case SCI_VERSION_0_LATE: { // SCI0 adlib driver doesn't support channel volumes, so we need to go this way - // TODO: this should take the actual master volume into account - int16 globalVolume = _volume * 15 / 127; + int16 globalVolume = _volume * _masterVolume / MUSIC_VOLUME_MAX; ((MidiPlayer *)_driver)->setVolume(globalVolume); break; } diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 90db06e539..9d0cb15e74 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -65,6 +65,7 @@ public: } void sendInitCommands(); void unloadMusic(); + void setMasterVolume(byte masterVolume); void setVolume(byte volume); void stop() { _abort_parse = true; @@ -104,7 +105,8 @@ protected: SoundResource::Track *_track; MusicEntry *_pSnd; uint32 _loopTick; - byte _volume; + byte _masterVolume; // the overall master volume (same for all tracks) + byte _volume; // the global volume of the current track bool _signalSet; int16 _signalToSet; diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index fc1e56fcea..0dfa02c83f 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -30,6 +30,7 @@ #include "sci/sci.h" #include "sci/console.h" #include "sci/resource.h" +#include "sci/engine/features.h" #include "sci/engine/kernel.h" #include "sci/engine/state.h" #include "sci/sound/midiparser_sci.h" @@ -65,9 +66,20 @@ void SciMusic::init() { // Default to MIDI in SCI2.1+ games, as many don't have AdLib support. Common::Platform platform = g_sci->getPlatform(); - uint32 dev = MidiDriver::detectDevice((getSciVersion() >= SCI_VERSION_2_1) ? (MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM) : (MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI)); - switch (MidiDriver::getMusicType(dev)) { + uint32 deviceFlags = MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI; + + if (getSciVersion() >= SCI_VERSION_2_1) + deviceFlags |= MDT_PREFER_GM; + + // Currently our CMS implementation only supports SCI1(.1) + if (getSciVersion() >= SCI_VERSION_1_EGA && getSciVersion() <= SCI_VERSION_1_1) + deviceFlags |= MDT_CMS; + + uint32 dev = MidiDriver::detectDevice(deviceFlags); + _musicType = MidiDriver::getMusicType(dev); + + switch (_musicType) { case MT_ADLIB: // FIXME: There's no Amiga sound option, so we hook it up to AdLib if (g_sci->getPlatform() == Common::kPlatformAmiga || platform == Common::kPlatformMacintosh) @@ -81,8 +93,11 @@ void SciMusic::init() { case MT_PCSPK: _pMidiDrv = MidiPlayer_PCSpeaker_create(_soundVersion); break; + case MT_CMS: + _pMidiDrv = MidiPlayer_CMS_create(_soundVersion); + break; default: - if (ConfMan.getBool("enable_fb01")) + if (ConfMan.getBool("native_fb01")) _pMidiDrv = MidiPlayer_Fb01_create(_soundVersion); else _pMidiDrv = MidiPlayer_Midi_create(_soundVersion); @@ -100,6 +115,7 @@ void SciMusic::init() { // Find out what the first possible channel is (used, when doing channel // remapping). _driverFirstChannel = _pMidiDrv->getFirstChannel(); + _driverLastChannel = _pMidiDrv->getLastChannel(); } void SciMusic::miditimerCallback(void *p) { @@ -260,6 +276,7 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { pSnd->pMidiParser = new MidiParser_SCI(_soundVersion, this); pSnd->pMidiParser->setMidiDriver(_pMidiDrv); pSnd->pMidiParser->setTimerRate(_dwTempo); + pSnd->pMidiParser->setMasterVolume(_masterVolume); } pSnd->pauseCounter = 0; @@ -288,6 +305,8 @@ int16 SciMusic::tryToOwnChannel(MusicEntry *caller, int16 bestChannel) { } // otherwise look for unused channel for (int channelNr = _driverFirstChannel; channelNr < 15; channelNr++) { + if (channelNr == 9) // never map to channel 9 (precussion) + continue; if (!_usedChannel[channelNr]) { _usedChannel[channelNr] = caller; return channelNr; @@ -349,20 +368,25 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { } } - if (pSnd->pStreamAud && !_pMixer->isSoundHandleActive(pSnd->hCurrentAud)) { - if (pSnd->loop > 1) { - pSnd->pLoopStream = new Audio::LoopingAudioStream(pSnd->pStreamAud, - pSnd->loop, DisposeAfterUse::NO); - _pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud, - pSnd->pLoopStream, -1, pSnd->volume, 0, - DisposeAfterUse::NO); - } else { - // Rewind in case we play the same sample multiple times - // (non-looped) like in pharkas right at the start - pSnd->pStreamAud->rewind(); - _pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud, - pSnd->pStreamAud, -1, pSnd->volume, 0, - DisposeAfterUse::NO); + if (pSnd->pStreamAud) { + if (!_pMixer->isSoundHandleActive(pSnd->hCurrentAud)) { + // Sierra SCI ignores volume set when playing samples via kDoSound + // At least freddy pharkas/CD has a script bug that sets volume to 0 + // when playing the "score" sample + if (pSnd->loop > 1) { + pSnd->pLoopStream = new Audio::LoopingAudioStream(pSnd->pStreamAud, + pSnd->loop, DisposeAfterUse::NO); + _pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud, + pSnd->pLoopStream, -1, _pMixer->kMaxChannelVolume, 0, + DisposeAfterUse::NO); + } else { + // Rewind in case we play the same sample multiple times + // (non-looped) like in pharkas right at the start + pSnd->pStreamAud->rewind(); + _pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud, + pSnd->pStreamAud, -1, _pMixer->kMaxChannelVolume, 0, + DisposeAfterUse::NO); + } } } else { if (pSnd->pMidiParser) { @@ -375,8 +399,14 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { if (pSnd->status == kSoundStopped) { pSnd->pMidiParser->jumpToTick(0); } else { + // Disable sound looping before fast forwarding to the last position, + // when loading a saved game. Fixes bug #3083151. + uint16 prevLoop = pSnd->loop; + pSnd->loop = 0; // Fast forward to the last position and perform associated events when loading pSnd->pMidiParser->jumpToTick(pSnd->ticker, true); + // Restore looping + pSnd->loop = prevLoop; } pSnd->pMidiParser->mainThreadEnd(); _mutex.unlock(); @@ -412,7 +442,8 @@ void SciMusic::soundStop(MusicEntry *pSnd) { void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) { assert(volume <= MUSIC_VOLUME_MAX); if (pSnd->pStreamAud) { - _pMixer->setChannelVolume(pSnd->hCurrentAud, volume * 2); // Mixer is 0-255, SCI is 0-127 + // we simply ignore volume changes for samples, because sierra sci also + // doesn't support volume for samples via kDoSound } else if (pSnd->pMidiParser) { _mutex.lock(); pSnd->pMidiParser->mainThreadBegin(); @@ -422,6 +453,13 @@ void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) { } } +// this is used to set volume of the sample, used for fading only! +void SciMusic::soundSetSampleVolume(MusicEntry *pSnd, byte volume) { + assert(volume <= MUSIC_VOLUME_MAX); + assert(pSnd->pStreamAud); + _pMixer->setChannelVolume(pSnd->hCurrentAud, volume * 2); // Mixer is 0-255, SCI is 0-127 +} + void SciMusic::soundSetPriority(MusicEntry *pSnd, byte prio) { Common::StackLock lock(_mutex); @@ -525,8 +563,11 @@ void SciMusic::soundSetMasterVolume(uint16 vol) { Common::StackLock lock(_mutex); - if (_pMidiDrv) - _pMidiDrv->setVolume(vol); + const MusicList::iterator end = _playList.end(); + for (MusicList::iterator i = _playList.begin(); i != end; ++i) { + if ((*i)->pMidiParser) + (*i)->pMidiParser->setMasterVolume(vol); + } } void SciMusic::sendMidiCommand(uint32 cmd) { @@ -681,4 +722,23 @@ void MusicEntry::doFade() { } } +void MusicEntry::setSignal(int newSignal) { + // For SCI0, we cache the signals to set, as some songs might + // update their signal faster than kGetEvent is called (which is where + // we manually invoke kDoSoundUpdateCues for SCI0 games). SCI01 and + // newer handle signalling inside kDoSoundUpdateCues. Refer to bug #3042981 + if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) { + if (!signal) { + signal = newSignal; + } else { + // signal already set and waiting for getting to scripts, queue new one + signalQueue.push_back(newSignal); + } + } else { + // Set the signal directly for newer games, otherwise the sound + // object might be deleted already later on (refer to bug #3045913) + signal = newSignal; + } +} + } // End of namespace Sci diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 3cf600fcf3..9fcbb9346d 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -47,6 +47,8 @@ enum SoundStatus { #define MUSIC_VOLUME_DEFAULT 127 #define MUSIC_VOLUME_MAX 127 +#define MUSIC_MASTERVOLUME_DEFAULT 15 +#define MUSIC_MASTERVOLUME_MAX 15 class MidiParser_SCI; class SegManager; @@ -109,6 +111,7 @@ public: void doFade(); void onTimer(); + void setSignal(int signal); virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -145,6 +148,7 @@ public: void soundResume(MusicEntry *pSnd); void soundToggle(MusicEntry *pSnd, bool pause); void soundSetVolume(MusicEntry *pSnd, byte volume); + void soundSetSampleVolume(MusicEntry *pSnd, byte volume); void soundSetPriority(MusicEntry *pSnd, byte prio); uint16 soundGetMasterVolume(); void soundSetMasterVolume(uint16 vol); @@ -152,6 +156,7 @@ public: void soundSetSoundOn(bool soundOnFlag); uint16 soundGetVoices(); uint32 soundGetTempo() const { return _dwTempo; } + MusicType soundGetMusicType() const { return _musicType; } bool soundIsActive(MusicEntry *pSnd) { assert(pSnd->pStreamAud != 0); @@ -215,8 +220,10 @@ private: MusicEntry *_usedChannel[16]; MidiCommandQueue _queuedCommands; + MusicType _musicType; int _driverFirstChannel; + int _driverLastChannel; }; } // End of namespace Sci diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 567a1605f3..790164cf41 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -78,8 +78,13 @@ void SoundCommandParser::processInitSound(reg_t obj) { // a relevant audio resource, play it, otherwise switch to synthesized // effects. If the resource exists, play it using map 65535 (sound // effects map) + bool checkAudioResource = getSciVersion() >= SCI_VERSION_1_1; + if (g_sci->getGameId() == GID_HOYLE4) + checkAudioResource = false; // hoyle 4 has garbled audio resources in place of the sound resources + // if we play those, we will only make the user deaf and break speakers. Sierra SCI doesn't play anything + // on soundblaster. FIXME: check, why this is - if (getSciVersion() >= SCI_VERSION_1_1 && _resMan->testResource(ResourceId(kResourceTypeAudio, resourceId))) { + if (checkAudioResource && _resMan->testResource(ResourceId(kResourceTypeAudio, resourceId))) { // Found a relevant audio resource, play it int sampleLen; newSound->pStreamAud = _audio->getAudioStream(resourceId, 65535, &sampleLen); @@ -284,8 +289,8 @@ reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc) if (argc > 0) { debugC(2, kDebugLevelSound, "kDoSound(masterVolume): %d", argv[0].toSint16()); - int vol = CLIP<int16>(argv[0].toSint16(), 0, kMaxSciVolume); - vol = vol * Audio::Mixer::kMaxMixerVolume / kMaxSciVolume; + int vol = CLIP<int16>(argv[0].toSint16(), 0, MUSIC_MASTERVOLUME_MAX); + vol = vol * Audio::Mixer::kMaxMixerVolume / MUSIC_MASTERVOLUME_MAX; ConfMan.setInt("music_volume", vol); ConfMan.setInt("sfx_volume", vol); g_engine->syncSoundSettings(); @@ -298,7 +303,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj)); + debugC(2, kDebugLevelSound, "kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj)); return acc; } @@ -402,7 +407,7 @@ void SoundCommandParser::processUpdateCues(reg_t obj) { } // We get a flag from MusicEntry::doFade() here to set volume for the stream if (musicSlot->fadeSetVolume) { - _music->soundSetVolume(musicSlot, musicSlot->volume); + _music->soundSetSampleVolume(musicSlot, musicSlot->volume); musicSlot->fadeSetVolume = false; } } else if (musicSlot->pMidiParser) { @@ -688,6 +693,7 @@ void SoundCommandParser::startNewSound(int number) { } void SoundCommandParser::setMasterVolume(int vol) { + // 0...15 _music->soundSetMasterVolume(vol); } @@ -695,4 +701,9 @@ void SoundCommandParser::pauseAll(bool pause) { _music->pauseAll(pause); } +MusicType SoundCommandParser::getMusicType() const { + assert(_music); + return _music->soundGetMusicType(); +} + } // End of namespace Sci diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h index 8e6fb81762..61371d903f 100644 --- a/engines/sci/sound/soundcmd.h +++ b/engines/sci/sound/soundcmd.h @@ -27,6 +27,7 @@ #define SCI_SOUNDCMD_H #include "common/list.h" +#include "sound/mididrv.h" // for MusicType #include "sci/engine/state.h" namespace Sci { @@ -47,10 +48,6 @@ public: SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion); ~SoundCommandParser(); - enum { - kMaxSciVolume = 15 - }; - //reg_t parseCommand(int argc, reg_t *argv, reg_t acc); // Functions used for game state loading @@ -71,6 +68,8 @@ public: void processPlaySound(reg_t obj); void processStopSound(reg_t obj, bool sampleFinishedPlaying); + MusicType getMusicType() const; + /** * Synchronizes the current state of the music list to the rest of the engine, so that * the changes that the sound thread makes to the music are registered with the engine |