[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