diff options
Diffstat (limited to 'sdl.cpp')
| -rw-r--r-- | sdl.cpp | 306 |
1 files changed, 304 insertions, 2 deletions
@@ -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.) |
