From 462fea2ccaa894d5b19372ddccc606d25333127a Mon Sep 17 00:00:00 2001 From: athrxx Date: Sun, 14 Apr 2019 16:02:11 +0200 Subject: 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. --- engines/kyra/module.mk | 6 +- engines/kyra/sound/sound_amiga.cpp | 232 ---------- engines/kyra/sound/sound_amiga_lok.cpp | 232 ++++++++++ engines/kyra/sound/sound_digital.cpp | 544 ----------------------- engines/kyra/sound/sound_digital.h | 119 ----- engines/kyra/sound/sound_digital_mr.cpp | 544 +++++++++++++++++++++++ engines/kyra/sound/sound_digital_mr.h | 119 +++++ engines/kyra/sound/sound_towns.cpp | 749 -------------------------------- engines/kyra/sound/sound_towns_lok.cpp | 749 ++++++++++++++++++++++++++++++++ 9 files changed, 1647 insertions(+), 1647 deletions(-) delete mode 100644 engines/kyra/sound/sound_amiga.cpp create mode 100644 engines/kyra/sound/sound_amiga_lok.cpp delete mode 100644 engines/kyra/sound/sound_digital.cpp delete mode 100644 engines/kyra/sound/sound_digital.h create mode 100644 engines/kyra/sound/sound_digital_mr.cpp create mode 100644 engines/kyra/sound/sound_digital_mr.h delete mode 100644 engines/kyra/sound/sound_towns.cpp create mode 100644 engines/kyra/sound/sound_towns_lok.cpp diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk index 9c20ad6569..98494bc19f 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 110400415f..0000000000 --- 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 0000000000..110400415f --- /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 e3586605e8..0000000000 --- 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(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 271dde6a21..0000000000 --- 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 0000000000..e3586605e8 --- /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(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 0000000000..271dde6a21 --- /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 621c2f1419..0000000000 --- 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 0000000000..621c2f1419 --- /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 -- cgit v1.2.3