mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-17 15:18:11 +00:00
a3310a5de9
instead of a copy of it. Otherwise the sound engine will never notice when a sample finishes playing since it's looking at the wrong sound handle. For whatever reason, this also seems to fix the "game crashes occasionally on startup with completely useless stack trace" bug for me. The crash was easily repeatable in Valgrind for me, but the messages it produced were just as unhelpful as the stack trace. These messages are also gone now. svn-id: r11757
232 lines
6.8 KiB
C++
232 lines
6.8 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2003 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$
|
|
*
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "sound.h"
|
|
#include "common/util.h"
|
|
#include "resman.h"
|
|
#include "logic.h"
|
|
|
|
#define SOUND_SPEECH_ID 1
|
|
#define SPEECH_FLAGS (SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE | SoundMixer::FLAG_LITTLE_ENDIAN)
|
|
|
|
SwordSound::SwordSound(const char *searchPath, SoundMixer *mixer, ResMan *pResMan) {
|
|
strcpy(_filePath, searchPath);
|
|
_mixer = mixer;
|
|
_resMan = pResMan;
|
|
_cowHeader = NULL;
|
|
initCowSystem();
|
|
_endOfQueue = 0;
|
|
}
|
|
|
|
int SwordSound::addToQueue(int32 fxNo) {
|
|
bool alreadyInQueue = false;
|
|
for (uint8 cnt = 0; (cnt < _endOfQueue) && (!alreadyInQueue); cnt++)
|
|
if (_fxQueue[cnt].id == (uint32)fxNo)
|
|
alreadyInQueue = true;
|
|
if (!alreadyInQueue) {
|
|
if (_endOfQueue == MAX_FXQ_LENGTH) {
|
|
warning("Sound queue overflow");
|
|
return 0;
|
|
}
|
|
_resMan->resOpen(_fxList[fxNo].sampleId);
|
|
_fxQueue[_endOfQueue].id = fxNo;
|
|
if (_fxList[fxNo].type == FX_SPOT)
|
|
_fxQueue[_endOfQueue].delay = _fxList[fxNo].delay + 1;
|
|
else
|
|
_fxQueue[_endOfQueue].delay = 1;
|
|
_endOfQueue++;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void SwordSound::engine(void) {
|
|
// first of all, add any random sfx to the queue...
|
|
for (uint16 cnt = 0; cnt < TOTAL_FX_PER_ROOM; cnt++) {
|
|
uint16 fxNo = _roomsFixedFx[SwordLogic::_scriptVars[SCREEN]][cnt];
|
|
if (fxNo) {
|
|
if (_fxList[fxNo].type == FX_RANDOM) {
|
|
if (_rnd.getRandomNumber(_fxList[fxNo].delay) == 0)
|
|
addToQueue(fxNo);
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
// now process the queue
|
|
for (uint8 cnt = 0; cnt < _endOfQueue; cnt++) {
|
|
if (_fxQueue[cnt].delay > 0) {
|
|
_fxQueue[cnt].delay--;
|
|
if (_fxQueue[cnt].delay == 0)
|
|
playSample(&_fxQueue[cnt]);
|
|
} else {
|
|
if (!_fxQueue[cnt].handle) { // sound finished
|
|
_resMan->resClose(_fxList[_fxQueue[cnt].id].sampleId);
|
|
if (cnt != _endOfQueue-1)
|
|
_fxQueue[cnt] = _fxQueue[_endOfQueue - 1];
|
|
_endOfQueue--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwordSound::fnStopFx(int32 fxNo) {
|
|
|
|
_mixer->stopID(fxNo);
|
|
for (uint8 cnt = 0; cnt < _endOfQueue; cnt++)
|
|
if (_fxQueue[cnt].id == (uint32)fxNo) {
|
|
if (!_fxQueue[cnt].delay) // sound was started
|
|
_resMan->resClose(_fxList[_fxQueue[cnt].id].sampleId);
|
|
if (cnt != _endOfQueue-1)
|
|
_fxQueue[cnt] = _fxQueue[_endOfQueue-1];
|
|
_endOfQueue--;
|
|
return ;
|
|
}
|
|
debug(8, "fnStopFx: id not found in queue");
|
|
}
|
|
|
|
bool SwordSound::amISpeaking(void) {
|
|
return true;
|
|
}
|
|
|
|
void SwordSound::clearAllFx(void) {
|
|
warning("Stub: SwordSound::clearAllFx()");
|
|
}
|
|
|
|
void SwordSound::closeCowSysten(void) {
|
|
warning("stub: SwordSound::closeCowSystem()");
|
|
}
|
|
|
|
bool SwordSound::speechFinished(void) {
|
|
//warning("stub: SwordSound::speechFinished()");
|
|
//return true;
|
|
return (_speechHandle == 0);
|
|
}
|
|
|
|
void SwordSound::newScreen(uint16 screen) {
|
|
// stop all running SFX
|
|
while (_endOfQueue)
|
|
fnStopFx(_fxQueue[0].id);
|
|
// I don't think that we even have to start SFX here.
|
|
}
|
|
|
|
void SwordSound::playSample(QueueElement *elem) {
|
|
uint8 *sampleData = (uint8*)_resMan->fetchRes(_fxList[elem->id].sampleId);
|
|
for (uint16 cnt = 0; cnt < MAX_ROOMS_PER_FX; cnt++) {
|
|
if (_fxList[elem->id].roomVolList[cnt].roomNo) {
|
|
if ((_fxList[elem->id].roomVolList[cnt].roomNo == (int)SwordLogic::_scriptVars[SCREEN]) ||
|
|
(_fxList[elem->id].roomVolList[cnt].roomNo == -1)) {
|
|
|
|
uint8 volL = _fxList[elem->id].roomVolList[cnt].leftVol * 10;
|
|
uint8 volR = _fxList[elem->id].roomVolList[cnt].rightVol * 10;
|
|
int8 pan = (volR - volL) / 2;
|
|
uint8 volume = (volR + volL) / 2;
|
|
uint32 size = READ_LE_UINT32(sampleData + 0x28);
|
|
uint8 flags;
|
|
if (READ_LE_UINT16(sampleData + 0x22) == 16)
|
|
flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_LITTLE_ENDIAN;
|
|
else
|
|
flags = SoundMixer::FLAG_UNSIGNED;
|
|
_mixer->playRaw(&elem->handle, sampleData + 0x2C, size, 11025, flags, elem->id, volume, pan);
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint32 SwordSound::uncompressedSize(uint8 *data) {
|
|
return READ_LE_UINT32(data + 0x28);
|
|
}
|
|
|
|
bool SwordSound::startSpeech(uint16 roomNo, uint16 localNo) {
|
|
if (_cowHeader == NULL) {
|
|
warning("SwordSound::startSpeech: COW file isn't open!");
|
|
return false;
|
|
}
|
|
|
|
uint32 locIndex = _cowHeader[roomNo] >> 2;
|
|
uint32 sampleSize = _cowHeader[locIndex + (localNo * 2)];
|
|
uint32 index = _cowHeader[locIndex + (localNo * 2) - 1];
|
|
debug(4, "startSpeech(%d, %d): locIndex %d, sampleSize %d, index %d", roomNo, localNo, locIndex, sampleSize, index);
|
|
if (sampleSize) {
|
|
_cowFile.seek(index + _cowHeaderSize);
|
|
uint8 *buf = (uint8*)malloc(sampleSize);
|
|
_cowFile.read(buf, sampleSize);
|
|
uint8 *smpBuf = (uint8*)malloc(uncompressedSize(buf));
|
|
uint32 size = expandSpeech(buf, smpBuf, sampleSize);
|
|
free(buf);
|
|
if (!size) {
|
|
free(smpBuf);
|
|
return false;
|
|
}
|
|
_mixer->playRaw(&_speechHandle, smpBuf, size, 11025, SPEECH_FLAGS, SOUND_SPEECH_ID);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
uint32 SwordSound::expandSpeech(void *src, void *dest, uint32 srcSize) {
|
|
int16 *compData = (int16*)src;
|
|
if (READ_BE_UINT32(compData + 0x12) != 'data') {
|
|
warning("SwordSound::expandSpeech: 'data' tag not found in wave header");
|
|
return 0;
|
|
}
|
|
srcSize >>= 1;
|
|
int16 *expData = (int16*)dest;
|
|
compData += 0x16;
|
|
srcSize -= 0x16;
|
|
|
|
uint32 srcPos = 0;
|
|
while (srcPos < srcSize) {
|
|
if ((int16)FROM_LE_16(compData[srcPos]) < 0) {
|
|
uint16 len = (uint16)(-(int16)FROM_LE_16(compData[srcPos]));
|
|
for (uint32 cnt = 0; cnt < len; cnt++)
|
|
*expData++ = compData[srcPos + 1];
|
|
srcPos += 2;
|
|
} else {
|
|
uint32 len = FROM_LE_16(compData[srcPos]);
|
|
memcpy(expData, compData + srcPos + 1, len * 2);
|
|
expData += len;
|
|
srcPos += len + 1;
|
|
}
|
|
}
|
|
return (uint8*)expData - (uint8*)dest;
|
|
}
|
|
|
|
void SwordSound::stopSpeech(void) {
|
|
_mixer->stopID(SOUND_SPEECH_ID);
|
|
}
|
|
|
|
void SwordSound::initCowSystem(void) {
|
|
_cowFile.open("SPEECH.CLU");
|
|
if (_cowFile.isOpen() == false)
|
|
_cowFile.open("speech/SPEECH.CLU");
|
|
if (_cowFile.isOpen()) {
|
|
_cowHeaderSize = _cowFile.readUint32LE();
|
|
_cowHeader = (uint32*)malloc(_cowHeaderSize);
|
|
if (_cowHeaderSize & 3)
|
|
error("Unexpected cow header size %d", _cowHeaderSize);
|
|
for (uint32 cnt = 0; cnt < (_cowHeaderSize / 4) - 1; cnt++)
|
|
_cowHeader[cnt] = _cowFile.readUint32LE();
|
|
} else
|
|
warning("SwordSound::initCowSystem: Can't open SPEECH.CLU");
|
|
}
|