aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Bacca2002-04-04 22:47:03 +0000
committerNicolas Bacca2002-04-04 22:47:03 +0000
commitcbad525cd30e266f23cfac8d974650cc52517119 (patch)
tree96fbffe336a4c0de3901364b7e6a215d15a849b8
parent796110715688a8d487b8380e9b115a3b13d1830d (diff)
downloadscummvm-rg350-cbad525cd30e266f23cfac8d974650cc52517119.tar.gz
scummvm-rg350-cbad525cd30e266f23cfac8d974650cc52517119.tar.bz2
scummvm-rg350-cbad525cd30e266f23cfac8d974650cc52517119.zip
MP3 cd music patch - still WIP, VBR doesn't work, compress the audio track X to MP3 CBR and name them trackX.mp3 in the game directory - only tested with Loom
svn-id: r3861
-rw-r--r--cdmusic.h2
-rw-r--r--script_v1.cpp2
-rw-r--r--sdl.cpp306
-rw-r--r--sound.cpp142
-rw-r--r--sound.h18
-rw-r--r--windows.cpp2
-rw-r--r--x11.cpp3
7 files changed, 467 insertions, 8 deletions
diff --git a/cdmusic.h b/cdmusic.h
index f8514504d7..384d59a4e1 100644
--- a/cdmusic.h
+++ b/cdmusic.h
@@ -22,7 +22,7 @@
#define CD_MUSIC_H
void cd_stop();
-void cd_play(int track, int num_loops, int start_frame, int end_track);
+void cd_play(Scumm *s, int track, int num_loops, int start_frame, int end_track);
int cd_is_running();
void cd_music_loop();
diff --git a/script_v1.cpp b/script_v1.cpp
index 0af6c42b8d..786a0b6f16 100644
--- a/script_v1.cpp
+++ b/script_v1.cpp
@@ -2476,7 +2476,7 @@ void Scumm::decodeParseString() {
int delay = (int)(getVarOrDirectWord(0x40) * 7.5) + 10;
if (_gameId == GID_LOOM256)
- cd_play(1, 0, offset, delay);
+ cd_play(this, 1, 0, offset, delay);
else
warning("parseString: 8");
}
diff --git a/sdl.cpp b/sdl.cpp
index 1723e010c4..3f8fcee5ac 100644
--- a/sdl.cpp
+++ b/sdl.cpp
@@ -676,11 +676,211 @@ static int cd_track, cd_num_loops = 0, cd_start_frame, cd_end_frame;
// time the track is expected to be finished.
static Uint32 cd_end_time, cd_stop_time, cd_next_second;
-void cd_play(int track, int num_loops, int start_frame, int end_frame) {
+#ifdef COMPRESSED_SOUND_FILE
+
+// MP3 CD track support
+
+
+// From xing.h in MAD
+
+# define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
+
+struct xing {
+ long flags; /* valid fields (see below) */
+ unsigned long frames; /* total number of frames */
+ unsigned long bytes; /* total number of bytes */
+ unsigned char toc[100]; /* 100-point seek table */
+ long scale; /* ?? */
+};
+
+enum {
+ XING_FRAMES = 0x00000001L,
+ XING_BYTES = 0x00000002L,
+ XING_TOC = 0x00000004L,
+ XING_SCALE = 0x00000008L
+};
+
+
+int _current_mp3_cd_track;
+struct xing _vbr_header;
+struct mad_header _mad_header;
+long _mp3_size;
+FILE *_mp3_track;
+MixerChannel *_mc;
+
+// From xing.c in MAD
+
+int xing_parse(struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
+{
+
+ xing->flags = 0;
+
+ if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC)
+ goto fail;
+
+ xing->flags = mad_bit_read(&ptr, 32);
+ bitlen -= 64;
+
+ if (xing->flags & XING_FRAMES) {
+ if (bitlen < 32)
+ goto fail;
+
+ xing->frames = mad_bit_read(&ptr, 32);
+ bitlen -= 32;
+ }
+
+ if (xing->flags & XING_BYTES) {
+ if (bitlen < 32)
+ goto fail;
+
+ xing->bytes = mad_bit_read(&ptr, 32);
+ bitlen -= 32;
+ }
+
+ if (xing->flags & XING_TOC) {
+ int i;
+
+ if (bitlen < 800)
+ goto fail;
+
+ for (i = 0; i < 100; ++i)
+ xing->toc[i] = (unsigned char)mad_bit_read(&ptr, 8);
+
+ bitlen -= 800;
+ }
+
+ if (xing->flags & XING_SCALE) {
+ if (bitlen < 32)
+ goto fail;
+
+ xing->scale = mad_bit_read(&ptr, 32);
+ bitlen -= 32;
+ }
+
+ return 0;
+
+ fail:
+ xing->flags = 0;
+ return -1;
+}
+
+// Borrowed from Winamp plugin in_mad.c
+
+BOOL parse_xing_vbr_tag()
+{
+ struct mad_stream stream;
+ struct mad_frame frame;
+ unsigned char buffer[8192];
+ unsigned int buflen = 0;
+ int count = 0, result = 0;
+
+ _vbr_header.flags = 0;
+
+ mad_stream_init(&stream);
+ mad_frame_init(&frame);
+
+ fseek(_mp3_track, 0, SEEK_SET);
+
+ while (1) {
+ if (buflen < sizeof(buffer)) {
+ DWORD bytes;
+
+ bytes = fread(buffer + buflen, 1, sizeof(buffer) - buflen, _mp3_track);
+ if (bytes <= 0) {
+ if (bytes == -1)
+ result = -1;
+ break;
+ }
+
+ buflen += bytes;
+ }
+
+ mad_stream_buffer(&stream, buffer, buflen);
+
+ while (1) {
+ if (mad_frame_decode(&frame, &stream) == -1) {
+ if (!MAD_RECOVERABLE(stream.error))
+ break;
+
+ if (stream.error != MAD_ERROR_BADCRC)
+ continue;
+ }
+
+ if (count++ ||
+ xing_parse(&_vbr_header, stream.anc_ptr, stream.anc_bitlen)
+ == -1)
+ break;
+ }
+
+ if (count || stream.error != MAD_ERROR_BUFLEN)
+ break;
+
+ memmove(buffer, stream.next_frame,
+ buflen = &buffer[buflen] - stream.next_frame);
+ }
+
+ if (count)
+ memcpy(&_mad_header, &frame.header, sizeof(mad_header));
+ else
+ result = -1;
+
+ mad_frame_finish(&frame);
+ mad_stream_finish(&stream);
+
+ return (result != -1);
+}
+
+uint32 calc_cd_file_offset(int start_frame) {
+ long offset;
+
+ if (!_vbr_header.flags) {
+ float frame_size;
+ //mad_timer_t timer; - recode with timer
+
+ /* Constant bit rate - perhaps not fully accurate, recheck */
+ frame_size = (float)(144 * _mad_header.bitrate / _mad_header.samplerate);
+ offset = (float)(float)start_frame / (float)CD_FPS * 1000 /
+ (float)((float)1152 / (float)_mad_header.samplerate * 1000) *
+ (float)(frame_size + 0.5);
+ }
+ else {
+ /* DOES NOT WORK AT THE MOMENT */
+ /* see Xing SDK */
+ long a;
+ float fa, fb, fx;
+ float percent = (float)start_frame / (float)CD_FPS * 1000 /
+ ((float)((float)1152 / (float)_mad_header.samplerate * 1000) * _vbr_header.frames) *
+ 100;
+
+ if( percent < 0.0f ) percent = 0.0f;
+ if( percent > 100.0f ) percent = 100.0f;
+
+ a = (int)percent;
+ if( a > 99 ) a = 99;
+ fa = _vbr_header.toc[a];
+ if( a < 99 ) {
+ fb = _vbr_header.toc[a+1];
+ }
+ else {
+ fb = 256.0f;
+ }
+
+ fx = fa + (fb-fa)*(percent-a);
+
+ offset = (int)((1.0f/256.0f)*fx*_vbr_header.bytes);
+
+ }
+
+ return offset;
+}
+
+
+#endif
+
+void real_cd_play(int track, int num_loops, int start_frame, int end_frame) {
// warning("cd_play(%d,%d,%d,%d)", track, num_loops, start_frame, end_frame);
if (!cdrom) return;
- scumm->_vars[14] = 0;
cd_track = track;
cd_num_loops = num_loops;
cd_start_frame = start_frame;
@@ -693,6 +893,108 @@ void cd_play(int track, int num_loops, int start_frame, int end_frame) {
cdrom->track[track].length * 1000 / CD_FPS;
}
+void cd_play(Scumm *s, int track, int num_loops, int start_frame, int end_frame) {
+ scumm->_vars[14] = 0;
+
+#ifdef COMPRESSED_SOUND_FILE
+
+ // See if we are already playing this track, else try to open it
+
+ if (_current_mp3_cd_track != track) {
+ char track_name[1024];
+
+ sprintf(track_name, "%strack%d.mp3", s->_gameDataPath, track);
+ _mp3_track = fopen(track_name, "rb");
+ if (!_mp3_track) {
+ warning("No CD and track %d not available in mp3 format", track);
+ real_cd_play(track, num_loops, start_frame, end_frame);
+ return;
+ }
+
+ if (!parse_xing_vbr_tag()) {
+ warning("Error parsing file header - ignoring file",
+ track);
+ fclose(_mp3_track);
+ real_cd_play(track, num_loops, start_frame, end_frame);
+ return;
+ }
+
+ if (_vbr_header.flags) {
+ if (!(
+ (_vbr_header.flags & XING_TOC) &&
+ (_vbr_header.flags & XING_BYTES) &&
+ (_vbr_header.flags & XING_FRAMES)
+ )) {
+ warning("Missing required part of VBR header - ignoring file");
+ fclose(_mp3_track);
+ _vbr_header.flags = 0;
+ real_cd_play(track, num_loops, start_frame, end_frame);
+ return;
+ }
+ }
+
+ // Allocate the music mixer if necessary
+
+ if (!_mc) {
+ _mc = s->allocateMixer();
+ if (!_mc) {
+ warning("No mixer channel available for MP3 music");
+ real_cd_play(track, num_loops, start_frame, end_frame);
+ return;
+ }
+ }
+
+ fseek(_mp3_track, 0, SEEK_END);
+ _mp3_size = ftell(_mp3_track);
+ fseek(_mp3_track, 0, SEEK_SET);
+
+ _mc->type = MIXER_MP3_CDMUSIC;
+ _mc->sound_data.mp3_cdmusic.file = _mp3_track;
+ _mc->sound_data.mp3_cdmusic.playing = FALSE;
+ _mc->sound_data.mp3_cdmusic.buffer_size = 200000;
+ _mc->_sfx_sound = malloc(_mc->sound_data.mp3_cdmusic.buffer_size);
+
+ /* see if it's enough */
+ mad_stream_init(&_mc->sound_data.mp3.stream);
+ if (_mad_header.samplerate == 44100)
+ mad_stream_options((mad_stream*)&_mc->sound_data.mp3.stream,
+ MAD_OPTION_HALFSAMPLERATE);
+
+ mad_frame_init(&_mc->sound_data.mp3.frame);
+ mad_synth_init(&_mc->sound_data.mp3.synth);
+
+ _current_mp3_cd_track = track;
+ }
+
+
+ if (_current_mp3_cd_track == track) {
+
+ uint32 where;
+
+ // See where we want to go
+ where = calc_cd_file_offset(start_frame);
+
+ if (start_frame < 0 || end_frame < 0) {
+ warning("Negative index in frame");
+ return;
+ }
+
+ mad_timer_set(&_mc->sound_data.mp3_cdmusic.duration,
+ 0,
+ end_frame,
+ CD_FPS);
+
+ fseek(_mp3_track, where, SEEK_SET);
+ _mc->sound_data.mp3_cdmusic.playing = TRUE;
+
+ return;
+ }
+
+#endif
+
+ real_cd_play(track, num_loops, start_frame, end_frame);
+}
+
// Schedule the music to be stopped after 1/10 sec, unless another
// track is started in the meantime. (On my machine, stopping and
// then restarting the CD takes a few seconds.)
diff --git a/sound.cpp b/sound.cpp
index b213179494..ec8b67754b 100644
--- a/sound.cpp
+++ b/sound.cpp
@@ -105,7 +105,7 @@ void Scumm::playSound(int sound) {
ptr = getResourceAddress(rtSound, sound);
if (ptr != NULL && READ_UINT32_UNALIGNED(ptr) == MKID('SOUN')) {
ptr += 8;
- cd_play(ptr[16], ptr[17] == 0xff ? -1 : ptr[17],
+ cd_play(this, ptr[16], ptr[17] == 0xff ? -1 : ptr[17],
(ptr[18] * 60 + ptr[19]) * 75 + ptr[20], 0);
current_cd_sound = sound;
return;
@@ -687,6 +687,7 @@ void MixerChannel::mix(int16 *data, uint32 len) {
clear();
#ifdef COMPRESSED_SOUND_FILE
} else {
+ if (type == MIXER_MP3) {
mad_fixed_t const *ch;
while (1) {
ch = sound_data.mp3.synth.pcm.samples[0] + sound_data.mp3.pos_in_frame;
@@ -725,6 +726,145 @@ void MixerChannel::mix(int16 *data, uint32 len) {
sound_data.mp3.position = (unsigned char *) sound_data.mp3.stream.next_frame - (unsigned char *) _sfx_sound;
}
}
+ else if (type == MIXER_MP3_CDMUSIC) {
+ mad_fixed_t const *ch;
+ mad_timer_t frame_duration;
+ static long last_pos = 0;
+
+ if (!sound_data.mp3_cdmusic.playing)
+ return;
+
+ while (1) {
+
+ // See if we just skipped
+ if (ftell(sound_data.mp3_cdmusic.file) != last_pos) {
+ int skip_loop;
+
+ // Read the new data
+ memset(_sfx_sound, 0, sound_data.mp3_cdmusic.buffer_size + MAD_BUFFER_GUARD);
+ sound_data.mp3_cdmusic.size =
+ fread(_sfx_sound, 1, sound_data.mp3_cdmusic.buffer_size,
+ sound_data.mp3_cdmusic.file);
+ if (!sound_data.mp3_cdmusic.size) {
+ sound_data.mp3_cdmusic.playing = FALSE;
+ return;
+ }
+ last_pos = ftell(sound_data.mp3_cdmusic.file);
+ // Resync
+ mad_stream_buffer(&sound_data.mp3_cdmusic.stream,
+ (unsigned char*)_sfx_sound,
+ sound_data.mp3_cdmusic.size
+ );
+ skip_loop = 2;
+ while (skip_loop != 0) {
+ if (mad_frame_decode(&sound_data.mp3_cdmusic.frame,
+ &sound_data.mp3_cdmusic.stream) == 0) {
+ /* Do not decrease duration - see if it's a problem */
+ skip_loop--;
+ if (skip_loop == 0) {
+ mad_synth_frame(&sound_data.mp3_cdmusic.synth,
+ &sound_data.mp3_cdmusic.frame);
+ }
+ }
+ else {
+ if (!MAD_RECOVERABLE(sound_data.mp3_cdmusic.stream.error)) {
+ debug(1, "Unrecoverable error while skipping !");
+ sound_data.mp3_cdmusic.playing = FALSE;
+ return;
+ }
+ }
+ }
+ // We are supposed to be in synch
+ mad_frame_mute(&sound_data.mp3_cdmusic.frame);
+ mad_synth_mute(&sound_data.mp3_cdmusic.synth);
+ // Resume decoding
+ if (mad_frame_decode(&sound_data.mp3_cdmusic.frame,
+ &sound_data.mp3_cdmusic.stream) == 0) {
+ sound_data.mp3_cdmusic.position =
+ (unsigned char *)sound_data.mp3_cdmusic.stream.next_frame -
+ (unsigned char *)_sfx_sound;
+ sound_data.mp3_cdmusic.pos_in_frame = 0;
+ }
+ else {
+ sound_data.mp3_cdmusic.playing = FALSE;
+ return;
+ }
+ }
+ // Get samples, play samples ...
+
+ ch = sound_data.mp3_cdmusic.synth.pcm.samples[0] +
+ sound_data.mp3_cdmusic.pos_in_frame;
+ while ((sound_data.mp3_cdmusic.pos_in_frame <
+ sound_data.mp3_cdmusic.synth.pcm.length) &&
+ (len > 0)) {
+ *data++ += scale_sample(*ch++);
+ len--;
+ sound_data.mp3_cdmusic.pos_in_frame++;
+ }
+ if (len == 0) {
+ return;
+ }
+
+ // See if we have finished
+ // May be incorrect to check the size at the end of a frame but I suppose
+ // they are short enough :)
+
+ frame_duration = sound_data.mp3_cdmusic.frame.header.duration;
+
+ mad_timer_negate(&frame_duration);
+ mad_timer_add(&sound_data.mp3_cdmusic.duration, frame_duration);
+ if (mad_timer_compare(sound_data.mp3_cdmusic.duration, mad_timer_zero) < 0) {
+ sound_data.mp3_cdmusic.playing = FALSE;
+ }
+
+ if (mad_frame_decode(&sound_data.mp3_cdmusic.frame,
+ &sound_data.mp3_cdmusic.stream) == -1) {
+
+ if (sound_data.mp3_cdmusic.stream.error == MAD_ERROR_BUFLEN) {
+ int not_decoded;
+
+ if (!sound_data.mp3_cdmusic.stream.next_frame) {
+ memset(_sfx_sound, 0, sound_data.mp3_cdmusic.buffer_size + MAD_BUFFER_GUARD);
+ sound_data.mp3_cdmusic.size =
+ fread(_sfx_sound, 1, sound_data.mp3_cdmusic.buffer_size,
+ sound_data.mp3_cdmusic.file);
+ sound_data.mp3_cdmusic.position = 0;
+ not_decoded = 0;
+ }
+ else {
+ not_decoded = sound_data.mp3_cdmusic.stream.bufend -
+ sound_data.mp3_cdmusic.stream.next_frame;
+ memcpy(_sfx_sound, sound_data.mp3_cdmusic.stream.next_frame,
+ not_decoded);
+
+ sound_data.mp3_cdmusic.size =
+ fread((unsigned char*)_sfx_sound + not_decoded, 1,
+ sound_data.mp3_cdmusic.buffer_size - not_decoded,
+ sound_data.mp3_cdmusic.file);
+ }
+ last_pos = ftell(sound_data.mp3_cdmusic.file);
+ sound_data.mp3_cdmusic.stream.error = MAD_ERROR_NONE;
+ // Restream
+ mad_stream_buffer(&sound_data.mp3_cdmusic.stream,
+ (unsigned char*)_sfx_sound,
+ sound_data.mp3_cdmusic.size + not_decoded
+ );
+ if (mad_frame_decode(&sound_data.mp3_cdmusic.frame, &sound_data.mp3_cdmusic.stream) == -1) {
+ debug(1, "Error decoding after restream %d !", sound_data.mp3.stream.error);
+ }
+ } else if (!MAD_RECOVERABLE(sound_data.mp3.stream.error)) {
+ error("MAD frame decode error in MP3 CDMUSIC !");
+ }
+ }
+
+ mad_synth_frame(&sound_data.mp3_cdmusic.synth, &sound_data.mp3_cdmusic.frame);
+ sound_data.mp3_cdmusic.pos_in_frame = 0;
+ sound_data.mp3_cdmusic.position =
+ (unsigned char *)sound_data.mp3_cdmusic.stream.next_frame -
+ (unsigned char *)_sfx_sound;
+ }
+ }
+ }
#endif
}
diff --git a/sound.h b/sound.h
index e54aab8875..6d59e9626a 100644
--- a/sound.h
+++ b/sound.h
@@ -17,6 +17,9 @@
*
* Change Log:
* $Log$
+ * Revision 1.10 2002/04/04 22:47:03 arisme
+ * MP3 cd music patch - still WIP, VBR doesn't work, compress the audio track X to MP3 CBR and name them trackX.mp3 in the game directory - only tested with Loom
+ *
* Revision 1.9 2002/03/21 16:12:02 ender
* Move some box stuff from scumm.h to new boxes.h
* Also move some sound-related items from scumm.h to sound.h
@@ -63,7 +66,8 @@ struct OffsetTable { /* Compressed Sound (.SO3) */
typedef enum { /* Mixer types */
MIXER_STANDARD,
- MIXER_MP3
+ MIXER_MP3,
+ MIXER_MP3_CDMUSIC
} MixerType;
struct MixerChannel { /* Mixer Channel */
@@ -86,6 +90,18 @@ struct MixerChannel { /* Mixer Channel */
uint32 position;
uint32 size;
} mp3;
+ struct {
+ struct mad_stream stream;
+ struct mad_frame frame;
+ struct mad_synth synth;
+ uint32 pos_in_frame;
+ uint32 position;
+ uint32 size;
+ uint32 buffer_size;
+ mad_timer_t duration;
+ BOOL playing;
+ FILE *file;
+ } mp3_cdmusic;
#endif
} sound_data;
void mix(int16 *data, uint32 len);
diff --git a/windows.cpp b/windows.cpp
index 3841d0d8a1..10afbec668 100644
--- a/windows.cpp
+++ b/windows.cpp
@@ -125,7 +125,7 @@ int mapKey(int key) {
// FIXME: CD Music Stubs
void cd_playtrack(int track, int offset, int delay) {;}
-void cd_play(int track, int num_loops, int start_frame) {;}
+void cd_play(Scumm *s, int track, int num_loops, int start_frame) {;}
void cd_stop() {;}
int cd_is_running() {return 0;}
diff --git a/x11.cpp b/x11.cpp
index c07aed84a0..c7dd71c723 100644
--- a/x11.cpp
+++ b/x11.cpp
@@ -186,7 +186,8 @@ static void create_empty_cursor(Display *display,
}
/* No CD on the iPAQ => stub functions */
-void cd_play(int track, int num_loops, int start_frame, int end_frame) {
+void cd_play(Scumm *s, int track, int num_loops, int start_frame, int end_frame) {
+/* Insert SDL.cpp MP3 code here :) */
}
int cd_is_running(void) {
return 1;