/* 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. * * $URL$ * $Id$ * */ #include "common/system.h" #include "common/mutex.h" #include "kyra/resource.h" #include "kyra/sound_intern.h" #include "sound/mixer.h" #include "sound/mods/maxtrax.h" #include "sound/audiostream.h" namespace { FORCEINLINE uint8 sfxTableGetNote(const byte* address) { return (uint8)address[0]; } FORCEINLINE uint8 sfxTableGetPatch(const byte* address) { return (uint8)address[1]; } FORCEINLINE uint16 sfxTableGetDuration(const byte* address) { return READ_BE_UINT16(&address[4]); } FORCEINLINE int8 sfxTableGetVolume(const byte* address) { return (int8)address[6]; } FORCEINLINE int8 sfxTableGetPan(const byte* address) { return (int8)address[7]; } } // end of namespace namespace Kyra { SoundAmiga::SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer), _driver(0), _musicHandle(), _fileLoaded(kFileNone), _tableSfxIntro(), _tableSfxGame() { } SoundAmiga::~SoundAmiga() { _mixer->stopHandle(_musicHandle); delete _driver; } extern const byte LoKAmigaSfxIntro[]; extern const byte LoKAmigaSfxGame[]; bool SoundAmiga::init() { _driver = new Audio::MaxTrax(_mixer->getOutputRate(), true); _tableSfxIntro = LoKAmigaSfxIntro; _tableSfxGame = LoKAmigaSfxGame; return _driver != 0 && _tableSfxIntro && _tableSfxGame; } 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->playInputStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, false); } } 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) { debugC(5, kDebugLevelSound, "SoundAmiga::playSoundEffect(%d)", track); const byte* tableEntry = 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 < 40) { tableEntry = &_tableSfxIntro[track * 8]; pan = (sfxTableGetPan(tableEntry) != 0); } break; case kFileGame: if (0x61 <= track && track <= 0x63) playTrack(track - 0x4F); assert(track < 120); if (sfxTableGetNote(&_tableSfxGame[track * 8])) { tableEntry = &_tableSfxGame[track * 8]; pan = (sfxTableGetPan(tableEntry) != 0) && (sfxTableGetPan(tableEntry) != 2); } break; default: ; } if (_sfxEnabled && tableEntry) { const bool success = _driver->playNote(sfxTableGetNote(tableEntry), sfxTableGetPatch(tableEntry), sfxTableGetDuration(tableEntry), sfxTableGetVolume(tableEntry), pan); if (success && !_mixer->isSoundHandleActive(_musicHandle)) _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, false); } } } // end of namespace Kyra