scummvm/saga/sndres.cpp
2004-12-24 12:47:16 +00:00

343 lines
7.6 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2004 The ScummVM project
*
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
*
* 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$
*
*/
// Sound resource management class
#include "saga/saga.h"
#include "saga/rscfile_mod.h"
#include "saga/sndres.h"
#include "saga/sound.h"
#include "saga/stream.h"
#include "common/file.h"
#include "sound/voc.h"
namespace Saga {
SndRes::SndRes(SagaEngine *vm) : _vm(vm) {
/* Load sound module resource file contexts */
_sfx_ctxt = _vm->getFileContext(GAME_SOUNDFILE, 0);
if (_sfx_ctxt == NULL) {
return;
}
_voice_ctxt = _vm->getFileContext(GAME_VOICEFILE, 0);
if (_voice_ctxt == NULL) {
return;
}
// Grab sound resource information for the current game
_snd_info = _vm->getSoundInfo();
_init = 1;
}
int SndRes::playSound(uint32 sound_rn, int volume, bool loop) {
SOUNDBUFFER snd_buffer;
debug(0, "SndRes::playSound(%ld)", sound_rn);
if (load(_sfx_ctxt, sound_rn, &snd_buffer) != SUCCESS) {
debug(0, "Failed to load sound");
return FAILURE;
}
_vm->_sound->playSound(&snd_buffer, volume, loop);
return SUCCESS;
}
int SndRes::playVoice(uint32 voice_rn) {
SOUNDBUFFER snd_buffer;
int result = FAILURE;
bool voiceFile = false;
debug(0, "SndRes::playVoice(%ld)", voice_rn);
if (_vm->_gameType == GType_ITE && voice_rn == 4) {
// The Wyrmkeep release of Inherit the Earth provides a
// separate file (p2_a.voc or P2_A.iaf), to correct voice 4 in
// the intro. Use that, if available.
File f;
uint32 size;
bool voc = false;
if (f.open("p2_a.voc"))
voc = true;
else
f.open("P2_A.iaf");
if (f.isOpen()) {
size = f.size();
byte *snd_res = (byte *)malloc(size);
f.read(snd_res, size);
f.close();
if (!voc) {
snd_buffer.s_stereo = 0;
snd_buffer.s_samplebits = 16;
snd_buffer.s_freq = 22050;
snd_buffer.s_buf = snd_res;
snd_buffer.s_buf_len = size;
snd_buffer.s_signed = 1;
result = SUCCESS;
} else {
result = loadVocSound(snd_res, size, &snd_buffer);
RSC_FreeResource(snd_res);
}
voiceFile = true;
}
}
// File is not present. It is DOS CD or Floppy
if (result != SUCCESS)
result = load(_voice_ctxt, voice_rn, &snd_buffer);
if (result != SUCCESS) {
return FAILURE;
}
if (_vm->_features & GF_VOX_VOICES && !voiceFile)
_vm->_sound->playVoxVoice(&snd_buffer);
else
_vm->_sound->playVoice(&snd_buffer);
return SUCCESS;
}
int SndRes::load(RSCFILE_CONTEXT *snd_ctxt, uint32 snd_rn, SOUNDBUFFER *snd_buf_i) {
byte *snd_res;
size_t snd_res_len;
int result;
assert((snd_ctxt != NULL) && (snd_buf_i != NULL));
result = RSC_LoadResource(snd_ctxt, snd_rn, &snd_res, &snd_res_len);
if (result != SUCCESS) {
return FAILURE;
}
switch (_snd_info.res_type) {
case GAME_SOUND_PCM:
snd_buf_i->s_freq = _snd_info.freq;
snd_buf_i->s_samplebits = _snd_info.sample_size;
snd_buf_i->s_stereo = _snd_info.stereo;
snd_buf_i->s_buf = snd_res;
snd_buf_i->s_buf_len = snd_res_len;
snd_buf_i->s_signed = 1;
break;
case GAME_SOUND_VOC:
result = loadVocSound(snd_res, snd_res_len, snd_buf_i);
RSC_FreeResource(snd_res);
if (result != SUCCESS) {
return FAILURE;
}
break;
case GAME_SOUND_WAV:
result = loadWavSound(snd_res, snd_res_len, snd_buf_i);
RSC_FreeResource(snd_res);
if (result != SUCCESS) {
return FAILURE;
}
break;
default:
/* Unknown sound type */
RSC_FreeResource(snd_res);
return FAILURE;
}
return SUCCESS;
}
int SndRes::loadVocSound(byte *snd_res, size_t snd_res_len, SOUNDBUFFER *snd_buf_i) {
MemoryReadStreamEndian readS(snd_res, snd_res_len, IS_BIG_ENDIAN);
byte *data;
int rate;
int len;
data = loadVOCFromStream(readS, len, rate);
if (!data) {
return FAILURE;
}
snd_buf_i->s_freq = rate;
snd_buf_i->s_samplebits = 8;
snd_buf_i->s_stereo = 0;
snd_buf_i->s_signed = 0;
snd_buf_i->s_buf = data;
snd_buf_i->s_buf_len = len;
return SUCCESS;
}
int SndRes::loadWavSound(byte *snd_res, size_t snd_res_len, SOUNDBUFFER *snd_buf_i) {
// TODO: This function should, perhaps, be made more robust.
MemoryReadStreamEndian readS(snd_res, snd_res_len, IS_BIG_ENDIAN);
byte buf[4];
readS.read(buf, sizeof(buf));
if (memcmp(buf, "RIFF", sizeof(buf)) != 0) {
return FAILURE;
}
readS.readUint32();
readS.read(buf, sizeof(buf));
if (memcmp(buf, "WAVE", sizeof(buf)) != 0) {
return FAILURE;
}
readS.read(buf, sizeof(buf));
if (memcmp(buf, "fmt ", sizeof(buf)) != 0) {
return FAILURE;
}
uint32 len = readS.readUint32();
uint32 pos = readS.pos();
readS.readUint16();
snd_buf_i->s_stereo = (readS.readUint16() == 2) ? 1 : 0;
snd_buf_i->s_freq = readS.readUint16();
snd_buf_i->s_samplebits = 16;
snd_buf_i->s_signed = 1;
readS.seek(pos + len);
for (;;) {
readS.read(buf, sizeof(buf));
if (memcmp(buf, "data", sizeof(buf)) == 0) {
break;
}
len = readS.readUint32();
readS.seek(len, SEEK_CUR);
}
snd_buf_i->s_buf_len = readS.readUint32();
byte *data = (byte *)malloc(snd_buf_i->s_buf_len);
if (!data) {
return FAILURE;
}
readS.read(data, snd_buf_i->s_buf_len);
snd_buf_i->s_buf = data;
return SUCCESS;
}
int SndRes::getVoiceLength(uint32 voice_rn) {
int res_type = _snd_info.res_type;
uint32 length = 0;
bool voiceFile = false;
double ms_f;
int ms_i = -1;
int result = FAILURE;
assert(_init);
File f;
// The Wyrmkeep release of Inherit the Earth provides a separate file
// (p2_a.voc or P2_A.iaf), to correct voice 4 in the intro. Use that,
// if available.
if (_vm->_gameType == GType_ITE && voice_rn == 4) {
if (f.open("p2_a.voc")) {
result = SUCCESS;
length = f.size();
res_type = GAME_SOUND_VOC;
f.close();
voiceFile = true;
} else if (f.open("P2_A.iaf")) {
result = SUCCESS;
length = f.size();
res_type = GAME_SOUND_PCM;
f.close();
voiceFile = true;
}
}
if (result == FAILURE) {
result = RSC_GetResourceSize(_voice_ctxt, voice_rn, &length);
if (result != SUCCESS) {
return -1;
}
}
if (_vm->_features & GF_VOX_VOICES && !voiceFile) {
// Rough hack, fix this to be accurate
ms_f = (double)length / 22050 * 2000.0;
ms_i = (int)ms_f;
} else if (res_type == GAME_SOUND_PCM) {
ms_f = (double)length / (_snd_info.sample_size / CHAR_BIT) / (_snd_info.freq) * 1000.0;
ms_i = (int)ms_f;
} else if (res_type == GAME_SOUND_VOC) {
// Rough hack, fix this to be accurate
ms_f = (double)length / 14705 * 1000.0;
ms_i = (int)ms_f;
} else if (res_type == GAME_SOUND_WAV) {
// IHNM does not use the same format for all its WAV sounds, so
// we just have to load the stupid thing and see what that will
// tell us about it.
SOUNDBUFFER snd_buffer;
result = load(_voice_ctxt, voice_rn, &snd_buffer);
if (result != SUCCESS) {
return -1;
}
free(snd_buffer.s_buf);
ms_f = (double)snd_buffer.s_buf_len;
if (snd_buffer.s_samplebits == 16) {
ms_f /= 2.0;
}
if (snd_buffer.s_stereo) {
ms_f /= 2.0;
}
ms_f = ms_f / snd_buffer.s_freq * 1000.0;
ms_i = (int)ms_f;
} else {
return -1;
}
return ms_i;
}
} // End of namespace Saga