aboutsummaryrefslogtreecommitdiff
path: root/saga/sndres.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'saga/sndres.cpp')
-rw-r--r--saga/sndres.cpp353
1 files changed, 353 insertions, 0 deletions
diff --git a/saga/sndres.cpp b/saga/sndres.cpp
new file mode 100644
index 0000000000..3592095578
--- /dev/null
+++ b/saga/sndres.cpp
@@ -0,0 +1,353 @@
+/* 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$
+ *
+ */
+/*
+ Description:
+
+ Sound resource management module
+
+ Notes:
+*/
+
+#include "reinherit.h"
+
+#include "yslib.h"
+
+#include <limits.h>
+
+/*
+ * Uses the following modules:
+\*--------------------------------------------------------------------------*/
+#include "game_mod.h"
+#include "rscfile_mod.h"
+
+/*
+ * Begin module component
+\*--------------------------------------------------------------------------*/
+#include "sndres_mod.h"
+#include "sndres.h"
+
+namespace Saga {
+
+R_SNDRES_MODULE SndModule;
+
+int SND_Init(void)
+{
+ int result;
+
+ /* Load sound module resource file contexts
+ * \*------------------------------------------------------------- */
+ result = GAME_GetFileContext(&SndModule.sfx_ctxt, R_GAME_SOUNDFILE, 0);
+ if (result != R_SUCCESS) {
+ return R_FAILURE;
+ }
+
+ result = GAME_GetFileContext(&SndModule.voice_ctxt,
+ R_GAME_VOICEFILE, 0);
+ if (result != R_SUCCESS) {
+ return R_FAILURE;
+ }
+
+ /* Grab sound resource information for the current game
+ * \*------------------------------------------------------------- */
+ GAME_GetSoundInfo(&SndModule.snd_info);
+
+ SndModule.init = 1;
+
+ return R_SUCCESS;
+}
+
+int SND_PlayVoice(ulong voice_rn)
+{
+ R_SOUNDBUFFER snd_buffer;
+ int result;
+
+ result = SND_Load(SndModule.voice_ctxt, voice_rn, &snd_buffer);
+ if (result != R_SUCCESS) {
+ return R_FAILURE;
+ }
+
+ SYSSOUND_PlayVoice(&snd_buffer);
+
+ return R_SUCCESS;
+}
+
+int
+SND_Load(R_RSCFILE_CONTEXT * snd_ctxt, ulong snd_rn, R_SOUNDBUFFER * snd_buf_i)
+{
+
+ uchar *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 != R_SUCCESS) {
+ return R_FAILURE;
+ }
+
+ switch (SndModule.snd_info.res_type) {
+
+ case R_GAME_SOUND_PCM:
+
+ snd_buf_i->s_freq = SndModule.snd_info.freq;
+ snd_buf_i->s_samplebits = SndModule.snd_info.sample_size;
+ snd_buf_i->s_stereo = SndModule.snd_info.stereo;
+
+ snd_buf_i->res_data = snd_res;
+ snd_buf_i->res_len = snd_res_len;
+
+ snd_buf_i->s_buf = snd_res;
+ snd_buf_i->s_buf_len = snd_res_len;
+
+ snd_buf_i->s_signed = 1;
+
+ break;
+
+ case R_GAME_SOUND_VOC:
+
+ if (LoadVocSound(snd_res, snd_res_len, snd_buf_i) != R_SUCCESS) {
+
+ RSC_FreeResource(snd_res);
+
+ return R_FAILURE;
+ }
+
+ break;
+
+ default:
+ /* Unknown sound type */
+
+ RSC_FreeResource(snd_res);
+
+ return R_FAILURE;
+ break;
+ }
+
+ return R_SUCCESS;
+}
+
+int
+LoadVocSound(const uchar * snd_res,
+ size_t snd_res_len, R_SOUNDBUFFER * snd_buf_i)
+{
+
+ R_VOC_HEADER_BLOCK voc_hb;
+ R_VOC_GENBLOCK voc_gb;
+ R_VOC_BLOCK1 voc_b1;
+
+ long byte_rate;
+
+ const uchar *read_p;
+ size_t read_len;
+
+ read_p = snd_res;
+ read_len = snd_res_len;
+
+ if (read_len < R_VOC_HEADER_BLOCK_LEN) {
+ return R_FAILURE;
+ }
+
+ memcpy(voc_hb.ft_desc, read_p, R_VOC_FILE_DESC_LEN);
+ read_p += R_VOC_FILE_DESC_LEN;
+ read_len -= R_VOC_FILE_DESC_LEN;
+
+ if (memcmp(voc_hb.ft_desc, R_VOC_FILE_DESC, R_VOC_FILE_DESC_LEN) != 0) {
+
+ /* Voc file desc string not found */
+ return R_FAILURE;
+ }
+
+ voc_hb.db_offset = ys_read_u16_le(read_p, &read_p);
+ voc_hb.voc_version = ys_read_u16_le(read_p, &read_p);
+ voc_hb.voc_fileid = ys_read_u16_le(read_p, &read_p);
+
+ if (read_len < voc_hb.db_offset + R_VOC_GENBLOCK_LEN) {
+
+ return R_FAILURE;
+ }
+
+ read_p = snd_res + voc_hb.db_offset;
+ read_len = snd_res_len - voc_hb.db_offset;
+
+ for (;;) {
+
+ /* Read generic block header
+ * \*--------------------------------------------------------- */
+ if (read_len < R_VOC_GENBLOCK_LEN) {
+ return R_FAILURE;
+ }
+
+ voc_gb.block_id = ys_read_u8(read_p, &read_p);
+ if (voc_gb.block_id == 0) {
+ return R_FAILURE;
+ }
+
+ voc_gb.block_len = ys_read_u24_le(read_p, &read_p);
+
+ read_len -= R_VOC_GENBLOCK_LEN;
+
+ /* Process block
+ * \*--------------------------------------------------------- */
+ switch (voc_gb.block_id) {
+
+ case 1: /* Sound data block */
+
+ voc_b1.time_constant = ys_read_u8(read_p, &read_p);
+ voc_b1.pack_method = ys_read_u8(read_p, &read_p);
+ read_len -= 2;
+
+ if (voc_b1.pack_method != 0) {
+ /* Packed VOC files not supported */
+ return R_FAILURE;
+ }
+
+ byte_rate = R_VOC_TIME_BASE / (R_VOC_TIME_CBASE -
+ (voc_b1.time_constant << 8));
+
+ snd_buf_i->s_stereo = 0;
+ snd_buf_i->s_samplebits = 8;
+ snd_buf_i->s_freq = byte_rate;
+
+ snd_buf_i->res_data = (uchar *) snd_res;
+ snd_buf_i->res_len = snd_res_len;
+
+ snd_buf_i->s_buf = (uchar *) read_p;
+ snd_buf_i->s_buf_len = read_len - 1; /* -1 for end block */
+
+ snd_buf_i->s_signed = 0;
+
+ return R_SUCCESS;
+
+ break;
+
+ default:
+
+ read_p += voc_gb.block_len;
+ read_len -= voc_gb.block_len;
+ break;
+ }
+ }
+
+ return R_SUCCESS;
+}
+
+int SND_GetVoiceLength(ulong voice_rn)
+{
+
+ ulong length;
+
+ double ms_f;
+ int ms_i = -1;
+
+ int result;
+
+ assert(SndModule.init);
+
+ result = RSC_GetResourceSize(SndModule.voice_ctxt, voice_rn, &length);
+ if (result != R_SUCCESS) {
+ return -1;
+ }
+
+ if (SndModule.snd_info.res_type == R_GAME_SOUND_PCM) {
+
+ ms_f = (double)length /
+ (SndModule.snd_info.sample_size / CHAR_BIT) /
+ (SndModule.snd_info.freq) * 1000.0;
+
+ ms_i = (int)ms_f;
+ } else if (SndModule.snd_info.res_type == R_GAME_SOUND_VOC) {
+
+ /* Rough hack, fix this to be accurate */
+
+ ms_f = (double)length / 14705 * 1000.0;
+ ms_i = (int)ms_f;
+ } else {
+ return -1;
+ }
+
+ return ms_i;
+}
+
+int
+SND_ITEVOC_Resample(long src_freq,
+ long dst_freq,
+ uchar * src_buf,
+ size_t src_buf_len, uchar ** dst_buf, size_t * dst_buf_len)
+{
+ uchar *resamp_buf;
+ size_t resamp_len;
+
+ uchar src_samp_a;
+ uchar src_samp_b;
+
+ const uchar *read_pa;
+ const uchar *read_pb;
+
+ uchar *write_pa;
+ uchar *write_pb;
+ uchar *write_pc;
+
+ size_t src_i;
+
+ assert(src_freq == 14705);
+ assert(dst_freq == 22050);
+
+ resamp_len = (size_t) (src_buf_len * 1.5);
+ resamp_buf = (uchar *)malloc(resamp_len);
+ if (resamp_buf == NULL) {
+ return R_FAILURE;
+ }
+
+ 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;
+ *write_pb = (uchar) ((src_samp_a / 2) + (src_samp_b / 2));
+ *write_pc = src_samp_b;
+
+ write_pa += 3;
+ write_pb += 3;
+ write_pc += 3;
+ }
+
+ *dst_buf = resamp_buf;
+ *dst_buf_len = resamp_len;
+
+ return R_SUCCESS;
+}
+
+} // End of namespace Saga