scummvm/sword2/driver/d_sound.cpp

1163 lines
26 KiB
C++
Raw Normal View History

2003-07-28 01:47:41 +00:00
/* Copyright (C) 1994-2003 Revolution Software Ltd
*
* 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$
*/
2003-09-26 14:19:03 +00:00
// FIXME: One feature still missing is the original's DipMusic() function
// which, as far as I can understand, softened the music volume when someone
// was speaking, but only if the music was playing loudly at the time.
//
// I'm not sure if we can implement this in any sensible fashion - I don't
// think we have that fine-grained control over the mixer - or if we really
// want it anyway.
//
// Simply adjusting the volume paramters to flow() is not enough. If you
// only adjust them a little you won't hear the difference anyway, and if you
// adjust them a lot it will sound really bad.
//
// Does anyone who can run the original interpreter have any
// opinions on this?
#include "common/stdafx.h"
#include "common/file.h"
#include "sword2/sword2.h"
#include "sound/rate.h"
2003-07-28 01:47:41 +00:00
2003-10-04 00:52:27 +00:00
namespace Sword2 {
// Fade-out takes half a second. This may need some tuning.
#define FADE_SAMPLES 11025
static File fpMus;
2003-07-28 01:47:41 +00:00
// Decompression macros
#define GetCompressedShift(n) ((n) >> 4)
#define GetCompressedSign(n) (((n) >> 3) & 1)
#define GetCompressedAmplitude(n) ((n) & 7)
static int32 musicVolTable[17] = {
2003-09-26 14:19:03 +00:00
0, 15, 31, 47, 63, 79, 95, 111, 127,
143, 159, 175, 191, 207, 223, 239, 255
};
int16 MusicHandle::read() {
uint8 in;
uint16 delta;
int16 out;
if (!_streaming)
return 0;
if (_firstTime) {
_lastSample = fpMus.readUint16LE();
_filePos += 2;
_firstTime = false;
return _lastSample;
}
// Assume the file handle has been correctly positioned already.
in = fpMus.readByte();
delta = GetCompressedAmplitude(in) << GetCompressedShift(in);
if (GetCompressedSign(in))
out = _lastSample - delta;
else
out = _lastSample + delta;
_filePos++;
_lastSample = out;
if (_looping) {
if (_filePos >= _fileEnd) {
_firstTime = true;
_filePos = _fileStart;
fpMus.seek(_filePos, SEEK_SET);
}
} else {
// Fade out at the end of the music. Is this really desirable
// behaviour?
if (_fileEnd - _filePos <= FADE_SAMPLES)
_fading = _fileEnd - _filePos;
}
if (_fading > 0) {
if (--_fading == 0) {
_streaming = false;
_looping = false;
}
out = (out * _fading) / FADE_SAMPLES;
}
return out;
}
bool MusicHandle::endOfData() const {
return (!_streaming || _filePos >= _fileEnd);
}
static void premix_proc(void *param, int16 *data, uint len) {
((Sound *) param)->fxServer(data, len);
}
2003-07-28 01:47:41 +00:00
Sound::Sound(Sword2Engine *vm) {
_vm = vm;
_mutex = _vm->_system->create_mutex();
_soundOn = false;
_speechStatus = false;
_fxPaused = false;
_speechPaused = false;
_speechVol = 14;
_fxVol = 14;
_speechMuted = 0;
_fxMuted = 0;
_musicVol = 16;
_musicMuted = 0;
memset(_fx, 0, sizeof(_fx));
_soundOn = true;
_converter = makeRateConverter(_music[0].getRate(), _vm->_mixer->getOutputRate(), _music[0].isStereo(), false);
_vm->_mixer->setupPremix(premix_proc, this);
}
Sound::~Sound() {
_vm->_mixer->setupPremix(0, 0);
if (_mutex)
_vm->_system->delete_mutex(_mutex);
}
// --------------------------------------------------------------------------
// This function reverse the pan table, thus reversing the stereo.
// --------------------------------------------------------------------------
// FIXME: We could probably use the FLAG_REVERSE_STEREO mixer flag here.
/**
* This function creates the pan table.
*/
void Sound::buildPanTable(bool reverse) {
int i;
for (i = 0; i < 16; i++) {
_panTable[i] = -127 + i * 8;
_panTable[i + 17] = (i + 1) * 8 - 1;
}
_panTable[16] = 0;
if (reverse) {
for (i = 0; i < 33; i++)
_panTable[i] = -_panTable[i];
}
2003-07-28 01:47:41 +00:00
}
// Save/Restore information about current music so that we can restore it
// after the credits.
void Sound::saveMusicState() {
Common::StackLock lock(_mutex);
int saveStream;
if (_music[0]._streaming && !_music[0]._fading) {
saveStream = 0;
} else if (_music[1]._streaming && !_music[0]._fading) {
saveStream = 1;
} else {
_music[2]._streaming = false;
2003-12-31 14:10:42 +00:00
savedMusicFilename = NULL;
return;
}
_music[2]._streaming = true;
_music[2]._fading = 0;
_music[2]._looping = _music[saveStream]._looping;
_music[2]._fileStart = _music[saveStream]._fileStart;
_music[2]._filePos = _music[saveStream]._filePos;
_music[2]._fileEnd = _music[saveStream]._fileEnd;
_music[2]._lastSample = _music[saveStream]._lastSample;
if (fpMus.isOpen())
savedMusicFilename = strdup(fpMus.name());
else
savedMusicFilename = NULL;
}
void Sound::restoreMusicState() {
Common::StackLock lock(_mutex);
int restoreStream;
if (!_music[2]._streaming)
return;
// Fade out any music that happens to be playing
for (int i = 0; i < MAXMUS; i++) {
if (_music[i]._streaming && !_music[i]._fading)
_music[i]._fading = FADE_SAMPLES;
}
if (!_music[0]._streaming && !_music[0]._fading) {
restoreStream = 0;
} else {
restoreStream = 1;
}
_music[restoreStream]._streaming = true;
_music[restoreStream]._fading = 0;
_music[restoreStream]._looping = _music[2]._looping;
_music[restoreStream]._fileStart = _music[2]._fileStart;
_music[restoreStream]._filePos = _music[2]._filePos;
_music[restoreStream]._fileEnd = _music[2]._fileEnd;
_music[restoreStream]._lastSample = _music[2]._lastSample;
if (savedMusicFilename) {
if (fpMus.isOpen())
fpMus.close();
fpMus.open(savedMusicFilename);
free(savedMusicFilename);
savedMusicFilename = NULL;
}
}
void Sound::playLeadOut(uint8 *leadOut) {
int i;
if (!leadOut)
return;
playFx(0, leadOut, 0, 0, RDSE_FXLEADOUT);
i = getFxIndex(-1);
if (i == MAXFX) {
warning("playLeadOut: Can't find lead-out sound handle");
return;
}
while (_fx[i]._handle.isActive()) {
_vm->_graphics->updateDisplay();
_vm->_system->delay_msecs(30);
}
}
// --------------------------------------------------------------------------
// This function returns the index of the sound effect with the ID passed in.
// --------------------------------------------------------------------------
int32 Sound::getFxIndex(int32 id) {
for (int i = 0; i < MAXFX; i++) {
if (_fx[i]._id == id)
return i;
2003-07-28 01:47:41 +00:00
}
return MAXFX;
2003-07-28 01:47:41 +00:00
}
int32 Sound::isFxOpen(int32 id) {
// FIXME: This seems backwards to me, but changing it breaks sound.
return getFxIndex(id) == MAXFX;
2003-07-28 01:47:41 +00:00
}
void Sound::fxServer(int16 *data, uint len) {
Common::StackLock lock(_mutex);
if (!_soundOn)
2003-07-28 01:47:41 +00:00
return;
updateCompSampleStreaming(data, len);
2003-07-28 01:47:41 +00:00
if (!_music[0]._streaming && !_music[1]._streaming && fpMus.isOpen())
2003-09-05 15:56:43 +00:00
fpMus.close();
2003-07-28 01:47:41 +00:00
}
/**
* Returns either RDSE_QUIET or RDSE_SPEAKING
*/
int32 Sound::amISpeaking() {
if (!_speechMuted && !_speechPaused && _soundHandleSpeech.isActive())
return RDSE_SPEAKING;
return RDSE_QUIET;
}
2003-07-28 01:47:41 +00:00
/**
* This function loads and decompresses a list of speech from a cluster, but
2003-12-02 07:41:04 +00:00
* does not play it. This is primarily used by playCompSpeech(), but also to
* store the voice-overs for the animated cutscenes until they are played.
* @param filename the file name of the speech cluster file
* @param speechid the text line id used to reference the speech
* @param buf a pointer to the buffer that will be allocated for the sound
*/
uint32 Sound::preFetchCompSpeech(const char *filename, uint32 speechid, uint16 **buf) {
uint32 i;
uint8 *data8;
2003-12-02 07:41:04 +00:00
uint32 speechPos, speechLength;
File fp;
uint32 bufferSize;
*buf = NULL;
// Open the speech cluster and find the data offset & size
fp.open(filename);
if (!fp.isOpen())
return 0;
2003-07-28 01:47:41 +00:00
fp.seek((speechid + 1) * 8, SEEK_SET);
2003-07-28 01:47:41 +00:00
2003-12-02 07:41:04 +00:00
speechPos = fp.readUint32LE();
speechLength = fp.readUint32LE();
2003-07-28 01:47:41 +00:00
2003-12-02 07:41:04 +00:00
if (!speechPos || !speechLength) {
fp.close();
return 0;
}
2003-07-28 01:47:41 +00:00
// Create a temporary buffer for compressed speech
2003-12-02 07:41:04 +00:00
data8 = (uint8 *) malloc(speechLength);
if (!data8) {
fp.close();
return 0;
}
2003-12-02 07:41:04 +00:00
fp.seek(speechPos, SEEK_SET);
2003-07-28 01:47:41 +00:00
2003-12-02 07:41:04 +00:00
if (fp.read(data8, speechLength) != speechLength) {
fp.close();
free(data8);
return 0;
}
2003-07-28 01:47:41 +00:00
fp.close();
2003-07-28 01:47:41 +00:00
// Decompress data into speech buffer.
2003-07-28 01:47:41 +00:00
2003-12-02 07:41:04 +00:00
bufferSize = (speechLength - 1) * 2;
2003-07-28 01:47:41 +00:00
*buf = (uint16 *) malloc(bufferSize);
if (!*buf) {
free(data8);
return 0;
2003-07-28 01:47:41 +00:00
}
uint16 *data16 = *buf;
2003-07-28 01:47:41 +00:00
// Starting Value
data16[0] = READ_LE_UINT16(data8);
2003-07-28 01:47:41 +00:00
2003-12-02 07:41:04 +00:00
for (i = 1; i < speechLength - 1; i++) {
if (GetCompressedSign(data8[i + 1]))
data16[i] = data16[i - 1] - (GetCompressedAmplitude(data8[i + 1]) << GetCompressedShift(data8[i + 1]));
2003-07-28 01:47:41 +00:00
else
data16[i] = data16[i - 1] + (GetCompressedAmplitude(data8[i + 1]) << GetCompressedShift(data8[i + 1]));
2003-07-28 01:47:41 +00:00
}
free(data8);
return bufferSize;
2003-07-28 01:47:41 +00:00
}
/**
* This function loads, decompresses and plays a line of speech. An error
* occurs if speech is already playing.
* @param filename the name of the speech cluster file
* @param speechid the text line id used to reference the speech
* @param vol volume, 0 (minimum) to 16 (maximum)
* @param pan panning, -16 (full left) to 16 (full right)
*/
int32 Sound::playCompSpeech(const char *filename, uint32 speechid, uint8 vol, int8 pan) {
uint16 *data16;
uint32 bufferSize;
if (!_speechMuted) {
if (getSpeechStatus() == RDERR_SPEECHPLAYING)
2003-07-28 01:47:41 +00:00
return RDERR_SPEECHPLAYING;
bufferSize = preFetchCompSpeech(filename, speechid, &data16);
// We don't know exactly what went wrong here.
if (bufferSize == 0)
return RDERR_OUTOFMEMORY;
2003-07-28 01:47:41 +00:00
2003-09-12 23:22:45 +00:00
// Modify the volume according to the master volume
byte volume = _speechMuted ? 0 : vol * _speechVol;
int8 p = _panTable[pan + 16];
2003-09-12 23:22:45 +00:00
// Start the speech playing
_speechPaused = true;
uint32 flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE;
#ifndef SCUMM_BIG_ENDIAN
flags |= SoundMixer::FLAG_LITTLE_ENDIAN;
#endif
_vm->_mixer->playRaw(&_soundHandleSpeech, data16, bufferSize, 22050, flags, -1, volume, p);
_speechStatus = true;
2003-07-28 01:47:41 +00:00
}
// DipMusic();
return RD_OK;
2003-07-28 01:47:41 +00:00
}
/**
* Stops the speech from playing.
*/
int32 Sound::stopSpeech(void) {
if (!_soundOn)
return RD_OK;
2003-07-28 01:47:41 +00:00
if (_speechStatus) {
_vm->_mixer->stopHandle(_soundHandleSpeech);
_speechStatus = false;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
return RDERR_SPEECHNOTPLAYING;
2003-07-28 01:47:41 +00:00
}
/**
* @return Either RDSE_SAMPLEPLAYING or RDSE_SAMPLEFINISHED
*/
int32 Sound::getSpeechStatus(void) {
if (!_soundOn || !_speechStatus)
return RDSE_SAMPLEFINISHED;
2003-07-28 01:47:41 +00:00
if (_speechPaused)
return RDSE_SAMPLEPLAYING;
2003-07-28 01:47:41 +00:00
if (!_soundHandleSpeech.isActive()) {
_speechStatus = false;
return RDSE_SAMPLEFINISHED;
2003-07-28 01:47:41 +00:00
}
return RDSE_SAMPLEPLAYING;
2003-07-28 01:47:41 +00:00
}
/**
* Set the volume of any future as well as playing speech samples.
* @param volume volume, from 0 (silent) to 14 (max)
*/
void Sound::setSpeechVolume(uint8 volume) {
if (volume > 14)
volume = 14;
_speechVol = volume;
if (_soundHandleSpeech.isActive() && !_speechMuted && getSpeechStatus() == RDSE_SAMPLEPLAYING) {
_vm->_mixer->setChannelVolume(_soundHandleSpeech, 16 * _speechVol);
}
2003-07-28 01:47:41 +00:00
}
/**
* @return the volume setting for speech
*/
uint8 Sound::getSpeechVolume() {
return _speechVol;
2003-07-28 01:47:41 +00:00
}
/**
* Mutes/Unmutes the speech.
* @param mute If mute is false, restore the volume to the last set master
* level. Otherwise the speech is muted (volume 0).
*/
void Sound::muteSpeech(bool mute) {
_speechMuted = mute;
2003-07-28 01:47:41 +00:00
if (getSpeechStatus() == RDSE_SAMPLEPLAYING) {
byte volume = mute ? 0 : 16 * _speechVol;
_vm->_mixer->setChannelVolume(_soundHandleSpeech, volume);
2003-07-28 01:47:41 +00:00
}
}
/**
* @return the speech's mute state, true if mute, false if not mute
*/
bool Sound::isSpeechMute(void) {
return _speechMuted;
2003-07-28 01:47:41 +00:00
}
/**
* Stops the speech dead in its tracks.
*/
void Sound::pauseSpeech(void) {
if (getSpeechStatus() == RDSE_SAMPLEPLAYING) {
_speechPaused = true;
_vm->_mixer->pauseHandle(_soundHandleSpeech, true);
2003-07-28 01:47:41 +00:00
}
}
/**
* Restarts the speech from where it was stopped.
*/
void Sound::unpauseSpeech(void) {
if (_speechPaused) {
_speechPaused = false;
_vm->_mixer->pauseHandle(_soundHandleSpeech, false);
2003-07-28 01:47:41 +00:00
}
}
/**
* This function opens a sound effect ready for playing. A unique id should be
* passed in so that each effect can be referenced individually.
* @param id the unique sound id
2003-10-03 13:59:44 +00:00
* @param data the WAV data
* @warning Zero is not a valid id
*/
int32 Sound::openFx(int32 id, uint8 *data) {
int32 i, fxi;
uint32 *data32 = NULL;
WavHeader *wav;
2003-07-28 01:47:41 +00:00
wav = (WavHeader *) data;
2003-07-28 01:47:41 +00:00
if (_soundOn) {
2003-07-28 01:47:41 +00:00
// Check for a valid id.
if (id == 0)
return RDERR_INVALIDID;
2003-07-28 01:47:41 +00:00
// Check that the fx is not already open
for (i = 0; i < MAXFX; i++) {
if (_fx[i]._id == id)
return RDERR_FXALREADYOPEN;
}
2003-07-28 01:47:41 +00:00
// Now choose a free slot for the fx
for (fxi = 0; fxi < MAXFX; fxi++) {
if (_fx[fxi]._id == 0)
2003-07-28 01:47:41 +00:00
break;
}
if (fxi == MAXFX) {
// Expire the first sound effect that isn't currently
// playing. This usually shouldn't happen since the
// game engine manually clears all sound effects (at
// least except for lead-ins and lead-outs) when moving
// between rooms.
for (fxi = 0; fxi < MAXFX; fxi++) {
if (!_fx[fxi]._handle.isActive())
break;
}
// Still no dice? I give up!
if (fxi == MAXFX) {
warning("openFx: No free sound slots");
return RDERR_NOFREEBUFFERS;
}
}
2003-07-28 01:47:41 +00:00
// Set the sample size - search for the size of the data.
2003-07-28 01:47:41 +00:00
i = 0;
while (i < 100) {
if (*data == 'd') {
data32 = (uint32 *) data;
2003-09-13 11:39:25 +00:00
if (READ_UINT32(data32) == MKID('data'))
break;
}
i++;
data++;
}
if (!data32)
return RDERR_INVALIDWAV;
_fx[fxi]._bufSize = READ_LE_UINT32(data32 + 1);
// Fill the speech buffer with data
if (_fx[fxi]._buf != NULL)
free(_fx[fxi]._buf);
_fx[fxi]._buf = (uint16 *) malloc(_fx[fxi]._bufSize);
memcpy(_fx[fxi]._buf, (uint8 *) (data32 + 2), _fx[fxi]._bufSize);
_fx[fxi]._flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_LITTLE_ENDIAN;
2003-09-13 01:59:11 +00:00
if (FROM_LE_16(wav->channels) == 2)
_fx[fxi]._flags |= SoundMixer::FLAG_STEREO;
_fx[fxi]._rate = FROM_LE_16(wav->samplesPerSec);
_fx[fxi]._id = id;
}
return RD_OK;
2003-07-28 01:47:41 +00:00
}
/**
* This function plays a sound effect. If the effect has already been opened
* then 'data' should be NULL, and the sound effect will simply be obtained
* from the id passed in. If the effect has not been opened, then the WAV data
* should be passed in 'data'. The sound effect will be closed when it has
* finished playing.
* @param id the sound id
* @param data either NULL or the WAV data
* @param vol volume, 0 (minimum) to 16 (maximum)
* @param pan panning, -16 (full left) to 16 (full right)
* @param type either RDSE_FXSPOT or RDSE_FXLOOP
* @warning Zero is not a valid id
*/
int32 Sound::playFx(int32 id, uint8 *data, uint8 vol, int8 pan, uint8 type) {
int32 i, loop;
2003-08-31 00:00:09 +00:00
uint32 hr;
2003-08-30 20:27:48 +00:00
if (type == RDSE_FXLOOP)
loop = 1;
else
loop = 0;
2003-07-28 01:47:41 +00:00
if (_soundOn) {
if (data == NULL) {
i = getFxIndex(id);
if (i == MAXFX) {
warning("playFx(%d, %d, %d, %d) - Not open", id, vol, pan, type);
return RDERR_FXNOTOPEN;
}
if (loop == 1)
_fx[i]._flags |= SoundMixer::FLAG_LOOP;
else
_fx[i]._flags &= ~SoundMixer::FLAG_LOOP;
_fx[i]._volume = vol;
// Start the sound effect playing
byte volume = _fxMuted ? 0 : vol * _fxVol;
int8 p = _panTable[pan + 16];
_vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, p);
} else {
if (type == RDSE_FXLEADIN || type == RDSE_FXLEADOUT) {
if (type == RDSE_FXLEADIN)
id = -2;
else
id = -1;
hr = openFx(id, data);
if (hr != RD_OK)
return hr;
i = getFxIndex(id);
if (i == MAXFX) {
warning("playFx(%d, %d, %d, %d) - Not found", id, vol, pan, type);
return RDERR_FXFUCKED;
}
_fx[i]._flags &= ~SoundMixer::FLAG_LOOP;
2003-11-08 19:47:20 +00:00
byte volume = _musicMuted ? 0 : musicVolTable[_musicVol];
_vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, 0);
} else {
hr = openFx(id, data);
if (hr != RD_OK) {
return hr;
}
i = getFxIndex(id);
if (i == MAXFX) {
warning("playFx(%d, %d, %d, %d) - Not found", id, vol, pan, type);
return RDERR_FXFUCKED;
}
if (loop == 1)
_fx[i]._flags |= SoundMixer::FLAG_LOOP;
else
_fx[i]._flags &= ~SoundMixer::FLAG_LOOP;
_fx[i]._volume = vol;
// Start the sound effect playing
byte volume = _fxMuted ? 0 : vol * _fxVol;
int8 p = _panTable[pan + 16];
_vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, p);
}
}
}
return RD_OK;
2003-07-28 01:47:41 +00:00
}
/**
* Sets the volume and pan of the sample which is currently playing
* @param id the id of the sample
* @param vol volume
* @param pan panning
*/
int32 Sound::setFxIdVolumePan(int32 id, uint8 vol, int8 pan) {
int32 i = getFxIndex(id);
2003-07-28 01:47:41 +00:00
if (i == MAXFX)
return RDERR_FXNOTOPEN;
if (vol > 14)
vol = 14;
_fx[i]._volume = vol;
if (!_fxMuted) {
_vm->_mixer->setChannelVolume(_fx[i]._handle, _fx[i]._volume * _fxVol);
_vm->_mixer->setChannelPan(_fx[i]._handle, _panTable[pan + 16]);
}
2003-07-28 01:47:41 +00:00
return RD_OK;
}
int32 Sound::setFxIdVolume(int32 id, uint8 vol) {
int32 i = getFxIndex(id);
2003-07-28 01:47:41 +00:00
if (i == MAXFX)
return RDERR_FXNOTOPEN;
_fx[i]._volume = vol;
if (!_fxMuted)
_vm->_mixer->setChannelVolume(_fx[i]._handle, vol * _fxVol);
2003-07-28 01:47:41 +00:00
return RD_OK;
}
/**
* This function clears all of the sound effects which are currently open or
* playing, irrespective of type.
*/
void Sound::clearAllFx(void) {
if (!_soundOn)
return;
2003-07-28 01:47:41 +00:00
for (int i = 0; i < MAXFX; i++) {
if (_fx[i]._id && _fx[i]._id != -1 && _fx[i]._id != -2) {
_vm->_mixer->stopHandle(_fx[i]._handle);
_fx[i]._id = 0;
_fx[i]._paused = false;
if (_fx[i]._buf != NULL) {
free(_fx[i]._buf);
_fx[i]._buf = NULL;
}
_fx[i]._bufSize = 0;
_fx[i]._flags = 0;
2003-07-28 01:47:41 +00:00
}
}
}
/**
* 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 id the id of the sound to close
*/
int32 Sound::closeFx(int32 id) {
int i;
2003-07-28 01:47:41 +00:00
if (!_soundOn)
return RD_OK;
2003-07-28 01:47:41 +00:00
i = getFxIndex(id);
if (i == MAXFX)
return RDERR_FXNOTOPEN;
_vm->_mixer->stopHandle(_fx[i]._handle);
_fx[i]._id = 0;
_fx[i]._paused = false;
if (_fx[i]._buf != NULL) {
free(_fx[i]._buf);
_fx[i]._buf = NULL;
2003-07-28 01:47:41 +00:00
}
_fx[i]._bufSize = 0;
_fx[i]._flags = 0;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
void Sound::pauseFx(void) {
if (!_fxPaused) {
for (int i = 0; i < MAXFX; i++) {
if (_fx[i]._id) {
_vm->_mixer->pauseHandle(_fx[i]._handle, true);
_fx[i]._paused = true;
} else
_fx[i]._paused = false;
2003-07-28 01:47:41 +00:00
}
_fxPaused = true;
2003-07-28 01:47:41 +00:00
}
}
void Sound::pauseFxForSequence(void) {
if (!_fxPaused) {
for (int i = 0; i < MAXFX; i++) {
if (_fx[i]._id && _fx[i]._id != -2) {
_vm->_mixer->pauseHandle(_fx[i]._handle, true);
_fx[i]._paused = true;
} else {
_fx[i]._paused = false;
2003-07-28 01:47:41 +00:00
}
}
_fxPaused = true;
2003-07-28 01:47:41 +00:00
}
}
void Sound::unpauseFx(void) {
if (_fxPaused) {
for (int i = 0; i < MAXFX; i++) {
if (_fx[i]._paused && _fx[i]._id) {
_vm->_mixer->pauseHandle(_fx[i]._handle, false);
2003-07-28 01:47:41 +00:00
}
}
_fxPaused = false;
2003-07-28 01:47:41 +00:00
}
}
/**
* @return the master volume setting for sound effects
*/
uint8 Sound::getFxVolume() {
return _fxVol;
2003-07-28 01:47:41 +00:00
}
/**
* Set the master volume of all sound effects. The effects still have their
* own volume setting as well as the master volume.
* @param volume volume, from 0 (silent) to 14 (max)
*/
void Sound::setFxVolume(uint8 volume) {
if (volume > 14)
volume = 14;
_fxVol = volume;
2003-07-28 01:47:41 +00:00
// Now update the volume of any fxs playing
for (int i = 0; i < MAXFX; i++) {
if (_fx[i]._id && !_fxMuted)
_vm->_mixer->setChannelVolume(_fx[i]._handle, _fx[i]._volume * _fxVol);
2003-07-28 01:47:41 +00:00
}
}
/**
* Mutes/Unmutes the sound effects.
* @param mute If mute is false, restore the volume to the last set master
* level. Otherwise the sound effects are muted (volume 0).
*/
void Sound::muteFx(bool mute) {
_fxMuted = mute;
2003-07-28 01:47:41 +00:00
// Now update the volume of any fxs playing
for (int i = 0; i < MAXFX; i++) {
if (_fx[i]._id) {
byte volume = mute ? 0 : _fx[i]._volume * _fxVol;
_vm->_mixer->setChannelVolume(_fx[i]._handle, volume);
2003-07-28 01:47:41 +00:00
}
}
}
/**
* @return the sound effects's mute state, true if mute, false if not mute
*/
bool Sound::isFxMute(void) {
return _fxMuted;
2003-07-28 01:47:41 +00:00
}
/**
* Streams music from a cluster file.
* @param filename the file name of the music cluster file
* @param musicId the id of the music to stream
* @param looping true if the music is to loop back to the start
* @return RD_OK or an error code
*/
int32 Sound::streamCompMusic(const char *filename, uint32 musicId, bool looping) {
Common::StackLock lock(_mutex);
uint32 len;
int32 primaryStream = -1;
int32 secondaryStream = -1;
2003-07-28 01:47:41 +00:00
// If both music streams are playing, that should mean one of them is
// fading out. Pick that one.
2003-07-28 01:47:41 +00:00
if (_music[0]._streaming && _music[1]._streaming) {
if (_music[0]._fading)
primaryStream = 0;
else
primaryStream = 1;
_music[primaryStream]._fading = 0;
_music[primaryStream]._streaming = false;
}
// Pick the available music stream. If no music is playing it doesn't
// matter which we use, so pick the first one.
if (_music[0]._streaming || _music[1]._streaming) {
if (_music[0]._streaming) {
primaryStream = 1;
secondaryStream = 0;
} else {
primaryStream = 0;
secondaryStream = 1;
}
} else
primaryStream = 0;
// Save looping info and tune id
_music[primaryStream]._looping = looping;
_music[primaryStream]._id = musicId;
// Don't start streaming if the volume is off.
if (isMusicMute())
return RD_OK;
2003-09-26 14:19:03 +00:00
// The assumption here is that we are never playing music from two
// different files at the same time.
if (!fpMus.isOpen())
fpMus.open(filename);
if (!fpMus.isOpen())
return RDERR_INVALIDFILENAME;
// Start other music stream fading out
if (secondaryStream != -1 && !_music[secondaryStream]._fading)
_music[secondaryStream]._fading = FADE_SAMPLES;
fpMus.seek((musicId + 1) * 8, SEEK_SET);
_music[primaryStream]._fileStart = fpMus.readUint32LE();
len = fpMus.readUint32LE();
if (!_music[primaryStream]._fileStart || !len)
return RDERR_INVALIDID;
_music[primaryStream]._fileEnd = _music[primaryStream]._fileStart + len;
_music[primaryStream]._filePos = _music[primaryStream]._fileStart;
_music[primaryStream]._streaming = true;
_music[primaryStream]._firstTime = true;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
void Sound::updateCompSampleStreaming(int16 *data, uint len) {
for (int i = 0; i < MAXMUS; i++) {
if (!_music[i]._streaming || _music[i]._paused)
continue;
st_volume_t volume = _musicMuted ? 0 : musicVolTable[_musicVol];
fpMus.seek(_music[i]._filePos, SEEK_SET);
_converter->flow(_music[i], data, len, volume, volume);
}
// DipMusic();
2003-07-28 01:47:41 +00:00
}
int32 Sound::dipMusic() {
// disable this func for now
return RD_OK;
2003-07-28 01:47:41 +00:00
/*
int32 len;
int32 readCursor, writeCursor;
int32 dwBytes1, dwBytes2;
int16 *sample;
int32 total = 0;
int32 i;
int32 status;
LPVOID lpv1, lpv2;
HRESULT hr = DS_OK;
LPDIRECTSOUNDBUFFER dsbMusic = NULL;
int32 currentMusicVol = musicVolTable[musicVol];
2003-07-28 01:47:41 +00:00
int32 minMusicVol;
// Find which music buffer is currently playing
for (i = 0; i<MAXMUS && !dsbMusic; i++)
{
if (musStreaming[i] && musFading[i] == 0)
dsbMusic = lpDsbMus[i];
}
if ((!_musicMuted) && dsbMusic && (!_speechMuted) && (musicVol>2))
2003-07-28 01:47:41 +00:00
{
minMusicVol = musicVolTable[musicVol - 3];
2003-07-28 01:47:41 +00:00
if (_speechStatus)
2003-07-28 01:47:41 +00:00
{
IDirectSoundBuffer_GetStatus(dsbSpeech, &status);
if ((hr = IDirectSoundBuffer_GetCurrentPosition(dsbMusic, &readCursor, &writeCursor)) != DS_OK)
return hr;
len = 44100 / 12 ;// 12th of a second
if ((hr = IDirectSoundBuffer_Lock(dsbMusic, readCursor, len, &lpv1, &dwBytes1, &lpv2, &dwBytes2, 0)) != DS_OK)
return hr;
for (i = 0, sample = (int16*)lpv1; sample<(int16*)((int8*)lpv1+dwBytes1); sample+= 30, i++) // 60 samples
{
if (*sample>0)
total += *sample;
else
total -= *sample;
}
total /= i;
total = minMusicVol + ( ( (currentMusicVol - minMusicVol) * total ) / 8000);
if (total > currentMusicVol)
total = currentMusicVol;
IDirectSoundBuffer_SetVolume(dsbMusic, total);
IDirectSoundBuffer_Unlock(dsbMusic,lpv1,dwBytes1,lpv2,dwBytes2);
}
else
{
IDirectSoundBuffer_GetVolume(dsbMusic, &total);
total += 50;
if (total > currentMusicVol)
total = currentMusicVol;
IDirectSoundBuffer_SetVolume(dsbMusic, total);
}
}
return hr;
2003-07-28 01:47:41 +00:00
*/
}
/**
* @return the time left for the current music, in seconds.
*/
int32 Sound::musicTimeRemaining() {
Common::StackLock lock(_mutex);
for (int i = 0; i < MAXMUS; i++) {
if (_music[i]._streaming && !_music[i]._fading)
return (_music[i]._fileEnd - _music[i]._filePos) / 22050;
2003-07-28 01:47:41 +00:00
}
return 0;
2003-07-28 01:47:41 +00:00
}
/**
* Fades out and stops the music.
*/
void Sound::stopMusic(void) {
Common::StackLock lock(_mutex);
for (int i = 0; i < MAXMUS; i++) {
if (_music[i]._streaming)
_music[i]._fading = FADE_SAMPLES;
else
_music[i]._looping = false;
}
2003-07-28 01:47:41 +00:00
}
/**
2003-10-08 17:09:51 +00:00
* Stops the music dead in its tracks. Any music that is currently being
* streamed is paued.
*/
void Sound::pauseMusic(void) {
Common::StackLock lock(_mutex);
if (_soundOn) {
2003-10-08 17:09:51 +00:00
for (int i = 0; i < MAXMUS; i++)
_music[i]._paused = _music[i]._streaming;
2003-07-28 01:47:41 +00:00
}
}
/**
* Restarts the music from where it was stopped.
*/
void Sound::unpauseMusic(void) {
Common::StackLock lock(_mutex);
if (_soundOn) {
for (int i = 0; i < MAXMUS; i++)
_music[i]._paused = false;
2003-07-28 01:47:41 +00:00
}
}
/**
* Set the volume of any future as well as playing music.
* @param volume volume, from 0 (silent) to 16 (max)
*/
void Sound::setMusicVolume(uint8 volume) {
if (volume > 16)
volume = 16;
_musicVol = volume;
2003-07-28 01:47:41 +00:00
}
/**
* @return the volume setting for music
*/
uint8 Sound::getMusicVolume() {
return _musicVol;
2003-07-28 01:47:41 +00:00
}
/**
* Mutes/Unmutes the music.
* @param mute If mute is false, restore the volume to the last set master
* level. Otherwise the music is muted (volume 0).
*/
void Sound::muteMusic(bool mute) {
_musicMuted = mute;
2003-07-28 01:47:41 +00:00
}
/**
* @return the music's mute state, true if mute, false if not mute
*/
bool Sound::isMusicMute(void) {
return _musicMuted;
2003-07-28 01:47:41 +00:00
}
2003-10-04 00:52:27 +00:00
} // End of namespace Sword2