aboutsummaryrefslogtreecommitdiff
path: root/engines/xeen
diff options
context:
space:
mode:
authorPaul Gilbert2018-06-30 10:20:15 -0700
committerPaul Gilbert2018-06-30 10:20:15 -0700
commiteb829a33c91496c516cb7245286c8e1029fd145a (patch)
tree9619480a6b875391269dc3c9e241c27045cc9d62 /engines/xeen
parente26259d758f3bc948fa6b2571d50a5944d94e9f0 (diff)
downloadscummvm-rg350-eb829a33c91496c516cb7245286c8e1029fd145a.tar.gz
scummvm-rg350-eb829a33c91496c516cb7245286c8e1029fd145a.tar.bz2
scummvm-rg350-eb829a33c91496c516cb7245286c8e1029fd145a.zip
XEEN: Split the Adlib sound driver into it's own file
Diffstat (limited to 'engines/xeen')
-rw-r--r--engines/xeen/module.mk1
-rw-r--r--engines/xeen/sound.cpp3
-rw-r--r--engines/xeen/sound_driver.cpp400
-rw-r--r--engines/xeen/sound_driver.h126
-rw-r--r--engines/xeen/sound_driver_adlib.cpp424
-rw-r--r--engines/xeen/sound_driver_adlib.h162
6 files changed, 589 insertions, 527 deletions
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<void, AdlibSoundDriver>(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<RegisterValue> _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<void, SoundDriverAdlib>(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<RegisterValue> _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 */