aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2016-09-14 22:17:38 -0400
committerPaul Gilbert2016-09-14 22:17:38 -0400
commita06d1b7efe0066d17fdc7d30e9c8983fcd3f8094 (patch)
tree091a27894994550f1427a603485e65ae5a1bff53
parentb93b8f8245400c937f0d571f9a076d552b6fd0ce (diff)
downloadscummvm-rg350-a06d1b7efe0066d17fdc7d30e9c8983fcd3f8094.tar.gz
scummvm-rg350-a06d1b7efe0066d17fdc7d30e9c8983fcd3f8094.tar.bz2
scummvm-rg350-a06d1b7efe0066d17fdc7d30e9c8983fcd3f8094.zip
XEEN: Implemented frequency shifting in music driver
-rw-r--r--engines/xeen/music.cpp137
-rw-r--r--engines/xeen/music.h44
2 files changed, 128 insertions, 53 deletions
diff --git a/engines/xeen/music.cpp b/engines/xeen/music.cpp
index 932cca1ccb..415b413ff8 100644
--- a/engines/xeen/music.cpp
+++ b/engines/xeen/music.cpp
@@ -34,10 +34,7 @@ namespace Xeen {
MusicDriver::MusicDriver() : _fieldF(false), _field1E(false), _lowMusicIgnored(false),
_musCountdownTimer(0), _fxCountdownTimer(0), _musDataPtr(nullptr),
_fxDataPtr(nullptr), _fxStartPtr(nullptr), _musStartPtr(nullptr) {
- Common::fill(&_flags[0], &_flags[CHANNEL_COUNT], false);
- Common::fill(&_field15C[0], &_field15C[CHANNEL_COUNT], 0);
- Common::fill(&_field165[0], &_field165[CHANNEL_COUNT], 0);
- Common::fill(&_field177[0], &_field177[CHANNEL_COUNT], 0);
+ _channels.resize(CHANNEL_COUNT);
}
void MusicDriver::execute() {
@@ -57,7 +54,8 @@ void MusicDriver::execute() {
}
if (flag) {
- postProcess();
+ // Do paused handling and exit immediately
+ pausePostProcess();
return;
}
@@ -92,8 +90,8 @@ bool MusicDriver::musSetCountdown(const byte *&srcP, byte param) {
_musCountdownTimer = param;
_musDataPtr = srcP;
- // Do post-processing and stop processing
- postProcess();
+ // Do paused handling and break out of processing loop
+ pausePostProcess();
return true;
}
@@ -107,17 +105,17 @@ bool MusicDriver::musSkipWord(const byte *&srcP, byte param) {
}
-bool MusicDriver::cmdClearFlag(const byte *&srcP, byte param) {
- _flags[param] = false;
+bool MusicDriver::cmdFreezeFrequency(const byte *&srcP, byte param) {
+ _channels[param]._changeFrequency = false;
return false;
}
-bool MusicDriver::cmdWibbly(const byte *&srcP, byte param) {
+bool MusicDriver::cmdChangeFrequency(const byte *&srcP, byte param) {
if (param != 7 || !_fieldF) {
- _field15C[param] = *srcP++;
- _field177[param] = 0xFF;
- _flags[param] = true;
- _field165[param] = READ_BE_UINT16(srcP);
+ _channels[param]._freqCtrChange = (int8)*srcP++;
+ _channels[param]._freqCtr = 0xFF;
+ _channels[param]._changeFrequency = true;
+ _channels[param]._freqChange = (int16)READ_BE_UINT16(srcP);
srcP += 2;
} else {
srcP += 3;
@@ -154,8 +152,8 @@ bool MusicDriver::fxSetCountdown(const byte *&srcP, byte param) {
_fxCountdownTimer = param;
_musDataPtr = srcP;
- // Do post-processing and stop processing
- postProcess();
+ // Do paused handling and break out of processing loop
+ pausePostProcess();
return true;
}
@@ -174,7 +172,7 @@ void MusicDriver::playFX(uint effectId, const byte *data) {
_musStartPtr = nullptr;
_fxDataPtr = _fxStartPtr = data;
_fxCountdownTimer = 0;
- _flags[7] = _flags[8] = 0;
+ _channels[7]._changeFrequency = _channels[8]._changeFrequency = false;
resetFX();
_lowMusicIgnored = true;
}
@@ -188,8 +186,8 @@ const CommandFn MusicDriver::MUSIC_COMMANDS[16] = {
&MusicDriver::musSetPanning, &MusicDriver::cmdNoOperation,
&MusicDriver::musFade, &MusicDriver::musStartNote,
&MusicDriver::musSetVolume, &MusicDriver::musInjectMidi,
- &MusicDriver::musPlayInstrument, &MusicDriver::cmdClearFlag,
- &MusicDriver::cmdWibbly, &MusicDriver::musEndSubroutine
+ &MusicDriver::musPlayInstrument, &MusicDriver::cmdFreezeFrequency,
+ &MusicDriver::cmdChangeFrequency, &MusicDriver::musEndSubroutine
};
const CommandFn MusicDriver::FX_COMMANDS[16] = {
@@ -199,18 +197,16 @@ const CommandFn MusicDriver::FX_COMMANDS[16] = {
&MusicDriver::fxSetPanning, &MusicDriver::fxChannelOff,
&MusicDriver::fxFade, &MusicDriver::fxStartNote,
&MusicDriver::cmdNoOperation, &MusicDriver::fxInjectMidi,
- &MusicDriver::fxPlayInstrument, &MusicDriver::cmdClearFlag,
- &MusicDriver::cmdWibbly, &MusicDriver::fxEndSubroutine
+ &MusicDriver::fxPlayInstrument, &MusicDriver::cmdFreezeFrequency,
+ &MusicDriver::cmdChangeFrequency, &MusicDriver::fxEndSubroutine
};
/*------------------------------------------------------------------------*/
-AdlibMusicDriver::AdlibMusicDriver() : _field180(0), _field182(0), _volume(127) {
+AdlibMusicDriver::AdlibMusicDriver() : _field180(0), _field181(0), _field182(0),
+ _volume(127) {
Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr);
Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr);
- Common::fill(&_frequencies[0], &_frequencies[7], 0);
- Common::fill(&_volumes[0], &_volumes[CHANNEL_COUNT], 0);
- Common::fill(&_scalingValues[0], &_scalingValues[CHANNEL_COUNT], 0);
_opl = OPL::Config::create();
_opl->init();
@@ -238,6 +234,11 @@ void AdlibMusicDriver::initialize() {
AdlibMusicDriver::resetFX();
}
+void AdlibMusicDriver::playFX(uint effectId, const byte *data) {
+ Common::StackLock slock(_driverMutex);
+ MusicDriver::playFX(effectId, data);
+}
+
void AdlibMusicDriver::write(int reg, int val) {
_queue.push(RegisterValue(reg, val));
}
@@ -251,23 +252,74 @@ void AdlibMusicDriver::flush() {
}
}
+void AdlibMusicDriver::pausePostProcess() {
+ if (_field180 && ((_field181 += _field180) < 0)) {
+ if (--_field182 < 0) {
+ _field1E = 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 != 6 || (channelNum == 7 && _fieldF); --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 AdlibMusicDriver::resetFX() {
if (!_fieldF) {
- _frequencies[7] = 0;
+ _channels[7]._frequency = 0;
setFrequency(7, 0);
- _volumes[7] = 63;
+ _channels[7]._volume = 63;
setOutputLevel(7, 63);
}
- _frequencies[8] = 0;
+ _channels[8]._frequency = 0;
setFrequency(8, 0);
- _volumes[8] = 63;
+ _channels[8]._volume = 63;
setOutputLevel(8, 63);
}
void AdlibMusicDriver::resetFrequencies() {
for (int opNum = 6; opNum >= 0; --opNum) {
- _frequencies[opNum] = 0;
+ _channels[opNum]._frequency = 0;
setFrequency(opNum, 0);
}
}
@@ -283,7 +335,7 @@ uint AdlibMusicDriver::calcFrequency(byte note) {
void AdlibMusicDriver::setOutputLevel(byte channelNum, uint level) {
write(0x40 + OPERATOR2_INDEXES[channelNum], level |
- (_scalingValues[channelNum] & 0xC0));
+ (_channels[channelNum]._scalingValue & 0xC0));
}
void AdlibMusicDriver::playInstrument(byte channelNum, const byte *data) {
@@ -298,7 +350,7 @@ void AdlibMusicDriver::playInstrument(byte channelNum, const byte *data) {
write(0x20 + op2, *data++);
int scalingVal = *data++;
- _scalingValues[channelNum] = scalingVal;
+ _channels[channelNum]._scalingValue = scalingVal;
scalingVal += (127 - _volume) / 2;
if (scalingVal > 63) {
@@ -336,7 +388,7 @@ bool AdlibMusicDriver::musSetPanning(const byte *&srcP, byte param) {
bool AdlibMusicDriver::musFade(const byte *&srcP, byte param) {
++srcP;
if (param < 7)
- setFrequency(param, _frequencies[param]);
+ setFrequency(param, _channels[param]._frequency);
return false;
}
@@ -347,7 +399,7 @@ bool AdlibMusicDriver::musStartNote(const byte *&srcP, byte param) {
++srcP; // Second byte is fade, which is unused by Adlib
uint freq = calcFrequency(note);
setFrequency(param, freq);
- _frequencies[param] = freq | 0x2000;
+ _channels[param]._frequency = freq | 0x2000;
setFrequency(param, freq);
} else {
srcP += 2;
@@ -358,7 +410,7 @@ bool AdlibMusicDriver::musStartNote(const byte *&srcP, byte param) {
bool AdlibMusicDriver::musSetVolume(const byte *&srcP, byte param) {
if (*srcP++ == 2 && !_field180) {
- _volumes[param] = *srcP;
+ _channels[param]._volume = *srcP;
setOutputLevel(param, *srcP);
}
@@ -391,7 +443,7 @@ bool AdlibMusicDriver::fxSetInstrument(const byte *&srcP, byte param) {
bool AdlibMusicDriver::fxSetVolume(const byte *&srcP, byte param) {
if (!_field180 && (!_fieldF || param != 7)) {
- _volumes[param] = *srcP;
+ _channels[param]._volume = *srcP;
setOutputLevel(param, *srcP);
}
@@ -412,22 +464,22 @@ bool AdlibMusicDriver::fxSetPanning(const byte *&srcP, byte param) {
if (!_fieldF || param != 7) {
uint freq = calcFrequency(note);
setFrequency(param, freq);
- _frequencies[param] = freq;
+ _channels[param]._frequency = freq;
}
return false;
}
bool AdlibMusicDriver::fxChannelOff(const byte *&srcP, byte param) {
- _frequencies[param] &= ~0x2000;
- write(0xB0 + param, _frequencies[param]);
+ _channels[param]._frequency &= ~0x2000;
+ write(0xB0 + param, _channels[param]._frequency);
return false;
}
bool AdlibMusicDriver::fxFade(const byte *&srcP, byte param) {
uint freq = calcFrequency(*srcP++);
if (!_fieldF || param != 7) {
- _frequencies[param] = freq;
+ _channels[param]._frequency = freq;
setFrequency(param, freq);
}
@@ -439,7 +491,7 @@ bool AdlibMusicDriver::fxStartNote(const byte *&srcP, byte param) {
byte note = *srcP++;
uint freq = calcFrequency(note);
setFrequency(param, freq);
- _frequencies[param] = freq | 0x2000;
+ _channels[param]._frequency = freq | 0x2000;
setFrequency(param, freq);
} else {
++srcP;
@@ -462,6 +514,7 @@ bool AdlibMusicDriver::fxPlayInstrument(const byte *&srcP, byte param) {
return false;
}
+
const byte AdlibMusicDriver::OPERATOR1_INDEXES[CHANNEL_COUNT] = {
0, 1, 2, 8, 9, 0xA, 0x10, 0x11, 0x12
};
@@ -480,10 +533,12 @@ const uint AdlibMusicDriver::WAVEFORMS[24] = {
Music::Music(Audio::Mixer *mixer) : _mixer(mixer), _musicDriver(nullptr) {
_mixer = mixer;
+ _musicDriver = new AdlibMusicDriver();
loadEffectsData();
}
Music::~Music() {
+ delete _musicDriver;
delete[] _effectsData;
}
diff --git a/engines/xeen/music.h b/engines/xeen/music.h
index 0d5188e697..e6cfbbade5 100644
--- a/engines/xeen/music.h
+++ b/engines/xeen/music.h
@@ -46,6 +46,7 @@ typedef bool (MusicDriver::*CommandFn)(const byte *&srcP, byte param);
* Base class for music drivers
*/
class MusicDriver {
+protected:
struct Subroutine {
const byte *_returnP;
const byte *_jumpP;
@@ -53,22 +54,28 @@ class MusicDriver {
Subroutine(const byte *returnP, const byte *endP) :
_returnP(returnP), _jumpP(endP) {}
};
+ struct Channel {
+ bool _changeFrequency;
+ int _freqCtrChange;
+ int _freqChange;
+ int _freqCtr;
+ byte _volume;
+ byte _scalingValue;
+ uint _frequency;
+ Channel() : _changeFrequency(false), _freqCtr(0), _freqCtrChange(0),
+ _freqChange(0), _volume(0), _scalingValue(0), _frequency(0) {}
+ };
private:
static const CommandFn FX_COMMANDS[16];
static const CommandFn MUSIC_COMMANDS[16];
private:
Common::Stack<Subroutine> _musSubroutines, _fxSubroutines;
- bool _field1E;
int _musCountdownTimer;
int _fxCountdownTimer;
bool _lowMusicIgnored;
const byte *_fxDataPtr, *_musDataPtr;
const byte *_fxStartPtr;
const byte *_musStartPtr;
- bool _flags[CHANNEL_COUNT];
- byte _field15C[CHANNEL_COUNT];
- byte _field165[CHANNEL_COUNT];
- byte _field177[CHANNEL_COUNT];
private:
/**
* Executes the next command
@@ -77,7 +84,9 @@ private:
*/
bool command(const byte *&srcP);
protected:
+ Common::Array<Channel> _channels;
bool _fieldF;
+ bool _field1E;
protected:
/**
* Executes a series of commands until instructed to stop
@@ -97,8 +106,8 @@ protected:
virtual bool musSetVolume(const byte *&srcP, byte param) = 0;
virtual bool musInjectMidi(const byte *&srcP, byte param) = 0;
virtual bool musPlayInstrument(const byte *&srcP, byte param) = 0;
- virtual bool cmdClearFlag(const byte *&srcP, byte param);
- virtual bool cmdWibbly(const byte *&srcP, byte param);
+ virtual bool cmdFreezeFrequency(const byte *&srcP, byte param);
+ virtual bool cmdChangeFrequency(const byte *&srcP, byte param);
virtual bool musEndSubroutine(const byte *&srcP, byte param);
// FX commands
@@ -116,7 +125,10 @@ protected:
virtual bool fxPlayInstrument(const byte *&srcP, byte param) = 0;
virtual bool fxEndSubroutine(const byte *&srcP, byte param);
- virtual void postProcess() = 0;
+ /**
+ * Post-processing done when a pause countdown starts or is in progress
+ */
+ virtual void pausePostProcess() = 0;
/**
* Does a reset of any sound effect
@@ -136,7 +148,7 @@ public:
/**
* Starts an special effect playing
*/
- void playFX(uint effectId, const byte *data);
+ virtual void playFX(uint effectId, const byte *data);
};
class AdlibMusicDriver : public MusicDriver {
@@ -156,12 +168,10 @@ private:
OPL::OPL *_opl;
Common::Queue<RegisterValue> _queue;
Common::Mutex _driverMutex;
- byte _volumes[CHANNEL_COUNT];
- byte _scalingValues[CHANNEL_COUNT];
const byte *_musInstrumentPtrs[16];
const byte *_fxInstrumentPtrs[16];
- uint _frequencies[7];
int _field180;
+ int _field181;
int _field182;
int _volume;
private:
@@ -232,6 +242,11 @@ protected:
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();
@@ -245,6 +260,11 @@ public:
* Destructor
*/
virtual ~AdlibMusicDriver();
+
+ /**
+ * Starts an special effect playing
+ */
+ virtual void playFX(uint effectId, const byte *data);
};