[Scummvm-git-logs] scummvm master -> 4cb79db612d2e4735a38538bb95aa07131fcda23

athrxx athrxx at scummvm.org
Mon Apr 15 21:54:02 CEST 2019


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

Summary:
462fea2cca KYRA: sound files/classes reorganization step #1
a563817db0 KYRA: sound files/classes reorganization step #2
9d52746334 KYRA: sound files/classes reorganization step #3
9144b8894e KYRA: sound files/classes reorganization step #4
145edf0b94 KYRA: sound files/classes reorganization step #5
4cb79db612 KYRA: add some #ifdefs for disabling EOB


Commit: 462fea2ccaa894d5b19372ddccc606d25333127a
    https://github.com/scummvm/scummvm/commit/462fea2ccaa894d5b19372ddccc606d25333127a
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-15T21:51:47+02:00

Commit Message:
KYRA: sound files/classes reorganization step #1

Rename files which are only used for one target.
This is actually wrong for my renaming of sound_towns.
But I'll move the code for other targets to different files in another commit. I want to do just the renaming here, so that Git might be better at preserving the history.

Changed paths:
  A engines/kyra/sound/sound_amiga_lok.cpp
  A engines/kyra/sound/sound_digital_mr.cpp
  A engines/kyra/sound/sound_digital_mr.h
  A engines/kyra/sound/sound_towns_lok.cpp
  R engines/kyra/sound/sound_amiga.cpp
  R engines/kyra/sound/sound_digital.cpp
  R engines/kyra/sound/sound_digital.h
  R engines/kyra/sound/sound_towns.cpp
    engines/kyra/module.mk


diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 9c20ad6..98494bc 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -61,11 +61,11 @@ MODULE_OBJS := \
 	sequence/sequences_hof.o \
 	sequence/sequences_mr.o \
 	sound/sound_adlib.o \
-	sound/sound_amiga.o \
-	sound/sound_digital.o \
+	sound/sound_amiga_lok.o \
+	sound/sound_digital_mr.o \
 	sound/sound_midi.o \
 	sound/sound_pcspk.o \
-	sound/sound_towns.o \
+	sound/sound_towns_lok.o \
 	sound/sound.o \
 	sound/sound_lok.o \
 	text/text.o \
diff --git a/engines/kyra/sound/sound_amiga.cpp b/engines/kyra/sound/sound_amiga.cpp
deleted file mode 100644
index 1104004..0000000
--- a/engines/kyra/sound/sound_amiga.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/* 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 "kyra/sound/sound_intern.h"
-#include "kyra/resource/resource.h"
-
-#include "audio/mixer.h"
-#include "audio/mods/maxtrax.h"
-
-namespace Kyra {
-
-SoundAmiga::SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer)
-	: Sound(vm, mixer),
-	  _driver(0),
-	  _musicHandle(),
-	  _fileLoaded(kFileNone),
-	  _tableSfxIntro(0),
-	  _tableSfxGame(0),
-	  _tableSfxIntro_Size(0),
-	  _tableSfxGame_Size(0) {
-}
-
-SoundAmiga::~SoundAmiga() {
-	_mixer->stopHandle(_musicHandle);
-	delete _driver;
-}
-
-bool SoundAmiga::init() {
-	_driver = new Audio::MaxTrax(_mixer->getOutputRate(), true);
-
-	_tableSfxIntro = _vm->staticres()->loadAmigaSfxTable(k1AmigaIntroSFXTable, _tableSfxIntro_Size);
-	_tableSfxGame = _vm->staticres()->loadAmigaSfxTable(k1AmigaGameSFXTable, _tableSfxGame_Size);
-
-	return _driver != 0 && _tableSfxIntro && _tableSfxGame;
-}
-
-void SoundAmiga::initAudioResourceInfo(int set, void *info) {
-	// See comment below
-}
-
-void SoundAmiga::selectAudioResourceSet(int set) {
-	// It seems that loadSoundFile() is doing what would normally be done in here.
-	// As long as this driver is only required for one single target (Kyra 1 Amiga)
-	// this doesn't matter much.
-}
-
-bool SoundAmiga::hasSoundFile(uint file) const {
-	if (file < 3)
-		return true;
-	return false;
-}
-
-void SoundAmiga::loadSoundFile(uint file) {
-	debugC(5, kDebugLevelSound, "SoundAmiga::loadSoundFile(%d)", file);
-
-	static const char *const tableFilenames[3][2] = {
-		{ "introscr.mx",  "introinst.mx" },
-		{ "kyramusic.mx", 0 },
-		{ "finalescr.mx", "introinst.mx" }
-	};
-	assert(file < ARRAYSIZE(tableFilenames));
-	if (_fileLoaded == (FileType)file)
-		return;
-	const char *scoreName = tableFilenames[file][0];
-	const char *sampleName = tableFilenames[file][1];
-	bool loaded = false;
-
-	Common::SeekableReadStream *scoreIn = _vm->resource()->createReadStream(scoreName);
-	if (sampleName) {
-		Common::SeekableReadStream *sampleIn = _vm->resource()->createReadStream(sampleName);
-		if (scoreIn && sampleIn) {
-			_fileLoaded = kFileNone;
-			loaded = _driver->load(*scoreIn, true, false);
-			loaded = loaded && _driver->load(*sampleIn, false, true);
-		} else
-			warning("SoundAmiga: missing atleast one of those music files: %s, %s", scoreName, sampleName);
-		delete sampleIn;
-	} else {
-		if (scoreIn) {
-			_fileLoaded = kFileNone;
-			loaded = _driver->load(*scoreIn);
-		} else
-			warning("SoundAmiga: missing music file: %s", scoreName);
-	}
-	delete scoreIn;
-
-	if (loaded)
-		_fileLoaded = (FileType)file;
-}
-
-void SoundAmiga::playTrack(uint8 track) {
-	debugC(5, kDebugLevelSound, "SoundAmiga::playTrack(%d)", track);
-
-	static const byte tempoIntro[] = { 0x46, 0x55, 0x3C, 0x41 };
-	static const byte tempoFinal[] = { 0x78, 0x50 };
-	static const byte tempoIngame[] = {
-		0x64, 0x64, 0x64, 0x64, 0x64, 0x73, 0x4B, 0x64,
-		0x64, 0x64, 0x55, 0x9C, 0x6E, 0x91, 0x78, 0x84,
-		0x32, 0x64, 0x64, 0x6E, 0x3C, 0xD8, 0xAF
-	};
-	static const byte loopIngame[] = {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
-		0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00
-	};
-
-	int score = -1;
-	bool loop = false;
-	byte volume = 0x40;
-	byte tempo = 0;
-
-
-	switch (_fileLoaded) {
-	case kFileIntro:
-		if (track >= 2 && track < ARRAYSIZE(tempoIntro) + 2) {
-			score = track - 2;
-			tempo = tempoIntro[score];
-		}
-		break;
-
-	case kFileGame:
-		if (track >= 11 && track < ARRAYSIZE(tempoIngame) + 11) {
-			score = track - 11;
-			loop = loopIngame[score] != 0;
-			tempo = tempoIngame[score];
-		}
-		break;
-
-	case kFileFinal:
-		// score 0 gets started immediately after loading the music-files with different tempo.
-		// we need to define a track-value for the fake call of this function
-		if (track >= 2 && track < ARRAYSIZE(tempoFinal) + 2) {
-			score = track - 2;
-			loop = true;
-			tempo = tempoFinal[score];
-		}
-		break;
-
-	default:
-		return;
-	}
-
-	if (score >= 0) {
-		if (_musicEnabled && _driver->playSong(score, loop)) {
-			_driver->setVolume(volume);
-			_driver->setTempo(tempo << 4);
-			if (!_mixer->isSoundHandleActive(_musicHandle))
-				_mixer->playStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
-		}
-	} else if (track == 0)
-		_driver->stopMusic();
-	else if (track == 1)
-		beginFadeOut();
-}
-
-void SoundAmiga::haltTrack() {
-	debugC(5, kDebugLevelSound, "SoundAmiga::haltTrack()");
-	_driver->stopMusic();
-}
-
-void SoundAmiga::beginFadeOut() {
-	debugC(5, kDebugLevelSound, "SoundAmiga::beginFadeOut()");
-	for (int i = 0x3F; i >= 0; --i) {
-		_driver->setVolume((byte)i);
-		_vm->delay(_vm->tickLength());
-	}
-
-	_driver->stopMusic();
-	_vm->delay(_vm->tickLength());
-	_driver->setVolume(0x40);
-}
-
-void SoundAmiga::playSoundEffect(uint8 track, uint8) {
-	debugC(5, kDebugLevelSound, "SoundAmiga::playSoundEffect(%d)", track);
-	const AmigaSfxTable *sfx = 0;
-	bool pan = false;
-
-	switch (_fileLoaded) {
-	case kFileFinal:
-	case kFileIntro:
-		// We only allow playing of sound effects, which are included in the table.
-		if (track < _tableSfxIntro_Size) {
-			sfx = &_tableSfxIntro[track];
-			pan = (sfx->pan != 0);
-		}
-		break;
-
-	case kFileGame:
-		if (0x61 <= track && track <= 0x63)
-			playTrack(track - 0x4F);
-
-		if (track >= _tableSfxGame_Size)
-			return;
-
-		if (_tableSfxGame[track].note) {
-			sfx = &_tableSfxGame[track];
-			pan = (sfx->pan != 0) && (sfx->pan != 2);
-		}
-
-		break;
-
-	default:
-		return;
-	}
-
-	if (_sfxEnabled && sfx) {
-		const bool success = _driver->playNote(sfx->note, sfx->patch, sfx->duration, sfx->volume, pan);
-		if (success && !_mixer->isSoundHandleActive(_musicHandle))
-			_mixer->playStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
-	}
-}
-
-} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_amiga_lok.cpp b/engines/kyra/sound/sound_amiga_lok.cpp
new file mode 100644
index 0000000..1104004
--- /dev/null
+++ b/engines/kyra/sound/sound_amiga_lok.cpp
@@ -0,0 +1,232 @@
+/* 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 "kyra/sound/sound_intern.h"
+#include "kyra/resource/resource.h"
+
+#include "audio/mixer.h"
+#include "audio/mods/maxtrax.h"
+
+namespace Kyra {
+
+SoundAmiga::SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer)
+	: Sound(vm, mixer),
+	  _driver(0),
+	  _musicHandle(),
+	  _fileLoaded(kFileNone),
+	  _tableSfxIntro(0),
+	  _tableSfxGame(0),
+	  _tableSfxIntro_Size(0),
+	  _tableSfxGame_Size(0) {
+}
+
+SoundAmiga::~SoundAmiga() {
+	_mixer->stopHandle(_musicHandle);
+	delete _driver;
+}
+
+bool SoundAmiga::init() {
+	_driver = new Audio::MaxTrax(_mixer->getOutputRate(), true);
+
+	_tableSfxIntro = _vm->staticres()->loadAmigaSfxTable(k1AmigaIntroSFXTable, _tableSfxIntro_Size);
+	_tableSfxGame = _vm->staticres()->loadAmigaSfxTable(k1AmigaGameSFXTable, _tableSfxGame_Size);
+
+	return _driver != 0 && _tableSfxIntro && _tableSfxGame;
+}
+
+void SoundAmiga::initAudioResourceInfo(int set, void *info) {
+	// See comment below
+}
+
+void SoundAmiga::selectAudioResourceSet(int set) {
+	// It seems that loadSoundFile() is doing what would normally be done in here.
+	// As long as this driver is only required for one single target (Kyra 1 Amiga)
+	// this doesn't matter much.
+}
+
+bool SoundAmiga::hasSoundFile(uint file) const {
+	if (file < 3)
+		return true;
+	return false;
+}
+
+void SoundAmiga::loadSoundFile(uint file) {
+	debugC(5, kDebugLevelSound, "SoundAmiga::loadSoundFile(%d)", file);
+
+	static const char *const tableFilenames[3][2] = {
+		{ "introscr.mx",  "introinst.mx" },
+		{ "kyramusic.mx", 0 },
+		{ "finalescr.mx", "introinst.mx" }
+	};
+	assert(file < ARRAYSIZE(tableFilenames));
+	if (_fileLoaded == (FileType)file)
+		return;
+	const char *scoreName = tableFilenames[file][0];
+	const char *sampleName = tableFilenames[file][1];
+	bool loaded = false;
+
+	Common::SeekableReadStream *scoreIn = _vm->resource()->createReadStream(scoreName);
+	if (sampleName) {
+		Common::SeekableReadStream *sampleIn = _vm->resource()->createReadStream(sampleName);
+		if (scoreIn && sampleIn) {
+			_fileLoaded = kFileNone;
+			loaded = _driver->load(*scoreIn, true, false);
+			loaded = loaded && _driver->load(*sampleIn, false, true);
+		} else
+			warning("SoundAmiga: missing atleast one of those music files: %s, %s", scoreName, sampleName);
+		delete sampleIn;
+	} else {
+		if (scoreIn) {
+			_fileLoaded = kFileNone;
+			loaded = _driver->load(*scoreIn);
+		} else
+			warning("SoundAmiga: missing music file: %s", scoreName);
+	}
+	delete scoreIn;
+
+	if (loaded)
+		_fileLoaded = (FileType)file;
+}
+
+void SoundAmiga::playTrack(uint8 track) {
+	debugC(5, kDebugLevelSound, "SoundAmiga::playTrack(%d)", track);
+
+	static const byte tempoIntro[] = { 0x46, 0x55, 0x3C, 0x41 };
+	static const byte tempoFinal[] = { 0x78, 0x50 };
+	static const byte tempoIngame[] = {
+		0x64, 0x64, 0x64, 0x64, 0x64, 0x73, 0x4B, 0x64,
+		0x64, 0x64, 0x55, 0x9C, 0x6E, 0x91, 0x78, 0x84,
+		0x32, 0x64, 0x64, 0x6E, 0x3C, 0xD8, 0xAF
+	};
+	static const byte loopIngame[] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
+		0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00
+	};
+
+	int score = -1;
+	bool loop = false;
+	byte volume = 0x40;
+	byte tempo = 0;
+
+
+	switch (_fileLoaded) {
+	case kFileIntro:
+		if (track >= 2 && track < ARRAYSIZE(tempoIntro) + 2) {
+			score = track - 2;
+			tempo = tempoIntro[score];
+		}
+		break;
+
+	case kFileGame:
+		if (track >= 11 && track < ARRAYSIZE(tempoIngame) + 11) {
+			score = track - 11;
+			loop = loopIngame[score] != 0;
+			tempo = tempoIngame[score];
+		}
+		break;
+
+	case kFileFinal:
+		// score 0 gets started immediately after loading the music-files with different tempo.
+		// we need to define a track-value for the fake call of this function
+		if (track >= 2 && track < ARRAYSIZE(tempoFinal) + 2) {
+			score = track - 2;
+			loop = true;
+			tempo = tempoFinal[score];
+		}
+		break;
+
+	default:
+		return;
+	}
+
+	if (score >= 0) {
+		if (_musicEnabled && _driver->playSong(score, loop)) {
+			_driver->setVolume(volume);
+			_driver->setTempo(tempo << 4);
+			if (!_mixer->isSoundHandleActive(_musicHandle))
+				_mixer->playStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+		}
+	} else if (track == 0)
+		_driver->stopMusic();
+	else if (track == 1)
+		beginFadeOut();
+}
+
+void SoundAmiga::haltTrack() {
+	debugC(5, kDebugLevelSound, "SoundAmiga::haltTrack()");
+	_driver->stopMusic();
+}
+
+void SoundAmiga::beginFadeOut() {
+	debugC(5, kDebugLevelSound, "SoundAmiga::beginFadeOut()");
+	for (int i = 0x3F; i >= 0; --i) {
+		_driver->setVolume((byte)i);
+		_vm->delay(_vm->tickLength());
+	}
+
+	_driver->stopMusic();
+	_vm->delay(_vm->tickLength());
+	_driver->setVolume(0x40);
+}
+
+void SoundAmiga::playSoundEffect(uint8 track, uint8) {
+	debugC(5, kDebugLevelSound, "SoundAmiga::playSoundEffect(%d)", track);
+	const AmigaSfxTable *sfx = 0;
+	bool pan = false;
+
+	switch (_fileLoaded) {
+	case kFileFinal:
+	case kFileIntro:
+		// We only allow playing of sound effects, which are included in the table.
+		if (track < _tableSfxIntro_Size) {
+			sfx = &_tableSfxIntro[track];
+			pan = (sfx->pan != 0);
+		}
+		break;
+
+	case kFileGame:
+		if (0x61 <= track && track <= 0x63)
+			playTrack(track - 0x4F);
+
+		if (track >= _tableSfxGame_Size)
+			return;
+
+		if (_tableSfxGame[track].note) {
+			sfx = &_tableSfxGame[track];
+			pan = (sfx->pan != 0) && (sfx->pan != 2);
+		}
+
+		break;
+
+	default:
+		return;
+	}
+
+	if (_sfxEnabled && sfx) {
+		const bool success = _driver->playNote(sfx->note, sfx->patch, sfx->duration, sfx->volume, pan);
+		if (success && !_mixer->isSoundHandleActive(_musicHandle))
+			_mixer->playStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+	}
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_digital.cpp b/engines/kyra/sound/sound_digital.cpp
deleted file mode 100644
index e358660..0000000
--- a/engines/kyra/sound/sound_digital.cpp
+++ /dev/null
@@ -1,544 +0,0 @@
-/* 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 "kyra/sound/sound_digital.h"
-#include "kyra/resource/resource.h"
-#include "kyra/engine/kyra_mr.h"
-
-#include "audio/audiostream.h"
-
-#include "audio/decoders/mp3.h"
-#include "audio/decoders/vorbis.h"
-#include "audio/decoders/flac.h"
-
-#include "common/util.h"
-
-namespace Kyra {
-
-class KyraAudioStream : public Audio::SeekableAudioStream {
-public:
-	KyraAudioStream(Audio::SeekableAudioStream *impl) : _impl(impl), _rate(impl->getRate()), _fadeSamples(0), _fadeCount(0), _fading(0), _endOfData(false) {}
-	~KyraAudioStream() { delete _impl; _impl = 0; }
-
-	int readBuffer(int16 *buffer, const int numSamples);
-	bool isStereo() const { return _impl->isStereo(); }
-	bool endOfData() const { return _impl->endOfData() | _endOfData; }
-	int getRate() const { return _rate; }
-
-	void setRate(int newRate) { _rate = newRate; }
-	void beginFadeOut(uint32 millis);
-
-	bool seek(const Audio::Timestamp &where) { return _impl->seek(where); }
-	Audio::Timestamp getLength() const { return _impl->getLength(); }
-private:
-	Audio::SeekableAudioStream *_impl;
-
-	int _rate;
-
-	int32 _fadeSamples;
-	int32 _fadeCount;
-	int _fading;
-
-	bool _endOfData;
-};
-
-void KyraAudioStream::beginFadeOut(uint32 millis) {
-	_fadeSamples = (millis * getRate()) / 1000;
-	if (_fading == 0)
-		_fadeCount = _fadeSamples;
-	_fading = -1;
-}
-
-int KyraAudioStream::readBuffer(int16 *buffer, const int numSamples) {
-	int samplesRead = _impl->readBuffer(buffer, numSamples);
-
-	if (_fading) {
-		int samplesProcessed = 0;
-		for (; samplesProcessed < samplesRead; ++samplesProcessed) {
-			// To help avoid overflows for long fade times, we divide both
-			// _fadeSamples and _fadeCount when calculating the new sample.
-
-			int32 div = _fadeSamples / 256;
-			if (_fading) {
-				*buffer = (*buffer * (_fadeCount / 256)) / div;
-				++buffer;
-
-				_fadeCount += _fading;
-
-				if (_fadeCount < 0) {
-					_fadeCount = 0;
-					_endOfData = true;
-				} else if (_fadeCount > _fadeSamples) {
-					_fadeCount = _fadeSamples;
-					_fading = 0;
-				}
-			}
-		}
-
-		if (_endOfData) {
-			memset(buffer, 0, (samplesRead - samplesProcessed) * sizeof(int16));
-			samplesRead = samplesProcessed;
-		}
-	}
-
-	return samplesRead;
-}
-
-// Thanks to Torbjorn Andersson (eriktorbjorn) for his aud player on which
-// this code is based on
-
-// TODO: cleanup of whole AUDStream
-
-class AUDStream : public Audio::SeekableAudioStream {
-public:
-	AUDStream(Common::SeekableReadStream *stream);
-	~AUDStream();
-
-	int readBuffer(int16 *buffer, const int numSamples);
-
-	bool isStereo() const { return false; }
-	bool endOfData() const { return _endOfData; }
-
-	int getRate() const { return _rate; }
-
-	bool seek(const Audio::Timestamp &where);
-	Audio::Timestamp getLength() const { return _length; }
-private:
-	Common::SeekableReadStream *_stream;
-	uint32 _streamStart;
-	bool _endOfData;
-	int _rate;
-	uint _processedSize;
-	uint _totalSize;
-	Audio::Timestamp _length;
-
-	int _bytesLeft;
-
-	byte *_outBuffer;
-	int _outBufferOffset;
-	uint _outBufferSize;
-
-	byte *_inBuffer;
-	uint _inBufferSize;
-
-	int readChunk(int16 *buffer, const int maxSamples);
-
-	static const int8 WSTable2Bit[];
-	static const int8 WSTable4Bit[];
-};
-
-const int8 AUDStream::WSTable2Bit[] = { -2, -1, 0, 1 };
-const int8 AUDStream::WSTable4Bit[] = {
-	-9, -8, -6, -5, -4, -3, -2, -1,
-	 0,  1,  2,  3,  4,  5,  6,  8
-};
-
-AUDStream::AUDStream(Common::SeekableReadStream *stream) : _stream(stream), _endOfData(true), _rate(0),
-	_processedSize(0), _totalSize(0), _length(0, 1), _bytesLeft(0), _outBuffer(0),
-	_outBufferOffset(0), _outBufferSize(0), _inBuffer(0), _inBufferSize(0) {
-
-	_rate = _stream->readUint16LE();
-	_totalSize = _stream->readUint32LE();
-
-	// TODO?: add checks
-	int flags = _stream->readByte();	// flags
-	int type = _stream->readByte();	// type
-
-	_streamStart = stream->pos();
-
-	debugC(5, kDebugLevelSound, "AUD Info: rate: %d, totalSize: %d, flags: %d, type: %d, streamStart: %d", _rate, _totalSize, flags, type, _streamStart);
-
-	_length = Audio::Timestamp(0, _rate);
-	for (uint32 i = 0; i < _totalSize;) {
-		uint16 size = _stream->readUint16LE();
-		uint16 outSize = _stream->readUint16LE();
-
-		_length = _length.addFrames(outSize);
-		stream->seek(size + 4, SEEK_CUR);
-		i += size + 8;
-	}
-
-	stream->seek(_streamStart, SEEK_SET);
-
-	if (type == 1 && !flags)
-		_endOfData = false;
-	else
-		warning("No AUD file (rate: %d, size: %d, flags: 0x%X, type: %d)", _rate, _totalSize, flags, type);
-}
-
-AUDStream::~AUDStream() {
-	delete[] _outBuffer;
-	delete[] _inBuffer;
-	delete _stream;
-}
-
-int AUDStream::readBuffer(int16 *buffer, const int numSamples) {
-	int samplesRead = 0, samplesLeft = numSamples;
-
-	while (samplesLeft > 0 && !_endOfData) {
-		int samples = readChunk(buffer, samplesLeft);
-		samplesRead += samples;
-		samplesLeft -= samples;
-		buffer += samples;
-	}
-
-	return samplesRead;
-}
-
-inline int16 clip8BitSample(int16 sample) {
-	return CLIP<int16>(sample, 0, 255);
-}
-
-int AUDStream::readChunk(int16 *buffer, const int maxSamples) {
-	int samplesProcessed = 0;
-
-	// if no bytes of the old chunk are left, read the next one
-	if (_bytesLeft <= 0) {
-		if (_processedSize >= _totalSize) {
-			_endOfData = true;
-			return 0;
-		}
-
-		uint16 size = _stream->readUint16LE();
-		uint16 outSize = _stream->readUint16LE();
-		uint32 id = _stream->readUint32LE();
-
-		assert(id == 0x0000DEAF);
-
-		_processedSize += 8 + size;
-
-		_outBufferOffset = 0;
-		if (size == outSize) {
-			if (outSize > _outBufferSize) {
-				_outBufferSize = outSize;
-				delete[] _outBuffer;
-				_outBuffer = new uint8[_outBufferSize];
-				assert(_outBuffer);
-			}
-
-			_bytesLeft = size;
-
-			_stream->read(_outBuffer, _bytesLeft);
-		} else {
-			_bytesLeft = outSize;
-
-			if (outSize > _outBufferSize) {
-				_outBufferSize = outSize;
-				delete[] _outBuffer;
-				_outBuffer = new uint8[_outBufferSize];
-				assert(_outBuffer);
-			}
-
-			if (size > _inBufferSize) {
-				_inBufferSize = size;
-				delete[] _inBuffer;
-				_inBuffer = new uint8[_inBufferSize];
-				assert(_inBuffer);
-			}
-
-			if (_stream->read(_inBuffer, size) != size) {
-				_endOfData = true;
-				return 0;
-			}
-
-			int16 curSample = 0x80;
-			byte code = 0;
-			int8 count = 0;
-			uint16 input = 0;
-			int j = 0;
-			int i = 0;
-
-			while (outSize > 0) {
-				input = _inBuffer[i++] << 2;
-				code = (input >> 8) & 0xFF;
-				count = (input & 0xFF) >> 2;
-
-				switch (code) {
-				case 2:
-					if (count & 0x20) {
-						/* NOTE: count is signed! */
-						count <<= 3;
-						curSample += (count >> 3);
-						_outBuffer[j++] = curSample & 0xFF;
-						outSize--;
-					} else {
-						for (; count >= 0; count--) {
-							_outBuffer[j++] = _inBuffer[i++];
-							outSize--;
-						}
-						curSample = _inBuffer[i - 1];
-					}
-					break;
-				case 1:
-					for (; count >= 0; count--) {
-						code = _inBuffer[i++];
-
-						curSample += WSTable4Bit[code & 0x0F];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample;
-
-						curSample += WSTable4Bit[code >> 4];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample;
-
-						outSize -= 2;
-					}
-					break;
-				case 0:
-					for (; count >= 0; count--) {
-						code = (uint8)_inBuffer[i++];
-
-						curSample += WSTable2Bit[code & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						curSample += WSTable2Bit[(code >> 2) & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						curSample += WSTable2Bit[(code >> 4) & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						curSample += WSTable2Bit[(code >> 6) & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						outSize -= 4;
-					}
-					break;
-				default:
-					for (; count >= 0; count--) {
-						_outBuffer[j++] = curSample & 0xFF;
-						outSize--;
-					}
-				}
-			}
-		}
-	}
-
-	// copies the chunk data to the output buffer
-	if (_bytesLeft > 0) {
-		int samples = MIN(_bytesLeft, maxSamples);
-		samplesProcessed += samples;
-		_bytesLeft -= samples;
-
-		while (samples--) {
-			int16 sample = (_outBuffer[_outBufferOffset++] << 8) ^ 0x8000;
-
-			*buffer++ = sample;
-		}
-	}
-
-	return samplesProcessed;
-}
-
-bool AUDStream::seek(const Audio::Timestamp &where) {
-	const uint32 seekSample = Audio::convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
-
-	_stream->seek(_streamStart);
-	_processedSize = 0;
-	_bytesLeft = 0;
-	_endOfData = false;
-
-	uint32 curSample = 0;
-
-	while (!endOfData()) {
-		uint16 size = _stream->readUint16LE();
-		uint16 outSize = _stream->readUint16LE();
-
-		if (curSample + outSize > seekSample) {
-			_stream->seek(-4, SEEK_CUR);
-
-			uint32 samples = seekSample - curSample;
-			int16 *temp = new int16[samples];
-			assert(temp);
-
-			readChunk(temp, samples);
-			delete[] temp;
-			curSample += samples;
-			break;
-		} else {
-			curSample += outSize;
-			_processedSize += 8 + size;
-			_stream->seek(size + 4, SEEK_CUR);
-		}
-	}
-
-	_endOfData = (_processedSize >= _totalSize);
-
-	return (curSample == seekSample);
-}
-
-#pragma mark -
-
-SoundDigital::SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
-	for (uint i = 0; i < ARRAYSIZE(_sounds); ++i)
-		_sounds[i].stream = 0;
-}
-
-SoundDigital::~SoundDigital() {
-	for (int i = 0; i < ARRAYSIZE(_sounds); ++i)
-		stopSound(i);
-}
-
-int SoundDigital::playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume, bool loop, int channel) {
-	Sound *use = 0;
-	if (channel != -1 && channel < ARRAYSIZE(_sounds)) {
-		stopSound(channel);
-		use = &_sounds[channel];
-	} else {
-		for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
-			if (!isPlaying(channel)) {
-				stopSound(channel);
-				use = &_sounds[channel];
-				break;
-			}
-		}
-
-		for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
-			if (strcmp(_sounds[channel].filename, filename) == 0) {
-				stopSound(channel);
-				use = &_sounds[channel];
-				break;
-			}
-		}
-
-		for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
-			if (_sounds[channel].priority <= priority) {
-				stopSound(channel);
-				use = &_sounds[channel];
-				break;
-			}
-		}
-
-		if (!use) {
-			warning("no free sound channel");
-			return -1;
-		}
-	}
-
-	Common::SeekableReadStream *stream = 0;
-	int usedCodec = -1;
-	for (int i = 0; _supportedCodecs[i].fileext; ++i) {
-		Common::String file = filename;
-		file += _supportedCodecs[i].fileext;
-
-		if (!_vm->resource()->exists(file.c_str()))
-			continue;
-
-		stream = _vm->resource()->createReadStream(file);
-		usedCodec = i;
-	}
-
-	if (!stream) {
-		warning("Couldn't find soundfile '%s'", filename);
-		return -1;
-	}
-
-	Common::strlcpy(use->filename, filename, sizeof(use->filename));
-	use->priority = priority;
-	debugC(5, kDebugLevelSound, "playSound: \"%s\"", use->filename);
-	Audio::SeekableAudioStream *audioStream = _supportedCodecs[usedCodec].streamFunc(stream, DisposeAfterUse::YES);
-	if (!audioStream) {
-		warning("Couldn't create audio stream for file '%s'", filename);
-		return -1;
-	}
-	use->stream = new KyraAudioStream(audioStream);
-	assert(use->stream);
-	if (use->stream->endOfData()) {
-		delete use->stream;
-		use->stream = 0;
-
-		return -1;
-	}
-
-	if (volume > 255)
-		volume = 255;
-	volume = (volume * Audio::Mixer::kMaxChannelVolume) / 255;
-
-	if (type == Audio::Mixer::kSpeechSoundType && _vm->heliumMode())
-		use->stream->setRate(32765);
-
-	_mixer->playStream(type, &use->handle, makeLoopingAudioStream(use->stream, loop ? 0 : 1), -1, volume);
-	return use - _sounds;
-}
-
-bool SoundDigital::isPlaying(int channel) {
-	if (channel == -1)
-		return false;
-
-	assert(channel >= 0 && channel < ARRAYSIZE(_sounds));
-
-	if (!_sounds[channel].stream)
-		return false;
-
-	return _mixer->isSoundHandleActive(_sounds[channel].handle);
-}
-
-void SoundDigital::stopSound(int channel) {
-	if (channel == -1)
-		return;
-
-	assert(channel >= 0 && channel < ARRAYSIZE(_sounds));
-	_mixer->stopHandle(_sounds[channel].handle);
-	_sounds[channel].stream = 0;
-}
-
-void SoundDigital::stopAllSounds() {
-	for (int i = 0; i < ARRAYSIZE(_sounds); ++i) {
-		if (isPlaying(i))
-			stopSound(i);
-	}
-}
-
-void SoundDigital::beginFadeOut(int channel, int ticks) {
-	if (isPlaying(channel))
-		_sounds[channel].stream->beginFadeOut(ticks * _vm->tickLength());
-}
-
-// static res
-
-namespace {
-
-Audio::SeekableAudioStream *makeAUDStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
-	return new AUDStream(stream);
-}
-
-} // end of anonymous namespace
-
-const SoundDigital::AudioCodecs SoundDigital::_supportedCodecs[] = {
-#ifdef USE_FLAC
-	{ ".FLA", Audio::makeFLACStream },
-#endif // USE_FLAC
-#ifdef USE_VORBIS
-	{ ".OGG", Audio::makeVorbisStream },
-#endif // USE_VORBIS
-#ifdef USE_MAD
-	{ ".MP3", Audio::makeMP3Stream },
-#endif // USE_MAD
-	{ ".AUD", makeAUDStream },
-	{ 0, 0 }
-};
-
-
-} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_digital.h b/engines/kyra/sound/sound_digital.h
deleted file mode 100644
index 271dde6..0000000
--- a/engines/kyra/sound/sound_digital.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/* 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 KYRA_SOUND_DIGITAL_H
-#define KYRA_SOUND_DIGITAL_H
-
-#include "audio/mixer.h"
-
-namespace Common {
-class SeekableReadStream;
-} // End of namespace Common
-
-namespace Audio {
-class SeekableAudioStream;
-} // End of namespace Audio
-
-namespace Kyra {
-
-// Digital Audio
-class KyraAudioStream;
-class KyraEngine_MR;
-
-/**
- * Digital audio output device.
- *
- * This is just used for Kyrandia 3.
- */
-class SoundDigital {
-public:
-	SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer);
-	~SoundDigital();
-
-	/**
-	 * Plays a sound.
-	 *
-	 * @param filename  file to be played
-	 * @param priority  priority of the sound
-	 * @param type      type
-	 * @param volume    channel volume
-	 * @param loop      true if the sound should loop (endlessly)
-	 * @param channel   tell the sound player to use a specific channel for playback
-	 *
-	 * @return channel playing the sound
-	 */
-	int playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume = 255, bool loop = false, int channel = -1);
-
-	/**
-	 * Checks if a given channel is playing a sound.
-	 *
-	 * @param channel channel number to check
-	 * @return true if playing, else false
-	 */
-	bool isPlaying(int channel);
-
-	/**
-	 * Stop the playback of a sound in the given
-	 * channel.
-	 *
-	 * @param channel channel number
-	 */
-	void stopSound(int channel);
-
-	/**
-	 * Stops playback of all sounds.
-	 */
-	void stopAllSounds();
-
-	/**
-	 * Makes the sound in a given channel
-	 * fading out.
-	 *
-	 * @param channel channel number
-	 * @param ticks   fadeout time
-	 */
-	void beginFadeOut(int channel, int ticks);
-private:
-	KyraEngine_MR *_vm;
-	Audio::Mixer *_mixer;
-
-	struct Sound {
-		Audio::SoundHandle handle;
-
-		char filename[16];
-		uint8 priority;
-		KyraAudioStream *stream;
-	} _sounds[4];
-
-	struct AudioCodecs {
-		const char *fileext;
-		Audio::SeekableAudioStream *(*streamFunc)(
-			Common::SeekableReadStream *stream,
-			DisposeAfterUse::Flag disposeAfterUse);
-	};
-
-	static const AudioCodecs _supportedCodecs[];
-};
-
-} // End of namespace Kyra
-
-#endif
diff --git a/engines/kyra/sound/sound_digital_mr.cpp b/engines/kyra/sound/sound_digital_mr.cpp
new file mode 100644
index 0000000..e358660
--- /dev/null
+++ b/engines/kyra/sound/sound_digital_mr.cpp
@@ -0,0 +1,544 @@
+/* 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 "kyra/sound/sound_digital.h"
+#include "kyra/resource/resource.h"
+#include "kyra/engine/kyra_mr.h"
+
+#include "audio/audiostream.h"
+
+#include "audio/decoders/mp3.h"
+#include "audio/decoders/vorbis.h"
+#include "audio/decoders/flac.h"
+
+#include "common/util.h"
+
+namespace Kyra {
+
+class KyraAudioStream : public Audio::SeekableAudioStream {
+public:
+	KyraAudioStream(Audio::SeekableAudioStream *impl) : _impl(impl), _rate(impl->getRate()), _fadeSamples(0), _fadeCount(0), _fading(0), _endOfData(false) {}
+	~KyraAudioStream() { delete _impl; _impl = 0; }
+
+	int readBuffer(int16 *buffer, const int numSamples);
+	bool isStereo() const { return _impl->isStereo(); }
+	bool endOfData() const { return _impl->endOfData() | _endOfData; }
+	int getRate() const { return _rate; }
+
+	void setRate(int newRate) { _rate = newRate; }
+	void beginFadeOut(uint32 millis);
+
+	bool seek(const Audio::Timestamp &where) { return _impl->seek(where); }
+	Audio::Timestamp getLength() const { return _impl->getLength(); }
+private:
+	Audio::SeekableAudioStream *_impl;
+
+	int _rate;
+
+	int32 _fadeSamples;
+	int32 _fadeCount;
+	int _fading;
+
+	bool _endOfData;
+};
+
+void KyraAudioStream::beginFadeOut(uint32 millis) {
+	_fadeSamples = (millis * getRate()) / 1000;
+	if (_fading == 0)
+		_fadeCount = _fadeSamples;
+	_fading = -1;
+}
+
+int KyraAudioStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samplesRead = _impl->readBuffer(buffer, numSamples);
+
+	if (_fading) {
+		int samplesProcessed = 0;
+		for (; samplesProcessed < samplesRead; ++samplesProcessed) {
+			// To help avoid overflows for long fade times, we divide both
+			// _fadeSamples and _fadeCount when calculating the new sample.
+
+			int32 div = _fadeSamples / 256;
+			if (_fading) {
+				*buffer = (*buffer * (_fadeCount / 256)) / div;
+				++buffer;
+
+				_fadeCount += _fading;
+
+				if (_fadeCount < 0) {
+					_fadeCount = 0;
+					_endOfData = true;
+				} else if (_fadeCount > _fadeSamples) {
+					_fadeCount = _fadeSamples;
+					_fading = 0;
+				}
+			}
+		}
+
+		if (_endOfData) {
+			memset(buffer, 0, (samplesRead - samplesProcessed) * sizeof(int16));
+			samplesRead = samplesProcessed;
+		}
+	}
+
+	return samplesRead;
+}
+
+// Thanks to Torbjorn Andersson (eriktorbjorn) for his aud player on which
+// this code is based on
+
+// TODO: cleanup of whole AUDStream
+
+class AUDStream : public Audio::SeekableAudioStream {
+public:
+	AUDStream(Common::SeekableReadStream *stream);
+	~AUDStream();
+
+	int readBuffer(int16 *buffer, const int numSamples);
+
+	bool isStereo() const { return false; }
+	bool endOfData() const { return _endOfData; }
+
+	int getRate() const { return _rate; }
+
+	bool seek(const Audio::Timestamp &where);
+	Audio::Timestamp getLength() const { return _length; }
+private:
+	Common::SeekableReadStream *_stream;
+	uint32 _streamStart;
+	bool _endOfData;
+	int _rate;
+	uint _processedSize;
+	uint _totalSize;
+	Audio::Timestamp _length;
+
+	int _bytesLeft;
+
+	byte *_outBuffer;
+	int _outBufferOffset;
+	uint _outBufferSize;
+
+	byte *_inBuffer;
+	uint _inBufferSize;
+
+	int readChunk(int16 *buffer, const int maxSamples);
+
+	static const int8 WSTable2Bit[];
+	static const int8 WSTable4Bit[];
+};
+
+const int8 AUDStream::WSTable2Bit[] = { -2, -1, 0, 1 };
+const int8 AUDStream::WSTable4Bit[] = {
+	-9, -8, -6, -5, -4, -3, -2, -1,
+	 0,  1,  2,  3,  4,  5,  6,  8
+};
+
+AUDStream::AUDStream(Common::SeekableReadStream *stream) : _stream(stream), _endOfData(true), _rate(0),
+	_processedSize(0), _totalSize(0), _length(0, 1), _bytesLeft(0), _outBuffer(0),
+	_outBufferOffset(0), _outBufferSize(0), _inBuffer(0), _inBufferSize(0) {
+
+	_rate = _stream->readUint16LE();
+	_totalSize = _stream->readUint32LE();
+
+	// TODO?: add checks
+	int flags = _stream->readByte();	// flags
+	int type = _stream->readByte();	// type
+
+	_streamStart = stream->pos();
+
+	debugC(5, kDebugLevelSound, "AUD Info: rate: %d, totalSize: %d, flags: %d, type: %d, streamStart: %d", _rate, _totalSize, flags, type, _streamStart);
+
+	_length = Audio::Timestamp(0, _rate);
+	for (uint32 i = 0; i < _totalSize;) {
+		uint16 size = _stream->readUint16LE();
+		uint16 outSize = _stream->readUint16LE();
+
+		_length = _length.addFrames(outSize);
+		stream->seek(size + 4, SEEK_CUR);
+		i += size + 8;
+	}
+
+	stream->seek(_streamStart, SEEK_SET);
+
+	if (type == 1 && !flags)
+		_endOfData = false;
+	else
+		warning("No AUD file (rate: %d, size: %d, flags: 0x%X, type: %d)", _rate, _totalSize, flags, type);
+}
+
+AUDStream::~AUDStream() {
+	delete[] _outBuffer;
+	delete[] _inBuffer;
+	delete _stream;
+}
+
+int AUDStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samplesRead = 0, samplesLeft = numSamples;
+
+	while (samplesLeft > 0 && !_endOfData) {
+		int samples = readChunk(buffer, samplesLeft);
+		samplesRead += samples;
+		samplesLeft -= samples;
+		buffer += samples;
+	}
+
+	return samplesRead;
+}
+
+inline int16 clip8BitSample(int16 sample) {
+	return CLIP<int16>(sample, 0, 255);
+}
+
+int AUDStream::readChunk(int16 *buffer, const int maxSamples) {
+	int samplesProcessed = 0;
+
+	// if no bytes of the old chunk are left, read the next one
+	if (_bytesLeft <= 0) {
+		if (_processedSize >= _totalSize) {
+			_endOfData = true;
+			return 0;
+		}
+
+		uint16 size = _stream->readUint16LE();
+		uint16 outSize = _stream->readUint16LE();
+		uint32 id = _stream->readUint32LE();
+
+		assert(id == 0x0000DEAF);
+
+		_processedSize += 8 + size;
+
+		_outBufferOffset = 0;
+		if (size == outSize) {
+			if (outSize > _outBufferSize) {
+				_outBufferSize = outSize;
+				delete[] _outBuffer;
+				_outBuffer = new uint8[_outBufferSize];
+				assert(_outBuffer);
+			}
+
+			_bytesLeft = size;
+
+			_stream->read(_outBuffer, _bytesLeft);
+		} else {
+			_bytesLeft = outSize;
+
+			if (outSize > _outBufferSize) {
+				_outBufferSize = outSize;
+				delete[] _outBuffer;
+				_outBuffer = new uint8[_outBufferSize];
+				assert(_outBuffer);
+			}
+
+			if (size > _inBufferSize) {
+				_inBufferSize = size;
+				delete[] _inBuffer;
+				_inBuffer = new uint8[_inBufferSize];
+				assert(_inBuffer);
+			}
+
+			if (_stream->read(_inBuffer, size) != size) {
+				_endOfData = true;
+				return 0;
+			}
+
+			int16 curSample = 0x80;
+			byte code = 0;
+			int8 count = 0;
+			uint16 input = 0;
+			int j = 0;
+			int i = 0;
+
+			while (outSize > 0) {
+				input = _inBuffer[i++] << 2;
+				code = (input >> 8) & 0xFF;
+				count = (input & 0xFF) >> 2;
+
+				switch (code) {
+				case 2:
+					if (count & 0x20) {
+						/* NOTE: count is signed! */
+						count <<= 3;
+						curSample += (count >> 3);
+						_outBuffer[j++] = curSample & 0xFF;
+						outSize--;
+					} else {
+						for (; count >= 0; count--) {
+							_outBuffer[j++] = _inBuffer[i++];
+							outSize--;
+						}
+						curSample = _inBuffer[i - 1];
+					}
+					break;
+				case 1:
+					for (; count >= 0; count--) {
+						code = _inBuffer[i++];
+
+						curSample += WSTable4Bit[code & 0x0F];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample;
+
+						curSample += WSTable4Bit[code >> 4];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample;
+
+						outSize -= 2;
+					}
+					break;
+				case 0:
+					for (; count >= 0; count--) {
+						code = (uint8)_inBuffer[i++];
+
+						curSample += WSTable2Bit[code & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						curSample += WSTable2Bit[(code >> 2) & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						curSample += WSTable2Bit[(code >> 4) & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						curSample += WSTable2Bit[(code >> 6) & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						outSize -= 4;
+					}
+					break;
+				default:
+					for (; count >= 0; count--) {
+						_outBuffer[j++] = curSample & 0xFF;
+						outSize--;
+					}
+				}
+			}
+		}
+	}
+
+	// copies the chunk data to the output buffer
+	if (_bytesLeft > 0) {
+		int samples = MIN(_bytesLeft, maxSamples);
+		samplesProcessed += samples;
+		_bytesLeft -= samples;
+
+		while (samples--) {
+			int16 sample = (_outBuffer[_outBufferOffset++] << 8) ^ 0x8000;
+
+			*buffer++ = sample;
+		}
+	}
+
+	return samplesProcessed;
+}
+
+bool AUDStream::seek(const Audio::Timestamp &where) {
+	const uint32 seekSample = Audio::convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
+
+	_stream->seek(_streamStart);
+	_processedSize = 0;
+	_bytesLeft = 0;
+	_endOfData = false;
+
+	uint32 curSample = 0;
+
+	while (!endOfData()) {
+		uint16 size = _stream->readUint16LE();
+		uint16 outSize = _stream->readUint16LE();
+
+		if (curSample + outSize > seekSample) {
+			_stream->seek(-4, SEEK_CUR);
+
+			uint32 samples = seekSample - curSample;
+			int16 *temp = new int16[samples];
+			assert(temp);
+
+			readChunk(temp, samples);
+			delete[] temp;
+			curSample += samples;
+			break;
+		} else {
+			curSample += outSize;
+			_processedSize += 8 + size;
+			_stream->seek(size + 4, SEEK_CUR);
+		}
+	}
+
+	_endOfData = (_processedSize >= _totalSize);
+
+	return (curSample == seekSample);
+}
+
+#pragma mark -
+
+SoundDigital::SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
+	for (uint i = 0; i < ARRAYSIZE(_sounds); ++i)
+		_sounds[i].stream = 0;
+}
+
+SoundDigital::~SoundDigital() {
+	for (int i = 0; i < ARRAYSIZE(_sounds); ++i)
+		stopSound(i);
+}
+
+int SoundDigital::playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume, bool loop, int channel) {
+	Sound *use = 0;
+	if (channel != -1 && channel < ARRAYSIZE(_sounds)) {
+		stopSound(channel);
+		use = &_sounds[channel];
+	} else {
+		for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
+			if (!isPlaying(channel)) {
+				stopSound(channel);
+				use = &_sounds[channel];
+				break;
+			}
+		}
+
+		for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
+			if (strcmp(_sounds[channel].filename, filename) == 0) {
+				stopSound(channel);
+				use = &_sounds[channel];
+				break;
+			}
+		}
+
+		for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
+			if (_sounds[channel].priority <= priority) {
+				stopSound(channel);
+				use = &_sounds[channel];
+				break;
+			}
+		}
+
+		if (!use) {
+			warning("no free sound channel");
+			return -1;
+		}
+	}
+
+	Common::SeekableReadStream *stream = 0;
+	int usedCodec = -1;
+	for (int i = 0; _supportedCodecs[i].fileext; ++i) {
+		Common::String file = filename;
+		file += _supportedCodecs[i].fileext;
+
+		if (!_vm->resource()->exists(file.c_str()))
+			continue;
+
+		stream = _vm->resource()->createReadStream(file);
+		usedCodec = i;
+	}
+
+	if (!stream) {
+		warning("Couldn't find soundfile '%s'", filename);
+		return -1;
+	}
+
+	Common::strlcpy(use->filename, filename, sizeof(use->filename));
+	use->priority = priority;
+	debugC(5, kDebugLevelSound, "playSound: \"%s\"", use->filename);
+	Audio::SeekableAudioStream *audioStream = _supportedCodecs[usedCodec].streamFunc(stream, DisposeAfterUse::YES);
+	if (!audioStream) {
+		warning("Couldn't create audio stream for file '%s'", filename);
+		return -1;
+	}
+	use->stream = new KyraAudioStream(audioStream);
+	assert(use->stream);
+	if (use->stream->endOfData()) {
+		delete use->stream;
+		use->stream = 0;
+
+		return -1;
+	}
+
+	if (volume > 255)
+		volume = 255;
+	volume = (volume * Audio::Mixer::kMaxChannelVolume) / 255;
+
+	if (type == Audio::Mixer::kSpeechSoundType && _vm->heliumMode())
+		use->stream->setRate(32765);
+
+	_mixer->playStream(type, &use->handle, makeLoopingAudioStream(use->stream, loop ? 0 : 1), -1, volume);
+	return use - _sounds;
+}
+
+bool SoundDigital::isPlaying(int channel) {
+	if (channel == -1)
+		return false;
+
+	assert(channel >= 0 && channel < ARRAYSIZE(_sounds));
+
+	if (!_sounds[channel].stream)
+		return false;
+
+	return _mixer->isSoundHandleActive(_sounds[channel].handle);
+}
+
+void SoundDigital::stopSound(int channel) {
+	if (channel == -1)
+		return;
+
+	assert(channel >= 0 && channel < ARRAYSIZE(_sounds));
+	_mixer->stopHandle(_sounds[channel].handle);
+	_sounds[channel].stream = 0;
+}
+
+void SoundDigital::stopAllSounds() {
+	for (int i = 0; i < ARRAYSIZE(_sounds); ++i) {
+		if (isPlaying(i))
+			stopSound(i);
+	}
+}
+
+void SoundDigital::beginFadeOut(int channel, int ticks) {
+	if (isPlaying(channel))
+		_sounds[channel].stream->beginFadeOut(ticks * _vm->tickLength());
+}
+
+// static res
+
+namespace {
+
+Audio::SeekableAudioStream *makeAUDStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+	return new AUDStream(stream);
+}
+
+} // end of anonymous namespace
+
+const SoundDigital::AudioCodecs SoundDigital::_supportedCodecs[] = {
+#ifdef USE_FLAC
+	{ ".FLA", Audio::makeFLACStream },
+#endif // USE_FLAC
+#ifdef USE_VORBIS
+	{ ".OGG", Audio::makeVorbisStream },
+#endif // USE_VORBIS
+#ifdef USE_MAD
+	{ ".MP3", Audio::makeMP3Stream },
+#endif // USE_MAD
+	{ ".AUD", makeAUDStream },
+	{ 0, 0 }
+};
+
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_digital_mr.h b/engines/kyra/sound/sound_digital_mr.h
new file mode 100644
index 0000000..271dde6
--- /dev/null
+++ b/engines/kyra/sound/sound_digital_mr.h
@@ -0,0 +1,119 @@
+/* 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 KYRA_SOUND_DIGITAL_H
+#define KYRA_SOUND_DIGITAL_H
+
+#include "audio/mixer.h"
+
+namespace Common {
+class SeekableReadStream;
+} // End of namespace Common
+
+namespace Audio {
+class SeekableAudioStream;
+} // End of namespace Audio
+
+namespace Kyra {
+
+// Digital Audio
+class KyraAudioStream;
+class KyraEngine_MR;
+
+/**
+ * Digital audio output device.
+ *
+ * This is just used for Kyrandia 3.
+ */
+class SoundDigital {
+public:
+	SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer);
+	~SoundDigital();
+
+	/**
+	 * Plays a sound.
+	 *
+	 * @param filename  file to be played
+	 * @param priority  priority of the sound
+	 * @param type      type
+	 * @param volume    channel volume
+	 * @param loop      true if the sound should loop (endlessly)
+	 * @param channel   tell the sound player to use a specific channel for playback
+	 *
+	 * @return channel playing the sound
+	 */
+	int playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume = 255, bool loop = false, int channel = -1);
+
+	/**
+	 * Checks if a given channel is playing a sound.
+	 *
+	 * @param channel channel number to check
+	 * @return true if playing, else false
+	 */
+	bool isPlaying(int channel);
+
+	/**
+	 * Stop the playback of a sound in the given
+	 * channel.
+	 *
+	 * @param channel channel number
+	 */
+	void stopSound(int channel);
+
+	/**
+	 * Stops playback of all sounds.
+	 */
+	void stopAllSounds();
+
+	/**
+	 * Makes the sound in a given channel
+	 * fading out.
+	 *
+	 * @param channel channel number
+	 * @param ticks   fadeout time
+	 */
+	void beginFadeOut(int channel, int ticks);
+private:
+	KyraEngine_MR *_vm;
+	Audio::Mixer *_mixer;
+
+	struct Sound {
+		Audio::SoundHandle handle;
+
+		char filename[16];
+		uint8 priority;
+		KyraAudioStream *stream;
+	} _sounds[4];
+
+	struct AudioCodecs {
+		const char *fileext;
+		Audio::SeekableAudioStream *(*streamFunc)(
+			Common::SeekableReadStream *stream,
+			DisposeAfterUse::Flag disposeAfterUse);
+	};
+
+	static const AudioCodecs _supportedCodecs[];
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sound/sound_towns.cpp b/engines/kyra/sound/sound_towns.cpp
deleted file mode 100644
index 621c2f1..0000000
--- a/engines/kyra/sound/sound_towns.cpp
+++ /dev/null
@@ -1,749 +0,0 @@
-/* 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 "kyra/sound/sound_intern.h"
-#include "kyra/resource/resource.h"
-
-#include "common/config-manager.h"
-#include "common/system.h"
-
-#include "backends/audiocd/audiocd.h"
-
-#include "audio/audiostream.h"
-#include "audio/decoders/raw.h"
-
-namespace Kyra {
-
-SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
-	: Sound(vm, mixer), _lastTrack(-1), _musicTrackData(0), _sfxFileData(0), _cdaPlaying(0),
-	_sfxFileIndex((uint)-1), _musicFadeTable(0), _sfxWDTable(0), _sfxBTTable(0), _sfxChannel(0x46), _currentResourceSet(0) {
-	memset(&_resInfo, 0, sizeof(_resInfo));
-	_player = new EuphonyPlayer(_mixer);
-}
-
-SoundTowns::~SoundTowns() {
-	g_system->getAudioCDManager()->stop();
-	haltTrack();
-	delete _player;
-	delete[] _musicTrackData;
-	delete[] _sfxFileData;
-	for (int i = 0; i < 3; i++)
-		initAudioResourceInfo(i, 0);
-}
-
-bool SoundTowns::init() {
-	_vm->checkCD();
-	int unused = 0;
-	_musicFadeTable = _vm->staticres()->loadRawData(k1TownsMusicFadeTable, unused);
-	_sfxWDTable = _vm->staticres()->loadRawData(k1TownsSFXwdTable, unused);
-	_sfxBTTable = _vm->staticres()->loadRawData(k1TownsSFXbtTable, unused);
-	_musicTrackData = new uint8[50570];
-
-	if (!_player->init())
-		return false;
-
-	if (!loadInstruments())
-		return false;
-
-	/*_player->driver()->intf()->callback(68);
-	_player->driver()->intf()->callback(70, 0x33);*/
-	_player->driver()->setOutputVolume(1, 118, 118);
-
-	// Initialize CD for audio
-	g_system->getAudioCDManager()->open();
-
-	return true;
-}
-
-void SoundTowns::process() {
-	g_system->getAudioCDManager()->update();
-}
-
-void SoundTowns::playTrack(uint8 track) {
-	if (track < 2)
-		return;
-	track -= 2;
-
-	uint tTableIndex = 3 * track;
-
-	assert(tTableIndex + 2 < res()->cdaTableSize);
-
-	int trackNum = (int)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 2]);
-	int32 loop = (int32)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 1]);
-
-	if (track == _lastTrack && _musicEnabled)
-		return;
-
-	beginFadeOut();
-
-	if (_musicEnabled == 2 && trackNum != -1) {
-		_player->driver()->setOutputVolume(1, 118, 118);
-		g_system->getAudioCDManager()->play(trackNum + 1, loop ? -1 : 1, 0, 0);
-		g_system->getAudioCDManager()->update();
-		_cdaPlaying = true;
-	} else if (_musicEnabled) {
-		playEuphonyTrack(READ_LE_UINT32(&res()->cdaTable[tTableIndex]), loop);
-		_cdaPlaying = false;
-	}
-
-	_lastTrack = track;
-}
-
-void SoundTowns::haltTrack() {
-	_lastTrack = -1;
-	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->update();
-	_cdaPlaying = false;
-
-	for (int i = 0; i < 6; i++)
-		_player->driver()->channelVolume(i, 0);
-	for (int i = 0x40; i < 0x46; i++)
-		_player->driver()->channelVolume(i, 0);
-	for (int i = 0; i < 32; i++)
-		_player->configPart_enable(i, 0);
-	_player->stop();
-}
-
-void SoundTowns::initAudioResourceInfo(int set, void *info) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		delete _resInfo[set];
-		_resInfo[set] = info ? new SoundResourceInfo_Towns(*(SoundResourceInfo_Towns*)info) : 0;
-	}
-}
-
-void SoundTowns::selectAudioResourceSet(int set) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		if (_resInfo[set])
-			_currentResourceSet = set;
-	}
-}
-
-bool SoundTowns::hasSoundFile(uint file) const {
-	if (file < res()->fileListSize)
-		return (res()->fileList[file] != 0);
-	return false;
-}
-
-void SoundTowns::loadSoundFile(uint file) {
-	if (_sfxFileIndex == file || file >= res()->fileListSize)
-		return;
-	_sfxFileIndex = file;
-	delete[] _sfxFileData;
-	_sfxFileData = _vm->resource()->fileData(res()->fileList[file], 0);
-}
-
-void SoundTowns::playSoundEffect(uint8 track, uint8) {
-	if (!_sfxEnabled || !_sfxFileData)
-		return;
-
-	if (track == 0 || track == 10) {
-		stopAllSoundEffects();
-		return;
-	} else if (track == 1) {
-		fadeOutSoundEffects();
-		return;
-	}
-
-	uint8 note = 60;
-	if (_sfxFileIndex == 5) {
-		if (track == 16) {
-			note = 62;
-			track = 15;
-		} else if (track == 17) {
-			note = 64;
-			track = 15;
-		} else if (track == 18) {
-			note = 65;
-			track = 15;
-		}
-	}
-
-	uint8 *fileBody = _sfxFileData + 0x01B8;
-	int32 offset = (int32)READ_LE_UINT32(_sfxFileData + (track - 0x0B) * 4);
-	if (offset == -1)
-		return;
-
-	if (!_player->driver()->soundEffectIsPlaying(_sfxChannel ^ 1)) {
-		_sfxChannel ^= 1;
-	} else if (_player->driver()->soundEffectIsPlaying(_sfxChannel)) {
-		_sfxChannel ^= 1;
-		_player->driver()->stopSoundEffect(_sfxChannel);
-	}
-
-	uint32 *sfxHeader = (uint32 *)(fileBody + offset);
-	uint32 sfxHeaderID = READ_LE_UINT32(sfxHeader);
-	uint32 playbackBufferSize = sfxHeaderID == 1 ? 30704 : READ_LE_UINT32(&sfxHeader[3]);
-
-	uint8 *sfxPlaybackBuffer = new uint8[playbackBufferSize + 32];
-	memcpy(sfxPlaybackBuffer, fileBody + offset, 32);
-
-	uint8 *dst = sfxPlaybackBuffer + 32;
-	memset(dst, 0x80, playbackBufferSize);
-
-	uint8 *sfxBody = ((uint8 *)sfxHeader) + 0x20;
-
-	if (!sfxHeaderID) {
-		memcpy(dst, sfxBody, playbackBufferSize);
-	} else if (sfxHeaderID == 1) {
-		Screen::decodeFrame4(sfxBody, dst, playbackBufferSize);
-	} else if (_sfxWDTable) {
-		uint8 *tgt = dst;
-		uint32 sfx_BtTable_Offset = 0;
-		uint32 sfx_WdTable_Offset = 0;
-		uint32 sfx_WdTable_Number = 5;
-		uint32 inSize = READ_LE_UINT32(&sfxHeader[1]);
-
-		for (uint32 i = 0; i < inSize; i++) {
-			sfx_WdTable_Offset = (sfx_WdTable_Number * 3 << 9) + sfxBody[i] * 6;
-			sfx_WdTable_Number = READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset);
-
-			sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 2);
-			*tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)];
-
-			sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 4);
-			*tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)];
-		}
-	}
-
-	_player->driver()->channelVolume(_sfxChannel, 127);
-	_player->driver()->channelPan(_sfxChannel, 0x40);
-	_player->driver()->channelPitch(_sfxChannel, 0);
-	_player->driver()->playSoundEffect(_sfxChannel, note, 127, sfxPlaybackBuffer);
-	delete[] sfxPlaybackBuffer;
-}
-
-void SoundTowns::updateVolumeSettings() {
-	if (!_player)
-		return;
-
-	bool mute = false;
-	if (ConfMan.hasKey("mute"))
-		mute = ConfMan.getBool("mute");
-
-	_player->driver()->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
-	_player->driver()->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
-}
-
-void SoundTowns::stopAllSoundEffects() {
-	_player->driver()->channelVolume(0x46, 0);
-	_player->driver()->channelVolume(0x47, 0);
-	_player->driver()->stopSoundEffect(0x46);
-	_player->driver()->stopSoundEffect(0x47);
-	_sfxChannel = 0x46;
-}
-
-void SoundTowns::beginFadeOut() {
-	if (_cdaPlaying) {
-		for (int i = 118; i > 103; i--) {
-			_player->driver()->setOutputVolume(1, i, i);
-			_vm->delay(2 * _vm->tickLength());
-		}
-
-		for (int i = 103; i > 83; i -= 2) {
-			_player->driver()->setOutputVolume(1, i, i);
-			_vm->delay(2 * _vm->tickLength());
-		}
-
-		for (int i = 83; i > 58; i -= 2) {
-			_player->driver()->setOutputVolume(1, i, i);
-			_vm->delay(_vm->tickLength());
-		}
-
-		for (int i = 58; i > 0; i--) {
-			_player->driver()->setOutputVolume(1, i, i);
-			_vm->delay(1);
-		}
-
-		_player->driver()->setOutputVolume(1, 0, 0);
-
-	} else {
-		if (_lastTrack == -1)
-			return;
-
-		uint32 ticks = 2;
-		int tickAdv = 0;
-
-		uint16 fadeVolCur[12];
-		uint16 fadeVolStep[12];
-
-		for (int i = 0; i < 6; i++) {
-			fadeVolCur[i] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + i) * 2]);
-			fadeVolStep[i] = fadeVolCur[i] / 50;
-			fadeVolCur[i + 6] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + 6 + i) * 2]);
-			fadeVolStep[i + 6] = fadeVolCur[i + 6] / 30;
-		}
-
-		for (int i = 0; i < 12; i++) {
-			for (int ii = 0; ii < 6; ii++)
-				_player->driver()->channelVolume(ii, fadeVolCur[ii]);
-			for (int ii = 0x40; ii < 0x46; ii++)
-				_player->driver()->channelVolume(ii, fadeVolCur[ii - 0x3A]);
-
-			for (int ii = 0; ii < 6; ii++) {
-				fadeVolCur[ii] -= fadeVolStep[ii];
-				if (fadeVolCur[ii] < 10)
-					fadeVolCur[ii] = 0;
-				fadeVolCur[ii + 6] -= fadeVolStep[ii + 6];
-				if (fadeVolCur[ii + 6] < 10)
-					fadeVolCur[ii + 6] = 0;
-			}
-
-			if (++tickAdv == 3) {
-				tickAdv = 0;
-				ticks += 2;
-			}
-			_vm->delay(ticks * _vm->tickLength());
-		}
-	}
-
-	haltTrack();
-}
-
-bool SoundTowns::loadInstruments() {
-	uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
-	if (!twm)
-		return false;
-
-	Screen::decodeFrame4(twm, _musicTrackData, 50570);
-	for (int i = 0; i < 128; i++)
-		_player->driver()->loadInstrument(0, i, &_musicTrackData[i * 48 + 8]);
-
-	Screen::decodeFrame4(twm + 3232, _musicTrackData, 50570);
-	for (int i = 0; i < 32; i++)
-		_player->driver()->loadInstrument(0x40, i, &_musicTrackData[i * 128 + 8]);
-
-	_player->driver()->unloadWaveTable(-1);
-	uint8 *src = &_musicTrackData[32 * 128 + 8];
-	for (int i = 0; i < 10; i++) {
-		_player->driver()->loadWaveTable(src);
-		src = src + READ_LE_UINT16(&src[12]) + 32;
-	}
-
-	_player->driver()->reserveSoundEffectChannels(2);
-
-	delete[] twm;
-
-	return true;
-}
-
-void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
-	uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
-	Screen::decodeFrame4(twm + 19312 + offset, _musicTrackData, 50570);
-	delete[] twm;
-
-	const uint8 *src = _musicTrackData + 852;
-	for (int i = 0; i < 32; i++)
-		_player->configPart_enable(i, *src++);
-	for (int i = 0; i < 32; i++)
-		_player->configPart_setType(i, *src++);
-	for (int i = 0; i < 32; i++)
-		_player->configPart_remap(i, *src++);
-	for (int i = 0; i < 32; i++)
-		_player->configPart_adjustVolume(i, *src++);
-	for (int i = 0; i < 32; i++)
-		_player->configPart_setTranspose(i, *src++);
-
-	src = _musicTrackData + 1748;
-	for (int i = 0; i < 6; i++)
-		_player->driver()->assignPartToChannel(i, *src++);
-	for (int i = 0x40; i < 0x46; i++)
-		_player->driver()->assignPartToChannel(i, *src++);
-
-	uint32 trackSize = READ_LE_UINT32(_musicTrackData + 2048);
-	uint8 startTick = _musicTrackData[2052];
-
-	_player->setTempo(_musicTrackData[2053]);
-
-	src = _musicTrackData + 2054;
-	uint32 l = READ_LE_UINT32(src + trackSize);
-	trackSize += (l + 4);
-	l = READ_LE_UINT32(src + trackSize);
-	trackSize += (l + 4);
-
-	_player->setLoopStatus(loop);
-	_player->startTrack(src, trackSize, startTick);
-}
-
-void SoundTowns::fadeOutSoundEffects() {
-	for (int i = 127; i > 0; i-= 12) {
-		_player->driver()->channelVolume(0x46, i);
-		_player->driver()->channelVolume(0x47, i);
-		_vm->delay(_vm->tickLength());
-	}
-	stopAllSoundEffects();
-}
-
-SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
-	Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _currentResourceSet(0) {
-	memset(&_resInfo, 0, sizeof(_resInfo));
-}
-
-SoundPC98::~SoundPC98() {
-	delete[] _musicTrackData;
-	delete[] _sfxTrackData;
-	delete _driver;
-	for (int i = 0; i < 3; i++)
-		initAudioResourceInfo(i, 0);
-}
-
-bool SoundPC98::init() {
-	_driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
-	bool reslt = _driver->init();
-	updateVolumeSettings();
-
-	// Initialize CD for audio
-	g_system->getAudioCDManager()->open();
-
-	return reslt;
-}
-
-void SoundPC98::initAudioResourceInfo(int set, void *info) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		delete _resInfo[set];
-		_resInfo[set] = info ? new Common::String(((SoundResourceInfo_PC98*)info)->pattern) : 0;
-	}
-}
-
-void SoundPC98::selectAudioResourceSet(int set) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		if (_resInfo[set])
-			_currentResourceSet = set;
-	}
-}
-
-bool SoundPC98::hasSoundFile(uint file) const {
-	return true;
-}
-
-void SoundPC98::loadSoundFile(uint) {
-	if (_currentResourceSet == kMusicIntro) {
-		delete[] _sfxTrackData;
-		_sfxTrackData = 0;
-
-		int dataSize = 0;
-		const uint8 *tmp = _vm->staticres()->loadRawData(k1PC98IntroSfx, dataSize);
-
-		if (!tmp) {
-			warning("Could not load static intro sound effects data\n");
-			return;
-		}
-
-		_sfxTrackData = new uint8[dataSize];
-		memcpy(_sfxTrackData, tmp, dataSize);
-	}
-}
-
-void SoundPC98::loadSoundFile(Common::String file) {
-	delete[] _sfxTrackData;
-	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
-}
-
-void SoundPC98::playTrack(uint8 track) {
-	track -= 1;
-
-	if (track == _lastTrack && _musicEnabled)
-		return;
-
-	beginFadeOut();
-
-	Common::String musicFile = Common::String::format(resPattern(), track);
-	delete[] _musicTrackData;
-	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
-	if (_musicEnabled)
-		_driver->loadMusicData(_musicTrackData);
-
-	_lastTrack = track;
-}
-
-void SoundPC98::haltTrack() {
-	_lastTrack = -1;
-	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->update();
-	_driver->reset();
-}
-
-void SoundPC98::beginFadeOut() {
-	if (!_driver->musicPlaying())
-		return;
-
-	for (int i = 0; i < 20; i++) {
-		_driver->fadeStep();
-		_vm->delay(32);
-	}
-	haltTrack();
-}
-
-void SoundPC98::playSoundEffect(uint8 track, uint8) {
-	if (!_sfxTrackData)
-		return;
-
-	_driver->loadSoundEffectData(_sfxTrackData, track);
-}
-
-void SoundPC98::updateVolumeSettings() {
-	if (!_driver)
-		return;
-
-	bool mute = false;
-	if (ConfMan.hasKey("mute"))
-		mute = ConfMan.getBool("mute");
-
-	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
-	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
-}
-
-//	KYRA 2
-
-SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
-	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false), _currentResourceSet(0) {
-	memset(&_resInfo, 0, sizeof(_resInfo));
-}
-
-SoundTownsPC98_v2::~SoundTownsPC98_v2() {
-	delete[] _musicTrackData;
-	delete[] _sfxTrackData;
-	delete _driver;
-	for (int i = 0; i < 3; i++)
-		initAudioResourceInfo(i, 0);
-}
-
-bool SoundTownsPC98_v2::init() {
-	_driver = new TownsPC98_AudioDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
-		TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns);
-
-	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
-		if (_resInfo[_currentResourceSet])
-			if (_resInfo[_currentResourceSet]->cdaTableSize)
-				_vm->checkCD();
-
-		// Initialize CD for audio
-		bool hasRealCD = g_system->getAudioCDManager()->open();
-
-		// FIXME: While checking for 'track1.XXX(X)' looks like
-		// a good idea, we should definitely not be doing this
-		// here. Basically our filenaming scheme could change
-		// or we could add support for other audio formats. Also
-		// this misses the possibility that we play the tracks
-		// right off CD. So we should find another way to
-		// check if we have access to CD audio.
-		Resource *r = _vm->resource();
-		if (_musicEnabled &&
-		    (hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
-		     || r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla")))
-				_musicEnabled = 2;
-		else
-			_musicEnabled = 1;
-		_useFmSfx = false;
-
-	} else {
-		_useFmSfx = true;
-	}
-
-	bool reslt = _driver->init();
-	updateVolumeSettings();
-	return reslt;
-}
-
-void SoundTownsPC98_v2::initAudioResourceInfo(int set, void *info) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		delete _resInfo[set];
-		_resInfo[set] = info ? new SoundResourceInfo_TownsPC98V2(*(SoundResourceInfo_TownsPC98V2*)info) : 0;
-	}
-}
-
-void SoundTownsPC98_v2::selectAudioResourceSet(int set) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		if (_resInfo[set])
-			_currentResourceSet = set;
-	}
-}
-
-bool SoundTownsPC98_v2::hasSoundFile(uint file) const {
-	if (file < res()->fileListSize)
-		return (res()->fileList[file] != 0);
-	return false;
-}
-
-void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
-	delete[] _sfxTrackData;
-	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
-}
-
-void SoundTownsPC98_v2::process() {
-	g_system->getAudioCDManager()->update();
-}
-
-void SoundTownsPC98_v2::playTrack(uint8 track) {
-	if (track == _lastTrack && _musicEnabled)
-		return;
-
-	int trackNum = -1;
-	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
-		for (uint i = 0; i < res()->cdaTableSize; i++) {
-			if (track == (uint8)READ_LE_UINT16(&res()->cdaTable[i * 2])) {
-				trackNum = (int)READ_LE_UINT16(&res()->cdaTable[i * 2 + 1]) - 1;
-				break;
-			}
-		}
-	}
-
-	beginFadeOut();
-
-	Common::String musicFile = res()->pattern ? Common::String::format(res()->pattern, track) : (res()->fileList ? res()->fileList[track] : 0);
-	if (musicFile.empty())
-		return;
-
-	delete[] _musicTrackData;
-
-	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
-	_driver->loadMusicData(_musicTrackData, true);
-
-	if (_musicEnabled == 2 && trackNum != -1) {
-		g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
-		g_system->getAudioCDManager()->update();
-	} else if (_musicEnabled) {
-		_driver->cont();
-	}
-
-	_lastTrack = track;
-}
-
-void SoundTownsPC98_v2::haltTrack() {
-	_lastTrack = -1;
-	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->update();
-	_driver->reset();
-}
-
-void SoundTownsPC98_v2::beginFadeOut() {
-	if (!_driver->musicPlaying())
-		return;
-
-	for (int i = 0; i < 20; i++) {
-		_driver->fadeStep();
-		_vm->delay(32);
-	}
-
-	haltTrack();
-}
-
-int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool) {
-	static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
-	static const char patternHOF[] = "%s.PCM";
-	static const char patternLOL[] = "%s.VOC";
-
-	int h = 0;
-	if (_currentSFX) {
-		while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
-			h++;
-
-		if (h >= kNumChannelHandles) {
-			h = 0;
-			while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
-				++h;
-			if (h < kNumChannelHandles)
-				voiceStop(&_soundChannels[h].handle);
-		}
-
-		if (h >= kNumChannelHandles)
-			return 0;
-	}
-
-	Common::String fileName = Common::String::format( _vm->game() == GI_LOL ? patternLOL : patternHOF, file);
-
-	uint8 *data = _vm->resource()->fileData(fileName.c_str(), 0);
-	uint8 *src = data;
-	if (!src)
-		return 0;
-
-	uint16 sfxRate = rates[READ_LE_UINT16(src)];
-	src += 2;
-	bool compressed = (READ_LE_UINT16(src) & 1) ? true : false;
-	src += 2;
-	uint32 outsize = READ_LE_UINT32(src);
-	uint8 *sfx = (uint8 *)malloc(outsize);
-	uint8 *dst = sfx;
-	src += 4;
-
-	if (compressed) {
-		for (uint32 i = outsize; i;) {
-			uint8 cnt = *src++;
-			if (cnt & 0x80) {
-				cnt &= 0x7F;
-				memset(dst, *src++, cnt);
-			} else {
-				memcpy(dst, src, cnt);
-				src += cnt;
-			}
-			dst += cnt;
-			i -= cnt;
-		}
-	} else {
-		memcpy(dst, src, outsize);
-	}
-
-	for (uint32 i = 0; i < outsize; i++) {
-		uint8 cmd = sfx[i];
-		if (cmd & 0x80) {
-			cmd = ~cmd;
-		} else {
-			cmd |= 0x80;
-			if (cmd == 0xFF)
-				cmd--;
-		}
-		if (cmd < 0x80)
-			cmd = 0x80 - cmd;
-		sfx[i] = cmd;
-	}
-
-	_currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
-	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h].handle, _currentSFX, -1, volume);
-	_soundChannels[h].priority = priority;
-	if (handle)
-		*handle = _soundChannels[h].handle;
-
-	delete[] data;
-	return 1;
-}
-
-void SoundTownsPC98_v2::playSoundEffect(uint8 track, uint8) {
-	if (!_useFmSfx || !_sfxTrackData)
-		return;
-
-	_driver->loadSoundEffectData(_sfxTrackData, track);
-}
-
-void SoundTownsPC98_v2::updateVolumeSettings() {
-	if (!_driver)
-		return;
-
-	bool mute = false;
-	if (ConfMan.hasKey("mute"))
-		mute = ConfMan.getBool("mute");
-
-	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
-	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
-}
-
-} // End of namespace Kyra
-
-#undef EUPHONY_FADEOUT_TICKS
diff --git a/engines/kyra/sound/sound_towns_lok.cpp b/engines/kyra/sound/sound_towns_lok.cpp
new file mode 100644
index 0000000..621c2f1
--- /dev/null
+++ b/engines/kyra/sound/sound_towns_lok.cpp
@@ -0,0 +1,749 @@
+/* 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 "kyra/sound/sound_intern.h"
+#include "kyra/resource/resource.h"
+
+#include "common/config-manager.h"
+#include "common/system.h"
+
+#include "backends/audiocd/audiocd.h"
+
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+
+namespace Kyra {
+
+SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
+	: Sound(vm, mixer), _lastTrack(-1), _musicTrackData(0), _sfxFileData(0), _cdaPlaying(0),
+	_sfxFileIndex((uint)-1), _musicFadeTable(0), _sfxWDTable(0), _sfxBTTable(0), _sfxChannel(0x46), _currentResourceSet(0) {
+	memset(&_resInfo, 0, sizeof(_resInfo));
+	_player = new EuphonyPlayer(_mixer);
+}
+
+SoundTowns::~SoundTowns() {
+	g_system->getAudioCDManager()->stop();
+	haltTrack();
+	delete _player;
+	delete[] _musicTrackData;
+	delete[] _sfxFileData;
+	for (int i = 0; i < 3; i++)
+		initAudioResourceInfo(i, 0);
+}
+
+bool SoundTowns::init() {
+	_vm->checkCD();
+	int unused = 0;
+	_musicFadeTable = _vm->staticres()->loadRawData(k1TownsMusicFadeTable, unused);
+	_sfxWDTable = _vm->staticres()->loadRawData(k1TownsSFXwdTable, unused);
+	_sfxBTTable = _vm->staticres()->loadRawData(k1TownsSFXbtTable, unused);
+	_musicTrackData = new uint8[50570];
+
+	if (!_player->init())
+		return false;
+
+	if (!loadInstruments())
+		return false;
+
+	/*_player->driver()->intf()->callback(68);
+	_player->driver()->intf()->callback(70, 0x33);*/
+	_player->driver()->setOutputVolume(1, 118, 118);
+
+	// Initialize CD for audio
+	g_system->getAudioCDManager()->open();
+
+	return true;
+}
+
+void SoundTowns::process() {
+	g_system->getAudioCDManager()->update();
+}
+
+void SoundTowns::playTrack(uint8 track) {
+	if (track < 2)
+		return;
+	track -= 2;
+
+	uint tTableIndex = 3 * track;
+
+	assert(tTableIndex + 2 < res()->cdaTableSize);
+
+	int trackNum = (int)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 2]);
+	int32 loop = (int32)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 1]);
+
+	if (track == _lastTrack && _musicEnabled)
+		return;
+
+	beginFadeOut();
+
+	if (_musicEnabled == 2 && trackNum != -1) {
+		_player->driver()->setOutputVolume(1, 118, 118);
+		g_system->getAudioCDManager()->play(trackNum + 1, loop ? -1 : 1, 0, 0);
+		g_system->getAudioCDManager()->update();
+		_cdaPlaying = true;
+	} else if (_musicEnabled) {
+		playEuphonyTrack(READ_LE_UINT32(&res()->cdaTable[tTableIndex]), loop);
+		_cdaPlaying = false;
+	}
+
+	_lastTrack = track;
+}
+
+void SoundTowns::haltTrack() {
+	_lastTrack = -1;
+	g_system->getAudioCDManager()->stop();
+	g_system->getAudioCDManager()->update();
+	_cdaPlaying = false;
+
+	for (int i = 0; i < 6; i++)
+		_player->driver()->channelVolume(i, 0);
+	for (int i = 0x40; i < 0x46; i++)
+		_player->driver()->channelVolume(i, 0);
+	for (int i = 0; i < 32; i++)
+		_player->configPart_enable(i, 0);
+	_player->stop();
+}
+
+void SoundTowns::initAudioResourceInfo(int set, void *info) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		delete _resInfo[set];
+		_resInfo[set] = info ? new SoundResourceInfo_Towns(*(SoundResourceInfo_Towns*)info) : 0;
+	}
+}
+
+void SoundTowns::selectAudioResourceSet(int set) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		if (_resInfo[set])
+			_currentResourceSet = set;
+	}
+}
+
+bool SoundTowns::hasSoundFile(uint file) const {
+	if (file < res()->fileListSize)
+		return (res()->fileList[file] != 0);
+	return false;
+}
+
+void SoundTowns::loadSoundFile(uint file) {
+	if (_sfxFileIndex == file || file >= res()->fileListSize)
+		return;
+	_sfxFileIndex = file;
+	delete[] _sfxFileData;
+	_sfxFileData = _vm->resource()->fileData(res()->fileList[file], 0);
+}
+
+void SoundTowns::playSoundEffect(uint8 track, uint8) {
+	if (!_sfxEnabled || !_sfxFileData)
+		return;
+
+	if (track == 0 || track == 10) {
+		stopAllSoundEffects();
+		return;
+	} else if (track == 1) {
+		fadeOutSoundEffects();
+		return;
+	}
+
+	uint8 note = 60;
+	if (_sfxFileIndex == 5) {
+		if (track == 16) {
+			note = 62;
+			track = 15;
+		} else if (track == 17) {
+			note = 64;
+			track = 15;
+		} else if (track == 18) {
+			note = 65;
+			track = 15;
+		}
+	}
+
+	uint8 *fileBody = _sfxFileData + 0x01B8;
+	int32 offset = (int32)READ_LE_UINT32(_sfxFileData + (track - 0x0B) * 4);
+	if (offset == -1)
+		return;
+
+	if (!_player->driver()->soundEffectIsPlaying(_sfxChannel ^ 1)) {
+		_sfxChannel ^= 1;
+	} else if (_player->driver()->soundEffectIsPlaying(_sfxChannel)) {
+		_sfxChannel ^= 1;
+		_player->driver()->stopSoundEffect(_sfxChannel);
+	}
+
+	uint32 *sfxHeader = (uint32 *)(fileBody + offset);
+	uint32 sfxHeaderID = READ_LE_UINT32(sfxHeader);
+	uint32 playbackBufferSize = sfxHeaderID == 1 ? 30704 : READ_LE_UINT32(&sfxHeader[3]);
+
+	uint8 *sfxPlaybackBuffer = new uint8[playbackBufferSize + 32];
+	memcpy(sfxPlaybackBuffer, fileBody + offset, 32);
+
+	uint8 *dst = sfxPlaybackBuffer + 32;
+	memset(dst, 0x80, playbackBufferSize);
+
+	uint8 *sfxBody = ((uint8 *)sfxHeader) + 0x20;
+
+	if (!sfxHeaderID) {
+		memcpy(dst, sfxBody, playbackBufferSize);
+	} else if (sfxHeaderID == 1) {
+		Screen::decodeFrame4(sfxBody, dst, playbackBufferSize);
+	} else if (_sfxWDTable) {
+		uint8 *tgt = dst;
+		uint32 sfx_BtTable_Offset = 0;
+		uint32 sfx_WdTable_Offset = 0;
+		uint32 sfx_WdTable_Number = 5;
+		uint32 inSize = READ_LE_UINT32(&sfxHeader[1]);
+
+		for (uint32 i = 0; i < inSize; i++) {
+			sfx_WdTable_Offset = (sfx_WdTable_Number * 3 << 9) + sfxBody[i] * 6;
+			sfx_WdTable_Number = READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset);
+
+			sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 2);
+			*tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)];
+
+			sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 4);
+			*tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)];
+		}
+	}
+
+	_player->driver()->channelVolume(_sfxChannel, 127);
+	_player->driver()->channelPan(_sfxChannel, 0x40);
+	_player->driver()->channelPitch(_sfxChannel, 0);
+	_player->driver()->playSoundEffect(_sfxChannel, note, 127, sfxPlaybackBuffer);
+	delete[] sfxPlaybackBuffer;
+}
+
+void SoundTowns::updateVolumeSettings() {
+	if (!_player)
+		return;
+
+	bool mute = false;
+	if (ConfMan.hasKey("mute"))
+		mute = ConfMan.getBool("mute");
+
+	_player->driver()->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+	_player->driver()->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
+
+void SoundTowns::stopAllSoundEffects() {
+	_player->driver()->channelVolume(0x46, 0);
+	_player->driver()->channelVolume(0x47, 0);
+	_player->driver()->stopSoundEffect(0x46);
+	_player->driver()->stopSoundEffect(0x47);
+	_sfxChannel = 0x46;
+}
+
+void SoundTowns::beginFadeOut() {
+	if (_cdaPlaying) {
+		for (int i = 118; i > 103; i--) {
+			_player->driver()->setOutputVolume(1, i, i);
+			_vm->delay(2 * _vm->tickLength());
+		}
+
+		for (int i = 103; i > 83; i -= 2) {
+			_player->driver()->setOutputVolume(1, i, i);
+			_vm->delay(2 * _vm->tickLength());
+		}
+
+		for (int i = 83; i > 58; i -= 2) {
+			_player->driver()->setOutputVolume(1, i, i);
+			_vm->delay(_vm->tickLength());
+		}
+
+		for (int i = 58; i > 0; i--) {
+			_player->driver()->setOutputVolume(1, i, i);
+			_vm->delay(1);
+		}
+
+		_player->driver()->setOutputVolume(1, 0, 0);
+
+	} else {
+		if (_lastTrack == -1)
+			return;
+
+		uint32 ticks = 2;
+		int tickAdv = 0;
+
+		uint16 fadeVolCur[12];
+		uint16 fadeVolStep[12];
+
+		for (int i = 0; i < 6; i++) {
+			fadeVolCur[i] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + i) * 2]);
+			fadeVolStep[i] = fadeVolCur[i] / 50;
+			fadeVolCur[i + 6] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + 6 + i) * 2]);
+			fadeVolStep[i + 6] = fadeVolCur[i + 6] / 30;
+		}
+
+		for (int i = 0; i < 12; i++) {
+			for (int ii = 0; ii < 6; ii++)
+				_player->driver()->channelVolume(ii, fadeVolCur[ii]);
+			for (int ii = 0x40; ii < 0x46; ii++)
+				_player->driver()->channelVolume(ii, fadeVolCur[ii - 0x3A]);
+
+			for (int ii = 0; ii < 6; ii++) {
+				fadeVolCur[ii] -= fadeVolStep[ii];
+				if (fadeVolCur[ii] < 10)
+					fadeVolCur[ii] = 0;
+				fadeVolCur[ii + 6] -= fadeVolStep[ii + 6];
+				if (fadeVolCur[ii + 6] < 10)
+					fadeVolCur[ii + 6] = 0;
+			}
+
+			if (++tickAdv == 3) {
+				tickAdv = 0;
+				ticks += 2;
+			}
+			_vm->delay(ticks * _vm->tickLength());
+		}
+	}
+
+	haltTrack();
+}
+
+bool SoundTowns::loadInstruments() {
+	uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
+	if (!twm)
+		return false;
+
+	Screen::decodeFrame4(twm, _musicTrackData, 50570);
+	for (int i = 0; i < 128; i++)
+		_player->driver()->loadInstrument(0, i, &_musicTrackData[i * 48 + 8]);
+
+	Screen::decodeFrame4(twm + 3232, _musicTrackData, 50570);
+	for (int i = 0; i < 32; i++)
+		_player->driver()->loadInstrument(0x40, i, &_musicTrackData[i * 128 + 8]);
+
+	_player->driver()->unloadWaveTable(-1);
+	uint8 *src = &_musicTrackData[32 * 128 + 8];
+	for (int i = 0; i < 10; i++) {
+		_player->driver()->loadWaveTable(src);
+		src = src + READ_LE_UINT16(&src[12]) + 32;
+	}
+
+	_player->driver()->reserveSoundEffectChannels(2);
+
+	delete[] twm;
+
+	return true;
+}
+
+void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
+	uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
+	Screen::decodeFrame4(twm + 19312 + offset, _musicTrackData, 50570);
+	delete[] twm;
+
+	const uint8 *src = _musicTrackData + 852;
+	for (int i = 0; i < 32; i++)
+		_player->configPart_enable(i, *src++);
+	for (int i = 0; i < 32; i++)
+		_player->configPart_setType(i, *src++);
+	for (int i = 0; i < 32; i++)
+		_player->configPart_remap(i, *src++);
+	for (int i = 0; i < 32; i++)
+		_player->configPart_adjustVolume(i, *src++);
+	for (int i = 0; i < 32; i++)
+		_player->configPart_setTranspose(i, *src++);
+
+	src = _musicTrackData + 1748;
+	for (int i = 0; i < 6; i++)
+		_player->driver()->assignPartToChannel(i, *src++);
+	for (int i = 0x40; i < 0x46; i++)
+		_player->driver()->assignPartToChannel(i, *src++);
+
+	uint32 trackSize = READ_LE_UINT32(_musicTrackData + 2048);
+	uint8 startTick = _musicTrackData[2052];
+
+	_player->setTempo(_musicTrackData[2053]);
+
+	src = _musicTrackData + 2054;
+	uint32 l = READ_LE_UINT32(src + trackSize);
+	trackSize += (l + 4);
+	l = READ_LE_UINT32(src + trackSize);
+	trackSize += (l + 4);
+
+	_player->setLoopStatus(loop);
+	_player->startTrack(src, trackSize, startTick);
+}
+
+void SoundTowns::fadeOutSoundEffects() {
+	for (int i = 127; i > 0; i-= 12) {
+		_player->driver()->channelVolume(0x46, i);
+		_player->driver()->channelVolume(0x47, i);
+		_vm->delay(_vm->tickLength());
+	}
+	stopAllSoundEffects();
+}
+
+SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+	Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _currentResourceSet(0) {
+	memset(&_resInfo, 0, sizeof(_resInfo));
+}
+
+SoundPC98::~SoundPC98() {
+	delete[] _musicTrackData;
+	delete[] _sfxTrackData;
+	delete _driver;
+	for (int i = 0; i < 3; i++)
+		initAudioResourceInfo(i, 0);
+}
+
+bool SoundPC98::init() {
+	_driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
+	bool reslt = _driver->init();
+	updateVolumeSettings();
+
+	// Initialize CD for audio
+	g_system->getAudioCDManager()->open();
+
+	return reslt;
+}
+
+void SoundPC98::initAudioResourceInfo(int set, void *info) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		delete _resInfo[set];
+		_resInfo[set] = info ? new Common::String(((SoundResourceInfo_PC98*)info)->pattern) : 0;
+	}
+}
+
+void SoundPC98::selectAudioResourceSet(int set) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		if (_resInfo[set])
+			_currentResourceSet = set;
+	}
+}
+
+bool SoundPC98::hasSoundFile(uint file) const {
+	return true;
+}
+
+void SoundPC98::loadSoundFile(uint) {
+	if (_currentResourceSet == kMusicIntro) {
+		delete[] _sfxTrackData;
+		_sfxTrackData = 0;
+
+		int dataSize = 0;
+		const uint8 *tmp = _vm->staticres()->loadRawData(k1PC98IntroSfx, dataSize);
+
+		if (!tmp) {
+			warning("Could not load static intro sound effects data\n");
+			return;
+		}
+
+		_sfxTrackData = new uint8[dataSize];
+		memcpy(_sfxTrackData, tmp, dataSize);
+	}
+}
+
+void SoundPC98::loadSoundFile(Common::String file) {
+	delete[] _sfxTrackData;
+	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
+}
+
+void SoundPC98::playTrack(uint8 track) {
+	track -= 1;
+
+	if (track == _lastTrack && _musicEnabled)
+		return;
+
+	beginFadeOut();
+
+	Common::String musicFile = Common::String::format(resPattern(), track);
+	delete[] _musicTrackData;
+	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
+	if (_musicEnabled)
+		_driver->loadMusicData(_musicTrackData);
+
+	_lastTrack = track;
+}
+
+void SoundPC98::haltTrack() {
+	_lastTrack = -1;
+	g_system->getAudioCDManager()->stop();
+	g_system->getAudioCDManager()->update();
+	_driver->reset();
+}
+
+void SoundPC98::beginFadeOut() {
+	if (!_driver->musicPlaying())
+		return;
+
+	for (int i = 0; i < 20; i++) {
+		_driver->fadeStep();
+		_vm->delay(32);
+	}
+	haltTrack();
+}
+
+void SoundPC98::playSoundEffect(uint8 track, uint8) {
+	if (!_sfxTrackData)
+		return;
+
+	_driver->loadSoundEffectData(_sfxTrackData, track);
+}
+
+void SoundPC98::updateVolumeSettings() {
+	if (!_driver)
+		return;
+
+	bool mute = false;
+	if (ConfMan.hasKey("mute"))
+		mute = ConfMan.getBool("mute");
+
+	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
+
+//	KYRA 2
+
+SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false), _currentResourceSet(0) {
+	memset(&_resInfo, 0, sizeof(_resInfo));
+}
+
+SoundTownsPC98_v2::~SoundTownsPC98_v2() {
+	delete[] _musicTrackData;
+	delete[] _sfxTrackData;
+	delete _driver;
+	for (int i = 0; i < 3; i++)
+		initAudioResourceInfo(i, 0);
+}
+
+bool SoundTownsPC98_v2::init() {
+	_driver = new TownsPC98_AudioDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
+		TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns);
+
+	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+		if (_resInfo[_currentResourceSet])
+			if (_resInfo[_currentResourceSet]->cdaTableSize)
+				_vm->checkCD();
+
+		// Initialize CD for audio
+		bool hasRealCD = g_system->getAudioCDManager()->open();
+
+		// FIXME: While checking for 'track1.XXX(X)' looks like
+		// a good idea, we should definitely not be doing this
+		// here. Basically our filenaming scheme could change
+		// or we could add support for other audio formats. Also
+		// this misses the possibility that we play the tracks
+		// right off CD. So we should find another way to
+		// check if we have access to CD audio.
+		Resource *r = _vm->resource();
+		if (_musicEnabled &&
+		    (hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
+		     || r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla")))
+				_musicEnabled = 2;
+		else
+			_musicEnabled = 1;
+		_useFmSfx = false;
+
+	} else {
+		_useFmSfx = true;
+	}
+
+	bool reslt = _driver->init();
+	updateVolumeSettings();
+	return reslt;
+}
+
+void SoundTownsPC98_v2::initAudioResourceInfo(int set, void *info) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		delete _resInfo[set];
+		_resInfo[set] = info ? new SoundResourceInfo_TownsPC98V2(*(SoundResourceInfo_TownsPC98V2*)info) : 0;
+	}
+}
+
+void SoundTownsPC98_v2::selectAudioResourceSet(int set) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		if (_resInfo[set])
+			_currentResourceSet = set;
+	}
+}
+
+bool SoundTownsPC98_v2::hasSoundFile(uint file) const {
+	if (file < res()->fileListSize)
+		return (res()->fileList[file] != 0);
+	return false;
+}
+
+void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
+	delete[] _sfxTrackData;
+	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
+}
+
+void SoundTownsPC98_v2::process() {
+	g_system->getAudioCDManager()->update();
+}
+
+void SoundTownsPC98_v2::playTrack(uint8 track) {
+	if (track == _lastTrack && _musicEnabled)
+		return;
+
+	int trackNum = -1;
+	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+		for (uint i = 0; i < res()->cdaTableSize; i++) {
+			if (track == (uint8)READ_LE_UINT16(&res()->cdaTable[i * 2])) {
+				trackNum = (int)READ_LE_UINT16(&res()->cdaTable[i * 2 + 1]) - 1;
+				break;
+			}
+		}
+	}
+
+	beginFadeOut();
+
+	Common::String musicFile = res()->pattern ? Common::String::format(res()->pattern, track) : (res()->fileList ? res()->fileList[track] : 0);
+	if (musicFile.empty())
+		return;
+
+	delete[] _musicTrackData;
+
+	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
+	_driver->loadMusicData(_musicTrackData, true);
+
+	if (_musicEnabled == 2 && trackNum != -1) {
+		g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
+		g_system->getAudioCDManager()->update();
+	} else if (_musicEnabled) {
+		_driver->cont();
+	}
+
+	_lastTrack = track;
+}
+
+void SoundTownsPC98_v2::haltTrack() {
+	_lastTrack = -1;
+	g_system->getAudioCDManager()->stop();
+	g_system->getAudioCDManager()->update();
+	_driver->reset();
+}
+
+void SoundTownsPC98_v2::beginFadeOut() {
+	if (!_driver->musicPlaying())
+		return;
+
+	for (int i = 0; i < 20; i++) {
+		_driver->fadeStep();
+		_vm->delay(32);
+	}
+
+	haltTrack();
+}
+
+int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool) {
+	static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
+	static const char patternHOF[] = "%s.PCM";
+	static const char patternLOL[] = "%s.VOC";
+
+	int h = 0;
+	if (_currentSFX) {
+		while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
+			h++;
+
+		if (h >= kNumChannelHandles) {
+			h = 0;
+			while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
+				++h;
+			if (h < kNumChannelHandles)
+				voiceStop(&_soundChannels[h].handle);
+		}
+
+		if (h >= kNumChannelHandles)
+			return 0;
+	}
+
+	Common::String fileName = Common::String::format( _vm->game() == GI_LOL ? patternLOL : patternHOF, file);
+
+	uint8 *data = _vm->resource()->fileData(fileName.c_str(), 0);
+	uint8 *src = data;
+	if (!src)
+		return 0;
+
+	uint16 sfxRate = rates[READ_LE_UINT16(src)];
+	src += 2;
+	bool compressed = (READ_LE_UINT16(src) & 1) ? true : false;
+	src += 2;
+	uint32 outsize = READ_LE_UINT32(src);
+	uint8 *sfx = (uint8 *)malloc(outsize);
+	uint8 *dst = sfx;
+	src += 4;
+
+	if (compressed) {
+		for (uint32 i = outsize; i;) {
+			uint8 cnt = *src++;
+			if (cnt & 0x80) {
+				cnt &= 0x7F;
+				memset(dst, *src++, cnt);
+			} else {
+				memcpy(dst, src, cnt);
+				src += cnt;
+			}
+			dst += cnt;
+			i -= cnt;
+		}
+	} else {
+		memcpy(dst, src, outsize);
+	}
+
+	for (uint32 i = 0; i < outsize; i++) {
+		uint8 cmd = sfx[i];
+		if (cmd & 0x80) {
+			cmd = ~cmd;
+		} else {
+			cmd |= 0x80;
+			if (cmd == 0xFF)
+				cmd--;
+		}
+		if (cmd < 0x80)
+			cmd = 0x80 - cmd;
+		sfx[i] = cmd;
+	}
+
+	_currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
+	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h].handle, _currentSFX, -1, volume);
+	_soundChannels[h].priority = priority;
+	if (handle)
+		*handle = _soundChannels[h].handle;
+
+	delete[] data;
+	return 1;
+}
+
+void SoundTownsPC98_v2::playSoundEffect(uint8 track, uint8) {
+	if (!_useFmSfx || !_sfxTrackData)
+		return;
+
+	_driver->loadSoundEffectData(_sfxTrackData, track);
+}
+
+void SoundTownsPC98_v2::updateVolumeSettings() {
+	if (!_driver)
+		return;
+
+	bool mute = false;
+	if (ConfMan.hasKey("mute"))
+		mute = ConfMan.getBool("mute");
+
+	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
+
+} // End of namespace Kyra
+
+#undef EUPHONY_FADEOUT_TICKS


Commit: a563817db0cb7e3927a11d921a702607758da460
    https://github.com/scummvm/scummvm/commit/a563817db0cb7e3927a11d921a702607758da460
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-15T21:51:47+02:00

Commit Message:
KYRA: sound files/classes reorganization step #2

This is a followup to the last commit which fixes compilation. I wanted to have the pure file renaming separately.

Changed paths:
    engines/kyra/engine/kyra_mr.cpp
    engines/kyra/engine/scene_mr.cpp
    engines/kyra/gui/gui_mr.cpp
    engines/kyra/sound/sound_digital_mr.cpp


diff --git a/engines/kyra/engine/kyra_mr.cpp b/engines/kyra/engine/kyra_mr.cpp
index 9cadf3c..4f87c53 100644
--- a/engines/kyra/engine/kyra_mr.cpp
+++ b/engines/kyra/engine/kyra_mr.cpp
@@ -28,7 +28,7 @@
 #include "kyra/gui/debugger.h"
 #include "kyra/gui/gui_mr.h"
 #include "kyra/resource/resource.h"
-#include "kyra/sound/sound_digital.h"
+#include "kyra/sound/sound_digital_mr.h"
 
 #include "common/system.h"
 #include "common/config-manager.h"
diff --git a/engines/kyra/engine/scene_mr.cpp b/engines/kyra/engine/scene_mr.cpp
index 8935863..ea0e69e 100644
--- a/engines/kyra/engine/scene_mr.cpp
+++ b/engines/kyra/engine/scene_mr.cpp
@@ -22,7 +22,7 @@
 
 #include "kyra/engine/kyra_mr.h"
 #include "kyra/graphics/screen_mr.h"
-#include "kyra/sound/sound_digital.h"
+#include "kyra/sound/sound_digital_mr.h"
 #include "kyra/resource/resource.h"
 
 #include "common/system.h"
diff --git a/engines/kyra/gui/gui_mr.cpp b/engines/kyra/gui/gui_mr.cpp
index dc335f1..b115e30 100644
--- a/engines/kyra/gui/gui_mr.cpp
+++ b/engines/kyra/gui/gui_mr.cpp
@@ -25,7 +25,7 @@
 #include "kyra/text/text_mr.h"
 #include "kyra/resource/resource.h"
 #include "kyra/engine/timer.h"
-#include "kyra/sound/sound_digital.h"
+#include "kyra/sound/sound_digital_mr.h"
 
 #include "common/system.h"
 
diff --git a/engines/kyra/sound/sound_digital_mr.cpp b/engines/kyra/sound/sound_digital_mr.cpp
index e358660..cf94eb0 100644
--- a/engines/kyra/sound/sound_digital_mr.cpp
+++ b/engines/kyra/sound/sound_digital_mr.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "kyra/sound/sound_digital.h"
+#include "kyra/sound/sound_digital_mr.h"
 #include "kyra/resource/resource.h"
 #include "kyra/engine/kyra_mr.h"
 


Commit: 9d527463343c627eac305d8b17a273c0583ffa63
    https://github.com/scummvm/scummvm/commit/9d527463343c627eac305d8b17a273c0583ffa63
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-15T21:51:47+02:00

Commit Message:
KYRA: sound files/classes reorganization step #3

Untangle FM-Towns and PC-98 sound classes and move them to separate files.

Changed paths:
  A engines/kyra/sound/sound_pc98_lok.cpp
  A engines/kyra/sound/sound_pc98_v2.cpp
    engines/kyra/module.mk
    engines/kyra/sound/sound_intern.h
    engines/kyra/sound/sound_towns_darkmoon.cpp
    engines/kyra/sound/sound_towns_lok.cpp


diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 98494bc..0f2634b 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -65,6 +65,8 @@ MODULE_OBJS := \
 	sound/sound_digital_mr.o \
 	sound/sound_midi.o \
 	sound/sound_pcspk.o \
+	sound/sound_pc98_lok.o \
+	sound/sound_pc98_v2.o \
 	sound/sound_towns_lok.o \
 	sound/sound.o \
 	sound/sound_lok.o \
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index 57afb26..4820133 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -24,17 +24,17 @@
 #define KYRA_SOUND_INTERN_H
 
 
-
 #include "kyra/sound/sound.h"
 #include "kyra/sound/sound_adlib.h"
 
-#include "common/mutex.h"
+#include "audio/midiparser.h"
+#include "audio/softsynth/emumidi.h"
+#include "audio/softsynth/fmtowns_pc98/towns_audio.h"
 
-#include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
-#include "audio/softsynth/fmtowns_pc98/towns_euphony.h"
+#include "common/mutex.h"
 
-#include "audio/softsynth/emumidi.h"
-#include "audio/midiparser.h"
+class EuphonyPlayer;
+class TownsPC98_AudioDriver;
 
 namespace Audio {
 class PCSpeaker;
diff --git a/engines/kyra/sound/sound_pc98_lok.cpp b/engines/kyra/sound/sound_pc98_lok.cpp
new file mode 100644
index 0000000..38a9a56
--- /dev/null
+++ b/engines/kyra/sound/sound_pc98_lok.cpp
@@ -0,0 +1,146 @@
+/* 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 "kyra/sound/sound_intern.h"
+#include "kyra/resource/resource.h"
+
+#include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
+
+#include "common/config-manager.h"
+
+namespace Kyra {
+
+SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+	Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _currentResourceSet(0) {
+	memset(&_resInfo, 0, sizeof(_resInfo));
+}
+
+SoundPC98::~SoundPC98() {
+	delete[] _musicTrackData;
+	delete[] _sfxTrackData;
+	delete _driver;
+	for (int i = 0; i < 3; i++)
+		initAudioResourceInfo(i, 0);
+}
+
+bool SoundPC98::init() {
+	_driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
+	bool reslt = _driver->init();
+	updateVolumeSettings();
+
+	return reslt;
+}
+
+void SoundPC98::initAudioResourceInfo(int set, void *info) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		delete _resInfo[set];
+		_resInfo[set] = info ? new Common::String(((SoundResourceInfo_PC98*)info)->pattern) : 0;
+	}
+}
+
+void SoundPC98::selectAudioResourceSet(int set) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		if (_resInfo[set])
+			_currentResourceSet = set;
+	}
+}
+
+bool SoundPC98::hasSoundFile(uint file) const {
+	return true;
+}
+
+void SoundPC98::loadSoundFile(uint) {
+	if (_currentResourceSet == kMusicIntro) {
+		delete[] _sfxTrackData;
+		_sfxTrackData = 0;
+
+		int dataSize = 0;
+		const uint8 *tmp = _vm->staticres()->loadRawData(k1PC98IntroSfx, dataSize);
+
+		if (!tmp) {
+			warning("Could not load static intro sound effects data\n");
+			return;
+		}
+
+		_sfxTrackData = new uint8[dataSize];
+		memcpy(_sfxTrackData, tmp, dataSize);
+	}
+}
+
+void SoundPC98::loadSoundFile(Common::String file) {
+	delete[] _sfxTrackData;
+	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
+}
+
+void SoundPC98::playTrack(uint8 track) {
+	track -= 1;
+
+	if (track == _lastTrack && _musicEnabled)
+		return;
+
+	beginFadeOut();
+
+	Common::String musicFile = Common::String::format(resPattern(), track);
+	delete[] _musicTrackData;
+	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
+	if (_musicEnabled)
+		_driver->loadMusicData(_musicTrackData);
+
+	_lastTrack = track;
+}
+
+void SoundPC98::haltTrack() {
+	_lastTrack = -1;
+	_driver->reset();
+}
+
+void SoundPC98::beginFadeOut() {
+	if (!_driver->musicPlaying())
+		return;
+
+	for (int i = 0; i < 20; i++) {
+		_driver->fadeStep();
+		_vm->delay(32);
+	}
+	haltTrack();
+}
+
+void SoundPC98::playSoundEffect(uint8 track, uint8) {
+	if (!_sfxTrackData)
+		return;
+
+	_driver->loadSoundEffectData(_sfxTrackData, track);
+}
+
+void SoundPC98::updateVolumeSettings() {
+	if (!_driver)
+		return;
+
+	bool mute = false;
+	if (ConfMan.hasKey("mute"))
+		mute = ConfMan.getBool("mute");
+
+	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_pc98_v2.cpp b/engines/kyra/sound/sound_pc98_v2.cpp
new file mode 100644
index 0000000..64c805a
--- /dev/null
+++ b/engines/kyra/sound/sound_pc98_v2.cpp
@@ -0,0 +1,269 @@
+/* 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 "kyra/sound/sound_intern.h"
+#include "kyra/resource/resource.h"
+
+#include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
+
+#include "common/config-manager.h"
+
+#include "backends/audiocd/audiocd.h"
+
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+
+namespace Kyra {
+
+SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false), _currentResourceSet(0) {
+	memset(&_resInfo, 0, sizeof(_resInfo));
+}
+
+SoundTownsPC98_v2::~SoundTownsPC98_v2() {
+	delete[] _musicTrackData;
+	delete[] _sfxTrackData;
+	delete _driver;
+	for (int i = 0; i < 3; i++)
+		initAudioResourceInfo(i, 0);
+}
+
+bool SoundTownsPC98_v2::init() {
+	_driver = new TownsPC98_AudioDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
+		TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns);
+
+	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+		if (_resInfo[_currentResourceSet])
+			if (_resInfo[_currentResourceSet]->cdaTableSize)
+				_vm->checkCD();
+
+		// Initialize CD for audio
+		bool hasRealCD = g_system->getAudioCDManager()->open();
+
+		// FIXME: While checking for 'track1.XXX(X)' looks like
+		// a good idea, we should definitely not be doing this
+		// here. Basically our filenaming scheme could change
+		// or we could add support for other audio formats. Also
+		// this misses the possibility that we play the tracks
+		// right off CD. So we should find another way to
+		// check if we have access to CD audio.
+		Resource *r = _vm->resource();
+		if (_musicEnabled &&
+		    (hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
+		     || r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla")))
+				_musicEnabled = 2;
+		else
+			_musicEnabled = 1;
+		_useFmSfx = false;
+
+	} else {
+		_useFmSfx = true;
+	}
+
+	bool reslt = _driver->init();
+	updateVolumeSettings();
+	return reslt;
+}
+
+void SoundTownsPC98_v2::initAudioResourceInfo(int set, void *info) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		delete _resInfo[set];
+		_resInfo[set] = info ? new SoundResourceInfo_TownsPC98V2(*(SoundResourceInfo_TownsPC98V2*)info) : 0;
+	}
+}
+
+void SoundTownsPC98_v2::selectAudioResourceSet(int set) {
+	if (set >= kMusicIntro && set <= kMusicFinale) {
+		if (_resInfo[set])
+			_currentResourceSet = set;
+	}
+}
+
+bool SoundTownsPC98_v2::hasSoundFile(uint file) const {
+	if (file < res()->fileListSize)
+		return (res()->fileList[file] != 0);
+	return false;
+}
+
+void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
+	delete[] _sfxTrackData;
+	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
+}
+
+void SoundTownsPC98_v2::process() {
+	g_system->getAudioCDManager()->update();
+}
+
+void SoundTownsPC98_v2::playTrack(uint8 track) {
+	if (track == _lastTrack && _musicEnabled)
+		return;
+
+	int trackNum = -1;
+	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+		for (uint i = 0; i < res()->cdaTableSize; i++) {
+			if (track == (uint8)READ_LE_UINT16(&res()->cdaTable[i * 2])) {
+				trackNum = (int)READ_LE_UINT16(&res()->cdaTable[i * 2 + 1]) - 1;
+				break;
+			}
+		}
+	}
+
+	beginFadeOut();
+
+	Common::String musicFile = res()->pattern ? Common::String::format(res()->pattern, track) : (res()->fileList ? res()->fileList[track] : 0);
+	if (musicFile.empty())
+		return;
+
+	delete[] _musicTrackData;
+
+	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
+	_driver->loadMusicData(_musicTrackData, true);
+
+	if (_musicEnabled == 2 && trackNum != -1) {
+		g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
+		g_system->getAudioCDManager()->update();
+	} else if (_musicEnabled) {
+		_driver->cont();
+	}
+
+	_lastTrack = track;
+}
+
+void SoundTownsPC98_v2::haltTrack() {
+	_lastTrack = -1;
+	g_system->getAudioCDManager()->stop();
+	g_system->getAudioCDManager()->update();
+	_driver->reset();
+}
+
+void SoundTownsPC98_v2::beginFadeOut() {
+	if (!_driver->musicPlaying())
+		return;
+
+	for (int i = 0; i < 20; i++) {
+		_driver->fadeStep();
+		_vm->delay(32);
+	}
+
+	haltTrack();
+}
+
+int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool) {
+	static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
+	static const char patternHOF[] = "%s.PCM";
+	static const char patternLOL[] = "%s.VOC";
+
+	int h = 0;
+	if (_currentSFX) {
+		while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
+			h++;
+
+		if (h >= kNumChannelHandles) {
+			h = 0;
+			while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
+				++h;
+			if (h < kNumChannelHandles)
+				voiceStop(&_soundChannels[h].handle);
+		}
+
+		if (h >= kNumChannelHandles)
+			return 0;
+	}
+
+	Common::String fileName = Common::String::format( _vm->game() == GI_LOL ? patternLOL : patternHOF, file);
+
+	uint8 *data = _vm->resource()->fileData(fileName.c_str(), 0);
+	uint8 *src = data;
+	if (!src)
+		return 0;
+
+	uint16 sfxRate = rates[READ_LE_UINT16(src)];
+	src += 2;
+	bool compressed = (READ_LE_UINT16(src) & 1) ? true : false;
+	src += 2;
+	uint32 outsize = READ_LE_UINT32(src);
+	uint8 *sfx = (uint8 *)malloc(outsize);
+	uint8 *dst = sfx;
+	src += 4;
+
+	if (compressed) {
+		for (uint32 i = outsize; i;) {
+			uint8 cnt = *src++;
+			if (cnt & 0x80) {
+				cnt &= 0x7F;
+				memset(dst, *src++, cnt);
+			} else {
+				memcpy(dst, src, cnt);
+				src += cnt;
+			}
+			dst += cnt;
+			i -= cnt;
+		}
+	} else {
+		memcpy(dst, src, outsize);
+	}
+
+	for (uint32 i = 0; i < outsize; i++) {
+		uint8 cmd = sfx[i];
+		if (cmd & 0x80) {
+			cmd = ~cmd;
+		} else {
+			cmd |= 0x80;
+			if (cmd == 0xFF)
+				cmd--;
+		}
+		if (cmd < 0x80)
+			cmd = 0x80 - cmd;
+		sfx[i] = cmd;
+	}
+
+	_currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
+	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h].handle, _currentSFX, -1, volume);
+	_soundChannels[h].priority = priority;
+	if (handle)
+		*handle = _soundChannels[h].handle;
+
+	delete[] data;
+	return 1;
+}
+
+void SoundTownsPC98_v2::playSoundEffect(uint8 track, uint8) {
+	if (!_useFmSfx || !_sfxTrackData)
+		return;
+
+	_driver->loadSoundEffectData(_sfxTrackData, track);
+}
+
+void SoundTownsPC98_v2::updateVolumeSettings() {
+	if (!_driver)
+		return;
+
+	bool mute = false;
+	if (ConfMan.hasKey("mute"))
+		mute = ConfMan.getBool("mute");
+
+	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
+
+} // End of namespace Kyra
+
diff --git a/engines/kyra/sound/sound_towns_darkmoon.cpp b/engines/kyra/sound/sound_towns_darkmoon.cpp
index 747e1bd..3758dff 100644
--- a/engines/kyra/sound/sound_towns_darkmoon.cpp
+++ b/engines/kyra/sound/sound_towns_darkmoon.cpp
@@ -24,7 +24,6 @@
 #include "kyra/resource/resource.h"
 
 #include "common/config-manager.h"
-#include "common/system.h"
 
 #include "backends/audiocd/audiocd.h"
 
diff --git a/engines/kyra/sound/sound_towns_lok.cpp b/engines/kyra/sound/sound_towns_lok.cpp
index 621c2f1..6a22f5f 100644
--- a/engines/kyra/sound/sound_towns_lok.cpp
+++ b/engines/kyra/sound/sound_towns_lok.cpp
@@ -23,14 +23,12 @@
 #include "kyra/sound/sound_intern.h"
 #include "kyra/resource/resource.h"
 
+#include "audio/softsynth/fmtowns_pc98/towns_euphony.h"
+
 #include "common/config-manager.h"
-#include "common/system.h"
 
 #include "backends/audiocd/audiocd.h"
 
-#include "audio/audiostream.h"
-#include "audio/decoders/raw.h"
-
 namespace Kyra {
 
 SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
@@ -392,358 +390,5 @@ void SoundTowns::fadeOutSoundEffects() {
 	stopAllSoundEffects();
 }
 
-SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
-	Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _currentResourceSet(0) {
-	memset(&_resInfo, 0, sizeof(_resInfo));
-}
-
-SoundPC98::~SoundPC98() {
-	delete[] _musicTrackData;
-	delete[] _sfxTrackData;
-	delete _driver;
-	for (int i = 0; i < 3; i++)
-		initAudioResourceInfo(i, 0);
-}
-
-bool SoundPC98::init() {
-	_driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
-	bool reslt = _driver->init();
-	updateVolumeSettings();
-
-	// Initialize CD for audio
-	g_system->getAudioCDManager()->open();
-
-	return reslt;
-}
-
-void SoundPC98::initAudioResourceInfo(int set, void *info) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		delete _resInfo[set];
-		_resInfo[set] = info ? new Common::String(((SoundResourceInfo_PC98*)info)->pattern) : 0;
-	}
-}
-
-void SoundPC98::selectAudioResourceSet(int set) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		if (_resInfo[set])
-			_currentResourceSet = set;
-	}
-}
-
-bool SoundPC98::hasSoundFile(uint file) const {
-	return true;
-}
-
-void SoundPC98::loadSoundFile(uint) {
-	if (_currentResourceSet == kMusicIntro) {
-		delete[] _sfxTrackData;
-		_sfxTrackData = 0;
-
-		int dataSize = 0;
-		const uint8 *tmp = _vm->staticres()->loadRawData(k1PC98IntroSfx, dataSize);
-
-		if (!tmp) {
-			warning("Could not load static intro sound effects data\n");
-			return;
-		}
-
-		_sfxTrackData = new uint8[dataSize];
-		memcpy(_sfxTrackData, tmp, dataSize);
-	}
-}
-
-void SoundPC98::loadSoundFile(Common::String file) {
-	delete[] _sfxTrackData;
-	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
-}
-
-void SoundPC98::playTrack(uint8 track) {
-	track -= 1;
-
-	if (track == _lastTrack && _musicEnabled)
-		return;
-
-	beginFadeOut();
-
-	Common::String musicFile = Common::String::format(resPattern(), track);
-	delete[] _musicTrackData;
-	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
-	if (_musicEnabled)
-		_driver->loadMusicData(_musicTrackData);
-
-	_lastTrack = track;
-}
-
-void SoundPC98::haltTrack() {
-	_lastTrack = -1;
-	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->update();
-	_driver->reset();
-}
-
-void SoundPC98::beginFadeOut() {
-	if (!_driver->musicPlaying())
-		return;
-
-	for (int i = 0; i < 20; i++) {
-		_driver->fadeStep();
-		_vm->delay(32);
-	}
-	haltTrack();
-}
-
-void SoundPC98::playSoundEffect(uint8 track, uint8) {
-	if (!_sfxTrackData)
-		return;
-
-	_driver->loadSoundEffectData(_sfxTrackData, track);
-}
-
-void SoundPC98::updateVolumeSettings() {
-	if (!_driver)
-		return;
-
-	bool mute = false;
-	if (ConfMan.hasKey("mute"))
-		mute = ConfMan.getBool("mute");
-
-	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
-	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
-}
-
-//	KYRA 2
-
-SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
-	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false), _currentResourceSet(0) {
-	memset(&_resInfo, 0, sizeof(_resInfo));
-}
-
-SoundTownsPC98_v2::~SoundTownsPC98_v2() {
-	delete[] _musicTrackData;
-	delete[] _sfxTrackData;
-	delete _driver;
-	for (int i = 0; i < 3; i++)
-		initAudioResourceInfo(i, 0);
-}
-
-bool SoundTownsPC98_v2::init() {
-	_driver = new TownsPC98_AudioDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
-		TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns);
-
-	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
-		if (_resInfo[_currentResourceSet])
-			if (_resInfo[_currentResourceSet]->cdaTableSize)
-				_vm->checkCD();
-
-		// Initialize CD for audio
-		bool hasRealCD = g_system->getAudioCDManager()->open();
-
-		// FIXME: While checking for 'track1.XXX(X)' looks like
-		// a good idea, we should definitely not be doing this
-		// here. Basically our filenaming scheme could change
-		// or we could add support for other audio formats. Also
-		// this misses the possibility that we play the tracks
-		// right off CD. So we should find another way to
-		// check if we have access to CD audio.
-		Resource *r = _vm->resource();
-		if (_musicEnabled &&
-		    (hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
-		     || r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla")))
-				_musicEnabled = 2;
-		else
-			_musicEnabled = 1;
-		_useFmSfx = false;
-
-	} else {
-		_useFmSfx = true;
-	}
-
-	bool reslt = _driver->init();
-	updateVolumeSettings();
-	return reslt;
-}
-
-void SoundTownsPC98_v2::initAudioResourceInfo(int set, void *info) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		delete _resInfo[set];
-		_resInfo[set] = info ? new SoundResourceInfo_TownsPC98V2(*(SoundResourceInfo_TownsPC98V2*)info) : 0;
-	}
-}
-
-void SoundTownsPC98_v2::selectAudioResourceSet(int set) {
-	if (set >= kMusicIntro && set <= kMusicFinale) {
-		if (_resInfo[set])
-			_currentResourceSet = set;
-	}
-}
-
-bool SoundTownsPC98_v2::hasSoundFile(uint file) const {
-	if (file < res()->fileListSize)
-		return (res()->fileList[file] != 0);
-	return false;
-}
-
-void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
-	delete[] _sfxTrackData;
-	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
-}
-
-void SoundTownsPC98_v2::process() {
-	g_system->getAudioCDManager()->update();
-}
-
-void SoundTownsPC98_v2::playTrack(uint8 track) {
-	if (track == _lastTrack && _musicEnabled)
-		return;
-
-	int trackNum = -1;
-	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
-		for (uint i = 0; i < res()->cdaTableSize; i++) {
-			if (track == (uint8)READ_LE_UINT16(&res()->cdaTable[i * 2])) {
-				trackNum = (int)READ_LE_UINT16(&res()->cdaTable[i * 2 + 1]) - 1;
-				break;
-			}
-		}
-	}
-
-	beginFadeOut();
-
-	Common::String musicFile = res()->pattern ? Common::String::format(res()->pattern, track) : (res()->fileList ? res()->fileList[track] : 0);
-	if (musicFile.empty())
-		return;
-
-	delete[] _musicTrackData;
-
-	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
-	_driver->loadMusicData(_musicTrackData, true);
-
-	if (_musicEnabled == 2 && trackNum != -1) {
-		g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
-		g_system->getAudioCDManager()->update();
-	} else if (_musicEnabled) {
-		_driver->cont();
-	}
-
-	_lastTrack = track;
-}
-
-void SoundTownsPC98_v2::haltTrack() {
-	_lastTrack = -1;
-	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->update();
-	_driver->reset();
-}
-
-void SoundTownsPC98_v2::beginFadeOut() {
-	if (!_driver->musicPlaying())
-		return;
-
-	for (int i = 0; i < 20; i++) {
-		_driver->fadeStep();
-		_vm->delay(32);
-	}
-
-	haltTrack();
-}
-
-int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool) {
-	static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
-	static const char patternHOF[] = "%s.PCM";
-	static const char patternLOL[] = "%s.VOC";
-
-	int h = 0;
-	if (_currentSFX) {
-		while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
-			h++;
-
-		if (h >= kNumChannelHandles) {
-			h = 0;
-			while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
-				++h;
-			if (h < kNumChannelHandles)
-				voiceStop(&_soundChannels[h].handle);
-		}
-
-		if (h >= kNumChannelHandles)
-			return 0;
-	}
-
-	Common::String fileName = Common::String::format( _vm->game() == GI_LOL ? patternLOL : patternHOF, file);
-
-	uint8 *data = _vm->resource()->fileData(fileName.c_str(), 0);
-	uint8 *src = data;
-	if (!src)
-		return 0;
-
-	uint16 sfxRate = rates[READ_LE_UINT16(src)];
-	src += 2;
-	bool compressed = (READ_LE_UINT16(src) & 1) ? true : false;
-	src += 2;
-	uint32 outsize = READ_LE_UINT32(src);
-	uint8 *sfx = (uint8 *)malloc(outsize);
-	uint8 *dst = sfx;
-	src += 4;
-
-	if (compressed) {
-		for (uint32 i = outsize; i;) {
-			uint8 cnt = *src++;
-			if (cnt & 0x80) {
-				cnt &= 0x7F;
-				memset(dst, *src++, cnt);
-			} else {
-				memcpy(dst, src, cnt);
-				src += cnt;
-			}
-			dst += cnt;
-			i -= cnt;
-		}
-	} else {
-		memcpy(dst, src, outsize);
-	}
-
-	for (uint32 i = 0; i < outsize; i++) {
-		uint8 cmd = sfx[i];
-		if (cmd & 0x80) {
-			cmd = ~cmd;
-		} else {
-			cmd |= 0x80;
-			if (cmd == 0xFF)
-				cmd--;
-		}
-		if (cmd < 0x80)
-			cmd = 0x80 - cmd;
-		sfx[i] = cmd;
-	}
-
-	_currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
-	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h].handle, _currentSFX, -1, volume);
-	_soundChannels[h].priority = priority;
-	if (handle)
-		*handle = _soundChannels[h].handle;
-
-	delete[] data;
-	return 1;
-}
-
-void SoundTownsPC98_v2::playSoundEffect(uint8 track, uint8) {
-	if (!_useFmSfx || !_sfxTrackData)
-		return;
-
-	_driver->loadSoundEffectData(_sfxTrackData, track);
-}
-
-void SoundTownsPC98_v2::updateVolumeSettings() {
-	if (!_driver)
-		return;
-
-	bool mute = false;
-	if (ConfMan.hasKey("mute"))
-		mute = ConfMan.getBool("mute");
-
-	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
-	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
-}
-
 } // End of namespace Kyra
 
-#undef EUPHONY_FADEOUT_TICKS


Commit: 9144b8894e53aa12ecd4f313d52aaeed604a40b2
    https://github.com/scummvm/scummvm/commit/9144b8894e53aa12ecd4f313d52aaeed604a40b2
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-15T21:51:47+02:00

Commit Message:
KYRA: sound files/classes reorganization step #4

Separate drivers from their wrapper classes and move them into their own files

Changed paths:
  A engines/kyra/sound/drivers/adlib.cpp
  A engines/kyra/sound/drivers/adlib.h
  A engines/kyra/sound/drivers/audstream.cpp
  A engines/kyra/sound/drivers/midi.cpp
  A engines/kyra/sound/drivers/midi.h
  A engines/kyra/sound/drivers/pcspeaker.cpp
  R engines/kyra/sound/sound_pcspk.cpp
    engines/kyra/module.mk
    engines/kyra/sound/sound_adlib.cpp
    engines/kyra/sound/sound_digital_mr.cpp
    engines/kyra/sound/sound_digital_mr.h
    engines/kyra/sound/sound_midi.cpp


diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 0f2634b..a45c0ac 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -64,12 +64,15 @@ MODULE_OBJS := \
 	sound/sound_amiga_lok.o \
 	sound/sound_digital_mr.o \
 	sound/sound_midi.o \
-	sound/sound_pcspk.o \
 	sound/sound_pc98_lok.o \
 	sound/sound_pc98_v2.o \
 	sound/sound_towns_lok.o \
 	sound/sound.o \
 	sound/sound_lok.o \
+	sound/drivers/adlib.o \
+	sound/drivers/audstream.o \
+	sound/drivers/midi.o \
+	sound/drivers/pcspeaker.o \
 	text/text.o \
 	text/text_lok.o \
 	text/text_hof.o \
diff --git a/engines/kyra/sound/drivers/adlib.cpp b/engines/kyra/sound/drivers/adlib.cpp
new file mode 100644
index 0000000..de6b8a4
--- /dev/null
+++ b/engines/kyra/sound/drivers/adlib.cpp
@@ -0,0 +1,1951 @@
+/* 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.
+ *
+ * LGPL License
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "kyra/sound/drivers/adlib.h"
+#include "audio/fmopl.h"
+
+
+#define CALLBACKS_PER_SECOND 72
+
+namespace Kyra {
+
+AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
+	setupParserOpcodeTable();
+
+	_version = version;
+	_numPrograms = (_version == 1) ? 150 : ((_version == 4) ? 500 : 250);
+
+	_mixer = mixer;
+
+	_adlib = OPL::Config::create();
+	if (!_adlib || !_adlib->init())
+		error("Failed to create OPL");
+
+	memset(_channels, 0, sizeof(_channels));
+	_soundData = 0;
+	_soundDataSize = 0;
+
+	_vibratoAndAMDepthBits = _curRegOffset = 0;
+
+	_curChannel = _rhythmSectionBits = 0;
+	_rnd = 0x1234;
+
+	_tempo = 0;
+	_soundTrigger = 0;
+	_programStartTimeout = 0;
+
+	_callbackTimer = 0xFF;
+	_unkValue1 = _unkValue2 = _unkValue4 = _unkValue5 = 0;
+	_unkValue6 = _unkValue7 = _unkValue8 = _unkValue9 = _unkValue10 = 0;
+	_unkValue11 = _unkValue12 = _unkValue13 = _unkValue14 = _unkValue15 =
+	_unkValue16 = _unkValue17 = _unkValue18 = _unkValue19 = _unkValue20 = 0;
+
+	_tablePtr1 = _tablePtr2 = 0;
+
+	_syncJumpMask = 0;
+
+	_musicVolume = 0;
+	_sfxVolume = 0;
+
+	_sfxPointer = 0;
+
+	_programQueueStart = _programQueueEnd = 0;
+	_retrySounds = false;
+
+	_adlib->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::callback), CALLBACKS_PER_SECOND);
+}
+
+AdLibDriver::~AdLibDriver() {
+	delete _adlib;
+	_adlib = 0;
+}
+
+void AdLibDriver::setMusicVolume(uint8 volume) {
+	Common::StackLock lock(_mutex);
+
+	_musicVolume = volume;
+
+	for (uint i = 0; i < 6; ++i) {
+		Channel &chan = _channels[i];
+		chan.volumeModifier = volume;
+
+		const uint8 regOffset = _regOffset[i];
+
+		// Level Key Scaling / Total Level
+		writeOPL(0x40 + regOffset, calculateOpLevel1(chan));
+		writeOPL(0x43 + regOffset, calculateOpLevel2(chan));
+	}
+
+	// For now we use the music volume for both sfx and music in Kyra1 and EoB
+	if (_version < 4) {
+		_sfxVolume = volume;
+
+		for (uint i = 6; i < 9; ++i) {
+			Channel &chan = _channels[i];
+			chan.volumeModifier = volume;
+
+			const uint8 regOffset = _regOffset[i];
+
+			// Level Key Scaling / Total Level
+			writeOPL(0x40 + regOffset, calculateOpLevel1(chan));
+			writeOPL(0x43 + regOffset, calculateOpLevel2(chan));
+		}
+	}
+}
+
+void AdLibDriver::setSfxVolume(uint8 volume) {
+	// We only support sfx volume in version 4 games.
+	if (_version < 4)
+		return;
+
+	Common::StackLock lock(_mutex);
+
+	_sfxVolume = volume;
+
+	for (uint i = 6; i < 9; ++i) {
+		Channel &chan = _channels[i];
+		chan.volumeModifier = volume;
+
+		const uint8 regOffset = _regOffset[i];
+
+		// Level Key Scaling / Total Level
+		writeOPL(0x40 + regOffset, calculateOpLevel1(chan));
+		writeOPL(0x43 + regOffset, calculateOpLevel2(chan));
+	}
+}
+
+void AdLibDriver::initDriver() {
+	Common::StackLock lock(_mutex);
+	resetAdLibState();
+}
+
+void AdLibDriver::setSoundData(uint8 *data, uint32 size) {
+	Common::StackLock lock(_mutex);
+
+	// Drop all tracks that are still queued. These would point to the old
+	// sound data.
+	_programQueueStart = _programQueueEnd = 0;
+	memset(_programQueue, 0, sizeof(_programQueue));
+
+	if (_soundData) {
+		delete[] _soundData;
+		_soundData = _sfxPointer = 0;
+	}
+
+	_soundData = data;
+	_soundDataSize = size;
+}
+
+void AdLibDriver::queueTrack(int track, int volume) {
+	Common::StackLock lock(_mutex);
+
+	uint8 *trackData = getProgram(track);
+	if (!trackData)
+		return;
+
+	// Don't drop tracks in EoB. The queue is always full there if a couple of monsters are around.
+	// If we drop the incoming tracks we get no sound effects, but tons of warnings instead.
+	if (_version >= 3 && _programQueueEnd == _programQueueStart && _programQueue[_programQueueEnd].data != 0) {
+		warning("AdLibDriver: Program queue full, dropping track %d", track);
+		return;
+	}
+
+	_programQueue[_programQueueEnd] = QueueEntry(trackData, track, volume);
+	_programQueueEnd = (_programQueueEnd + 1) & 15;
+}
+
+bool AdLibDriver::isChannelPlaying(int channel) const {
+	Common::StackLock lock(_mutex);
+
+	assert(channel >= 0 && channel <= 9);
+	return (_channels[channel].dataptr != 0);
+}
+
+void AdLibDriver::stopAllChannels() {
+	Common::StackLock lock(_mutex);
+
+	for (int channel = 0; channel <= 9; ++channel) {
+		_curChannel = channel;
+
+		Channel &chan = _channels[_curChannel];
+		chan.priority = 0;
+		chan.dataptr = 0;
+
+		if (channel != 9)
+			noteOff(chan);
+	}
+	_retrySounds = false;
+}
+
+// timer callback
+
+void AdLibDriver::callback() {
+	Common::StackLock lock(_mutex);
+	if (_programStartTimeout)
+		--_programStartTimeout;
+	else
+		setupPrograms();
+	executePrograms();
+
+	uint8 temp = _callbackTimer;
+	_callbackTimer += _tempo;
+	if (_callbackTimer < temp) {
+		if (!(--_unkValue2)) {
+			_unkValue2 = _unkValue1;
+			++_unkValue4;
+		}
+	}
+}
+
+void AdLibDriver::setupPrograms() {
+	// If there is no program queued, we skip this.
+	if (_programQueueStart == _programQueueEnd)
+		return;
+
+	uint8 *ptr = _programQueue[_programQueueStart].data;
+
+	// The AdLib driver (in its old versions used for EOB) is not suitable for modern (fast) CPUs.
+	// The stop sound track (track 0 which has a priority of 50) will often still be busy when the
+	// next sound (with a lower priority) starts which will cause that sound to be skipped. We simply
+	// restart incoming sounds during stop sound execution.
+	// UPDATE: This stilly applies after introduction of the _programQueue.
+	QueueEntry retrySound;
+	if (_version < 3 && _programQueue[_programQueueStart].id == 0)
+		_retrySounds = true;
+	else if (_retrySounds)
+		retrySound = _programQueue[_programQueueStart];
+
+	// Adjust data in case we hit a sound effect.
+	adjustSfxData(ptr, _programQueue[_programQueueStart].volume);
+
+	// Clear the queue entry
+	_programQueue[_programQueueStart].data = 0;
+	_programQueueStart = (_programQueueStart + 1) & 15;
+
+	const int chan = *ptr++;
+	const int priority = *ptr++;
+
+	// Only start this sound if its priority is higher than the one
+	// already playing.
+
+	Channel &channel = _channels[chan];
+
+	if (priority >= channel.priority) {
+		initChannel(channel);
+		channel.priority = priority;
+		channel.dataptr = ptr;
+		channel.tempo = 0xFF;
+		channel.position = 0xFF;
+		channel.duration = 1;
+
+		if (chan <= 5)
+			channel.volumeModifier = _musicVolume;
+		else
+			channel.volumeModifier = _sfxVolume;
+
+		unkOutput2(chan);
+
+		// We need to wait two callback calls till we can start another track.
+		// This is (probably) required to assure that the sfx are started with
+		// the correct priority and velocity.
+		_programStartTimeout = 2;
+
+		retrySound = QueueEntry();
+	}
+
+	if (retrySound.data) {
+		debugC(9, kDebugLevelSound, "AdLibDriver::setupPrograms(): WORKAROUND - Restarting skipped sound %d)", retrySound.id);
+		queueTrack(retrySound.id, retrySound.volume);
+	}
+}
+
+void AdLibDriver::adjustSfxData(uint8 *ptr, int volume) {
+	// Check whether we need to reset the data of an old sfx which has been
+	// started.
+	if (_sfxPointer) {
+		_sfxPointer[1] = _sfxPriority;
+		_sfxPointer[3] = _sfxVelocity;
+		_sfxPointer = 0;
+	}
+
+	// Only music tracks are started on channel 9, thus we need to make sure
+	// we do not have a music track here.
+	if (*ptr == 9)
+		return;
+
+	// Store the pointer so we can reset the data when a new program is started.
+	_sfxPointer = ptr;
+
+	// Store the old values.
+	_sfxPriority = ptr[1];
+	_sfxVelocity = ptr[3];
+
+	// Adjust the values.
+	if (volume != 0xFF) {
+		if (_version >= 3) {
+			int newVal = ((((ptr[3]) + 63) * volume) >> 8) & 0xFF;
+			ptr[3] = -newVal + 63;
+			ptr[1] = ((ptr[1] * volume) >> 8) & 0xFF;
+		} else {
+			int newVal = ((_sfxVelocity << 2) ^ 0xFF) * volume;
+			ptr[3] = (newVal >> 10) ^ 0x3F;
+			ptr[1] = newVal >> 11;
+		}
+	}
+}
+
+// A few words on opcode parsing and timing:
+//
+// First of all, We simulate a timer callback 72 times per second. Each timeout
+// we update each channel that has something to play.
+//
+// Each channel has its own individual tempo, which is added to its position.
+// This will frequently cause the position to "wrap around" but that is
+// intentional. In fact, it's the signal to go ahead and do more stuff with
+// that channel.
+//
+// Each channel also has a duration, indicating how much time is left on the
+// its current task. This duration is decreased by one. As long as it still has
+// not reached zero, the only thing that can happen is that the note is turned
+// off depending on manual or automatic note spacing. Once the duration reaches
+// zero, a new set of musical opcodes are executed.
+//
+// An opcode is one byte, followed by a variable number of parameters. Since
+// most opcodes have at least one one-byte parameter, we read that as well. Any
+// opcode that doesn't have that one parameter is responsible for moving the
+// data pointer back again.
+//
+// If the most significant bit of the opcode is 1, it's a function; call it.
+// The opcode functions return either 0 (continue), 1 (stop) or 2 (stop, and do
+// not run the effects callbacks).
+//
+// If the most significant bit of the opcode is 0, it's a note, and the first
+// parameter is its duration. (There are cases where the duration is modified
+// but that's an exception.) The note opcode is assumed to return 1, and is the
+// last opcode unless its duration is zero.
+//
+// Finally, most of the times that the callback is called, it will invoke the
+// effects callbacks. The final opcode in a set can prevent this, if it's a
+// function and it returns anything other than 1.
+
+void AdLibDriver::executePrograms() {
+	// Each channel runs its own program. There are ten channels: One for
+	// each AdLib channel (0-8), plus one "control channel" (9) which is
+	// the one that tells the other channels what to do.
+
+	// This is where we ensure that channels that are made to jump "in
+	// sync" do so.
+
+	if (_syncJumpMask) {
+		bool forceUnlock = true;
+
+		for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
+			if ((_syncJumpMask & (1 << _curChannel)) == 0)
+				continue;
+
+			if (_channels[_curChannel].dataptr && !_channels[_curChannel].lock)
+				forceUnlock = false;
+		}
+
+		if (forceUnlock) {
+			for (_curChannel = 9; _curChannel >= 0; --_curChannel)
+				if (_syncJumpMask & (1 << _curChannel))
+					_channels[_curChannel].lock = false;
+		}
+	}
+
+	for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
+		int result = 1;
+
+		if (!_channels[_curChannel].dataptr)
+			continue;
+
+		if (_channels[_curChannel].lock && (_syncJumpMask & (1 << _curChannel)))
+			continue;
+
+		Channel &channel = _channels[_curChannel];
+		if (_curChannel == 9)
+			_curRegOffset = 0;
+		else
+			_curRegOffset = _regOffset[_curChannel];
+
+		if (channel.tempoReset)
+			channel.tempo = _tempo;
+
+		uint8 backup = channel.position;
+		channel.position += channel.tempo;
+		if (channel.position < backup) {
+			if (--channel.duration) {
+				if (channel.duration == channel.spacing2)
+					noteOff(channel);
+				if (channel.duration == channel.spacing1 && _curChannel != 9)
+					noteOff(channel);
+			} else {
+				// An opcode is not allowed to modify its own
+				// data pointer except through the 'dataptr'
+				// parameter. To enforce that, we have to work
+				// on a copy of the data pointer.
+				//
+				// This fixes a subtle music bug where the
+				// wrong music would play when getting the
+				// quill in Kyra 1.
+				const uint8 *dataptr = channel.dataptr;
+				while (dataptr) {
+					uint8 opcode = *dataptr++;
+					uint8 param = *dataptr++;
+
+					if (opcode & 0x80) {
+						opcode &= 0x7F;
+						if (opcode >= _parserOpcodeTableSize)
+							opcode = _parserOpcodeTableSize - 1;
+						debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", _parserOpcodeTable[opcode].name, opcode, _curChannel);
+						result = (this->*(_parserOpcodeTable[opcode].function))(dataptr, channel, param);
+						channel.dataptr = dataptr;
+						if (result)
+							break;
+					} else {
+						debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, param, _curChannel);
+						setupNote(opcode, channel);
+						noteOn(channel);
+						setupDuration(param, channel);
+						if (param) {
+							// We need to make sure we are always running the
+							// effects after this. Otherwise some sounds are
+							// wrong. Like the sfx when bumping into a wall in
+							// LoL.
+							result = 1;
+							channel.dataptr = dataptr;
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		if (result == 1) {
+			if (channel.primaryEffect)
+				(this->*(channel.primaryEffect))(channel);
+			if (channel.secondaryEffect)
+				(this->*(channel.secondaryEffect))(channel);
+		}
+	}
+}
+
+//
+
+void AdLibDriver::resetAdLibState() {
+	debugC(9, kDebugLevelSound, "resetAdLibState()");
+	_rnd = 0x1234;
+
+	// Authorize the control of the waveforms
+	writeOPL(0x01, 0x20);
+
+	// Select FM music mode
+	writeOPL(0x08, 0x00);
+
+	// I would guess the main purpose of this is to turn off the rhythm,
+	// thus allowing us to use 9 melodic voices instead of 6.
+	writeOPL(0xBD, 0x00);
+
+	int loop = 10;
+	while (loop--) {
+		if (loop != 9) {
+			// Silence the channel
+			writeOPL(0x40 + _regOffset[loop], 0x3F);
+			writeOPL(0x43 + _regOffset[loop], 0x3F);
+		}
+		initChannel(_channels[loop]);
+	}
+}
+
+// Old calling style: output0x388(0xABCD)
+// New calling style: writeOPL(0xAB, 0xCD)
+
+void AdLibDriver::writeOPL(byte reg, byte val) {
+	_adlib->writeReg(reg, val);
+}
+
+void AdLibDriver::initChannel(Channel &channel) {
+	debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels));
+	memset(&channel.dataptr, 0, sizeof(Channel) - ((char *)&channel.dataptr - (char *)&channel));
+
+	channel.tempo = 0xFF;
+	channel.priority = 0;
+	// normally here are nullfuncs but we set 0 for now
+	channel.primaryEffect = 0;
+	channel.secondaryEffect = 0;
+	channel.spacing1 = 1;
+	channel.lock = false;
+}
+
+void AdLibDriver::noteOff(Channel &channel) {
+	debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels));
+
+	// The control channel has no corresponding AdLib channel
+
+	if (_curChannel >= 9)
+		return;
+
+	// When the rhythm section is enabled, channels 6, 7 and 8 are special.
+
+	if (_rhythmSectionBits && _curChannel >= 6)
+		return;
+
+	// This means the "Key On" bit will always be 0
+	channel.regBx &= 0xDF;
+
+	// Octave / F-Number / Key-On
+	writeOPL(0xB0 + _curChannel, channel.regBx);
+}
+
+void AdLibDriver::unkOutput2(uint8 chan) {
+	debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan);
+
+	// The control channel has no corresponding AdLib channel
+
+	if (chan >= 9)
+		return;
+
+	// I believe this has to do with channels 6, 7, and 8 being special
+	// when AdLib's rhythm section is enabled.
+
+	if (_rhythmSectionBits && chan >= 6)
+		return;
+
+	uint8 offset = _regOffset[chan];
+
+	// The channel is cleared: First the attack/delay rate, then the
+	// sustain level/release rate, and finally the note is turned off.
+
+	writeOPL(0x60 + offset, 0xFF);
+	writeOPL(0x63 + offset, 0xFF);
+
+	writeOPL(0x80 + offset, 0xFF);
+	writeOPL(0x83 + offset, 0xFF);
+
+	writeOPL(0xB0 + chan, 0x00);
+
+	// ...and then the note is turned on again, with whatever value is
+	// still lurking in the A0 + chan register, but everything else -
+	// including the two most significant frequency bit, and the octave -
+	// set to zero.
+	//
+	// This is very strange behavior, and causes problems with the ancient
+	// FMOPL code we borrowed from AdPlug. I've added a workaround. See
+	// audio/softsynth/opl/mame.cpp for more details.
+	//
+	// Fortunately, the more modern DOSBox FMOPL code does not seem to have
+	// any trouble with this.
+
+	writeOPL(0xB0 + chan, 0x20);
+}
+
+// I believe this is a random number generator. It actually does seem to
+// generate an even distribution of almost all numbers from 0 through 65535,
+// though in my tests some numbers were never generated.
+
+uint16 AdLibDriver::getRandomNr() {
+	_rnd += 0x9248;
+	uint16 lowBits = _rnd & 7;
+	_rnd >>= 3;
+	_rnd |= (lowBits << 13);
+	return _rnd;
+}
+
+void AdLibDriver::setupDuration(uint8 duration, Channel &channel) {
+	debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels));
+	if (channel.durationRandomness) {
+		channel.duration = duration + (getRandomNr() & channel.durationRandomness);
+		return;
+	}
+	if (channel.fractionalSpacing)
+		channel.spacing2 = (duration >> 3) * channel.fractionalSpacing;
+	channel.duration = duration;
+}
+
+// This function may or may not play the note. It's usually followed by a call
+// to noteOn(), which will always play the current note.
+
+void AdLibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) {
+	debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels));
+
+	if (_curChannel >= 9)
+		return;
+
+	channel.rawNote = rawNote;
+
+	int8 note = (rawNote & 0x0F) + channel.baseNote;
+	int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F;
+
+	// There are only twelve notes. If we go outside that, we have to
+	// adjust the note and octave.
+
+	if (note >= 12) {
+		note -= 12;
+		octave++;
+	} else if (note < 0) {
+		note += 12;
+		octave--;
+	}
+
+	// The calculation of frequency looks quite different from the original
+	// disassembly at a first glance, but when you consider that the
+	// largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if
+	// baseFreq is unsigned), freq is still a 10-bit value, just as it
+	// should be to fit in the Ax and Bx registers.
+	//
+	// If it were larger than that, it could have overflowed into the
+	// octave bits, and that could possibly have been used in some sound.
+	// But as it is now, I can't see any way it would happen.
+
+	uint16 freq = _freqTable[note] + channel.baseFreq;
+
+	// When called from callback 41, the behavior is slightly different:
+	// We adjust the frequency, even when channel.pitchBend is 0.
+
+	if (channel.pitchBend || flag) {
+		const uint8 *table;
+
+		if (channel.pitchBend >= 0) {
+			table = _pitchBendTables[(channel.rawNote & 0x0F) + 2];
+			freq += table[channel.pitchBend];
+		} else {
+			table = _pitchBendTables[channel.rawNote & 0x0F];
+			freq -= table[-channel.pitchBend];
+		}
+	}
+
+	channel.regAx = freq & 0xFF;
+	channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03);
+
+	// Keep the note on or off
+	writeOPL(0xA0 + _curChannel, channel.regAx);
+	writeOPL(0xB0 + _curChannel, channel.regBx);
+}
+
+void AdLibDriver::setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel) {
+	debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels));
+
+	if (_curChannel >= 9)
+		return;
+
+	// Amplitude Modulation / Vibrato / Envelope Generator Type /
+	// Keyboard Scaling Rate / Modulator Frequency Multiple
+	writeOPL(0x20 + regOffset, *dataptr++);
+	writeOPL(0x23 + regOffset, *dataptr++);
+
+	uint8 temp = *dataptr++;
+
+	// Feedback / Algorithm
+
+	// It is very likely that _curChannel really does refer to the same
+	// channel as regOffset, but there's only one Cx register per channel.
+
+	writeOPL(0xC0 + _curChannel, temp);
+
+	// The algorithm bit. I don't pretend to understand this fully, but
+	// "If set to 0, operator 1 modulates operator 2. In this case,
+	// operator 2 is the only one producing sound. If set to 1, both
+	// operators produce sound directly. Complex sounds are more easily
+	// created if the algorithm is set to 0."
+
+	channel.twoChan = temp & 1;
+
+	// Waveform Select
+	writeOPL(0xE0 + regOffset, *dataptr++);
+	writeOPL(0xE3 + regOffset, *dataptr++);
+
+	channel.opLevel1 = *dataptr++;
+	channel.opLevel2 = *dataptr++;
+
+	// Level Key Scaling / Total Level
+	writeOPL(0x40 + regOffset, calculateOpLevel1(channel));
+	writeOPL(0x43 + regOffset, calculateOpLevel2(channel));
+
+	// Attack Rate / Decay Rate
+	writeOPL(0x60 + regOffset, *dataptr++);
+	writeOPL(0x63 + regOffset, *dataptr++);
+
+	// Sustain Level / Release Rate
+	writeOPL(0x80 + regOffset, *dataptr++);
+	writeOPL(0x83 + regOffset, *dataptr++);
+}
+
+// Apart from playing the note, this function also updates the variables for
+// primary effect 2.
+
+void AdLibDriver::noteOn(Channel &channel) {
+	debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels));
+
+	// The "note on" bit is set, and the current note is played.
+
+	if (_curChannel >= 9)
+		return;
+
+	channel.regBx |= 0x20;
+	writeOPL(0xB0 + _curChannel, channel.regBx);
+
+	int8 shift = 9 - channel.unk33;
+	uint16 temp = channel.regAx | (channel.regBx << 8);
+	channel.unk37 = ((temp & 0x3FF) >> shift) & 0xFF;
+	channel.unk38 = channel.unk36;
+}
+
+void AdLibDriver::adjustVolume(Channel &channel) {
+	debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels));
+
+	if (_curChannel >= 9)
+		return;
+
+	// Level Key Scaling / Total Level
+
+	writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel));
+	if (channel.twoChan)
+		writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel));
+}
+
+// This is presumably only used for some sound effects, e.g. Malcolm blowing up
+// the trees in the intro (but not the effect where he "booby-traps" the big
+// tree) and turning Kallak to stone. Related functions and variables:
+//
+// update_setupPrimaryEffect1()
+//    - Initializes unk29, unk30 and unk31
+//    - unk29 is not further modified
+//    - unk30 is not further modified, except by update_removePrimaryEffect1()
+//
+// update_removePrimaryEffect1()
+//    - Deinitializes unk30
+//
+// unk29 - determines how often the notes are played
+// unk30 - modifies the frequency
+// unk31 - determines how often the notes are played
+
+void AdLibDriver::primaryEffect1(Channel &channel) {
+	debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel);
+
+	if (_curChannel >= 9)
+		return;
+
+	uint8 temp = channel.unk31;
+	channel.unk31 += channel.unk29;
+	if (channel.unk31 >= temp)
+		return;
+
+	// Initialize unk1 to the current frequency
+	int16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx;
+
+	// This is presumably to shift the "note on" bit so far to the left
+	// that it won't be affected by any of the calculations below.
+	int16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C);
+
+	int16 unk3 = (int16)channel.unk30;
+
+	if (unk3 >= 0) {
+		unk1 += unk3;
+		if (unk1 >= 734) {
+			// The new frequency is too high. Shift it down and go
+			// up one octave.
+			unk1 >>= 1;
+			if (!(unk1 & 0x3FF))
+				++unk1;
+			unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF);
+			unk2 &= 0xFF1C;
+		}
+	} else {
+		unk1 += unk3;
+		if (unk1 < 388) {
+			// The new frequency is too low. Shift it up and go
+			// down one octave.
+			unk1 <<= 1;
+			if (!(unk1 & 0x3FF))
+				--unk1;
+			unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF);
+			unk2 &= 0xFF1C;
+		}
+	}
+
+	// Make sure that the new frequency is still a 10-bit value.
+	unk1 &= 0x3FF;
+
+	writeOPL(0xA0 + _curChannel, unk1 & 0xFF);
+	channel.regAx = unk1 & 0xFF;
+
+	// Shift down the "note on" bit again.
+	uint8 value = unk1 >> 8;
+	value |= (unk2 >> 8) & 0xFF;
+	value |= unk2 & 0xFF;
+
+	writeOPL(0xB0 + _curChannel, value);
+	channel.regBx = value;
+}
+
+// This is presumably only used for some sound effects, e.g. Malcolm entering
+// and leaving Kallak's hut. Related functions and variables:
+//
+// update_setupPrimaryEffect2()
+//    - Initializes unk32, unk33, unk34, unk35 and unk36
+//    - unk32 is not further modified
+//    - unk33 is not further modified
+//    - unk34 is a countdown that gets reinitialized to unk35 on zero
+//    - unk35 is based on unk34 and not further modified
+//    - unk36 is not further modified
+//
+// noteOn()
+//    - Plays the current note
+//    - Updates unk37 with a new (lower?) frequency
+//    - Copies unk36 to unk38. The unk38 variable is a countdown.
+//
+// unk32 - determines how often the notes are played
+// unk33 - modifies the frequency
+// unk34 - countdown, updates frequency on zero
+// unk35 - initializer for unk34 countdown
+// unk36 - initializer for unk38 countdown
+// unk37 - frequency
+// unk38 - countdown, begins playing on zero
+// unk41 - determines how often the notes are played
+//
+// Note that unk41 is never initialized. Not that it should matter much, but it
+// is a bit sloppy.
+
+void AdLibDriver::primaryEffect2(Channel &channel) {
+	debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel);
+
+	if (_curChannel >= 9)
+		return;
+
+	if (channel.unk38) {
+		--channel.unk38;
+		return;
+	}
+
+	uint8 temp = channel.unk41;
+	channel.unk41 += channel.unk32;
+	if (channel.unk41 < temp) {
+		uint16 unk1 = channel.unk37;
+		if (!(--channel.unk34)) {
+			unk1 ^= 0xFFFF;
+			++unk1;
+			channel.unk37 = unk1;
+			channel.unk34 = channel.unk35;
+		}
+
+		uint16 unk2 = (channel.regAx | (channel.regBx << 8)) & 0x3FF;
+		unk2 += unk1;
+
+		channel.regAx = unk2 & 0xFF;
+		channel.regBx = (channel.regBx & 0xFC) | (unk2 >> 8);
+
+		// Octave / F-Number / Key-On
+		writeOPL(0xA0 + _curChannel, channel.regAx);
+		writeOPL(0xB0 + _curChannel, channel.regBx);
+	}
+}
+
+// I don't know where this is used. The same operation is performed several
+// times on the current channel, using a chunk of the _soundData[] buffer for
+// parameters. The parameters are used starting at the end of the chunk.
+//
+// Since we use _curRegOffset to specify the final register, it's quite
+// unlikely that this function is ever used to play notes. It's probably only
+// used to modify the sound. Another thing that supports this idea is that it
+// can be combined with any of the effects callbacks above.
+//
+// Related functions and variables:
+//
+// update_setupSecondaryEffect1()
+//    - Initialies unk18, unk19, unk20, unk21, unk22 and offset
+//    - unk19 is not further modified
+//    - unk20 is not further modified
+//    - unk22 is not further modified
+//    - offset is not further modified
+//
+// unk18 -  determines how often the operation is performed
+// unk19 -  determines how often the operation is performed
+// unk20 -  the start index into the data chunk
+// unk21 -  the current index into the data chunk
+// unk22 -  the operation to perform
+// offset - the offset to the data chunk
+
+void AdLibDriver::secondaryEffect1(Channel &channel) {
+	debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel);
+
+	if (_curChannel >= 9)
+		return;
+
+	uint8 temp = channel.unk18;
+	channel.unk18 += channel.unk19;
+	if (channel.unk18 < temp) {
+		if (--channel.unk21 < 0)
+			channel.unk21 = channel.unk20;
+		writeOPL(channel.unk22 + _curRegOffset, _soundData[channel.offset + channel.unk21]);
+	}
+}
+
+uint8 AdLibDriver::calculateOpLevel1(Channel &channel) {
+	int8 value = channel.opLevel1 & 0x3F;
+
+	if (channel.twoChan) {
+		value += channel.opExtraLevel1;
+		value += channel.opExtraLevel2;
+
+		uint16 level3 = (channel.opExtraLevel3 ^ 0x3F) * channel.volumeModifier;
+		if (level3) {
+			level3 += 0x3F;
+			level3 >>= 8;
+		}
+
+		value += level3 ^ 0x3F;
+	}
+
+	value = CLIP<int8>(value, 0, 0x3F);
+
+	if (!channel.volumeModifier)
+		value = 0x3F;
+
+	// Preserve the scaling level bits from opLevel1
+
+	return checkValue(value) | (channel.opLevel1 & 0xC0);
+}
+
+uint8 AdLibDriver::calculateOpLevel2(Channel &channel) {
+	int8 value = channel.opLevel2 & 0x3F;
+
+	value += channel.opExtraLevel1;
+	value += channel.opExtraLevel2;
+
+	uint16 level3 = (channel.opExtraLevel3 ^ 0x3F) * channel.volumeModifier;
+	if (level3) {
+		level3 += 0x3F;
+		level3 >>= 8;
+	}
+
+	value += level3 ^ 0x3F;
+
+	value = CLIP<int8>(value, 0, 0x3F);
+
+	if (!channel.volumeModifier)
+		value = 0x3F;
+
+	// Preserve the scaling level bits from opLevel2
+
+	return checkValue(value) | (channel.opLevel2 & 0xC0);
+}
+
+// parser opcodes
+
+int AdLibDriver::update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.repeatCounter = value;
+	return 0;
+}
+
+int AdLibDriver::update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	++dataptr;
+	if (--channel.repeatCounter) {
+		int16 add = READ_LE_UINT16(dataptr - 2);
+		dataptr += add;
+	}
+	return 0;
+}
+
+int AdLibDriver::update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	if (value == 0xFF)
+		return 0;
+
+	const uint8 *ptr = getProgram(value);
+
+	// In case we encounter an invalid program we simply ignore it and do
+	// nothing instead. The original did not care about invalid programs and
+	// simply tried to play them anyway... But to avoid crashes due we ingore
+	// them.
+	// This, for example, happens in the Lands of Lore intro when Scotia gets
+	// the ring in the intro.
+	if (!ptr) {
+		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupProgram: Invalid program %d specified", value);
+		return 0;
+	}
+
+	uint8 chan = *ptr++;
+	uint8 priority = *ptr++;
+
+	Channel &channel2 = _channels[chan];
+
+	if (priority >= channel2.priority) {
+		// We keep new tracks from being started for two further iterations of
+		// the callback. This assures the correct velocity is used for this
+		// program.
+		_programStartTimeout = 2;
+		initChannel(channel2);
+		channel2.priority = priority;
+		channel2.dataptr = ptr;
+		channel2.tempo = 0xFF;
+		channel2.position = 0xFF;
+		channel2.duration = 1;
+
+		if (chan <= 5)
+			channel2.volumeModifier = _musicVolume;
+		else
+			channel2.volumeModifier = _sfxVolume;
+
+		unkOutput2(chan);
+	}
+
+	return 0;
+}
+
+int AdLibDriver::update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.spacing1 = value;
+	return 0;
+}
+
+int AdLibDriver::update_jump(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
+	if (_version == 1)
+		dataptr = _soundData + add - 191;
+	else
+		dataptr += add;
+	if (_syncJumpMask & (1 << (&channel - _channels)))
+		channel.lock = true;
+	return 0;
+}
+
+int AdLibDriver::update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
+	channel.dataptrStack[channel.dataptrStackPos++] = dataptr;
+	if (_version < 3)
+		dataptr = _soundData + add - 191;
+	else
+		dataptr += add;
+	return 0;
+}
+
+int AdLibDriver::update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	dataptr = channel.dataptrStack[--channel.dataptrStackPos];
+	return 0;
+}
+
+int AdLibDriver::update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.baseOctave = value;
+	return 0;
+}
+
+int AdLibDriver::update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.priority = 0;
+	if (_curChannel != 9)
+		noteOff(channel);
+	dataptr = 0;
+	return 2;
+}
+
+int AdLibDriver::update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	setupDuration(value, channel);
+	noteOff(channel);
+	return (value != 0);
+}
+
+int AdLibDriver::update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	writeOPL(value, *dataptr++);
+	return 0;
+}
+
+int AdLibDriver::update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	setupNote(value, channel);
+	value = *dataptr++;
+	setupDuration(value, channel);
+	return (value != 0);
+}
+
+int AdLibDriver::update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.baseNote = value;
+	return 0;
+}
+
+int AdLibDriver::update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.unk18 = value;
+	channel.unk19 = value;
+	channel.unk20 = channel.unk21 = *dataptr++;
+	channel.unk22 = *dataptr++;
+	// WORKAROUND: The original code reads a true offset which later gets translated via xlat (in
+	// the current segment). This means that the outcome depends on the sound data offset.
+	// Unfortunately this offset is different in most implementations of the audio driver and
+	// probably also different from the offset assumed by the sequencer.
+	// It seems that the driver assumes an offset of 191 which is wrong for all the game driver
+	// implementations.
+	// This bug has probably not been noticed, since the effect is hardly used and the sounds are
+	// not necessarily worse. I noticed the difference between ScummVM and DOSBox for the EOB II
+	// teleporter sound. I also found the location of the table which is supposed to be used here
+	// (simple enough: it is located at the end of the track after the 0x88 ending opcode).
+	// Teleporters in EOB I and II now sound exactly the same which I am sure was the intended way,
+	// since the sound data is exactly the same.
+	// In DOSBox the teleporters will sound different in EOB I and II, due to different sound
+	// data offsets.
+	channel.offset = READ_LE_UINT16(dataptr) - 191; dataptr += 2;
+	channel.secondaryEffect = &AdLibDriver::secondaryEffect1;
+	return 0;
+}
+
+int AdLibDriver::update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	Channel &channel2 = _channels[value];
+	channel2.duration = 0;
+	channel2.priority = 0;
+	channel2.dataptr = 0;
+	return 0;
+}
+
+int AdLibDriver::update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	const uint8 *ptr = getProgram(value);
+
+	// Safety check in case an invalid program is specified. This would make
+	// getProgram return a nullptr and thus cause invalid memory reads.
+	if (!ptr) {
+		debugC(3, kDebugLevelSound, "AdLibDriver::update_waitForEndOfProgram: Invalid program %d specified", value);
+		return 0;
+	}
+
+	uint8 chan = *ptr;
+
+	if (!_channels[chan].dataptr)
+		return 0;
+
+	dataptr -= 2;
+	return 2;
+}
+
+int AdLibDriver::update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	const uint8 *instrument = getInstrument(value);
+
+	// We add a safety check to avoid setting up invalid instruments. This is
+	// not done in the original. However, to avoid crashes due to invalid
+	// memory reads we simply ignore the request.
+	// This happens, for example, in Hand of Fate when using the swampsnake
+	// potion on Zanthia to scare off the rat in the cave in the first chapter
+	// of the game.
+	if (!instrument) {
+		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupInstrument: Invalid instrument %d specified", value);
+		return 0;
+	}
+
+	setupInstrument(_curRegOffset, instrument, channel);
+	return 0;
+}
+
+int AdLibDriver::update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.unk29 = value;
+	channel.unk30 = READ_BE_UINT16(dataptr);
+	dataptr += 2;
+	channel.primaryEffect = &AdLibDriver::primaryEffect1;
+	channel.unk31 = 0xFF;
+	return 0;
+}
+
+int AdLibDriver::update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	channel.primaryEffect = 0;
+	channel.unk30 = 0;
+	return 0;
+}
+
+int AdLibDriver::update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.baseFreq = value;
+	return 0;
+}
+
+int AdLibDriver::update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.unk32 = value;
+	channel.unk33 = *dataptr++;
+	uint8 temp = *dataptr++;
+	channel.unk34 = temp + 1;
+	channel.unk35 = temp << 1;
+	channel.unk36 = *dataptr++;
+	channel.primaryEffect = &AdLibDriver::primaryEffect2;
+	return 0;
+}
+
+int AdLibDriver::update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.priority = value;
+	return 0;
+}
+
+int AdLibDriver::updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	value >>= 1;
+	_unkValue1 = _unkValue2 = value;
+	_callbackTimer = 0xFF;
+	_unkValue4 = _unkValue5 = 0;
+	return 0;
+}
+
+int AdLibDriver::updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	if (_unkValue5) {
+		if (_unkValue4 & value) {
+			_unkValue5 = 0;
+			return 0;
+		}
+	}
+
+	if (!(value & _unkValue4))
+		++_unkValue5;
+
+	dataptr -= 2;
+	channel.duration = 1;
+	return 2;
+}
+
+int AdLibDriver::update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.opExtraLevel1 = value;
+	adjustVolume(channel);
+	return 0;
+}
+
+int AdLibDriver::update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	setupDuration(value, channel);
+	return (value != 0);
+}
+
+int AdLibDriver::update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	setupDuration(value, channel);
+	noteOn(channel);
+	return (value != 0);
+}
+
+int AdLibDriver::update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.fractionalSpacing = value & 7;
+	return 0;
+}
+
+int AdLibDriver::update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	_tempo = value;
+	return 0;
+}
+
+int AdLibDriver::update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	channel.secondaryEffect = 0;
+	return 0;
+}
+
+int AdLibDriver::update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.tempo = value;
+	return 0;
+}
+
+int AdLibDriver::update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.opExtraLevel3 = value;
+	return 0;
+}
+
+int AdLibDriver::update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	int channelBackUp = _curChannel;
+
+	_curChannel = value;
+	Channel &channel2 = _channels[value];
+	channel2.opExtraLevel2 = *dataptr++;
+	adjustVolume(channel2);
+
+	_curChannel = channelBackUp;
+	return 0;
+}
+
+int AdLibDriver::update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	int channelBackUp = _curChannel;
+
+	_curChannel = value;
+	Channel &channel2 = _channels[value];
+	channel2.opExtraLevel2 += *dataptr++;
+	adjustVolume(channel2);
+
+	_curChannel = channelBackUp;
+	return 0;
+}
+
+// Apart from initializing to zero, these two functions are the only ones that
+// modify _vibratoAndAMDepthBits.
+
+int AdLibDriver::update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	if (value & 1)
+		_vibratoAndAMDepthBits |= 0x80;
+	else
+		_vibratoAndAMDepthBits &= 0x7F;
+
+	writeOPL(0xBD, _vibratoAndAMDepthBits);
+	return 0;
+}
+
+int AdLibDriver::update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	if (value & 1)
+		_vibratoAndAMDepthBits |= 0x40;
+	else
+		_vibratoAndAMDepthBits &= 0xBF;
+
+	writeOPL(0xBD, _vibratoAndAMDepthBits);
+	return 0;
+}
+
+int AdLibDriver::update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.opExtraLevel1 += value;
+	adjustVolume(channel);
+	return 0;
+}
+
+int AdLibDriver::updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	int channelBackUp = _curChannel;
+
+	_curChannel = value;
+	Channel &channel2 = _channels[value];
+	channel2.duration = channel2.priority = 0;
+	channel2.dataptr = 0;
+	channel2.opExtraLevel2 = 0;
+
+	if (value != 9) {
+		uint8 outValue = _regOffset[value];
+
+		// Feedback strength / Connection type
+		writeOPL(0xC0 + _curChannel, 0x00);
+
+		// Key scaling level / Operator output level
+		writeOPL(0x43 + outValue, 0x3F);
+
+		// Sustain Level / Release Rate
+		writeOPL(0x83 + outValue, 0xFF);
+
+		// Key On / Octave / Frequency
+		writeOPL(0xB0 + _curChannel, 0x00);
+	}
+
+	_curChannel = channelBackUp;
+	return 0;
+}
+
+int AdLibDriver::updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	if (_curChannel >= 9)
+		return 0;
+
+	uint16 unk = *dataptr++;
+	unk |= value << 8;
+	unk &= getRandomNr();
+
+	uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx;
+	unk2 += unk;
+	unk2 |= ((channel.regBx & 0x20) << 8);
+
+	// Frequency
+	writeOPL(0xA0 + _curChannel, unk2 & 0xFF);
+
+	// Key On / Octave / Frequency
+	writeOPL(0xB0 + _curChannel, (unk2 & 0xFF00) >> 8);
+
+	return 0;
+}
+
+int AdLibDriver::update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	channel.primaryEffect = 0;
+	return 0;
+}
+
+int AdLibDriver::update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.pitchBend = value;
+	setupNote(channel.rawNote, channel, true);
+	return 0;
+}
+
+int AdLibDriver::update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	channel.tempo = _tempo;
+	return 0;
+}
+
+int AdLibDriver::update_nop(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	return 0;
+}
+
+int AdLibDriver::update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.durationRandomness = value;
+	return 0;
+}
+
+int AdLibDriver::update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	int tempo = channel.tempo + (int8)value;
+
+	if (tempo <= 0)
+		tempo = 1;
+	else if (tempo > 255)
+		tempo = 255;
+
+	channel.tempo = tempo;
+	return 0;
+}
+
+int AdLibDriver::updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	uint8 entry = *dataptr++;
+	_tablePtr1 = _unkTable2[entry++];
+	_tablePtr2 = _unkTable2[entry];
+	if (value == 2) {
+		// Frequency
+		writeOPL(0xA0, _tablePtr2[0]);
+	}
+	return 0;
+}
+
+int AdLibDriver::update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	int channelBackUp = _curChannel;
+	int regOffsetBackUp = _curRegOffset;
+
+	_curChannel = 6;
+	_curRegOffset = _regOffset[6];
+
+	const uint8 *instrument;
+	instrument = getInstrument(value);
+	if (instrument) {
+		setupInstrument(_curRegOffset, instrument, channel);
+	} else {
+		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 6 specified", value);
+	}
+	_unkValue6 = channel.opLevel2;
+
+	_curChannel = 7;
+	_curRegOffset = _regOffset[7];
+
+	instrument = getInstrument(*dataptr++);
+	if (instrument) {
+		setupInstrument(_curRegOffset, instrument, channel);
+	} else {
+		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 7 specified", value);
+	}
+	_unkValue7 = channel.opLevel1;
+	_unkValue8 = channel.opLevel2;
+
+	_curChannel = 8;
+	_curRegOffset = _regOffset[8];
+
+	instrument = getInstrument(*dataptr++);
+	if (instrument) {
+		setupInstrument(_curRegOffset, instrument, channel);
+	} else {
+		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 8 specified", value);
+	}
+	_unkValue9 = channel.opLevel1;
+	_unkValue10 = channel.opLevel2;
+
+	// Octave / F-Number / Key-On for channels 6, 7 and 8
+
+	_channels[6].regBx = *dataptr++ & 0x2F;
+	writeOPL(0xB6, _channels[6].regBx);
+	writeOPL(0xA6, *dataptr++);
+
+	_channels[7].regBx = *dataptr++ & 0x2F;
+	writeOPL(0xB7, _channels[7].regBx);
+	writeOPL(0xA7, *dataptr++);
+
+	_channels[8].regBx = *dataptr++ & 0x2F;
+	writeOPL(0xB8, _channels[8].regBx);
+	writeOPL(0xA8, *dataptr++);
+
+	_rhythmSectionBits = 0x20;
+
+	_curRegOffset = regOffsetBackUp;
+	_curChannel = channelBackUp;
+	return 0;
+}
+
+int AdLibDriver::update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	// Any instrument that we want to play, and which was already playing,
+	// is temporarily keyed off. Instruments that were off already, or
+	// which we don't want to play, retain their old on/off status. This is
+	// probably so that the instrument's envelope is played from its
+	// beginning again...
+
+	writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20);
+
+	// ...but since we only set the rhythm instrument bits, and never clear
+	// them (until the entire rhythm section is disabled), I'm not sure how
+	// useful the cleverness above is. We could perhaps simply turn off all
+	// the rhythm instruments instead.
+
+	_rhythmSectionBits |= value;
+
+	writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits);
+	return 0;
+}
+
+int AdLibDriver::update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	--dataptr;
+	_rhythmSectionBits = 0;
+
+	// All the rhythm bits are cleared. The AM and Vibrato depth bits
+	// remain unchanged.
+
+	writeOPL(0xBD, _vibratoAndAMDepthBits);
+	return 0;
+}
+
+int AdLibDriver::updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	uint8 value2 = *dataptr++;
+
+	if (value & 1) {
+		_unkValue12 = value2;
+
+		// Channel 7, op1: Level Key Scaling / Total Level
+		writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12));
+	}
+
+	if (value & 2) {
+		_unkValue14 = value2;
+
+		// Channel 8, op2: Level Key Scaling / Total Level
+		writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14));
+	}
+
+	if (value & 4) {
+		_unkValue15 = value2;
+
+		// Channel 8, op1: Level Key Scaling / Total Level
+		writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15));
+	}
+
+	if (value & 8) {
+		_unkValue18 = value2;
+
+		// Channel 7, op2: Level Key Scaling / Total Level
+		writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18));
+	}
+
+	if (value & 16) {
+		_unkValue20 = value2;
+
+		// Channel 6, op2: Level Key Scaling / Total Level
+		writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20));
+	}
+
+	return 0;
+}
+
+int AdLibDriver::updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	uint8 value2 = *dataptr++;
+
+	if (value & 1) {
+		_unkValue11 = checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12);
+
+		// Channel 7, op1: Level Key Scaling / Total Level
+		writeOPL(0x51, _unkValue11);
+	}
+
+	if (value & 2) {
+		_unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14);
+
+		// Channel 8, op2: Level Key Scaling / Total Level
+		writeOPL(0x55, _unkValue13);
+	}
+
+	if (value & 4) {
+		_unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15);
+
+		// Channel 8, op1: Level Key Scaling / Total Level
+		writeOPL(0x52, _unkValue16);
+	}
+
+	if (value & 8) {
+		_unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18);
+
+		// Channel 7, op2: Level Key Scaling / Total Level
+		writeOPL(0x54, _unkValue17);
+	}
+
+	if (value & 16) {
+		_unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20);
+
+		// Channel 6, op2: Level Key Scaling / Total Level
+		writeOPL(0x53, _unkValue19);
+	}
+
+	return 0;
+}
+
+int AdLibDriver::updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	uint8 value2 = *dataptr++;
+
+	if (value & 1) {
+		_unkValue11 = value2;
+
+		// Channel 7, op1: Level Key Scaling / Total Level
+		writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12));
+	}
+
+	if (value & 2) {
+		_unkValue13 = value2;
+
+		// Channel 8, op2: Level Key Scaling / Total Level
+		writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14));
+	}
+
+	if (value & 4) {
+		_unkValue16 = value2;
+
+		// Channel 8, op1: Level Key Scaling / Total Level
+		writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15));
+	}
+
+	if (value & 8) {
+		_unkValue17 = value2;
+
+		// Channel 7, op2: Level Key Scaling / Total Level
+		writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18));
+	}
+
+	if (value & 16) {
+		_unkValue19 = value2;
+
+		// Channel 6, op2: Level Key Scaling / Total Level
+		writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue20));
+	}
+
+	return 0;
+}
+
+int AdLibDriver::update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	_soundTrigger = value;
+	return 0;
+}
+
+int AdLibDriver::update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.tempoReset = value;
+	return 0;
+}
+
+int AdLibDriver::updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value) {
+	channel.unk39 = value;
+	channel.unk40 = *dataptr++;
+	return 0;
+}
+
+// static res
+
+#define COMMAND(x) { &AdLibDriver::x, #x }
+
+void AdLibDriver::setupParserOpcodeTable() {
+	static const ParserOpcode parserOpcodeTable[] = {
+		// 0
+		COMMAND(update_setRepeat),
+		COMMAND(update_checkRepeat),
+		COMMAND(update_setupProgram),
+		COMMAND(update_setNoteSpacing),
+
+		// 4
+		COMMAND(update_jump),
+		COMMAND(update_jumpToSubroutine),
+		COMMAND(update_returnFromSubroutine),
+		COMMAND(update_setBaseOctave),
+
+		// 8
+		COMMAND(update_stopChannel),
+		COMMAND(update_playRest),
+		COMMAND(update_writeAdLib),
+		COMMAND(update_setupNoteAndDuration),
+
+		// 12
+		COMMAND(update_setBaseNote),
+		COMMAND(update_setupSecondaryEffect1),
+		COMMAND(update_stopOtherChannel),
+		COMMAND(update_waitForEndOfProgram),
+
+		// 16
+		COMMAND(update_setupInstrument),
+		COMMAND(update_setupPrimaryEffect1),
+		COMMAND(update_removePrimaryEffect1),
+		COMMAND(update_setBaseFreq),
+
+		// 20
+		COMMAND(update_stopChannel),
+		COMMAND(update_setupPrimaryEffect2),
+		COMMAND(update_stopChannel),
+		COMMAND(update_stopChannel),
+
+		// 24
+		COMMAND(update_stopChannel),
+		COMMAND(update_stopChannel),
+		COMMAND(update_setPriority),
+		COMMAND(update_stopChannel),
+
+		// 28
+		COMMAND(updateCallback23),
+		COMMAND(updateCallback24),
+		COMMAND(update_setExtraLevel1),
+		COMMAND(update_stopChannel),
+
+		// 32
+		COMMAND(update_setupDuration),
+		COMMAND(update_playNote),
+		COMMAND(update_stopChannel),
+		COMMAND(update_stopChannel),
+
+		// 36
+		COMMAND(update_setFractionalNoteSpacing),
+		COMMAND(update_stopChannel),
+		COMMAND(update_setTempo),
+		COMMAND(update_removeSecondaryEffect1),
+
+		// 40
+		COMMAND(update_stopChannel),
+		COMMAND(update_setChannelTempo),
+		COMMAND(update_stopChannel),
+		COMMAND(update_setExtraLevel3),
+
+		// 44
+		COMMAND(update_setExtraLevel2),
+		COMMAND(update_changeExtraLevel2),
+		COMMAND(update_setAMDepth),
+		COMMAND(update_setVibratoDepth),
+
+		// 48
+		COMMAND(update_changeExtraLevel1),
+		COMMAND(update_stopChannel),
+		COMMAND(update_stopChannel),
+		COMMAND(updateCallback38),
+
+		// 52
+		COMMAND(update_stopChannel),
+		COMMAND(updateCallback39),
+		COMMAND(update_removePrimaryEffect2),
+		COMMAND(update_stopChannel),
+
+		// 56
+		COMMAND(update_stopChannel),
+		COMMAND(update_pitchBend),
+		COMMAND(update_resetToGlobalTempo),
+		COMMAND(update_nop),
+
+		// 60
+		COMMAND(update_setDurationRandomness),
+		COMMAND(update_changeChannelTempo),
+		COMMAND(update_stopChannel),
+		COMMAND(updateCallback46),
+
+		// 64
+		COMMAND(update_nop),
+		COMMAND(update_setupRhythmSection),
+		COMMAND(update_playRhythmSection),
+		COMMAND(update_removeRhythmSection),
+
+		// 68
+		COMMAND(updateCallback51),
+		COMMAND(updateCallback52),
+		COMMAND(updateCallback53),
+		COMMAND(update_setSoundTrigger),
+
+		// 72
+		COMMAND(update_setTempoReset),
+		COMMAND(updateCallback56),
+		COMMAND(update_stopChannel)
+	};
+
+	_parserOpcodeTable = parserOpcodeTable;
+	_parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable);
+}
+#undef COMMAND
+
+// This table holds the register offset for operator 1 for each of the nine
+// channels. To get the register offset for operator 2, simply add 3.
+
+const uint8 AdLibDriver::_regOffset[] = {
+	0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11,
+	0x12
+};
+
+//These are the F-Numbers (10 bits) for the notes of the 12-tone scale.
+// However, it does not match the table in the AdLib documentation I've seen.
+
+const uint16 AdLibDriver::_freqTable[] = {
+	0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9,
+	0x0207, 0x0225, 0x0246
+};
+
+// These tables are currently only used by updateCallback46(), which only ever
+// uses the first element of one of the sub-tables.
+
+const uint8 *const AdLibDriver::_unkTable2[] = {
+	AdLibDriver::_unkTable2_1,
+	AdLibDriver::_unkTable2_2,
+	AdLibDriver::_unkTable2_1,
+	AdLibDriver::_unkTable2_2,
+	AdLibDriver::_unkTable2_3,
+	AdLibDriver::_unkTable2_2
+};
+
+const uint8 AdLibDriver::_unkTable2_1[] = {
+	0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D,
+	0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49,
+	0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45,
+	0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41,
+	0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D,
+	0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39,
+	0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35,
+	0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31,
+	0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D,
+	0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29,
+	0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25,
+	0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21,
+	0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D,
+	0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19,
+	0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15,
+	0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11,
+	0x10, 0x10
+};
+
+// no don't ask me WHY this table exsits!
+const uint8 AdLibDriver::_unkTable2_2[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+	0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F,
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+	0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+	0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
+};
+
+const uint8 AdLibDriver::_unkTable2_3[] = {
+	0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E,
+	0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B,
+	0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39,
+	0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36,
+	0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33,
+	0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31,
+	0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E,
+	0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B,
+	0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29,
+	0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26,
+	0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23,
+	0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21,
+	0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E,
+	0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B,
+	0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19,
+	0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16,
+	0x16, 0x15
+};
+
+// This table is used to modify the frequency of the notes, depending on the
+// note value and the pitch bend value. In theory, we could very well try to
+// access memory outside this table, but in reality that probably won't happen.
+//
+
+const uint8 AdLibDriver::_pitchBendTables[][32] = {
+	// 0
+	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
+	  0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+	  0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19,
+	  0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 },
+	// 1
+	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09,
+	  0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,
+	  0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A,
+	  0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 },
+	// 2
+	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09,
+	  0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
+	  0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D,
+	  0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 },
+	// 3
+	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
+	  0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
+	  0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D,
+	  0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 },
+	// 4
+	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
+	  0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15,
+	  0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
+	  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A },
+	// 5
+	{ 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
+	  0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
+	  0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
+	  0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D },
+	// 6
+	{ 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
+	  0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
+	  0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24,
+	  0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 },
+	// 7
+	{ 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C,
+	  0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18,
+	  0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25,
+	  0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 },
+	// 8
+	{ 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D,
+	  0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A,
+	  0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28,
+	  0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 },
+	// 9
+	{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
+	  0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B,
+	  0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29,
+	  0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 },
+	// 10
+	{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
+	  0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E,
+	  0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D,
+	  0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C },
+	// 11
+	{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F,
+	  0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E,
+	  0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E,
+	  0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F },
+	// 12
+	{ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10,
+	  0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21,
+	  0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32,
+	  0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 },
+	// 13
+	{ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11,
+	  0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23,
+	  0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35,
+	  0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 }
+};
+
+} // End of namespace Kyra
+
+#undef CALLBACKS_PER_SECOND
diff --git a/engines/kyra/sound/drivers/adlib.h b/engines/kyra/sound/drivers/adlib.h
new file mode 100644
index 0000000..71896ff
--- /dev/null
+++ b/engines/kyra/sound/drivers/adlib.h
@@ -0,0 +1,393 @@
+/* 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.
+ *
+ * LGPL License
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef KYRA_SOUND_ADLIBDRIVER_H
+#define KYRA_SOUND_ADLIBDRIVER_H
+
+#include "kyra/resource/resource.h"
+#include "common/mutex.h"
+
+
+// Basic AdLib Programming:
+// http://www.gamedev.net/reference/articles/article446.asp
+
+
+namespace Audio {
+class Mixer;
+}
+
+namespace OPL {
+class OPL;
+}
+
+namespace Kyra {
+
+class AdLibDriver {
+public:
+	AdLibDriver(Audio::Mixer *mixer, int version);
+	~AdLibDriver();
+
+	void initDriver();
+	void setSoundData(uint8 *data, uint32 size);
+	void queueTrack(int track, int volume);
+	bool isChannelPlaying(int channel) const;
+	void stopAllChannels();
+	int getSoundTrigger() const { return _soundTrigger; }
+	void resetSoundTrigger() { _soundTrigger = 0; }
+
+	void callback();
+
+	void setSyncJumpMask(uint16 mask) { _syncJumpMask = mask; }
+
+	void setMusicVolume(uint8 volume);
+	void setSfxVolume(uint8 volume);
+
+private:
+	// These variables have not yet been named, but some of them are partly
+	// known nevertheless:
+	//
+	// pitchBend - Sound-related. Possibly some sort of pitch bend.
+	// unk18 - Sound-effect. Used for secondaryEffect1()
+	// unk19 - Sound-effect. Used for secondaryEffect1()
+	// unk20 - Sound-effect. Used for secondaryEffect1()
+	// unk21 - Sound-effect. Used for secondaryEffect1()
+	// unk22 - Sound-effect. Used for secondaryEffect1()
+	// unk29 - Sound-effect. Used for primaryEffect1()
+	// unk30 - Sound-effect. Used for primaryEffect1()
+	// unk31 - Sound-effect. Used for primaryEffect1()
+	// unk32 - Sound-effect. Used for primaryEffect2()
+	// unk33 - Sound-effect. Used for primaryEffect2()
+	// unk34 - Sound-effect. Used for primaryEffect2()
+	// unk35 - Sound-effect. Used for primaryEffect2()
+	// unk36 - Sound-effect. Used for primaryEffect2()
+	// unk37 - Sound-effect. Used for primaryEffect2()
+	// unk38 - Sound-effect. Used for primaryEffect2()
+	// unk39 - Currently unused, except for updateCallback56()
+	// unk40 - Currently unused, except for updateCallback56()
+	// unk41 - Sound-effect. Used for primaryEffect2()
+
+	struct Channel {
+		bool lock;	// New to ScummVM
+		uint8 opExtraLevel2;
+		const uint8 *dataptr;
+		uint8 duration;
+		uint8 repeatCounter;
+		int8 baseOctave;
+		uint8 priority;
+		uint8 dataptrStackPos;
+		const uint8 *dataptrStack[4];
+		int8 baseNote;
+		uint8 unk29;
+		uint8 unk31;
+		uint16 unk30;
+		uint16 unk37;
+		uint8 unk33;
+		uint8 unk34;
+		uint8 unk35;
+		uint8 unk36;
+		uint8 unk32;
+		uint8 unk41;
+		uint8 unk38;
+		uint8 opExtraLevel1;
+		uint8 spacing2;
+		uint8 baseFreq;
+		uint8 tempo;
+		uint8 position;
+		uint8 regAx;
+		uint8 regBx;
+		typedef void (AdLibDriver::*Callback)(Channel&);
+		Callback primaryEffect;
+		Callback secondaryEffect;
+		uint8 fractionalSpacing;
+		uint8 opLevel1;
+		uint8 opLevel2;
+		uint8 opExtraLevel3;
+		uint8 twoChan;
+		uint8 unk39;
+		uint8 unk40;
+		uint8 spacing1;
+		uint8 durationRandomness;
+		uint8 unk19;
+		uint8 unk18;
+		int8 unk20;
+		int8 unk21;
+		uint8 unk22;
+		uint16 offset;
+		uint8 tempoReset;
+		uint8 rawNote;
+		int8 pitchBend;
+		uint8 volumeModifier;
+	};
+
+	void primaryEffect1(Channel &channel);
+	void primaryEffect2(Channel &channel);
+	void secondaryEffect1(Channel &channel);
+
+	void resetAdLibState();
+	void writeOPL(byte reg, byte val);
+	void initChannel(Channel &channel);
+	void noteOff(Channel &channel);
+	void unkOutput2(uint8 num);
+
+	uint16 getRandomNr();
+	void setupDuration(uint8 duration, Channel &channel);
+
+	void setupNote(uint8 rawNote, Channel &channel, bool flag = false);
+	void setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel);
+	void noteOn(Channel &channel);
+
+	void adjustVolume(Channel &channel);
+
+	uint8 calculateOpLevel1(Channel &channel);
+	uint8 calculateOpLevel2(Channel &channel);
+
+	uint16 checkValue(int16 val) {
+		if (val < 0)
+			val = 0;
+		else if (val > 0x3F)
+			val = 0x3F;
+		return val;
+	}
+
+	// The sound data has at least two lookup tables:
+	//
+	// * One for programs, starting at offset 0.
+	// * One for instruments, starting at offset 500.
+
+	uint8 *getProgram(int progId) {
+		const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
+
+		// In case an invalid offset is specified we return nullptr to
+		// indicate an error. 0xFFFF seems to indicate "this is not a valid
+		// program/instrument". However, 0 is also invalid because it points
+		// inside the offset table itself. We also ignore any offsets outside
+		// of the actual data size.
+		// The original does not contain any safety checks and will simply
+		// read outside of the valid sound data in case an invalid offset is
+		// encountered.
+		if (offset == 0 || offset >= _soundDataSize) {
+			return nullptr;
+		} else {
+			return _soundData + offset;
+		}
+	}
+
+	const uint8 *getInstrument(int instrumentId) {
+		return getProgram(_numPrograms + instrumentId);
+	}
+
+	void setupPrograms();
+	void executePrograms();
+
+	struct ParserOpcode {
+		typedef int (AdLibDriver::*POpcode)(const uint8 *&dataptr, Channel &channel, uint8 value);
+		POpcode function;
+		const char *name;
+	};
+
+	void setupParserOpcodeTable();
+	const ParserOpcode *_parserOpcodeTable;
+	int _parserOpcodeTableSize;
+
+	int update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_jump(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_nop(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value);
+	int updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value);
+private:
+	// These variables have not yet been named, but some of them are partly
+	// known nevertheless:
+	//
+	// _unkValue1      - Unknown. Used for updating _unkValue2
+	// _unkValue2      - Unknown. Used for updating _unkValue4
+	// _unkValue4      - Unknown. Used for updating _unkValue5
+	// _unkValue5      - Unknown. Used for controlling updateCallback24().
+	// _unkValue6      - Unknown. Rhythm section volume?
+	// _unkValue7      - Unknown. Rhythm section volume?
+	// _unkValue8      - Unknown. Rhythm section volume?
+	// _unkValue9      - Unknown. Rhythm section volume?
+	// _unkValue10     - Unknown. Rhythm section volume?
+	// _unkValue11     - Unknown. Rhythm section volume?
+	// _unkValue12     - Unknown. Rhythm section volume?
+	// _unkValue13     - Unknown. Rhythm section volume?
+	// _unkValue14     - Unknown. Rhythm section volume?
+	// _unkValue15     - Unknown. Rhythm section volume?
+	// _unkValue16     - Unknown. Rhythm section volume?
+	// _unkValue17     - Unknown. Rhythm section volume?
+	// _unkValue18     - Unknown. Rhythm section volume?
+	// _unkValue19     - Unknown. Rhythm section volume?
+	// _unkValue20     - Unknown. Rhythm section volume?
+	// _freqTable[]     - Probably frequences for the 12-tone scale.
+	// _unkTable2[]    - Unknown. Currently only used by updateCallback46()
+	// _unkTable2_1[]  - One of the tables in _unkTable2[]
+	// _unkTable2_2[]  - One of the tables in _unkTable2[]
+	// _unkTable2_3[]  - One of the tables in _unkTable2[]
+
+	int _curChannel;
+	uint8 _soundTrigger;
+
+	uint16 _rnd;
+
+	uint8 _unkValue1;
+	uint8 _unkValue2;
+	uint8 _callbackTimer;
+	uint8 _unkValue4;
+	uint8 _unkValue5;
+	uint8 _unkValue6;
+	uint8 _unkValue7;
+	uint8 _unkValue8;
+	uint8 _unkValue9;
+	uint8 _unkValue10;
+	uint8 _unkValue11;
+	uint8 _unkValue12;
+	uint8 _unkValue13;
+	uint8 _unkValue14;
+	uint8 _unkValue15;
+	uint8 _unkValue16;
+	uint8 _unkValue17;
+	uint8 _unkValue18;
+	uint8 _unkValue19;
+	uint8 _unkValue20;
+
+	OPL::OPL *_adlib;
+
+	uint8 *_soundData;
+	uint32 _soundDataSize;
+
+	struct QueueEntry {
+		QueueEntry() : data(0), id(0), volume(0) {}
+		QueueEntry(uint8 *ptr, uint8 track, uint8 vol) : data(ptr), id(track), volume(vol) {}
+		uint8 *data;
+		uint8 id;
+		uint8 volume;
+	};
+
+	QueueEntry _programQueue[16];
+	int _programStartTimeout;
+	int _programQueueStart, _programQueueEnd;
+	bool _retrySounds;
+
+	void adjustSfxData(uint8 *data, int volume);
+	uint8 *_sfxPointer;
+	int _sfxPriority;
+	int _sfxVelocity;
+
+	Channel _channels[10];
+
+	uint8 _vibratoAndAMDepthBits;
+	uint8 _rhythmSectionBits;
+
+	uint8 _curRegOffset;
+	uint8 _tempo;
+
+	const uint8 *_tablePtr1;
+	const uint8 *_tablePtr2;
+
+	static const uint8 _regOffset[];
+	static const uint16 _freqTable[];
+	static const uint8 *const _unkTable2[];
+	static const uint8 _unkTable2_1[];
+	static const uint8 _unkTable2_2[];
+	static const uint8 _unkTable2_3[];
+	static const uint8 _pitchBendTables[][32];
+
+	uint16 _syncJumpMask;
+
+	Common::Mutex _mutex;
+	Audio::Mixer *_mixer;
+
+	uint8 _musicVolume, _sfxVolume;
+
+	int _numPrograms;
+	int _version;
+};
+
+} // End of namespace Kyra
+
+#endif
\ No newline at end of file
diff --git a/engines/kyra/sound/drivers/audstream.cpp b/engines/kyra/sound/drivers/audstream.cpp
new file mode 100644
index 0000000..24522ee
--- /dev/null
+++ b/engines/kyra/sound/drivers/audstream.cpp
@@ -0,0 +1,320 @@
+/* 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 "kyra/resource/resource.h"
+#include "audio/audiostream.h"
+
+#include "common/util.h"
+
+namespace Kyra {
+
+// Thanks to Torbjorn Andersson (eriktorbjorn) for his aud player on which
+// this code is based on
+
+// TODO: cleanup of whole AUDStream
+
+class AUDStream : public Audio::SeekableAudioStream {
+public:
+	AUDStream(Common::SeekableReadStream* stream);
+	~AUDStream();
+
+	int readBuffer(int16* buffer, const int numSamples);
+
+	bool isStereo() const { return false; }
+	bool endOfData() const { return _endOfData; }
+
+	int getRate() const { return _rate; }
+
+	bool seek(const Audio::Timestamp& where);
+	Audio::Timestamp getLength() const { return _length; }
+private:
+	Common::SeekableReadStream* _stream;
+	uint32 _streamStart;
+	bool _endOfData;
+	int _rate;
+	uint _processedSize;
+	uint _totalSize;
+	Audio::Timestamp _length;
+
+	int _bytesLeft;
+
+	byte* _outBuffer;
+	int _outBufferOffset;
+	uint _outBufferSize;
+
+	byte* _inBuffer;
+	uint _inBufferSize;
+
+	int readChunk(int16* buffer, const int maxSamples);
+
+	static const int8 WSTable2Bit[];
+	static const int8 WSTable4Bit[];
+};
+
+const int8 AUDStream::WSTable2Bit[] = { -2, -1, 0, 1 };
+const int8 AUDStream::WSTable4Bit[] = {
+	-9, -8, -6, -5, -4, -3, -2, -1,
+	 0,  1,  2,  3,  4,  5,  6,  8
+};
+
+AUDStream::AUDStream(Common::SeekableReadStream *stream) : _stream(stream), _endOfData(true), _rate(0),
+	_processedSize(0), _totalSize(0), _length(0, 1), _bytesLeft(0), _outBuffer(0),
+	_outBufferOffset(0), _outBufferSize(0), _inBuffer(0), _inBufferSize(0) {
+
+	_rate = _stream->readUint16LE();
+	_totalSize = _stream->readUint32LE();
+
+	// TODO?: add checks
+	int flags = _stream->readByte();	// flags
+	int type = _stream->readByte();	// type
+
+	_streamStart = stream->pos();
+
+	debugC(5, kDebugLevelSound, "AUD Info: rate: %d, totalSize: %d, flags: %d, type: %d, streamStart: %d", _rate, _totalSize, flags, type, _streamStart);
+
+	_length = Audio::Timestamp(0, _rate);
+	for (uint32 i = 0; i < _totalSize;) {
+		uint16 size = _stream->readUint16LE();
+		uint16 outSize = _stream->readUint16LE();
+
+		_length = _length.addFrames(outSize);
+		stream->seek(size + 4, SEEK_CUR);
+		i += size + 8;
+	}
+
+	stream->seek(_streamStart, SEEK_SET);
+
+	if (type == 1 && !flags)
+		_endOfData = false;
+	else
+		warning("No AUD file (rate: %d, size: %d, flags: 0x%X, type: %d)", _rate, _totalSize, flags, type);
+}
+
+AUDStream::~AUDStream() {
+	delete[] _outBuffer;
+	delete[] _inBuffer;
+	delete _stream;
+}
+
+int AUDStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samplesRead = 0, samplesLeft = numSamples;
+
+	while (samplesLeft > 0 && !_endOfData) {
+		int samples = readChunk(buffer, samplesLeft);
+		samplesRead += samples;
+		samplesLeft -= samples;
+		buffer += samples;
+	}
+
+	return samplesRead;
+}
+
+inline int16 clip8BitSample(int16 sample) {
+	return CLIP<int16>(sample, 0, 255);
+}
+
+int AUDStream::readChunk(int16 *buffer, const int maxSamples) {
+	int samplesProcessed = 0;
+
+	// if no bytes of the old chunk are left, read the next one
+	if (_bytesLeft <= 0) {
+		if (_processedSize >= _totalSize) {
+			_endOfData = true;
+			return 0;
+		}
+
+		uint16 size = _stream->readUint16LE();
+		uint16 outSize = _stream->readUint16LE();
+		uint32 id = _stream->readUint32LE();
+
+		assert(id == 0x0000DEAF);
+
+		_processedSize += 8 + size;
+
+		_outBufferOffset = 0;
+		if (size == outSize) {
+			if (outSize > _outBufferSize) {
+				_outBufferSize = outSize;
+				delete[] _outBuffer;
+				_outBuffer = new uint8[_outBufferSize];
+				assert(_outBuffer);
+			}
+
+			_bytesLeft = size;
+
+			_stream->read(_outBuffer, _bytesLeft);
+		} else {
+			_bytesLeft = outSize;
+
+			if (outSize > _outBufferSize) {
+				_outBufferSize = outSize;
+				delete[] _outBuffer;
+				_outBuffer = new uint8[_outBufferSize];
+				assert(_outBuffer);
+			}
+
+			if (size > _inBufferSize) {
+				_inBufferSize = size;
+				delete[] _inBuffer;
+				_inBuffer = new uint8[_inBufferSize];
+				assert(_inBuffer);
+			}
+
+			if (_stream->read(_inBuffer, size) != size) {
+				_endOfData = true;
+				return 0;
+			}
+
+			int16 curSample = 0x80;
+			byte code = 0;
+			int8 count = 0;
+			uint16 input = 0;
+			int j = 0;
+			int i = 0;
+
+			while (outSize > 0) {
+				input = _inBuffer[i++] << 2;
+				code = (input >> 8) & 0xFF;
+				count = (input & 0xFF) >> 2;
+
+				switch (code) {
+				case 2:
+					if (count & 0x20) {
+						/* NOTE: count is signed! */
+						count <<= 3;
+						curSample += (count >> 3);
+						_outBuffer[j++] = curSample & 0xFF;
+						outSize--;
+					} else {
+						for (; count >= 0; count--) {
+							_outBuffer[j++] = _inBuffer[i++];
+							outSize--;
+						}
+						curSample = _inBuffer[i - 1];
+					}
+					break;
+				case 1:
+					for (; count >= 0; count--) {
+						code = _inBuffer[i++];
+
+						curSample += WSTable4Bit[code & 0x0F];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample;
+
+						curSample += WSTable4Bit[code >> 4];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample;
+
+						outSize -= 2;
+					}
+					break;
+				case 0:
+					for (; count >= 0; count--) {
+						code = (uint8)_inBuffer[i++];
+
+						curSample += WSTable2Bit[code & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						curSample += WSTable2Bit[(code >> 2) & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						curSample += WSTable2Bit[(code >> 4) & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						curSample += WSTable2Bit[(code >> 6) & 0x03];
+						curSample = clip8BitSample(curSample);
+						_outBuffer[j++] = curSample & 0xFF;
+
+						outSize -= 4;
+					}
+					break;
+				default:
+					for (; count >= 0; count--) {
+						_outBuffer[j++] = curSample & 0xFF;
+						outSize--;
+					}
+				}
+			}
+		}
+	}
+
+	// copies the chunk data to the output buffer
+	if (_bytesLeft > 0) {
+		int samples = MIN(_bytesLeft, maxSamples);
+		samplesProcessed += samples;
+		_bytesLeft -= samples;
+
+		while (samples--) {
+			int16 sample = (_outBuffer[_outBufferOffset++] << 8) ^ 0x8000;
+
+			*buffer++ = sample;
+		}
+	}
+
+	return samplesProcessed;
+}
+
+bool AUDStream::seek(const Audio::Timestamp &where) {
+	const uint32 seekSample = Audio::convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
+
+	_stream->seek(_streamStart);
+	_processedSize = 0;
+	_bytesLeft = 0;
+	_endOfData = false;
+
+	uint32 curSample = 0;
+
+	while (!endOfData()) {
+		uint16 size = _stream->readUint16LE();
+		uint16 outSize = _stream->readUint16LE();
+
+		if (curSample + outSize > seekSample) {
+			_stream->seek(-4, SEEK_CUR);
+
+			uint32 samples = seekSample - curSample;
+			int16 *temp = new int16[samples];
+			assert(temp);
+
+			readChunk(temp, samples);
+			delete[] temp;
+			curSample += samples;
+			break;
+		} else {
+			curSample += outSize;
+			_processedSize += 8 + size;
+			_stream->seek(size + 4, SEEK_CUR);
+		}
+	}
+
+	_endOfData = (_processedSize >= _totalSize);
+
+	return (curSample == seekSample);
+}
+
+Audio::SeekableAudioStream* makeAUDStream(Common::SeekableReadStream* stream, DisposeAfterUse::Flag disposeAfterUse) {
+	return new AUDStream(stream);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/drivers/midi.cpp b/engines/kyra/sound/drivers/midi.cpp
new file mode 100644
index 0000000..5e8460a
--- /dev/null
+++ b/engines/kyra/sound/drivers/midi.cpp
@@ -0,0 +1,355 @@
+/* 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 "kyra/sound/drivers/midi.h"
+
+namespace Kyra {
+
+MidiOutput::MidiOutput(OSystem *system, MidiDriver *output, bool isMT32, bool defaultMT32) : _system(system), _output(output) {
+	_isMT32 = isMT32;
+	_defaultMT32 = defaultMT32;
+
+	int ret = _output->open();
+	if (ret != MidiDriver::MERR_ALREADY_OPEN && ret != 0)
+		error("Couldn't open midi driver");
+
+	static const Controller defaultControllers[] = {
+		{ 0x07, 0x7F }, { 0x01, 0x00 }, { 0x0A, 0x40 },
+		{ 0x0B, 0x7F }, { 0x40, 0x00 }, { 0x72, 0x00 },
+		{ 0x6E, 0x00 }, { 0x6F, 0x00 }, { 0x70, 0x00 }
+	};
+
+	static const byte defaultPrograms[] = {
+		0x44, 0x30, 0x5F, 0x4E, 0x29, 0x03, 0x6E, 0x7A, 0xFF
+	};
+
+	static const byte sysEx1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+	static const byte sysEx2[] = { 3, 4, 3, 4, 3, 4, 3, 4, 4 };
+	static const byte sysEx3[] = { 0, 3, 2 };
+
+	if (_isMT32) {
+		sendSysEx(0x7F, 0x00, 0x00, sysEx1, 1);
+		sendSysEx(0x10, 0x00, 0x0D, sysEx1, 9);
+		sendSysEx(0x10, 0x00, 0x04, sysEx2, 9);
+		sendSysEx(0x10, 0x00, 0x01, sysEx3, 3);
+	} else {
+		_output->sendGMReset();
+	}
+
+	memset(_channels, 0, sizeof(_channels));
+	for (int i = 0; i < 16; ++i) {
+		for (int j = 0; j < 9; ++j)
+			_channels[i].controllers[j] = defaultControllers[j];
+		_channels[i].pitchWheel = -1;
+		_channels[i].program = 0xFF;
+	}
+
+	for (int i = 0; i < 9; ++i) {
+		for (int j = 1; j <= 9; ++j)
+			sendIntern(0xB0, j, defaultControllers[i].controller, defaultControllers[i].value);
+	}
+
+	for (int i = 1; i <= 9; ++i) {
+		sendIntern(0xE0, i, 0x00, 0x40);
+		if (defaultPrograms[i - 1] != 0xFF)
+			sendIntern(0xC0, i, defaultPrograms[i - 1], 0x00);
+	}
+
+	for (int i = 0; i < 4; ++i) {
+		_sources[i].volume = 256;
+		initSource(i);
+	}
+}
+
+
+MidiOutput::~MidiOutput() {
+	_output->close();
+	delete _output;
+}
+
+void MidiOutput::send(uint32 b) {
+	const byte event = b & 0xF0;
+	const byte channel = b & 0x0F;
+	byte param1 = (b >>  8) & 0xFF;
+	byte param2 = (b >> 16) & 0xFF;
+
+	if (event == 0xE0) {							// Pitch-Wheel
+		_channels[channel].pitchWheel =
+		_sources[_curSource].channelPW[channel] = (param2 << 8) | param1;
+	} else if (event == 0xC0) {						// Program change
+		_channels[channel].program =
+		_sources[_curSource].channelProgram[channel] = param1;
+	} else if (event == 0xB0) {						// Controller change
+		for (int i = 0; i < 9; ++i) {
+			Controller &cont = _sources[_curSource].controllers[channel][i];
+			if (cont.controller == param1) {
+				cont.value = param2;
+				break;
+			}
+		}
+
+		if (param1 == 0x07) {
+			param2 = (param2 * _sources[_curSource].volume) >> 8;
+		} else if (param1 == 0x6E) {	// Lock Channel
+			if (param2 >= 0x40) {	// Lock Channel
+				int chan = lockChannel();
+				if (chan < 0)
+					chan = channel;
+				_sources[_curSource].channelMap[channel] = chan;
+			} else {				// Unlock Channel
+				stopNotesOnChannel(channel);
+				unlockChannel(_sources[_curSource].channelMap[channel]);
+				_sources[_curSource].channelMap[channel] = channel;
+			}
+		} else if (param1 == 0x6F) {	// Protect Channel
+			if (param2 >= 0x40) {	// Protect Channel
+				_channels[channel].flags |= kChannelProtected;
+			} else {				// Unprotect Channel
+				_channels[channel].flags &= ~kChannelProtected;
+			}
+		} else if (param1 == 0x7B) {	// All notes off
+			// FIXME: Since the XMIDI parsers sends this
+			// on track change, we simply ignore it.
+			return;
+		}
+	} else if (event == 0x90 || event == 0x80) {	// Note On/Off
+		if (!(_channels[channel].flags & kChannelLocked)) {
+			const bool remove = (event == 0x80) || (param2 == 0x00);
+			int note = -1;
+
+			for (int i = 0; i < 32; ++i) {
+				if (remove) {
+					if (_sources[_curSource].notes[i].channel == channel &&
+						_sources[_curSource].notes[i].note == param1) {
+						note = i;
+						break;
+					}
+				} else {
+					if (_sources[_curSource].notes[i].channel == 0xFF) {
+						note = i;
+						break;
+					}
+				}
+			}
+
+			if (note != -1) {
+				if (remove) {
+					_sources[_curSource].notes[note].channel = 0xFF;
+
+					--_channels[_sources[_curSource].channelMap[channel]].noteCount;
+				} else {
+					_sources[_curSource].notes[note].channel = channel;
+					_sources[_curSource].notes[note].note = param1;
+
+					++_channels[_sources[_curSource].channelMap[channel]].noteCount;
+				}
+
+				sendIntern(event, _sources[_curSource].channelMap[channel], param1, param2);
+			}
+		}
+		return;
+	}
+
+	if (!(_channels[channel].flags & kChannelLocked))
+		sendIntern(event, _sources[_curSource].channelMap[channel], param1, param2);
+}
+
+void MidiOutput::sendIntern(const byte event, const byte channel, byte param1, const byte param2) {
+	if (event == 0xC0) {
+		// MT32 -> GM conversion
+		if (!_isMT32 && _defaultMT32)
+			param1 = MidiDriver::_mt32ToGm[param1];
+	}
+
+	_output->send(event | channel, param1, param2);
+}
+
+void MidiOutput::sysEx(const byte *msg, uint16 length) {
+	// Wait the time it takes to send the SysEx data
+	uint32 delay = (length + 2) * 1000 / 3125;
+
+	// Plus an additional delay for the MT-32 rev00
+	if (_isMT32)
+		delay += 40;
+
+	_output->sysEx(msg, length);
+	_system->delayMillis(delay);
+}
+
+void MidiOutput::sendSysEx(const byte p1, const byte p2, const byte p3, const byte *buffer, const int size) {
+	int bufferSize = 8 + size;
+	byte *outBuffer = new byte[bufferSize];
+	assert(outBuffer);
+
+	outBuffer[0] = 0x41;
+	outBuffer[1] = 0x10;
+	outBuffer[2] = 0x16;
+	outBuffer[3] = 0x12;
+
+	outBuffer[4] = p1;
+	outBuffer[5] = p2;
+	outBuffer[6] = p3;
+
+	memcpy(outBuffer + 7, buffer, size);
+
+	uint16 checkSum = p1 + p2 + p3;
+	for (int i = 0; i < size; ++i)
+		checkSum += buffer[i];
+	checkSum &= 0x7F;
+	checkSum -= 0x80;
+	checkSum = -checkSum;
+	checkSum &= 0x7F;
+
+	outBuffer[7+size] = checkSum;
+
+	sysEx(outBuffer, bufferSize);
+
+	delete[] outBuffer;
+}
+
+void MidiOutput::metaEvent(byte type, byte *data, uint16 length) {
+	if (type == 0x2F) // End of Track
+		deinitSource(_curSource);
+
+	_output->metaEvent(type, data, length);
+}
+
+void MidiOutput::setSourceVolume(int source, int volume, bool apply) {
+	_sources[source].volume = volume;
+
+	if (apply) {
+		for (int i = 0; i < 16; ++i) {
+			// Controller 0 in the state table should always be '7' aka
+			// volume control
+			byte realVol = (_sources[source].controllers[i][0].value * volume) >> 8;
+			sendIntern(0xB0, i, 0x07, realVol);
+		}
+	}
+}
+
+void MidiOutput::initSource(int source) {
+	memset(_sources[source].notes, -1, sizeof(_sources[source].notes));
+
+	for (int i = 0; i < 16; ++i) {
+		_sources[source].channelMap[i] = i;
+		_sources[source].channelProgram[i] = 0xFF;
+		_sources[source].channelPW[i] = -1;
+
+		for (int j = 0; j < 9; ++j)
+			_sources[source].controllers[i][j] = _channels[i].controllers[j];
+	}
+}
+
+void MidiOutput::deinitSource(int source) {
+	for (int i = 0; i < 16; ++i) {
+		for (int j = 0; j < 9; ++j) {
+			const Controller &cont = _sources[source].controllers[i][j];
+
+			if (cont.controller == 0x40) {
+				if (cont.value >= 0x40)
+					sendIntern(0xB0, i, 0x40, 0);
+			} else if (cont.controller == 0x6E) {
+				if (cont.value >= 0x40) {
+					stopNotesOnChannel(i);
+					unlockChannel(_sources[source].channelMap[i]);
+					_sources[source].channelMap[i] = i;
+				}
+			} else if (cont.controller == 0x6F) {
+				if (cont.value >= 0x40)
+					_channels[i].flags &= ~kChannelProtected;
+			} else if (cont.controller == 0x70) {
+				if (cont.value >= 0x40)
+					sendIntern(0xB0, i, 0x70, 0);
+			}
+		}
+	}
+}
+
+int MidiOutput::lockChannel() {
+	int channel = -1;
+	int notes = 0xFF;
+	byte flags = kChannelLocked | kChannelProtected;
+
+	while (channel == -1) {
+		for (int i = _isMT32 ? 8 : 15; i >= 1; --i) {
+			if (_channels[i].flags & flags)
+				continue;
+			if (_channels[i].noteCount < notes) {
+				channel = i;
+				notes = _channels[i].noteCount;
+			}
+		}
+
+		if (channel == -1) {
+			if (flags & kChannelProtected)
+				flags &= ~kChannelProtected;
+			else
+				break;
+		}
+	}
+
+	if (channel == -1)
+		return -1;
+
+	sendIntern(0xB0, channel, 0x40, 0);
+	stopNotesOnChannel(channel);
+	_channels[channel].noteCount = 0;
+	_channels[channel].flags |= kChannelLocked;
+
+	return channel;
+}
+
+void MidiOutput::unlockChannel(int channel) {
+	if (!(_channels[channel].flags & kChannelLocked))
+		return;
+
+	_channels[channel].flags &= ~kChannelLocked;
+	_channels[channel].noteCount = 0;
+	sendIntern(0xB0, channel, 0x40, 0);
+	sendIntern(0xB0, channel, 0x7B, 0);
+
+	for (int i = 0; i < 9; ++i) {
+		if (_channels[channel].controllers[i].value != 0xFF)
+			sendIntern(0xB0, channel, _channels[channel].controllers[i].controller, _channels[channel].controllers[i].value);
+	}
+
+	if (_channels[channel].program != 0xFF)
+		sendIntern(0xC0, channel, _channels[channel].program, 0);
+
+	if (_channels[channel].pitchWheel != -1)
+		sendIntern(0xE0, channel, _channels[channel].pitchWheel & 0xFF, (_channels[channel].pitchWheel >> 8) & 0xFF);
+}
+
+void MidiOutput::stopNotesOnChannel(int channel) {
+	for (int i = 0; i < 4; ++i) {
+		SoundSource &sound = _sources[i];
+		for (int j = 0; j < 32; ++j) {
+			if (sound.notes[j].channel == channel) {
+				sound.notes[j].channel = 0xFF;
+				sendIntern(0x80, sound.channelMap[channel], sound.notes[j].note, 0);
+				--_channels[sound.channelMap[channel]].noteCount;
+			}
+		}
+	}
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/drivers/midi.h b/engines/kyra/sound/drivers/midi.h
new file mode 100644
index 0000000..386852e
--- /dev/null
+++ b/engines/kyra/sound/drivers/midi.h
@@ -0,0 +1,108 @@
+/* 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 KYRA_SOUND_MIDIDRIVER_H
+#define KYRA_SOUND_MIDIDRIVER_H
+
+#include "kyra/sound/sound_intern.h"
+
+namespace Kyra {
+
+class MidiOutput : public MidiDriver_BASE {
+public:
+	MidiOutput(OSystem *system, MidiDriver *output, bool isMT32, bool defaultMT32);
+	~MidiOutput();
+
+	void setSourceVolume(int source, int volume, bool apply=false);
+
+	void initSource(int source);
+	void deinitSource(int source);
+	void stopNotesOnChannel(int channel);
+
+	void setSoundSource(int source) { _curSource = source; }
+
+	// MidiDriver_BASE interface
+	virtual void send(uint32 b);
+	virtual void sysEx(const byte *msg, uint16 length);
+	virtual void metaEvent(byte type, byte *data, uint16 length);
+
+	// TODO: Get rid of the following two methods
+	void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { _output->setTimerCallback(timerParam, timerProc); }
+	uint32 getBaseTempo() { return _output->getBaseTempo(); }
+
+
+private:
+	void sendIntern(const byte event, const byte channel, byte param1, const byte param2);
+	void sendSysEx(const byte p1, const byte p2, const byte p3, const byte *buffer, const int size);
+
+	OSystem *_system;
+	MidiDriver *_output;
+
+	bool _isMT32;
+	bool _defaultMT32;
+
+	struct Controller {
+		byte controller;
+		byte value;
+	};
+
+	enum {
+		kChannelLocked = 0x80,
+		kChannelProtected = 0x40
+	};
+
+	struct Channel {
+		byte flags;
+
+		byte program;
+		int16 pitchWheel;
+
+		byte noteCount;
+
+		Controller controllers[9];
+	} _channels[16];
+
+	int lockChannel();
+	void unlockChannel(int channel);
+
+	int _curSource;
+
+	struct SoundSource {
+		int volume;
+
+		int8 channelMap[16];
+		byte channelProgram[16];
+		int16 channelPW[16];
+		Controller controllers[16][9];
+
+		struct Note {
+			byte channel;
+			byte note;
+		};
+
+		Note notes[32];
+	} _sources[4];
+};
+
+} // End of namespace Kyra
+
+#endif
\ No newline at end of file
diff --git a/engines/kyra/sound/drivers/pcspeaker.cpp b/engines/kyra/sound/drivers/pcspeaker.cpp
new file mode 100644
index 0000000..110adde
--- /dev/null
+++ b/engines/kyra/sound/drivers/pcspeaker.cpp
@@ -0,0 +1,366 @@
+/* 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 "kyra/sound/sound_intern.h"
+
+#include "audio/mixer.h"
+#include "audio/softsynth/pcspk.h"
+
+namespace Kyra {
+
+MidiDriver_PCSpeaker::MidiDriver_PCSpeaker(Audio::Mixer *mixer)
+	: MidiDriver_Emulated(mixer), _rate(mixer->getOutputRate()) {
+	_timerValue = 0;
+	memset(_channel, 0, sizeof(_channel));
+	memset(_note, 0, sizeof(_note));
+
+	for (int i = 0; i < 2; ++i)
+		_note[i].hardwareChannel = 0xFF;
+
+	_speaker = new Audio::PCSpeaker(_rate);
+	assert(_speaker);
+	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+	_countdown = 0xFFFF;
+	_hardwareChannel[0] = 0xFF;
+	_modulationFlag = false;
+}
+
+MidiDriver_PCSpeaker::~MidiDriver_PCSpeaker() {
+	_mixer->stopHandle(_mixerSoundHandle);
+	delete _speaker;
+	_speaker = 0;
+}
+
+void MidiDriver_PCSpeaker::send(uint32 data) {
+	Common::StackLock lock(_mutex);
+
+	uint8 channel = data & 0x0F;
+	uint8 param1 = (data >>  8) & 0xFF;
+	uint8 param2 = (data >> 16) & 0xFF;
+
+	uint8 flags = 0x00;
+
+	if (channel > 1)
+		return;
+
+	switch (data & 0xF0) {
+	case 0x80:	// note off
+		noteOff(channel, param1);
+		return;
+
+	case 0x90:	// note on
+		if (channel > 1)
+			return;
+
+		if (param2)
+			noteOn(channel, param1);
+		else
+			noteOff(channel, param1);
+		return;
+
+	case 0xB0:	// controller
+		switch (param1) {
+		case 0x01:	// modulation
+			_channel[channel].modulation = param2;
+			break;
+
+		case 0x40:	// hold
+			_channel[channel].hold = param2;
+			if (param2 < 0x40)
+				resetController(channel);
+			return;
+
+		case 0x70:	// voice protect
+			_channel[channel].voiceProtect = param2;
+			return;
+
+		case 0x79:	// all notes off
+			_channel[channel].hold = 0;
+			resetController(channel);
+			_channel[channel].modulation = 0;
+			_channel[channel].pitchBendLow = 0;
+			_channel[channel].pitchBendHigh = 0x40;
+			flags = 0x01;
+			break;
+
+		default:
+			return;
+		}
+		break;
+
+	case 0xE0:	// pitch bend
+		flags = 0x01;
+		_channel[channel].pitchBendLow = param1;
+		_channel[channel].pitchBendHigh = param2;
+		break;
+
+	default:
+		return;
+	}
+
+	for (int i = 0; i < 2; ++i) {
+		if (_note[i].enabled && _note[i].midiChannel == channel) {
+			_note[i].flags |= flags;
+			setupTone(i);
+		}
+	}
+}
+
+void MidiDriver_PCSpeaker::resetController(int channel) {
+	for (int i = 0; i < 2; ++i) {
+		if (_note[i].enabled && _note[i].midiChannel == channel && _note[i].processHold)
+			noteOff(channel, _note[i].note);
+	}
+}
+
+void MidiDriver_PCSpeaker::noteOn(int channel, int note) {
+	int n = 0;
+
+	while (n < 2 && _note[n].enabled)
+		++n;
+
+	if (n >= 2)
+		return;
+
+	_note[n].midiChannel = channel;
+	_note[n].note = note;
+	_note[n].enabled = true;
+	_note[n].processHold = false;
+	_note[n].hardwareFlags = 0x20;
+	_note[n].priority = 0x7FFF;
+	_note[n].flags = 0x01;
+
+	turnNoteOn(n);
+}
+
+void MidiDriver_PCSpeaker::turnNoteOn(int note) {
+	if (_hardwareChannel[0] == 0xFF) {
+		_note[note].hardwareChannel = 0;
+		++_channel[_note[note].midiChannel].noteCount;
+		_hardwareChannel[0] = _note[note].midiChannel;
+		_note[note].flags = 0x01;
+
+		setupTone(note);
+	} else {
+		overwriteNote(note);
+	}
+}
+
+void MidiDriver_PCSpeaker::overwriteNote(int note) {
+	int totalNotes = 0;
+	for (int i = 0; i < 2; ++i) {
+		if (_note[i].enabled) {
+			++totalNotes;
+			const int channel = _note[i].midiChannel;
+
+			uint16 priority = 0xFFFF;
+			if (_channel[channel].voiceProtect < 0x40)
+				priority = _note[i].priority;
+
+			if (_channel[channel].noteCount > priority)
+				priority = 0;
+			else
+				priority -= _channel[channel].noteCount;
+
+			_note[i].precedence = priority;
+		}
+	}
+
+	if (totalNotes <= 1)
+		return;
+
+	do {
+		uint16 maxValue = 0;
+		uint16 minValue = 0xFFFF;
+		int newNote = 0;
+
+		for (int i = 0; i < 2; ++i) {
+			if (_note[i].enabled) {
+				if (_note[i].hardwareChannel == 0xFF) {
+					if (_note[i].precedence >= maxValue) {
+						maxValue = _note[i].precedence;
+						newNote = i;
+					}
+				} else {
+					if (_note[i].precedence <= minValue) {
+						minValue = _note[i].precedence;
+						note = i;
+					}
+				}
+			}
+		}
+
+		if (maxValue < minValue)
+			return;
+
+		turnNoteOff(_note[note].hardwareChannel);
+		_note[note].enabled = false;
+
+		_note[newNote].hardwareChannel = _note[note].hardwareChannel;
+		++_channel[_note[newNote].midiChannel].noteCount;
+		_hardwareChannel[_note[note].hardwareChannel] = _note[newNote].midiChannel;
+		_note[newNote].flags = 0x01;
+
+		setupTone(newNote);
+	} while (--totalNotes);
+}
+
+void MidiDriver_PCSpeaker::noteOff(int channel, int note) {
+	for (int i = 0; i < 2; ++i) {
+		if (_note[i].enabled && _note[i].note == note && _note[i].midiChannel == channel) {
+			if (_channel[i].hold < 0x40) {
+				turnNoteOff(i);
+				_note[i].enabled = false;
+			} else {
+				_note[i].processHold = true;
+			}
+		}
+	}
+}
+
+void MidiDriver_PCSpeaker::turnNoteOff(int note) {
+	if (_note[note].hardwareChannel != 0xFF) {
+		_note[note].hardwareFlags &= 0xDF;
+		_note[note].flags |= 1;
+
+		setupTone(note);
+
+		--_channel[_note[note].midiChannel].noteCount;
+
+		_hardwareChannel[_note[note].hardwareChannel] = 0xFF;
+		_note[note].hardwareChannel = 0xFF;
+	}
+}
+
+void MidiDriver_PCSpeaker::setupTone(int note) {
+	if (_note[note].hardwareChannel == 0xFF)
+		return;
+
+	if (!(_note[note].flags & 0x01))
+		return;
+
+	if (!(_note[note].hardwareFlags & 0x20)) {
+		_speaker->stop();
+	} else {
+		const int midiChannel = _note[note].midiChannel;
+		uint16 pitchBend = (_channel[midiChannel].pitchBendHigh << 7) | _channel[midiChannel].pitchBendLow;
+
+		int noteValue = _note[note].note;
+
+		noteValue -= 24;
+		do {
+			noteValue += 12;
+		} while (noteValue < 0);
+
+		noteValue += 12;
+		do {
+			noteValue -= 12;
+		} while (noteValue > 95);
+
+		int16 modulation = _note[note].modulation;
+
+		int tableIndex = MAX(noteValue - 12, 0);
+		uint16 note1 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
+		tableIndex = MIN(noteValue + 12, 95);
+		uint16 note2 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
+		uint16 note3 = (_noteTable2[noteValue] << 8) | _noteTable1[noteValue];
+
+		int32 countdown = pitchBend - 0x2000;
+		countdown += modulation;
+
+		if (countdown >= 0)
+			countdown *= (note2 - note3);
+		else
+			countdown *= (note3 - note1);
+
+		countdown /= 0x2000;
+		countdown += note3;
+
+		countdown = uint16(countdown & 0xFFFF);
+		if (countdown != _countdown)
+			_countdown = countdown;
+
+		_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / _countdown, -1);
+	}
+
+	_note[note].flags &= 0xFE;
+}
+
+void MidiDriver_PCSpeaker::generateSamples(int16 *buffer, int numSamples) {
+	Common::StackLock lock(_mutex);
+	_speaker->readBuffer(buffer, numSamples);
+}
+
+void MidiDriver_PCSpeaker::onTimer() {
+	/*Common::StackLock lock(_mutex);
+
+	_timerValue += 20;
+	if (_timerValue < 120)
+		return;
+	_timerValue -= 120;
+
+	_modulationFlag = !_modulationFlag;
+	for (int i = 0; i < 2; ++i) {
+		if (_note[i].enabled) {
+			uint16 modValue = 5 * _channel[_note[i].midiChannel].modulation;
+			if (_modulationFlag)
+				modValue = -modValue;
+			_note[i].modulation = modValue;
+			_note[i].flags |= 1;
+
+			setupTone(i);
+		}
+	}*/
+}
+
+const uint8 MidiDriver_PCSpeaker::_noteTable1[] = {
+	0x88, 0xB5, 0x4E, 0x40, 0x41, 0xCD, 0xC4, 0x3D,
+	0x43, 0x7C, 0x2A, 0xD6, 0x88, 0xB5, 0xFF, 0xD1,
+	0x20, 0xA7, 0xE2, 0x1E, 0xCE, 0xBE, 0xF2, 0x8A,
+	0x44, 0x41, 0x7F, 0xE8, 0x90, 0x63, 0x63, 0x8F,
+	0xE7, 0x5F, 0x01, 0xBD, 0xA2, 0xA0, 0xBF, 0xF4,
+	0x48, 0xB1, 0x31, 0xC7, 0x70, 0x2F, 0xFE, 0xE0,
+	0xD1, 0xD0, 0xDE, 0xFB, 0x24, 0x58, 0x98, 0xE3,
+	0x39, 0x97, 0xFF, 0x6F, 0xE8, 0x68, 0xEF, 0x7D,
+	0x11, 0xAC, 0x4C, 0xF1, 0x9C, 0x4B, 0xFF, 0xB7,
+	0x74, 0x34, 0xF7, 0xBE, 0x88, 0x56, 0x26, 0xF8,
+	0xCE, 0xA5, 0x7F, 0x5B, 0x3A, 0x1A, 0xFB, 0xDF,
+	0xC4, 0xAB, 0x93, 0x7C, 0x67, 0x52, 0x3F, 0x2D
+};
+
+const uint8 MidiDriver_PCSpeaker::_noteTable2[] = {
+	0x8E, 0x86, 0xFD, 0xF0, 0xE2, 0xD5, 0xC9, 0xBE,
+	0xB3, 0xA9, 0xA0, 0x96, 0x8E, 0x86, 0x7E, 0x77,
+	0x71, 0x6A, 0x64, 0x5F, 0x59, 0x54, 0x4F, 0x4B,
+	0x47, 0x43, 0x3F, 0x3B, 0x38, 0x35, 0x32, 0x2F,
+	0x2C, 0x2A, 0x28, 0x25, 0x23, 0x21, 0x1F, 0x1D,
+	0x1C, 0x1A, 0x19, 0x17, 0x16, 0x15, 0x13, 0x12,
+	0x11, 0x10, 0x0F, 0x0E, 0x0E, 0x0D, 0x0C, 0x0B,
+	0x0B, 0x0A, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07,
+	0x07, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04,
+	0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_adlib.cpp b/engines/kyra/sound/sound_adlib.cpp
index 7a87e71..599221c 100644
--- a/engines/kyra/sound/sound_adlib.cpp
+++ b/engines/kyra/sound/sound_adlib.cpp
@@ -38,2258 +38,14 @@
 
 
 #include "kyra/sound/sound_intern.h"
-#include "kyra/resource/resource.h"
+#include "kyra/sound/drivers/adlib.h"
 
 #include "common/system.h"
-#include "common/mutex.h"
 #include "common/config-manager.h"
 
-#include "audio/fmopl.h"
-
-// Basic AdLib Programming:
-// http://www.gamedev.net/reference/articles/article446.asp
-
-#define CALLBACKS_PER_SECOND 72
-
-namespace Audio {
-class Mixer;
-}
 
 namespace Kyra {
 
-class AdLibDriver {
-public:
-	AdLibDriver(Audio::Mixer *mixer, int version);
-	~AdLibDriver();
-
-	void initDriver();
-	void setSoundData(uint8 *data, uint32 size);
-	void queueTrack(int track, int volume);
-	bool isChannelPlaying(int channel) const;
-	void stopAllChannels();
-	int getSoundTrigger() const { return _soundTrigger; }
-	void resetSoundTrigger() { _soundTrigger = 0; }
-
-	void callback();
-
-	void setSyncJumpMask(uint16 mask) { _syncJumpMask = mask; }
-
-	void setMusicVolume(uint8 volume);
-	void setSfxVolume(uint8 volume);
-
-private:
-	// These variables have not yet been named, but some of them are partly
-	// known nevertheless:
-	//
-	// pitchBend - Sound-related. Possibly some sort of pitch bend.
-	// unk18 - Sound-effect. Used for secondaryEffect1()
-	// unk19 - Sound-effect. Used for secondaryEffect1()
-	// unk20 - Sound-effect. Used for secondaryEffect1()
-	// unk21 - Sound-effect. Used for secondaryEffect1()
-	// unk22 - Sound-effect. Used for secondaryEffect1()
-	// unk29 - Sound-effect. Used for primaryEffect1()
-	// unk30 - Sound-effect. Used for primaryEffect1()
-	// unk31 - Sound-effect. Used for primaryEffect1()
-	// unk32 - Sound-effect. Used for primaryEffect2()
-	// unk33 - Sound-effect. Used for primaryEffect2()
-	// unk34 - Sound-effect. Used for primaryEffect2()
-	// unk35 - Sound-effect. Used for primaryEffect2()
-	// unk36 - Sound-effect. Used for primaryEffect2()
-	// unk37 - Sound-effect. Used for primaryEffect2()
-	// unk38 - Sound-effect. Used for primaryEffect2()
-	// unk39 - Currently unused, except for updateCallback56()
-	// unk40 - Currently unused, except for updateCallback56()
-	// unk41 - Sound-effect. Used for primaryEffect2()
-
-	struct Channel {
-		bool lock;	// New to ScummVM
-		uint8 opExtraLevel2;
-		const uint8 *dataptr;
-		uint8 duration;
-		uint8 repeatCounter;
-		int8 baseOctave;
-		uint8 priority;
-		uint8 dataptrStackPos;
-		const uint8 *dataptrStack[4];
-		int8 baseNote;
-		uint8 unk29;
-		uint8 unk31;
-		uint16 unk30;
-		uint16 unk37;
-		uint8 unk33;
-		uint8 unk34;
-		uint8 unk35;
-		uint8 unk36;
-		uint8 unk32;
-		uint8 unk41;
-		uint8 unk38;
-		uint8 opExtraLevel1;
-		uint8 spacing2;
-		uint8 baseFreq;
-		uint8 tempo;
-		uint8 position;
-		uint8 regAx;
-		uint8 regBx;
-		typedef void (AdLibDriver::*Callback)(Channel&);
-		Callback primaryEffect;
-		Callback secondaryEffect;
-		uint8 fractionalSpacing;
-		uint8 opLevel1;
-		uint8 opLevel2;
-		uint8 opExtraLevel3;
-		uint8 twoChan;
-		uint8 unk39;
-		uint8 unk40;
-		uint8 spacing1;
-		uint8 durationRandomness;
-		uint8 unk19;
-		uint8 unk18;
-		int8 unk20;
-		int8 unk21;
-		uint8 unk22;
-		uint16 offset;
-		uint8 tempoReset;
-		uint8 rawNote;
-		int8 pitchBend;
-		uint8 volumeModifier;
-	};
-
-	void primaryEffect1(Channel &channel);
-	void primaryEffect2(Channel &channel);
-	void secondaryEffect1(Channel &channel);
-
-	void resetAdLibState();
-	void writeOPL(byte reg, byte val);
-	void initChannel(Channel &channel);
-	void noteOff(Channel &channel);
-	void unkOutput2(uint8 num);
-
-	uint16 getRandomNr();
-	void setupDuration(uint8 duration, Channel &channel);
-
-	void setupNote(uint8 rawNote, Channel &channel, bool flag = false);
-	void setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel);
-	void noteOn(Channel &channel);
-
-	void adjustVolume(Channel &channel);
-
-	uint8 calculateOpLevel1(Channel &channel);
-	uint8 calculateOpLevel2(Channel &channel);
-
-	uint16 checkValue(int16 val) {
-		if (val < 0)
-			val = 0;
-		else if (val > 0x3F)
-			val = 0x3F;
-		return val;
-	}
-
-	// The sound data has at least two lookup tables:
-	//
-	// * One for programs, starting at offset 0.
-	// * One for instruments, starting at offset 500.
-
-	uint8 *getProgram(int progId) {
-		const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
-
-		// In case an invalid offset is specified we return nullptr to
-		// indicate an error. 0xFFFF seems to indicate "this is not a valid
-		// program/instrument". However, 0 is also invalid because it points
-		// inside the offset table itself. We also ignore any offsets outside
-		// of the actual data size.
-		// The original does not contain any safety checks and will simply
-		// read outside of the valid sound data in case an invalid offset is
-		// encountered.
-		if (offset == 0 || offset >= _soundDataSize) {
-			return nullptr;
-		} else {
-			return _soundData + offset;
-		}
-	}
-
-	const uint8 *getInstrument(int instrumentId) {
-		return getProgram(_numPrograms + instrumentId);
-	}
-
-	void setupPrograms();
-	void executePrograms();
-
-	struct ParserOpcode {
-		typedef int (AdLibDriver::*POpcode)(const uint8 *&dataptr, Channel &channel, uint8 value);
-		POpcode function;
-		const char *name;
-	};
-
-	void setupParserOpcodeTable();
-	const ParserOpcode *_parserOpcodeTable;
-	int _parserOpcodeTableSize;
-
-	int update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_jump(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_nop(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value);
-	int updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value);
-private:
-	// These variables have not yet been named, but some of them are partly
-	// known nevertheless:
-	//
-	// _unkValue1      - Unknown. Used for updating _unkValue2
-	// _unkValue2      - Unknown. Used for updating _unkValue4
-	// _unkValue4      - Unknown. Used for updating _unkValue5
-	// _unkValue5      - Unknown. Used for controlling updateCallback24().
-	// _unkValue6      - Unknown. Rhythm section volume?
-	// _unkValue7      - Unknown. Rhythm section volume?
-	// _unkValue8      - Unknown. Rhythm section volume?
-	// _unkValue9      - Unknown. Rhythm section volume?
-	// _unkValue10     - Unknown. Rhythm section volume?
-	// _unkValue11     - Unknown. Rhythm section volume?
-	// _unkValue12     - Unknown. Rhythm section volume?
-	// _unkValue13     - Unknown. Rhythm section volume?
-	// _unkValue14     - Unknown. Rhythm section volume?
-	// _unkValue15     - Unknown. Rhythm section volume?
-	// _unkValue16     - Unknown. Rhythm section volume?
-	// _unkValue17     - Unknown. Rhythm section volume?
-	// _unkValue18     - Unknown. Rhythm section volume?
-	// _unkValue19     - Unknown. Rhythm section volume?
-	// _unkValue20     - Unknown. Rhythm section volume?
-	// _freqTable[]     - Probably frequences for the 12-tone scale.
-	// _unkTable2[]    - Unknown. Currently only used by updateCallback46()
-	// _unkTable2_1[]  - One of the tables in _unkTable2[]
-	// _unkTable2_2[]  - One of the tables in _unkTable2[]
-	// _unkTable2_3[]  - One of the tables in _unkTable2[]
-
-	int _curChannel;
-	uint8 _soundTrigger;
-
-	uint16 _rnd;
-
-	uint8 _unkValue1;
-	uint8 _unkValue2;
-	uint8 _callbackTimer;
-	uint8 _unkValue4;
-	uint8 _unkValue5;
-	uint8 _unkValue6;
-	uint8 _unkValue7;
-	uint8 _unkValue8;
-	uint8 _unkValue9;
-	uint8 _unkValue10;
-	uint8 _unkValue11;
-	uint8 _unkValue12;
-	uint8 _unkValue13;
-	uint8 _unkValue14;
-	uint8 _unkValue15;
-	uint8 _unkValue16;
-	uint8 _unkValue17;
-	uint8 _unkValue18;
-	uint8 _unkValue19;
-	uint8 _unkValue20;
-
-	OPL::OPL *_adlib;
-
-	uint8 *_soundData;
-	uint32 _soundDataSize;
-
-	struct QueueEntry {
-		QueueEntry() : data(0), id(0), volume(0) {}
-		QueueEntry(uint8 *ptr, uint8 track, uint8 vol) : data(ptr), id(track), volume(vol) {}
-		uint8 *data;
-		uint8 id;
-		uint8 volume;
-	};
-
-	QueueEntry _programQueue[16];
-	int _programStartTimeout;
-	int _programQueueStart, _programQueueEnd;
-	bool _retrySounds;
-
-	void adjustSfxData(uint8 *data, int volume);
-	uint8 *_sfxPointer;
-	int _sfxPriority;
-	int _sfxVelocity;
-
-	Channel _channels[10];
-
-	uint8 _vibratoAndAMDepthBits;
-	uint8 _rhythmSectionBits;
-
-	uint8 _curRegOffset;
-	uint8 _tempo;
-
-	const uint8 *_tablePtr1;
-	const uint8 *_tablePtr2;
-
-	static const uint8 _regOffset[];
-	static const uint16 _freqTable[];
-	static const uint8 *const _unkTable2[];
-	static const uint8 _unkTable2_1[];
-	static const uint8 _unkTable2_2[];
-	static const uint8 _unkTable2_3[];
-	static const uint8 _pitchBendTables[][32];
-
-	uint16 _syncJumpMask;
-
-	Common::Mutex _mutex;
-	Audio::Mixer *_mixer;
-
-	uint8 _musicVolume, _sfxVolume;
-
-	int _numPrograms;
-	int _version;
-};
-
-AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
-	setupParserOpcodeTable();
-
-	_version = version;
-	_numPrograms = (_version == 1) ? 150 : ((_version == 4) ? 500 : 250);
-
-	_mixer = mixer;
-
-	_adlib = OPL::Config::create();
-	if (!_adlib || !_adlib->init())
-		error("Failed to create OPL");
-
-	memset(_channels, 0, sizeof(_channels));
-	_soundData = 0;
-	_soundDataSize = 0;
-
-	_vibratoAndAMDepthBits = _curRegOffset = 0;
-
-	_curChannel = _rhythmSectionBits = 0;
-	_rnd = 0x1234;
-
-	_tempo = 0;
-	_soundTrigger = 0;
-	_programStartTimeout = 0;
-
-	_callbackTimer = 0xFF;
-	_unkValue1 = _unkValue2 = _unkValue4 = _unkValue5 = 0;
-	_unkValue6 = _unkValue7 = _unkValue8 = _unkValue9 = _unkValue10 = 0;
-	_unkValue11 = _unkValue12 = _unkValue13 = _unkValue14 = _unkValue15 =
-	_unkValue16 = _unkValue17 = _unkValue18 = _unkValue19 = _unkValue20 = 0;
-
-	_tablePtr1 = _tablePtr2 = 0;
-
-	_syncJumpMask = 0;
-
-	_musicVolume = 0;
-	_sfxVolume = 0;
-
-	_sfxPointer = 0;
-
-	_programQueueStart = _programQueueEnd = 0;
-	_retrySounds = false;
-
-	_adlib->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::callback), CALLBACKS_PER_SECOND);
-}
-
-AdLibDriver::~AdLibDriver() {
-	delete _adlib;
-	_adlib = 0;
-}
-
-void AdLibDriver::setMusicVolume(uint8 volume) {
-	Common::StackLock lock(_mutex);
-
-	_musicVolume = volume;
-
-	for (uint i = 0; i < 6; ++i) {
-		Channel &chan = _channels[i];
-		chan.volumeModifier = volume;
-
-		const uint8 regOffset = _regOffset[i];
-
-		// Level Key Scaling / Total Level
-		writeOPL(0x40 + regOffset, calculateOpLevel1(chan));
-		writeOPL(0x43 + regOffset, calculateOpLevel2(chan));
-	}
-
-	// For now we use the music volume for both sfx and music in Kyra1 and EoB
-	if (_version < 4) {
-		_sfxVolume = volume;
-
-		for (uint i = 6; i < 9; ++i) {
-			Channel &chan = _channels[i];
-			chan.volumeModifier = volume;
-
-			const uint8 regOffset = _regOffset[i];
-
-			// Level Key Scaling / Total Level
-			writeOPL(0x40 + regOffset, calculateOpLevel1(chan));
-			writeOPL(0x43 + regOffset, calculateOpLevel2(chan));
-		}
-	}
-}
-
-void AdLibDriver::setSfxVolume(uint8 volume) {
-	// We only support sfx volume in version 4 games.
-	if (_version < 4)
-		return;
-
-	Common::StackLock lock(_mutex);
-
-	_sfxVolume = volume;
-
-	for (uint i = 6; i < 9; ++i) {
-		Channel &chan = _channels[i];
-		chan.volumeModifier = volume;
-
-		const uint8 regOffset = _regOffset[i];
-
-		// Level Key Scaling / Total Level
-		writeOPL(0x40 + regOffset, calculateOpLevel1(chan));
-		writeOPL(0x43 + regOffset, calculateOpLevel2(chan));
-	}
-}
-
-void AdLibDriver::initDriver() {
-	Common::StackLock lock(_mutex);
-	resetAdLibState();
-}
-
-void AdLibDriver::setSoundData(uint8 *data, uint32 size) {
-	Common::StackLock lock(_mutex);
-
-	// Drop all tracks that are still queued. These would point to the old
-	// sound data.
-	_programQueueStart = _programQueueEnd = 0;
-	memset(_programQueue, 0, sizeof(_programQueue));
-
-	if (_soundData) {
-		delete[] _soundData;
-		_soundData = _sfxPointer = 0;
-	}
-
-	_soundData = data;
-	_soundDataSize = size;
-}
-
-void AdLibDriver::queueTrack(int track, int volume) {
-	Common::StackLock lock(_mutex);
-
-	uint8 *trackData = getProgram(track);
-	if (!trackData)
-		return;
-
-	// Don't drop tracks in EoB. The queue is always full there if a couple of monsters are around.
-	// If we drop the incoming tracks we get no sound effects, but tons of warnings instead.
-	if (_version >= 3 && _programQueueEnd == _programQueueStart && _programQueue[_programQueueEnd].data != 0) {
-		warning("AdLibDriver: Program queue full, dropping track %d", track);
-		return;
-	}
-
-	_programQueue[_programQueueEnd] = QueueEntry(trackData, track, volume);
-	_programQueueEnd = (_programQueueEnd + 1) & 15;
-}
-
-bool AdLibDriver::isChannelPlaying(int channel) const {
-	Common::StackLock lock(_mutex);
-
-	assert(channel >= 0 && channel <= 9);
-	return (_channels[channel].dataptr != 0);
-}
-
-void AdLibDriver::stopAllChannels() {
-	Common::StackLock lock(_mutex);
-
-	for (int channel = 0; channel <= 9; ++channel) {
-		_curChannel = channel;
-
-		Channel &chan = _channels[_curChannel];
-		chan.priority = 0;
-		chan.dataptr = 0;
-
-		if (channel != 9)
-			noteOff(chan);
-	}
-	_retrySounds = false;
-}
-
-// timer callback
-
-void AdLibDriver::callback() {
-	Common::StackLock lock(_mutex);
-	if (_programStartTimeout)
-		--_programStartTimeout;
-	else
-		setupPrograms();
-	executePrograms();
-
-	uint8 temp = _callbackTimer;
-	_callbackTimer += _tempo;
-	if (_callbackTimer < temp) {
-		if (!(--_unkValue2)) {
-			_unkValue2 = _unkValue1;
-			++_unkValue4;
-		}
-	}
-}
-
-void AdLibDriver::setupPrograms() {
-	// If there is no program queued, we skip this.
-	if (_programQueueStart == _programQueueEnd)
-		return;
-
-	uint8 *ptr = _programQueue[_programQueueStart].data;
-
-	// The AdLib driver (in its old versions used for EOB) is not suitable for modern (fast) CPUs.
-	// The stop sound track (track 0 which has a priority of 50) will often still be busy when the
-	// next sound (with a lower priority) starts which will cause that sound to be skipped. We simply
-	// restart incoming sounds during stop sound execution.
-	// UPDATE: This stilly applies after introduction of the _programQueue.
-	QueueEntry retrySound;
-	if (_version < 3 && _programQueue[_programQueueStart].id == 0)
-		_retrySounds = true;
-	else if (_retrySounds)
-		retrySound = _programQueue[_programQueueStart];
-
-	// Adjust data in case we hit a sound effect.
-	adjustSfxData(ptr, _programQueue[_programQueueStart].volume);
-
-	// Clear the queue entry
-	_programQueue[_programQueueStart].data = 0;
-	_programQueueStart = (_programQueueStart + 1) & 15;
-
-	const int chan = *ptr++;
-	const int priority = *ptr++;
-
-	// Only start this sound if its priority is higher than the one
-	// already playing.
-
-	Channel &channel = _channels[chan];
-
-	if (priority >= channel.priority) {
-		initChannel(channel);
-		channel.priority = priority;
-		channel.dataptr = ptr;
-		channel.tempo = 0xFF;
-		channel.position = 0xFF;
-		channel.duration = 1;
-
-		if (chan <= 5)
-			channel.volumeModifier = _musicVolume;
-		else
-			channel.volumeModifier = _sfxVolume;
-
-		unkOutput2(chan);
-
-		// We need to wait two callback calls till we can start another track.
-		// This is (probably) required to assure that the sfx are started with
-		// the correct priority and velocity.
-		_programStartTimeout = 2;
-
-		retrySound = QueueEntry();
-	}
-
-	if (retrySound.data) {
-		debugC(9, kDebugLevelSound, "AdLibDriver::setupPrograms(): WORKAROUND - Restarting skipped sound %d)", retrySound.id);
-		queueTrack(retrySound.id, retrySound.volume);
-	}
-}
-
-void AdLibDriver::adjustSfxData(uint8 *ptr, int volume) {
-	// Check whether we need to reset the data of an old sfx which has been
-	// started.
-	if (_sfxPointer) {
-		_sfxPointer[1] = _sfxPriority;
-		_sfxPointer[3] = _sfxVelocity;
-		_sfxPointer = 0;
-	}
-
-	// Only music tracks are started on channel 9, thus we need to make sure
-	// we do not have a music track here.
-	if (*ptr == 9)
-		return;
-
-	// Store the pointer so we can reset the data when a new program is started.
-	_sfxPointer = ptr;
-
-	// Store the old values.
-	_sfxPriority = ptr[1];
-	_sfxVelocity = ptr[3];
-
-	// Adjust the values.
-	if (volume != 0xFF) {
-		if (_version >= 3) {
-			int newVal = ((((ptr[3]) + 63) * volume) >> 8) & 0xFF;
-			ptr[3] = -newVal + 63;
-			ptr[1] = ((ptr[1] * volume) >> 8) & 0xFF;
-		} else {
-			int newVal = ((_sfxVelocity << 2) ^ 0xFF) * volume;
-			ptr[3] = (newVal >> 10) ^ 0x3F;
-			ptr[1] = newVal >> 11;
-		}
-	}
-}
-
-// A few words on opcode parsing and timing:
-//
-// First of all, We simulate a timer callback 72 times per second. Each timeout
-// we update each channel that has something to play.
-//
-// Each channel has its own individual tempo, which is added to its position.
-// This will frequently cause the position to "wrap around" but that is
-// intentional. In fact, it's the signal to go ahead and do more stuff with
-// that channel.
-//
-// Each channel also has a duration, indicating how much time is left on the
-// its current task. This duration is decreased by one. As long as it still has
-// not reached zero, the only thing that can happen is that the note is turned
-// off depending on manual or automatic note spacing. Once the duration reaches
-// zero, a new set of musical opcodes are executed.
-//
-// An opcode is one byte, followed by a variable number of parameters. Since
-// most opcodes have at least one one-byte parameter, we read that as well. Any
-// opcode that doesn't have that one parameter is responsible for moving the
-// data pointer back again.
-//
-// If the most significant bit of the opcode is 1, it's a function; call it.
-// The opcode functions return either 0 (continue), 1 (stop) or 2 (stop, and do
-// not run the effects callbacks).
-//
-// If the most significant bit of the opcode is 0, it's a note, and the first
-// parameter is its duration. (There are cases where the duration is modified
-// but that's an exception.) The note opcode is assumed to return 1, and is the
-// last opcode unless its duration is zero.
-//
-// Finally, most of the times that the callback is called, it will invoke the
-// effects callbacks. The final opcode in a set can prevent this, if it's a
-// function and it returns anything other than 1.
-
-void AdLibDriver::executePrograms() {
-	// Each channel runs its own program. There are ten channels: One for
-	// each AdLib channel (0-8), plus one "control channel" (9) which is
-	// the one that tells the other channels what to do.
-
-	// This is where we ensure that channels that are made to jump "in
-	// sync" do so.
-
-	if (_syncJumpMask) {
-		bool forceUnlock = true;
-
-		for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
-			if ((_syncJumpMask & (1 << _curChannel)) == 0)
-				continue;
-
-			if (_channels[_curChannel].dataptr && !_channels[_curChannel].lock)
-				forceUnlock = false;
-		}
-
-		if (forceUnlock) {
-			for (_curChannel = 9; _curChannel >= 0; --_curChannel)
-				if (_syncJumpMask & (1 << _curChannel))
-					_channels[_curChannel].lock = false;
-		}
-	}
-
-	for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
-		int result = 1;
-
-		if (!_channels[_curChannel].dataptr)
-			continue;
-
-		if (_channels[_curChannel].lock && (_syncJumpMask & (1 << _curChannel)))
-			continue;
-
-		Channel &channel = _channels[_curChannel];
-		if (_curChannel == 9)
-			_curRegOffset = 0;
-		else
-			_curRegOffset = _regOffset[_curChannel];
-
-		if (channel.tempoReset)
-			channel.tempo = _tempo;
-
-		uint8 backup = channel.position;
-		channel.position += channel.tempo;
-		if (channel.position < backup) {
-			if (--channel.duration) {
-				if (channel.duration == channel.spacing2)
-					noteOff(channel);
-				if (channel.duration == channel.spacing1 && _curChannel != 9)
-					noteOff(channel);
-			} else {
-				// An opcode is not allowed to modify its own
-				// data pointer except through the 'dataptr'
-				// parameter. To enforce that, we have to work
-				// on a copy of the data pointer.
-				//
-				// This fixes a subtle music bug where the
-				// wrong music would play when getting the
-				// quill in Kyra 1.
-				const uint8 *dataptr = channel.dataptr;
-				while (dataptr) {
-					uint8 opcode = *dataptr++;
-					uint8 param = *dataptr++;
-
-					if (opcode & 0x80) {
-						opcode &= 0x7F;
-						if (opcode >= _parserOpcodeTableSize)
-							opcode = _parserOpcodeTableSize - 1;
-						debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", _parserOpcodeTable[opcode].name, opcode, _curChannel);
-						result = (this->*(_parserOpcodeTable[opcode].function))(dataptr, channel, param);
-						channel.dataptr = dataptr;
-						if (result)
-							break;
-					} else {
-						debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, param, _curChannel);
-						setupNote(opcode, channel);
-						noteOn(channel);
-						setupDuration(param, channel);
-						if (param) {
-							// We need to make sure we are always running the
-							// effects after this. Otherwise some sounds are
-							// wrong. Like the sfx when bumping into a wall in
-							// LoL.
-							result = 1;
-							channel.dataptr = dataptr;
-							break;
-						}
-					}
-				}
-			}
-		}
-
-		if (result == 1) {
-			if (channel.primaryEffect)
-				(this->*(channel.primaryEffect))(channel);
-			if (channel.secondaryEffect)
-				(this->*(channel.secondaryEffect))(channel);
-		}
-	}
-}
-
-//
-
-void AdLibDriver::resetAdLibState() {
-	debugC(9, kDebugLevelSound, "resetAdLibState()");
-	_rnd = 0x1234;
-
-	// Authorize the control of the waveforms
-	writeOPL(0x01, 0x20);
-
-	// Select FM music mode
-	writeOPL(0x08, 0x00);
-
-	// I would guess the main purpose of this is to turn off the rhythm,
-	// thus allowing us to use 9 melodic voices instead of 6.
-	writeOPL(0xBD, 0x00);
-
-	int loop = 10;
-	while (loop--) {
-		if (loop != 9) {
-			// Silence the channel
-			writeOPL(0x40 + _regOffset[loop], 0x3F);
-			writeOPL(0x43 + _regOffset[loop], 0x3F);
-		}
-		initChannel(_channels[loop]);
-	}
-}
-
-// Old calling style: output0x388(0xABCD)
-// New calling style: writeOPL(0xAB, 0xCD)
-
-void AdLibDriver::writeOPL(byte reg, byte val) {
-	_adlib->writeReg(reg, val);
-}
-
-void AdLibDriver::initChannel(Channel &channel) {
-	debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels));
-	memset(&channel.dataptr, 0, sizeof(Channel) - ((char *)&channel.dataptr - (char *)&channel));
-
-	channel.tempo = 0xFF;
-	channel.priority = 0;
-	// normally here are nullfuncs but we set 0 for now
-	channel.primaryEffect = 0;
-	channel.secondaryEffect = 0;
-	channel.spacing1 = 1;
-	channel.lock = false;
-}
-
-void AdLibDriver::noteOff(Channel &channel) {
-	debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels));
-
-	// The control channel has no corresponding AdLib channel
-
-	if (_curChannel >= 9)
-		return;
-
-	// When the rhythm section is enabled, channels 6, 7 and 8 are special.
-
-	if (_rhythmSectionBits && _curChannel >= 6)
-		return;
-
-	// This means the "Key On" bit will always be 0
-	channel.regBx &= 0xDF;
-
-	// Octave / F-Number / Key-On
-	writeOPL(0xB0 + _curChannel, channel.regBx);
-}
-
-void AdLibDriver::unkOutput2(uint8 chan) {
-	debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan);
-
-	// The control channel has no corresponding AdLib channel
-
-	if (chan >= 9)
-		return;
-
-	// I believe this has to do with channels 6, 7, and 8 being special
-	// when AdLib's rhythm section is enabled.
-
-	if (_rhythmSectionBits && chan >= 6)
-		return;
-
-	uint8 offset = _regOffset[chan];
-
-	// The channel is cleared: First the attack/delay rate, then the
-	// sustain level/release rate, and finally the note is turned off.
-
-	writeOPL(0x60 + offset, 0xFF);
-	writeOPL(0x63 + offset, 0xFF);
-
-	writeOPL(0x80 + offset, 0xFF);
-	writeOPL(0x83 + offset, 0xFF);
-
-	writeOPL(0xB0 + chan, 0x00);
-
-	// ...and then the note is turned on again, with whatever value is
-	// still lurking in the A0 + chan register, but everything else -
-	// including the two most significant frequency bit, and the octave -
-	// set to zero.
-	//
-	// This is very strange behavior, and causes problems with the ancient
-	// FMOPL code we borrowed from AdPlug. I've added a workaround. See
-	// audio/softsynth/opl/mame.cpp for more details.
-	//
-	// Fortunately, the more modern DOSBox FMOPL code does not seem to have
-	// any trouble with this.
-
-	writeOPL(0xB0 + chan, 0x20);
-}
-
-// I believe this is a random number generator. It actually does seem to
-// generate an even distribution of almost all numbers from 0 through 65535,
-// though in my tests some numbers were never generated.
-
-uint16 AdLibDriver::getRandomNr() {
-	_rnd += 0x9248;
-	uint16 lowBits = _rnd & 7;
-	_rnd >>= 3;
-	_rnd |= (lowBits << 13);
-	return _rnd;
-}
-
-void AdLibDriver::setupDuration(uint8 duration, Channel &channel) {
-	debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels));
-	if (channel.durationRandomness) {
-		channel.duration = duration + (getRandomNr() & channel.durationRandomness);
-		return;
-	}
-	if (channel.fractionalSpacing)
-		channel.spacing2 = (duration >> 3) * channel.fractionalSpacing;
-	channel.duration = duration;
-}
-
-// This function may or may not play the note. It's usually followed by a call
-// to noteOn(), which will always play the current note.
-
-void AdLibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) {
-	debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels));
-
-	if (_curChannel >= 9)
-		return;
-
-	channel.rawNote = rawNote;
-
-	int8 note = (rawNote & 0x0F) + channel.baseNote;
-	int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F;
-
-	// There are only twelve notes. If we go outside that, we have to
-	// adjust the note and octave.
-
-	if (note >= 12) {
-		note -= 12;
-		octave++;
-	} else if (note < 0) {
-		note += 12;
-		octave--;
-	}
-
-	// The calculation of frequency looks quite different from the original
-	// disassembly at a first glance, but when you consider that the
-	// largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if
-	// baseFreq is unsigned), freq is still a 10-bit value, just as it
-	// should be to fit in the Ax and Bx registers.
-	//
-	// If it were larger than that, it could have overflowed into the
-	// octave bits, and that could possibly have been used in some sound.
-	// But as it is now, I can't see any way it would happen.
-
-	uint16 freq = _freqTable[note] + channel.baseFreq;
-
-	// When called from callback 41, the behavior is slightly different:
-	// We adjust the frequency, even when channel.pitchBend is 0.
-
-	if (channel.pitchBend || flag) {
-		const uint8 *table;
-
-		if (channel.pitchBend >= 0) {
-			table = _pitchBendTables[(channel.rawNote & 0x0F) + 2];
-			freq += table[channel.pitchBend];
-		} else {
-			table = _pitchBendTables[channel.rawNote & 0x0F];
-			freq -= table[-channel.pitchBend];
-		}
-	}
-
-	channel.regAx = freq & 0xFF;
-	channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03);
-
-	// Keep the note on or off
-	writeOPL(0xA0 + _curChannel, channel.regAx);
-	writeOPL(0xB0 + _curChannel, channel.regBx);
-}
-
-void AdLibDriver::setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel) {
-	debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels));
-
-	if (_curChannel >= 9)
-		return;
-
-	// Amplitude Modulation / Vibrato / Envelope Generator Type /
-	// Keyboard Scaling Rate / Modulator Frequency Multiple
-	writeOPL(0x20 + regOffset, *dataptr++);
-	writeOPL(0x23 + regOffset, *dataptr++);
-
-	uint8 temp = *dataptr++;
-
-	// Feedback / Algorithm
-
-	// It is very likely that _curChannel really does refer to the same
-	// channel as regOffset, but there's only one Cx register per channel.
-
-	writeOPL(0xC0 + _curChannel, temp);
-
-	// The algorithm bit. I don't pretend to understand this fully, but
-	// "If set to 0, operator 1 modulates operator 2. In this case,
-	// operator 2 is the only one producing sound. If set to 1, both
-	// operators produce sound directly. Complex sounds are more easily
-	// created if the algorithm is set to 0."
-
-	channel.twoChan = temp & 1;
-
-	// Waveform Select
-	writeOPL(0xE0 + regOffset, *dataptr++);
-	writeOPL(0xE3 + regOffset, *dataptr++);
-
-	channel.opLevel1 = *dataptr++;
-	channel.opLevel2 = *dataptr++;
-
-	// Level Key Scaling / Total Level
-	writeOPL(0x40 + regOffset, calculateOpLevel1(channel));
-	writeOPL(0x43 + regOffset, calculateOpLevel2(channel));
-
-	// Attack Rate / Decay Rate
-	writeOPL(0x60 + regOffset, *dataptr++);
-	writeOPL(0x63 + regOffset, *dataptr++);
-
-	// Sustain Level / Release Rate
-	writeOPL(0x80 + regOffset, *dataptr++);
-	writeOPL(0x83 + regOffset, *dataptr++);
-}
-
-// Apart from playing the note, this function also updates the variables for
-// primary effect 2.
-
-void AdLibDriver::noteOn(Channel &channel) {
-	debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels));
-
-	// The "note on" bit is set, and the current note is played.
-
-	if (_curChannel >= 9)
-		return;
-
-	channel.regBx |= 0x20;
-	writeOPL(0xB0 + _curChannel, channel.regBx);
-
-	int8 shift = 9 - channel.unk33;
-	uint16 temp = channel.regAx | (channel.regBx << 8);
-	channel.unk37 = ((temp & 0x3FF) >> shift) & 0xFF;
-	channel.unk38 = channel.unk36;
-}
-
-void AdLibDriver::adjustVolume(Channel &channel) {
-	debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels));
-
-	if (_curChannel >= 9)
-		return;
-
-	// Level Key Scaling / Total Level
-
-	writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel));
-	if (channel.twoChan)
-		writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel));
-}
-
-// This is presumably only used for some sound effects, e.g. Malcolm blowing up
-// the trees in the intro (but not the effect where he "booby-traps" the big
-// tree) and turning Kallak to stone. Related functions and variables:
-//
-// update_setupPrimaryEffect1()
-//    - Initializes unk29, unk30 and unk31
-//    - unk29 is not further modified
-//    - unk30 is not further modified, except by update_removePrimaryEffect1()
-//
-// update_removePrimaryEffect1()
-//    - Deinitializes unk30
-//
-// unk29 - determines how often the notes are played
-// unk30 - modifies the frequency
-// unk31 - determines how often the notes are played
-
-void AdLibDriver::primaryEffect1(Channel &channel) {
-	debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel);
-
-	if (_curChannel >= 9)
-		return;
-
-	uint8 temp = channel.unk31;
-	channel.unk31 += channel.unk29;
-	if (channel.unk31 >= temp)
-		return;
-
-	// Initialize unk1 to the current frequency
-	int16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx;
-
-	// This is presumably to shift the "note on" bit so far to the left
-	// that it won't be affected by any of the calculations below.
-	int16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C);
-
-	int16 unk3 = (int16)channel.unk30;
-
-	if (unk3 >= 0) {
-		unk1 += unk3;
-		if (unk1 >= 734) {
-			// The new frequency is too high. Shift it down and go
-			// up one octave.
-			unk1 >>= 1;
-			if (!(unk1 & 0x3FF))
-				++unk1;
-			unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF);
-			unk2 &= 0xFF1C;
-		}
-	} else {
-		unk1 += unk3;
-		if (unk1 < 388) {
-			// The new frequency is too low. Shift it up and go
-			// down one octave.
-			unk1 <<= 1;
-			if (!(unk1 & 0x3FF))
-				--unk1;
-			unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF);
-			unk2 &= 0xFF1C;
-		}
-	}
-
-	// Make sure that the new frequency is still a 10-bit value.
-	unk1 &= 0x3FF;
-
-	writeOPL(0xA0 + _curChannel, unk1 & 0xFF);
-	channel.regAx = unk1 & 0xFF;
-
-	// Shift down the "note on" bit again.
-	uint8 value = unk1 >> 8;
-	value |= (unk2 >> 8) & 0xFF;
-	value |= unk2 & 0xFF;
-
-	writeOPL(0xB0 + _curChannel, value);
-	channel.regBx = value;
-}
-
-// This is presumably only used for some sound effects, e.g. Malcolm entering
-// and leaving Kallak's hut. Related functions and variables:
-//
-// update_setupPrimaryEffect2()
-//    - Initializes unk32, unk33, unk34, unk35 and unk36
-//    - unk32 is not further modified
-//    - unk33 is not further modified
-//    - unk34 is a countdown that gets reinitialized to unk35 on zero
-//    - unk35 is based on unk34 and not further modified
-//    - unk36 is not further modified
-//
-// noteOn()
-//    - Plays the current note
-//    - Updates unk37 with a new (lower?) frequency
-//    - Copies unk36 to unk38. The unk38 variable is a countdown.
-//
-// unk32 - determines how often the notes are played
-// unk33 - modifies the frequency
-// unk34 - countdown, updates frequency on zero
-// unk35 - initializer for unk34 countdown
-// unk36 - initializer for unk38 countdown
-// unk37 - frequency
-// unk38 - countdown, begins playing on zero
-// unk41 - determines how often the notes are played
-//
-// Note that unk41 is never initialized. Not that it should matter much, but it
-// is a bit sloppy.
-
-void AdLibDriver::primaryEffect2(Channel &channel) {
-	debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel);
-
-	if (_curChannel >= 9)
-		return;
-
-	if (channel.unk38) {
-		--channel.unk38;
-		return;
-	}
-
-	uint8 temp = channel.unk41;
-	channel.unk41 += channel.unk32;
-	if (channel.unk41 < temp) {
-		uint16 unk1 = channel.unk37;
-		if (!(--channel.unk34)) {
-			unk1 ^= 0xFFFF;
-			++unk1;
-			channel.unk37 = unk1;
-			channel.unk34 = channel.unk35;
-		}
-
-		uint16 unk2 = (channel.regAx | (channel.regBx << 8)) & 0x3FF;
-		unk2 += unk1;
-
-		channel.regAx = unk2 & 0xFF;
-		channel.regBx = (channel.regBx & 0xFC) | (unk2 >> 8);
-
-		// Octave / F-Number / Key-On
-		writeOPL(0xA0 + _curChannel, channel.regAx);
-		writeOPL(0xB0 + _curChannel, channel.regBx);
-	}
-}
-
-// I don't know where this is used. The same operation is performed several
-// times on the current channel, using a chunk of the _soundData[] buffer for
-// parameters. The parameters are used starting at the end of the chunk.
-//
-// Since we use _curRegOffset to specify the final register, it's quite
-// unlikely that this function is ever used to play notes. It's probably only
-// used to modify the sound. Another thing that supports this idea is that it
-// can be combined with any of the effects callbacks above.
-//
-// Related functions and variables:
-//
-// update_setupSecondaryEffect1()
-//    - Initialies unk18, unk19, unk20, unk21, unk22 and offset
-//    - unk19 is not further modified
-//    - unk20 is not further modified
-//    - unk22 is not further modified
-//    - offset is not further modified
-//
-// unk18 -  determines how often the operation is performed
-// unk19 -  determines how often the operation is performed
-// unk20 -  the start index into the data chunk
-// unk21 -  the current index into the data chunk
-// unk22 -  the operation to perform
-// offset - the offset to the data chunk
-
-void AdLibDriver::secondaryEffect1(Channel &channel) {
-	debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel);
-
-	if (_curChannel >= 9)
-		return;
-
-	uint8 temp = channel.unk18;
-	channel.unk18 += channel.unk19;
-	if (channel.unk18 < temp) {
-		if (--channel.unk21 < 0)
-			channel.unk21 = channel.unk20;
-		writeOPL(channel.unk22 + _curRegOffset, _soundData[channel.offset + channel.unk21]);
-	}
-}
-
-uint8 AdLibDriver::calculateOpLevel1(Channel &channel) {
-	int8 value = channel.opLevel1 & 0x3F;
-
-	if (channel.twoChan) {
-		value += channel.opExtraLevel1;
-		value += channel.opExtraLevel2;
-
-		uint16 level3 = (channel.opExtraLevel3 ^ 0x3F) * channel.volumeModifier;
-		if (level3) {
-			level3 += 0x3F;
-			level3 >>= 8;
-		}
-
-		value += level3 ^ 0x3F;
-	}
-
-	value = CLIP<int8>(value, 0, 0x3F);
-
-	if (!channel.volumeModifier)
-		value = 0x3F;
-
-	// Preserve the scaling level bits from opLevel1
-
-	return checkValue(value) | (channel.opLevel1 & 0xC0);
-}
-
-uint8 AdLibDriver::calculateOpLevel2(Channel &channel) {
-	int8 value = channel.opLevel2 & 0x3F;
-
-	value += channel.opExtraLevel1;
-	value += channel.opExtraLevel2;
-
-	uint16 level3 = (channel.opExtraLevel3 ^ 0x3F) * channel.volumeModifier;
-	if (level3) {
-		level3 += 0x3F;
-		level3 >>= 8;
-	}
-
-	value += level3 ^ 0x3F;
-
-	value = CLIP<int8>(value, 0, 0x3F);
-
-	if (!channel.volumeModifier)
-		value = 0x3F;
-
-	// Preserve the scaling level bits from opLevel2
-
-	return checkValue(value) | (channel.opLevel2 & 0xC0);
-}
-
-// parser opcodes
-
-int AdLibDriver::update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.repeatCounter = value;
-	return 0;
-}
-
-int AdLibDriver::update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	++dataptr;
-	if (--channel.repeatCounter) {
-		int16 add = READ_LE_UINT16(dataptr - 2);
-		dataptr += add;
-	}
-	return 0;
-}
-
-int AdLibDriver::update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	if (value == 0xFF)
-		return 0;
-
-	const uint8 *ptr = getProgram(value);
-
-	// In case we encounter an invalid program we simply ignore it and do
-	// nothing instead. The original did not care about invalid programs and
-	// simply tried to play them anyway... But to avoid crashes due we ingore
-	// them.
-	// This, for example, happens in the Lands of Lore intro when Scotia gets
-	// the ring in the intro.
-	if (!ptr) {
-		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupProgram: Invalid program %d specified", value);
-		return 0;
-	}
-
-	uint8 chan = *ptr++;
-	uint8 priority = *ptr++;
-
-	Channel &channel2 = _channels[chan];
-
-	if (priority >= channel2.priority) {
-		// We keep new tracks from being started for two further iterations of
-		// the callback. This assures the correct velocity is used for this
-		// program.
-		_programStartTimeout = 2;
-		initChannel(channel2);
-		channel2.priority = priority;
-		channel2.dataptr = ptr;
-		channel2.tempo = 0xFF;
-		channel2.position = 0xFF;
-		channel2.duration = 1;
-
-		if (chan <= 5)
-			channel2.volumeModifier = _musicVolume;
-		else
-			channel2.volumeModifier = _sfxVolume;
-
-		unkOutput2(chan);
-	}
-
-	return 0;
-}
-
-int AdLibDriver::update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.spacing1 = value;
-	return 0;
-}
-
-int AdLibDriver::update_jump(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
-	if (_version == 1)
-		dataptr = _soundData + add - 191;
-	else
-		dataptr += add;
-	if (_syncJumpMask & (1 << (&channel - _channels)))
-		channel.lock = true;
-	return 0;
-}
-
-int AdLibDriver::update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
-	channel.dataptrStack[channel.dataptrStackPos++] = dataptr;
-	if (_version < 3)
-		dataptr = _soundData + add - 191;
-	else
-		dataptr += add;
-	return 0;
-}
-
-int AdLibDriver::update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	dataptr = channel.dataptrStack[--channel.dataptrStackPos];
-	return 0;
-}
-
-int AdLibDriver::update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.baseOctave = value;
-	return 0;
-}
-
-int AdLibDriver::update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.priority = 0;
-	if (_curChannel != 9)
-		noteOff(channel);
-	dataptr = 0;
-	return 2;
-}
-
-int AdLibDriver::update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	setupDuration(value, channel);
-	noteOff(channel);
-	return (value != 0);
-}
-
-int AdLibDriver::update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	writeOPL(value, *dataptr++);
-	return 0;
-}
-
-int AdLibDriver::update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	setupNote(value, channel);
-	value = *dataptr++;
-	setupDuration(value, channel);
-	return (value != 0);
-}
-
-int AdLibDriver::update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.baseNote = value;
-	return 0;
-}
-
-int AdLibDriver::update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.unk18 = value;
-	channel.unk19 = value;
-	channel.unk20 = channel.unk21 = *dataptr++;
-	channel.unk22 = *dataptr++;
-	// WORKAROUND: The original code reads a true offset which later gets translated via xlat (in
-	// the current segment). This means that the outcome depends on the sound data offset.
-	// Unfortunately this offset is different in most implementations of the audio driver and
-	// probably also different from the offset assumed by the sequencer.
-	// It seems that the driver assumes an offset of 191 which is wrong for all the game driver
-	// implementations.
-	// This bug has probably not been noticed, since the effect is hardly used and the sounds are
-	// not necessarily worse. I noticed the difference between ScummVM and DOSBox for the EOB II
-	// teleporter sound. I also found the location of the table which is supposed to be used here
-	// (simple enough: it is located at the end of the track after the 0x88 ending opcode).
-	// Teleporters in EOB I and II now sound exactly the same which I am sure was the intended way,
-	// since the sound data is exactly the same.
-	// In DOSBox the teleporters will sound different in EOB I and II, due to different sound
-	// data offsets.
-	channel.offset = READ_LE_UINT16(dataptr) - 191; dataptr += 2;
-	channel.secondaryEffect = &AdLibDriver::secondaryEffect1;
-	return 0;
-}
-
-int AdLibDriver::update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	Channel &channel2 = _channels[value];
-	channel2.duration = 0;
-	channel2.priority = 0;
-	channel2.dataptr = 0;
-	return 0;
-}
-
-int AdLibDriver::update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	const uint8 *ptr = getProgram(value);
-
-	// Safety check in case an invalid program is specified. This would make
-	// getProgram return a nullptr and thus cause invalid memory reads.
-	if (!ptr) {
-		debugC(3, kDebugLevelSound, "AdLibDriver::update_waitForEndOfProgram: Invalid program %d specified", value);
-		return 0;
-	}
-
-	uint8 chan = *ptr;
-
-	if (!_channels[chan].dataptr)
-		return 0;
-
-	dataptr -= 2;
-	return 2;
-}
-
-int AdLibDriver::update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	const uint8 *instrument = getInstrument(value);
-
-	// We add a safety check to avoid setting up invalid instruments. This is
-	// not done in the original. However, to avoid crashes due to invalid
-	// memory reads we simply ignore the request.
-	// This happens, for example, in Hand of Fate when using the swampsnake
-	// potion on Zanthia to scare off the rat in the cave in the first chapter
-	// of the game.
-	if (!instrument) {
-		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupInstrument: Invalid instrument %d specified", value);
-		return 0;
-	}
-
-	setupInstrument(_curRegOffset, instrument, channel);
-	return 0;
-}
-
-int AdLibDriver::update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.unk29 = value;
-	channel.unk30 = READ_BE_UINT16(dataptr);
-	dataptr += 2;
-	channel.primaryEffect = &AdLibDriver::primaryEffect1;
-	channel.unk31 = 0xFF;
-	return 0;
-}
-
-int AdLibDriver::update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	channel.primaryEffect = 0;
-	channel.unk30 = 0;
-	return 0;
-}
-
-int AdLibDriver::update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.baseFreq = value;
-	return 0;
-}
-
-int AdLibDriver::update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.unk32 = value;
-	channel.unk33 = *dataptr++;
-	uint8 temp = *dataptr++;
-	channel.unk34 = temp + 1;
-	channel.unk35 = temp << 1;
-	channel.unk36 = *dataptr++;
-	channel.primaryEffect = &AdLibDriver::primaryEffect2;
-	return 0;
-}
-
-int AdLibDriver::update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.priority = value;
-	return 0;
-}
-
-int AdLibDriver::updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	value >>= 1;
-	_unkValue1 = _unkValue2 = value;
-	_callbackTimer = 0xFF;
-	_unkValue4 = _unkValue5 = 0;
-	return 0;
-}
-
-int AdLibDriver::updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	if (_unkValue5) {
-		if (_unkValue4 & value) {
-			_unkValue5 = 0;
-			return 0;
-		}
-	}
-
-	if (!(value & _unkValue4))
-		++_unkValue5;
-
-	dataptr -= 2;
-	channel.duration = 1;
-	return 2;
-}
-
-int AdLibDriver::update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.opExtraLevel1 = value;
-	adjustVolume(channel);
-	return 0;
-}
-
-int AdLibDriver::update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	setupDuration(value, channel);
-	return (value != 0);
-}
-
-int AdLibDriver::update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	setupDuration(value, channel);
-	noteOn(channel);
-	return (value != 0);
-}
-
-int AdLibDriver::update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.fractionalSpacing = value & 7;
-	return 0;
-}
-
-int AdLibDriver::update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	_tempo = value;
-	return 0;
-}
-
-int AdLibDriver::update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	channel.secondaryEffect = 0;
-	return 0;
-}
-
-int AdLibDriver::update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.tempo = value;
-	return 0;
-}
-
-int AdLibDriver::update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.opExtraLevel3 = value;
-	return 0;
-}
-
-int AdLibDriver::update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	int channelBackUp = _curChannel;
-
-	_curChannel = value;
-	Channel &channel2 = _channels[value];
-	channel2.opExtraLevel2 = *dataptr++;
-	adjustVolume(channel2);
-
-	_curChannel = channelBackUp;
-	return 0;
-}
-
-int AdLibDriver::update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	int channelBackUp = _curChannel;
-
-	_curChannel = value;
-	Channel &channel2 = _channels[value];
-	channel2.opExtraLevel2 += *dataptr++;
-	adjustVolume(channel2);
-
-	_curChannel = channelBackUp;
-	return 0;
-}
-
-// Apart from initializing to zero, these two functions are the only ones that
-// modify _vibratoAndAMDepthBits.
-
-int AdLibDriver::update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	if (value & 1)
-		_vibratoAndAMDepthBits |= 0x80;
-	else
-		_vibratoAndAMDepthBits &= 0x7F;
-
-	writeOPL(0xBD, _vibratoAndAMDepthBits);
-	return 0;
-}
-
-int AdLibDriver::update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	if (value & 1)
-		_vibratoAndAMDepthBits |= 0x40;
-	else
-		_vibratoAndAMDepthBits &= 0xBF;
-
-	writeOPL(0xBD, _vibratoAndAMDepthBits);
-	return 0;
-}
-
-int AdLibDriver::update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.opExtraLevel1 += value;
-	adjustVolume(channel);
-	return 0;
-}
-
-int AdLibDriver::updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	int channelBackUp = _curChannel;
-
-	_curChannel = value;
-	Channel &channel2 = _channels[value];
-	channel2.duration = channel2.priority = 0;
-	channel2.dataptr = 0;
-	channel2.opExtraLevel2 = 0;
-
-	if (value != 9) {
-		uint8 outValue = _regOffset[value];
-
-		// Feedback strength / Connection type
-		writeOPL(0xC0 + _curChannel, 0x00);
-
-		// Key scaling level / Operator output level
-		writeOPL(0x43 + outValue, 0x3F);
-
-		// Sustain Level / Release Rate
-		writeOPL(0x83 + outValue, 0xFF);
-
-		// Key On / Octave / Frequency
-		writeOPL(0xB0 + _curChannel, 0x00);
-	}
-
-	_curChannel = channelBackUp;
-	return 0;
-}
-
-int AdLibDriver::updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	if (_curChannel >= 9)
-		return 0;
-
-	uint16 unk = *dataptr++;
-	unk |= value << 8;
-	unk &= getRandomNr();
-
-	uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx;
-	unk2 += unk;
-	unk2 |= ((channel.regBx & 0x20) << 8);
-
-	// Frequency
-	writeOPL(0xA0 + _curChannel, unk2 & 0xFF);
-
-	// Key On / Octave / Frequency
-	writeOPL(0xB0 + _curChannel, (unk2 & 0xFF00) >> 8);
-
-	return 0;
-}
-
-int AdLibDriver::update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	channel.primaryEffect = 0;
-	return 0;
-}
-
-int AdLibDriver::update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.pitchBend = value;
-	setupNote(channel.rawNote, channel, true);
-	return 0;
-}
-
-int AdLibDriver::update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	channel.tempo = _tempo;
-	return 0;
-}
-
-int AdLibDriver::update_nop(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	return 0;
-}
-
-int AdLibDriver::update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.durationRandomness = value;
-	return 0;
-}
-
-int AdLibDriver::update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	int tempo = channel.tempo + (int8)value;
-
-	if (tempo <= 0)
-		tempo = 1;
-	else if (tempo > 255)
-		tempo = 255;
-
-	channel.tempo = tempo;
-	return 0;
-}
-
-int AdLibDriver::updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	uint8 entry = *dataptr++;
-	_tablePtr1 = _unkTable2[entry++];
-	_tablePtr2 = _unkTable2[entry];
-	if (value == 2) {
-		// Frequency
-		writeOPL(0xA0, _tablePtr2[0]);
-	}
-	return 0;
-}
-
-int AdLibDriver::update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	int channelBackUp = _curChannel;
-	int regOffsetBackUp = _curRegOffset;
-
-	_curChannel = 6;
-	_curRegOffset = _regOffset[6];
-
-	const uint8 *instrument;
-	instrument = getInstrument(value);
-	if (instrument) {
-		setupInstrument(_curRegOffset, instrument, channel);
-	} else {
-		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 6 specified", value);
-	}
-	_unkValue6 = channel.opLevel2;
-
-	_curChannel = 7;
-	_curRegOffset = _regOffset[7];
-
-	instrument = getInstrument(*dataptr++);
-	if (instrument) {
-		setupInstrument(_curRegOffset, instrument, channel);
-	} else {
-		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 7 specified", value);
-	}
-	_unkValue7 = channel.opLevel1;
-	_unkValue8 = channel.opLevel2;
-
-	_curChannel = 8;
-	_curRegOffset = _regOffset[8];
-
-	instrument = getInstrument(*dataptr++);
-	if (instrument) {
-		setupInstrument(_curRegOffset, instrument, channel);
-	} else {
-		debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 8 specified", value);
-	}
-	_unkValue9 = channel.opLevel1;
-	_unkValue10 = channel.opLevel2;
-
-	// Octave / F-Number / Key-On for channels 6, 7 and 8
-
-	_channels[6].regBx = *dataptr++ & 0x2F;
-	writeOPL(0xB6, _channels[6].regBx);
-	writeOPL(0xA6, *dataptr++);
-
-	_channels[7].regBx = *dataptr++ & 0x2F;
-	writeOPL(0xB7, _channels[7].regBx);
-	writeOPL(0xA7, *dataptr++);
-
-	_channels[8].regBx = *dataptr++ & 0x2F;
-	writeOPL(0xB8, _channels[8].regBx);
-	writeOPL(0xA8, *dataptr++);
-
-	_rhythmSectionBits = 0x20;
-
-	_curRegOffset = regOffsetBackUp;
-	_curChannel = channelBackUp;
-	return 0;
-}
-
-int AdLibDriver::update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	// Any instrument that we want to play, and which was already playing,
-	// is temporarily keyed off. Instruments that were off already, or
-	// which we don't want to play, retain their old on/off status. This is
-	// probably so that the instrument's envelope is played from its
-	// beginning again...
-
-	writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20);
-
-	// ...but since we only set the rhythm instrument bits, and never clear
-	// them (until the entire rhythm section is disabled), I'm not sure how
-	// useful the cleverness above is. We could perhaps simply turn off all
-	// the rhythm instruments instead.
-
-	_rhythmSectionBits |= value;
-
-	writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits);
-	return 0;
-}
-
-int AdLibDriver::update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	--dataptr;
-	_rhythmSectionBits = 0;
-
-	// All the rhythm bits are cleared. The AM and Vibrato depth bits
-	// remain unchanged.
-
-	writeOPL(0xBD, _vibratoAndAMDepthBits);
-	return 0;
-}
-
-int AdLibDriver::updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	uint8 value2 = *dataptr++;
-
-	if (value & 1) {
-		_unkValue12 = value2;
-
-		// Channel 7, op1: Level Key Scaling / Total Level
-		writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12));
-	}
-
-	if (value & 2) {
-		_unkValue14 = value2;
-
-		// Channel 8, op2: Level Key Scaling / Total Level
-		writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14));
-	}
-
-	if (value & 4) {
-		_unkValue15 = value2;
-
-		// Channel 8, op1: Level Key Scaling / Total Level
-		writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15));
-	}
-
-	if (value & 8) {
-		_unkValue18 = value2;
-
-		// Channel 7, op2: Level Key Scaling / Total Level
-		writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18));
-	}
-
-	if (value & 16) {
-		_unkValue20 = value2;
-
-		// Channel 6, op2: Level Key Scaling / Total Level
-		writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20));
-	}
-
-	return 0;
-}
-
-int AdLibDriver::updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	uint8 value2 = *dataptr++;
-
-	if (value & 1) {
-		_unkValue11 = checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12);
-
-		// Channel 7, op1: Level Key Scaling / Total Level
-		writeOPL(0x51, _unkValue11);
-	}
-
-	if (value & 2) {
-		_unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14);
-
-		// Channel 8, op2: Level Key Scaling / Total Level
-		writeOPL(0x55, _unkValue13);
-	}
-
-	if (value & 4) {
-		_unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15);
-
-		// Channel 8, op1: Level Key Scaling / Total Level
-		writeOPL(0x52, _unkValue16);
-	}
-
-	if (value & 8) {
-		_unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18);
-
-		// Channel 7, op2: Level Key Scaling / Total Level
-		writeOPL(0x54, _unkValue17);
-	}
-
-	if (value & 16) {
-		_unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20);
-
-		// Channel 6, op2: Level Key Scaling / Total Level
-		writeOPL(0x53, _unkValue19);
-	}
-
-	return 0;
-}
-
-int AdLibDriver::updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	uint8 value2 = *dataptr++;
-
-	if (value & 1) {
-		_unkValue11 = value2;
-
-		// Channel 7, op1: Level Key Scaling / Total Level
-		writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12));
-	}
-
-	if (value & 2) {
-		_unkValue13 = value2;
-
-		// Channel 8, op2: Level Key Scaling / Total Level
-		writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14));
-	}
-
-	if (value & 4) {
-		_unkValue16 = value2;
-
-		// Channel 8, op1: Level Key Scaling / Total Level
-		writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15));
-	}
-
-	if (value & 8) {
-		_unkValue17 = value2;
-
-		// Channel 7, op2: Level Key Scaling / Total Level
-		writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18));
-	}
-
-	if (value & 16) {
-		_unkValue19 = value2;
-
-		// Channel 6, op2: Level Key Scaling / Total Level
-		writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue20));
-	}
-
-	return 0;
-}
-
-int AdLibDriver::update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	_soundTrigger = value;
-	return 0;
-}
-
-int AdLibDriver::update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.tempoReset = value;
-	return 0;
-}
-
-int AdLibDriver::updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value) {
-	channel.unk39 = value;
-	channel.unk40 = *dataptr++;
-	return 0;
-}
-
-// static res
-
-#define COMMAND(x) { &AdLibDriver::x, #x }
-
-void AdLibDriver::setupParserOpcodeTable() {
-	static const ParserOpcode parserOpcodeTable[] = {
-		// 0
-		COMMAND(update_setRepeat),
-		COMMAND(update_checkRepeat),
-		COMMAND(update_setupProgram),
-		COMMAND(update_setNoteSpacing),
-
-		// 4
-		COMMAND(update_jump),
-		COMMAND(update_jumpToSubroutine),
-		COMMAND(update_returnFromSubroutine),
-		COMMAND(update_setBaseOctave),
-
-		// 8
-		COMMAND(update_stopChannel),
-		COMMAND(update_playRest),
-		COMMAND(update_writeAdLib),
-		COMMAND(update_setupNoteAndDuration),
-
-		// 12
-		COMMAND(update_setBaseNote),
-		COMMAND(update_setupSecondaryEffect1),
-		COMMAND(update_stopOtherChannel),
-		COMMAND(update_waitForEndOfProgram),
-
-		// 16
-		COMMAND(update_setupInstrument),
-		COMMAND(update_setupPrimaryEffect1),
-		COMMAND(update_removePrimaryEffect1),
-		COMMAND(update_setBaseFreq),
-
-		// 20
-		COMMAND(update_stopChannel),
-		COMMAND(update_setupPrimaryEffect2),
-		COMMAND(update_stopChannel),
-		COMMAND(update_stopChannel),
-
-		// 24
-		COMMAND(update_stopChannel),
-		COMMAND(update_stopChannel),
-		COMMAND(update_setPriority),
-		COMMAND(update_stopChannel),
-
-		// 28
-		COMMAND(updateCallback23),
-		COMMAND(updateCallback24),
-		COMMAND(update_setExtraLevel1),
-		COMMAND(update_stopChannel),
-
-		// 32
-		COMMAND(update_setupDuration),
-		COMMAND(update_playNote),
-		COMMAND(update_stopChannel),
-		COMMAND(update_stopChannel),
-
-		// 36
-		COMMAND(update_setFractionalNoteSpacing),
-		COMMAND(update_stopChannel),
-		COMMAND(update_setTempo),
-		COMMAND(update_removeSecondaryEffect1),
-
-		// 40
-		COMMAND(update_stopChannel),
-		COMMAND(update_setChannelTempo),
-		COMMAND(update_stopChannel),
-		COMMAND(update_setExtraLevel3),
-
-		// 44
-		COMMAND(update_setExtraLevel2),
-		COMMAND(update_changeExtraLevel2),
-		COMMAND(update_setAMDepth),
-		COMMAND(update_setVibratoDepth),
-
-		// 48
-		COMMAND(update_changeExtraLevel1),
-		COMMAND(update_stopChannel),
-		COMMAND(update_stopChannel),
-		COMMAND(updateCallback38),
-
-		// 52
-		COMMAND(update_stopChannel),
-		COMMAND(updateCallback39),
-		COMMAND(update_removePrimaryEffect2),
-		COMMAND(update_stopChannel),
-
-		// 56
-		COMMAND(update_stopChannel),
-		COMMAND(update_pitchBend),
-		COMMAND(update_resetToGlobalTempo),
-		COMMAND(update_nop),
-
-		// 60
-		COMMAND(update_setDurationRandomness),
-		COMMAND(update_changeChannelTempo),
-		COMMAND(update_stopChannel),
-		COMMAND(updateCallback46),
-
-		// 64
-		COMMAND(update_nop),
-		COMMAND(update_setupRhythmSection),
-		COMMAND(update_playRhythmSection),
-		COMMAND(update_removeRhythmSection),
-
-		// 68
-		COMMAND(updateCallback51),
-		COMMAND(updateCallback52),
-		COMMAND(updateCallback53),
-		COMMAND(update_setSoundTrigger),
-
-		// 72
-		COMMAND(update_setTempoReset),
-		COMMAND(updateCallback56),
-		COMMAND(update_stopChannel)
-	};
-
-	_parserOpcodeTable = parserOpcodeTable;
-	_parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable);
-}
-#undef COMMAND
-
-// This table holds the register offset for operator 1 for each of the nine
-// channels. To get the register offset for operator 2, simply add 3.
-
-const uint8 AdLibDriver::_regOffset[] = {
-	0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11,
-	0x12
-};
-
-//These are the F-Numbers (10 bits) for the notes of the 12-tone scale.
-// However, it does not match the table in the AdLib documentation I've seen.
-
-const uint16 AdLibDriver::_freqTable[] = {
-	0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9,
-	0x0207, 0x0225, 0x0246
-};
-
-// These tables are currently only used by updateCallback46(), which only ever
-// uses the first element of one of the sub-tables.
-
-const uint8 *const AdLibDriver::_unkTable2[] = {
-	AdLibDriver::_unkTable2_1,
-	AdLibDriver::_unkTable2_2,
-	AdLibDriver::_unkTable2_1,
-	AdLibDriver::_unkTable2_2,
-	AdLibDriver::_unkTable2_3,
-	AdLibDriver::_unkTable2_2
-};
-
-const uint8 AdLibDriver::_unkTable2_1[] = {
-	0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D,
-	0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49,
-	0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45,
-	0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41,
-	0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D,
-	0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39,
-	0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35,
-	0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31,
-	0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D,
-	0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29,
-	0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25,
-	0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21,
-	0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D,
-	0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19,
-	0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15,
-	0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11,
-	0x10, 0x10
-};
-
-// no don't ask me WHY this table exsits!
-const uint8 AdLibDriver::_unkTable2_2[] = {
-	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
-	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
-	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
-	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
-	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
-	0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
-	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
-	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
-	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
-	0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F,
-	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
-	0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
-	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
-	0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
-};
-
-const uint8 AdLibDriver::_unkTable2_3[] = {
-	0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E,
-	0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B,
-	0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39,
-	0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36,
-	0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33,
-	0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31,
-	0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E,
-	0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B,
-	0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29,
-	0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26,
-	0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23,
-	0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21,
-	0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E,
-	0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B,
-	0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19,
-	0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16,
-	0x16, 0x15
-};
-
-// This table is used to modify the frequency of the notes, depending on the
-// note value and the pitch bend value. In theory, we could very well try to
-// access memory outside this table, but in reality that probably won't happen.
-//
-
-const uint8 AdLibDriver::_pitchBendTables[][32] = {
-	// 0
-	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
-	  0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
-	  0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19,
-	  0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 },
-	// 1
-	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09,
-	  0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,
-	  0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A,
-	  0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 },
-	// 2
-	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09,
-	  0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
-	  0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D,
-	  0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 },
-	// 3
-	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
-	  0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
-	  0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D,
-	  0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 },
-	// 4
-	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
-	  0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15,
-	  0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
-	  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A },
-	// 5
-	{ 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
-	  0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
-	  0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
-	  0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D },
-	// 6
-	{ 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
-	  0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
-	  0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24,
-	  0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 },
-	// 7
-	{ 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C,
-	  0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18,
-	  0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25,
-	  0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 },
-	// 8
-	{ 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D,
-	  0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A,
-	  0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28,
-	  0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 },
-	// 9
-	{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
-	  0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B,
-	  0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29,
-	  0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 },
-	// 10
-	{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
-	  0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E,
-	  0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D,
-	  0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C },
-	// 11
-	{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F,
-	  0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E,
-	  0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E,
-	  0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F },
-	// 12
-	{ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10,
-	  0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21,
-	  0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32,
-	  0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 },
-	// 13
-	{ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11,
-	  0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23,
-	  0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35,
-	  0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 }
-};
-
-#pragma mark -
-
 // Kyra 1 sound triggers. Most noticeably, these are used towards the end of
 // the game, in the castle, to cycle between different songs. The same music is
 // used in other places throughout the game, but the player is less likely to
diff --git a/engines/kyra/sound/sound_digital_mr.cpp b/engines/kyra/sound/sound_digital_mr.cpp
index cf94eb0..ffcfcd1 100644
--- a/engines/kyra/sound/sound_digital_mr.cpp
+++ b/engines/kyra/sound/sound_digital_mr.cpp
@@ -25,7 +25,6 @@
 #include "kyra/engine/kyra_mr.h"
 
 #include "audio/audiostream.h"
-
 #include "audio/decoders/mp3.h"
 #include "audio/decoders/vorbis.h"
 #include "audio/decoders/flac.h"
@@ -103,294 +102,6 @@ int KyraAudioStream::readBuffer(int16 *buffer, const int numSamples) {
 	return samplesRead;
 }
 
-// Thanks to Torbjorn Andersson (eriktorbjorn) for his aud player on which
-// this code is based on
-
-// TODO: cleanup of whole AUDStream
-
-class AUDStream : public Audio::SeekableAudioStream {
-public:
-	AUDStream(Common::SeekableReadStream *stream);
-	~AUDStream();
-
-	int readBuffer(int16 *buffer, const int numSamples);
-
-	bool isStereo() const { return false; }
-	bool endOfData() const { return _endOfData; }
-
-	int getRate() const { return _rate; }
-
-	bool seek(const Audio::Timestamp &where);
-	Audio::Timestamp getLength() const { return _length; }
-private:
-	Common::SeekableReadStream *_stream;
-	uint32 _streamStart;
-	bool _endOfData;
-	int _rate;
-	uint _processedSize;
-	uint _totalSize;
-	Audio::Timestamp _length;
-
-	int _bytesLeft;
-
-	byte *_outBuffer;
-	int _outBufferOffset;
-	uint _outBufferSize;
-
-	byte *_inBuffer;
-	uint _inBufferSize;
-
-	int readChunk(int16 *buffer, const int maxSamples);
-
-	static const int8 WSTable2Bit[];
-	static const int8 WSTable4Bit[];
-};
-
-const int8 AUDStream::WSTable2Bit[] = { -2, -1, 0, 1 };
-const int8 AUDStream::WSTable4Bit[] = {
-	-9, -8, -6, -5, -4, -3, -2, -1,
-	 0,  1,  2,  3,  4,  5,  6,  8
-};
-
-AUDStream::AUDStream(Common::SeekableReadStream *stream) : _stream(stream), _endOfData(true), _rate(0),
-	_processedSize(0), _totalSize(0), _length(0, 1), _bytesLeft(0), _outBuffer(0),
-	_outBufferOffset(0), _outBufferSize(0), _inBuffer(0), _inBufferSize(0) {
-
-	_rate = _stream->readUint16LE();
-	_totalSize = _stream->readUint32LE();
-
-	// TODO?: add checks
-	int flags = _stream->readByte();	// flags
-	int type = _stream->readByte();	// type
-
-	_streamStart = stream->pos();
-
-	debugC(5, kDebugLevelSound, "AUD Info: rate: %d, totalSize: %d, flags: %d, type: %d, streamStart: %d", _rate, _totalSize, flags, type, _streamStart);
-
-	_length = Audio::Timestamp(0, _rate);
-	for (uint32 i = 0; i < _totalSize;) {
-		uint16 size = _stream->readUint16LE();
-		uint16 outSize = _stream->readUint16LE();
-
-		_length = _length.addFrames(outSize);
-		stream->seek(size + 4, SEEK_CUR);
-		i += size + 8;
-	}
-
-	stream->seek(_streamStart, SEEK_SET);
-
-	if (type == 1 && !flags)
-		_endOfData = false;
-	else
-		warning("No AUD file (rate: %d, size: %d, flags: 0x%X, type: %d)", _rate, _totalSize, flags, type);
-}
-
-AUDStream::~AUDStream() {
-	delete[] _outBuffer;
-	delete[] _inBuffer;
-	delete _stream;
-}
-
-int AUDStream::readBuffer(int16 *buffer, const int numSamples) {
-	int samplesRead = 0, samplesLeft = numSamples;
-
-	while (samplesLeft > 0 && !_endOfData) {
-		int samples = readChunk(buffer, samplesLeft);
-		samplesRead += samples;
-		samplesLeft -= samples;
-		buffer += samples;
-	}
-
-	return samplesRead;
-}
-
-inline int16 clip8BitSample(int16 sample) {
-	return CLIP<int16>(sample, 0, 255);
-}
-
-int AUDStream::readChunk(int16 *buffer, const int maxSamples) {
-	int samplesProcessed = 0;
-
-	// if no bytes of the old chunk are left, read the next one
-	if (_bytesLeft <= 0) {
-		if (_processedSize >= _totalSize) {
-			_endOfData = true;
-			return 0;
-		}
-
-		uint16 size = _stream->readUint16LE();
-		uint16 outSize = _stream->readUint16LE();
-		uint32 id = _stream->readUint32LE();
-
-		assert(id == 0x0000DEAF);
-
-		_processedSize += 8 + size;
-
-		_outBufferOffset = 0;
-		if (size == outSize) {
-			if (outSize > _outBufferSize) {
-				_outBufferSize = outSize;
-				delete[] _outBuffer;
-				_outBuffer = new uint8[_outBufferSize];
-				assert(_outBuffer);
-			}
-
-			_bytesLeft = size;
-
-			_stream->read(_outBuffer, _bytesLeft);
-		} else {
-			_bytesLeft = outSize;
-
-			if (outSize > _outBufferSize) {
-				_outBufferSize = outSize;
-				delete[] _outBuffer;
-				_outBuffer = new uint8[_outBufferSize];
-				assert(_outBuffer);
-			}
-
-			if (size > _inBufferSize) {
-				_inBufferSize = size;
-				delete[] _inBuffer;
-				_inBuffer = new uint8[_inBufferSize];
-				assert(_inBuffer);
-			}
-
-			if (_stream->read(_inBuffer, size) != size) {
-				_endOfData = true;
-				return 0;
-			}
-
-			int16 curSample = 0x80;
-			byte code = 0;
-			int8 count = 0;
-			uint16 input = 0;
-			int j = 0;
-			int i = 0;
-
-			while (outSize > 0) {
-				input = _inBuffer[i++] << 2;
-				code = (input >> 8) & 0xFF;
-				count = (input & 0xFF) >> 2;
-
-				switch (code) {
-				case 2:
-					if (count & 0x20) {
-						/* NOTE: count is signed! */
-						count <<= 3;
-						curSample += (count >> 3);
-						_outBuffer[j++] = curSample & 0xFF;
-						outSize--;
-					} else {
-						for (; count >= 0; count--) {
-							_outBuffer[j++] = _inBuffer[i++];
-							outSize--;
-						}
-						curSample = _inBuffer[i - 1];
-					}
-					break;
-				case 1:
-					for (; count >= 0; count--) {
-						code = _inBuffer[i++];
-
-						curSample += WSTable4Bit[code & 0x0F];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample;
-
-						curSample += WSTable4Bit[code >> 4];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample;
-
-						outSize -= 2;
-					}
-					break;
-				case 0:
-					for (; count >= 0; count--) {
-						code = (uint8)_inBuffer[i++];
-
-						curSample += WSTable2Bit[code & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						curSample += WSTable2Bit[(code >> 2) & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						curSample += WSTable2Bit[(code >> 4) & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						curSample += WSTable2Bit[(code >> 6) & 0x03];
-						curSample = clip8BitSample(curSample);
-						_outBuffer[j++] = curSample & 0xFF;
-
-						outSize -= 4;
-					}
-					break;
-				default:
-					for (; count >= 0; count--) {
-						_outBuffer[j++] = curSample & 0xFF;
-						outSize--;
-					}
-				}
-			}
-		}
-	}
-
-	// copies the chunk data to the output buffer
-	if (_bytesLeft > 0) {
-		int samples = MIN(_bytesLeft, maxSamples);
-		samplesProcessed += samples;
-		_bytesLeft -= samples;
-
-		while (samples--) {
-			int16 sample = (_outBuffer[_outBufferOffset++] << 8) ^ 0x8000;
-
-			*buffer++ = sample;
-		}
-	}
-
-	return samplesProcessed;
-}
-
-bool AUDStream::seek(const Audio::Timestamp &where) {
-	const uint32 seekSample = Audio::convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
-
-	_stream->seek(_streamStart);
-	_processedSize = 0;
-	_bytesLeft = 0;
-	_endOfData = false;
-
-	uint32 curSample = 0;
-
-	while (!endOfData()) {
-		uint16 size = _stream->readUint16LE();
-		uint16 outSize = _stream->readUint16LE();
-
-		if (curSample + outSize > seekSample) {
-			_stream->seek(-4, SEEK_CUR);
-
-			uint32 samples = seekSample - curSample;
-			int16 *temp = new int16[samples];
-			assert(temp);
-
-			readChunk(temp, samples);
-			delete[] temp;
-			curSample += samples;
-			break;
-		} else {
-			curSample += outSize;
-			_processedSize += 8 + size;
-			_stream->seek(size + 4, SEEK_CUR);
-		}
-	}
-
-	_endOfData = (_processedSize >= _totalSize);
-
-	return (curSample == seekSample);
-}
-
-#pragma mark -
-
 SoundDigital::SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
 	for (uint i = 0; i < ARRAYSIZE(_sounds); ++i)
 		_sounds[i].stream = 0;
@@ -518,13 +229,7 @@ void SoundDigital::beginFadeOut(int channel, int ticks) {
 
 // static res
 
-namespace {
-
-Audio::SeekableAudioStream *makeAUDStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
-	return new AUDStream(stream);
-}
-
-} // end of anonymous namespace
+Audio::SeekableAudioStream *makeAUDStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
 
 const SoundDigital::AudioCodecs SoundDigital::_supportedCodecs[] = {
 #ifdef USE_FLAC
@@ -540,5 +245,4 @@ const SoundDigital::AudioCodecs SoundDigital::_supportedCodecs[] = {
 	{ 0, 0 }
 };
 
-
 } // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_digital_mr.h b/engines/kyra/sound/sound_digital_mr.h
index 271dde6..1aece2d 100644
--- a/engines/kyra/sound/sound_digital_mr.h
+++ b/engines/kyra/sound/sound_digital_mr.h
@@ -20,8 +20,8 @@
  *
  */
 
-#ifndef KYRA_SOUND_DIGITAL_H
-#define KYRA_SOUND_DIGITAL_H
+#ifndef KYRA_SOUND_DIGITAL_MR_H
+#define KYRA_SOUND_DIGITAL_MR_H
 
 #include "audio/mixer.h"
 
diff --git a/engines/kyra/sound/sound_midi.cpp b/engines/kyra/sound/sound_midi.cpp
index c0cf6c1..2390b75 100644
--- a/engines/kyra/sound/sound_midi.cpp
+++ b/engines/kyra/sound/sound_midi.cpp
@@ -20,7 +20,8 @@
  *
  */
 
-#include "kyra/sound/sound_intern.h"
+#include "kyra/sound/drivers/midi.h"
+
 #include "kyra/resource/resource.h"
 
 #include "common/system.h"
@@ -31,412 +32,6 @@
 
 namespace Kyra {
 
-class MidiOutput : public MidiDriver_BASE {
-public:
-	MidiOutput(OSystem *system, MidiDriver *output, bool isMT32, bool defaultMT32);
-	~MidiOutput();
-
-	void setSourceVolume(int source, int volume, bool apply=false);
-
-	void initSource(int source);
-	void deinitSource(int source);
-	void stopNotesOnChannel(int channel);
-
-	void setSoundSource(int source) { _curSource = source; }
-
-	// MidiDriver_BASE interface
-	virtual void send(uint32 b);
-	virtual void sysEx(const byte *msg, uint16 length);
-	virtual void metaEvent(byte type, byte *data, uint16 length);
-
-	// TODO: Get rid of the following two methods
-	void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { _output->setTimerCallback(timerParam, timerProc); }
-	uint32 getBaseTempo() { return _output->getBaseTempo(); }
-
-
-private:
-	void sendIntern(const byte event, const byte channel, byte param1, const byte param2);
-	void sendSysEx(const byte p1, const byte p2, const byte p3, const byte *buffer, const int size);
-
-	OSystem *_system;
-	MidiDriver *_output;
-
-	bool _isMT32;
-	bool _defaultMT32;
-
-	struct Controller {
-		byte controller;
-		byte value;
-	};
-
-	enum {
-		kChannelLocked = 0x80,
-		kChannelProtected = 0x40
-	};
-
-	struct Channel {
-		byte flags;
-
-		byte program;
-		int16 pitchWheel;
-
-		byte noteCount;
-
-		Controller controllers[9];
-	} _channels[16];
-
-	int lockChannel();
-	void unlockChannel(int channel);
-
-	int _curSource;
-
-	struct SoundSource {
-		int volume;
-
-		int8 channelMap[16];
-		byte channelProgram[16];
-		int16 channelPW[16];
-		Controller controllers[16][9];
-
-		struct Note {
-			byte channel;
-			byte note;
-		};
-
-		Note notes[32];
-	} _sources[4];
-};
-
-MidiOutput::MidiOutput(OSystem *system, MidiDriver *output, bool isMT32, bool defaultMT32) : _system(system), _output(output) {
-	_isMT32 = isMT32;
-	_defaultMT32 = defaultMT32;
-
-	int ret = _output->open();
-	if (ret != MidiDriver::MERR_ALREADY_OPEN && ret != 0)
-		error("Couldn't open midi driver");
-
-	static const Controller defaultControllers[] = {
-		{ 0x07, 0x7F }, { 0x01, 0x00 }, { 0x0A, 0x40 },
-		{ 0x0B, 0x7F }, { 0x40, 0x00 }, { 0x72, 0x00 },
-		{ 0x6E, 0x00 }, { 0x6F, 0x00 }, { 0x70, 0x00 }
-	};
-
-	static const byte defaultPrograms[] = {
-		0x44, 0x30, 0x5F, 0x4E, 0x29, 0x03, 0x6E, 0x7A, 0xFF
-	};
-
-	static const byte sysEx1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-	static const byte sysEx2[] = { 3, 4, 3, 4, 3, 4, 3, 4, 4 };
-	static const byte sysEx3[] = { 0, 3, 2 };
-
-	if (_isMT32) {
-		sendSysEx(0x7F, 0x00, 0x00, sysEx1, 1);
-		sendSysEx(0x10, 0x00, 0x0D, sysEx1, 9);
-		sendSysEx(0x10, 0x00, 0x04, sysEx2, 9);
-		sendSysEx(0x10, 0x00, 0x01, sysEx3, 3);
-	} else {
-		_output->sendGMReset();
-	}
-
-	memset(_channels, 0, sizeof(_channels));
-	for (int i = 0; i < 16; ++i) {
-		for (int j = 0; j < 9; ++j)
-			_channels[i].controllers[j] = defaultControllers[j];
-		_channels[i].pitchWheel = -1;
-		_channels[i].program = 0xFF;
-	}
-
-	for (int i = 0; i < 9; ++i) {
-		for (int j = 1; j <= 9; ++j)
-			sendIntern(0xB0, j, defaultControllers[i].controller, defaultControllers[i].value);
-	}
-
-	for (int i = 1; i <= 9; ++i) {
-		sendIntern(0xE0, i, 0x00, 0x40);
-		if (defaultPrograms[i - 1] != 0xFF)
-			sendIntern(0xC0, i, defaultPrograms[i - 1], 0x00);
-	}
-
-	for (int i = 0; i < 4; ++i) {
-		_sources[i].volume = 256;
-		initSource(i);
-	}
-}
-
-
-MidiOutput::~MidiOutput() {
-	_output->close();
-	delete _output;
-}
-
-void MidiOutput::send(uint32 b) {
-	const byte event = b & 0xF0;
-	const byte channel = b & 0x0F;
-	byte param1 = (b >>  8) & 0xFF;
-	byte param2 = (b >> 16) & 0xFF;
-
-	if (event == 0xE0) {							// Pitch-Wheel
-		_channels[channel].pitchWheel =
-		_sources[_curSource].channelPW[channel] = (param2 << 8) | param1;
-	} else if (event == 0xC0) {						// Program change
-		_channels[channel].program =
-		_sources[_curSource].channelProgram[channel] = param1;
-	} else if (event == 0xB0) {						// Controller change
-		for (int i = 0; i < 9; ++i) {
-			Controller &cont = _sources[_curSource].controllers[channel][i];
-			if (cont.controller == param1) {
-				cont.value = param2;
-				break;
-			}
-		}
-
-		if (param1 == 0x07) {
-			param2 = (param2 * _sources[_curSource].volume) >> 8;
-		} else if (param1 == 0x6E) {	// Lock Channel
-			if (param2 >= 0x40) {	// Lock Channel
-				int chan = lockChannel();
-				if (chan < 0)
-					chan = channel;
-				_sources[_curSource].channelMap[channel] = chan;
-			} else {				// Unlock Channel
-				stopNotesOnChannel(channel);
-				unlockChannel(_sources[_curSource].channelMap[channel]);
-				_sources[_curSource].channelMap[channel] = channel;
-			}
-		} else if (param1 == 0x6F) {	// Protect Channel
-			if (param2 >= 0x40) {	// Protect Channel
-				_channels[channel].flags |= kChannelProtected;
-			} else {				// Unprotect Channel
-				_channels[channel].flags &= ~kChannelProtected;
-			}
-		} else if (param1 == 0x7B) {	// All notes off
-			// FIXME: Since the XMIDI parsers sends this
-			// on track change, we simply ignore it.
-			return;
-		}
-	} else if (event == 0x90 || event == 0x80) {	// Note On/Off
-		if (!(_channels[channel].flags & kChannelLocked)) {
-			const bool remove = (event == 0x80) || (param2 == 0x00);
-			int note = -1;
-
-			for (int i = 0; i < 32; ++i) {
-				if (remove) {
-					if (_sources[_curSource].notes[i].channel == channel &&
-						_sources[_curSource].notes[i].note == param1) {
-						note = i;
-						break;
-					}
-				} else {
-					if (_sources[_curSource].notes[i].channel == 0xFF) {
-						note = i;
-						break;
-					}
-				}
-			}
-
-			if (note != -1) {
-				if (remove) {
-					_sources[_curSource].notes[note].channel = 0xFF;
-
-					--_channels[_sources[_curSource].channelMap[channel]].noteCount;
-				} else {
-					_sources[_curSource].notes[note].channel = channel;
-					_sources[_curSource].notes[note].note = param1;
-
-					++_channels[_sources[_curSource].channelMap[channel]].noteCount;
-				}
-
-				sendIntern(event, _sources[_curSource].channelMap[channel], param1, param2);
-			}
-		}
-		return;
-	}
-
-	if (!(_channels[channel].flags & kChannelLocked))
-		sendIntern(event, _sources[_curSource].channelMap[channel], param1, param2);
-}
-
-void MidiOutput::sendIntern(const byte event, const byte channel, byte param1, const byte param2) {
-	if (event == 0xC0) {
-		// MT32 -> GM conversion
-		if (!_isMT32 && _defaultMT32)
-			param1 = MidiDriver::_mt32ToGm[param1];
-	}
-
-	_output->send(event | channel, param1, param2);
-}
-
-void MidiOutput::sysEx(const byte *msg, uint16 length) {
-	// Wait the time it takes to send the SysEx data
-	uint32 delay = (length + 2) * 1000 / 3125;
-
-	// Plus an additional delay for the MT-32 rev00
-	if (_isMT32)
-		delay += 40;
-
-	_output->sysEx(msg, length);
-	_system->delayMillis(delay);
-}
-
-void MidiOutput::sendSysEx(const byte p1, const byte p2, const byte p3, const byte *buffer, const int size) {
-	int bufferSize = 8 + size;
-	byte *outBuffer = new byte[bufferSize];
-	assert(outBuffer);
-
-	outBuffer[0] = 0x41;
-	outBuffer[1] = 0x10;
-	outBuffer[2] = 0x16;
-	outBuffer[3] = 0x12;
-
-	outBuffer[4] = p1;
-	outBuffer[5] = p2;
-	outBuffer[6] = p3;
-
-	memcpy(outBuffer + 7, buffer, size);
-
-	uint16 checkSum = p1 + p2 + p3;
-	for (int i = 0; i < size; ++i)
-		checkSum += buffer[i];
-	checkSum &= 0x7F;
-	checkSum -= 0x80;
-	checkSum = -checkSum;
-	checkSum &= 0x7F;
-
-	outBuffer[7+size] = checkSum;
-
-	sysEx(outBuffer, bufferSize);
-
-	delete[] outBuffer;
-}
-
-void MidiOutput::metaEvent(byte type, byte *data, uint16 length) {
-	if (type == 0x2F) // End of Track
-		deinitSource(_curSource);
-
-	_output->metaEvent(type, data, length);
-}
-
-void MidiOutput::setSourceVolume(int source, int volume, bool apply) {
-	_sources[source].volume = volume;
-
-	if (apply) {
-		for (int i = 0; i < 16; ++i) {
-			// Controller 0 in the state table should always be '7' aka
-			// volume control
-			byte realVol = (_sources[source].controllers[i][0].value * volume) >> 8;
-			sendIntern(0xB0, i, 0x07, realVol);
-		}
-	}
-}
-
-void MidiOutput::initSource(int source) {
-	memset(_sources[source].notes, -1, sizeof(_sources[source].notes));
-
-	for (int i = 0; i < 16; ++i) {
-		_sources[source].channelMap[i] = i;
-		_sources[source].channelProgram[i] = 0xFF;
-		_sources[source].channelPW[i] = -1;
-
-		for (int j = 0; j < 9; ++j)
-			_sources[source].controllers[i][j] = _channels[i].controllers[j];
-	}
-}
-
-void MidiOutput::deinitSource(int source) {
-	for (int i = 0; i < 16; ++i) {
-		for (int j = 0; j < 9; ++j) {
-			const Controller &cont = _sources[source].controllers[i][j];
-
-			if (cont.controller == 0x40) {
-				if (cont.value >= 0x40)
-					sendIntern(0xB0, i, 0x40, 0);
-			} else if (cont.controller == 0x6E) {
-				if (cont.value >= 0x40) {
-					stopNotesOnChannel(i);
-					unlockChannel(_sources[source].channelMap[i]);
-					_sources[source].channelMap[i] = i;
-				}
-			} else if (cont.controller == 0x6F) {
-				if (cont.value >= 0x40)
-					_channels[i].flags &= ~kChannelProtected;
-			} else if (cont.controller == 0x70) {
-				if (cont.value >= 0x40)
-					sendIntern(0xB0, i, 0x70, 0);
-			}
-		}
-	}
-}
-
-int MidiOutput::lockChannel() {
-	int channel = -1;
-	int notes = 0xFF;
-	byte flags = kChannelLocked | kChannelProtected;
-
-	while (channel == -1) {
-		for (int i = _isMT32 ? 8 : 15; i >= 1; --i) {
-			if (_channels[i].flags & flags)
-				continue;
-			if (_channels[i].noteCount < notes) {
-				channel = i;
-				notes = _channels[i].noteCount;
-			}
-		}
-
-		if (channel == -1) {
-			if (flags & kChannelProtected)
-				flags &= ~kChannelProtected;
-			else
-				break;
-		}
-	}
-
-	if (channel == -1)
-		return -1;
-
-	sendIntern(0xB0, channel, 0x40, 0);
-	stopNotesOnChannel(channel);
-	_channels[channel].noteCount = 0;
-	_channels[channel].flags |= kChannelLocked;
-
-	return channel;
-}
-
-void MidiOutput::unlockChannel(int channel) {
-	if (!(_channels[channel].flags & kChannelLocked))
-		return;
-
-	_channels[channel].flags &= ~kChannelLocked;
-	_channels[channel].noteCount = 0;
-	sendIntern(0xB0, channel, 0x40, 0);
-	sendIntern(0xB0, channel, 0x7B, 0);
-
-	for (int i = 0; i < 9; ++i) {
-		if (_channels[channel].controllers[i].value != 0xFF)
-			sendIntern(0xB0, channel, _channels[channel].controllers[i].controller, _channels[channel].controllers[i].value);
-	}
-
-	if (_channels[channel].program != 0xFF)
-		sendIntern(0xC0, channel, _channels[channel].program, 0);
-
-	if (_channels[channel].pitchWheel != -1)
-		sendIntern(0xE0, channel, _channels[channel].pitchWheel & 0xFF, (_channels[channel].pitchWheel >> 8) & 0xFF);
-}
-
-void MidiOutput::stopNotesOnChannel(int channel) {
-	for (int i = 0; i < 4; ++i) {
-		SoundSource &sound = _sources[i];
-		for (int j = 0; j < 32; ++j) {
-			if (sound.notes[j].channel == channel) {
-				sound.notes[j].channel = 0xFF;
-				sendIntern(0x80, sound.channelMap[channel], sound.notes[j].note, 0);
-				--_channels[sound.channelMap[channel]].noteCount;
-			}
-		}
-	}
-}
-
-#pragma mark -
-
 SoundMidiPC::SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *driver, kType type) : Sound(vm, mixer) {
 	_driver = driver;
 	_output = 0;
diff --git a/engines/kyra/sound/sound_pcspk.cpp b/engines/kyra/sound/sound_pcspk.cpp
deleted file mode 100644
index 110adde..0000000
--- a/engines/kyra/sound/sound_pcspk.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-/* 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 "kyra/sound/sound_intern.h"
-
-#include "audio/mixer.h"
-#include "audio/softsynth/pcspk.h"
-
-namespace Kyra {
-
-MidiDriver_PCSpeaker::MidiDriver_PCSpeaker(Audio::Mixer *mixer)
-	: MidiDriver_Emulated(mixer), _rate(mixer->getOutputRate()) {
-	_timerValue = 0;
-	memset(_channel, 0, sizeof(_channel));
-	memset(_note, 0, sizeof(_note));
-
-	for (int i = 0; i < 2; ++i)
-		_note[i].hardwareChannel = 0xFF;
-
-	_speaker = new Audio::PCSpeaker(_rate);
-	assert(_speaker);
-	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
-	_countdown = 0xFFFF;
-	_hardwareChannel[0] = 0xFF;
-	_modulationFlag = false;
-}
-
-MidiDriver_PCSpeaker::~MidiDriver_PCSpeaker() {
-	_mixer->stopHandle(_mixerSoundHandle);
-	delete _speaker;
-	_speaker = 0;
-}
-
-void MidiDriver_PCSpeaker::send(uint32 data) {
-	Common::StackLock lock(_mutex);
-
-	uint8 channel = data & 0x0F;
-	uint8 param1 = (data >>  8) & 0xFF;
-	uint8 param2 = (data >> 16) & 0xFF;
-
-	uint8 flags = 0x00;
-
-	if (channel > 1)
-		return;
-
-	switch (data & 0xF0) {
-	case 0x80:	// note off
-		noteOff(channel, param1);
-		return;
-
-	case 0x90:	// note on
-		if (channel > 1)
-			return;
-
-		if (param2)
-			noteOn(channel, param1);
-		else
-			noteOff(channel, param1);
-		return;
-
-	case 0xB0:	// controller
-		switch (param1) {
-		case 0x01:	// modulation
-			_channel[channel].modulation = param2;
-			break;
-
-		case 0x40:	// hold
-			_channel[channel].hold = param2;
-			if (param2 < 0x40)
-				resetController(channel);
-			return;
-
-		case 0x70:	// voice protect
-			_channel[channel].voiceProtect = param2;
-			return;
-
-		case 0x79:	// all notes off
-			_channel[channel].hold = 0;
-			resetController(channel);
-			_channel[channel].modulation = 0;
-			_channel[channel].pitchBendLow = 0;
-			_channel[channel].pitchBendHigh = 0x40;
-			flags = 0x01;
-			break;
-
-		default:
-			return;
-		}
-		break;
-
-	case 0xE0:	// pitch bend
-		flags = 0x01;
-		_channel[channel].pitchBendLow = param1;
-		_channel[channel].pitchBendHigh = param2;
-		break;
-
-	default:
-		return;
-	}
-
-	for (int i = 0; i < 2; ++i) {
-		if (_note[i].enabled && _note[i].midiChannel == channel) {
-			_note[i].flags |= flags;
-			setupTone(i);
-		}
-	}
-}
-
-void MidiDriver_PCSpeaker::resetController(int channel) {
-	for (int i = 0; i < 2; ++i) {
-		if (_note[i].enabled && _note[i].midiChannel == channel && _note[i].processHold)
-			noteOff(channel, _note[i].note);
-	}
-}
-
-void MidiDriver_PCSpeaker::noteOn(int channel, int note) {
-	int n = 0;
-
-	while (n < 2 && _note[n].enabled)
-		++n;
-
-	if (n >= 2)
-		return;
-
-	_note[n].midiChannel = channel;
-	_note[n].note = note;
-	_note[n].enabled = true;
-	_note[n].processHold = false;
-	_note[n].hardwareFlags = 0x20;
-	_note[n].priority = 0x7FFF;
-	_note[n].flags = 0x01;
-
-	turnNoteOn(n);
-}
-
-void MidiDriver_PCSpeaker::turnNoteOn(int note) {
-	if (_hardwareChannel[0] == 0xFF) {
-		_note[note].hardwareChannel = 0;
-		++_channel[_note[note].midiChannel].noteCount;
-		_hardwareChannel[0] = _note[note].midiChannel;
-		_note[note].flags = 0x01;
-
-		setupTone(note);
-	} else {
-		overwriteNote(note);
-	}
-}
-
-void MidiDriver_PCSpeaker::overwriteNote(int note) {
-	int totalNotes = 0;
-	for (int i = 0; i < 2; ++i) {
-		if (_note[i].enabled) {
-			++totalNotes;
-			const int channel = _note[i].midiChannel;
-
-			uint16 priority = 0xFFFF;
-			if (_channel[channel].voiceProtect < 0x40)
-				priority = _note[i].priority;
-
-			if (_channel[channel].noteCount > priority)
-				priority = 0;
-			else
-				priority -= _channel[channel].noteCount;
-
-			_note[i].precedence = priority;
-		}
-	}
-
-	if (totalNotes <= 1)
-		return;
-
-	do {
-		uint16 maxValue = 0;
-		uint16 minValue = 0xFFFF;
-		int newNote = 0;
-
-		for (int i = 0; i < 2; ++i) {
-			if (_note[i].enabled) {
-				if (_note[i].hardwareChannel == 0xFF) {
-					if (_note[i].precedence >= maxValue) {
-						maxValue = _note[i].precedence;
-						newNote = i;
-					}
-				} else {
-					if (_note[i].precedence <= minValue) {
-						minValue = _note[i].precedence;
-						note = i;
-					}
-				}
-			}
-		}
-
-		if (maxValue < minValue)
-			return;
-
-		turnNoteOff(_note[note].hardwareChannel);
-		_note[note].enabled = false;
-
-		_note[newNote].hardwareChannel = _note[note].hardwareChannel;
-		++_channel[_note[newNote].midiChannel].noteCount;
-		_hardwareChannel[_note[note].hardwareChannel] = _note[newNote].midiChannel;
-		_note[newNote].flags = 0x01;
-
-		setupTone(newNote);
-	} while (--totalNotes);
-}
-
-void MidiDriver_PCSpeaker::noteOff(int channel, int note) {
-	for (int i = 0; i < 2; ++i) {
-		if (_note[i].enabled && _note[i].note == note && _note[i].midiChannel == channel) {
-			if (_channel[i].hold < 0x40) {
-				turnNoteOff(i);
-				_note[i].enabled = false;
-			} else {
-				_note[i].processHold = true;
-			}
-		}
-	}
-}
-
-void MidiDriver_PCSpeaker::turnNoteOff(int note) {
-	if (_note[note].hardwareChannel != 0xFF) {
-		_note[note].hardwareFlags &= 0xDF;
-		_note[note].flags |= 1;
-
-		setupTone(note);
-
-		--_channel[_note[note].midiChannel].noteCount;
-
-		_hardwareChannel[_note[note].hardwareChannel] = 0xFF;
-		_note[note].hardwareChannel = 0xFF;
-	}
-}
-
-void MidiDriver_PCSpeaker::setupTone(int note) {
-	if (_note[note].hardwareChannel == 0xFF)
-		return;
-
-	if (!(_note[note].flags & 0x01))
-		return;
-
-	if (!(_note[note].hardwareFlags & 0x20)) {
-		_speaker->stop();
-	} else {
-		const int midiChannel = _note[note].midiChannel;
-		uint16 pitchBend = (_channel[midiChannel].pitchBendHigh << 7) | _channel[midiChannel].pitchBendLow;
-
-		int noteValue = _note[note].note;
-
-		noteValue -= 24;
-		do {
-			noteValue += 12;
-		} while (noteValue < 0);
-
-		noteValue += 12;
-		do {
-			noteValue -= 12;
-		} while (noteValue > 95);
-
-		int16 modulation = _note[note].modulation;
-
-		int tableIndex = MAX(noteValue - 12, 0);
-		uint16 note1 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
-		tableIndex = MIN(noteValue + 12, 95);
-		uint16 note2 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
-		uint16 note3 = (_noteTable2[noteValue] << 8) | _noteTable1[noteValue];
-
-		int32 countdown = pitchBend - 0x2000;
-		countdown += modulation;
-
-		if (countdown >= 0)
-			countdown *= (note2 - note3);
-		else
-			countdown *= (note3 - note1);
-
-		countdown /= 0x2000;
-		countdown += note3;
-
-		countdown = uint16(countdown & 0xFFFF);
-		if (countdown != _countdown)
-			_countdown = countdown;
-
-		_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / _countdown, -1);
-	}
-
-	_note[note].flags &= 0xFE;
-}
-
-void MidiDriver_PCSpeaker::generateSamples(int16 *buffer, int numSamples) {
-	Common::StackLock lock(_mutex);
-	_speaker->readBuffer(buffer, numSamples);
-}
-
-void MidiDriver_PCSpeaker::onTimer() {
-	/*Common::StackLock lock(_mutex);
-
-	_timerValue += 20;
-	if (_timerValue < 120)
-		return;
-	_timerValue -= 120;
-
-	_modulationFlag = !_modulationFlag;
-	for (int i = 0; i < 2; ++i) {
-		if (_note[i].enabled) {
-			uint16 modValue = 5 * _channel[_note[i].midiChannel].modulation;
-			if (_modulationFlag)
-				modValue = -modValue;
-			_note[i].modulation = modValue;
-			_note[i].flags |= 1;
-
-			setupTone(i);
-		}
-	}*/
-}
-
-const uint8 MidiDriver_PCSpeaker::_noteTable1[] = {
-	0x88, 0xB5, 0x4E, 0x40, 0x41, 0xCD, 0xC4, 0x3D,
-	0x43, 0x7C, 0x2A, 0xD6, 0x88, 0xB5, 0xFF, 0xD1,
-	0x20, 0xA7, 0xE2, 0x1E, 0xCE, 0xBE, 0xF2, 0x8A,
-	0x44, 0x41, 0x7F, 0xE8, 0x90, 0x63, 0x63, 0x8F,
-	0xE7, 0x5F, 0x01, 0xBD, 0xA2, 0xA0, 0xBF, 0xF4,
-	0x48, 0xB1, 0x31, 0xC7, 0x70, 0x2F, 0xFE, 0xE0,
-	0xD1, 0xD0, 0xDE, 0xFB, 0x24, 0x58, 0x98, 0xE3,
-	0x39, 0x97, 0xFF, 0x6F, 0xE8, 0x68, 0xEF, 0x7D,
-	0x11, 0xAC, 0x4C, 0xF1, 0x9C, 0x4B, 0xFF, 0xB7,
-	0x74, 0x34, 0xF7, 0xBE, 0x88, 0x56, 0x26, 0xF8,
-	0xCE, 0xA5, 0x7F, 0x5B, 0x3A, 0x1A, 0xFB, 0xDF,
-	0xC4, 0xAB, 0x93, 0x7C, 0x67, 0x52, 0x3F, 0x2D
-};
-
-const uint8 MidiDriver_PCSpeaker::_noteTable2[] = {
-	0x8E, 0x86, 0xFD, 0xF0, 0xE2, 0xD5, 0xC9, 0xBE,
-	0xB3, 0xA9, 0xA0, 0x96, 0x8E, 0x86, 0x7E, 0x77,
-	0x71, 0x6A, 0x64, 0x5F, 0x59, 0x54, 0x4F, 0x4B,
-	0x47, 0x43, 0x3F, 0x3B, 0x38, 0x35, 0x32, 0x2F,
-	0x2C, 0x2A, 0x28, 0x25, 0x23, 0x21, 0x1F, 0x1D,
-	0x1C, 0x1A, 0x19, 0x17, 0x16, 0x15, 0x13, 0x12,
-	0x11, 0x10, 0x0F, 0x0E, 0x0E, 0x0D, 0x0C, 0x0B,
-	0x0B, 0x0A, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07,
-	0x07, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04,
-	0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
-	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
-	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
-};
-
-} // End of namespace Kyra


Commit: 145edf0b946c3288732543a2f45956bf77af1cb5
    https://github.com/scummvm/scummvm/commit/145edf0b946c3288732543a2f45956bf77af1cb5
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-15T21:51:48+02:00

Commit Message:
KYRA: sound files/classes reorganization step #5

Rename some sound classes to reflect the recent file renamings.

Changed paths:
    engines/kyra/engine/kyra_lok.h
    engines/kyra/engine/kyra_mr.cpp
    engines/kyra/engine/kyra_mr.h
    engines/kyra/engine/kyra_v1.cpp
    engines/kyra/sound/sound_amiga_lok.cpp
    engines/kyra/sound/sound_digital_mr.cpp
    engines/kyra/sound/sound_digital_mr.h
    engines/kyra/sound/sound_intern.h
    engines/kyra/sound/sound_pc98_lok.cpp
    engines/kyra/sound/sound_towns_lok.cpp


diff --git a/engines/kyra/engine/kyra_lok.h b/engines/kyra/engine/kyra_lok.h
index 0505387..5165486 100644
--- a/engines/kyra/engine/kyra_lok.h
+++ b/engines/kyra/engine/kyra_lok.h
@@ -32,7 +32,7 @@
 namespace Kyra {
 
 class Movie;
-class SoundDigital;
+class SoundDigital_MR;
 class SeqPlayer;
 class Sprites;
 class Animator_LoK;
diff --git a/engines/kyra/engine/kyra_mr.cpp b/engines/kyra/engine/kyra_mr.cpp
index 4f87c53..b489b0f 100644
--- a/engines/kyra/engine/kyra_mr.cpp
+++ b/engines/kyra/engine/kyra_mr.cpp
@@ -211,7 +211,7 @@ Common::Error KyraEngine_MR::init() {
 	KyraEngine_v1::init();
 	initStaticResource();
 
-	_soundDigital = new SoundDigital(this, _mixer);
+	_soundDigital = new SoundDigital_MR(this, _mixer);
 	assert(_soundDigital);
 	KyraEngine_v1::_text = _text = new TextDisplayer_MR(this, _screen);
 	assert(_text);
diff --git a/engines/kyra/engine/kyra_mr.h b/engines/kyra/engine/kyra_mr.h
index 83c97eb..e44303b 100644
--- a/engines/kyra/engine/kyra_mr.h
+++ b/engines/kyra/engine/kyra_mr.h
@@ -33,7 +33,7 @@
 
 namespace Kyra {
 
-class SoundDigital;
+class SoundDigital_MR;
 class Screen_MR;
 class MainMenu;
 class WSAMovie_v2;
@@ -55,7 +55,7 @@ public:
 	Screen *screen() { return _screen; }
 	Screen_v2 *screen_v2() const { return _screen; }
 	GUI *gui() const { return _gui; }
-	SoundDigital *soundDigital() { return _soundDigital; }
+	SoundDigital_MR *soundDigital() { return _soundDigital; }
 	int language() const { return _lang; }
 	bool heliumMode() const { return _configHelium; }
 
@@ -80,7 +80,7 @@ private:
 
 	// --
 	Screen_MR *_screen;
-	SoundDigital *_soundDigital;
+	SoundDigital_MR *_soundDigital;
 
 	Common::Error init();
 
diff --git a/engines/kyra/engine/kyra_v1.cpp b/engines/kyra/engine/kyra_v1.cpp
index e2896eb..7b93851 100644
--- a/engines/kyra/engine/kyra_v1.cpp
+++ b/engines/kyra/engine/kyra_v1.cpp
@@ -99,16 +99,16 @@ Common::Error KyraEngine_v1::init() {
 	if (!_flags.useDigSound) {
 		if (_flags.platform == Common::kPlatformFMTowns) {
 			if (_flags.gameID == GI_KYRA1)
-				_sound = new SoundTowns(this, _mixer);
+				_sound = new SoundTowns_LoK(this, _mixer);
 			else
 				_sound = new SoundTownsPC98_v2(this, _mixer);
 		} else if (_flags.platform == Common::kPlatformPC98) {
 			if (_flags.gameID == GI_KYRA1)
-				_sound = new SoundPC98(this, _mixer);
+				_sound = new SoundPC98_LoK(this, _mixer);
 			else
 				_sound = new SoundTownsPC98_v2(this, _mixer);
 		} else if (_flags.platform == Common::kPlatformAmiga) {
-			_sound = new SoundAmiga(this, _mixer);
+			_sound = new SoundAmiga_LoK(this, _mixer);
 		} else {
 			// In Kyra 1 users who have specified a default MT-32 device in the launcher settings
 			// will get MT-32 music, otherwise AdLib. In Kyra 2 and LoL users who have specified a
diff --git a/engines/kyra/sound/sound_amiga_lok.cpp b/engines/kyra/sound/sound_amiga_lok.cpp
index 1104004..5edea54 100644
--- a/engines/kyra/sound/sound_amiga_lok.cpp
+++ b/engines/kyra/sound/sound_amiga_lok.cpp
@@ -28,7 +28,7 @@
 
 namespace Kyra {
 
-SoundAmiga::SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer)
+SoundAmiga_LoK::SoundAmiga_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer)
 	: Sound(vm, mixer),
 	  _driver(0),
 	  _musicHandle(),
@@ -39,12 +39,12 @@ SoundAmiga::SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer)
 	  _tableSfxGame_Size(0) {
 }
 
-SoundAmiga::~SoundAmiga() {
+SoundAmiga_LoK::~SoundAmiga_LoK() {
 	_mixer->stopHandle(_musicHandle);
 	delete _driver;
 }
 
-bool SoundAmiga::init() {
+bool SoundAmiga_LoK::init() {
 	_driver = new Audio::MaxTrax(_mixer->getOutputRate(), true);
 
 	_tableSfxIntro = _vm->staticres()->loadAmigaSfxTable(k1AmigaIntroSFXTable, _tableSfxIntro_Size);
@@ -53,24 +53,24 @@ bool SoundAmiga::init() {
 	return _driver != 0 && _tableSfxIntro && _tableSfxGame;
 }
 
-void SoundAmiga::initAudioResourceInfo(int set, void *info) {
+void SoundAmiga_LoK::initAudioResourceInfo(int set, void *info) {
 	// See comment below
 }
 
-void SoundAmiga::selectAudioResourceSet(int set) {
+void SoundAmiga_LoK::selectAudioResourceSet(int set) {
 	// It seems that loadSoundFile() is doing what would normally be done in here.
 	// As long as this driver is only required for one single target (Kyra 1 Amiga)
 	// this doesn't matter much.
 }
 
-bool SoundAmiga::hasSoundFile(uint file) const {
+bool SoundAmiga_LoK::hasSoundFile(uint file) const {
 	if (file < 3)
 		return true;
 	return false;
 }
 
-void SoundAmiga::loadSoundFile(uint file) {
-	debugC(5, kDebugLevelSound, "SoundAmiga::loadSoundFile(%d)", file);
+void SoundAmiga_LoK::loadSoundFile(uint file) {
+	debugC(5, kDebugLevelSound, "SoundAmiga_LoK::loadSoundFile(%d)", file);
 
 	static const char *const tableFilenames[3][2] = {
 		{ "introscr.mx",  "introinst.mx" },
@@ -92,14 +92,14 @@ void SoundAmiga::loadSoundFile(uint file) {
 			loaded = _driver->load(*scoreIn, true, false);
 			loaded = loaded && _driver->load(*sampleIn, false, true);
 		} else
-			warning("SoundAmiga: missing atleast one of those music files: %s, %s", scoreName, sampleName);
+			warning("SoundAmiga_LoK: missing atleast one of those music files: %s, %s", scoreName, sampleName);
 		delete sampleIn;
 	} else {
 		if (scoreIn) {
 			_fileLoaded = kFileNone;
 			loaded = _driver->load(*scoreIn);
 		} else
-			warning("SoundAmiga: missing music file: %s", scoreName);
+			warning("SoundAmiga_LoK: missing music file: %s", scoreName);
 	}
 	delete scoreIn;
 
@@ -107,8 +107,8 @@ void SoundAmiga::loadSoundFile(uint file) {
 		_fileLoaded = (FileType)file;
 }
 
-void SoundAmiga::playTrack(uint8 track) {
-	debugC(5, kDebugLevelSound, "SoundAmiga::playTrack(%d)", track);
+void SoundAmiga_LoK::playTrack(uint8 track) {
+	debugC(5, kDebugLevelSound, "SoundAmiga_LoK::playTrack(%d)", track);
 
 	static const byte tempoIntro[] = { 0x46, 0x55, 0x3C, 0x41 };
 	static const byte tempoFinal[] = { 0x78, 0x50 };
@@ -172,13 +172,13 @@ void SoundAmiga::playTrack(uint8 track) {
 		beginFadeOut();
 }
 
-void SoundAmiga::haltTrack() {
-	debugC(5, kDebugLevelSound, "SoundAmiga::haltTrack()");
+void SoundAmiga_LoK::haltTrack() {
+	debugC(5, kDebugLevelSound, "SoundAmiga_LoK::haltTrack()");
 	_driver->stopMusic();
 }
 
-void SoundAmiga::beginFadeOut() {
-	debugC(5, kDebugLevelSound, "SoundAmiga::beginFadeOut()");
+void SoundAmiga_LoK::beginFadeOut() {
+	debugC(5, kDebugLevelSound, "SoundAmiga_LoK::beginFadeOut()");
 	for (int i = 0x3F; i >= 0; --i) {
 		_driver->setVolume((byte)i);
 		_vm->delay(_vm->tickLength());
@@ -189,8 +189,8 @@ void SoundAmiga::beginFadeOut() {
 	_driver->setVolume(0x40);
 }
 
-void SoundAmiga::playSoundEffect(uint8 track, uint8) {
-	debugC(5, kDebugLevelSound, "SoundAmiga::playSoundEffect(%d)", track);
+void SoundAmiga_LoK::playSoundEffect(uint8 track, uint8) {
+	debugC(5, kDebugLevelSound, "SoundAmiga_LoK::playSoundEffect(%d)", track);
 	const AmigaSfxTable *sfx = 0;
 	bool pan = false;
 
diff --git a/engines/kyra/sound/sound_digital_mr.cpp b/engines/kyra/sound/sound_digital_mr.cpp
index ffcfcd1..79791a6 100644
--- a/engines/kyra/sound/sound_digital_mr.cpp
+++ b/engines/kyra/sound/sound_digital_mr.cpp
@@ -102,17 +102,17 @@ int KyraAudioStream::readBuffer(int16 *buffer, const int numSamples) {
 	return samplesRead;
 }
 
-SoundDigital::SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
+SoundDigital_MR::SoundDigital_MR(KyraEngine_MR *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
 	for (uint i = 0; i < ARRAYSIZE(_sounds); ++i)
 		_sounds[i].stream = 0;
 }
 
-SoundDigital::~SoundDigital() {
+SoundDigital_MR::~SoundDigital_MR() {
 	for (int i = 0; i < ARRAYSIZE(_sounds); ++i)
 		stopSound(i);
 }
 
-int SoundDigital::playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume, bool loop, int channel) {
+int SoundDigital_MR::playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume, bool loop, int channel) {
 	Sound *use = 0;
 	if (channel != -1 && channel < ARRAYSIZE(_sounds)) {
 		stopSound(channel);
@@ -194,7 +194,7 @@ int SoundDigital::playSound(const char *filename, uint8 priority, Audio::Mixer::
 	return use - _sounds;
 }
 
-bool SoundDigital::isPlaying(int channel) {
+bool SoundDigital_MR::isPlaying(int channel) {
 	if (channel == -1)
 		return false;
 
@@ -206,7 +206,7 @@ bool SoundDigital::isPlaying(int channel) {
 	return _mixer->isSoundHandleActive(_sounds[channel].handle);
 }
 
-void SoundDigital::stopSound(int channel) {
+void SoundDigital_MR::stopSound(int channel) {
 	if (channel == -1)
 		return;
 
@@ -215,14 +215,14 @@ void SoundDigital::stopSound(int channel) {
 	_sounds[channel].stream = 0;
 }
 
-void SoundDigital::stopAllSounds() {
+void SoundDigital_MR::stopAllSounds() {
 	for (int i = 0; i < ARRAYSIZE(_sounds); ++i) {
 		if (isPlaying(i))
 			stopSound(i);
 	}
 }
 
-void SoundDigital::beginFadeOut(int channel, int ticks) {
+void SoundDigital_MR::beginFadeOut(int channel, int ticks) {
 	if (isPlaying(channel))
 		_sounds[channel].stream->beginFadeOut(ticks * _vm->tickLength());
 }
@@ -231,7 +231,7 @@ void SoundDigital::beginFadeOut(int channel, int ticks) {
 
 Audio::SeekableAudioStream *makeAUDStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
 
-const SoundDigital::AudioCodecs SoundDigital::_supportedCodecs[] = {
+const SoundDigital_MR::AudioCodecs SoundDigital_MR::_supportedCodecs[] = {
 #ifdef USE_FLAC
 	{ ".FLA", Audio::makeFLACStream },
 #endif // USE_FLAC
diff --git a/engines/kyra/sound/sound_digital_mr.h b/engines/kyra/sound/sound_digital_mr.h
index 1aece2d..b41929d 100644
--- a/engines/kyra/sound/sound_digital_mr.h
+++ b/engines/kyra/sound/sound_digital_mr.h
@@ -44,10 +44,10 @@ class KyraEngine_MR;
  *
  * This is just used for Kyrandia 3.
  */
-class SoundDigital {
+class SoundDigital_MR {
 public:
-	SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer);
-	~SoundDigital();
+	SoundDigital_MR(KyraEngine_MR *vm, Audio::Mixer *mixer);
+	~SoundDigital_MR();
 
 	/**
 	 * Plays a sound.
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index 4820133..8d76a80 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -110,10 +110,10 @@ private:
 	Common::Mutex _mutex;
 };
 
-class SoundTowns : public Sound {
+class SoundTowns_LoK : public Sound {
 public:
-	SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer);
-	virtual ~SoundTowns();
+	SoundTowns_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+	virtual ~SoundTowns_LoK();
 
 	virtual kType getMusicType() const { return kTowns; }
 
@@ -164,10 +164,10 @@ private:
 	const uint8 *_sfxWDTable;
 };
 
-class SoundPC98 : public Sound {
+class SoundPC98_LoK : public Sound {
 public:
-	SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer);
-	virtual ~SoundPC98();
+	SoundPC98_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+	virtual ~SoundPC98_LoK();
 
 	virtual kType getMusicType() const override { return kPC98; }
 
@@ -315,10 +315,10 @@ struct AmigaSfxTable {
 	uint8 pan;
 };
 
-class SoundAmiga : public Sound {
+class SoundAmiga_LoK : public Sound {
 public:
-	SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer);
-	virtual ~SoundAmiga();
+	SoundAmiga_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+	virtual ~SoundAmiga_LoK();
 
 	virtual kType getMusicType() const override { return kAmiga; } //FIXME
 
diff --git a/engines/kyra/sound/sound_pc98_lok.cpp b/engines/kyra/sound/sound_pc98_lok.cpp
index 38a9a56..8695fc5 100644
--- a/engines/kyra/sound/sound_pc98_lok.cpp
+++ b/engines/kyra/sound/sound_pc98_lok.cpp
@@ -29,12 +29,12 @@
 
 namespace Kyra {
 
-SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+SoundPC98_LoK::SoundPC98_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
 	Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _currentResourceSet(0) {
 	memset(&_resInfo, 0, sizeof(_resInfo));
 }
 
-SoundPC98::~SoundPC98() {
+SoundPC98_LoK::~SoundPC98_LoK() {
 	delete[] _musicTrackData;
 	delete[] _sfxTrackData;
 	delete _driver;
@@ -42,7 +42,7 @@ SoundPC98::~SoundPC98() {
 		initAudioResourceInfo(i, 0);
 }
 
-bool SoundPC98::init() {
+bool SoundPC98_LoK::init() {
 	_driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
 	bool reslt = _driver->init();
 	updateVolumeSettings();
@@ -50,25 +50,25 @@ bool SoundPC98::init() {
 	return reslt;
 }
 
-void SoundPC98::initAudioResourceInfo(int set, void *info) {
+void SoundPC98_LoK::initAudioResourceInfo(int set, void *info) {
 	if (set >= kMusicIntro && set <= kMusicFinale) {
 		delete _resInfo[set];
 		_resInfo[set] = info ? new Common::String(((SoundResourceInfo_PC98*)info)->pattern) : 0;
 	}
 }
 
-void SoundPC98::selectAudioResourceSet(int set) {
+void SoundPC98_LoK::selectAudioResourceSet(int set) {
 	if (set >= kMusicIntro && set <= kMusicFinale) {
 		if (_resInfo[set])
 			_currentResourceSet = set;
 	}
 }
 
-bool SoundPC98::hasSoundFile(uint file) const {
+bool SoundPC98_LoK::hasSoundFile(uint file) const {
 	return true;
 }
 
-void SoundPC98::loadSoundFile(uint) {
+void SoundPC98_LoK::loadSoundFile(uint) {
 	if (_currentResourceSet == kMusicIntro) {
 		delete[] _sfxTrackData;
 		_sfxTrackData = 0;
@@ -86,12 +86,12 @@ void SoundPC98::loadSoundFile(uint) {
 	}
 }
 
-void SoundPC98::loadSoundFile(Common::String file) {
+void SoundPC98_LoK::loadSoundFile(Common::String file) {
 	delete[] _sfxTrackData;
 	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
 }
 
-void SoundPC98::playTrack(uint8 track) {
+void SoundPC98_LoK::playTrack(uint8 track) {
 	track -= 1;
 
 	if (track == _lastTrack && _musicEnabled)
@@ -108,12 +108,12 @@ void SoundPC98::playTrack(uint8 track) {
 	_lastTrack = track;
 }
 
-void SoundPC98::haltTrack() {
+void SoundPC98_LoK::haltTrack() {
 	_lastTrack = -1;
 	_driver->reset();
 }
 
-void SoundPC98::beginFadeOut() {
+void SoundPC98_LoK::beginFadeOut() {
 	if (!_driver->musicPlaying())
 		return;
 
@@ -124,14 +124,14 @@ void SoundPC98::beginFadeOut() {
 	haltTrack();
 }
 
-void SoundPC98::playSoundEffect(uint8 track, uint8) {
+void SoundPC98_LoK::playSoundEffect(uint8 track, uint8) {
 	if (!_sfxTrackData)
 		return;
 
 	_driver->loadSoundEffectData(_sfxTrackData, track);
 }
 
-void SoundPC98::updateVolumeSettings() {
+void SoundPC98_LoK::updateVolumeSettings() {
 	if (!_driver)
 		return;
 
diff --git a/engines/kyra/sound/sound_towns_lok.cpp b/engines/kyra/sound/sound_towns_lok.cpp
index 6a22f5f..d66b649 100644
--- a/engines/kyra/sound/sound_towns_lok.cpp
+++ b/engines/kyra/sound/sound_towns_lok.cpp
@@ -31,14 +31,14 @@
 
 namespace Kyra {
 
-SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
+SoundTowns_LoK::SoundTowns_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer)
 	: Sound(vm, mixer), _lastTrack(-1), _musicTrackData(0), _sfxFileData(0), _cdaPlaying(0),
 	_sfxFileIndex((uint)-1), _musicFadeTable(0), _sfxWDTable(0), _sfxBTTable(0), _sfxChannel(0x46), _currentResourceSet(0) {
 	memset(&_resInfo, 0, sizeof(_resInfo));
 	_player = new EuphonyPlayer(_mixer);
 }
 
-SoundTowns::~SoundTowns() {
+SoundTowns_LoK::~SoundTowns_LoK() {
 	g_system->getAudioCDManager()->stop();
 	haltTrack();
 	delete _player;
@@ -48,7 +48,7 @@ SoundTowns::~SoundTowns() {
 		initAudioResourceInfo(i, 0);
 }
 
-bool SoundTowns::init() {
+bool SoundTowns_LoK::init() {
 	_vm->checkCD();
 	int unused = 0;
 	_musicFadeTable = _vm->staticres()->loadRawData(k1TownsMusicFadeTable, unused);
@@ -72,11 +72,11 @@ bool SoundTowns::init() {
 	return true;
 }
 
-void SoundTowns::process() {
+void SoundTowns_LoK::process() {
 	g_system->getAudioCDManager()->update();
 }
 
-void SoundTowns::playTrack(uint8 track) {
+void SoundTowns_LoK::playTrack(uint8 track) {
 	if (track < 2)
 		return;
 	track -= 2;
@@ -106,7 +106,7 @@ void SoundTowns::playTrack(uint8 track) {
 	_lastTrack = track;
 }
 
-void SoundTowns::haltTrack() {
+void SoundTowns_LoK::haltTrack() {
 	_lastTrack = -1;
 	g_system->getAudioCDManager()->stop();
 	g_system->getAudioCDManager()->update();
@@ -121,27 +121,27 @@ void SoundTowns::haltTrack() {
 	_player->stop();
 }
 
-void SoundTowns::initAudioResourceInfo(int set, void *info) {
+void SoundTowns_LoK::initAudioResourceInfo(int set, void *info) {
 	if (set >= kMusicIntro && set <= kMusicFinale) {
 		delete _resInfo[set];
 		_resInfo[set] = info ? new SoundResourceInfo_Towns(*(SoundResourceInfo_Towns*)info) : 0;
 	}
 }
 
-void SoundTowns::selectAudioResourceSet(int set) {
+void SoundTowns_LoK::selectAudioResourceSet(int set) {
 	if (set >= kMusicIntro && set <= kMusicFinale) {
 		if (_resInfo[set])
 			_currentResourceSet = set;
 	}
 }
 
-bool SoundTowns::hasSoundFile(uint file) const {
+bool SoundTowns_LoK::hasSoundFile(uint file) const {
 	if (file < res()->fileListSize)
 		return (res()->fileList[file] != 0);
 	return false;
 }
 
-void SoundTowns::loadSoundFile(uint file) {
+void SoundTowns_LoK::loadSoundFile(uint file) {
 	if (_sfxFileIndex == file || file >= res()->fileListSize)
 		return;
 	_sfxFileIndex = file;
@@ -149,7 +149,7 @@ void SoundTowns::loadSoundFile(uint file) {
 	_sfxFileData = _vm->resource()->fileData(res()->fileList[file], 0);
 }
 
-void SoundTowns::playSoundEffect(uint8 track, uint8) {
+void SoundTowns_LoK::playSoundEffect(uint8 track, uint8) {
 	if (!_sfxEnabled || !_sfxFileData)
 		return;
 
@@ -229,7 +229,7 @@ void SoundTowns::playSoundEffect(uint8 track, uint8) {
 	delete[] sfxPlaybackBuffer;
 }
 
-void SoundTowns::updateVolumeSettings() {
+void SoundTowns_LoK::updateVolumeSettings() {
 	if (!_player)
 		return;
 
@@ -241,7 +241,7 @@ void SoundTowns::updateVolumeSettings() {
 	_player->driver()->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
 }
 
-void SoundTowns::stopAllSoundEffects() {
+void SoundTowns_LoK::stopAllSoundEffects() {
 	_player->driver()->channelVolume(0x46, 0);
 	_player->driver()->channelVolume(0x47, 0);
 	_player->driver()->stopSoundEffect(0x46);
@@ -249,7 +249,7 @@ void SoundTowns::stopAllSoundEffects() {
 	_sfxChannel = 0x46;
 }
 
-void SoundTowns::beginFadeOut() {
+void SoundTowns_LoK::beginFadeOut() {
 	if (_cdaPlaying) {
 		for (int i = 118; i > 103; i--) {
 			_player->driver()->setOutputVolume(1, i, i);
@@ -316,7 +316,7 @@ void SoundTowns::beginFadeOut() {
 	haltTrack();
 }
 
-bool SoundTowns::loadInstruments() {
+bool SoundTowns_LoK::loadInstruments() {
 	uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
 	if (!twm)
 		return false;
@@ -343,7 +343,7 @@ bool SoundTowns::loadInstruments() {
 	return true;
 }
 
-void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
+void SoundTowns_LoK::playEuphonyTrack(uint32 offset, int loop) {
 	uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
 	Screen::decodeFrame4(twm + 19312 + offset, _musicTrackData, 50570);
 	delete[] twm;
@@ -381,7 +381,7 @@ void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
 	_player->startTrack(src, trackSize, startTick);
 }
 
-void SoundTowns::fadeOutSoundEffects() {
+void SoundTowns_LoK::fadeOutSoundEffects() {
 	for (int i = 127; i > 0; i-= 12) {
 		_player->driver()->channelVolume(0x46, i);
 		_player->driver()->channelVolume(0x47, i);


Commit: 4cb79db612d2e4735a38538bb95aa07131fcda23
    https://github.com/scummvm/scummvm/commit/4cb79db612d2e4735a38538bb95aa07131fcda23
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-15T21:51:48+02:00

Commit Message:
KYRA: add some #ifdefs for disabling EOB

Disabling EOB should already work without error. This adds some #ifdefs to recently added files.

Changed paths:
    engines/kyra/sound/drivers/audiomaster2.cpp
    engines/kyra/sound/drivers/audiomaster2.h
    engines/kyra/sound/sound_amiga_eob.cpp
    engines/kyra/sound/sound_intern.h
    engines/kyra/sound/sound_towns_darkmoon.cpp


diff --git a/engines/kyra/sound/drivers/audiomaster2.cpp b/engines/kyra/sound/drivers/audiomaster2.cpp
index 7db9887..d50d519 100644
--- a/engines/kyra/sound/drivers/audiomaster2.cpp
+++ b/engines/kyra/sound/drivers/audiomaster2.cpp
@@ -20,6 +20,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #include "kyra/resource/resource.h"
 #include "kyra/sound/drivers/audiomaster2.h"
 
@@ -1190,7 +1192,7 @@ void AudioMaster2IFFLoader::initResource() {
 AudioMaster2Internal *AudioMaster2Internal::_refInstance = 0;
 int AudioMaster2Internal::_refCount = 0;
 
-AudioMaster2Internal::AudioMaster2Internal(Audio::Mixer *mixer) : Paula(true, mixer->getOutputRate(), mixer->getOutputRate() / 50), _mixer(mixer), _res(0), _fadeOutSteps(0), _durationCounter(0), _ready(false) {
+AudioMaster2Internal::AudioMaster2Internal(Audio::Mixer *mixer) : Paula(true, mixer->getOutputRate(), mixer->getOutputRate() / 50), _mixer(mixer), _res(0), _io(0), _fadeOutSteps(0), _durationCounter(0), _ready(false) {
 	_channels[0] = _channels[1] = _channels[2] = _channels[3] = 0;
 	setAudioFilter(true);
 }
@@ -1553,3 +1555,5 @@ void AudioMaster2::setSoundEffectVolume(int volume) {
 }
 
 } // End of namespace Kyra
+
+#endif
\ No newline at end of file
diff --git a/engines/kyra/sound/drivers/audiomaster2.h b/engines/kyra/sound/drivers/audiomaster2.h
index 0ac21d2..7c02fb3 100644
--- a/engines/kyra/sound/drivers/audiomaster2.h
+++ b/engines/kyra/sound/drivers/audiomaster2.h
@@ -36,6 +36,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #ifndef KYRA_SOUND_AUDIOMASTER2_H
 #define KYRA_SOUND_AUDIOMASTER2_H
 
@@ -81,3 +83,5 @@ private:
 } // End of namespace Kyra
 
 #endif
+
+#endif
\ No newline at end of file
diff --git a/engines/kyra/sound/sound_amiga_eob.cpp b/engines/kyra/sound/sound_amiga_eob.cpp
index f572c11..eded4fe 100644
--- a/engines/kyra/sound/sound_amiga_eob.cpp
+++ b/engines/kyra/sound/sound_amiga_eob.cpp
@@ -20,6 +20,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #include "kyra/sound/sound_intern.h"
 #include "kyra/resource/resource.h"
 #include "kyra/sound/drivers/audiomaster2.h"
@@ -234,3 +236,5 @@ int SoundAmiga_EoB::checkTrigger() {
 }
 
 } // End of namespace Kyra
+
+#endif
\ No newline at end of file
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index 8d76a80..29572f6 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -349,6 +349,8 @@ protected:
 	int _tableSfxGame_Size;
 };
 
+#ifdef ENABLE_EOB
+
 class SoundTowns_Darkmoon : public Sound, public TownsAudioInterfacePluginDriver {
 public:
 	SoundTowns_Darkmoon(KyraEngine_v1 *vm, Audio::Mixer *mixer);
@@ -441,6 +443,8 @@ private:
 	bool _ready;
 };
 
+#endif
+
 } // End of namespace Kyra
 
 #endif
diff --git a/engines/kyra/sound/sound_towns_darkmoon.cpp b/engines/kyra/sound/sound_towns_darkmoon.cpp
index 3758dff..a6032f8 100644
--- a/engines/kyra/sound/sound_towns_darkmoon.cpp
+++ b/engines/kyra/sound/sound_towns_darkmoon.cpp
@@ -20,6 +20,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #include "kyra/sound/sound_intern.h"
 #include "kyra/resource/resource.h"
 
@@ -283,3 +285,5 @@ void SoundTowns_Darkmoon::resetTrigger() {
 }
 
 } // End of namespace Kyra
+
+#endif
\ No newline at end of file





More information about the Scummvm-git-logs mailing list