2004-04-12 21:40:49 +00:00
|
|
|
/* 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$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// Sound resource management class
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/saga.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/game_mod.h"
|
|
|
|
#include "saga/rscfile_mod.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/sndres.h"
|
|
|
|
#include "saga/sound.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-17 16:11:04 +00:00
|
|
|
#include "common/file.h"
|
|
|
|
|
2004-04-12 21:40:49 +00:00
|
|
|
namespace Saga {
|
|
|
|
|
2004-07-31 23:00:48 +00:00
|
|
|
SndRes::SndRes(SagaEngine *vm) : _vm(vm) {
|
2004-04-12 21:40:49 +00:00
|
|
|
int result;
|
|
|
|
|
2004-04-28 22:21:51 +00:00
|
|
|
/* Load sound module resource file contexts */
|
2004-10-27 21:32:28 +00:00
|
|
|
result = GAME_GetFileContext(&_sfx_ctxt, GAME_SOUNDFILE, 0);
|
|
|
|
if (result != SUCCESS) {
|
2004-04-28 23:10:59 +00:00
|
|
|
return;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
result = GAME_GetFileContext(&_voice_ctxt, GAME_VOICEFILE, 0);
|
|
|
|
if (result != SUCCESS) {
|
2004-04-28 23:10:59 +00:00
|
|
|
return;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// Grab sound resource information for the current game
|
2004-04-28 23:10:59 +00:00
|
|
|
GAME_GetSoundInfo(&_snd_info);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-04-28 23:10:59 +00:00
|
|
|
_init = 1;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-19 17:12:53 +00:00
|
|
|
int SndRes::playSound(uint32 sound_rn, int volume) {
|
2004-10-27 21:32:28 +00:00
|
|
|
SOUNDBUFFER snd_buffer;
|
2004-10-19 17:12:53 +00:00
|
|
|
|
|
|
|
debug(0, "SndRes::playSound(%ld)", sound_rn);
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (load(_sfx_ctxt, sound_rn, &snd_buffer) != SUCCESS) {
|
|
|
|
debug(0, "Failed to load sound");
|
|
|
|
return FAILURE;
|
2004-10-19 17:12:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_vm->_sound->playSound(&snd_buffer, volume);
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-10-19 17:12:53 +00:00
|
|
|
}
|
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
int SndRes::playVoice(uint32 voice_rn) {
|
2004-10-27 21:32:28 +00:00
|
|
|
SOUNDBUFFER snd_buffer;
|
2004-04-12 21:40:49 +00:00
|
|
|
int result;
|
|
|
|
|
2004-04-29 01:24:18 +00:00
|
|
|
debug(0, "SndRes::playVoice(%ld)", voice_rn);
|
|
|
|
|
2004-10-01 06:45:13 +00:00
|
|
|
if (GAME_GetGameType() == GID_ITE && voice_rn == 4) {
|
2004-10-10 12:27:43 +00:00
|
|
|
// 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.
|
2004-10-01 06:45:13 +00:00
|
|
|
|
|
|
|
File f;
|
|
|
|
uint32 size;
|
|
|
|
bool voc = false;
|
|
|
|
|
|
|
|
if (f.open("p2_a.voc"))
|
|
|
|
voc = true;
|
|
|
|
else
|
|
|
|
f.open("P2_A.iaf");
|
|
|
|
|
|
|
|
if (!f.isOpen())
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-10-01 06:45:13 +00:00
|
|
|
|
|
|
|
size = f.size();
|
2004-05-17 16:11:04 +00:00
|
|
|
byte *snd_res = (byte *)malloc(size);
|
|
|
|
f.read(snd_res, size);
|
|
|
|
f.close();
|
2004-10-01 06:45:13 +00:00
|
|
|
|
|
|
|
if (!voc) {
|
|
|
|
snd_buffer.s_stereo = 0;
|
|
|
|
snd_buffer.s_samplebits = 16;
|
|
|
|
snd_buffer.s_freq = 22050;
|
2004-10-10 12:27:43 +00:00
|
|
|
snd_buffer.s_buf = snd_res;
|
|
|
|
snd_buffer.s_buf_len = size;
|
2004-10-01 06:45:13 +00:00
|
|
|
snd_buffer.s_signed = 1;
|
2004-10-27 21:32:28 +00:00
|
|
|
result = SUCCESS;
|
2004-10-10 12:27:43 +00:00
|
|
|
} else {
|
2004-10-01 06:45:13 +00:00
|
|
|
result = loadVocSound(snd_res, size, &snd_buffer);
|
2004-10-10 12:27:43 +00:00
|
|
|
RSC_FreeResource(snd_res);
|
|
|
|
}
|
2004-05-17 16:11:04 +00:00
|
|
|
} else
|
|
|
|
result = load(_voice_ctxt, voice_rn, &snd_buffer);
|
2004-04-30 17:49:33 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result != SUCCESS) {
|
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-04-28 23:54:40 +00:00
|
|
|
_vm->_sound->playVoice(&snd_buffer);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
int SndRes::load(RSCFILE_CONTEXT *snd_ctxt, uint32 snd_rn, SOUNDBUFFER *snd_buf_i) {
|
2004-04-30 23:02:23 +00:00
|
|
|
byte *snd_res;
|
2004-04-12 21:40:49 +00:00
|
|
|
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);
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result != SUCCESS) {
|
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-04-28 23:10:59 +00:00
|
|
|
switch (_snd_info.res_type) {
|
2004-10-27 21:32:28 +00:00
|
|
|
case GAME_SOUND_PCM:
|
2004-04-28 23:10:59 +00:00
|
|
|
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;
|
2004-04-12 21:40:49 +00:00
|
|
|
snd_buf_i->s_buf = snd_res;
|
|
|
|
snd_buf_i->s_buf_len = snd_res_len;
|
|
|
|
snd_buf_i->s_signed = 1;
|
|
|
|
break;
|
2004-10-27 21:32:28 +00:00
|
|
|
case GAME_SOUND_VOC:
|
2004-10-10 12:27:43 +00:00
|
|
|
result = loadVocSound(snd_res, snd_res_len, snd_buf_i);
|
|
|
|
RSC_FreeResource(snd_res);
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result != SUCCESS) {
|
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Unknown sound type */
|
|
|
|
RSC_FreeResource(snd_res);
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
int SndRes::loadVocSound(byte *snd_res, size_t snd_res_len, SOUNDBUFFER *snd_buf_i) {
|
|
|
|
VOC_HEADER_BLOCK voc_hb;
|
|
|
|
VOC_GENBLOCK voc_gb;
|
|
|
|
VOC_BLOCK1 voc_b1;
|
2004-10-10 12:27:43 +00:00
|
|
|
byte *data;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
long byte_rate;
|
2004-05-04 03:33:03 +00:00
|
|
|
size_t i;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (snd_res_len < VOC_HEADER_BLOCK_LEN) {
|
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:24:22 +00:00
|
|
|
MemoryReadStream readS(snd_res, snd_res_len);
|
2004-05-04 03:33:03 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
for (i = 0; i < VOC_FILE_DESC_LEN; i++)
|
2004-08-01 23:24:22 +00:00
|
|
|
voc_hb.ft_desc[i] = readS.readByte();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (memcmp(voc_hb.ft_desc, VOC_FILE_DESC, VOC_FILE_DESC_LEN) != 0) {
|
2004-04-12 21:40:49 +00:00
|
|
|
/* Voc file desc string not found */
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:24:22 +00:00
|
|
|
voc_hb.db_offset = readS.readUint16LE();
|
|
|
|
voc_hb.voc_version = readS.readUint16LE();
|
|
|
|
voc_hb.voc_fileid = readS.readUint16LE();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if ((int32)(snd_res_len - readS.pos()) < (int32)(voc_hb.db_offset + VOC_GENBLOCK_LEN)) {
|
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:24:22 +00:00
|
|
|
while (readS.pos() < voc_hb.db_offset)
|
|
|
|
readS.readByte();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
for (;;) {
|
2004-04-28 22:21:51 +00:00
|
|
|
/* Read generic block header */
|
2004-10-27 21:32:28 +00:00
|
|
|
if (snd_res_len - readS.pos() < VOC_GENBLOCK_LEN) {
|
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:24:22 +00:00
|
|
|
voc_gb.block_id = readS.readByte();
|
2004-04-12 21:40:49 +00:00
|
|
|
if (voc_gb.block_id == 0) {
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:24:22 +00:00
|
|
|
voc_gb.block_len = readS.readUint24LE();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-04-28 22:21:51 +00:00
|
|
|
/* Process block */
|
2004-04-12 21:40:49 +00:00
|
|
|
switch (voc_gb.block_id) {
|
|
|
|
case 1: /* Sound data block */
|
2004-08-01 23:24:22 +00:00
|
|
|
voc_b1.time_constant = readS.readByte();
|
|
|
|
voc_b1.pack_method = readS.readByte();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
if (voc_b1.pack_method != 0) {
|
2004-04-28 23:10:59 +00:00
|
|
|
debug(0, "Packed VOC files not supported");
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
byte_rate = VOC_TIME_BASE / (VOC_TIME_CBASE - (voc_b1.time_constant << 8));
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
snd_buf_i->s_stereo = 0;
|
|
|
|
snd_buf_i->s_samplebits = 8;
|
|
|
|
snd_buf_i->s_freq = byte_rate;
|
2004-10-10 12:27:43 +00:00
|
|
|
snd_buf_i->s_buf_len = snd_res_len - readS.pos() - 1; /* -1 for end block */
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-10 12:27:43 +00:00
|
|
|
data = (byte *)malloc(snd_buf_i->s_buf_len);
|
|
|
|
if (!data) {
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-10-10 12:27:43 +00:00
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-10 12:27:43 +00:00
|
|
|
readS.read(data, snd_buf_i->s_buf_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-10 12:27:43 +00:00
|
|
|
snd_buf_i->s_buf = data;
|
2004-04-12 21:40:49 +00:00
|
|
|
snd_buf_i->s_signed = 0;
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
default:
|
2004-05-04 03:33:03 +00:00
|
|
|
for (i = 0; i < voc_gb.block_len; i++)
|
2004-08-01 23:24:22 +00:00
|
|
|
readS.readByte();
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
int SndRes::getVoiceLength(uint32 voice_rn) {
|
2004-05-17 16:11:04 +00:00
|
|
|
int res_type = _snd_info.res_type;
|
2004-10-16 20:38:37 +00:00
|
|
|
uint32 length = 0;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
double ms_f;
|
|
|
|
int ms_i = -1;
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
int result = FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-04-28 23:10:59 +00:00
|
|
|
assert(_init);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-17 16:11:04 +00:00
|
|
|
File f;
|
|
|
|
|
2004-10-10 12:27:43 +00:00
|
|
|
// 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.
|
2004-05-17 16:11:04 +00:00
|
|
|
|
2004-10-10 12:27:43 +00:00
|
|
|
if (GAME_GetGameType() == GID_ITE && voice_rn == 4) {
|
|
|
|
if (f.open("p2_a.voc")) {
|
2004-10-27 21:32:28 +00:00
|
|
|
result = SUCCESS;
|
2004-10-10 12:27:43 +00:00
|
|
|
length = f.size();
|
2004-10-27 21:32:28 +00:00
|
|
|
res_type = GAME_SOUND_VOC;
|
2004-10-10 12:27:43 +00:00
|
|
|
f.close();
|
|
|
|
} else if (f.open("P2_A.iaf")) {
|
2004-10-27 21:32:28 +00:00
|
|
|
result = SUCCESS;
|
2004-10-10 12:27:43 +00:00
|
|
|
length = f.size();
|
2004-10-27 21:32:28 +00:00
|
|
|
res_type = GAME_SOUND_PCM;
|
2004-10-10 12:27:43 +00:00
|
|
|
f.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result == FAILURE) {
|
2004-05-17 16:11:04 +00:00
|
|
|
result = RSC_GetResourceSize(_voice_ctxt, voice_rn, &length);
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result != SUCCESS) {
|
2004-05-17 16:11:04 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (res_type == GAME_SOUND_PCM) {
|
2004-05-01 16:15:55 +00:00
|
|
|
ms_f = (double)length / (_snd_info.sample_size / CHAR_BIT) / (_snd_info.freq) * 1000.0;
|
2004-04-12 21:40:49 +00:00
|
|
|
ms_i = (int)ms_f;
|
2004-10-27 21:32:28 +00:00
|
|
|
} else if (res_type == GAME_SOUND_VOC) {
|
2004-05-01 16:15:55 +00:00
|
|
|
// Rough hack, fix this to be accurate
|
2004-04-12 21:40:49 +00:00
|
|
|
ms_f = (double)length / 14705 * 1000.0;
|
|
|
|
ms_i = (int)ms_f;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ms_i;
|
|
|
|
}
|
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
int SndRes::ITEVOC_Resample(long src_freq, long dst_freq, byte *src_buf,
|
|
|
|
size_t src_buf_len, byte **dst_buf, size_t *dst_buf_len) {
|
|
|
|
byte *resamp_buf;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t resamp_len;
|
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
byte src_samp_a;
|
|
|
|
byte src_samp_b;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
const byte *read_pa;
|
|
|
|
const byte *read_pb;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
byte *write_pa;
|
|
|
|
byte *write_pb;
|
|
|
|
byte *write_pc;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
size_t src_i;
|
|
|
|
|
|
|
|
assert(src_freq == 14705);
|
|
|
|
assert(dst_freq == 22050);
|
|
|
|
|
|
|
|
resamp_len = (size_t) (src_buf_len * 1.5);
|
2004-04-30 23:02:23 +00:00
|
|
|
resamp_buf = (byte *)malloc(resamp_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (resamp_buf == NULL) {
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
read_pa = src_buf;
|
|
|
|
read_pb = src_buf + 1;
|
|
|
|
|
|
|
|
write_pa = resamp_buf;
|
|
|
|
write_pb = resamp_buf + 1;
|
|
|
|
write_pc = resamp_buf + 2;
|
|
|
|
|
|
|
|
for (src_i = 0; src_i < src_buf_len / 2; src_i++) {
|
|
|
|
src_samp_a = *read_pa;
|
|
|
|
src_samp_b = *read_pb;
|
|
|
|
|
|
|
|
read_pa += 2;
|
|
|
|
read_pb += 2;
|
|
|
|
|
|
|
|
*write_pa = src_samp_a;
|
2004-04-30 23:02:23 +00:00
|
|
|
*write_pb = (byte) ((src_samp_a / 2) + (src_samp_b / 2));
|
2004-04-12 21:40:49 +00:00
|
|
|
*write_pc = src_samp_b;
|
|
|
|
|
|
|
|
write_pa += 3;
|
|
|
|
write_pb += 3;
|
|
|
|
write_pc += 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
*dst_buf = resamp_buf;
|
|
|
|
*dst_buf_len = resamp_len;
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Saga
|