aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
diff options
context:
space:
mode:
authorathrxx2019-06-21 14:18:03 +0200
committerathrxx2019-06-21 14:19:42 +0200
commit80f163b0fc7fc033aa99b829f626784804bf5176 (patch)
treeb2fb2b2040a5bda107ea9b2576f8d96a262a03f1 /engines/sci
parent08dba769f10ace19e46555eddb957ec7d508f58d (diff)
downloadscummvm-rg350-80f163b0fc7fc033aa99b829f626784804bf5176.tar.gz
scummvm-rg350-80f163b0fc7fc033aa99b829f626784804bf5176.tar.bz2
scummvm-rg350-80f163b0fc7fc033aa99b829f626784804bf5176.zip
ALL: revert accidental commits
Diffstat (limited to 'engines/sci')
-rw-r--r--engines/sci/engine/kscripts.cpp4
-rw-r--r--engines/sci/engine/script_patches.cpp60
-rw-r--r--engines/sci/sound/drivers/cms.cpp1453
-rw-r--r--engines/sci/sound/drivers/pc9801.cpp1
-rw-r--r--engines/sci/sound/music.cpp5
5 files changed, 439 insertions, 1084 deletions
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index 5bdbb4fb46..61b0f16cae 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -257,10 +257,6 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
// Returns script dispatch address index in the supplied script
reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
int script = argv[0].toUint16();
-
- if (Sci::g_sci->getGameId() == GID_KQ4 && script == 701)
- script--;
-
uint16 index = (argc > 1) ? argv[1].toUint16() : 0;
if (argv[0].getSegment())
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index f1bbb3ee2e..af7003f2d0 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -5255,24 +5255,6 @@ static const uint16 laurabow1PatchLeftStairsLockupFix[] = {
PATCH_END
};
-// Copy Protection
-static const uint16 laurabow1SignatureCp[] = {
- SIG_MAGICDWORD,
- 0x30, 0x48, 0x00, 0x8f, 0x01, 0x8d, 0x00, 0x35, 0x04, 0x06,
- 0x93, 0x05, 0x1e, 0x30, 0x36, 0x00, 0x8f, 0x02, 0x8d, 0x00,
- SIG_END
-};
-
-static const uint16 laurabow1PatchCp[] = {
- 0x39, 0x00, 0xab, 0x00, 0x39, 0x00, 0xab, 0x01, 0x39, 0x00,
- 0xab, 0x35, 0x39, 0x27, 0xaf, 0x01, 0x39, 0x27, 0xaf, 0x02,
- PATCH_GETORIGINALBYTE(0), PATCH_GETORIGINALBYTE(1), PATCH_GETORIGINALBYTE(2),
- PATCH_GETORIGINALBYTE(3), PATCH_GETORIGINALBYTE(4), PATCH_GETORIGINALBYTE(5),
- PATCH_GETORIGINALBYTE(6), PATCH_GETORIGINALBYTE(7), PATCH_GETORIGINALBYTE(8),
- PATCH_GETORIGINALBYTE(9), PATCH_GETORIGINALBYTE(10),
- PATCH_END
-};
-
// script, description, signature patch
static const SciScriptPatcherEntry laurabow1Signatures[] = {
{ true, 4, "easter egg view fix", 1, laurabow1SignatureEasterEggViewFix, laurabow1PatchEasterEggViewFix },
@@ -5285,7 +5267,6 @@ static const SciScriptPatcherEntry laurabow1Signatures[] = {
{ true, 58, "chapel candles persistence", 1, laurabow1SignatureChapelCandlesPersistence, laurabow1PatchChapelCandlesPersistence },
{ true, 236, "tell Lilly about Gertie blocking fix 1/2", 1, laurabow1SignatureTellLillyAboutGerieBlockingFix1, laurabow1PatchTellLillyAboutGertieBlockingFix1 },
{ true, 236, "tell Lilly about Gertie blocking fix 2/2", 1, laurabow1SignatureTellLillyAboutGerieBlockingFix2, laurabow1PatchTellLillyAboutGertieBlockingFix2 },
- { true, 414, "copy protection", 1, laurabow1SignatureCp, laurabow1PatchCp },
{ true, 998, "obstacle collision lockups fix", 1, laurabow1SignatureObstacleCollisionLockupsFix, laurabow1PatchObstacleCollisionLockupsFix },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -14024,44 +14005,6 @@ static const SciScriptPatcherEntry torinSignatures[] = {
#endif
-
-// Copy Protection
-static const uint16 pq2EnSignatureCp[] = {
- SIG_MAGICDWORD,
- 0x35, 0x07, 0x12, 0xa5,
- SIG_ADDTOOFFSET(67),
- 0x30, 0xcf, 0x00, 0x35,
- SIG_END
-};
-
-static const uint16 pq2EnPatchCp[] = {
- 0x35, 0x00, 0x12, 0xa5,
- PATCH_ADDTOOFFSET(67),
- 0x30, 0x00, 0x00, 0x35,
- PATCH_END
-};
-
-static const uint16 pq2JpSignatureCp[] = {
- SIG_MAGICDWORD,
- 0x35, 0x07, 0x12, 0xa5,
- SIG_ADDTOOFFSET(75),
- 0x30, 0xcf, 0x00, 0x35,
- SIG_END
-};
-
-static const uint16 pq2JpPatchCp[] = {
- 0x35, 0x00, 0x12, 0xa5,
- PATCH_ADDTOOFFSET(75),
- 0x30, 0x00, 0x00, 0x35,
- PATCH_END
-};
-
-static const SciScriptPatcherEntry pq2Signatures[] = {
- { true, 701, "copy protection", 1, pq2EnSignatureCp, pq2EnPatchCp },
- { true, 701, "copy protection", 1, pq2JpSignatureCp, pq2JpPatchCp },
- SCI_SIGNATUREENTRY_TERMINATOR
-};
-
// =================================================================================
ScriptPatcher::ScriptPatcher() {
@@ -14593,9 +14536,6 @@ void ScriptPatcher::processScript(uint16 scriptNr, SciSpan<byte> scriptData) {
case GID_PQ1:
signatureTable = pq1vgaSignatures;
break;
- case GID_PQ2:
- signatureTable = pq2Signatures;
- break;
case GID_PQ3:
signatureTable = pq3Signatures;
break;
diff --git a/engines/sci/sound/drivers/cms.cpp b/engines/sci/sound/drivers/cms.cpp
index 86529956be..8b92432cb9 100644
--- a/engines/sci/sound/drivers/cms.cpp
+++ b/engines/sci/sound/drivers/cms.cpp
@@ -33,157 +33,14 @@
namespace Sci {
-class MidiDriver_CMS;
-
-class CMSVoice {
-public:
- CMSVoice(uint8 id, MidiDriver_CMS *driver, CMSEmulator *cms, SciSpan<const uint8>& patchData);
- virtual ~CMSVoice();
-
- virtual void noteOn(int note, int velocity) = 0;
- virtual void noteOff() = 0;
- virtual void stop() = 0;
- virtual void programChange(int program) = 0;
- virtual void pitchWheel() {}
-
- virtual void update() = 0;
-
- virtual void reset() {}
- virtual void setPanMask(uint8) {}
-
- uint8 _assign;
- uint8 _note;
- bool _sustained;
- uint16 _duration;
- uint16 _releaseDuration;
- CMSVoice *_secondaryVoice;
-
-protected:
- void sendFrequency();
- void cmsWrite(uint8 reg, uint8 val);
-
- CMSEmulator *_cms;
- MidiDriver_CMS *_driver;
- SciSpan<const uint8> _patchData;
-
- const uint8 _id;
- const uint8 _regOffset;
- const uint8 _portOffset;
-
- static uint8 _octaveRegs[6];
- static const int _frequencyTable[48];
-
-private:
- virtual void recalculateFrequency(uint8 &freq, uint8 &octave) = 0;
-};
-
-class CMSVoice_V0 : public CMSVoice {
-public:
- CMSVoice_V0(uint8 id, MidiDriver_CMS *driver, CMSEmulator *cms, SciSpan<const uint8>& patchData);
- virtual ~CMSVoice_V0();
-
- void noteOn(int note, int);
- void noteOff();
- void stop();
- void programChange(int program);
-
- void update();
-
- void reset();
- void setPanMask(uint8 mask);
-
-private:
- void recalculateFrequency(uint8 &frequency, uint8 &octave);
- void recalculateEvelopeLevels();
- void selectEnvelope(int id);
-
- enum EnvelopeState {
- kReady = 0,
- kRestart = 1,
- kAttack = 2,
- kDecay = 3,
- kSustain = 4,
- kRelease = 5
- };
-
- EnvelopeState _envState;
- uint8 _envAR;
- uint8 _envTL;
- uint8 _envDR;
- uint8 _envSL;
- uint8 _envRR;
- uint8 _envSLI;
- uint8 _envPAC;
- uint8 _envPA;
-
- static uint8 _envAR1;
-
- uint8 _envNote;
- uint8 _envSSL;
- uint8 _panMask;
- uint8 _strMask;
-
- int8 _transFreq;
- int8 _transOct;
-
- bool _vbrOn;
- uint8 _vbrSteps;
- uint8 _vbrState;
- int8 _vbrMod;
- int8 _vbrCur;
- int16 _vbrPhase;
-
- int _currentLevel;
- bool _updateCMS;
-
- const bool _isSecondary;
-
- static const uint8 _envelopeDataTable[256];
- static const uint8 _volumeTable[176];
- static const uint8 _pitchWheelTable[65];
-};
-
-class CMSVoice_V1 : public CMSVoice {
-public:
- CMSVoice_V1(uint8 id, MidiDriver_CMS *driver, CMSEmulator *cms, SciSpan<const uint8>& patchData);
- virtual ~CMSVoice_V1();
-
- void noteOn(int note, int velocity);
- void noteOff();
- void stop();
- void programChange(int program);
- void pitchWheel();
-
- void update();
-
-private:
- void recalculateFrequency(uint8 &frequency, uint8 &octave);
-
- void updateVoiceAmplitude();
- void setupVoiceAmplitude();
-
- SciSpan<const uint8> _patchDataCur;
- uint8 _velocity;
- uint8 _patchDataIndex;
- uint8 _amplitudeTimer;
- uint8 _amplitudeModifier;
- bool _release;
-
- static const int _velocityTable[32];
-};
+// 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:
- enum {
- MIDI_PROP_CHANNEL_VOLUME = 1,
- MIDI_PROP_CHANNEL_PITCHWHEEL = 2,
- MIDI_PROP_CHANNEL_PANPOS = 3,
- MIDI_PROP_PLAYSWITCH = 4
- };
-
-public:
- MidiDriver_CMS(Audio::Mixer *mixer, ResourceManager *resMan, SciVersion version);
- ~MidiDriver_CMS();
+ MidiDriver_CMS(Audio::Mixer *mixer, ResourceManager *resMan)
+ : MidiDriver_Emulated(mixer), _resMan(resMan), _cms(0), _rate(0), _playSwitch(true), _masterVolume(0) {
+ }
int open();
void close();
@@ -191,102 +48,105 @@ public:
void send(uint32 b);
uint32 property(int prop, uint32 param);
- void initTrack(SciSpan<const byte>& header);
-
- void onTimer();
-
MidiChannel *allocateChannel() { return 0; }
MidiChannel *getPercussionChannel() { return 0; }
bool isStereo() const { return true; }
int getRate() const { return _rate; }
+ void playSwitch(bool play);
private:
- void noteOn(int channelNr, int note, int velocity);
- void noteOff(int channelNr, int note);
- void controlChange(int channelNr, int control, int value);
- void programChange(int channelNr, int value);
- void pitchWheel(int channelNr, int value);
-
- void voiceMapping(int channelNr, int value);
- void bindVoices(int channelNr, int voices, bool bindSecondary, bool doProgramChange);
- void unbindVoices(int channelNr, int voices, bool bindSecondary);
- void donateVoices(bool bindSecondary);
- int findVoice(int channelNr, int note);
- int findVoiceBasic(int channelNr);
-
- void writeToChip(int chip, int address, int data);
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;
+
+ Common::SpanOwner<SciSpan<uint8> > _patchData;
+
struct Channel {
- Channel() : program(0), volume(0), pan(0x40), hold(0), missingVoices(0), lastVoiceUsed(0), pitchWheel(0x2000), isValid(true) {}
- uint8 program;
+ 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 missingVoices;
- uint8 lastVoiceUsed;
+ uint8 extraVoices;
uint16 pitchWheel;
- bool isValid;
+ uint8 pitchModifier;
+ bool pitchAdditive;
+ uint8 lastVoiceUsed;
};
Channel _channel[16];
- CMSVoice *_voice[12];
-
- const int _numVoicesPrimary;
- const int _numVoicesSecondary;
-
- CMSEmulator *_cms;
- ResourceManager *_resMan;
- Common::SpanOwner<SciSpan<const uint8> > _patchData;
- bool _playSwitch;
- uint16 _masterVolume;
-
- const int _actualTimerInterval;
- const int _reqTimerInterval;
- int _updateTimer;
- int _rate;
+ struct Voice {
+ Voice() : channel(0xFF), note(0xFF), sustained(0xFF), ticks(0),
+ turnOffTicks(0), patchDataPtr(), patchDataIndex(0),
+ amplitudeTimer(0), amplitudeModifier(0), turnOff(false),
+ velocity(0) {
+ }
- SciVersion _version;
-};
+ uint8 channel;
+ uint8 note;
+ uint8 sustained;
+ uint16 ticks;
+ uint16 turnOffTicks;
+ SciSpan<uint8> patchDataPtr;
+ uint8 patchDataIndex;
+ uint8 amplitudeTimer;
+ uint8 amplitudeModifier;
+ bool turnOff;
+ uint8 velocity;
+ };
-CMSVoice::CMSVoice(uint8 id, MidiDriver_CMS* driver, CMSEmulator *cms, SciSpan<const uint8>& patchData) : _id(id), _regOffset(id > 5 ? id - 6 : id), _portOffset(id > 5 ? 2 : 0),
- _driver(driver), _cms(cms), _assign(0xFF), _note(0xFF), _sustained(false), _duration(0), _releaseDuration(0), _secondaryVoice(0), _patchData(patchData) {
- assert(_id < 12);
- _octaveRegs[_id >> 1] = 0;
-}
+ Voice _voice[12];
-CMSVoice::~CMSVoice() {
+ void voiceOn(int voice, int note, int velocity);
+ void voiceOff(int voice);
-}
+ void noteSend(int voice);
-void CMSVoice::sendFrequency() {
- uint8 frequency = 0;
- uint8 octave = 0;
+ 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);
- recalculateFrequency(frequency, octave);
+ void voiceMapping(int channel, int value);
+ void bindVoices(int channel, int voices);
+ void unbindVoices(int channel, int voices);
+ void donateVoices();
+ int findVoice(int channel);
- uint8 octaveData = _octaveRegs[_id >> 1];
- octaveData = (_id & 1) ? (octaveData & 0x0F) | (octave << 4) : (octaveData & 0xF0) | octave;
+ int findVoiceBasic(int channel);
- cmsWrite(8 + _regOffset, frequency);
- cmsWrite(0x10 + (_regOffset >> 1), octaveData);
-}
+ void updateVoiceAmplitude(int voice);
+ void setupVoiceAmplitude(int voice);
-void CMSVoice::cmsWrite(uint8 reg, uint8 val) {
- _cms->portWrite(0x221 + _portOffset, reg);
- _cms->portWrite(0x220 + _portOffset, val);
+ uint8 _octaveRegs[2][3];
- if (reg >= 16 && reg <= 18)
- _octaveRegs[_id >> 1] = val;
-}
+ static const int _timerFreq = 60;
-uint8 CMSVoice::_octaveRegs[6] = {
- 0, 0, 0, 0, 0, 0
+ static const int _frequencyTable[];
+ static const int _velocityTable[];
};
-const int CMSVoice::_frequencyTable[48] = {
+const int MidiDriver_CMS::_frequencyTable[] = {
3, 10, 17, 24,
31, 38, 46, 51,
58, 64, 71, 77,
@@ -301,495 +161,13 @@ const int CMSVoice::_frequencyTable[48] = {
242, 246, 250, 253
};
-CMSVoice_V0::CMSVoice_V0(uint8 id, MidiDriver_CMS* driver, CMSEmulator *cms, SciSpan<const uint8>& patchData) : CMSVoice(id, driver, cms, patchData), _envState(kReady), _currentLevel(0), _strMask(0),
- _envAR(0), _envTL(0), _envDR(0), _envSL(0), _envRR(0), _envSLI(0), _vbrOn(false), _vbrSteps(0), _vbrState(0), _vbrMod(0), _vbrCur(0), _isSecondary(id > 7),
- _vbrPhase(0), _transOct(0), _transFreq(0), _envPAC(0), _envPA(0), _panMask(_id & 1 ? 0xF0 : 0x0F), _envSSL(0), _envNote(0xFF), _updateCMS(false) {
-}
-
-CMSVoice_V0::~CMSVoice_V0() {
-}
-
-void CMSVoice_V0::noteOn(int note, int) {
- if (!_driver->property(MidiDriver_CMS::MIDI_PROP_PLAYSWITCH, 0xFFFF) || !_envTL)
- return;
-
- _note = note;
- _envNote = note + 3;
- _envState = kRestart;
- _vbrPhase = 0;
- _vbrCur = _vbrMod;
- _vbrState = _vbrSteps & 0x0F;
- _envPAC = _envPA;
-
- //debug("NOTEON: Voice: %02d, Note: %02d, AR: 0x%02x, TL: 0x%02x, DR: 0x%02x, SLI: 0x%02x, RR: 0x%02x, VBR: 0x%02x", _id, note, _envAR, _envTL, _envDR, _envSLI, _envRR, _vbrMod);
-
- if (_secondaryVoice)
- _secondaryVoice->noteOn(note, 127);
-}
-
-void CMSVoice_V0::noteOff() {
- if (!_driver->property(MidiDriver_CMS::MIDI_PROP_PLAYSWITCH, 0xFFFF) || !_envTL)
- return;
-
- //debug("NOTEOFF: Voice: %02d", _id);
-
- _note = 0xFF;
- _envState = kRelease;
- if (_secondaryVoice)
- _secondaryVoice->noteOff();
-}
-
-void CMSVoice_V0::stop() {
- _note = 0xFF;
- if (_envState != kReady)
- _envState = kRelease;
- if (_secondaryVoice)
- _secondaryVoice->stop();
-}
-
-void CMSVoice_V0::programChange(int program) {
- assert(program < 128);
- if (program == 127) {
- // This seems to replace the start of track offset with the current position so that 0xFC (kEndOfTrack)
- // midi events would not reset the track to the start, but to the current position instead. This cannot
- // be handled here. All versions of the SCI0 driver that I have seen so far do this. Still, I somehow
- // doubt that it will ever come up, but let's see...
- warning("CMSVoice_V0::programChange(): Unhandled program change 127");
- return;
- }
-
- SciSpan<const uint8> data = _patchData.subspan(128 + (_patchData.getUint8At(program) << 3));
- uint8 pos = _isSecondary ? 3 : 0;
-
- selectEnvelope(data.getUint8At(pos++));
-
- if (_isSecondary) {
- _envSSL = data.getUint8At(pos++);
- // This decides whether the secondary voice has the same or the opposite pan position as the primary voice.
- _panMask = _strMask ^ -(_envSSL & 1);
- }
-
- _transOct = data.getInt8At(pos++);
- _transFreq = data.getInt8At(pos++);
-
- debug("CMSVoice_V0::programChange: Voice: %02d, Envelope: %02d, TransOct: %02d, TransFreq: %02d", _id, data[_isSecondary ? 3 : 0], _transOct, _transFreq);
-
- if (_isSecondary)
- _envPA = data.getUint8At(pos++);
-
- if (_secondaryVoice) {
- assert(!_isSecondary);
- if (data.getUint8At(pos) == 0xFF) {
- _secondaryVoice->stop();
- _secondaryVoice->_assign = 0xFF;
- _secondaryVoice = 0;
- } else {
- _secondaryVoice->setPanMask(_panMask);
- _secondaryVoice->programChange(program);
- }
- }
-}
-
-void CMSVoice_V0::update() {
- if (_updateCMS) {
- sendFrequency();
- cmsWrite(_regOffset, ((_currentLevel & 0xF0) | (_currentLevel >> 4)) & _panMask);
- _updateCMS = false;
- }
-
- recalculateEvelopeLevels();
-
- switch (_envState) {
- case kReady:
- _envNote = 0xFF;
- return;
-
- case kRestart:
- if (_envPAC) {
- --_envPAC;
- break;
- } else {
- //if ((_currentLevel >> 1) > _envAR)
- _currentLevel = ((_currentLevel >> 1) > (int8)_envAR) ? ((_currentLevel >> 1) - _envAR1) & 0xFF : (_envAR - _envAR1) & 0xFF;
- //_currentLevel = (uint8)MIN<int8>(0, (_currentLevel >> 1) - _envAR);
- _envState = kAttack;
- }
- // fall through
-
- case kAttack:
- _currentLevel = _currentLevel + _envAR;
- if (_currentLevel > _envTL || _currentLevel > 0xFF) {
- _currentLevel = _envTL;
- _envState = kDecay;
- }
- break;
-
- case kDecay:
- _currentLevel -= _envDR;
- if (_currentLevel <= _envSL) {
- if (_currentLevel < 0)
- _currentLevel = 0;
- _envState = kSustain;
- }
- break;
-
- case kSustain:
- _currentLevel = _envSL;
- break;
-
- case kRelease:
- _currentLevel -= _envRR;
- if (_currentLevel < 0) {
- _currentLevel = 0;
- _envState = kReady;
- }
- break;
-
- default:
- break;
- }
-
- if (_vbrOn && _envState != kRestart) {
- _vbrPhase += _vbrCur;
- if (!--_vbrState) {
- _vbrCur = -_vbrCur;
- _vbrState = (_vbrSteps & 0x0F) << 1;
- }
- }
-
- _updateCMS = true;
- ++_duration;
-}
-
-void CMSVoice_V0::reset() {
- _envState = kReady;
- _secondaryVoice = 0;
- _assign = _note = _envNote = 0xFF;
- _panMask = _id & 1 ? 0xF0 : 0x0F;
- _envTL = 0;
- _currentLevel = 0;
- _duration = 0;
- _envPA = 0;
- _transFreq = _transOct = 0;
- selectEnvelope(3);
-}
-
-void CMSVoice_V0::setPanMask(uint8 mask) {
- _strMask = mask;
-}
-
-void CMSVoice_V0::recalculateFrequency(uint8 &freq, uint8 &octave) {
- if (_assign == 0xFF || _envNote == 0xFF)
- return;
-
- uint8 note = _envNote % 12;
- octave = CLIP<int>(_envNote / 12 - 2, 0, 7);
-
- int16 pw = (_driver->property(MidiDriver_CMS::MIDI_PROP_CHANNEL_PITCHWHEEL, _assign) & 0x7FFF) - 0x2000;
- int16 sg = (pw < 0) ? -1 : 0;
- pw = (pw ^ sg) - sg;
- pw = ((pw >> 7) & 1) + (pw >> 8);
- assert(pw < ARRAYSIZE(_pitchWheelTable));
- pw = (_pitchWheelTable[pw] ^ sg) - sg;
-
- int frequency = note * 4 + pw;
-
- if (frequency < 0) {
- if (octave) {
- frequency += 48;
- --octave;
- } else {
- frequency = 0;
- }
- } else if (frequency >= 48) {
- if (octave < 7) {
- frequency -= 48;
- ++octave;
- } else {
- frequency = 47;
- }
- }
-
- octave = CLIP<int8>(octave + _transOct, 0, 7);
- frequency = _frequencyTable[frequency & 0xFF] + _transFreq + _vbrPhase;
-
- if (frequency > 255) {
- frequency &= 0xFF;
- octave++;
- } else if (frequency < 0) {
- frequency &= 0xFF;
- octave--;
- }
-
- octave = CLIP<int8>(octave, 0, 7);
- freq = frequency;
-}
-
-void CMSVoice_V0::recalculateEvelopeLevels() {
- uint8 chanVol = _driver->property(MidiDriver_CMS::MIDI_PROP_CHANNEL_VOLUME, _assign);
-
- if (_envTL && _isSecondary) {
- int volIndexTLS = (chanVol >> 4) | (_envSSL & 0xF0);
- assert(volIndexTLS < ARRAYSIZE(_volumeTable));
- _envTL = _volumeTable[volIndexTLS];
- } else if (_envTL) {
- _envTL = chanVol;
- }
-
- int volIndexSL = (_envSLI << 4) + (_envTL >> 4);
- assert(volIndexSL < ARRAYSIZE(_volumeTable));
- _envSL = _volumeTable[volIndexSL];
-}
-
-uint8 CMSVoice_V0::_envAR1 = 0;
-
-void CMSVoice_V0::selectEnvelope(int id) {
- const uint8 *in = &_envelopeDataTable[(id & 0x1F) << 3];
- _envAR = *in++;
- _envTL = *in++;
- _envDR = *in++;
- _envSLI = *in++;
- _envRR = *in++;
- /*unused*/in++;
- _vbrMod = *in++;
- _vbrSteps = *in++;
- _vbrOn = _vbrMod;
- _vbrCur = _vbrMod;
- _vbrState = _vbrSteps & 0x0F;
- _vbrPhase = 0;
- if (_id == 1)
- _envAR1 = _envAR;
-}
-
-const uint8 CMSVoice_V0::_envelopeDataTable[256] = {
- 0xff, 0xff, 0x02, 0x05, 0x08, 0x00, 0x01, 0x02,
- 0xff, 0xff, 0x40, 0x02, 0x01, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0x08, 0x02, 0x02, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0x10, 0x02, 0x02, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0x18, 0x02, 0x01, 0x00, 0x00, 0x00,
- 0x20, 0xff, 0x01, 0x06, 0x08, 0x00, 0x00, 0x00,
- 0x20, 0xff, 0x08, 0x02, 0x03, 0x00, 0x00, 0x00,
- 0x20, 0xe0, 0x02, 0x05, 0x02, 0x00, 0x00, 0x00,
- 0x10, 0xe0, 0x10, 0x03, 0x02, 0x00, 0x00, 0x00,
- 0x20, 0xe0, 0x08, 0x03, 0x03, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0x04, 0x06, 0x08, 0x00, 0x01, 0x02,
- 0xff, 0xff, 0x04, 0x02, 0x04, 0x00, 0x01, 0x02,
- 0xff, 0xff, 0x08, 0x02, 0x04, 0x00, 0x01, 0x02,
- 0xff, 0xff, 0x10, 0x02, 0x04, 0x00, 0x01, 0x02,
- 0xa0, 0xff, 0x0c, 0x03, 0x08, 0x00, 0x03, 0x01,
- 0x20, 0xff, 0x01, 0x06, 0x0c, 0x00, 0x01, 0x02,
- 0x20, 0xff, 0x08, 0x02, 0x04, 0x00, 0x01, 0x02,
- 0x20, 0xd0, 0x02, 0x05, 0x02, 0x00, 0x01, 0x02,
- 0x10, 0xff, 0x20, 0x05, 0x04, 0x00, 0x02, 0x02,
- 0x08, 0xc0, 0x10, 0x04, 0x04, 0x00, 0x02, 0x02,
- 0xc8, 0xff, 0x02, 0x05, 0x10, 0x00, 0x01, 0x02,
- 0xff, 0xff, 0x04, 0x04, 0x10, 0x00, 0x01, 0x02,
- 0xff, 0xff, 0x08, 0x03, 0x08, 0x00, 0x01, 0x02,
- 0xff, 0xff, 0x0c, 0x02, 0x08, 0x00, 0x01, 0x02,
- 0x19, 0xc4, 0x04, 0x04, 0x08, 0x00, 0x02, 0x02,
- 0x10, 0xff, 0x01, 0x06, 0x08, 0x00, 0x04, 0x02,
- 0x0a, 0xff, 0x01, 0x06, 0x08, 0x00, 0x05, 0x01,
- 0x10, 0xff, 0x0a, 0x01, 0x02, 0x00, 0x05, 0x02,
- 0xff, 0xff, 0x0a, 0x02, 0x08, 0x00, 0x05, 0x01,
- 0xff, 0xff, 0x03, 0x03, 0x08, 0x00, 0x02, 0x01,
- 0xff, 0xff, 0x08, 0x01, 0x04, 0x00, 0x0c, 0x02,
- 0xff, 0xff, 0x10, 0x02, 0x04, 0x00, 0x0f, 0x0a
-};
-
-const uint8 CMSVoice_V0::_volumeTable[176] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x40, 0x40,
- 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x20, 0x30, 0x30, 0x40, 0x40, 0x40, 0x50, 0x50, 0x60,
- 0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x30, 0x30, 0x40, 0x40, 0x50, 0x50, 0x60, 0x60, 0x70, 0x70,
- 0x00, 0x00, 0x10, 0x10, 0x20, 0x30, 0x30, 0x40, 0x40, 0x50, 0x60, 0x60, 0x70, 0x70, 0x80, 0x90,
- 0x00, 0x00, 0x10, 0x20, 0x20, 0x30, 0x40, 0x40, 0x50, 0x60, 0x70, 0x70, 0x80, 0x90, 0x90, 0xa0,
- 0x00, 0x00, 0x10, 0x20, 0x30, 0x40, 0x40, 0x50, 0x60, 0x70, 0x80, 0x80, 0x90, 0xa0, 0xb0, 0xc0,
- 0x00, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0x90, 0xa0, 0xb0, 0xc0, 0xd0,
- 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0
-};
-
-const uint8 CMSVoice_V0::_pitchWheelTable[65] = {
- 0x00, 0x01, 0x02, 0x02, 0x03, 0x04, 0x05, 0x05,
- 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0b,
- 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x11, 0x11,
- 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x17, 0x17,
- 0x18, 0x19, 0x1a, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d,
- 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, 0x23,
- 0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29,
- 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f,
- 0x30
-};
-
-CMSVoice_V1::CMSVoice_V1(uint8 id, MidiDriver_CMS* driver, CMSEmulator *cms, SciSpan<const uint8>& patchData) : CMSVoice(id, driver, cms, patchData), _velocity(0), _patchDataIndex(0),
- _amplitudeTimer(0), _amplitudeModifier(0), _release(false) {
-}
-
-CMSVoice_V1::~CMSVoice_V1() {
-}
-
-void CMSVoice_V1::noteOn(int note, int velocity) {
- _note = note;
- _release = false;
- _patchDataIndex = 0;
- _amplitudeTimer = 0;
- _duration = 0;
- _releaseDuration = 0;
- _velocity = velocity ? _velocityTable[velocity >> 3] : 0;
- sendFrequency();
-}
-
-void CMSVoice_V1::noteOff() {
- _release = true;
-}
-
-void CMSVoice_V1::stop() {
- _velocity = 0;
- _note = 0xFF;
- _sustained = false;
- _release = false;
- _patchDataIndex = 0;
- _amplitudeTimer = 0;
- _amplitudeModifier = 0;
- _duration = 0;
- _releaseDuration = 0;
-
- setupVoiceAmplitude();
-}
-
-void CMSVoice_V1::programChange(int program) {
- _patchDataCur = _patchData.subspan(_patchData.getUint16LEAt(program << 1));
-}
-
-void CMSVoice_V1::pitchWheel() {
- sendFrequency();
-}
-
-void CMSVoice_V1::update() {
- if (_note == 0xFF)
- return;
-
- if (_release)
- ++_releaseDuration;
- ++_duration;
-
- updateVoiceAmplitude();
- setupVoiceAmplitude();
-}
-
-void CMSVoice_V1::recalculateFrequency(uint8 &freq, uint8 &octave) {
- assert(_assign != 0xFF);
-
- int frequency = (CLIP<int>(_note, 21, 116) - 21) * 4;
- int16 pw = _driver->property(MidiDriver_CMS::MIDI_PROP_CHANNEL_PITCHWHEEL, _assign);
- int modifier = (pw < 0x2000) ? (0x2000 - pw) / 170 : ((pw > 0x2000) ? (pw - 0x2000) / 170 : 0);
-
- if (modifier) {
- if (pw < 0x2000) {
- if (frequency > modifier)
- frequency -= modifier;
- else
- frequency = 0;
- } else {
- int tempFrequency = 384 - frequency;
- if (modifier < tempFrequency)
- frequency += modifier;
- else
- frequency = 383;
- }
- }
-
- octave = 0;
- while (frequency >= 48) {
- frequency -= 48;
- ++octave;
- }
-
- freq = _frequencyTable[frequency & 0xFF];
-}
-
-void CMSVoice_V1::updateVoiceAmplitude() {
- if (_amplitudeTimer != 0 && _amplitudeTimer != 254) {
- --_amplitudeTimer;
- return;
- } else if (_amplitudeTimer == 254) {
- if (!_release)
- return;
- _amplitudeTimer = 0;
- }
-
- int nextDataIndex = _patchDataIndex;
- uint8 timerData = 0;
- uint8 amplitudeData = _patchDataCur[nextDataIndex];
-
- if (amplitudeData == 255) {
- timerData = amplitudeData = 0;
- stop();
- } else {
- timerData = _patchDataCur[nextDataIndex + 1];
- nextDataIndex += 2;
- }
-
- _patchDataIndex = nextDataIndex;
- _amplitudeTimer = timerData;
- _amplitudeModifier = amplitudeData;
-}
-
-void CMSVoice_V1::setupVoiceAmplitude() {
- assert(_assign != 0xFF);
- uint amplitude = 0;
- uint8 chanVolume = _driver->property(MidiDriver_CMS::MIDI_PROP_CHANNEL_VOLUME, _assign);
- uint8 masterVolume = _driver->property(MIDI_PROP_MASTER_VOLUME, 0xFFFF);
-
- if (chanVolume && _velocity && _amplitudeModifier && masterVolume) {
- amplitude = chanVolume * _velocity;
- amplitude /= 0x0F;
- amplitude *= _amplitudeModifier;
- amplitude /= 0x0F;
- amplitude *= masterVolume;
- amplitude /= 0x0F;
-
- if (!amplitude)
- ++amplitude;
- }
-
- uint8 amplitudeData = 0;
- int pan = _driver->property(MidiDriver_CMS::MIDI_PROP_CHANNEL_PANPOS, _assign) >> 2;
- if (pan >= 16) {
- amplitudeData = (amplitude * (31 - pan) / 0x0F) & 0x0F;
- amplitudeData |= (amplitude << 4);
- } else {
- amplitudeData = (amplitude * pan / 0x0F) & 0x0F;
- amplitudeData <<= 4;
- amplitudeData |= amplitude;
- }
-
- if (!_driver->property(MidiDriver_CMS::MIDI_PROP_PLAYSWITCH, 0xFFFF))
- amplitudeData = 0;
-
- cmsWrite(_regOffset, amplitudeData);
-}
-
-const int CMSVoice_V1::_velocityTable[32] = {
+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
};
-MidiDriver_CMS::MidiDriver_CMS(Audio::Mixer* mixer, ResourceManager* resMan, SciVersion version) : MidiDriver_Emulated(mixer), _resMan(resMan),
- _version(version), _cms(0), _rate(0), _playSwitch(true), _masterVolume(0), _numVoicesPrimary(version > SCI_VERSION_0_LATE ? 12 : 8),
- _actualTimerInterval(1000000000 /(_baseFreq*1000)), _reqTimerInterval(1000000000/60000), _numVoicesSecondary(version > SCI_VERSION_0_LATE ? 0 : 4) {
- memset(_voice, 0, sizeof(_voice));
- _updateTimer = _reqTimerInterval;
-}
-
-MidiDriver_CMS::~MidiDriver_CMS() {
- for (int i = 0; i < 12; ++i)
- delete _voice[i];
-}
-
int MidiDriver_CMS::open() {
if (_cms)
return MERR_ALREADY_OPEN;
@@ -798,42 +176,36 @@ int MidiDriver_CMS::open() {
Resource *res = _resMan->findResource(ResourceId(kResourceTypePatch, 101), false);
if (!res)
return -1;
-
- _patchData->allocateFromSpan(_version < SCI_VERSION_1_EARLY ? res->subspan(30) : *res);
- _rate = _mixer->getOutputRate();
- _cms = new CMSEmulator(_rate);
- assert(_cms);
-
+ _patchData->allocateFromSpan(*res);
+
for (uint i = 0; i < ARRAYSIZE(_channel); ++i)
_channel[i] = Channel();
- for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
- if (_version < SCI_VERSION_1_EARLY)
- _voice[i] = new CMSVoice_V0(i, this, _cms, *_patchData);
- else
- _voice[i] = new CMSVoice_V1(i, this, _cms, *_patchData);
- }
-
+ 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) {
- writeToChip(0, i, 0);
- writeToChip(1, i, 0);
+ writeToChip1(i, 0);
+ writeToChip2(i, 0);
}
- // Enable frequency for all channels
- writeToChip(0, 0x14, _version < SCI_VERSION_1_EARLY ? 0x3F : 0xFF);
- writeToChip(1, 0x14, _version < SCI_VERSION_1_EARLY ? 0x3F : 0xFF);
+ writeToChip1(0x14, 0xFF);
+ writeToChip2(0x14, 0xFF);
- // Sync and reset generators
- writeToChip(0, 0x1C, 2);
- writeToChip(1, 0x1C, 2);
+ writeToChip1(0x1C, 1);
+ writeToChip2(0x1C, 1);
- // Enable all channels
- writeToChip(0, 0x1C, 1);
- writeToChip(1, 0x1C, 1);
+ _samplesPerCallback = getRate() / _timerFreq;
+ _samplesPerCallbackRemainder = getRate() % _timerFreq;
+ _samplesTillCallback = 0;
+ _samplesTillCallbackRemainder = 0;
int retVal = MidiDriver_Emulated::open();
if (retVal != 0)
@@ -857,10 +229,6 @@ void MidiDriver_CMS::send(uint32 b) {
const uint8 op1 = (b >> 8) & 0xff;
const uint8 op2 = (b >> 16) & 0xff;
- // This is a SCI0 only feature. For SCI1 we simply set all channels to valid by default so that this check will always pass.
- if (!_channel[channel].isValid)
- return;
-
switch (command) {
case 0x80:
noteOff(channel, op1);
@@ -875,7 +243,7 @@ void MidiDriver_CMS::send(uint32 b) {
break;
case 0xC0:
- programChange(channel, op1);
+ _channel[channel].patch = op1;
break;
case 0xE0:
@@ -893,140 +261,215 @@ uint32 MidiDriver_CMS::property(int prop, uint32 param) {
if (param != 0xffff)
_masterVolume = param;
return _masterVolume;
- case MIDI_PROP_PLAYSWITCH:
- if (param != 0xffff)
- _playSwitch = param ? true : false;
- return _playSwitch ? 1 : 0;
- case MIDI_PROP_CHANNEL_VOLUME:
- return (param < 16) ? _channel[param].volume : 0;
- case MIDI_PROP_CHANNEL_PITCHWHEEL:
- return (param < 16) ? _channel[param].pitchWheel : 0;
- case MIDI_PROP_CHANNEL_PANPOS:
- return (param < 16) ? _channel[param].pan : 0;
+
default:
return MidiDriver_Emulated::property(prop, param);
}
}
-void MidiDriver_CMS::initTrack(SciSpan<const byte>& header) {
- if (!_isOpen || _version > SCI_VERSION_0_LATE)
- return;
+void MidiDriver_CMS::playSwitch(bool play) {
+ _playSwitch = play;
+}
- uint8 readPos = 0;
- uint8 caps = header.getInt8At(readPos++);
- int numChan = (caps == 2) ? 15 : 16;
- if (caps != 0 && caps != 2)
- return;
+void MidiDriver_CMS::writeToChip1(int address, int data) {
+ _cms->portWrite(0x221, address);
+ _cms->portWrite(0x220, data);
- for (int i = 0; i < 12; ++i)
- _voice[i]->reset();
+ if (address >= 16 && address <= 18)
+ _octaveRegs[0][address - 16] = data;
+}
- for (int i = 0; i < 16; ++i) {
- _channel[i].isValid = false;
- _channel[i].volume = 180;
- _channel[i].pitchWheel = 0x2000;
- _channel[i].pan = 0;
+void MidiDriver_CMS::writeToChip2(int address, int data) {
+ _cms->portWrite(0x223, address);
+ _cms->portWrite(0x222, data);
- if (i >= numChan)
- continue;
+ if (address >= 16 && address <= 18)
+ _octaveRegs[1][address - 16] = data;
+}
- uint8 num = header.getInt8At(readPos++) & 0x0F;
- uint8 flags = header.getInt8At(readPos++);
+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->subspan(_patchData->getUint16LEAt(_channel[voice.channel].patch * 2));
+ if (velocity)
+ velocity = _velocityTable[(velocity >> 3)];
+ voice.velocity = velocity;
+ noteSend(voiceNr);
+}
- if (num == 15 || !(flags & 4))
- continue;
+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;
- // Another strange thing about this driver... All channels to be used have to be marked as valid here
- // or they will be blocked in send(). Even control change voice mapping won't be accessible. This means
- // that a num == 0 setting could even make sense here, since it will mark that channel as valid for
- // later use (e.g. assigning voices via control change voice mapping).
- _channel[i].isValid = true;
- if (num == 0)
- continue;
+ setupVoiceAmplitude(voiceNr);
+}
+
+void MidiDriver_CMS::noteSend(int voiceNr) {
+ Voice &voice = _voice[voiceNr];
- // This weird driver will assign a second voice if the number of requested voices is exactly 1.
- // The secondary voice is configured differently (has its own instrument patch data). The secondary
- // voice is controlled through the primary voice. It will not receive its own separate commands.
- // The main purpose seems providing stereo channels with 2 discrete voices for the left and right
- // speaker output. However, the instrument patch can also turn this around so that both voices
- // use the same panning. What an awesome concept...
- bindVoices(i, num, num == 1, false);
+ 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;
+ }
}
-}
-void MidiDriver_CMS::onTimer() {
- for (_updateTimer -= _actualTimerInterval; _updateTimer <= 0; _updateTimer += _reqTimerInterval) {
- for (uint i = 0; i < ARRAYSIZE(_voice); ++i)
- _voice[i]->update();
+ 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 channelNr, int note, int velocity) {
+void MidiDriver_CMS::noteOn(int channel, int note, int velocity) {
if (note < 21 || note > 116)
return;
if (velocity == 0) {
- noteOff(channelNr, note);
+ noteOff(channel, note);
return;
}
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == channelNr && _voice[i]->_note == note) {
- if (_version > SCI_VERSION_0_LATE) {
- _voice[i]->stop();
- _voice[i]->programChange(_channel[channelNr].program);
- }
- _voice[i]->noteOn(note, velocity);
+ 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 id = findVoiceBasic(channelNr);
+ int voice = findVoiceBasic(channel);
#else
- int id = findVoice(channelNr, note);
+ int voice = findVoice(channel);
#endif
- if (id != -1) {
- if (_version > SCI_VERSION_0_LATE)
- _voice[id]->programChange(_channel[channelNr].program);
- _voice[id]->noteOn(note, velocity);
+ 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 >= 0) {
+ voiceOff(oldestVoice);
+ voice = oldestVoice;
+ } else {
+ return -1;
+ }
}
+
+ _voice[voice].channel = channel;
+ _channel[channel].lastVoiceUsed = voice;
+ return voice;
}
-void MidiDriver_CMS::noteOff(int channelNr, int note) {
+void MidiDriver_CMS::noteOff(int channel, int note) {
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
- if (_voice[i]->_assign == channelNr && _voice[i]->_note == note) {
- if (_channel[channelNr].hold != 0)
- _voice[i]->_sustained = true;
+ if (_voice[i].channel == channel && _voice[i].note == note) {
+ if (_channel[channel].hold != 0)
+ _voice[i].sustained = true;
else
- _voice[i]->noteOff();
+ _voice[i].turnOff = true;
}
}
}
-void MidiDriver_CMS::controlChange(int channelNr, int control, int value) {
- // The original SCI0 CMS drivers do not have Midi control 123. I support it nonetheless,
- // since our current music engine seems to want to have it and it does not cause problems either.
- if (_version < SCI_VERSION_1_EARLY && (control == 10 || control == 64))
- return;
-
+void MidiDriver_CMS::controlChange(int channel, int control, int value) {
switch (control) {
case 7:
- _channel[channelNr].volume = (_version < SCI_VERSION_1_EARLY) ? MAX<uint8>((value & 0x78) << 1, 0x40) : (value ? MAX<uint8>(value >> 3, 1) : 0);
+ if (value) {
+ value >>= 3;
+ if (!value)
+ ++value;
+ }
+
+ _channel[channel].volume = value;
break;
case 10:
- _channel[channelNr].pan = value;
+ _channel[channel].pan = value;
break;
case 64:
- _channel[channelNr].hold = value;
+ _channel[channel].hold = value;
if (!value) {
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == channelNr && _voice[i]->_sustained) {
- _voice[i]->_sustained = false;
- _voice[i]->noteOff();
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channel && _voice[i].sustained) {
+ _voice[i].sustained = 0;
+ _voice[i].turnOff = true;
}
}
}
@@ -1034,14 +477,14 @@ void MidiDriver_CMS::controlChange(int channelNr, int control, int value) {
case 75:
#ifndef CMS_DISABLE_VOICE_MAPPING
- voiceMapping(channelNr, value);
+ voiceMapping(channel, value);
#endif
break;
case 123:
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
- if (_voice[i]->_assign == channelNr && _voice[i]->_note != 0xFF)
- _voice[i]->stop();
+ if (_voice[i].channel == channel && _voice[i].note != 0xFF)
+ voiceOff(i);
}
break;
@@ -1050,100 +493,80 @@ void MidiDriver_CMS::controlChange(int channelNr, int control, int value) {
}
}
-void MidiDriver_CMS::programChange(int channelNr, int value) {
- _channel[channelNr].program = value;
- if (_version > SCI_VERSION_0_LATE)
- return;
+void MidiDriver_CMS::pitchWheel(int channelNr, int value) {
+ Channel &channel = _channel[channelNr];
+ channel.pitchWheel = value;
+ channel.pitchAdditive = false;
+ channel.pitchModifier = 0;
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == channelNr)
- _voice[i]->programChange(value);
+ if (value < 0x2000) {
+ channel.pitchModifier = (0x2000 - value) / 170;
+ } else if (value > 0x2000) {
+ channel.pitchModifier = (value - 0x2000) / 170;
+ channel.pitchAdditive = true;
}
-}
-void MidiDriver_CMS::pitchWheel(int channelNr, int value) {
- _channel[channelNr].pitchWheel = value;
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == channelNr && _voice[i]->_note != 0xFF)
- _voice[i]->pitchWheel();
+ 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 (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == channelNr)
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channelNr)
++curVoices;
}
- curVoices += _channel[channelNr].missingVoices;
+ curVoices += _channel[channelNr].extraVoices;
- if (curVoices < value) {
- bindVoices(channelNr, value - curVoices, curVoices == 0 && value == 1, true);
- } else if (curVoices > value) {
- unbindVoices(channelNr, curVoices - value, value == 1);
- donateVoices(value == 1);
+ if (curVoices == value) {
+ return;
+ } else if (curVoices < value) {
+ bindVoices(channelNr, value - curVoices);
+ } else {
+ unbindVoices(channelNr, curVoices - value);
+ donateVoices();
}
}
-void MidiDriver_CMS::bindVoices(int channelNr, int voices, bool bindSecondary, bool doProgramChange) {
- int secondary = bindSecondary ? _numVoicesSecondary : 0;
-
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign != 0xFF)
+void MidiDriver_CMS::bindVoices(int channel, int voices) {
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == 0xFF)
continue;
- //debug("===== MidiDriver_CMS: hardware channel %02d is assigned to device channel %02d (primary voice) =======", i, channelNr);
- _voice[i]->_assign = channelNr;
- if (_voice[i]->_note != 0xFF)
- _voice[i]->stop();
-
- for (int ii = _numVoicesPrimary; ii < _numVoicesPrimary + secondary; ++ii) {
- if (_voice[ii]->_assign != 0xFF)
- continue;
-
- _voice[ii]->_assign = channelNr;
- _voice[i]->_secondaryVoice = _voice[ii];
-
- //debug("===== MidiDriver_CMS: hardware channel %02d is assigned to device channel %02d (secondary voice) =====", ii, channelNr);
- break;
- }
+ Voice &voice = _voice[i];
+ voice.channel = channel;
- // This will also release the secondary voice binding immediately if the current patch does
- // not require such an extra channel. This condition will not be checked when called from initTrack().
- if (doProgramChange)
- _voice[i]->programChange(_channel[channelNr].program);
+ if (voice.note != 0xFF)
+ voiceOff(i);
--voices;
if (voices == 0)
break;
}
- _channel[channelNr].missingVoices += voices;
+ _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, bool bindSecondary) {
- int secondary = bindSecondary ? _numVoicesSecondary : 0;
+void MidiDriver_CMS::unbindVoices(int channelNr, int voices) {
Channel &channel = _channel[channelNr];
- if (channel.missingVoices >= voices) {
- channel.missingVoices -= voices;
+ if (channel.extraVoices >= voices) {
+ channel.extraVoices -= voices;
} else {
- voices -= channel.missingVoices;
- channel.missingVoices = 0;
-
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == channelNr && _voice[i]->_note == 0xFF) {
- _voice[i]->_assign = 0xFF;
-
- CMSVoice *sec = _voice[i]->_secondaryVoice;
- if (sec) {
- sec->stop();
- sec->_assign = 0xFF;
- _voice[i]->_secondaryVoice = 0;
- }
+ 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;
@@ -1154,15 +577,15 @@ void MidiDriver_CMS::unbindVoices(int channelNr, int voices, bool bindSecondary)
uint16 voiceTime = 0;
uint voiceNr = 0;
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign != channelNr)
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel != channelNr)
continue;
- uint16 curTime = _voice[i]->_releaseDuration;
+ uint16 curTime = _voice[i].turnOffTicks;
if (curTime)
curTime += 0x8000;
else
- curTime = _voice[i]->_duration;
+ curTime = _voice[i].ticks;
if (curTime >= voiceTime) {
voiceNr = i;
@@ -1170,112 +593,72 @@ void MidiDriver_CMS::unbindVoices(int channelNr, int voices, bool bindSecondary)
}
}
- _voice[voiceNr]->_sustained = false;
- _voice[voiceNr]->stop();
- _voice[voiceNr]->_assign = 0xFF;
-
- CMSVoice *sec = _voice[voiceNr]->_secondaryVoice;
- if (sec) {
- sec->stop();
- sec->_assign = 0xFF;
- _voice[voiceNr]->_secondaryVoice = 0;
- }
-
+ _voice[voiceNr].sustained = 0;
+ voiceOff(voiceNr);
+ _voice[voiceNr].channel = 0xFF;
--voices;
} while (voices != 0);
}
-
- for (int i = _numVoicesPrimary; i < _numVoicesPrimary + secondary; ++i) {
- if (_voice[i]->_assign != 0xFF)
- continue;
-
- _voice[i]->_assign = channelNr;
- if (_voice[i]->_note != 0xFF)
- _voice[i]->stop();
-
- for (int ii = 0; ii < _numVoicesPrimary; ++ii) {
- if (_voice[ii]->_assign != channelNr)
- continue;
- _voice[ii]->_secondaryVoice = _voice[i];
- // This will release the secondary binding immediately if the current patch does not require such an extra channel.
- _voice[ii]->programChange(_channel[channelNr].program);
- break;
- }
-
- if (_voice[i]->_assign == channelNr && _voice[i]->_note != 0xFF)
- _voice[i]->stop();
- break;
- }
}
-void MidiDriver_CMS::donateVoices(bool bindSecondary) {
+void MidiDriver_CMS::donateVoices() {
int freeVoices = 0;
- for (int i = 0; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == 0xFF)
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == 0xFF)
++freeVoices;
}
if (!freeVoices)
return;
- for (int i = 0; i < ARRAYSIZE(_channel); ++i) {
+ for (uint i = 0; i < ARRAYSIZE(_channel); ++i) {
Channel &channel = _channel[i];
- if (!channel.missingVoices) {
+ if (!channel.extraVoices) {
continue;
- } else if (channel.missingVoices < freeVoices) {
- freeVoices -= channel.missingVoices;
- int missing = channel.missingVoices;
- channel.missingVoices = 0;
- bindVoices(i, missing, bindSecondary, true);
+ } else if (channel.extraVoices < freeVoices) {
+ freeVoices -= channel.extraVoices;
+ channel.extraVoices = 0;
+ bindVoices(i, channel.extraVoices);
} else {
- channel.missingVoices -= freeVoices;
- bindVoices(i, freeVoices, bindSecondary, true);
+ channel.extraVoices -= freeVoices;
+ bindVoices(i, freeVoices);
return;
}
}
}
-int MidiDriver_CMS::findVoice(int channelNr, int note) {
+int MidiDriver_CMS::findVoice(int channelNr) {
Channel &channel = _channel[channelNr];
int voiceNr = channel.lastVoiceUsed;
+
int newVoice = 0;
- int newVoiceAltSCI0 = (_version > SCI_VERSION_0_LATE) ? -2 : -1;
uint16 newVoiceTime = 0;
bool loopDone = false;
do {
++voiceNr;
- if (voiceNr == _numVoicesPrimary)
+ if (voiceNr == 12)
voiceNr = 0;
+ Voice &voice = _voice[voiceNr];
+
if (voiceNr == channel.lastVoiceUsed)
loopDone = true;
- if (_voice[voiceNr]->_assign == channelNr) {
- if (_voice[voiceNr]->_note == 0xFF) {
- channel.lastVoiceUsed = (_version > SCI_VERSION_0_LATE) ? voiceNr : _numVoicesPrimary - 1;
+ if (voice.channel == channelNr) {
+ if (voice.note == 0xFF) {
+ channel.lastVoiceUsed = voiceNr;
return voiceNr;
}
- int cnt = 1;
- for (int i = voiceNr + 1; i < _numVoicesPrimary; ++i) {
- if (_voice[i]->_assign == channelNr)
- ++cnt;
- }
-
- // The SCI0 driver will (before resorting to the "note age test") simply return the first
- // assigned voice as long as there are no other (primary) voices assigned to the midi part.
- if (cnt == 1 && newVoiceAltSCI0 == -1)
- newVoiceAltSCI0 = voiceNr;
-
- uint16 curTime = _voice[voiceNr]->_releaseDuration;
+ uint16 curTime = voice.turnOffTicks;
if (curTime)
curTime += 0x8000;
else
- curTime = _voice[voiceNr]->_duration;
+ curTime = voice.ticks;
if (curTime >= newVoiceTime) {
newVoice = voiceNr;
@@ -1284,109 +667,147 @@ int MidiDriver_CMS::findVoice(int channelNr, int note) {
}
} while (!loopDone);
- if (newVoiceAltSCI0 >= 0)
- return newVoiceAltSCI0;
-
if (newVoiceTime > 0) {
voiceNr = newVoice;
- channel.lastVoiceUsed = _numVoicesPrimary - 1;
-
- if (_version > SCI_VERSION_0_LATE) {
- _voice[voiceNr]->stop();
- channel.lastVoiceUsed = voiceNr;
- }
-
+ _voice[voiceNr].sustained = 0;
+ voiceOff(voiceNr);
+ channel.lastVoiceUsed = voiceNr;
return voiceNr;
+ } else {
+ return -1;
}
-
- return -1;
}
-int MidiDriver_CMS::findVoiceBasic(int channelNr) {
- int voice = -1;
- int oldestVoice = -1;
- int oldestAge = -1;
+void MidiDriver_CMS::updateVoiceAmplitude(int voiceNr) {
+ Voice &voice = _voice[voiceNr];
- // Try to find a voice assigned to this channel that is free (round-robin)
- for (int i = 0; i < _numVoicesPrimary; i++) {
- int v = (_channel[channelNr].lastVoiceUsed + i + 1) % _numVoicesPrimary;
-
- if (_voice[v]->_note == 0xFF) {
- voice = v;
- break;
- }
+ if (voice.amplitudeTimer != 0 && voice.amplitudeTimer != 254) {
+ --voice.amplitudeTimer;
+ return;
+ } else if (voice.amplitudeTimer == 254) {
+ if (!voice.turnOff)
+ return;
- // We also keep track of the oldest note in case the search fails
- if (_voice[v]->_duration > oldestAge) {
- oldestAge = _voice[v]->_duration;
- oldestVoice = v;
- }
+ voice.amplitudeTimer = 0;
}
- if (voice == -1) {
- if (oldestVoice >= 0) {
- _voice[oldestVoice]->stop();
- voice = oldestVoice;
- } else {
- return -1;
- }
+ 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[voice]->_assign = channelNr;
- _channel[channelNr].lastVoiceUsed = (_version > SCI_VERSION_0_LATE) ? voice : 0;
- return voice;
+ voice.patchDataIndex = nextDataIndex;
+ voice.amplitudeTimer = timerData;
+ voice.amplitudeModifier = amplitudeData;
}
-void MidiDriver_CMS::writeToChip(int chip, int address, int data) {
- assert(chip == 0 || chip == 1);
- _cms->portWrite(0x221 + (chip << 1), address);
- _cms->portWrite(0x220 + (chip << 1), data);
+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) {
- _cms->readBuffer(buffer, 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) {}
+ MidiPlayer_CMS(SciVersion version) : MidiPlayer(version) {
+ }
- int open(ResourceManager *resMan);
- void close();
+ int open(ResourceManager *resMan) {
+ if (_driver)
+ return MidiDriver::MERR_ALREADY_OPEN;
- void MidiPlayer_CMS::initTrack(SciSpan<const byte>& trackData);
+ _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 = nullptr;
+ }
bool hasRhythmChannel() const { return false; }
- byte getPlayId() const { return _version > SCI_VERSION_0_LATE ? 9 : 4; }
+ byte getPlayId() const { return 9; }
int getPolyphony() const { return 12; }
- void playSwitch(bool play) { _driver->property(MidiDriver_CMS::MIDI_PROP_PLAYSWITCH, play ? 1 : 0); }
+ void playSwitch(bool play) { static_cast<MidiDriver_CMS *>(_driver)->playSwitch(play); }
};
-int MidiPlayer_CMS::open(ResourceManager *resMan) {
- if (_driver)
- return MidiDriver::MERR_ALREADY_OPEN;
-
- _driver = new MidiDriver_CMS(g_system->getMixer(), resMan, _version);
- int driverRetVal = _driver->open();
- if (driverRetVal != 0)
- return driverRetVal;
-
- return 0;
-}
-
-void MidiPlayer_CMS::close() {
- _driver->setTimerCallback(0, 0);
- _driver->close();
- delete _driver;
- _driver = nullptr;
-}
-
-void MidiPlayer_CMS::initTrack(SciSpan<const byte>& trackData) {
- if (_driver)
- static_cast<MidiDriver_CMS*>(_driver)->initTrack(trackData);
-}
-
MidiPlayer *MidiPlayer_CMS_create(SciVersion version) {
return new MidiPlayer_CMS(version);
}
diff --git a/engines/sci/sound/drivers/pc9801.cpp b/engines/sci/sound/drivers/pc9801.cpp
index 824c4c9428..fe7d5bf7ba 100644
--- a/engines/sci/sound/drivers/pc9801.cpp
+++ b/engines/sci/sound/drivers/pc9801.cpp
@@ -1343,7 +1343,6 @@ int MidiDriver_PC9801::open() {
return MERR_CANNOT_CONNECT;
_pc98a->setSoundEffectChanMask(0);
_pc98a->ssgSetVolume(205);
- _pc98a->writeReg(0, 0x26, 256 - _baseTempo / 288);
_ready = true;
}
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index dc62acb395..5977cf0177 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -84,9 +84,8 @@ void SciMusic::init() {
if (g_sci->_features->useAltWinGMSound())
deviceFlags |= MDT_PREFER_GM;
- // SCI_VERSION_0_EARLY games apparently don't support the CMS. At least there
- // is no patch resource 101 and I also haven't seen any CMS driver file so far.
- if (getSciVersion() > SCI_VERSION_0_EARLY && getSciVersion() <= SCI_VERSION_1_1)
+ // Currently our CMS implementation only supports SCI1(.1)
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY && getSciVersion() <= SCI_VERSION_1_1)
deviceFlags |= MDT_CMS;
if (g_sci->getPlatform() == Common::kPlatformFMTowns) {