From eb829a33c91496c516cb7245286c8e1029fd145a Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sat, 30 Jun 2018 10:20:15 -0700 Subject: XEEN: Split the Adlib sound driver into it's own file --- engines/xeen/module.mk | 1 + engines/xeen/sound.cpp | 3 +- engines/xeen/sound_driver.cpp | 400 ---------------------------------- engines/xeen/sound_driver.h | 126 ----------- engines/xeen/sound_driver_adlib.cpp | 424 ++++++++++++++++++++++++++++++++++++ engines/xeen/sound_driver_adlib.h | 162 ++++++++++++++ 6 files changed, 589 insertions(+), 527 deletions(-) create mode 100644 engines/xeen/sound_driver_adlib.cpp create mode 100644 engines/xeen/sound_driver_adlib.h (limited to 'engines/xeen') diff --git a/engines/xeen/module.mk b/engines/xeen/module.mk index 5b3f69710f..4b8272df3c 100644 --- a/engines/xeen/module.mk +++ b/engines/xeen/module.mk @@ -53,6 +53,7 @@ MODULE_OBJS := \ scripts.o \ sound.o \ sound_driver.o \ + sound_driver_adlib.o \ spells.o \ sprites.o \ subtitles.o \ diff --git a/engines/xeen/sound.cpp b/engines/xeen/sound.cpp index ae70c1f5d8..5148a006b5 100644 --- a/engines/xeen/sound.cpp +++ b/engines/xeen/sound.cpp @@ -24,6 +24,7 @@ #include "audio/decoders/voc.h" #include "common/config-manager.h" #include "xeen/sound.h" +#include "xeen/sound_driver_adlib.h" #include "xeen/xeen.h" namespace Xeen { @@ -31,7 +32,7 @@ namespace Xeen { Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer), _fxOn(true), _musicOn(true), _subtitles(false), _songData(nullptr), _effectsData(nullptr), _musicSide(0), _musicPercent(100), _musicVolume(0), _sfxVolume(0) { - _SoundDriver = new AdlibSoundDriver(); + _SoundDriver = new SoundDriverAdlib(); } Sound::~Sound() { diff --git a/engines/xeen/sound_driver.cpp b/engines/xeen/sound_driver.cpp index 967f53ac49..108eba2e5a 100644 --- a/engines/xeen/sound_driver.cpp +++ b/engines/xeen/sound_driver.cpp @@ -28,10 +28,6 @@ namespace Xeen { -#define CALLBACKS_PER_SECOND 73 - -/*------------------------------------------------------------------------*/ - SoundDriver::SoundDriver() : _musicPlaying(false), _fxPlaying(false), _musCountdownTimer(0), _fxCountdownTimer(0), _musDataPtr(nullptr), _fxDataPtr(nullptr), _fxStartPtr(nullptr), _musStartPtr(nullptr), @@ -259,400 +255,4 @@ const CommandFn SoundDriver::FX_COMMANDS[16] = { &SoundDriver::cmdChangeFrequency, &SoundDriver::fxEndSubroutine }; -/*------------------------------------------------------------------------*/ - -AdlibSoundDriver::AdlibSoundDriver() : _field180(0), _field181(0), _field182(0), - _musicVolume(0), _sfxVolume(0) { - Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr); - Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr); - - _opl = OPL::Config::create(); - _opl->init(); - _opl->start(new Common::Functor0Mem(this, &AdlibSoundDriver::onTimer), CALLBACKS_PER_SECOND); - initialize(); -} - -AdlibSoundDriver::~AdlibSoundDriver() { - _opl->stop(); - delete _opl; -} - -void AdlibSoundDriver::onTimer() { - Common::StackLock slock(_driverMutex); - execute(); - flush(); -} - -void AdlibSoundDriver::initialize() { - write(1, 0x20); - write(8, 0); - write(0xBD, 0); - - resetFrequencies(); - AdlibSoundDriver::resetFX(); -} - -void AdlibSoundDriver::playFX(uint effectId, const byte *data) { - Common::StackLock slock(_driverMutex); - SoundDriver::playFX(effectId, data); -} - -void AdlibSoundDriver::playSong(const byte *data) { - Common::StackLock slock(_driverMutex); - SoundDriver::playSong(data); - _field180 = 0; - resetFrequencies(); -} - -int AdlibSoundDriver::songCommand(uint commandId, byte musicVolume, byte sfxVolume) { - Common::StackLock slock(_driverMutex); - SoundDriver::songCommand(commandId, musicVolume, sfxVolume); - - if (commandId == STOP_SONG) { - _field180 = 0; - resetFrequencies(); - } else if (commandId == RESTART_SONG) { - _field180 = 0; - _musicPlaying = true; - } else if (commandId < 0x100) { - if (_musicPlaying) { - _field180 = commandId; - _field182 = 63; - } - } else if (commandId == SET_VOLUME) { - _musicVolume = musicVolume; - _sfxVolume = sfxVolume; - } else if (commandId == GET_STATUS) { - return _field180; - } - - return 0; -} - -void AdlibSoundDriver::write(int reg, int val) { - _queue.push(RegisterValue(reg, val)); - debugC(9, kDebugSound, "%.2x %.2x", reg, val); -} - -void AdlibSoundDriver::flush() { - Common::StackLock slock(_driverMutex); - - while (!_queue.empty()) { - RegisterValue v = _queue.pop(); - _opl->writeReg(v._regNum, v._value); - } -} - -void AdlibSoundDriver::pausePostProcess() { - if (_field180 && ((_field181 += _field180) < 0)) { - if (--_field182 < 0) { - _musicPlaying = false; - _field180 = 0; - resetFrequencies(); - } else { - for (int channelNum = 6; channelNum >= 0; --channelNum) { - if (_channels[channelNum]._volume < 63) - setOutputLevel(channelNum, ++_channels[channelNum]._volume); - } - } - } - - for (int channelNum = 8; channelNum > (_exclude7 ? 7 : 6); --channelNum) { - Channel &chan = _channels[channelNum]; - if (!chan._changeFrequency || (chan._freqCtr += chan._freqCtrChange) >= 0) - continue; - - uint freq = chan._frequency & 0x3FF; - uint val = chan._frequency >> 8; - byte val1 = val & 0x20; - byte val2 = val & 0x1C; - - freq += chan._freqChange; - if (chan._freqChange < 0) { - if (freq <= 388) { - freq <<= 1; - if (!(freq & 0x3FF)) - --freq; - } - - val2 = (val2 - 4) & 0x1C; - } else { - if (freq >= 734) { - freq >>= 1; - if (!(freq & 0x3FF)) - ++freq; - } - - val2 = (val2 + 4) & 0x1C; - } - - freq &= 0x3FF; - freq |= (val2 << 8); - freq |= val1; - chan._frequency = freq; - setFrequency(channelNum, freq); - } -} - -void AdlibSoundDriver::resetFX() { - if (!_exclude7) { - _channels[7]._frequency = 0; - setFrequency(7, 0); - _channels[7]._volume = 63; - setOutputLevel(7, 63); - } - - _channels[8]._frequency = 0; - setFrequency(8, 0); - _channels[8]._volume = 63; - setOutputLevel(8, 63); -} - -void AdlibSoundDriver::resetFrequencies() { - for (int opNum = 6; opNum >= 0; --opNum) { - _channels[opNum]._frequency = 0; - setFrequency(opNum, 0); - } -} - -void AdlibSoundDriver::setFrequency(byte operatorNum, uint frequency) { - write(0xA0 + operatorNum, frequency & 0xff); - write(0xB0 + operatorNum, (frequency >> 8)); -} - -uint AdlibSoundDriver::calcFrequency(byte note) { - return WAVEFORMS[note & 0x1F] + ((note & 0xE0) << 5); -} - -void AdlibSoundDriver::setOutputLevel(byte channelNum, uint level) { - write(0x40 + OPERATOR2_INDEXES[channelNum], level | - (_channels[channelNum]._scalingValue & 0xC0)); -} - -void AdlibSoundDriver::playInstrument(byte channelNum, const byte *data, byte volume) { - byte op1 = OPERATOR1_INDEXES[channelNum]; - byte op2 = OPERATOR2_INDEXES[channelNum]; - debugC(2, kDebugSound, "---START-playInstrument - %d", channelNum); - write(0x20 + op1, *data++); - write(0x40 + op1, *data++); - write(0x60 + op1, *data++); - write(0x80 + op1, *data++); - write(0xE0 + op1, *data++); - write(0x20 + op2, *data++); - - int scalingVal = *data++; - _channels[channelNum]._scalingValue = scalingVal; - scalingVal += (127 - volume) / 2; - - if (scalingVal > 63) { - scalingVal = 63; - if (_field180) - scalingVal = (scalingVal & 0xC0) | _field182; - } - write(0x40 + op2, scalingVal); - - write(0x60 + op2, *data++); - write(0x80 + op2, *data++); - write(0xE0 + op2, *data++); - write(0xC0 + channelNum, *data++); - - debugC(2, kDebugSound, "---END-playInstrument"); -} - -bool AdlibSoundDriver::musSetInstrument(const byte *&srcP, byte param) { - debugC(3, kDebugSound, "musSetInstrument %d", param); - _musInstrumentPtrs[param] = srcP; - srcP += 26; - - return false; -} - -bool AdlibSoundDriver::musSetPitchWheel(const byte *&srcP, byte param) { - // Adlib does not support this - debugC(3, kDebugSound, "musSetPitchWheel"); - srcP += 2; - return false; -} - -bool AdlibSoundDriver::musSetPanning(const byte *&srcP, byte param) { - // Adlib does not support this - debugC(3, kDebugSound, "musSetPanning"); - ++srcP; - return false; -} - -bool AdlibSoundDriver::musFade(const byte *&srcP, byte param) { - ++srcP; - if (param < 7) - setFrequency(param, _channels[param]._frequency); - debugC(3, kDebugSound, "musFade"); - - return false; -} - -bool AdlibSoundDriver::musStartNote(const byte *&srcP, byte param) { - if (param < 7) { - byte note = *srcP++; - ++srcP; // Second byte is fade, which is unused by Adlib - uint freq = calcFrequency(note); - debugC(3, kDebugSound, "musStartNote %x -> %x", note, freq); - - setFrequency(param, freq); - freq |= 0x2000; - _channels[param]._frequency = freq; - setFrequency(param, freq); - } else { - srcP += 2; - debugC(3, kDebugSound, "musStartNote skipped"); - } - - return false; -} - -bool AdlibSoundDriver::musSetVolume(const byte *&srcP, byte param) { - debugC(3, kDebugSound, "musSetVolume %d", (int)*srcP); - - if (*srcP++ == 5 && !_field180) { - _channels[param]._volume = *srcP; - setOutputLevel(param, *srcP); - } - - ++srcP; - return false; -} - -bool AdlibSoundDriver::musInjectMidi(const byte *&srcP, byte param) { - // Adlib does not support MIDI. So simply keep skipping over bytes - // until an 'F7' byte is found that flags the end of the MIDI data - debugC(3, kDebugSound, "musInjectMidi"); - while (*srcP++ != 0xF7) - ; - - return false; -} - -bool AdlibSoundDriver::musPlayInstrument(const byte *&srcP, byte param) { - byte instrument = *srcP++; - debugC(3, kDebugSound, "musPlayInstrument %d, %d", param, instrument); - - if (param < 7) - playInstrument(param, _musInstrumentPtrs[instrument], _musicVolume); - - return false; -} - -bool AdlibSoundDriver::fxSetInstrument(const byte *&srcP, byte param) { - debugC(3, kDebugSound, "fxSetInstrument %d", param); - _fxInstrumentPtrs[param] = srcP; - srcP += 11; - - return false; -} - -bool AdlibSoundDriver::fxSetVolume(const byte *&srcP, byte param) { - debugC(3, kDebugSound, "fxSetVolume %d", (int)*srcP); - - if (!_field180 && (!_exclude7 || param != 7)) { - _channels[param]._volume = *srcP; - setOutputLevel(param, *srcP); - } - - ++srcP; - return false; -} - -bool AdlibSoundDriver::fxMidiReset(const byte *&srcP, byte param) { - debugC(3, kDebugSound, "fxMidiReset"); - return false; -} - -bool AdlibSoundDriver::fxMidiDword(const byte *&srcP, byte param) { - debugC(3, kDebugSound, "fxMidiDword"); - return false; -} - -bool AdlibSoundDriver::fxSetPanning(const byte *&srcP, byte param) { - byte note = *srcP++; - debugC(3, kDebugSound, "fxSetPanning - %x", note); - - if (!_exclude7 || param != 7) { - uint freq = calcFrequency(note); - setFrequency(param, freq); - _channels[param]._frequency = freq; - } - - return false; -} - -bool AdlibSoundDriver::fxChannelOff(const byte *&srcP, byte param) { - debugC(3, kDebugSound, "fxChannelOff %d", param); - _channels[param]._frequency &= ~0x2000; - write(0xB0 + param, _channels[param]._frequency); - return false; -} - -bool AdlibSoundDriver::fxFade(const byte *&srcP, byte param) { - uint freq = calcFrequency(*srcP++); - debugC(3, kDebugSound, "fxFade %d %x", param, freq); - - if (!_exclude7 || param != 7) { - _channels[param]._frequency = freq; - setFrequency(param, freq); - } - - return false; -} - -bool AdlibSoundDriver::fxStartNote(const byte *&srcP, byte param) { - if (!_exclude7 || param != 7) { - byte note = *srcP++; - uint freq = calcFrequency(note); - debugC(3, kDebugSound, "fxStartNote %x -> %x", note, freq); - - setFrequency(param, freq); - freq |= 0x2000; - _channels[param]._frequency = freq; - setFrequency(param, freq); - } else { - ++srcP; - debugC(3, kDebugSound, "fxStartNote skipped"); - } - - return false; -} - -bool AdlibSoundDriver::fxInjectMidi(const byte *&srcP, byte param) { - // Surpringly, unlike the musInjectMidi, this version doesn't have - // any logic to skip over following MIDI data. Which must mean the opcode - // and/or it's data aren't present in the admus driver file - debugC(3, kDebugSound, "fxInjectMidi"); - return false; -} - -bool AdlibSoundDriver::fxPlayInstrument(const byte *&srcP, byte param) { - byte instrument = *srcP++; - debugC(3, kDebugSound, "fxPlayInstrument %d, %d", param, instrument); - - if (!_exclude7 || param != 7) - playInstrument(param, _fxInstrumentPtrs[instrument], _sfxVolume); - - return false; -} - -/*------------------------------------------------------------------------*/ - -const byte AdlibSoundDriver::OPERATOR1_INDEXES[CHANNEL_COUNT] = { - 0, 1, 2, 8, 9, 0xA, 0x10, 0x11, 0x12 -}; - -const byte AdlibSoundDriver::OPERATOR2_INDEXES[CHANNEL_COUNT] = { - 3, 4, 5, 0xB, 0xC, 0xD, 0x13, 0x14, 0x15 -}; - -const uint AdlibSoundDriver::WAVEFORMS[24] = { - 0, 347, 388, 436, 462, 519, 582, 646, - 0, 362, 406, 455, 484, 542, 607, 680, - 0, 327, 367, 412, 436, 489, 549, 618 -}; - } // End of namespace Xeen diff --git a/engines/xeen/sound_driver.h b/engines/xeen/sound_driver.h index d4edd49c05..bad357b604 100644 --- a/engines/xeen/sound_driver.h +++ b/engines/xeen/sound_driver.h @@ -177,132 +177,6 @@ public: bool isPlaying() const { return _musicPlaying; } }; -class AdlibSoundDriver : public SoundDriver { - struct RegisterValue { - uint8 _regNum; - uint8 _value; - - RegisterValue(int regNum, int value) { - _regNum = regNum; _value = value; - } - }; -private: - static const byte OPERATOR1_INDEXES[CHANNEL_COUNT]; - static const byte OPERATOR2_INDEXES[CHANNEL_COUNT]; - static const uint WAVEFORMS[24]; -private: - OPL::OPL *_opl; - Common::Queue _queue; - Common::Mutex _driverMutex; - const byte *_musInstrumentPtrs[16]; - const byte *_fxInstrumentPtrs[16]; - int _field180; - int _field181; - int _field182; - int _musicVolume, _sfxVolume; -private: - /** - * Initializes the state of the Adlib OPL driver - */ - void initialize(); - - /** - * Adds a register write to the pending queue that will be flushed - * out to the OPL on the next timer call - */ - void write(int reg, int val); - - /** - * Timer function for OPL - */ - void onTimer(); - - /** - * Flushes any pending writes to the OPL - */ - void flush(); - - /** - * Resets all the output frequencies - */ - void resetFrequencies(); - - /** - * Sets the frequency for an operator - */ - void setFrequency(byte operatorNum, uint frequency); - - /** - * Calculates the frequency for a note - */ - uint calcFrequency(byte note); - - /** - * Sets the output level for a channel - */ - void setOutputLevel(byte channelNum, uint level); - - /** - * Starts playing an instrument - */ - void playInstrument(byte channelNum, const byte *data, byte volume); -protected: - virtual bool musSetInstrument(const byte *&srcP, byte param); - virtual bool musSetPitchWheel(const byte *&srcP, byte param); - virtual bool musSetPanning(const byte *&srcP, byte param); - virtual bool musFade(const byte *&srcP, byte param); - virtual bool musStartNote(const byte *&srcP, byte param); - virtual bool musSetVolume(const byte *&srcP, byte param); - virtual bool musInjectMidi(const byte *&srcP, byte param); - virtual bool musPlayInstrument(const byte *&srcP, byte param); - - virtual bool fxSetInstrument(const byte *&srcP, byte param); - virtual bool fxSetVolume(const byte *&srcP, byte param); - virtual bool fxMidiReset(const byte *&srcP, byte param); - virtual bool fxMidiDword(const byte *&srcP, byte param); - virtual bool fxSetPanning(const byte *&srcP, byte param); - virtual bool fxChannelOff(const byte *&srcP, byte param); - virtual bool fxFade(const byte *&srcP, byte param); - virtual bool fxStartNote(const byte *&srcP, byte param); - virtual bool fxInjectMidi(const byte *&srcP, byte param); - virtual bool fxPlayInstrument(const byte *&srcP, byte param); - - /** - * Post-processing done when a pause countdown starts or is in progress - */ - virtual void pausePostProcess(); - - /** - * Does a reset of any sound effect - */ - virtual void resetFX(); -public: - /** - * Constructor - */ - AdlibSoundDriver(); - - /** - * Destructor - */ - virtual ~AdlibSoundDriver(); - - /** - * Starts an special effect playing - */ - virtual void playFX(uint effectId, const byte *data); - - /** - * Plays a song - */ - virtual void playSong(const byte *data); - - /** - * Executes special music command - */ - virtual int songCommand(uint commandId, byte musicVolume = 0, byte sfxVolume = 0); -}; - } // End of namespace Xeen #endif /* XEEN_SOUND_DRIVER_H */ diff --git a/engines/xeen/sound_driver_adlib.cpp b/engines/xeen/sound_driver_adlib.cpp new file mode 100644 index 0000000000..a1a13fae9a --- /dev/null +++ b/engines/xeen/sound_driver_adlib.cpp @@ -0,0 +1,424 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "xeen/sound_driver_adlib.h" +#include "xeen/xeen.h" + +namespace Xeen { + +#define CALLBACKS_PER_SECOND 73 + +const byte SoundDriverAdlib::OPERATOR1_INDEXES[CHANNEL_COUNT] = { + 0, 1, 2, 8, 9, 0xA, 0x10, 0x11, 0x12 +}; + +const byte SoundDriverAdlib::OPERATOR2_INDEXES[CHANNEL_COUNT] = { + 3, 4, 5, 0xB, 0xC, 0xD, 0x13, 0x14, 0x15 +}; + +const uint SoundDriverAdlib::WAVEFORMS[24] = { + 0, 347, 388, 436, 462, 519, 582, 646, + 0, 362, 406, 455, 484, 542, 607, 680, + 0, 327, 367, 412, 436, 489, 549, 618 +}; + +/*------------------------------------------------------------------------*/ + +SoundDriverAdlib::SoundDriverAdlib() : _field180(0), _field181(0), _field182(0), + _musicVolume(0), _sfxVolume(0) { + Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr); + Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr); + + _opl = OPL::Config::create(); + _opl->init(); + _opl->start(new Common::Functor0Mem(this, &SoundDriverAdlib::onTimer), CALLBACKS_PER_SECOND); + initialize(); +} + +SoundDriverAdlib::~SoundDriverAdlib() { + _opl->stop(); + delete _opl; +} + +void SoundDriverAdlib::onTimer() { + Common::StackLock slock(_driverMutex); + execute(); + flush(); +} + +void SoundDriverAdlib::initialize() { + write(1, 0x20); + write(8, 0); + write(0xBD, 0); + + resetFrequencies(); + SoundDriverAdlib::resetFX(); +} + +void SoundDriverAdlib::playFX(uint effectId, const byte *data) { + Common::StackLock slock(_driverMutex); + SoundDriver::playFX(effectId, data); +} + +void SoundDriverAdlib::playSong(const byte *data) { + Common::StackLock slock(_driverMutex); + SoundDriver::playSong(data); + _field180 = 0; + resetFrequencies(); +} + +int SoundDriverAdlib::songCommand(uint commandId, byte musicVolume, byte sfxVolume) { + Common::StackLock slock(_driverMutex); + SoundDriver::songCommand(commandId, musicVolume, sfxVolume); + + if (commandId == STOP_SONG) { + _field180 = 0; + resetFrequencies(); + } else if (commandId == RESTART_SONG) { + _field180 = 0; + _musicPlaying = true; + } else if (commandId < 0x100) { + if (_musicPlaying) { + _field180 = commandId; + _field182 = 63; + } + } else if (commandId == SET_VOLUME) { + _musicVolume = musicVolume; + _sfxVolume = sfxVolume; + } else if (commandId == GET_STATUS) { + return _field180; + } + + return 0; +} + +void SoundDriverAdlib::write(int reg, int val) { + _queue.push(RegisterValue(reg, val)); + debugC(9, kDebugSound, "%.2x %.2x", reg, val); +} + +void SoundDriverAdlib::flush() { + Common::StackLock slock(_driverMutex); + + while (!_queue.empty()) { + RegisterValue v = _queue.pop(); + _opl->writeReg(v._regNum, v._value); + } +} + +void SoundDriverAdlib::pausePostProcess() { + if (_field180 && ((_field181 += _field180) < 0)) { + if (--_field182 < 0) { + _musicPlaying = false; + _field180 = 0; + resetFrequencies(); + } else { + for (int channelNum = 6; channelNum >= 0; --channelNum) { + if (_channels[channelNum]._volume < 63) + setOutputLevel(channelNum, ++_channels[channelNum]._volume); + } + } + } + + for (int channelNum = 8; channelNum > (_exclude7 ? 7 : 6); --channelNum) { + Channel &chan = _channels[channelNum]; + if (!chan._changeFrequency || (chan._freqCtr += chan._freqCtrChange) >= 0) + continue; + + uint freq = chan._frequency & 0x3FF; + uint val = chan._frequency >> 8; + byte val1 = val & 0x20; + byte val2 = val & 0x1C; + + freq += chan._freqChange; + if (chan._freqChange < 0) { + if (freq <= 388) { + freq <<= 1; + if (!(freq & 0x3FF)) + --freq; + } + + val2 = (val2 - 4) & 0x1C; + } else { + if (freq >= 734) { + freq >>= 1; + if (!(freq & 0x3FF)) + ++freq; + } + + val2 = (val2 + 4) & 0x1C; + } + + freq &= 0x3FF; + freq |= (val2 << 8); + freq |= val1; + chan._frequency = freq; + setFrequency(channelNum, freq); + } +} + +void SoundDriverAdlib::resetFX() { + if (!_exclude7) { + _channels[7]._frequency = 0; + setFrequency(7, 0); + _channels[7]._volume = 63; + setOutputLevel(7, 63); + } + + _channels[8]._frequency = 0; + setFrequency(8, 0); + _channels[8]._volume = 63; + setOutputLevel(8, 63); +} + +void SoundDriverAdlib::resetFrequencies() { + for (int opNum = 6; opNum >= 0; --opNum) { + _channels[opNum]._frequency = 0; + setFrequency(opNum, 0); + } +} + +void SoundDriverAdlib::setFrequency(byte operatorNum, uint frequency) { + write(0xA0 + operatorNum, frequency & 0xff); + write(0xB0 + operatorNum, (frequency >> 8)); +} + +uint SoundDriverAdlib::calcFrequency(byte note) { + return WAVEFORMS[note & 0x1F] + ((note & 0xE0) << 5); +} + +void SoundDriverAdlib::setOutputLevel(byte channelNum, uint level) { + write(0x40 + OPERATOR2_INDEXES[channelNum], level | + (_channels[channelNum]._scalingValue & 0xC0)); +} + +void SoundDriverAdlib::playInstrument(byte channelNum, const byte *data, byte volume) { + byte op1 = OPERATOR1_INDEXES[channelNum]; + byte op2 = OPERATOR2_INDEXES[channelNum]; + debugC(2, kDebugSound, "---START-playInstrument - %d", channelNum); + write(0x20 + op1, *data++); + write(0x40 + op1, *data++); + write(0x60 + op1, *data++); + write(0x80 + op1, *data++); + write(0xE0 + op1, *data++); + write(0x20 + op2, *data++); + + int scalingVal = *data++; + _channels[channelNum]._scalingValue = scalingVal; + scalingVal += (127 - volume) / 2; + + if (scalingVal > 63) { + scalingVal = 63; + if (_field180) + scalingVal = (scalingVal & 0xC0) | _field182; + } + write(0x40 + op2, scalingVal); + + write(0x60 + op2, *data++); + write(0x80 + op2, *data++); + write(0xE0 + op2, *data++); + write(0xC0 + channelNum, *data++); + + debugC(2, kDebugSound, "---END-playInstrument"); +} + +bool SoundDriverAdlib::musSetInstrument(const byte *&srcP, byte param) { + debugC(3, kDebugSound, "musSetInstrument %d", param); + _musInstrumentPtrs[param] = srcP; + srcP += 26; + + return false; +} + +bool SoundDriverAdlib::musSetPitchWheel(const byte *&srcP, byte param) { + // Adlib does not support this + debugC(3, kDebugSound, "musSetPitchWheel"); + srcP += 2; + return false; +} + +bool SoundDriverAdlib::musSetPanning(const byte *&srcP, byte param) { + // Adlib does not support this + debugC(3, kDebugSound, "musSetPanning"); + ++srcP; + return false; +} + +bool SoundDriverAdlib::musFade(const byte *&srcP, byte param) { + ++srcP; + if (param < 7) + setFrequency(param, _channels[param]._frequency); + debugC(3, kDebugSound, "musFade"); + + return false; +} + +bool SoundDriverAdlib::musStartNote(const byte *&srcP, byte param) { + if (param < 7) { + byte note = *srcP++; + ++srcP; // Second byte is fade, which is unused by Adlib + uint freq = calcFrequency(note); + debugC(3, kDebugSound, "musStartNote %x -> %x", note, freq); + + setFrequency(param, freq); + freq |= 0x2000; + _channels[param]._frequency = freq; + setFrequency(param, freq); + } else { + srcP += 2; + debugC(3, kDebugSound, "musStartNote skipped"); + } + + return false; +} + +bool SoundDriverAdlib::musSetVolume(const byte *&srcP, byte param) { + debugC(3, kDebugSound, "musSetVolume %d", (int)*srcP); + + if (*srcP++ == 5 && !_field180) { + _channels[param]._volume = *srcP; + setOutputLevel(param, *srcP); + } + + ++srcP; + return false; +} + +bool SoundDriverAdlib::musInjectMidi(const byte *&srcP, byte param) { + // Adlib does not support MIDI. So simply keep skipping over bytes + // until an 'F7' byte is found that flags the end of the MIDI data + debugC(3, kDebugSound, "musInjectMidi"); + while (*srcP++ != 0xF7) + ; + + return false; +} + +bool SoundDriverAdlib::musPlayInstrument(const byte *&srcP, byte param) { + byte instrument = *srcP++; + debugC(3, kDebugSound, "musPlayInstrument %d, %d", param, instrument); + + if (param < 7) + playInstrument(param, _musInstrumentPtrs[instrument], _musicVolume); + + return false; +} + +bool SoundDriverAdlib::fxSetInstrument(const byte *&srcP, byte param) { + debugC(3, kDebugSound, "fxSetInstrument %d", param); + _fxInstrumentPtrs[param] = srcP; + srcP += 11; + + return false; +} + +bool SoundDriverAdlib::fxSetVolume(const byte *&srcP, byte param) { + debugC(3, kDebugSound, "fxSetVolume %d", (int)*srcP); + + if (!_field180 && (!_exclude7 || param != 7)) { + _channels[param]._volume = *srcP; + setOutputLevel(param, *srcP); + } + + ++srcP; + return false; +} + +bool SoundDriverAdlib::fxMidiReset(const byte *&srcP, byte param) { + debugC(3, kDebugSound, "fxMidiReset"); + return false; +} + +bool SoundDriverAdlib::fxMidiDword(const byte *&srcP, byte param) { + debugC(3, kDebugSound, "fxMidiDword"); + return false; +} + +bool SoundDriverAdlib::fxSetPanning(const byte *&srcP, byte param) { + byte note = *srcP++; + debugC(3, kDebugSound, "fxSetPanning - %x", note); + + if (!_exclude7 || param != 7) { + uint freq = calcFrequency(note); + setFrequency(param, freq); + _channels[param]._frequency = freq; + } + + return false; +} + +bool SoundDriverAdlib::fxChannelOff(const byte *&srcP, byte param) { + debugC(3, kDebugSound, "fxChannelOff %d", param); + _channels[param]._frequency &= ~0x2000; + write(0xB0 + param, _channels[param]._frequency); + return false; +} + +bool SoundDriverAdlib::fxFade(const byte *&srcP, byte param) { + uint freq = calcFrequency(*srcP++); + debugC(3, kDebugSound, "fxFade %d %x", param, freq); + + if (!_exclude7 || param != 7) { + _channels[param]._frequency = freq; + setFrequency(param, freq); + } + + return false; +} + +bool SoundDriverAdlib::fxStartNote(const byte *&srcP, byte param) { + if (!_exclude7 || param != 7) { + byte note = *srcP++; + uint freq = calcFrequency(note); + debugC(3, kDebugSound, "fxStartNote %x -> %x", note, freq); + + setFrequency(param, freq); + freq |= 0x2000; + _channels[param]._frequency = freq; + setFrequency(param, freq); + } else { + ++srcP; + debugC(3, kDebugSound, "fxStartNote skipped"); + } + + return false; +} + +bool SoundDriverAdlib::fxInjectMidi(const byte *&srcP, byte param) { + // Surpringly, unlike the musInjectMidi, this version doesn't have + // any logic to skip over following MIDI data. Which must mean the opcode + // and/or it's data aren't present in the admus driver file + debugC(3, kDebugSound, "fxInjectMidi"); + return false; +} + +bool SoundDriverAdlib::fxPlayInstrument(const byte *&srcP, byte param) { + byte instrument = *srcP++; + debugC(3, kDebugSound, "fxPlayInstrument %d, %d", param, instrument); + + if (!_exclude7 || param != 7) + playInstrument(param, _fxInstrumentPtrs[instrument], _sfxVolume); + + return false; +} + +} // End of namespace Xeen diff --git a/engines/xeen/sound_driver_adlib.h b/engines/xeen/sound_driver_adlib.h new file mode 100644 index 0000000000..def25574f6 --- /dev/null +++ b/engines/xeen/sound_driver_adlib.h @@ -0,0 +1,162 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef XEEN_SOUND_DRIVER_ADLIB_H +#define XEEN_SOUND_DRIVER_ADLIB_H + +#include "xeen/sound_driver.h" + +namespace OPL { + class OPL; +} + +namespace Xeen { + +class SoundDriverAdlib : public SoundDriver { + struct RegisterValue { + uint8 _regNum; + uint8 _value; + + RegisterValue(int regNum, int value) { + _regNum = regNum; _value = value; + } + }; +private: + static const byte OPERATOR1_INDEXES[CHANNEL_COUNT]; + static const byte OPERATOR2_INDEXES[CHANNEL_COUNT]; + static const uint WAVEFORMS[24]; +private: + OPL::OPL *_opl; + Common::Queue _queue; + Common::Mutex _driverMutex; + const byte *_musInstrumentPtrs[16]; + const byte *_fxInstrumentPtrs[16]; + int _field180; + int _field181; + int _field182; + int _musicVolume, _sfxVolume; +private: + /** + * Initializes the state of the Adlib OPL driver + */ + void initialize(); + + /** + * Adds a register write to the pending queue that will be flushed + * out to the OPL on the next timer call + */ + void write(int reg, int val); + + /** + * Timer function for OPL + */ + void onTimer(); + + /** + * Flushes any pending writes to the OPL + */ + void flush(); + + /** + * Resets all the output frequencies + */ + void resetFrequencies(); + + /** + * Sets the frequency for an operator + */ + void setFrequency(byte operatorNum, uint frequency); + + /** + * Calculates the frequency for a note + */ + uint calcFrequency(byte note); + + /** + * Sets the output level for a channel + */ + void setOutputLevel(byte channelNum, uint level); + + /** + * Starts playing an instrument + */ + void playInstrument(byte channelNum, const byte *data, byte volume); +protected: + virtual bool musSetInstrument(const byte *&srcP, byte param); + virtual bool musSetPitchWheel(const byte *&srcP, byte param); + virtual bool musSetPanning(const byte *&srcP, byte param); + virtual bool musFade(const byte *&srcP, byte param); + virtual bool musStartNote(const byte *&srcP, byte param); + virtual bool musSetVolume(const byte *&srcP, byte param); + virtual bool musInjectMidi(const byte *&srcP, byte param); + virtual bool musPlayInstrument(const byte *&srcP, byte param); + + virtual bool fxSetInstrument(const byte *&srcP, byte param); + virtual bool fxSetVolume(const byte *&srcP, byte param); + virtual bool fxMidiReset(const byte *&srcP, byte param); + virtual bool fxMidiDword(const byte *&srcP, byte param); + virtual bool fxSetPanning(const byte *&srcP, byte param); + virtual bool fxChannelOff(const byte *&srcP, byte param); + virtual bool fxFade(const byte *&srcP, byte param); + virtual bool fxStartNote(const byte *&srcP, byte param); + virtual bool fxInjectMidi(const byte *&srcP, byte param); + virtual bool fxPlayInstrument(const byte *&srcP, byte param); + + /** + * Post-processing done when a pause countdown starts or is in progress + */ + virtual void pausePostProcess(); + + /** + * Does a reset of any sound effect + */ + virtual void resetFX(); +public: + /** + * Constructor + */ + SoundDriverAdlib(); + + /** + * Destructor + */ + virtual ~SoundDriverAdlib(); + + /** + * Starts an special effect playing + */ + virtual void playFX(uint effectId, const byte *data); + + /** + * Plays a song + */ + virtual void playSong(const byte *data); + + /** + * Executes special music command + */ + virtual int songCommand(uint commandId, byte musicVolume = 0, byte sfxVolume = 0); +}; + +} // End of namespace Xeen + +#endif /* XEEN_SOUND_DRIVER_H */ -- cgit v1.2.3