/engines/fullpipe/sound.cpp
C++ | 535 lines | 377 code | 136 blank | 22 comment | 93 complexity | c7944866891d2393acf5792860c3c0be MD5 | raw file
- /* 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 "fullpipe/fullpipe.h"
- #include "fullpipe/objects.h"
- #include "fullpipe/scene.h"
- #include "fullpipe/sound.h"
- #include "fullpipe/ngiarchive.h"
- #include "fullpipe/messages.h"
- #include "fullpipe/statics.h"
- #include "common/config-manager.h"
- #include "common/memstream.h"
- #include "audio/mixer.h"
- #include "audio/audiostream.h"
- #include "audio/decoders/vorbis.h"
- #include "audio/decoders/wave.h"
- namespace Fullpipe {
- bool SoundList::load(MfcArchive &file, const Common::String &fname) {
- debugC(5, kDebugLoading, "SoundList::load()");
- uint32 count = file.readUint32LE();
- _soundItems.resize(count);
- if (!fname.empty()) {
- _libHandle.reset(makeNGIArchive(fname));
- } else {
- _libHandle.reset();
- }
- for (uint i = 0; i < count; i++) {
- _soundItems[i].load(file, _libHandle.get());
- }
- return true;
- }
- bool SoundList::loadFile(const Common::String &fname, const Common::String &libname) {
- Common::File file;
- if (!file.open(fname))
- return false;
- MfcArchive archive(&file);
- return load(archive, libname);
- }
- Sound *SoundList::getSoundItemById(int id) {
- for (uint i = 0; i < _soundItems.size(); ++i) {
- if (_soundItems[i].getId() == id)
- return &_soundItems[i];
- }
- return nullptr;
- }
- Sound::Sound() :
- _id(0),
- _directSoundBuffer(0),
- _directSoundBuffers(),
- _soundData(nullptr),
- _handle(new Audio::SoundHandle()),
- _volume(100),
- _objectId(0) {}
- Sound::~Sound() {
- freeSound();
- delete _handle;
- }
- bool Sound::load(MfcArchive &file, NGIArchive *archive) {
- debugC(5, kDebugLoading, "Sound::load()");
- MemoryObject::load(file);
- _id = file.readUint32LE();
- /*_description = */file.readPascalString();
- assert(g_fp->_gameProjectVersion >= 6);
- _objectId = file.readUint16LE();
- if (archive && archive->hasFile(_memfilename)) {
- Common::SeekableReadStream *s = archive->createReadStreamForMember(_memfilename);
- _soundData = (byte *)calloc(s->size(), 1);
- s->read(_soundData, s->size());
- delete s;
- }
- return true;
- }
- void Sound::updateVolume() {
- // not needed in our implementation
- }
- void Sound::setPanAndVolumeByStaticAni() {
- if (!_objectId)
- return;
- StaticANIObject *ani = g_fp->_currentScene->getStaticANIObject1ById(_objectId, -1);
- if (!ani)
- return;
- int a, b;
- if (ani->_ox >= g_fp->_sceneRect.left) {
- int par, pan;
- if (ani->_ox <= g_fp->_sceneRect.right) {
- int dx;
- if (ani->_oy <= g_fp->_sceneRect.bottom) {
- if (ani->_oy >= g_fp->_sceneRect.top) {
- setPanAndVolume(g_fp->_sfxVolume, 0);
- return;
- }
- dx = g_fp->_sceneRect.top - ani->_oy;
- } else {
- dx = ani->_oy - g_fp->_sceneRect.bottom;
- }
- par = 0;
- if (dx > 800) {
- setPanAndVolume(-3500, 0);
- return;
- }
- pan = -3500;
- a = g_fp->_sfxVolume - (-3500);
- b = 800 - dx;
- } else {
- int dx = ani->_ox - g_fp->_sceneRect.right;
- if (dx > 800) {
- setPanAndVolume(-3500, 0);
- return;
- }
- pan = -3500;
- par = dx * (-3500) / -800;
- a = g_fp->_sfxVolume - (-3500);
- b = 800 - dx;
- }
- int32 pp = b * a;
- setPanAndVolume(pan + pp / 800, par);
- return;
- }
- int dx = g_fp->_sceneRect.left - ani->_ox;
- if (dx <= 800) {
- int32 s = (800 - dx) * (g_fp->_sfxVolume - (-3500));
- int32 p = -3500 + s / 800;
- if (p > g_fp->_sfxVolume)
- p = g_fp->_sfxVolume;
- setPanAndVolume(p, dx * (-3500) / 800);
- } else {
- setPanAndVolume(-3500, 0);
- }
- }
- void Sound::setPanAndVolume(int vol, int pan) {
- g_fp->_mixer->setChannelVolume(*_handle, MIN((vol + 10000) / 39, 255)); // -10000..0
- g_fp->_mixer->setChannelBalance(*_handle, CLIP(pan / 78, -127, 127)); // -10000..10000
- }
- void Sound::play(int flag) {
- Audio::SoundHandle *handle = getHandle();
- if (g_fp->_mixer->isSoundHandleActive(*handle)) { // We need to restart the music
- g_fp->_mixer->stopHandle(*handle);
- }
- byte *soundData = loadData();
- Common::MemoryReadStream *dataStream = new Common::MemoryReadStream(soundData, getDataSize());
- Audio::RewindableAudioStream *wav = Audio::makeWAVStream(dataStream, DisposeAfterUse::YES);
- Audio::AudioStream *audioStream = new Audio::LoopingAudioStream(wav, (flag == 1) ? 0 : 1);
- g_fp->_mixer->playStream(Audio::Mixer::kSFXSoundType, handle, audioStream);
- }
- void Sound::freeSound() {
- stop();
- free(_soundData);
- }
- int Sound::getVolume() {
- return g_fp->_mixer->getChannelVolume(*_handle) * 39; // 0..10000
- }
- void Sound::stop() {
- g_fp->_mixer->stopHandle(*_handle);
- }
- void FullpipeEngine::setSceneMusicParameters(GameVar *gvar) {
- stopSoundStream2();
- if (_mixer->isSoundHandleActive(_soundStream3))
- _mixer->stopHandle(_soundStream4);
- if (_musicLocal)
- stopAllSoundStreams();
- GameVar *var = gvar->getSubVarByName("MUSIC");
- for (int i = 0; i < 10; i++)
- _sceneTracks[i].clear();
- _numSceneTracks = 0;
- _sceneTrackHasSequence = false;
- if (!var)
- return;
- _musicGameVar = var;
- GameVar *tr = var->getSubVarByName("TRACKS");
- if (tr) {
- GameVar *sub = tr->_subVars;
- while (sub) {
- if (_musicAllowed & sub->_value.intValue) {
- _sceneTracks[_numSceneTracks] = sub->_varName;
- _numSceneTracks++;
- }
- sub = sub->_nextVarObj;
- }
- }
- _musicMinDelay = var->getSubVarAsInt("MINDELAY");
- _musicMaxDelay = var->getSubVarAsInt("MAXDELAY");
- _musicLocal = var->getSubVarAsInt("LOCAL");
- GameVar *seq = var->getSubVarByName("SEQUENCE");
- if (seq) {
- _sceneTrackHasSequence = true;
- _trackName = seq->_value.stringValue;
- }
- if (_musicLocal)
- stopAllSoundStreams();
- if (!_sceneTrackIsPlaying || _musicLocal)
- _trackStartDelay = var->getSubVarAsInt("STARTDELAY");
- }
- void FullpipeEngine::updateTrackDelay() {
- _sceneTrackIsPlaying = false;
- _trackStartDelay = _musicMinDelay + (_musicMaxDelay - _musicMinDelay) * (_updateTicks % 10) / 9;
- }
- void FullpipeEngine::startSceneTrack() {
- if (_sceneTrackIsPlaying) {
- if (!_mixer->isSoundHandleActive(_soundStream1)) { // Simulate end of sound callback
- updateTrackDelay();
- }
- }
- if (!_sceneTrackIsPlaying && _numSceneTracks > 0) {
- if (_trackStartDelay > 0) {
- _trackStartDelay--;
- } else {
- int trackNum = getSceneTrack();
- if (trackNum == -1) {
- _sceneTracksCurrentTrack = "silence";
- _trackStartDelay = 2880;
- _sceneTrackIsPlaying = 0;
- } else {
- _sceneTracksCurrentTrack = _sceneTracks[trackNum];
- startSoundStream1(_sceneTracksCurrentTrack);
- _sceneTrackIsPlaying = true;
- }
- }
- }
- }
- int FullpipeEngine::getSceneTrack() {
- int res;
- if (_sceneTrackHasSequence) {
- int num = _musicGameVar->getSubVarAsInt("TRACKS");
- if (_trackName[num + 1] == 's') { // 'silence'
- res = -1;
- } else {
- res = _trackName[num + 1] - '0';
- if (res < 0 || res >= _numSceneTracks)
- res = 0;
- }
- int track = num + 1;
- if (num + 2 >= (int)_trackName.size())
- track = 0;
- _musicGameVar->setSubVarAsInt("TRACKS", track);
- } else {
- res = _numSceneTracks * (_updateTicks % 10) / 10;
- }
- return res;
- }
- void FullpipeEngine::startSoundStream1(const Common::String &trackName) {
- stopAllSoundStreams();
- playOggSound(trackName, _soundStream1);
- }
- void FullpipeEngine::playOggSound(const Common::String &trackName, Audio::SoundHandle &stream) {
- #ifdef USE_VORBIS
- if (_mixer->isSoundHandleActive(stream))
- return;
- Common::File *track = new Common::File();
- if (!track->open(trackName)) {
- warning("Could not open %s", trackName.c_str());
- delete track;
- return;
- }
- Audio::RewindableAudioStream *ogg = Audio::makeVorbisStream(track, DisposeAfterUse::YES);
- _mixer->playStream(Audio::Mixer::kMusicSoundType, &stream, ogg);
- #endif
- }
- void FullpipeEngine::stopAllSounds() {
- for (int i = 0; i < _currSoundListCount; i++)
- for (int j = 0; j < _currSoundList1[i]->getCount(); j++) {
- _currSoundList1[i]->getSoundByIndex(j).stop();
- }
- }
- void FullpipeEngine::toggleMute() {
- if (_soundEnabled) {
- _sfxVolume = _sfxVolume != -10000 ? -10000 : 0;
- updateSoundVolume();
- }
- }
- void FullpipeEngine::playSound(int id, int flag) {
- Sound *sound = 0;
- for (int i = 0; i < _currSoundListCount; i++) {
- sound = _currSoundList1[i]->getSoundItemById(id);
- if (sound)
- break;
- }
- if (!sound) {
- warning("playSound: Can't find sound with ID %d", id);
- return;
- }
- sound->play(flag);
- }
- void FullpipeEngine::playTrack(GameVar *sceneVar, const char *name, bool delayed) {
- if (_mixer->isSoundHandleActive(_soundStream3))
- _mixer->stopHandle(_soundStream4);
- stopSoundStream2();
- if (_musicLocal)
- stopAllSoundStreams();
- GameVar *var = sceneVar->getSubVarByName(name);
- for (int i = 0; i < 10; i++)
- _sceneTracks[i].clear();
- _numSceneTracks = 0;
- _sceneTrackHasSequence = false;
- if (!var)
- return;
- _musicGameVar = var;
- GameVar *tr = var->getSubVarByName("TRACKS");
- if (tr) {
- GameVar *sub = tr->_subVars;
- while (sub) {
- if (_musicAllowed & sub->_value.intValue) {
- _sceneTracks[_numSceneTracks] = sub->_varName;
- _numSceneTracks++;
- }
- sub = sub->_nextVarObj;
- }
- }
- _musicMinDelay = var->getSubVarAsInt("MINDELAY");
- _musicMaxDelay = var->getSubVarAsInt("MAXDELAY");
- _musicLocal = var->getSubVarAsInt("LOCAL");
- GameVar *seq = var->getSubVarByName("SEQUENCE");
- if (seq) {
- _sceneTrackHasSequence = true;
- _trackName = seq->_value.stringValue;
- }
- if (delayed) {
- if (_sceneTrackIsPlaying && _numSceneTracks == 1) {
- if (_sceneTracksCurrentTrack != _sceneTracks[0])
- stopAllSoundStreams();
- }
- _trackStartDelay = var->getSubVarAsInt("STARTDELAY");
- }
- }
- void global_messageHandler_handleSound(ExCommand *cmd) {
- if (!g_fp->_soundEnabled)
- return;
- Sound *snd = 0;
- for (int i = 0; i < g_fp->_currSoundListCount; i++)
- if ((snd = g_fp->_currSoundList1[i]->getSoundItemById(cmd->_messageNum)) != NULL)
- break;
- if (!snd)
- return;
- if (cmd->_z & 1) {
- if (!g_fp->_flgSoundList && (cmd->_z & 4))
- snd->freeSound();
- snd->updateVolume();
- if (snd->_objectId && g_fp->_currentScene->getStaticANIObject1ById(snd->_objectId, -1))
- snd->setPanAndVolumeByStaticAni();
- else
- snd->setPanAndVolume(g_fp->_sfxVolume, 0);
- if (snd->getVolume() > -3500)
- snd->play(cmd->_param);
- } else if (cmd->_z & 2) {
- snd->stop();
- }
- }
- void FullpipeEngine::stopSoundStream2() {
- _stream2playing = false;
- if (_mixer->isSoundHandleActive(_soundStream3)) {
- _mixer->stopHandle(_soundStream2);
- _mixer->stopHandle(_soundStream3);
- }
- }
- void FullpipeEngine::stopAllSoundStreams() {
- _mixer->stopHandle(_soundStream1);
- _mixer->stopHandle(_soundStream2);
- _mixer->stopHandle(_soundStream3);
- _mixer->stopHandle(_soundStream4);
- _stream2playing = false;
- }
- void FullpipeEngine::stopAllSoundInstances(int id) {
- for (int i = 0; i < _currSoundListCount; i++) {
- Sound *sound = _currSoundList1[i]->getSoundItemById(id);
- if (sound)
- sound->stop();
- }
- }
- void FullpipeEngine::updateSoundVolume() {
- ConfMan.setInt("sfx_volume", MAX((_sfxVolume + 10000) / 39, 255));
- syncSoundSettings();
- for (int i = 0; i < _currSoundListCount; i++)
- for (int j = 0; j < _currSoundList1[i]->getCount(); j++) {
- _currSoundList1[i]->getSoundByIndex(j).setPanAndVolume(_sfxVolume, 0);
- }
- }
- void FullpipeEngine::setMusicVolume(int vol) {
- _musicVolume = vol;
- ConfMan.setInt("music_volume", _musicVolume);
- syncSoundSettings();
- }
- } // End of namespace Fullpipe