diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/cine/script_fw.cpp | 24 | ||||
-rw-r--r-- | engines/kyra/sound/drivers/audiomaster2.cpp | 10 | ||||
-rw-r--r-- | engines/sci/engine/kscripts.cpp | 4 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.cpp | 60 | ||||
-rw-r--r-- | engines/sci/sound/drivers/cms.cpp | 1453 | ||||
-rw-r--r-- | engines/sci/sound/drivers/pc9801.cpp | 1 | ||||
-rw-r--r-- | engines/sci/sound/music.cpp | 5 |
7 files changed, 444 insertions, 1113 deletions
diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index 9692c54b7b..86eb709d5a 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -707,29 +707,7 @@ int FWScript::execute() { if (_script._size) { while (!ret) { - - if (g_cine->getGameType() == Cine::GType_OS) { - if (_pos == 0x0f9a) { - if (!scumm_stricmp(currentPrcName, "AUTO00.PRC")) - g_cine->_globalVars[200] = g_cine->_objectTable[235].costume; - } - } else { - if (_pos == 0x2c) { - if (g_cine->_objectTable[1].x == 0x0131) { - if (!scumm_stricmp(currentPrcName, "TOTO.PRC")) - g_cine->_objectTable[2].x = 0x02; - } - } /*else if (!scumm_stricmp(currentPrcName, "CODE2.PRC")) { - if (_pos == 1) { - //globalVars[0] = objectTable[scriptElement->scriptPtr[6]].frame; - } else if (_pos == 504) { - //_currentScriptElement->localVars[1] = _currentScriptElement->localVars[2] = 0; - //globalVars[251] = 0; - g_cine->_globalVars[251] = g_cine->_globalVars[251]; - } - }*/ - } - + _line = _pos; byte opcode = getNextByte(); OpFunc handler = _info->opcodeHandler(opcode); diff --git a/engines/kyra/sound/drivers/audiomaster2.cpp b/engines/kyra/sound/drivers/audiomaster2.cpp index c22f8737cc..d50d51966f 100644 --- a/engines/kyra/sound/drivers/audiomaster2.cpp +++ b/engines/kyra/sound/drivers/audiomaster2.cpp @@ -892,8 +892,6 @@ void AudioMaster2ResourceManager::initResource(SoundResource *resource) { if (!resource) return; - Common::StackLock lock(_mutex); - SoundResource *res = retrieveFromChain(resource->getName()); // The driver does not replace resources with the same name, but disposes the new resource instead. // So these names seem to be considered "globally unique". @@ -1286,7 +1284,7 @@ void AudioMaster2Internal::fadeOut(int delay) { } bool AudioMaster2Internal::isFading() { - return _ready ? _io->isFading() : false; + return _io->isFading(); } void AudioMaster2Internal::setMusicVolume(int volume) { @@ -1315,18 +1313,18 @@ void AudioMaster2Internal::resetCounter() { } int AudioMaster2Internal::getPlayDuration() { - return _ready ? _durationCounter : 0; + return _durationCounter; } void AudioMaster2Internal::sync(SoundResource *res) { if (!_ready || !res) return; - Common::StackLock lock(_mutex); - if (res->getType() != 1) return; + Common::StackLock lock(_mutex); + SoundResourceSMUS *smus = static_cast<SoundResourceSMUS*>(res); _io->_tempo = smus->getTempo(); smus->setSync(_io->_sync); 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) { |