diff options
author | athrxx | 2019-07-17 20:44:32 +0200 |
---|---|---|
committer | athrxx | 2019-07-17 20:46:32 +0200 |
commit | a6bf63bc1a8e79712c9dcf53c04ab3108919ec2c (patch) | |
tree | a4d876c81b81716a0b481c0e6c9c71d0a1e1fd85 /engines/sci/sound/drivers | |
parent | f62f0c2db25d6a12b2b2d4723f0405e2531de2b3 (diff) | |
download | scummvm-rg350-a6bf63bc1a8e79712c9dcf53c04ab3108919ec2c.tar.gz scummvm-rg350-a6bf63bc1a8e79712c9dcf53c04ab3108919ec2c.tar.bz2 scummvm-rg350-a6bf63bc1a8e79712c9dcf53c04ab3108919ec2c.zip |
SCI: revert accidental commits
Diffstat (limited to 'engines/sci/sound/drivers')
-rw-r--r-- | engines/sci/sound/drivers/cms.cpp | 1462 | ||||
-rw-r--r-- | engines/sci/sound/drivers/fb01.cpp | 140 | ||||
-rw-r--r-- | engines/sci/sound/drivers/pc9801.cpp | 1 |
3 files changed, 448 insertions, 1155 deletions
diff --git a/engines/sci/sound/drivers/cms.cpp b/engines/sci/sound/drivers/cms.cpp index 908b0ef596..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]; + + 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; + } + } - // 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) and it - // 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 chipNumber = 0; + if (voiceNr >= 6) { + voiceNr -= 6; + chipNumber = 1; } -} -void MidiDriver_CMS::onTimer() { - for (_updateTimer -= _actualTimerInterval; _updateTimer <= 0; _updateTimer += _reqTimerInterval) { - for (uint i = 0; i < ARRAYSIZE(_voice); ++i) - _voice[i]->update(); + 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,105 +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; - - 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); - }/*else if (_version < SCI_VERSION_1_EARLY && value == 1) { - // The purpose of these lines would be to fill up missing secondary voices. - // I have commented them out, since the original driver doesn't do that either. - unbindVoices(channelNr, 1, true); - bindVoices(channelNr, 1, true, true); - }*/ -} + curVoices += _channel[channelNr].extraVoices; -void MidiDriver_CMS::bindVoices(int channelNr, int voices, bool bindSecondary, bool doProgramChange) { - int secondary = bindSecondary ? _numVoicesSecondary : 0; + if (curVoices == value) { + return; + } else if (curVoices < value) { + bindVoices(channelNr, value - curVoices); + } else { + unbindVoices(channelNr, curVoices - value); + donateVoices(); + } +} - 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; @@ -1159,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; @@ -1175,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; @@ -1289,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 initTrack(SciSpan<const byte>& header); + _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>& header) { - if (_driver) - static_cast<MidiDriver_CMS*>(_driver)->initTrack(header); -} - MidiPlayer *MidiPlayer_CMS_create(SciVersion version) { return new MidiPlayer_CMS(version); } diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp index 4713b2346b..3f3f581ee2 100644 --- a/engines/sci/sound/drivers/fb01.cpp +++ b/engines/sci/sound/drivers/fb01.cpp @@ -55,7 +55,6 @@ public: int open(ResourceManager *resMan); void close(); - void initTrack(SciSpan<const byte>& header); void send(uint32 b); void sysEx(const byte *msg, uint16 length); bool hasRhythmChannel() const { return false; } @@ -75,7 +74,6 @@ private: void setSystemParam(byte sysChan, byte param, byte value); void sendVoiceData(byte instrument, const SciSpan<const byte> &data); void sendBanks(const SciSpan<const byte> &data); - void sendDisplayString(int bank); void storeVoiceData(byte instrument, byte bank, byte index); void initVoices(); @@ -104,7 +102,6 @@ private: struct Voice { int8 channel; // MIDI channel that this voice is assigned to or -1 - uint8 poly; // Number of hardware voices int8 note; // Currently playing MIDI note or -1 int bank; // Current bank setting or -1 int patch; // Currently playing patch or -1 @@ -112,13 +109,11 @@ private: bool isSustained; // Flag indicating a note that is being sustained by the hold pedal uint16 age; // Age of the current note - Voice() : channel(-1), note(-1), bank(-1), patch(-1), velocity(0), isSustained(false), age(0), poly(0) { } + Voice() : channel(-1), note(-1), bank(-1), patch(-1), velocity(0), isSustained(false), age(0) { } }; bool _playSwitch; int _masterVolume; - int _numParts; - bool _isOpen; Channel _channels[16]; Voice _voices[kVoices]; @@ -131,8 +126,7 @@ private: byte _sysExBuf[kMaxSysExSize]; }; -MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL), - _numParts(version == SCI_VERSION_0_LATE ? 0 : kVoices), _isOpen(false) { +MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL) { MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI); _driver = MidiDriver::createMidi(dev); @@ -270,22 +264,16 @@ int MidiPlayer_Fb01::findVoice(int channel) { } void MidiPlayer_Fb01::sendToChannel(byte channel, byte command, byte op1, byte op2) { - for (int i = 0; i < _numParts; i++) { + for (int i = 0; i < kVoices; i++) { // Send command to all voices assigned to this channel if (_voices[i].channel == channel) - _driver->send(command | (_version == SCI_VERSION_0_LATE ? channel : i), op1, op2); + _driver->send(command | i, op1, op2); } } void MidiPlayer_Fb01::setPatch(int channel, int patch) { int bank = 0; - if (_version == SCI_VERSION_0_LATE && channel == 15) { - // The original driver has some parsing related handling for program 127. - // We can't handle that here. - return; - } - _channels[channel].patch = patch; if (patch >= 48) { @@ -293,13 +281,13 @@ void MidiPlayer_Fb01::setPatch(int channel, int patch) { bank = 1; } - for (int voice = 0; voice < _numParts; voice++) { + for (int voice = 0; voice < kVoices; voice++) { if (_voices[voice].channel == channel) { if (_voices[voice].bank != bank) { _voices[voice].bank = bank; setVoiceParam(voice, 4, bank); } - _driver->send(0xc0 | (_version == SCI_VERSION_0_LATE ? channel : voice), patch, 0); + _driver->send(0xc0 | voice, patch, 0); } } } @@ -354,22 +342,6 @@ void MidiPlayer_Fb01::noteOn(int channel, int note, int velocity) { } void MidiPlayer_Fb01::controlChange(int channel, int control, int value) { - if (_version == SCI_VERSION_0_LATE) { - // The SCI specific 0x4B control is the only one that gets filtered here. - // We also ignore the control channel 15. - if (control == 0x4B) { - for (int i = 0; i < _numParts; ++i) { - if (_voices[i].channel == channel && _voices[i].poly != value) { - _voices[i].poly = value; - setVoiceParam(i, 0, value); - } - } - } else if (channel != 15) { - sendToChannel(channel, 0xb0, control, value); - } - return; - } - switch (control) { case 0x07: { _channels[channel].volume = value; @@ -413,16 +385,6 @@ void MidiPlayer_Fb01::send(uint32 b) { byte op1 = (b >> 8) & 0x7f; byte op2 = (b >> 16) & 0x7f; - if (_version == SCI_VERSION_0_LATE && command != 0xB0 && command != 0xC0) { - // Since the voice mapping takes place inside the hardware, most messages - // are simply passed through. Channel 15 is never assigned to a part but is - // used for certain parsing related events which we cannot handle here. - // Just making sure that no nonsense is sent to the device... - if (channel != 15) - sendToChannel(channel, command, op1, op2); - return; - } - switch (command) { case 0x80: noteOff(channel, op1); @@ -485,40 +447,23 @@ void MidiPlayer_Fb01::sendBanks(const SciSpan<const byte> &data) { if (data.size() < 3072) error("Failed to read FB-01 patch"); - int first = (_version == SCI_VERSION_0_LATE) ? 1 : 0; - sendDisplayString(0); - // SSCI sends bank dumps containing 48 instruments at once. We cannot do that // due to the limited maximum SysEx length. Instead we send the instruments // one by one and store them in the banks. - for (int i = first; i < 48; i++) { + for (int i = 0; i < 48; i++) { sendVoiceData(0, data.subspan(i * 64)); storeVoiceData(0, 0, i); } // Send second bank if available if (data.size() >= 6146 && data.getUint16BEAt(3072) == 0xabcd) { - sendDisplayString(1); - for (int i = first; i < 48; i++) { + for (int i = 0; i < 48; i++) { sendVoiceData(0, data.subspan(3074 + i * 64)); storeVoiceData(0, 1, i); } } } -void MidiPlayer_Fb01::sendDisplayString(int bank) { - if (_version != SCI_VERSION_0_LATE) - return; - - Common::String sierraStr = Common::String::format("SIERRA %d", bank + 1); - byte buf[64]; - memset(buf, 0, 64); - Common::strlcpy((char*)buf, sierraStr.c_str(), sierraStr.size()); - SciSpan<const byte> displayStr(buf, 64); - - sendVoiceData(0, displayStr); -} - int MidiPlayer_Fb01::open(ResourceManager *resMan) { assert(resMan != NULL); @@ -529,8 +474,7 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) { } // Set system channel to 0. We send this command over all 16 system channels - int n = (_version == SCI_VERSION_0_LATE) ? 1 : 16; - for (int i = 0; i < n; i++) + for (int i = 0; i < 16; i++) setSystemParam(i, 0x20, 0); // Turn off memory protection @@ -581,8 +525,6 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) { // Set master volume setSystemParam(0, 0x24, 0x7f); - _isOpen = true; - return 0; } @@ -590,65 +532,6 @@ void MidiPlayer_Fb01::close() { _driver->close(); } -void MidiPlayer_Fb01::initTrack(SciSpan<const byte>& header) { - // I haven't seen any SCI0_EARLY variant of this driver. Skip this for now... - if (!_isOpen || _version != SCI_VERSION_0_LATE) - return; - - uint8 readPos = 0; - uint8 caps = header.getInt8At(readPos++); - if (caps != 0 && caps != 2) - return; - - for (int i = 0; i < 8; ++i) - _voices[i] = Voice(); - - _numParts = 0; - for (int i = 0; i < 16; ++i) { - uint8 num = header.getInt8At(readPos++) & 0x7F; - uint8 flags = header.getInt8At(readPos++); - - if (flags & 0x02) { - _voices[_numParts].channel = i; - _voices[_numParts].poly = num; - _numParts++; - } - } - - for (int i = 0; i < _numParts; ++i) - setVoiceParam(i, 1, _voices[i].channel); - - // From here this is more or less a copy of initVoices() with some modifications which are relevant for the - // correct assignment of hardware channels. TODO: Maybe merge this somehow. I'd have to see a SCI1 driver for - // that, though. Right now, this is only about fixing SCI_0_LATE... - int i = 2; - _sysExBuf[i++] = 0x70; - - for (int j = 0; j < 16; j++) { - _sysExBuf[i++] = 0x70 | j; - _sysExBuf[i++] = 0x00; - _sysExBuf[i++] = 0x00; - } - - for (int j = 0; j < _numParts; ++j) { - _sysExBuf[i] = _sysExBuf[i + 3] = _sysExBuf[i + 6] = _sysExBuf[i + 9] = _sysExBuf[i + 12] = _voices[j].channel | 0x70; - _sysExBuf[i + 1] = 0; - _sysExBuf[i + 2] = _voices[j].poly; - _sysExBuf[i + 4] = 2; - _sysExBuf[i + 5] = 127; - _sysExBuf[i + 7] = 3; - _sysExBuf[i + 8] = 0; - _sysExBuf[i + 10] = 4; - _sysExBuf[i + 11] = 0; - _sysExBuf[i + 13] = 5; - _sysExBuf[i + 14] = 10; - i += 15; - } - - sysEx(_sysExBuf, i); - setSystemParam(0, 0x24, (_masterVolume << 3) + 7); -} - void MidiPlayer_Fb01::setVoiceParam(byte voice, byte param, byte value) { _sysExBuf[2] = 0x00; _sysExBuf[3] = 0x18 | voice; @@ -699,11 +582,6 @@ void MidiPlayer_Fb01::storeVoiceData(byte instrument, byte bank, byte index) { } void MidiPlayer_Fb01::initVoices() { - // There is no need for this in SCI0, since the voice assignment is done in initTrack(). - // Maybe this can be merged (would require that I actually look at the SCI1 version of the driver). - if (_version == SCI_VERSION_0_LATE) - return; - int i = 2; _sysExBuf[i++] = 0x70; 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; } |