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
This commit is contained in:
parent
7961107156
commit
cbad525cd3
7 changed files with 467 additions and 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();
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
306
sdl.cpp
306
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.)
|
||||
|
|
142
sound.cpp
142
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
|
||||
}
|
||||
|
||||
|
|
18
sound.h
18
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);
|
||||
|
|
|
@ -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;}
|
||||
|
||||
|
|
3
x11.cpp
3
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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue