[Scummvm-git-logs] scummvm master -> b93b8f8245400c937f0d571f9a076d552b6fd0ce

dreammaster dreammaster at scummvm.org
Thu Sep 15 02:53:47 CEST 2016


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
b93b8f8245 XEEN: Fleshing out music player


Commit: b93b8f8245400c937f0d571f9a076d552b6fd0ce
    https://github.com/scummvm/scummvm/commit/b93b8f8245400c937f0d571f9a076d552b6fd0ce
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2016-09-14T20:53:35-04:00

Commit Message:
XEEN: Fleshing out music player

Changed paths:
    engines/xeen/music.cpp
    engines/xeen/music.h



diff --git a/engines/xeen/music.cpp b/engines/xeen/music.cpp
index 72ff386..932cca1 100644
--- a/engines/xeen/music.cpp
+++ b/engines/xeen/music.cpp
@@ -27,277 +27,492 @@
 
 namespace Xeen {
 
-#define CALLBACKS_PER_SECOND 72
+#define CALLBACKS_PER_SECOND 73
 
-Music::Music(Audio::Mixer *mixer) : _mixer(mixer), _effectsData(nullptr),
-		_musicPtr1(nullptr), _musicPtr2(nullptr), _dataPtr(nullptr),
-		_lowMusicIgnored(false),
-		_fieldF(false), _field1C(false), _field1E(false), _field109(0),
-		_field10B(0), _field114(0), _field115(0), _field116(0), _field117(0) {
-	_channels.resize(ADLIB_CHANNEL_COUNT);
-	Common::fill(&_fieldFB[0], &_fieldFB[7], 0);
-	Common::fill(&_field10D[0], &_field10D[7], 0);
+/*------------------------------------------------------------------------*/
 
-	_mixer = mixer;
-	_opl = OPL::Config::create();
-	_opl->init();
-	_opl->start(new Common::Functor0Mem<void, Music>(this, &Music::onTimer), CALLBACKS_PER_SECOND);
-	initialize();
-
-	loadEffectsData();
+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);
 }
 
-Music::~Music() {
-	_opl->stop();
-	delete _opl;
-	delete[] _effectsData;
+void MusicDriver::execute() {
+	bool isFX = false;
+	bool flag = !_field1E;
+	const byte *srcP = _musDataPtr;
+
+	if (!flag) {
+		if (_musCountdownTimer && --_musCountdownTimer == 0)
+			flag = true;
+	}
+	if (flag && _lowMusicIgnored) {
+		srcP = _fxDataPtr;
+		isFX = true;
+		if (!_fxCountdownTimer || --_fxCountdownTimer == 0)
+			flag = false;
+	}
+
+	if (flag) {
+		postProcess();
+		return;
+	}
+
+	// Main loop
+	bool breakFlag = false;
+	while (!breakFlag) {
+		byte nextByte = *srcP++;
+		int cmd = (nextByte >> 4) & 15;
+		int param = (nextByte & 15);
+
+		CommandFn fn = isFX ? FX_COMMANDS[cmd] : MUSIC_COMMANDS[cmd];
+		breakFlag = (this->*fn)(srcP, param);
+	}
 }
 
-void Music::initialize() {
-	write(1, 0x20);
-	write(8, 0);
-	write(0xBD, 0);
 
-	resetFrequencies();
-	reset();
+bool MusicDriver::musCallSubroutine(const byte *&srcP, byte param) {
+	if (_musSubroutines.size() < 16) {
+		const byte *returnP = srcP + 2;
+		srcP = _musStartPtr + READ_LE_UINT16(srcP);
+
+		_musSubroutines.push(Subroutine(returnP, srcP));
+	}
+
+	return false;
 }
 
-void Music::loadEffectsData() {
-	File file("admus");
-	Common::String md5str = Common::computeStreamMD5AsString(file, 8192);
-	
-	if (md5str != "be8989a5e868913f0e53963046e3ea13")
-		error("Unknown music driver encountered");
+bool MusicDriver::musSetCountdown(const byte *&srcP, byte param) {
+	// Set the countdown timer
+	if (!param)
+		param = *++srcP;
+	_musCountdownTimer = param;
+	_musDataPtr = srcP;
 
-	// Load in the driver data
-	const int EFFECTS_OFFSET = 0x91D;
-	byte *effectsData = new byte[file.size() - EFFECTS_OFFSET];
-	file.seek(EFFECTS_OFFSET);
-	file.read(effectsData, file.size() - EFFECTS_OFFSET);
-	file.close();
-	_effectsData = effectsData;
+	// Do post-processing and stop processing
+	postProcess();
+	return true;
+}
 
-	// Extract the effects offsets
-	_effectsOffsets.resize(180);
-	for (int idx = 0; idx < 180; ++idx)
-		_effectsOffsets[idx] = READ_LE_UINT16(&effectsData[idx * 2]) - EFFECTS_OFFSET;
+bool MusicDriver::cmdNoOperation(const byte *&srcP, byte param) {
+	return false;
 }
 
-void Music::onTimer() {
-	Common::StackLock slock(_driverMutex);
-	update();
-	flush();
+bool MusicDriver::musSkipWord(const byte *&srcP, byte param) {
+	srcP += 2;
+	return false;
 }
 
-void Music::write(int reg, int val) {
-	_queue.push(RegisterValue(reg, val));
+
+bool MusicDriver::cmdClearFlag(const byte *&srcP, byte param) {
+	_flags[param] = false;
+	return false;
 }
 
-void Music::flush() {
-	Common::StackLock slock(_driverMutex);
+bool MusicDriver::cmdWibbly(const byte *&srcP, byte param) {
+	if (param != 7 || !_fieldF) {
+		_field15C[param] = *srcP++;
+		_field177[param] = 0xFF;
+		_flags[param] = true;
+		_field165[param] = READ_BE_UINT16(srcP);
+		srcP += 2;
+	} else {
+		srcP += 3;
+	}
 
-	while (!_queue.empty()) {
-		RegisterValue v = _queue.pop();
-		_opl->writeReg(v._regNum, v._value);
+	return true;
+}
+
+bool MusicDriver::musEndSubroutine(const byte *&srcP, byte param) {
+	if (param != 15) {
+		_field1E = 0;
+		return true;
 	}
+
+	srcP = _musSubroutines.empty() ? _musStartPtr : _musSubroutines.pop()._returnP;
+	return false;
 }
 
-void Music::update() {
-	const byte *srcP = _dataPtr;
+bool MusicDriver::fxCallSubroutine(const byte *&srcP, byte param) {
+	if (_fxSubroutines.size() < 16) {
+		const byte *startP = srcP + 2;
+		srcP = _musStartPtr + READ_LE_UINT16(srcP);
 
-	bool flag = !_field1E;
-	if (!flag) {
-		_field1C = 0;
-		if (_field116 && --_field116 == 0)
-			flag = true;
-	}
-	if (flag && _lowMusicIgnored) {
-		srcP = _musicPtr1;
-		_field1C = 1;
-		if (!_field117 || --_field117 == 0)
-			flag = false;
+		_fxSubroutines.push(Subroutine(startP, srcP));
 	}
 
-	if (flag) {
-		postProcess();
-		return;
-	}
+	return false;
+}
 
-	// Main loop
-	bool breakFlag = false;
-	while (!breakFlag) {
-		byte nextByte = *srcP++;
-		int cmd = (nextByte >> 3) & 15;
+bool MusicDriver::fxSetCountdown(const byte *&srcP, byte param) {
+	// Set the countdown timer
+	if (!param)
+		param = *++srcP;
+	_fxCountdownTimer = param;
+	_musDataPtr = srcP;
+
+	// Do post-processing and stop processing
+	postProcess();
+	return true;
+}
 
-		CommandFn fn = (_field1C == 1) ? COMMAND_TABLE2[cmd] : COMMAND_TABLE1[cmd];
-		breakFlag = (this->*fn)(srcP, nextByte);
+bool MusicDriver::fxEndSubroutine(const byte *&srcP, byte param) {
+	if (param != 15) {
+		_lowMusicIgnored = false;
+		return true;
 	}
+
+	srcP = _fxSubroutines.empty() ? _fxStartPtr : _fxSubroutines.pop()._returnP;
+	return false;
 }
 
-void Music::playEffect(uint effectId) {
+void MusicDriver::playFX(uint effectId, const byte *data) {
 	if (!_lowMusicIgnored || effectId < 7 || effectId >= 11) {
-		if (effectId < _effectsOffsets.size()) {
-			_musicPtr1 = _musicPtr2 = &_effectsData[_effectsOffsets[effectId]];
-			_field117 = 0;
-			_field115 = 0;
-			_field114 = 0;
-			reset();
-			_lowMusicIgnored = true;
-		}
+		_musStartPtr = nullptr;
+		_fxDataPtr = _fxStartPtr = data;
+		_fxCountdownTimer = 0;
+		_flags[7] = _flags[8] = 0;
+		resetFX();
+		_lowMusicIgnored = true;
+	}
+}
+
+
+const CommandFn MusicDriver::MUSIC_COMMANDS[16] = {
+	&MusicDriver::musCallSubroutine,		&MusicDriver::musSetCountdown,
+	&MusicDriver::musSetInstrument,		&MusicDriver::cmdNoOperation,
+	&MusicDriver::musSetPitchWheel,		&MusicDriver::musSkipWord,
+	&MusicDriver::musSetPanning,		&MusicDriver::cmdNoOperation,
+	&MusicDriver::musFade,				&MusicDriver::musStartNote,
+	&MusicDriver::musSetVolume,			&MusicDriver::musInjectMidi,
+	&MusicDriver::musPlayInstrument,	&MusicDriver::cmdClearFlag,
+	&MusicDriver::cmdWibbly,			&MusicDriver::musEndSubroutine
+};
+
+const CommandFn MusicDriver::FX_COMMANDS[16] = {
+	&MusicDriver::fxCallSubroutine,	&MusicDriver::fxSetCountdown,
+	&MusicDriver::fxSetInstrument,	&MusicDriver::fxSetVolume,
+	&MusicDriver::fxMidiReset,		&MusicDriver::fxMidiDword,
+	&MusicDriver::fxSetPanning,		&MusicDriver::fxChannelOff,
+	&MusicDriver::fxFade,			&MusicDriver::fxStartNote,
+	&MusicDriver::cmdNoOperation,	&MusicDriver::fxInjectMidi,
+	&MusicDriver::fxPlayInstrument,	&MusicDriver::cmdClearFlag,
+	&MusicDriver::cmdWibbly,		&MusicDriver::fxEndSubroutine
+};
+
+/*------------------------------------------------------------------------*/
+
+AdlibMusicDriver::AdlibMusicDriver() : _field180(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();
+	_opl->start(new Common::Functor0Mem<void, AdlibMusicDriver>(this, &AdlibMusicDriver::onTimer), CALLBACKS_PER_SECOND);
+	initialize();
+}
+
+AdlibMusicDriver::~AdlibMusicDriver() {
+	_opl->stop();
+	delete _opl;
+}
+
+void AdlibMusicDriver::onTimer() {
+	Common::StackLock slock(_driverMutex);
+	execute();
+	flush();
+}
+
+void AdlibMusicDriver::initialize() {
+	write(1, 0x20);
+	write(8, 0);
+	write(0xBD, 0);
+
+	resetFrequencies();
+	AdlibMusicDriver::resetFX();
+}
+
+void AdlibMusicDriver::write(int reg, int val) {
+	_queue.push(RegisterValue(reg, val));
+}
+
+void AdlibMusicDriver::flush() {
+	Common::StackLock slock(_driverMutex);
+
+	while (!_queue.empty()) {
+		RegisterValue v = _queue.pop();
+		_opl->writeReg(v._regNum, v._value);
 	}
 }
 
-void Music::reset() {
+void AdlibMusicDriver::resetFX() {
 	if (!_fieldF) {
-		_field109 = 0;
+		_frequencies[7] = 0;
 		setFrequency(7, 0);
-		_channels[7]._outputLevel = 63;
+		_volumes[7] = 63;
 		setOutputLevel(7, 63);
 	}
 
-	_field10B = 0;
+	_frequencies[8] = 0;
 	setFrequency(8, 0);
-	_channels[8]._outputLevel = 63;
+	_volumes[8] = 63;
 	setOutputLevel(8, 63);
 }
 
-void Music::resetFrequencies() {
+void AdlibMusicDriver::resetFrequencies() {
 	for (int opNum = 6; opNum >= 0; --opNum) {
-		_fieldFB[opNum] = 0;
+		_frequencies[opNum] = 0;
 		setFrequency(opNum, 0);
 	}
 }
 
-void Music::setFrequency(byte operatorNum, uint frequency) {
+void AdlibMusicDriver::setFrequency(byte operatorNum, uint frequency) {
 	write(0xA0 + operatorNum, frequency & 0xff);
 	write(0xB0 + operatorNum, (frequency >> 8));
 }
 
-void Music::postProcess() {
-	// TODO
+uint AdlibMusicDriver::calcFrequency(byte note) {
+	return WAVEFORMS[note & 0x1F] + ((note & 0xE0) << 5);
 }
 
-bool Music::cmd1(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+void AdlibMusicDriver::setOutputLevel(byte channelNum, uint level) {
+	write(0x40 + OPERATOR2_INDEXES[channelNum], level |
+		(_scalingValues[channelNum] & 0xC0));
 }
 
-bool Music::cmd2(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
-}
+void AdlibMusicDriver::playInstrument(byte channelNum, const byte *data) {
+	byte op1 = OPERATOR1_INDEXES[channelNum];
+	byte op2 = OPERATOR2_INDEXES[channelNum];
 
-bool Music::cmd3(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
-}
+	write(0x20 + op1, *data++);
+	write(0x40 + op1, *data++);
+	write(0x60 + op1, *data++);
+	write(0x80 + op1, *data++);
+	write(0xE0 + op1, *data++);
+	write(0x20 + op2, *data++);
 
-bool Music::cmd4(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
-}
+	int scalingVal = *data++;
+	_scalingValues[channelNum] = scalingVal;
+	scalingVal += (127 - _volume) / 2;
 
-bool Music::cmd5(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
-}
+	if (scalingVal > 63) {
+		scalingVal = 63;
+		if (_field180)
+			scalingVal = (scalingVal & 0xC0) | _field182;
+	}
+	write(0x40 + op2, scalingVal);
 
-bool Music::cmd6(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+	write(0x60 + op2, *data++);
+	write(0x80 + op2, *data++);
+	write(0xE0 + op2, *data++);
+	write(0xC0 + op2, *data++);
 }
 
-bool Music::cmd7(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
-}
+bool AdlibMusicDriver::musSetInstrument(const byte *&srcP, byte param) {
+	_musInstrumentPtrs[param] = srcP;
+	srcP += 26;
 
-bool Music::cmd8(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+	return false;
 }
 
-bool Music::cmd9(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::musSetPitchWheel(const byte *&srcP, byte param) {
+	// Adlib does not support this
+	srcP += 2;
+	return false;
 }
 
-bool Music::cmd10(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::musSetPanning(const byte *&srcP, byte param) {
+	// Adlib does not support this
+	++srcP;
+	return false;
 }
 
-bool Music::cmd11(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
-}
+bool AdlibMusicDriver::musFade(const byte *&srcP, byte param) {
+	++srcP;
+	if (param < 7)
+		setFrequency(param, _frequencies[param]);
 
-bool Music::cmd12(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+	return false;
 }
 
-bool Music::cmd13(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::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);
+		setFrequency(param, freq);
+		_frequencies[param] = freq | 0x2000;
+		setFrequency(param, freq);
+	} else {
+		srcP += 2;
+	}
+
+	return false;
 }
 
-bool Music::cmd14(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::musSetVolume(const byte *&srcP, byte param) {
+	if (*srcP++ == 2 && !_field180) {
+		_volumes[param] = *srcP;
+		setOutputLevel(param, *srcP);
+	}
+
+	++srcP;
+	return false;
 }
 
-bool Music::cmd15(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::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
+	while (*srcP++ != 0xF7)
+		;
+
+	return false;
 }
 
-bool Music::cmd16(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::musPlayInstrument(const byte *&srcP, byte param) {
+	if (param < 7)
+		playInstrument(param, _musInstrumentPtrs[param]);
+
+	return false;
 }
 
-bool Music::cmd17(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxSetInstrument(const byte *&srcP, byte param) {
+	_fxInstrumentPtrs[param] = srcP;
+	srcP += 11;
+
+	return false;
 }
 
-bool Music::cmd18(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxSetVolume(const byte *&srcP, byte param) {
+	if (!_field180 && (!_fieldF || param != 7)) {
+		_volumes[param] = *srcP;
+		setOutputLevel(param, *srcP);
+	}
+
+	++srcP;
+	return false;
 }
 
-bool Music::cmd19(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxMidiReset(const byte *&srcP, byte param) {
+	return false;
 }
 
-bool Music::cmd20(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxMidiDword(const byte *&srcP, byte param) {
+	return false;
 }
 
-bool Music::cmd21(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxSetPanning(const byte *&srcP, byte param) {
+	byte note = *srcP++;
+	if (!_fieldF || param != 7) {
+		uint freq = calcFrequency(note);
+		setFrequency(param, freq);
+		_frequencies[param] = freq;
+	}
+
+	return false;
 }
 
-bool Music::cmd22(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxChannelOff(const byte *&srcP, byte param) {
+	_frequencies[param] &= ~0x2000;
+	write(0xB0 + param, _frequencies[param]);
+	return false;
 }
 
-bool Music::cmd23(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxFade(const byte *&srcP, byte param) {
+	uint freq = calcFrequency(*srcP++);
+	if (!_fieldF || param != 7) {
+		_frequencies[param] = freq;
+		setFrequency(param, freq);
+	}
+
+	return false;
 }
 
-bool Music::cmd24(const byte *&srcP, byte nextByte) {
-	return false;  // TODO
+bool AdlibMusicDriver::fxStartNote(const byte *&srcP, byte param) {
+	if (!_fieldF || param != 7) {
+		byte note = *srcP++;
+		uint freq = calcFrequency(note);
+		setFrequency(param, freq);
+		_frequencies[param] = freq | 0x2000;
+		setFrequency(param, freq);
+	} else {
+		++srcP;
+	}
+
+	return false;
 }
 
-const CommandFn Music::COMMAND_TABLE1[16] = {
-	&Music::cmd1,  &Music::cmd2,  &Music::cmd3,  &Music::cmd4,
-	&Music::cmd5,  &Music::cmd5,  &Music::cmd6,  &Music::cmd4,
-	&Music::cmd7,  &Music::cmd8,  &Music::cmd9,  &Music::cmd10,
-	&Music::cmd11, &Music::cmd12, &Music::cmd13, &Music::cmd14
-};
+bool AdlibMusicDriver::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
+	return false;
+}
 
-const CommandFn Music::COMMAND_TABLE2[16] = {
-	&Music::cmd15, &Music::cmd16, &Music::cmd17, &Music::cmd18,
-	&Music::cmd4,  &Music::cmd4,  &Music::cmd19, &Music::cmd20,
-	&Music::cmd21, &Music::cmd22, &Music::cmd4,  &Music::cmd4,
-	&Music::cmd23, &Music::cmd12, &Music::cmd13, &Music::cmd24
-};
+bool AdlibMusicDriver::fxPlayInstrument(const byte *&srcP, byte param) {
+	if (!_fieldF || param != 7)
+		playInstrument(param, _fxInstrumentPtrs[param]);
 
-void Music::setOutputLevel(byte channelNum, uint level) {
-	write(0x40 + OPERATOR2_INDEXES[channelNum], level |
-		(_channels[channelNum]._scalingValue & 0xC0));
+	return false;
 }
 
-const byte Music::OPERATOR1_INDEXES[ADLIB_CHANNEL_COUNT] = {
+const byte AdlibMusicDriver::OPERATOR1_INDEXES[CHANNEL_COUNT] = {
 	0, 1, 2, 8, 9, 0xA, 0x10, 0x11, 0x12
 };
 
-const byte Music::OPERATOR2_INDEXES[ADLIB_CHANNEL_COUNT] = {
+const byte AdlibMusicDriver::OPERATOR2_INDEXES[CHANNEL_COUNT] = {
 	3, 4, 5, 0xB, 0xC, 0xD, 0x13, 0x14, 0x15
 };
 
+const uint AdlibMusicDriver::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
+};
+
+/*------------------------------------------------------------------------*/
+
+Music::Music(Audio::Mixer *mixer) : _mixer(mixer), _musicDriver(nullptr) {
+	_mixer = mixer;
+	loadEffectsData();
+}
+
+Music::~Music() {
+	delete[] _effectsData;
+}
+
+void Music::loadEffectsData() {
+	File file("admus");
+	Common::String md5str = Common::computeStreamMD5AsString(file, 8192);
+
+	if (md5str != "be8989a5e868913f0e53963046e3ea13")
+		error("Unknown music driver encountered");
+
+	// Load in the driver data
+	byte *effectsData = new byte[file.size()];
+	file.seek(0);
+	file.read(effectsData, file.size());
+	file.close();
+	_effectsData = effectsData;
+
+	// Extract the effects offsets
+	_effectsOffsets.resize(180);
+	const int EFFECTS_OFFSET = 0x91D;
+	for (int idx = 0; idx < 180; ++idx)
+		_effectsOffsets[idx] = READ_LE_UINT16(&effectsData[EFFECTS_OFFSET + idx * 2]);
+}
+
+void Music::playEffect(uint effectId) {
+	if (effectId < _effectsOffsets.size()) {
+		const byte *dataP = &_effectsData[_effectsOffsets[effectId]];
+		_musicDriver->playFX(effectId, dataP);
+	}
+}
+
 } // End of namespace Xeen
diff --git a/engines/xeen/music.h b/engines/xeen/music.h
index b1fa43d..0d5188e 100644
--- a/engines/xeen/music.h
+++ b/engines/xeen/music.h
@@ -28,8 +28,9 @@
 #include "common/array.h"
 #include "common/mutex.h"
 #include "common/queue.h"
+#include "common/stack.h"
 
-#define ADLIB_CHANNEL_COUNT 9
+#define CHANNEL_COUNT 9
 
 namespace OPL {
 	class OPL;
@@ -37,52 +38,132 @@ namespace OPL {
 
 namespace Xeen {
 
-class Music;
+class MusicDriver;
 
-typedef bool (Music::*CommandFn)(const byte *&srcP, byte nextByte);
+typedef bool (MusicDriver::*CommandFn)(const byte *&srcP, byte param);
 
-struct RegisterValue {
-	uint8 _regNum;
-	uint8 _value;
+/**
+ * Base class for music drivers
+ */
+class MusicDriver {
+	struct Subroutine {
+		const byte *_returnP;
+		const byte *_jumpP;
+		Subroutine() : _returnP(nullptr), _jumpP(nullptr) {}
+		Subroutine(const byte *returnP, const byte *endP) :
+			_returnP(returnP), _jumpP(endP) {}
+	};
+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
+	 * @param srcP		Command data pointer
+	 * @returns		If true, execution of commands for the current timer call stops
+	 */
+	bool command(const byte *&srcP);
+protected:
+	bool _fieldF;
+protected:
+	/**
+	 * Executes a series of commands until instructed to stop
+	 */
+	void execute();
+
+	// Music commands (with some also used by FX)
+	virtual bool musCallSubroutine(const byte *&srcP, byte param);
+	virtual bool musSetCountdown(const byte *&srcP, byte param);
+	virtual bool musSetInstrument(const byte *&srcP, byte param) = 0;
+	virtual bool cmdNoOperation(const byte *&srcP, byte param);
+	virtual bool musSetPitchWheel(const byte *&srcP, byte param) = 0;
+	virtual bool musSkipWord(const byte *&srcP, byte param);
+	virtual bool musSetPanning(const byte *&srcP, byte param) = 0;
+	virtual bool musFade(const byte *&srcP, byte param) = 0;
+	virtual bool musStartNote(const byte *&srcP, byte param) = 0;
+	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 musEndSubroutine(const byte *&srcP, byte param);
+
+	// FX commands
+	virtual bool fxCallSubroutine(const byte *&srcP, byte param);
+	virtual bool fxSetCountdown(const byte *&srcP, byte param);
+	virtual bool fxSetInstrument(const byte *&srcP, byte param) = 0;
+	virtual bool fxSetVolume(const byte *&srcP, byte param) = 0;
+	virtual bool fxMidiReset(const byte *&srcP, byte param) = 0;
+	virtual bool fxMidiDword(const byte *&srcP, byte param) = 0;
+	virtual bool fxSetPanning(const byte *&srcP, byte param) = 0;
+	virtual bool fxChannelOff(const byte *&srcP, byte param) = 0;
+	virtual bool fxFade(const byte *&srcP, byte param) = 0;
+	virtual bool fxStartNote(const byte *&srcP, byte param) = 0;
+	virtual bool fxInjectMidi(const byte *&srcP, byte param) = 0;
+	virtual bool fxPlayInstrument(const byte *&srcP, byte param) = 0;
+	virtual bool fxEndSubroutine(const byte *&srcP, byte param);
 
-	RegisterValue(int regNum, int value) {
-		_regNum = regNum; _value = value;
-	}
+	virtual void postProcess() = 0;
+
+	/**
+	 * Does a reset of any sound effect
+	 */
+	virtual void resetFX() = 0;
+public:
+	/**
+	 * Constructor
+	 */
+	MusicDriver();
+
+	/**
+	 * Destructor
+	 */
+	virtual ~MusicDriver() {}
+
+	/**
+	 * Starts an special effect playing
+	 */
+	void playFX(uint effectId, const byte *data);
 };
 
-class Music {
-	struct Channel {
-		byte _outputLevel;
-		byte _scalingValue;
+class AdlibMusicDriver : public MusicDriver {
+	struct RegisterValue {
+		uint8 _regNum;
+		uint8 _value;
 
-		Channel() : _outputLevel(0), _scalingValue(0) {}
+		RegisterValue(int regNum, int value) {
+			_regNum = regNum; _value = value;
+		}
 	};
 private:
-	static const byte OPERATOR1_INDEXES[ADLIB_CHANNEL_COUNT];
-	static const byte OPERATOR2_INDEXES[ADLIB_CHANNEL_COUNT];
-	static const CommandFn COMMAND_TABLE1[16];
-	static const CommandFn COMMAND_TABLE2[16];
+	static const byte OPERATOR1_INDEXES[CHANNEL_COUNT];
+	static const byte OPERATOR2_INDEXES[CHANNEL_COUNT];
+	static const uint WAVEFORMS[24];
 private:
 	OPL::OPL *_opl;
-	Common::Mutex _driverMutex;
-	Common::Array<Channel> _channels;
 	Common::Queue<RegisterValue> _queue;
-	const byte *_effectsData;
-	Common::Array<uint16> _effectsOffsets;
-	const byte *_musicPtr1, *_musicPtr2;
-	const byte *_dataPtr;
-	bool _fieldF;
-	bool _field1C;
-	bool _field1E;
-	uint _fieldFB[7];
-	int _field109;
-	int _field10B;
-	byte _field10D[7];
-	int _field114;
-	int _field115;
-	int _field116;
-	int _field117;
-	bool _lowMusicIgnored;
+	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 _field182;
+	int _volume;
 private:
 	/**
 	 * Initializes the state of the Adlib OPL driver
@@ -90,11 +171,6 @@ private:
 	void initialize();
 
 	/**
-	 * Loads effects data that was embedded in the music driver
-	 */
-	void loadEffectsData();
-
-	/**
 	 * Adds a register write to the pending queue that will be flushed
 	 * out to the OPL on the next timer call
 	 */
@@ -111,16 +187,6 @@ private:
 	void flush();
 
 	/**
-	 * Updates any playing music
-	 */
-	void update();
-
-	/**
-	 * Does a reset
-	 */
-	void reset();
-
-	/**
 	 * Resets all the output frequencies
 	 */
 	void resetFrequencies();
@@ -131,42 +197,73 @@ private:
 	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);
 
 	/**
-	 * Post-process
-	 */
-	void postProcess();
-
-	/**
-	 * Update command methods
-	 */
-	bool cmd1(const byte *&srcP, byte nextByte);
-	bool cmd2(const byte *&srcP, byte nextByte);
-	bool cmd3(const byte *&srcP, byte nextByte);
-	bool cmd4(const byte *&srcP, byte nextByte);
-	bool cmd5(const byte *&srcP, byte nextByte);
-	bool cmd6(const byte *&srcP, byte nextByte);
-	bool cmd7(const byte *&srcP, byte nextByte);
-	bool cmd8(const byte *&srcP, byte nextByte);
-	bool cmd9(const byte *&srcP, byte nextByte);
-	bool cmd10(const byte *&srcP, byte nextByte);
-	bool cmd11(const byte *&srcP, byte nextByte);
-	bool cmd12(const byte *&srcP, byte nextByte);
-	bool cmd13(const byte *&srcP, byte nextByte);
-	bool cmd14(const byte *&srcP, byte nextByte);
-	bool cmd15(const byte *&srcP, byte nextByte);
-	bool cmd16(const byte *&srcP, byte nextByte);
-	bool cmd17(const byte *&srcP, byte nextByte);
-	bool cmd18(const byte *&srcP, byte nextByte);
-	bool cmd19(const byte *&srcP, byte nextByte);
-	bool cmd20(const byte *&srcP, byte nextByte);
-	bool cmd21(const byte *&srcP, byte nextByte);
-	bool cmd22(const byte *&srcP, byte nextByte);
-	bool cmd23(const byte *&srcP, byte nextByte);
-	bool cmd24(const byte *&srcP, byte nextByte);
+	 * Starts playing an instrument
+	 */
+	void playInstrument(byte channelNum, const byte *data);
+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);
+
+	/**
+	 * Does a reset of any sound effect
+	 */
+	virtual void resetFX();
+public:
+	/**
+	 * Constructor
+	 */
+	AdlibMusicDriver();
+
+	/**
+	 * Destructor
+	 */
+	virtual ~AdlibMusicDriver();
+};
+
+
+class Music {
+private:
+	MusicDriver *_musicDriver;
+	const byte *_effectsData;
+	Common::Array<uint16> _effectsOffsets;
+private:
+	/**
+	 * Loads effects data that was embedded in the music driver
+	 */
+	void loadEffectsData();
+
+	/**
+	 * Updates any playing music
+	 */
+	void update();
+
 protected:
 	Audio::Mixer *_mixer;
 public:





More information about the Scummvm-git-logs mailing list