aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/kyra/engine/eobcommon.cpp2
-rw-r--r--engines/kyra/module.mk4
-rw-r--r--engines/kyra/sound/drivers/audiomaster2.cpp1390
-rw-r--r--engines/kyra/sound/drivers/audiomaster2.h78
-rw-r--r--engines/kyra/sound/sound.h9
-rw-r--r--engines/kyra/sound/sound_amiga_eob.cpp311
-rw-r--r--engines/kyra/sound/sound_intern.h31
7 files changed, 1824 insertions, 1 deletions
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 21677ac697..0e3e130c94 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -404,6 +404,8 @@ Common::Error EoBCoreEngine::init() {
_sound = new SoundTowns_Darkmoon(this, _mixer);
} else if (_flags.platform == Common::kPlatformPC98) {
+ } else if (_flags.platform == Common::kPlatformAmiga) {
+ _sound = new SoundAmiga_EoB(this, _mixer);
}
assert(_sound);
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 037ebe916e..9c20ad6569 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -123,7 +123,9 @@ MODULE_OBJS += \
script/script_eob.o \
sequence/sequences_eob.o \
sequence/sequences_darkmoon.o \
- sound/sound_towns_darkmoon.o
+ sound/sound_amiga_eob.o \
+ sound/sound_towns_darkmoon.o \
+ sound/drivers/audiomaster2.o
endif
# This module can be built as a plugin
diff --git a/engines/kyra/sound/drivers/audiomaster2.cpp b/engines/kyra/sound/drivers/audiomaster2.cpp
new file mode 100644
index 0000000000..404bb8468d
--- /dev/null
+++ b/engines/kyra/sound/drivers/audiomaster2.cpp
@@ -0,0 +1,1390 @@
+/* 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 "kyra/sound/drivers/audiomaster2.h"
+
+#include "audio/mods/paula.h"
+
+#include "common/iff_container.h"
+
+namespace Kyra {
+
+class AudioMaster2ResourceManager;
+class AudioMaster2Internal;
+class SoundResource;
+
+class AudioMaster2IOManager {
+public:
+ AudioMaster2IOManager();
+ ~AudioMaster2IOManager();
+
+ struct IOUnit {
+ IOUnit() : _next(0), _sampleData(0), _sampleDataRepeat(0), _lenOnce(0), _lenRepeat(0), _startTick(0), _endTick(0), _transposeData(0), _rate(0), _period(0), _transposePara(0), _transposeCounter(0),
+ _levelAdjustData(0), _volumeSetting(0), _outputVolume(0), _levelAdjustPara(0), _levelAdjustCounter(0), _flags(0) {}
+
+ IOUnit *_next;
+ const int8 *_sampleData;
+ const int8 *_sampleDataRepeat;
+ uint32 _lenOnce;
+ uint32 _lenRepeat;
+ uint32 _startTick;
+ uint32 _endTick;
+ const uint8 *_transposeData;
+ uint16 _rate;
+ uint16 _period;
+ uint16 _transposePara;
+ uint8 _transposeCounter;
+ const uint8 *_levelAdjustData;
+ uint16 _volumeSetting;
+ uint16 _outputVolume;
+ int16 _levelAdjustPara;
+ uint8 _levelAdjustCounter;
+ uint8 _flags;
+ };
+
+ void clearChain();
+ void deployChannels(IOUnit **dest);
+ IOUnit *requestFreeUnit();
+
+ uint32 _sync;
+ uint32 _tempo;
+
+private:
+ IOUnit *_units[8];
+ IOUnit *_ioChain;
+};
+
+class AudioMaster2Internal : public Audio::Paula {
+friend class AudioMaster2IOManager;
+private:
+ AudioMaster2Internal(Audio::Mixer *mixer);
+public:
+ ~AudioMaster2Internal();
+
+ static AudioMaster2Internal *open(Audio::Mixer *mixer);
+ static void close();
+
+ bool init();
+ bool loadRessourceFile(Common::SeekableReadStream *data);
+
+ bool startSound(const Common::String &name);
+ bool stopSound(const Common::String &name);
+
+ void flushResource(const Common::String &name);
+ void flushAllResources();
+
+ void interrupt();
+
+ void sync(SoundResource *res);
+ void stopChannels();
+
+private:
+ void updateDevice();
+
+ AudioMaster2IOManager::IOUnit *_channels[4];
+
+ AudioMaster2IOManager *_io;
+ AudioMaster2ResourceManager *_res;
+ Audio::Mixer *_mixer;
+
+ static AudioMaster2Internal *_refInstance;
+ static int _refCount;
+
+ Audio::SoundHandle _soundHandle;
+ bool _ready;
+};
+
+class SoundResource {
+protected:
+ SoundResource(AudioMaster2ResourceManager *res, int type) : _res(res), _type(type), _playing(false), _next(0), _refCnt(1) {}
+ virtual ~SoundResource() {}
+public:
+ void loadName(Common::ReadStream *stream, uint32 size);
+
+ void open();
+ void close();
+
+ virtual void prepare() {}
+ virtual void setSync(uint32 sync) {}
+
+ const Common::String &getName() const;
+ uint8 getType() const;
+ bool getPlayStatus() const;
+ void setPlayStatus(bool playing);
+
+ virtual void interrupt(AudioMaster2IOManager *io);
+ virtual void setupMusicNote(AudioMaster2IOManager::IOUnit *unit, uint8 note, uint16 volume) {}
+
+ enum Mode {
+ kIdle = 0,
+ kRestart = 1
+ };
+
+ SoundResource *_next;
+
+protected:
+ Common::String _name;
+ const uint8 _type;
+ uint8 _flags;
+ AudioMaster2ResourceManager *_res;
+
+private:
+ virtual void release() = 0;
+ virtual void setupEnvelopes(AudioMaster2IOManager::IOUnit *unit) {}
+ virtual void setupSoundEffect(AudioMaster2IOManager::IOUnit *unit, uint32 sync, uint32 tempo) {}
+
+ int _refCnt;
+ bool _playing;
+};
+
+class SoundResource8SVX : public SoundResource {
+public:
+ SoundResource8SVX(AudioMaster2ResourceManager *res);
+private:
+ virtual ~SoundResource8SVX();
+public:
+ void loadHeader(Common::ReadStream *stream, uint32 size);
+ void loadData(Common::ReadStream *stream, uint32 size);
+
+ void setupMusicNote(AudioMaster2IOManager::IOUnit *unit, uint8 note, uint16 volume);
+ void setupSoundEffect(AudioMaster2IOManager::IOUnit *unit, uint32 sync, uint32 tempo);
+
+private:
+ void release();
+
+ void setupEnvelopes(AudioMaster2IOManager::IOUnit *unit);
+
+ uint32 _numSamplesOnce;
+ uint32 _numSamplesRepeat;
+ uint32 _numSamplesPerCycle;
+ uint16 _rate;
+ uint8 _numBlocks;
+ uint8 _format;
+ uint32 _volume;
+
+ const int8 *_data;
+ uint32 _dataSize;
+
+ static const uint32 _periods[128];
+};
+
+class SoundResourceINST : public SoundResource {
+public:
+ SoundResourceINST(AudioMaster2ResourceManager *res) : SoundResource(res, 2), _samplesResource(0), _transpose(0), _levelAdjust(0) {}
+private:
+ virtual ~SoundResourceINST();
+public:
+ void loadPitchData(Common::ReadStream *stream, uint32 size);
+ void loadSamples(Common::ReadStream *stream, uint32 size);
+ void loadVolumeData(Common::ReadStream *stream, uint32 size);
+
+ void setupMusicNote(AudioMaster2IOManager::IOUnit *unit, uint8 note, uint16 volume);
+
+ struct EnvelopeData {
+ EnvelopeData(const uint8 *data, uint32 size) : volume(0x40), _data(data), _dataSize(size) {}
+ ~EnvelopeData() { delete[] _data; }
+ uint8 volume;
+ const uint8 *_data;
+ uint32 _dataSize;
+ };
+
+private:
+ void release();
+
+ void setupEnvelopes(AudioMaster2IOManager::IOUnit *unit);
+ void setupSoundEffect(AudioMaster2IOManager::IOUnit *unit, uint32 sync, uint32 rate);
+
+ EnvelopeData *_transpose;
+ EnvelopeData *_levelAdjust;
+ SoundResource8SVX *_samplesResource;
+};
+
+class SoundResourceSMUS : public SoundResource {
+public:
+ SoundResourceSMUS(AudioMaster2ResourceManager *res) : SoundResource(res, 1), _tempo(0), _vol(0), _masterVolume(0x40), _playFlags(0) {}
+private:
+ virtual ~SoundResourceSMUS();
+public:
+ void loadHeader(Common::ReadStream *stream, uint32 size);
+ void loadInstrument(Common::ReadStream *stream, uint32 size);
+ void loadTrack(Common::ReadStream *stream, uint32 size);
+
+ void prepare();
+ uint16 getTempo() const;
+ void setSync(uint32 sync);
+
+ void interrupt(AudioMaster2IOManager *io);
+
+private:
+ void release();
+
+ struct Track {
+ Track() : _dataStart(0), _dataEnd(0), _dataCur(0), _instrument(0), _volume(0), _sync(0) {}
+ ~Track() {
+ if (_instrument)
+ _instrument->close();
+ delete[] _dataStart;
+ }
+
+ void setInstrument(SoundResource *instr) {
+ if (_instrument)
+ _instrument->close();
+ _instrument = instr;
+ _instrument->open();
+ }
+
+ uint32 _sync;
+ SoundResource *_instrument;
+ uint8 _volume;
+ const uint8 *_dataStart;
+ const uint8 *_dataEnd;
+ const uint8 *_dataCur;
+ };
+
+ bool parse(AudioMaster2IOManager *io, Track *track);
+
+ uint16 _tempo;
+ uint16 _masterVolume;
+ uint8 _vol;
+
+ static const uint16 _durationTable[64];
+
+ Common::Array<Track*> _tracks;
+ Common::Array<SoundResource*> _instruments;
+ uint16 _playFlags;
+};
+
+class AudioMaster2ResourceManager {
+public:
+ AudioMaster2ResourceManager(AudioMaster2Internal *driver, Common::Mutex *mutex) : _driver(driver), _mutex(mutex), _chainPlaying(0), _chainInactive(0) {}
+ ~AudioMaster2ResourceManager();
+
+ void loadResourceFile(Common::SeekableReadStream *data);
+
+ void initResource(SoundResource *resource);
+ void releaseResource(const Common::String &resName);
+
+ void flush();
+
+ SoundResource *getResource(const Common::String &resName, SoundResource::Mode mode);
+
+ void interrupt(AudioMaster2IOManager *io);
+
+private:
+ SoundResource *retrieveFromChain(const Common::String &resName);
+ void linkToChain(SoundResource *resource, SoundResource::Mode mode);
+ void stopChain();
+
+ SoundResource *_chainPlaying;
+ SoundResource *_chainInactive;
+
+ AudioMaster2Internal *_driver;
+ Common::Mutex *_mutex;
+};
+
+class AudioMaster2IFFLoader : public Common::IFFParser {
+public:
+ AudioMaster2IFFLoader(Common::SeekableReadStream *stream, AudioMaster2ResourceManager *res) : Common::IFFParser(stream), _res(res), _curSong(0), _curSfx(0), _curIns(0) {}
+ virtual ~AudioMaster2IFFLoader();
+
+ bool loadChunk(Common::IFFChunk &chunk);
+
+private:
+ void initResource();
+
+ // define only additional chunk types which aren't already defined in common/iff_container.h
+ enum ChunkTypes {
+ ID_INST = MKTAG('I', 'N', 'S', 'T'),
+ ID_PTCH = MKTAG('P', 'T', 'C', 'H'),
+ ID_SAMP = MKTAG('S', 'A', 'M', 'P'),
+ ID_VLUM = MKTAG('V', 'L', 'U', 'M'),
+ ID_SMUS = MKTAG('S', 'M', 'U', 'S'),
+ ID_TRAK = MKTAG('T', 'R', 'A', 'K'),
+ ID_SHDR = MKTAG('S', 'H', 'D', 'R'),
+ ID_INS1 = MKTAG('I', 'N', 'S', '1')
+ };
+
+ SoundResourceINST *_curIns;
+ SoundResourceSMUS *_curSong;
+ SoundResource8SVX *_curSfx;
+
+ AudioMaster2ResourceManager *_res;
+};
+
+AudioMaster2IOManager::AudioMaster2IOManager() : _sync(1), _tempo(1), _ioChain(0) {
+ for (int i = 0; i < 8; ++i)
+ _units[i] = new IOUnit();
+}
+
+AudioMaster2IOManager::~AudioMaster2IOManager() {
+ for (int i = 0; i < 8; ++i)
+ delete _units[i];
+}
+
+void AudioMaster2IOManager::clearChain() {
+ _ioChain = 0;
+}
+
+void AudioMaster2IOManager::deployChannels(IOUnit **dest) {
+ IOUnit *cur = _ioChain;
+ IOUnit *prev = 0;
+ _ioChain = 0;
+
+ for (; cur; cur = cur->_next) {
+ if (!(cur->_flags & 1)) {
+ cur->_flags &= ~2;
+
+ if (prev)
+ prev->_next = cur->_next;
+ else
+ _ioChain = cur->_next;
+
+ continue;
+ }
+
+ IOUnit *unit = 0;
+ uint32 lowest = 0xFFFFFFFF;
+ uint8 chan = 3;
+ bool foundFree = false;
+
+ for (uint8 i = 3; i < 4; --i) {
+ unit = dest[i];
+ if (!unit) {
+ chan = i;
+ foundFree = true;
+ break;
+ }
+
+ if (unit->_endTick < lowest) {
+ lowest = unit->_endTick;
+ chan = i;
+ }
+ }
+
+ if (!foundFree) {
+ dest[chan]->_flags &= ~2;
+ // The original driver sends ADCMD_FINISH here, but we don't need to do anything,
+ // since this driver runs on the same thread as the emulated hardware.
+ }
+
+ dest[chan] = cur;
+ prev = cur;
+ }
+}
+
+AudioMaster2IOManager::IOUnit *AudioMaster2IOManager::requestFreeUnit() {
+ for (int i = 0; i < 8; ++i) {
+ if (_units[i]->_flags & 2)
+ continue;
+ _units[i]->_flags = 7;
+ _units[i]->_next = _ioChain;
+ _ioChain = _units[i];
+ return _units[i];
+ }
+
+ return 0;
+}
+
+void SoundResource::loadName(Common::ReadStream *stream, uint32 size) {
+ char *data = new char[size + 1];
+
+ stream->read(data, size);
+ data[size] = '\0';
+ _name = data;
+
+ delete[] data;
+}
+
+void SoundResource::open() {
+ _refCnt++;
+}
+
+void SoundResource::close() {
+ if (--_refCnt <= 0)
+ release();
+}
+
+const Common::String &SoundResource::getName() const {
+ return _name;
+}
+
+uint8 SoundResource::getType() const {
+ return _type;
+}
+
+bool SoundResource::getPlayStatus() const {
+ return _playing;
+}
+
+void SoundResource::setPlayStatus(bool playing) {
+ _playing = playing;
+}
+
+void SoundResource::interrupt(AudioMaster2IOManager *io) {
+ setPlayStatus(false);
+ AudioMaster2IOManager::IOUnit *unit = io->requestFreeUnit();
+ setupSoundEffect(unit, io->_sync, io->_tempo);
+}
+
+SoundResource8SVX::SoundResource8SVX(AudioMaster2ResourceManager *res) : SoundResource(res, 4) {
+ _numSamplesOnce = _numSamplesRepeat = _numSamplesPerCycle = _volume = _dataSize = 0;
+ _rate = 0;
+ _numBlocks = _format = 0;
+ _data = 0;
+}
+
+SoundResource8SVX::~SoundResource8SVX() {
+ delete[] _data;
+}
+
+void SoundResource8SVX::loadHeader(Common::ReadStream *stream, uint32 size) {
+ if (size < 20)
+ error("SoundResource8SVX:loadHeader(): Invalid data chunk size");
+
+ _numSamplesOnce = stream->readUint32BE();
+ _numSamplesRepeat = stream->readUint32BE();
+ _numSamplesPerCycle = stream->readUint32BE();
+ _rate = stream->readUint16BE();
+ _numBlocks = stream->readByte();
+ _format = stream->readByte();
+
+ if (_format)
+ error("SoundResource8SVX:loadHeader(): Unsupported data format");
+
+ _volume = stream->readUint32BE();
+}
+
+void SoundResource8SVX::loadData(Common::ReadStream *stream, uint32 size) {
+ delete[] _data;
+ _dataSize = size;
+ int8 *data = new int8[size];
+ stream->read(data, size);
+ _data = data;
+}
+
+void SoundResource8SVX::setupMusicNote(AudioMaster2IOManager::IOUnit *unit, uint8 note, uint16 volume) {
+ uint32 no = _numSamplesOnce;
+ uint32 nr = _numSamplesRepeat;
+ // The original code uses 3579546 here. But since the Paula code uses kPalPaulaClock I do the same.
+ uint32 rt = Audio::Paula::kPalPaulaClock;
+ uint32 offs = 0;
+
+ if (_numSamplesRepeat && _numSamplesPerCycle) {
+ uint32 octave = _numBlocks;
+ rt = _periods[note] << 13;
+
+ for (rt /= _numSamplesPerCycle; rt >= 0x4000000 && octave > 1; octave--) {
+ offs += (no + nr);
+ no <<= 1;
+ nr <<= 1;
+ rt >>= 1;
+ rt /= _numSamplesPerCycle;
+ }
+
+ for (; octave > 1 && rt >= 0x46000; octave--) {
+ offs += (no + nr);
+ no <<= 1;
+ nr <<= 1;
+ rt >>= 1;
+ }
+ rt >>= 13;
+
+ } else if (_rate) {
+ rt /= _rate;
+ }
+
+ unit->_sampleData = _data + offs;
+ unit->_sampleDataRepeat = nr ? unit->_sampleData + no : 0;
+ unit->_lenOnce = no;
+ unit->_lenRepeat = nr;
+ unit->_rate = unit->_period = rt;
+ unit->_volumeSetting = unit->_outputVolume = volume;
+ setupEnvelopes(unit);
+}
+
+void SoundResource8SVX::setupSoundEffect(AudioMaster2IOManager::IOUnit *unit, uint32 sync, uint32 tempo) {
+ if (!unit)
+ return;
+
+ unit->_startTick = sync;
+ // The original code uses 3579546 here. But since the Paula code uses kPalPaulaClock I do the same.
+ unit->_rate = unit->_period = Audio::Paula::kPalPaulaClock / (_rate ? _rate : 2000);
+
+ uint32 no = _numSamplesOnce;
+ uint32 nr = _numSamplesRepeat;
+ uint32 octave = _numBlocks;
+ uint32 offs = 0;
+
+ for (; octave > 1; octave--) {
+ offs += (no + nr);
+ no <<= 1;
+ nr <<= 1;
+ }
+
+ unit->_sampleData = _data + offs;
+ unit->_sampleDataRepeat = nr ? unit->_sampleData + no : 0;
+ unit->_lenOnce = no;
+ unit->_lenRepeat = nr;
+
+ unit->_endTick = _numSamplesRepeat ? 0xFFFFFFFF : (tempo * _numSamplesOnce * 60) / _rate + sync;
+ unit->_volumeSetting = unit->_outputVolume = (_volume >= 0xFFFF) ? _volume >> 2 : 0x4000;
+ setupEnvelopes(unit);
+}
+
+void SoundResource8SVX::release() {
+ delete this;
+}
+
+void SoundResource8SVX::setupEnvelopes(AudioMaster2IOManager::IOUnit *unit) {
+ assert(unit);
+ unit->_transposeData = 0;
+ unit->_levelAdjustData = 0;
+}
+
+const uint32 SoundResource8SVX::_periods[128] = {
+ 0x0006ae3f, 0x00064e42, 0x0005f3a8, 0x00059e23, 0x00054d6c, 0x0005013c, 0x0004b953, 0x00047573,
+ 0x00043563, 0x0003f8eb, 0x0003bfd7, 0x000389f8, 0x0003571f, 0x00032721, 0x0002f9d4, 0x0002cf11,
+ 0x0002a6b6, 0x0002809e, 0x00025ca9, 0x00023ab9, 0x00021ab1, 0x0001fc75, 0x0001dfeb, 0x0001c4fc,
+ 0x0001ab8f, 0x00019390, 0x00017cea, 0x00016788, 0x0001535b, 0x0001404f, 0x00012e54, 0x00011d5c,
+ 0x00010d58, 0x0000fe3a, 0x0000eff5, 0x0000e27e, 0x0000d5c7, 0x0000c9c8, 0x0000be75, 0x0000b3c4,
+ 0x0000a9ad, 0x0000a027, 0x0000972a, 0x00008eae, 0x000086ac, 0x00007f1d, 0x000077fa, 0x0000713f,
+ 0x00006ae3, 0x000064e4, 0x00005f3a, 0x000059e2, 0x000054d6, 0x00005013, 0x00004b95, 0x00004757,
+ 0x00004356, 0x00003f8e, 0x00003bfd, 0x0000389f, 0x00003571, 0x00003272, 0x00002f9d, 0x00002cf1,
+ 0x00002a6b, 0x00002809, 0x000025ca, 0x000023ab, 0x000021ab, 0x00001fc7, 0x00001dfe, 0x00001c4f,
+ 0x00001ab8, 0x00001939, 0x000017ce, 0x00001678, 0x00001535, 0x00001404, 0x000012e5, 0x000011d5,
+ 0x000010d5, 0x00000fe3, 0x00000eff, 0x00000e27, 0x00000d5c, 0x00000c9c, 0x00000be7, 0x00000b3c,
+ 0x00000a9a, 0x00000a02, 0x00000972, 0x000008ea, 0x0000086a, 0x000007f1, 0x0000077f, 0x00000713,
+ 0x000006ae, 0x0000064e, 0x000005f3, 0x0000059e, 0x0000054d, 0x00000501, 0x000004b9, 0x00000475,
+ 0x00000435, 0x000003f8, 0x000003bf, 0x00000389, 0x00000357, 0x00000327, 0x000002f9, 0x000002cf,
+ 0x000002a6, 0x00000280, 0x0000025c, 0x0000023a, 0x0000021a, 0x000001fc, 0x000001df, 0x000001c4,
+ 0x000001ab, 0x00000193, 0x0000017c, 0x00000167, 0x00000153, 0x00000140, 0x0000012e, 0x0000011d
+};
+
+SoundResourceINST::~SoundResourceINST() {
+ if (_samplesResource)
+ _samplesResource->close();
+
+ delete _transpose;
+ delete _levelAdjust;
+}
+
+void SoundResourceINST::loadPitchData(Common::ReadStream *stream, uint32 size) {
+ delete _transpose;
+ uint8 *data = new uint8[size];
+ stream->read(data, size);
+ _transpose = new EnvelopeData(data, size);
+}
+
+void SoundResourceINST::loadSamples(Common::ReadStream *stream, uint32 size) {
+ char *data = new char[size + 1];
+ stream->read(data, size);
+ data[size] = '\0';
+
+ if (_samplesResource)
+ _samplesResource->close();
+
+ SoundResource *instr = _res->getResource(data, SoundResource::kIdle);
+ if (instr) {
+ int type = instr->getType();
+ if (type != 4)
+ error("SoundResourceINST::loadInstrument(): Unexpected resource type");
+ instr->open();
+ _samplesResource = (SoundResource8SVX*)instr;
+ } else {
+ warning("SoundResourceINST::loadInstrument(): Samples resource '%s' not found for '%s'.", data, _name.c_str());
+ }
+
+ delete[] data;
+}
+
+void SoundResourceINST::loadVolumeData(Common::ReadStream *stream, uint32 size) {
+ delete _levelAdjust;
+ uint8 *data = new uint8[size];
+ stream->read(data, size);
+ _levelAdjust = new EnvelopeData(data, size);
+}
+
+void SoundResourceINST::setupMusicNote(AudioMaster2IOManager::IOUnit *unit, uint8 note, uint16 volume) {
+ assert(unit);
+ _samplesResource->setupMusicNote(unit, note, volume);
+ setupEnvelopes(unit);
+}
+
+void SoundResourceINST::release() {
+ delete this;
+}
+
+void SoundResourceINST::setupEnvelopes(AudioMaster2IOManager::IOUnit *unit) {
+ assert(unit);
+ if (_transpose) {
+ unit->_transposeData = _transpose->_data;
+ unit->_transposeCounter = 0;
+ unit->_transposePara = 0;
+ } else {
+ unit->_transposeData = 0;
+ }
+
+ if (_levelAdjust) {
+ unit->_levelAdjustData = _levelAdjust->_data;
+ unit->_levelAdjustCounter = 0;
+ unit->_levelAdjustPara = 0;
+ } else {
+ unit->_levelAdjustData = 0;
+ }
+}
+
+void SoundResourceINST::setupSoundEffect(AudioMaster2IOManager::IOUnit *unit, uint32 sync, uint32 rate) {
+ if (!unit)
+ return;
+
+ if (_samplesResource)
+ _samplesResource->setupSoundEffect(unit, sync, rate);
+
+ setupEnvelopes(unit);
+}
+
+SoundResourceSMUS::~SoundResourceSMUS() {
+ for (Common::Array<Track*>::iterator i = _tracks.begin(); i != _tracks.end(); ++i)
+ delete *i;
+ for (Common::Array<SoundResource*>::iterator i = _instruments.begin(); i != _instruments.end(); ++i)
+ (*i)->close();
+}
+
+void SoundResourceSMUS::loadHeader(Common::ReadStream *stream, uint32 size) {
+ if (size < 3)
+ error("SoundResourceSMUS:loadHeader(): Invalid data chunk size");
+
+ _tempo = stream->readUint16BE() / 68;
+ _vol = stream->readByte();
+}
+
+void SoundResourceSMUS::loadInstrument(Common::ReadStream *stream, uint32 size) {
+ stream->readUint32BE();
+ size -= 4;
+
+ char *data = new char[size + 1];
+ stream->read(data, size);
+ data[size] = '\0';
+
+ SoundResource *instr = _res->getResource(data, SoundResource::kIdle);
+ if (instr) {
+ int type = instr->getType();
+ if (type == 1)
+ error("SoundResourceSMUS::loadInstrument(): Unexpected resource type");
+ instr->open();
+ _instruments.push_back(instr);
+ } else {
+ warning("SoundResourceSMUS::loadInstrument(): Samples resource '%s' not found for '%s'.", data, _name.c_str());
+ }
+
+ delete[] data;
+}
+
+void SoundResourceSMUS::loadTrack(Common::ReadStream *stream, uint32 size) {
+ Track *track = new Track();
+ uint8 *data = new uint8[size];
+ stream->read(data, size);
+
+ track->_dataStart = data;
+ track->_dataEnd = data + size;
+ track->_volume = 128;
+
+ _tracks.push_back(track);
+}
+
+void SoundResourceSMUS::prepare() {
+ _playFlags = 0;
+ for (Common::Array<Track*>::iterator trk = _tracks.begin(); trk != _tracks.end(); ++trk) {
+ (*trk)->_dataCur = (*trk)->_dataStart;
+
+ for (Common::Array<SoundResource*>::iterator instr = _instruments.begin(); instr != _instruments.end(); ++instr) {
+ (*trk)->setInstrument(*instr);
+ break;
+ }
+
+ if (!(*trk)->_instrument)
+ error("SoundResourceSMUS::prepare():: Unable to assign default instrument to track (resource files loaded in the wrong order?)");
+
+ _playFlags = (_playFlags << 1) | 1;
+ }
+}
+
+uint16 SoundResourceSMUS::getTempo() const {
+ return _tempo;
+}
+
+void SoundResourceSMUS::setSync(uint32 sync) {
+ for (Common::Array<Track*>::iterator i = _tracks.begin(); i != _tracks.end(); ++i)
+ (*i)->_sync = sync;
+ _masterVolume = 0x40;
+}
+
+void SoundResourceSMUS::interrupt(AudioMaster2IOManager *io) {
+ for (uint32 i = 0; i < _tracks.size(); ++i) {
+ if (!parse(io, _tracks[i]))
+ _playFlags &= ~(1 << i);
+ }
+
+ if (!_playFlags)
+ setPlayStatus(false);
+}
+
+void SoundResourceSMUS::release() {
+ delete this;
+}
+
+bool SoundResourceSMUS::parse(AudioMaster2IOManager *io, Track *track) {
+ uint32 duration = 0;
+
+ while (track->_sync <= io->_sync) {
+ if (track->_dataCur >= track->_dataEnd)
+ return false;
+
+ uint8 cmd = *track->_dataCur++;
+ uint8 para = *track->_dataCur++;
+
+ if (cmd <= 0x80) {
+ if (para & 0x80)
+ continue;
+
+ duration += _durationTable[para & 0x3f];
+ if (para & 0x40)
+ continue;
+
+ if (cmd < 0x80) {
+ AudioMaster2IOManager::IOUnit *unit = io->requestFreeUnit();
+ if (unit) {
+ unit->_startTick = track->_sync;
+ unit->_endTick = unit->_startTick + duration;
+ track->_instrument->setupMusicNote(unit, cmd, _masterVolume * track->_volume);
+ }
+ }
+
+ track->_sync += duration;
+ duration = 0;
+
+ } else {
+ switch (cmd) {
+ case 0x81:
+ assert(para < _instruments.size());
+ track->setInstrument(_instruments[para]);
+ break;
+
+ case 0x84:
+ track->_volume = para;
+ break;
+
+ case 0x88:
+ //not implemented
+ break;
+
+ case 0xFF:
+ return false;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+const uint16 SoundResourceSMUS::_durationTable[64] = {
+ 0x6900, 0x3480, 0x1a40, 0x0d20, 0x0690, 0x0348, 0x01a4, 0x00d2,
+ 0x9d80, 0x4ec0, 0x2760, 0x13b0, 0x09d8, 0x04ec, 0x0276, 0x013b,
+ 0x4600, 0x2300, 0x1180, 0x08c0, 0x0460, 0x0230, 0x0118, 0x008c,
+ 0x6900, 0x3480, 0x1a40, 0x0d20, 0x0690, 0x0348, 0x01a4, 0x00d2,
+ 0x5400, 0x2a00, 0x1500, 0x0a80, 0x0540, 0x02a0, 0x0150, 0x00a8,
+ 0x7e00, 0x3f00, 0x1f80, 0x0fc0, 0x07e0, 0x03f0, 0x01f8, 0x00fc,
+ 0x5a00, 0x2d00, 0x1680, 0x0b40, 0x05a0, 0x02d0, 0x0168, 0x00b4,
+ 0x8700, 0x4380, 0x21c0, 0x10e0, 0x0870, 0x0438, 0x021c, 0x010e
+};
+
+AudioMaster2ResourceManager::~AudioMaster2ResourceManager() {
+ flush();
+}
+
+void AudioMaster2ResourceManager::loadResourceFile(Common::SeekableReadStream *data) {
+ do {
+ AudioMaster2IFFLoader loader(data, this);
+ Common::Functor1Mem<Common::IFFChunk&, bool, AudioMaster2IFFLoader> cb(&loader, &AudioMaster2IFFLoader::loadChunk);
+ loader.parse(cb);
+ } while (data->pos() < data->size());
+}
+
+void AudioMaster2ResourceManager::initResource(SoundResource *resource) {
+ if (!resource)
+ return;
+
+ SoundResource *res = retrieveFromChain(resource->getName());
+ // The driver does not replace resources with the same name, but disposes the new resource instead.
+ // So these names seem to be considered "globally unique".
+ if (res)
+ resource->close();
+ else
+ res = resource;
+
+ linkToChain(res, SoundResource::kIdle);
+}
+
+void AudioMaster2ResourceManager::releaseResource(const Common::String &resName) {
+ stopChain();
+
+ SoundResource *res = retrieveFromChain(resName);
+ if (!res)
+ return;
+
+ res->setPlayStatus(false);
+ res->close();
+}
+
+void AudioMaster2ResourceManager::flush() {
+ stopChain();
+
+ Common::StackLock lock(*_mutex);
+
+ for (SoundResource *res = _chainPlaying; _chainPlaying; res = _chainPlaying) {
+ _chainPlaying = res->_next;
+ res->_next = 0;
+ res->setPlayStatus(false);
+ res->close();
+ }
+
+ for (SoundResource *res = _chainInactive; _chainInactive; res = _chainInactive) {
+ _chainInactive = res->_next;
+ res->_next = 0;
+ res->setPlayStatus(false);
+ res->close();
+ }
+}
+
+SoundResource *AudioMaster2ResourceManager::getResource(const Common::String &resName, SoundResource::Mode mode) {
+ if (resName.empty())
+ return 0;
+
+ SoundResource *res = retrieveFromChain(resName);
+ if (!res)
+ return 0;
+
+ linkToChain(res, mode);
+
+ return res;
+}
+
+void AudioMaster2ResourceManager::interrupt(AudioMaster2IOManager *io) {
+ SoundResource *cur = _chainPlaying;
+ SoundResource *prev = 0;
+
+ while (cur) {
+ cur->interrupt(io);
+
+ if (cur->getPlayStatus()) {
+ prev = cur;
+ cur = cur->_next;
+ } else if (prev) {
+ prev->_next = cur->_next;
+ cur->_next = _chainInactive;
+ _chainInactive = cur;
+ cur = prev->_next;
+ } else {
+ _chainPlaying = cur->_next;
+ cur->_next = _chainInactive;
+ _chainInactive = cur;
+ cur = _chainPlaying;
+ }
+ }
+}
+
+SoundResource *AudioMaster2ResourceManager::retrieveFromChain(const Common::String &resName) {
+ if (resName.empty())
+ return 0;
+
+ // The requesting (SMUS or INST) resources use shorter (cut off) versions of the actual name strings stored in the requested
+ // (INST or 8SVX) resources. E. g. the EOB intro song will request (among other things) a 'flute' and a 'vibe', although
+ // the actual resource names are 'flute3c' and 'vibe3c'. I would have liked to have all these strings hashed into uint32 IDs,
+ // but this "feature" spoils that idea.
+
+ const char *srchStr = resName.c_str();
+ uint32 srchDepth = strlen(srchStr);
+
+ SoundResource *cur = _chainPlaying;
+ SoundResource *prev = 0;
+
+ Common::StackLock lock(*_mutex);
+
+ while (cur) {
+ if (!scumm_strnicmp(cur->getName().c_str(), srchStr, srchDepth)) {
+ if (prev)
+ prev->_next = cur->_next;
+ else
+ _chainPlaying = cur->_next;
+ cur->_next = 0;
+ return cur;
+ }
+ prev = cur;
+ cur = cur->_next;
+ }
+
+ cur = _chainInactive;
+ prev = 0;
+
+ while (cur) {
+ if (!scumm_strnicmp(cur->getName().c_str(), srchStr, srchDepth)) {
+ if (prev)
+ prev->_next = cur->_next;
+ else
+ _chainInactive = cur->_next;
+ cur->_next = 0;
+ return cur;
+ }
+ prev = cur;
+ cur = cur->_next;
+ }
+
+ return 0;
+}
+
+
+void AudioMaster2ResourceManager::linkToChain(SoundResource *resource, SoundResource::Mode mode) {
+ if (resource->getType() == 1) {
+ stopChain();
+ resource->prepare();
+ }
+
+ Common::StackLock lock(*_mutex);
+
+ if (mode == SoundResource::kRestart) {
+ resource->setPlayStatus(true);
+ resource->_next = _chainPlaying;
+ _chainPlaying = resource;
+
+ if (resource->getType() == 1)
+ _driver->sync(resource);
+
+ } else {
+ resource->_next = _chainInactive;
+ _chainInactive = resource;
+ }
+}
+
+void AudioMaster2ResourceManager::stopChain() {
+ Common::StackLock lock(*_mutex);
+
+ SoundResource *cur = _chainPlaying;
+ while (cur) {
+ cur->setPlayStatus(false);
+ cur = cur->_next;
+ }
+
+ _driver->stopChannels();
+}
+
+AudioMaster2IFFLoader::~AudioMaster2IFFLoader() {
+ initResource();
+}
+
+bool AudioMaster2IFFLoader::loadChunk(Common::IFFChunk &chunk) {
+ if (_formType == ID_INST) {
+ if (_curIns == 0)
+ _curIns = new SoundResourceINST(_res);
+
+ switch (chunk._type) {
+ case ID_NAME:
+ _curIns->loadName(chunk._stream, chunk._size);
+ break;
+ case ID_SAMP:
+ _curIns->loadSamples(chunk._stream, chunk._size);
+ break;
+ case ID_VLUM:
+ _curIns->loadVolumeData(chunk._stream, chunk._size);
+ break;
+ case ID_PTCH:
+ _curIns->loadPitchData(chunk._stream, chunk._size);
+ break;
+ default:
+ break;
+ }
+
+ } else if (_formType == ID_SMUS) {
+ if (_curSong == 0)
+ _curSong = new SoundResourceSMUS(_res);
+
+ switch (chunk._type) {
+ case ID_SHDR:
+ _curSong->loadHeader(chunk._stream, chunk._size);
+ break;
+ case ID_NAME:
+ _curSong->loadName(chunk._stream, chunk._size);
+ break;
+ case ID_TRAK:
+ _curSong->loadTrack(chunk._stream, chunk._size);
+ break;
+ case ID_INS1:
+ _curSong->loadInstrument(chunk._stream, chunk._size);
+ break;
+ default:
+ break;
+ }
+
+ } else if (_formType == ID_8SVX) {
+ if (_curSfx == 0)
+ _curSfx = new SoundResource8SVX(_res);
+
+ switch (chunk._type) {
+ case ID_VHDR:
+ _curSfx->loadHeader(chunk._stream, chunk._size);
+ break;
+ case ID_NAME:
+ _curSfx->loadName(chunk._stream, chunk._size);
+ break;
+ case ID_BODY:
+ _curSfx->loadData(chunk._stream, chunk._size);
+ break;
+ case ID_ANNO:
+ break;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void AudioMaster2IFFLoader::initResource() {
+ if (_curIns) {
+ _res->initResource(_curIns);
+ _curIns = 0;
+ } else if (_curSong) {
+ _res->initResource(_curSong);
+ _curSong = 0;
+ } else if (_curSfx) {
+ _res->initResource(_curSfx);
+ _curSfx = 0;
+ }
+}
+
+AudioMaster2Internal *AudioMaster2Internal::_refInstance = 0;
+int AudioMaster2Internal::_refCount = 0;
+
+AudioMaster2Internal::AudioMaster2Internal(Audio::Mixer *mixer) : Paula(true, mixer->getOutputRate(), mixer->getOutputRate() / 50), _mixer(mixer), _res(0), _ready(false) {
+ _channels[0] = _channels[1] = _channels[2] = _channels[3] = 0;
+}
+
+AudioMaster2Internal::~AudioMaster2Internal() {
+ Common::StackLock lock(_mutex);
+
+ stopPaula();
+
+ _mixer->stopHandle(_soundHandle);
+
+ delete _res;
+ delete _io;
+}
+
+AudioMaster2Internal *AudioMaster2Internal::open(Audio::Mixer *mixer) {
+ _refCount++;
+
+ if (_refCount == 1 && _refInstance == 0)
+ _refInstance = new AudioMaster2Internal(mixer);
+ else if (_refCount < 2 || _refInstance == 0)
+ error("AudioMaster2Internal::open(): Internal instance management failure");
+
+ return _refInstance;
+}
+
+void AudioMaster2Internal::close() {
+ if (!_refCount)
+ return;
+
+ _refCount--;
+
+ if (!_refCount) {
+ delete _refInstance;
+ _refInstance = 0;
+ }
+}
+
+bool AudioMaster2Internal::init() {
+ if (_ready)
+ return true;
+
+ _io = new AudioMaster2IOManager();
+ _res = new AudioMaster2ResourceManager(this, &_mutex);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType,
+ &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ startPaula();
+
+ _ready = true;
+
+ return true;
+}
+
+bool AudioMaster2Internal::loadRessourceFile(Common::SeekableReadStream *data) {
+ if (!_ready || !data)
+ return false;
+
+ _res->loadResourceFile(data);
+
+ return true;
+}
+
+bool AudioMaster2Internal::startSound(const Common::String &name) {
+ return _ready ? (_res->getResource(name, SoundResource::kRestart) != 0) : false;
+}
+
+bool AudioMaster2Internal::stopSound(const Common::String &name) {
+ return _ready ? (_res->getResource(name, SoundResource::kIdle) != 0) : false;
+}
+
+void AudioMaster2Internal::flushResource(const Common::String &name) {
+ if (_ready)
+ _res->releaseResource(name);
+}
+
+void AudioMaster2Internal::flushAllResources() {
+ if (_ready)
+ _res->flush();
+}
+
+void AudioMaster2Internal::interrupt() {
+ if (!_ready)
+ return;
+
+ _io->_sync += _io->_tempo;
+ _res->interrupt(_io);
+ _io->deployChannels(_channels);
+ updateDevice();
+}
+
+void AudioMaster2Internal::sync(SoundResource *res) {
+ if (!_ready || !res)
+ return;
+
+ if (res->getType() != 1)
+ return;
+
+ SoundResourceSMUS *smus = static_cast<SoundResourceSMUS*>(res);
+
+ Common::StackLock lock(_mutex);
+ _io->_tempo = smus->getTempo();
+ smus->setSync(_io->_sync);
+}
+
+void AudioMaster2Internal::stopChannels() {
+ if (!_ready)
+ return;
+
+ Common::StackLock lock(_mutex);
+
+ for (uint8 i = 0; i < 4; ++i) {
+ if (_channels[i]) {
+ _channels[i]->_endTick = 0;
+ disableChannel(i);
+ }
+ }
+
+ _io->clearChain();
+}
+
+void AudioMaster2Internal::updateDevice() {
+ for (uint8 i = 3; i < 4; --i) {
+ AudioMaster2IOManager::IOUnit *unit = _channels[i];
+ if (!unit)
+ continue;
+
+ if (_io->_sync > unit->_endTick) {
+ _channels[i] = 0;
+ unit->_flags &= ~2;
+ disableChannel(i);
+ continue;
+ }
+
+ bool next = false;
+
+ if (unit->_transposeData) {
+ unit->_period += unit->_transposePara;
+ const uint8 *data = unit->_transposeData;
+
+ if (unit->_transposeCounter-- <= 1) {
+ for (bool loop = true; loop; ) {
+ uint8 para = *data++;
+
+ if (para == 0xFF) {
+ para = *data++;
+
+ if (para == 0) {
+ unit->_flags &= ~2;
+ disableChannel(i);
+ loop = false;
+ next = true;
+ continue;
+
+ } else if (para == 1) {
+ unit->_transposeData = 0;
+ loop = false;
+
+ } else {
+ unit->_period = READ_BE_UINT16(data);
+ data += 2;
+ }
+
+ } else if (para == 0xFE) {
+ para = *data++;
+ data -= ((para + 1) << 1);
+
+ } else {
+ unit->_transposeCounter = para;
+ unit->_transposePara = *data++;
+ unit->_transposeData = data;
+ loop = false;
+ }
+ }
+ }
+ }
+
+ if (next)
+ continue;
+
+ next = false;
+
+ if (unit->_levelAdjustData) {
+ unit->_outputVolume += unit->_levelAdjustPara;
+ const uint8 *data = unit->_levelAdjustData;
+
+ if (unit->_levelAdjustCounter-- <= 1) {
+ for (bool loop = true; loop; ) {
+ uint8 para = *data++;
+ if (para == 0xFF) {
+ para = *data++;
+
+ if (para == 0) {
+ unit->_flags &= ~2;
+ disableChannel(i);
+ loop = false;
+ next = true;
+ continue;
+
+ } else {
+ unit->_levelAdjustData = 0;
+ loop = false;
+ }
+
+ } else if (para == 0xFE) {
+ para = *data++;
+ data -= ((para + 1) << 1);
+
+ } else {
+ uint16 para2 = *data++;
+
+ if (para2 & 0x80) {
+ para2 = unit->_outputVolume + ((para2 - 0xC0) << 8);
+
+ } else {
+ para2 = (unit->_volumeSetting * para2) >> 6;
+ if (para2 > 0x4000)
+ para2 = 0x4000;
+ }
+
+ if (!para) {
+ unit->_outputVolume = para2;
+ continue;
+ }
+
+ unit->_levelAdjustCounter = para;
+
+ if (para == 1) {
+ unit->_outputVolume = para2;
+ unit->_levelAdjustPara = 0;
+ } else {
+ int16 va = para2 - unit->_outputVolume;
+ va /= para;
+ unit->_levelAdjustPara = va / para;
+ }
+
+ unit->_levelAdjustData = data;
+ loop = false;
+ }
+ }
+ }
+ }
+
+ if (next)
+ continue;
+
+ if (unit->_flags & 4) {
+ unit->_flags &= ~4;
+
+ setChannelPeriod(i, unit->_period);
+ setChannelVolume(i, unit->_outputVolume >> 8);
+
+ if (unit->_lenOnce) {
+ setChannelData(i, unit->_sampleData, unit->_sampleDataRepeat, unit->_lenOnce, unit->_lenRepeat);
+ } else if (unit->_lenRepeat) {
+ setChannelSampleStart(i, unit->_sampleDataRepeat);
+ setChannelSampleLen(i, unit->_lenRepeat);
+ }
+
+ } else if (unit->_transposeData || unit->_levelAdjustData) {
+ setChannelPeriod(i, unit->_period);
+ setChannelVolume(i, unit->_outputVolume >> 8);
+ }
+ }
+}
+
+AudioMaster2::AudioMaster2(Audio::Mixer *mixer) {
+ _am2i = AudioMaster2Internal::open(mixer);
+}
+
+AudioMaster2::~AudioMaster2() {
+ AudioMaster2Internal::close();
+ _am2i = 0;
+}
+
+bool AudioMaster2::init() {
+ return _am2i->init();
+}
+
+bool AudioMaster2::loadRessourceFile(Common::SeekableReadStream *data) {
+ return _am2i->loadRessourceFile(data);
+}
+
+bool AudioMaster2::startSound(const Common::String &name) {
+ return _am2i->startSound(name);
+}
+
+bool AudioMaster2::stopSound(const Common::String &name) {
+ return _am2i->stopSound(name);
+}
+
+void AudioMaster2::flushResource(const Common::String &name) {
+ _am2i->flushResource(name);
+}
+
+void AudioMaster2::flushAllResources() {
+ _am2i->flushAllResources();
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/drivers/audiomaster2.h b/engines/kyra/sound/drivers/audiomaster2.h
new file mode 100644
index 0000000000..d20048cc1f
--- /dev/null
+++ b/engines/kyra/sound/drivers/audiomaster2.h
@@ -0,0 +1,78 @@
+/* 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_AUDIOMASTER2_H
+#define KYRA_SOUND_AUDIOMASTER2_H
+
+namespace Audio {
+ class Mixer;
+}
+
+namespace Common {
+ class SeekableReadStream;
+ class String;
+}
+
+namespace Kyra {
+
+class AudioMaster2Internal;
+
+class AudioMaster2 {
+public:
+ AudioMaster2(Audio::Mixer *mixer);
+ ~AudioMaster2();
+
+ bool init();
+ bool loadRessourceFile(Common::SeekableReadStream *data);
+
+ bool startSound(const Common::String &name);
+ bool stopSound(const Common::String &name);
+
+ void flushResource(const Common::String &name);
+ void flushAllResources();
+
+ void setMusicVolume(int volume) {}
+ void setSoundEffectVolume(int volume) {}
+
+private:
+ AudioMaster2Internal *_am2i;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sound/sound.h b/engines/kyra/sound/sound.h
index 3f4216c5fb..ab0e6a16d1 100644
--- a/engines/kyra/sound/sound.h
+++ b/engines/kyra/sound/sound.h
@@ -73,6 +73,15 @@ struct SoundResourceInfo_TownsEoB {
int pcmVolume;
};
+
+struct SoundResourceInfo_AmigaEoB {
+ SoundResourceInfo_AmigaEoB(const char *const *files, int numFiles, const char *const *sounds, int numSounds) : fileList(files), fileListSize(numFiles), soundList(sounds), soundListSize(numSounds) {}
+ const char *const *fileList;
+ uint fileListSize;
+ const char *const *soundList;
+ uint soundListSize;
+};
+
/**
* Analog audio output device API for Kyrandia games.
* It contains functionality to play music tracks,
diff --git a/engines/kyra/sound/sound_amiga_eob.cpp b/engines/kyra/sound/sound_amiga_eob.cpp
new file mode 100644
index 0000000000..781ec46f4d
--- /dev/null
+++ b/engines/kyra/sound/sound_amiga_eob.cpp
@@ -0,0 +1,311 @@
+/* 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 "kyra/sound/drivers/audiomaster2.h"
+
+#include "common/memstream.h"
+
+namespace Kyra {
+
+SoundAmiga_EoB::SoundAmiga_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer), _vm(vm), _driver(0), _currentResourceSet(0), _ready(false) {
+ _fileBuffer = new uint8[64000];
+ _version2 = _vm->game() == GI_EOB2;
+ memset(_resInfo, 0, sizeof(_resInfo));
+}
+
+SoundAmiga_EoB::~SoundAmiga_EoB() {
+ delete _driver;
+ delete[] _fileBuffer;
+ for (int i = 0; i < 3; i++)
+ initAudioResourceInfo(i, 0);
+}
+
+Sound::kType SoundAmiga_EoB::getMusicType() const {
+ return kAmiga;
+}
+
+bool SoundAmiga_EoB::init() {
+ _driver = new AudioMaster2(_mixer);
+ if (!_driver->init())
+ return false;
+
+ _ready = true;
+ return true;
+}
+
+void SoundAmiga_EoB::initAudioResourceInfo(int set, void *info) {
+ delete _resInfo[set];
+ _resInfo[set] = info ? new SoundResourceInfo_AmigaEoB(*(SoundResourceInfo_AmigaEoB*)info) : 0;
+}
+
+void SoundAmiga_EoB::selectAudioResourceSet(int set) {
+ _driver->flushAllResources();
+ if (!_resInfo[set])
+ return;
+
+ for (uint i = 0; i < _resInfo[set]->fileListSize; ++i)
+ loadSoundFile(_resInfo[set]->fileList[i]);
+
+ _currentResourceSet = set;
+}
+
+bool SoundAmiga_EoB::hasSoundFile(uint file) const {
+ return false;
+}
+
+void SoundAmiga_EoB::loadSoundFile(uint file) {
+ /*
+ _sound->loadSoundFile("INTRO1.CPS");
+ _sound->loadSoundFile("INTRO2.CPS");
+ _sound->loadSoundFile("INTRO4.CPS");
+ _sound->loadSoundFile("INTRO5.CPS");
+ _sound->loadSoundFile("NEWINTRO1.CPS");
+ _sound->loadSoundFile("CHARGEN1.CPS");
+
+ _sound->loadSoundFile("SFX1.CPS");
+ _sound->loadSoundFile("SFX2.CPS");
+ _sound->loadSoundFile("SFX3.CPS");
+ _sound->loadSoundFile("SFX4.CPS");
+
+ _sound->loadSoundFile("HUM1.CPS");
+
+ _sound->loadSoundFile("SPIDERMOV1.CPS");
+ _sound->loadSoundFile("MOVE1.CPS");
+ _sound->loadSoundFile("MOVE21.CPS");
+ _sound->loadSoundFile("MOVE31.CPS");
+ _sound->loadSoundFile("BEASTATK1.CPS");
+ _sound->loadSoundFile("BLADE1.CPS");
+ _sound->loadSoundFile("MANTISMOV1.CPS");
+ _sound->loadSoundFile("FLAYERATK1.CPS");
+ _sound->loadSoundFile("LEECHMOV1.CPS");
+ _sound->loadSoundFile("SLOSHSUCK1.CPS");
+ _sound->loadSoundFile("SCREAM1.CPS");
+ _sound->loadSoundFile("RUSTATK1.CPS");
+ _sound->loadSoundFile("HOUNDATK1.CPS");
+ _sound->loadSoundFile("KUOTOAMOV1.CPS");
+
+ _sound->loadSoundFile("FINALE2.CPS");
+ _sound->loadSoundFile("FINALE1.CPS");
+ _sound->loadSoundFile("FINALE.CPS");
+
+ for (int i = 1; i < 12; ++i) {
+ char n[13];
+ sprintf(n, "LEVELSAM%d.CPS", i);
+ _sound->loadSoundFile(n);
+ }*/
+}
+
+void SoundAmiga_EoB::loadSoundFile(Common::String file) {
+ if (!_ready)
+ return;
+
+ Common::SeekableReadStream *in = _vm->resource()->createReadStream(file);
+ if (!in)
+ return;
+
+ uint16 readSize = in->readUint16LE() - 10;
+ uint8 cmp = in->readByte();
+ in->seek(1, SEEK_CUR);
+ uint32 outSize = in->readUint32LE();
+ in->seek(2, SEEK_CUR);
+
+ if (in->read(_fileBuffer, readSize) != readSize)
+ error("SoundAmiga_EoB::loadSoundFile(): Failed to load sound file '%s'", file.c_str());
+ delete in;
+
+ uint8 *buf = new uint8[outSize];
+
+ if (cmp == 0) {
+ assert(readSize == outSize);
+ memcpy(buf, _fileBuffer, outSize);
+ } else if (cmp == 3) {
+ Screen::decodeFrame3(_fileBuffer, buf, outSize);
+ } else if (cmp == 4) {
+ Screen::decodeFrame4(_fileBuffer, buf, outSize);
+ } else {
+ error("SoundAmiga_EoB::loadSoundFile(): Failed to load sound file '%s'", file.c_str());
+ }
+
+ Common::MemoryReadStream soundFile(buf, outSize);
+ if (!_driver->loadRessourceFile(&soundFile))
+ error("SoundAmiga_EoB::loadSoundFile(): Failed to load sound file '%s'", file.c_str());
+
+ delete[] buf;
+}
+
+void SoundAmiga_EoB::playTrack(uint8 track) {
+ _driver->startSound("newintro1.smus");
+ //_driver->startSound("hum1.sam");
+ //_driver->startSound("hum");
+ //_driver->startSound("playswing");
+ //_driver->startSound("door");
+ //_driver->startSound("death");
+ //_driver->startSound("teleport");
+ //_driver->startSound("scream");
+ //_driver->startSound("magica");
+ //_driver->startSound("magicb");
+
+ //_driver->startSound("chargen1.smus");
+ //_driver->startSound("finale.smus");
+}
+
+void SoundAmiga_EoB::haltTrack() {
+
+}
+
+void SoundAmiga_EoB::playSoundEffect(uint8 track, uint8 volume) {
+ if (!_resInfo[_currentResourceSet]->soundList || track >= 120 || !_sfxEnabled)
+ return;
+
+ _driver->startSound(_resInfo[_currentResourceSet]->soundList[track]);
+
+ static const char *const kEoB1SoundsAmiga[120] = {
+ 0,
+ "button",
+ "L1M1A",
+ "door",
+ "door",
+ "slam",
+ "button",
+ "button",
+ "transmute",
+ "eat",
+ "magica",
+ "throw",
+ "plate",
+ "passage",
+ "unlock",
+ "teleport",
+ "undead",
+ "pit",
+ "itemland",
+ 0,
+ 0,
+ "playhit",
+ "death",
+ "text",
+ "electric",
+ "dart",
+ "dart",
+ "unlock",
+ "bonus",
+ "bump",
+ 0,
+ "electric",
+ "playswing",
+ "hum",
+ "panel",
+ "explode",
+ "L10M2M",
+ "L10M2A",
+ "L4M1M",
+ "beastatk",
+ "L9M2M",
+ "L8M1A",
+ "L8M1M",
+ "L7M1A",
+ "L7M1M",
+ "L5M1A",
+ "L5M1M",
+ "flindatk",
+ "L3M2M",
+ "L4M1A",
+ "L8M2M",
+ "houndatk",
+ "scream",
+ "L6M1M",
+ "L3M1A",
+ "L3M1M",
+ "sloshsuck",
+ "L1M2M",
+ "flayeratk",
+ 0,
+ "rustatk",
+ "L9M1M",
+ "L10M1A",
+ "L10M1M",
+ "blade",
+ "L7M2M",
+ "blade",
+ "L2M2M",
+ "L12M2A",
+ "L12M2M",
+ 0,
+ "L11M1M",
+ "L11M1A",
+ "L2M1A",
+ "L2M1M",
+ "L1M1M",
+ "button",
+ 0,
+ "drop",
+ "text",
+ "magicb",
+ "lock",
+ 0,
+ 0,
+ 0,
+ "Missile",
+ 0,
+ "burnhands",
+ "electric",
+ "fireball",
+ 0,
+ "magica",
+ "magica",
+ "magica",
+ "magicb",
+ "magicb",
+ "acid",
+ "magicb",
+ "fireball",
+ "acid",
+ "magica",
+ "magicb",
+ "magicb",
+ "undead",
+ "magica",
+ "magica",
+ "magica",
+ "magicb",
+ "cause",
+ "magicb",
+ "magicb",
+ "magica",
+ "magicb",
+ "magica",
+ "magica",
+ "magica",
+ "magica",
+ "cause",
+ 0,
+ "door"
+ };
+}
+
+void SoundAmiga_EoB::beginFadeOut() {
+
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index 77436d08ee..fccdc03eaf 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -402,6 +402,37 @@ private:
TownsAudioInterface *_intf;
};
+class AudioMaster2;
+class SoundAmiga_EoB: public Sound {
+public:
+ SoundAmiga_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+ virtual ~SoundAmiga_EoB();
+
+ kType getMusicType() const;
+
+ bool init();
+ void initAudioResourceInfo(int set, void *info);
+ void selectAudioResourceSet(int set);
+ bool hasSoundFile(uint file) const;
+ void loadSoundFile(uint file);
+ void loadSoundFile(Common::String file);
+ void playTrack(uint8 track);
+ void haltTrack();
+ void playSoundEffect(uint8 track, uint8 volume = 0xFF);
+ void beginFadeOut();
+
+private:
+ uint8 *_fileBuffer;
+
+ KyraEngine_v1 *_vm;
+ AudioMaster2 *_driver;
+ SoundResourceInfo_AmigaEoB *_resInfo[3];
+ int _currentResourceSet;
+
+ bool _version2;
+ bool _ready;
+};
+
} // End of namespace Kyra
#endif