mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-31 22:53:54 +00:00
43cfe01f3d
classes: Screen and Mouse. Screen handles most of the drawing, except the mouse cursor and in-game menus. The old Graphics class is no more. I've also fixed some "reverse stereo" regressions from the first part of the restructuring. I'm not sure what the next step will be, but hopefully it will be smaller than this one was. svn-id: r16812
292 lines
6.9 KiB
C++
292 lines
6.9 KiB
C++
/* Copyright (C) 1994-1998 Revolution Software Ltd.
|
|
* Copyright (C) 2003-2005 The ScummVM project
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// BROKEN SWORD 2
|
|
//
|
|
// SOUND.CPP Contains the sound engine, fx & music functions
|
|
// Some very 'sound' code in here ;)
|
|
//
|
|
// (16Dec96 JEL)
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#include "common/stdafx.h"
|
|
#include "common/file.h"
|
|
#include "common/system.h"
|
|
|
|
#include "sword2/sword2.h"
|
|
#include "sword2/defs.h"
|
|
#include "sword2/logic.h"
|
|
#include "sword2/resman.h"
|
|
#include "sword2/sound.h"
|
|
|
|
#include "sound/wave.h"
|
|
|
|
namespace Sword2 {
|
|
|
|
Sound::Sound(Sword2Engine *vm) {
|
|
int i;
|
|
|
|
_vm = vm;
|
|
|
|
for (i = 0; i < FXQ_LENGTH; i++)
|
|
_fxQueue[i].resource = 0;
|
|
|
|
for (i = 0; i < MAXMUS; i++)
|
|
_music[i] = NULL;
|
|
|
|
_speechPaused = false;
|
|
_musicPaused = false;
|
|
_fxPaused = false;
|
|
|
|
_speechMuted = false;
|
|
_musicMuted = false;
|
|
_fxMuted = false;
|
|
|
|
_mixBuffer = NULL;
|
|
_mixBufferLen = 0;
|
|
|
|
_vm->_mixer->setupPremix(this, SoundMixer::kMusicAudioDataType);
|
|
}
|
|
|
|
Sound::~Sound() {
|
|
_vm->_mixer->setupPremix(0);
|
|
|
|
clearFxQueue();
|
|
stopMusic(true);
|
|
stopSpeech();
|
|
|
|
free(_mixBuffer);
|
|
|
|
for (int i = 0; i < MAXMUS; i++) {
|
|
if (_musicFile[i].isOpen())
|
|
_musicFile[i].close();
|
|
}
|
|
}
|
|
|
|
void Sound::setReverseStereo(bool reverse) {
|
|
if (reverse != _reverseStereo) {
|
|
_reverseStereo = reverse;
|
|
|
|
for (int i = 0; i < FXQ_LENGTH; i++) {
|
|
if (!_fxQueue[i].resource)
|
|
continue;
|
|
|
|
_fxQueue[i].pan = -_fxQueue[i].pan;
|
|
_vm->_mixer->setChannelBalance(_fxQueue[i].handle, _fxQueue[i].pan);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop all sounds, close their resources and clear the FX queue.
|
|
*/
|
|
|
|
void Sound::clearFxQueue() {
|
|
for (int i = 0; i < FXQ_LENGTH; i++) {
|
|
if (_fxQueue[i].resource) {
|
|
stopFx(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process the FX queue. This function is called once every game cycle.
|
|
*/
|
|
|
|
void Sound::processFxQueue() {
|
|
for (int i = 0; i < FXQ_LENGTH; i++) {
|
|
if (!_fxQueue[i].resource)
|
|
continue;
|
|
|
|
switch (_fxQueue[i].type) {
|
|
case FX_RANDOM:
|
|
// 1 in 'delay' chance of this fx occurring
|
|
if (_vm->_rnd.getRandomNumber(_fxQueue[i].delay) == 0)
|
|
playFx(&_fxQueue[i]);
|
|
break;
|
|
case FX_SPOT:
|
|
if (_fxQueue[i].delay)
|
|
_fxQueue[i].delay--;
|
|
else {
|
|
playFx(&_fxQueue[i]);
|
|
_fxQueue[i].type = FX_SPOT2;
|
|
}
|
|
break;
|
|
case FX_LOOP:
|
|
playFx(&_fxQueue[i]);
|
|
_fxQueue[i].type = FX_LOOPING;
|
|
break;
|
|
case FX_SPOT2:
|
|
// Once the FX has finished remove it from the queue.
|
|
if (!_fxQueue[i].handle.isActive()) {
|
|
_vm->_resman->closeResource(_fxQueue[i].resource);
|
|
_fxQueue[i].resource = 0;
|
|
}
|
|
break;
|
|
case FX_LOOPING:
|
|
// Once the looped FX has started we can ignore it,
|
|
// but we can't close it since the WAV data is in use.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Queue a sound effect for playing later.
|
|
* @param res the sound resource number
|
|
* @param type the type of sound effect
|
|
* @param delay when to play the sound effect
|
|
* @param volume the sound effect volume (0 through 16)
|
|
* @param pan the sound effect panning (-16 through 16)
|
|
*/
|
|
|
|
void Sound::queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan) {
|
|
if (_vm->_wantSfxDebug) {
|
|
const char *typeStr;
|
|
|
|
switch (type) {
|
|
case FX_SPOT:
|
|
typeStr = "SPOT";
|
|
break;
|
|
case FX_LOOP:
|
|
typeStr = "LOOPED";
|
|
break;
|
|
case FX_RANDOM:
|
|
typeStr = "RANDOM";
|
|
break;
|
|
default:
|
|
typeStr = "INVALID";
|
|
break;
|
|
}
|
|
|
|
byte buf[NAME_LEN];
|
|
|
|
debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->fetchObjectName(res, buf), volume, pan, delay, typeStr);
|
|
}
|
|
|
|
for (int i = 0; i < FXQ_LENGTH; i++) {
|
|
if (!_fxQueue[i].resource) {
|
|
byte *data = _vm->_resman->openResource(res);
|
|
StandardHeader *header = (StandardHeader *) data;
|
|
|
|
assert(header->fileType == WAV_FILE);
|
|
|
|
uint32 len = _vm->_resman->fetchLen(res) - sizeof(StandardHeader);
|
|
|
|
if (type == FX_RANDOM) {
|
|
// For spot effects and loops the delay is the
|
|
// number of frames to wait. For random
|
|
// effects, however, it's the average number of
|
|
// seconds between playing the sound, so we
|
|
// have to multiply by the frame rate.
|
|
delay *= 12;
|
|
}
|
|
|
|
volume = (volume * SoundMixer::kMaxChannelVolume) / 16;
|
|
pan = (pan * 127) / 16;
|
|
|
|
if (isReverseStereo())
|
|
pan = -pan;
|
|
|
|
_fxQueue[i].resource = res;
|
|
_fxQueue[i].data = data + sizeof(StandardHeader);
|
|
_fxQueue[i].len = len;
|
|
_fxQueue[i].delay = delay;
|
|
_fxQueue[i].volume = volume;
|
|
_fxQueue[i].pan = pan;
|
|
_fxQueue[i].type = type;
|
|
|
|
// Keep track of the index in the loop so that
|
|
// fnStopFx() can be used later to kill this sound.
|
|
// Mainly for FX_LOOP and FX_RANDOM.
|
|
|
|
Logic::_scriptVars[RESULT] = i;
|
|
return;
|
|
}
|
|
}
|
|
|
|
warning("No free slot in FX queue");
|
|
}
|
|
|
|
int32 Sound::playFx(FxQueueEntry *fx) {
|
|
return playFx(&fx->handle, fx->data, fx->len, fx->volume, fx->pan, (fx->type == FX_LOOP), SoundMixer::kSFXAudioDataType);
|
|
}
|
|
|
|
int32 Sound::playFx(PlayingSoundHandle *handle, byte *data, uint32 len, uint8 vol, int8 pan, bool loop, SoundMixer::SoundType soundType) {
|
|
if (_fxMuted)
|
|
return RD_OK;
|
|
|
|
if (handle->isActive())
|
|
return RDERR_FXALREADYOPEN;
|
|
|
|
Common::MemoryReadStream stream(data, len);
|
|
int rate, size;
|
|
byte flags;
|
|
|
|
if (!loadWAVFromStream(stream, size, rate, flags)) {
|
|
warning("playFX: Not a valid WAV file");
|
|
return RDERR_INVALIDWAV;
|
|
}
|
|
|
|
if (isReverseStereo())
|
|
flags |= SoundMixer::FLAG_REVERSE_STEREO;
|
|
|
|
if (loop)
|
|
flags |= SoundMixer::FLAG_LOOP;
|
|
|
|
_vm->_mixer->playRaw(handle, data + stream.pos(), size, rate, flags, -1, vol, pan, 0, 0, soundType);
|
|
return RD_OK;
|
|
}
|
|
|
|
/**
|
|
* This function closes a sound effect which has been previously opened for
|
|
* playing. Sound effects must be closed when they are finished with, otherwise
|
|
* you will run out of sound effect buffers.
|
|
* @param i the index of the sound to close
|
|
*/
|
|
|
|
int32 Sound::stopFx(int32 i) {
|
|
if (!_fxQueue[i].resource)
|
|
return RDERR_FXNOTOPEN;
|
|
|
|
if (_fxQueue[i].handle.isActive())
|
|
_vm->_mixer->stopHandle(_fxQueue[i].handle);
|
|
|
|
_vm->_resman->closeResource(_fxQueue[i].resource);
|
|
_fxQueue[i].resource = 0;
|
|
return RD_OK;
|
|
}
|
|
|
|
void Sound::pauseAllSound() {
|
|
pauseMusic();
|
|
pauseSpeech();
|
|
pauseFx();
|
|
}
|
|
|
|
void Sound::unpauseAllSound() {
|
|
unpauseMusic();
|
|
unpauseSpeech();
|
|
unpauseFx();
|
|
}
|
|
|
|
} // End of namespace Sword2
|