aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/sound/drivers/cms.cpp183
1 files changed, 182 insertions, 1 deletions
diff --git a/engines/sci/sound/drivers/cms.cpp b/engines/sci/sound/drivers/cms.cpp
index b7b13550c0..cd7b101f03 100644
--- a/engines/sci/sound/drivers/cms.cpp
+++ b/engines/sci/sound/drivers/cms.cpp
@@ -33,6 +33,9 @@
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)
@@ -124,6 +127,12 @@ private:
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);
@@ -383,7 +392,11 @@ void MidiDriver_CMS::noteOn(int channel, int note, int velocity) {
}
}
+#ifdef CMS_DISABLE_VOICE_MAPPING
int voice = findVoiceBasic(channel);
+#else
+ int voice = findVoice(channel);
+#endif
if (voice != -1)
voiceOn(voice, note, velocity);
}
@@ -464,7 +477,9 @@ void MidiDriver_CMS::controlChange(int channel, int control, int value) {
break;
case 75:
- //ChannelMapping
+#ifndef CMS_DISABLE_VOICE_MAPPING
+ voiceMapping(channel, value);
+#endif
break;
case 123:
@@ -498,6 +513,172 @@ void MidiDriver_CMS::pitchWheel(int channelNr, int value) {
}
}
+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];