aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sound
diff options
context:
space:
mode:
authorJohannes Schickel2010-10-13 03:57:44 +0000
committerJohannes Schickel2010-10-13 03:57:44 +0000
commit75e8452b6e6a2bf4fb2f588aa00b428a60d873b5 (patch)
treef29541d55309487a94bd1d38e8b53bb3dde9aec6 /engines/sci/sound
parent48ee83b88957dab86bc763e9ef21a70179fa8679 (diff)
parente9f50882ea5b6beeefa994040be9d3bab6a1f107 (diff)
downloadscummvm-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.cpp10
-rw-r--r--engines/sci/sound/drivers/amigamac.cpp4
-rw-r--r--engines/sci/sound/drivers/cms.cpp817
-rw-r--r--engines/sci/sound/drivers/fb01.cpp4
-rw-r--r--engines/sci/sound/drivers/map-mt32-to-gm.h170
-rw-r--r--engines/sci/sound/drivers/midi.cpp58
-rw-r--r--engines/sci/sound/drivers/mididriver.h25
-rw-r--r--engines/sci/sound/drivers/pcjr.cpp8
-rw-r--r--engines/sci/sound/midiparser_sci.cpp43
-rw-r--r--engines/sci/sound/midiparser_sci.h4
-rw-r--r--engines/sci/sound/music.cpp100
-rw-r--r--engines/sci/sound/music.h7
-rw-r--r--engines/sci/sound/soundcmd.cpp21
-rw-r--r--engines/sci/sound/soundcmd.h7
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