From 37900b18ee177845b70b1a7dc5c7d86c87887640 Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Sat, 30 May 2015 16:23:46 +0200 Subject: SHERLOCK: adlib driver: pitch bend + proper reset --- engines/sherlock/scalpel/drivers/adlib.cpp | 105 ++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 9 deletions(-) (limited to 'engines/sherlock/scalpel') diff --git a/engines/sherlock/scalpel/drivers/adlib.cpp b/engines/sherlock/scalpel/drivers/adlib.cpp index 7ee5256138..5786a33234 100644 --- a/engines/sherlock/scalpel/drivers/adlib.cpp +++ b/engines/sherlock/scalpel/drivers/adlib.cpp @@ -253,11 +253,17 @@ private: // stores information about all FM voice channels adlib_ChannelEntry _channels[SHERLOCK_ADLIB_VOICES_COUNT]; - void programChange(byte channel, byte parameter); + void resetAdLib(); + void resetAdLib_OperatorRegisters(byte baseRegister, byte value); + void resetAdLib_FMVoiceChannelRegisters(byte baseRegister, byte value); + + void programChange(byte MIDIchannel, byte parameter); void setRegister(int reg, int value); - void noteOn(byte channel, byte note, byte velocity); - void noteOff(byte channel, byte note); + void noteOn(byte MIDIchannel, byte note, byte velocity); + void noteOff(byte MIDIchannel, byte note); void voiceOnOff(byte FMVoiceChannel, bool KeyOn, byte note, byte velocity); + + void pitchBendChange(byte MIDIchannel, byte parameter1, byte parameter2); }; #if USE_SCI_MIDI_PLAYER @@ -295,10 +301,6 @@ int MidiDriver_AdLib::open() { _opl->init(rate); - setRegister(0xBD, 0); - setRegister(0x08, 0); - setRegister(0x01, 0x20); - MidiDriver_Emulated::open(); _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); @@ -324,11 +326,56 @@ void MidiDriver_AdLib::newMusicData(byte *musicData, int32 musicDataSize) { // MIDI Channel <-> FM Voice Channel mapping at offset 0x22 of music data memcpy(&_voiceChannelMapping, musicData + 0x22, 9); - // reset OPL here? + // reset OPL + resetAdLib(); + // reset current channel data memset(&_channels, 0, sizeof(_channels)); } +void MidiDriver_AdLib::resetAdLib() { + + setRegister(0x01, 0x20); // enable waveform control on both operators + setRegister(0x04, 0xE0); // Timer control + + setRegister(0x08, 0); // select FM music mode + setRegister(0xBD, 0); // disable Rhythm + + // reset FM voice instrument data + resetAdLib_OperatorRegisters(0x20, 0); + resetAdLib_OperatorRegisters(0x60, 0); + resetAdLib_OperatorRegisters(0x80, 0); + resetAdLib_FMVoiceChannelRegisters(0xA0, 0); + resetAdLib_FMVoiceChannelRegisters(0xB0, 0); + resetAdLib_FMVoiceChannelRegisters(0xC0, 0); + resetAdLib_OperatorRegisters(0xE0, 0); + resetAdLib_OperatorRegisters(0x40, 0x3F); +} + +void MidiDriver_AdLib::resetAdLib_OperatorRegisters(byte baseRegister, byte value) { + byte operatorIndex; + + for (operatorIndex = 0; operatorIndex < 0x16; operatorIndex++) { + switch (operatorIndex) { + case 0x06: + case 0x07: + case 0x0E: + case 0x0F: + break; + default: + setRegister(baseRegister + operatorIndex, value); + } + } +} + +void MidiDriver_AdLib::resetAdLib_FMVoiceChannelRegisters(byte baseRegister, byte value) { + byte FMvoiceChannel; + + for (FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) { + setRegister(baseRegister + FMvoiceChannel, value); + } +} + // MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php void MidiDriver_AdLib::send(uint32 b) { byte command = b & 0xf0; @@ -354,8 +401,8 @@ void MidiDriver_AdLib::send(uint32 b) { // Aftertouch doesn't seem to be implemented in the Sherlock Holmes adlib driver break; case 0xe0: - // TODO: Implement this, occurs right in the intro, second scene warning("pitch bend change"); + pitchBendChange(channel, op1, op2); break; case 0xf0: // SysEx warning("SysEx: %lx", b); @@ -395,6 +442,7 @@ void MidiDriver_AdLib::noteOff(byte MIDIchannel, byte note) { if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) { if (_channels[FMvoiceChannel].currentNote == note) { _channels[FMvoiceChannel].inUse = false; + _channels[FMvoiceChannel].currentNote = 0; voiceOnOff(FMvoiceChannel, false, note, 0); return; @@ -444,6 +492,45 @@ void MidiDriver_AdLib::voiceOnOff(byte FMvoiceChannel, bool keyOn, byte note, by _channels[FMvoiceChannel].currentB0hReg = regValueB0h; } +void MidiDriver_AdLib::pitchBendChange(byte MIDIchannel, byte parameter1, byte parameter2) { + uint16 channelFrequency = 0; + byte channelRegB0hWithoutFrequency = 0; + uint16 parameter = 0; + byte regValueA0h = 0; + byte regValueB0h = 0; + + for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) { + if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) { + if (_channels[FMvoiceChannel].inUse) { + // FM voice channel found and it's currently in use -> apply pitch bend change + + // Remove frequency bits from current channel B0h-register + channelFrequency = ((_channels[FMvoiceChannel].currentB0hReg << 8) | (_channels[FMvoiceChannel].currentA0hReg)) & 0x3FF; + channelRegB0hWithoutFrequency = _channels[FMvoiceChannel].currentB0hReg & 0xFC; + + if (parameter2 < 0x40) { + channelFrequency = channelFrequency / 2; + } else { + parameter2 = parameter2 - 0x40; + } + parameter1 = parameter1 * 2; + parameter = parameter1 | (parameter2 << 8); + parameter = parameter * 4; + + parameter = (parameter >> 8) + 0xFF; + channelFrequency = channelFrequency * parameter; + channelFrequency = (channelFrequency >> 8) | (parameter << 8); + + regValueA0h = channelFrequency & 0xFF; + regValueB0h = (channelFrequency >> 8) | channelRegB0hWithoutFrequency; + + setRegister(0xA0 + FMvoiceChannel, regValueA0h); + setRegister(0xB0 + FMvoiceChannel, regValueB0h); + } + } + } +} + void MidiDriver_AdLib::programChange(byte MIDIchannel, byte op1) { const adlib_InstrumentEntry *instrumentPtr; byte op1Reg = 0; -- cgit v1.2.3