diff options
author | Nicolas Bacca | 2002-04-04 22:47:03 +0000 |
---|---|---|
committer | Nicolas Bacca | 2002-04-04 22:47:03 +0000 |
commit | cbad525cd30e266f23cfac8d974650cc52517119 (patch) | |
tree | 96fbffe336a4c0de3901364b7e6a215d15a849b8 | |
parent | 796110715688a8d487b8380e9b115a3b13d1830d (diff) | |
download | scummvm-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.h | 2 | ||||
-rw-r--r-- | script_v1.cpp | 2 | ||||
-rw-r--r-- | sdl.cpp | 306 | ||||
-rw-r--r-- | sound.cpp | 142 | ||||
-rw-r--r-- | sound.h | 18 | ||||
-rw-r--r-- | windows.cpp | 2 | ||||
-rw-r--r-- | x11.cpp | 3 |
7 files changed, 467 insertions, 8 deletions
@@ -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"); } @@ -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.) @@ -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 } @@ -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;} @@ -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; |