diff options
| -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; | 
