mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-09 19:32:11 +00:00
762 lines
25 KiB
C++
762 lines
25 KiB
C++
/* 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.
|
|
*
|
|
* Additional copyright for this file:
|
|
* Copyright (C) 1995 Presto Studios, Inc.
|
|
*
|
|
* 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 "audio/audiostream.h"
|
|
#include "audio/mixer.h"
|
|
#include "audio/decoders/wave.h"
|
|
#include "common/archive.h"
|
|
#include "common/events.h"
|
|
#include "common/system.h"
|
|
|
|
#include "buried/buried.h"
|
|
#include "buried/graphics.h"
|
|
#include "buried/resources.h"
|
|
#include "buried/sound.h"
|
|
|
|
namespace Buried {
|
|
|
|
#define TIMED_EFFECT_NONE 0x00
|
|
#define TIMED_EFFECT_VOLUME 0x01
|
|
|
|
#define SOUND_FLAG_DESTROY_AFTER_COMPLETION 0x01
|
|
|
|
static inline int clipVolume(int volume) {
|
|
return CLIP<int>(volume, 0, Audio::Mixer::kMaxChannelVolume);
|
|
}
|
|
|
|
SoundManager::SoundManager(BuriedEngine *vm) : _vm(vm) {
|
|
_fileIDFootsteps = -1;
|
|
_lastAmbient = 1;
|
|
startup();
|
|
}
|
|
|
|
SoundManager::~SoundManager() {
|
|
for (int i = 0; i < kMaxSounds; i++)
|
|
delete _soundData[i];
|
|
}
|
|
|
|
bool SoundManager::startup() {
|
|
_paused = false;
|
|
|
|
for (int i = 0; i < kMaxSounds; i++)
|
|
_soundData[i] = new Sound();
|
|
|
|
return true;
|
|
}
|
|
|
|
void SoundManager::shutDown() {
|
|
if (_paused)
|
|
return;
|
|
|
|
for (int i = 0; i < kMaxSounds; i++) {
|
|
delete _soundData[i];
|
|
_soundData[i] = new Sound();
|
|
}
|
|
}
|
|
|
|
void SoundManager::pause(bool shouldPause) {
|
|
for (int i = 0; i < kMaxSounds; i++)
|
|
_soundData[i]->pause(shouldPause);
|
|
}
|
|
|
|
bool SoundManager::setAmbientSound(const Common::String &fileName, bool fade, byte finalVolumeLevel) {
|
|
// Determine which of the two ambient tracks to use
|
|
int newAmbientTrack = (_lastAmbient == 0) ? 1 : 0;
|
|
|
|
// If this ambient track is currently in use, stop and kill it now
|
|
if (_soundData[kAmbientIndexBase + newAmbientTrack]->isPlaying()) {
|
|
delete _soundData[kAmbientIndexBase + newAmbientTrack];
|
|
_soundData[kAmbientIndexBase + newAmbientTrack] = new Sound();
|
|
}
|
|
|
|
bool retVal = true;
|
|
|
|
if (fileName.empty()) {
|
|
if (fade) {
|
|
// Set parameters for the current ambient, if there is one
|
|
if (_soundData[kAmbientIndexBase + _lastAmbient]->isPlaying()) {
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_loop = true;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectIndex = TIMED_EFFECT_VOLUME;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_flags = SOUND_FLAG_DESTROY_AFTER_COMPLETION;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectSteps = 16;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectDelta = -(_soundData[kAmbientIndexBase + _lastAmbient]->_volume / 16);
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectRemaining = 2000;
|
|
|
|
// Reset parameters for current ambient
|
|
g_system->getMixer()->setChannelVolume(*_soundData[kAmbientIndexBase + _lastAmbient]->_handle, clipVolume(_soundData[kAmbientIndexBase + _lastAmbient]->_volume << 1));
|
|
// clone2727: The original resets the loop count, but I don't think that's necessary
|
|
}
|
|
} else {
|
|
// Stop the current ambient
|
|
delete _soundData[kAmbientIndexBase + _lastAmbient];
|
|
_soundData[kAmbientIndexBase + _lastAmbient] = new Sound();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (fade) {
|
|
// Set parameters for the current ambient, if there is one
|
|
if (_soundData[kAmbientIndexBase + _lastAmbient]->_handle) {
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_loop = true;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectIndex = TIMED_EFFECT_VOLUME;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_flags = SOUND_FLAG_DESTROY_AFTER_COMPLETION;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectSteps = 16;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectDelta = -(_soundData[kAmbientIndexBase + _lastAmbient]->_volume / 16);
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectRemaining = 2000;
|
|
|
|
// Reset parameters for the current ambient
|
|
g_system->getMixer()->setChannelVolume(*_soundData[kAmbientIndexBase + _lastAmbient]->_handle,
|
|
clipVolume(_soundData[kAmbientIndexBase + _lastAmbient]->_volume << 1));
|
|
}
|
|
|
|
// Load the new ambient
|
|
if (_soundData[kAmbientIndexBase + newAmbientTrack]->load(fileName)) {
|
|
// Set the parameters of the new ambient
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_volume = 0;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_loop = true;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectIndex = TIMED_EFFECT_VOLUME;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_flags = 0;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectSteps = 16;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectDelta = finalVolumeLevel / 16;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectRemaining = 2000;
|
|
|
|
// Start the new ambient
|
|
retVal = _soundData[kAmbientIndexBase + newAmbientTrack]->start();
|
|
}
|
|
} else {
|
|
// Load the new ambient
|
|
if (!fileName.empty()) {
|
|
if (_soundData[kAmbientIndexBase + newAmbientTrack]->load(fileName)) {
|
|
// Set some parameters
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_volume = finalVolumeLevel;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_loop = true;
|
|
|
|
// Stop the current ambient
|
|
delete _soundData[kAmbientIndexBase + _lastAmbient];
|
|
_soundData[kAmbientIndexBase + _lastAmbient] = new Sound();
|
|
|
|
// Start the new ambient
|
|
retVal = _soundData[kAmbientIndexBase + newAmbientTrack]->start();
|
|
}
|
|
} else {
|
|
// Stop the current ambient
|
|
delete _soundData[kAmbientIndexBase + _lastAmbient];
|
|
_soundData[kAmbientIndexBase + _lastAmbient] = new Sound();
|
|
}
|
|
}
|
|
|
|
// Reset the last ambient index
|
|
_lastAmbient = newAmbientTrack;
|
|
|
|
// Return success
|
|
return retVal;
|
|
}
|
|
|
|
bool SoundManager::adjustAmbientSoundVolume(byte newVolumeLevel, bool fade, byte steps, uint32 fadeLength) {
|
|
// If we are not playing an ambient track, simply return false
|
|
if (!_soundData[kAmbientIndexBase + _lastAmbient]->_handle)
|
|
return false;
|
|
|
|
// Compare the new volume level to the current one, returning success if they are the same
|
|
if (_soundData[kAmbientIndexBase + _lastAmbient]->_volume == newVolumeLevel)
|
|
return true;
|
|
|
|
// If we already have any timed channels in the current ambient channel, kill them now
|
|
if (_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectIndex != TIMED_EFFECT_NONE) {
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectIndex = TIMED_EFFECT_NONE;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_flags = 0; // clone2727 says: is this right?
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectSteps = 0;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectDelta = 0;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectStart = 0;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectRemaining = 0;
|
|
}
|
|
|
|
// Switch on whether or not we are fading to the new volume level
|
|
if (fade) {
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectIndex = TIMED_EFFECT_VOLUME;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectSteps = steps;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectDelta =
|
|
((int)newVolumeLevel - (int)_soundData[kAmbientIndexBase + _lastAmbient]->_volume) / (int)steps;
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_timedEffectRemaining = fadeLength;
|
|
} else {
|
|
// We are not fading between the current and new volume levels, so simply change the level
|
|
// and reset the volume for the sample
|
|
_soundData[kAmbientIndexBase + _lastAmbient]->_volume = newVolumeLevel;
|
|
g_system->getMixer()->setChannelVolume(*_soundData[kAmbientIndexBase + _lastAmbient]->_handle,
|
|
clipVolume(newVolumeLevel << 1));
|
|
}
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::isAmbientSoundPlaying() {
|
|
return _soundData[kAmbientIndexBase + _lastAmbient]->_handle != 0;
|
|
}
|
|
|
|
bool SoundManager::setSecondaryAmbientSound(const Common::String &fileName, bool fade, byte finalVolumeLevel) {
|
|
if (fileName.empty())
|
|
return false;
|
|
|
|
// Determine which of the two ambient tracks to use
|
|
int newAmbientTrack = (_lastAmbient == 0) ? 1 : 0;
|
|
|
|
// If this ambient track is currently in use, stop and kill it now
|
|
if (_soundData[kAmbientIndexBase + newAmbientTrack]->_handle) {
|
|
delete _soundData[kAmbientIndexBase + newAmbientTrack];
|
|
_soundData[kAmbientIndexBase + newAmbientTrack] = new Sound();
|
|
}
|
|
|
|
// Are we flagged for fade?
|
|
if (fade) {
|
|
// Load the new ambient
|
|
if (!_soundData[kAmbientIndexBase + newAmbientTrack]->load(fileName))
|
|
return false;
|
|
|
|
// Set the parameters for the new ambient
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_volume = 0;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_loop = true;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectIndex = TIMED_EFFECT_VOLUME;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_flags = 0;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectSteps = 16;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectDelta = finalVolumeLevel / 16;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_timedEffectRemaining = 2000;
|
|
|
|
// Start the new ambient
|
|
return _soundData[kAmbientIndexBase + newAmbientTrack]->start();
|
|
}
|
|
|
|
// Load the new ambient
|
|
if (!_soundData[kAmbientIndexBase + newAmbientTrack]->load(fileName))
|
|
return false;
|
|
|
|
// Set some parameters
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_volume = finalVolumeLevel;
|
|
_soundData[kAmbientIndexBase + newAmbientTrack]->_loop = true;
|
|
|
|
// Start the new ambient
|
|
return _soundData[kAmbientIndexBase + newAmbientTrack]->start();
|
|
}
|
|
|
|
bool SoundManager::adjustSecondaryAmbientSoundVolume(byte newVolumeLevel, bool fade, byte steps, uint32 fadeLength) {
|
|
// Determine which of the two ambient tracks to modify
|
|
int ambientTrack = (_lastAmbient == 0) ? 1 : 0;
|
|
|
|
// If we are not playing an ambient track, simply return false
|
|
if (!_soundData[kAmbientIndexBase + ambientTrack]->_handle)
|
|
return false;
|
|
|
|
// Compare the new volume level to the current one, returning success if they are the same
|
|
if (_soundData[kAmbientIndexBase + ambientTrack]->_volume == newVolumeLevel)
|
|
return true;
|
|
|
|
// If we already have any timed channels in the current ambient channel, kill them now
|
|
if (_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectIndex != TIMED_EFFECT_NONE) {
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectIndex = TIMED_EFFECT_NONE;
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_flags = 0; // clone2727 says: is this right?
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectSteps = 0;
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectDelta = 0;
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectStart = 0;
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectRemaining = 0;
|
|
}
|
|
|
|
// Switch on whether or not we are fading to the new volume level
|
|
if (fade) {
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectIndex = TIMED_EFFECT_VOLUME;
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectSteps = steps;
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectDelta =
|
|
((int)newVolumeLevel - (int)_soundData[kAmbientIndexBase + ambientTrack]->_volume) / (int)steps;
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_timedEffectRemaining = fadeLength;
|
|
} else {
|
|
// We are not fading between the current and new volume levels, so simply change the level
|
|
// and reset the volume for the sample
|
|
_soundData[kAmbientIndexBase + ambientTrack]->_volume = newVolumeLevel;
|
|
g_system->getMixer()->setChannelVolume(*_soundData[kAmbientIndexBase + ambientTrack]->_handle,
|
|
clipVolume(newVolumeLevel << 1));
|
|
}
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
uint32 SoundManager::getSecondaryAmbientPosition() {
|
|
int ambientTrack = (_lastAmbient == 0) ? 1 : 0;
|
|
|
|
if (!_soundData[kAmbientIndexBase + ambientTrack]->isPlaying())
|
|
return 0;
|
|
|
|
// We need to return the position in *bytes* in the buffer here
|
|
// So, let's work some magic with this
|
|
Audio::Timestamp time = g_system->getMixer()->getElapsedTime(*_soundData[kAmbientIndexBase + ambientTrack]->_handle);
|
|
|
|
// Convert to the sound rate before getting to the magic.
|
|
time = time.convertToFramerate(_soundData[kAmbientIndexBase + ambientTrack]->_soundData->getRate());
|
|
|
|
// Here's the magic. We're assuming everything is 8-bit, mono.
|
|
return time.totalNumberOfFrames();
|
|
}
|
|
|
|
bool SoundManager::restartSecondaryAmbientSound() {
|
|
int ambientTrack = (_lastAmbient == 0) ? 1 : 0;
|
|
|
|
if (!_soundData[kAmbientIndexBase + ambientTrack]->isPlaying())
|
|
return 0;
|
|
|
|
_soundData[kAmbientIndexBase + ambientTrack]->start();
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::playSynchronousAIComment(const Common::String &fileName) {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// Load the sound file
|
|
if (!_soundData[kAIVoiceIndex]->load(fileName))
|
|
return false;
|
|
|
|
// Play the file
|
|
bool retVal = _soundData[kAIVoiceIndex]->start();
|
|
|
|
while (retVal && !_vm->shouldQuit() && _soundData[kAIVoiceIndex]->isPlaying()) {
|
|
timerCallback();
|
|
_vm->yield();
|
|
}
|
|
|
|
// Now that is has been played, kill it here and now
|
|
delete _soundData[kAIVoiceIndex];
|
|
_soundData[kAIVoiceIndex] = new Sound();
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::playAsynchronousAIComment(const Common::String &fileName) {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// Load the sound file
|
|
if (!_soundData[kAIVoiceIndex]->load(fileName))
|
|
return false;
|
|
|
|
// Set some parameters
|
|
_soundData[kAIVoiceIndex]->_flags = SOUND_FLAG_DESTROY_AFTER_COMPLETION;
|
|
_soundData[kAIVoiceIndex]->_volume = 127;
|
|
|
|
// Play the file
|
|
return _soundData[kAIVoiceIndex]->start();
|
|
}
|
|
|
|
bool SoundManager::isAsynchronousAICommentPlaying() {
|
|
if (_paused)
|
|
return false;
|
|
|
|
return _soundData[kAIVoiceIndex]->isPlaying();
|
|
}
|
|
|
|
int SoundManager::playSoundEffect(const Common::String &fileName, int volume, bool loop, bool oneShot) {
|
|
if (fileName.empty())
|
|
return -1;
|
|
|
|
if (_paused)
|
|
return -1;
|
|
|
|
// Make sure that we have a free sound effect channel
|
|
int effectChannel = -1;
|
|
if (!_soundData[kEffectsIndexB]->_handle)
|
|
effectChannel = 1;
|
|
if (!_soundData[kEffectsIndexA]->_handle)
|
|
effectChannel = 0;
|
|
if (effectChannel == -1)
|
|
return -1;
|
|
|
|
// Reinitialize the structure
|
|
delete _soundData[kEffectsIndexBase + effectChannel];
|
|
_soundData[kEffectsIndexBase + effectChannel] = new Sound();
|
|
|
|
// Load the sound file
|
|
if (!_soundData[kEffectsIndexBase + effectChannel]->load(fileName))
|
|
return -1;
|
|
|
|
// Set some parameters
|
|
_soundData[kEffectsIndexBase + effectChannel]->_volume = volume;
|
|
_soundData[kEffectsIndexBase + effectChannel]->_loop = loop;
|
|
if (oneShot)
|
|
_soundData[kEffectsIndexBase + effectChannel]->_flags = SOUND_FLAG_DESTROY_AFTER_COMPLETION;
|
|
|
|
// Play the file
|
|
_soundData[kEffectsIndexBase + effectChannel]->start();
|
|
|
|
// Return the index of the channel used
|
|
return effectChannel;
|
|
}
|
|
|
|
bool SoundManager::playSynchronousSoundEffect(const Common::String &fileName, int volume) {
|
|
// Reset the cursor
|
|
Cursor oldCursor = _vm->_gfx->setCursor(kCursorWait);
|
|
g_system->updateScreen();
|
|
|
|
// Attempt to start the sound playing using the standard sound effect playback function
|
|
int soundChannel = playSoundEffect(fileName, volume, false, true);
|
|
|
|
// If the sound channel passed to us was invalid, return false right now
|
|
if (soundChannel < 0)
|
|
return false;
|
|
|
|
// Otherwise, assume the sound has started playing and enter a wait and see loop until
|
|
// the sound finishes playing
|
|
do {
|
|
timerCallback();
|
|
_vm->yield();
|
|
} while (!_vm->shouldQuit() && isSoundEffectPlaying(soundChannel));
|
|
|
|
// One last callback check
|
|
timerCallback();
|
|
|
|
// Reset the cursor
|
|
_vm->_gfx->setCursor(oldCursor);
|
|
g_system->updateScreen();
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::stopSoundEffect(int effectID) {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// Confirm that we have a valid sound effect channel
|
|
if (effectID < 0 || effectID > 1)
|
|
return false;
|
|
|
|
// Return the result
|
|
return _soundData[kEffectsIndexBase + effectID]->stop();
|
|
}
|
|
|
|
bool SoundManager::isSoundEffectPlaying(int effectID) {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// Confirm that we have a valid sound effect channel
|
|
if (effectID < 0 || effectID > 1)
|
|
return false;
|
|
|
|
// Return the result
|
|
return _soundData[kEffectsIndexBase + effectID]->isPlaying();
|
|
}
|
|
|
|
bool SoundManager::adjustSoundEffectSoundVolume(int effectID, byte newVolumeLevel, bool fade, byte steps, uint32 fadeLength) {
|
|
// Confirm that we have a valid sound effect channel
|
|
if (effectID < 0 || effectID > 1)
|
|
return false;
|
|
|
|
// If the effect is not playing, then stop it now
|
|
if (!_soundData[kEffectsIndexBase + effectID]->isPlaying())
|
|
return false;
|
|
|
|
// Compare the new volume level to the current one, returning success if they are the same
|
|
if (_soundData[kEffectsIndexBase + effectID]->_volume == newVolumeLevel)
|
|
return true;
|
|
|
|
// If we already have any timed channels in the current effect channel, kill them now
|
|
if (_soundData[kEffectsIndexBase + effectID]->_timedEffectIndex != TIMED_EFFECT_NONE) {
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectIndex = TIMED_EFFECT_NONE;
|
|
_soundData[kEffectsIndexBase + effectID]->_flags = 0; // clone2727 says: is this right?
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectSteps = 0;
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectDelta = 0;
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectStart = 0;
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectRemaining = 0;
|
|
}
|
|
|
|
// Switch on whether or not we are fading to the new volume level
|
|
if (fade) {
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectIndex = TIMED_EFFECT_VOLUME;
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectSteps = steps;
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectDelta =
|
|
((int)newVolumeLevel - (int)_soundData[kEffectsIndexBase + effectID]->_volume) / (int)steps;
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[kEffectsIndexBase + effectID]->_timedEffectRemaining = fadeLength;
|
|
} else {
|
|
// We are not fading between the current and new volume levels, so simply change the level
|
|
// and reset the volume for the sample
|
|
_soundData[kEffectsIndexBase + effectID]->_volume = newVolumeLevel;
|
|
g_system->getMixer()->setChannelVolume(*_soundData[kEffectsIndexBase + effectID]->_handle,
|
|
clipVolume(newVolumeLevel << 1));
|
|
}
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::playInterfaceSound(const Common::String &fileName) {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// If we have a sound playing, stop and destroy it
|
|
if (_soundData[kInterfaceIndex]->_handle) {
|
|
delete _soundData[kInterfaceIndex];
|
|
_soundData[kInterfaceIndex] = new Sound();
|
|
}
|
|
|
|
// Load the sound file
|
|
if (!_soundData[kInterfaceIndex]->load(fileName))
|
|
return false;
|
|
|
|
_soundData[kInterfaceIndex]->_flags = SOUND_FLAG_DESTROY_AFTER_COMPLETION;
|
|
|
|
// Play the file
|
|
return _soundData[kInterfaceIndex]->start();
|
|
}
|
|
|
|
bool SoundManager::stopInterfaceSound() {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// Stop the sound
|
|
delete _soundData[kInterfaceIndex];
|
|
_soundData[kInterfaceIndex] = new Sound();
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::isInterfaceSoundPlaying() {
|
|
if (_paused)
|
|
return false;
|
|
|
|
return _soundData[kInterfaceIndex]->isPlaying();
|
|
}
|
|
|
|
bool SoundManager::startFootsteps(int footstepsID) {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// Check the passed ID
|
|
if (footstepsID < 0)
|
|
return false;
|
|
|
|
// Compare the ID against the current ID
|
|
if (_fileIDFootsteps != footstepsID) {
|
|
// Swap the current footsteps ID
|
|
_fileIDFootsteps = footstepsID;
|
|
|
|
// Dispose and reinitialize the current footsteps sample
|
|
delete _soundData[kFootstepsIndex];
|
|
_soundData[kFootstepsIndex] = new Sound();
|
|
|
|
// Load the footsteps sample data and modify the internal flags
|
|
_soundData[kFootstepsIndex]->load(_vm->getFilePath(IDS_FOOTSTEPS_FILENAME_BASE + footstepsID));
|
|
_soundData[kFootstepsIndex]->_loop = true;
|
|
}
|
|
|
|
// Play the footsteps
|
|
_soundData[kFootstepsIndex]->start();
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::stopFootsteps() {
|
|
if (_paused)
|
|
return false;
|
|
|
|
// Make sure that if the footsteps are currently playing, they are stopped
|
|
_soundData[kFootstepsIndex]->stop();
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::stop() {
|
|
if (_paused)
|
|
return true;
|
|
|
|
// Stop any playing sounds, but keep them in memory
|
|
for (int i = 0; i < kMaxSounds; i++) {
|
|
if (_soundData[i]->stop()) {
|
|
if (i < 2) {
|
|
_soundData[i]->_wasPlaying = true;
|
|
} else {
|
|
delete _soundData[i];
|
|
_soundData[i] = new Sound();
|
|
}
|
|
}
|
|
}
|
|
|
|
_paused = true;
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::restart() {
|
|
if (!_paused)
|
|
return true;
|
|
|
|
// Check all samples, and if they were playing, restart them now
|
|
for (int i = 0; i < kMaxSounds; i++) {
|
|
if (_soundData[i]->_wasPlaying) {
|
|
_soundData[i]->start();
|
|
_soundData[i]->_wasPlaying = false;
|
|
}
|
|
}
|
|
|
|
_paused = false;
|
|
|
|
// Return success
|
|
return true;
|
|
}
|
|
|
|
void SoundManager::timerCallback() {
|
|
if (_paused)
|
|
return;
|
|
|
|
// Check the playing sounds, and if they are playing, check for time-based effects
|
|
for (int i = 0; i < kMaxSounds; i++) {
|
|
if (_soundData[i]->_handle) {
|
|
if (_soundData[i]->_timedEffectIndex != TIMED_EFFECT_NONE) {
|
|
// Has the right amount of time passed for a change to be made?
|
|
if (g_system->getMillis() >= (_soundData[i]->_timedEffectStart + (_soundData[i]->_timedEffectRemaining / _soundData[i]->_timedEffectSteps))) {
|
|
// Change the specified member and notify the mixer of the change
|
|
if (_soundData[i]->_timedEffectIndex == TIMED_EFFECT_VOLUME) {
|
|
_soundData[i]->_volume += _soundData[i]->_timedEffectDelta;
|
|
g_system->getMixer()->setChannelVolume(*_soundData[i]->_handle, clipVolume(_soundData[i]->_volume << 1));
|
|
}
|
|
|
|
// Update the start time, step counter, and remaining time
|
|
_soundData[i]->_timedEffectRemaining -= (_soundData[i]->_timedEffectRemaining / _soundData[i]->_timedEffectSteps);
|
|
_soundData[i]->_timedEffectStart = g_system->getMillis();
|
|
_soundData[i]->_timedEffectSteps--;
|
|
|
|
// If the effect has finished, then remove the transition type
|
|
if (_soundData[i]->_timedEffectSteps == 0) {
|
|
// If we are flagged for destruction after completion, do it now
|
|
if (_soundData[i]->_flags & SOUND_FLAG_DESTROY_AFTER_COMPLETION) {
|
|
delete _soundData[i];
|
|
_soundData[i] = new Sound();
|
|
}
|
|
|
|
// Reset effect data
|
|
_soundData[i]->_timedEffectIndex = TIMED_EFFECT_NONE;
|
|
_soundData[i]->_flags = 0;
|
|
_soundData[i]->_timedEffectSteps = 0;
|
|
_soundData[i]->_timedEffectDelta = 0;
|
|
_soundData[i]->_timedEffectStart = 0;
|
|
_soundData[i]->_timedEffectRemaining = 0;
|
|
}
|
|
}
|
|
} else {
|
|
// Even though we are not flagged for a timed effect, is this sound flagged for destruction?
|
|
if ((_soundData[i]->_flags & SOUND_FLAG_DESTROY_AFTER_COMPLETION) && !_soundData[i]->isPlaying()) {
|
|
delete _soundData[i];
|
|
_soundData[i] = new Sound();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SoundManager::Sound::Sound() {
|
|
_soundData = nullptr;
|
|
_handle = nullptr;
|
|
|
|
_volume = 127;
|
|
_loop = false;
|
|
|
|
_flags = 0;
|
|
_timedEffectIndex = TIMED_EFFECT_NONE;
|
|
_timedEffectSteps = 0;
|
|
_timedEffectDelta = 0;
|
|
_timedEffectStart = 0;
|
|
_timedEffectRemaining = 0;
|
|
|
|
_wasPlaying = false;
|
|
}
|
|
|
|
SoundManager::Sound::~Sound() {
|
|
stop();
|
|
|
|
delete _soundData;
|
|
}
|
|
|
|
bool SoundManager::Sound::load(const Common::String &fileName) {
|
|
if (fileName.empty())
|
|
return false;
|
|
|
|
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName);
|
|
|
|
if (!stream)
|
|
return false;
|
|
|
|
_soundData = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
|
|
return _soundData != 0;
|
|
}
|
|
|
|
bool SoundManager::Sound::start() {
|
|
if (!_soundData)
|
|
return false;
|
|
|
|
stop();
|
|
|
|
_handle = new Audio::SoundHandle();
|
|
|
|
Audio::AudioStream *audioStream = _soundData;
|
|
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO;
|
|
|
|
_soundData->rewind();
|
|
|
|
if (_loop) {
|
|
audioStream = new Audio::LoopingAudioStream(_soundData, 0, DisposeAfterUse::NO);
|
|
disposeAfterUse = DisposeAfterUse::YES;
|
|
}
|
|
|
|
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, audioStream,
|
|
-1, clipVolume(_volume << 1), 0, disposeAfterUse);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::Sound::isPlaying() const {
|
|
return _soundData && _handle && g_system->getMixer()->isSoundHandleActive(*_handle);
|
|
}
|
|
|
|
bool SoundManager::Sound::stop() {
|
|
if (!isPlaying())
|
|
return false;
|
|
|
|
g_system->getMixer()->stopHandle(*_handle);
|
|
delete _handle;
|
|
_handle = nullptr;
|
|
return true;
|
|
}
|
|
|
|
void SoundManager::Sound::pause(bool shouldPause) {
|
|
if (_soundData && _handle)
|
|
g_system->getMixer()->pauseHandle(*_handle, shouldPause);
|
|
}
|
|
|
|
} // End of namespace Buried
|