diff options
| author | Jonathan Gray | 2002-10-27 01:12:10 +0000 | 
|---|---|---|
| committer | Jonathan Gray | 2002-10-27 01:12:10 +0000 | 
| commit | d93e63908659a9601ee29269606500ded088175e (patch) | |
| tree | 420f1bb6c9a364cf25317622984381d9ae94aeec | |
| parent | f89b40f0276601fb279dd2db03eed30c650df22a (diff) | |
| download | scummvm-rg350-d93e63908659a9601ee29269606500ded088175e.tar.gz scummvm-rg350-d93e63908659a9601ee29269606500ded088175e.tar.bz2 scummvm-rg350-d93e63908659a9601ee29269606500ded088175e.zip  | |
patch #628997 support for ogg vorbis instead of cd tracks by Daniel Schepler. Uncomment the relevant lines in the makefile to use
svn-id: r5320
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | common/scummsys.h | 4 | ||||
| -rw-r--r-- | scumm/sound.cpp | 298 | ||||
| -rw-r--r-- | scumm/sound.h | 58 | ||||
| -rw-r--r-- | sound/mixer.cpp | 100 | ||||
| -rw-r--r-- | sound/mixer.h | 29 | 
6 files changed, 401 insertions, 94 deletions
@@ -36,9 +36,13 @@ OBJS	 =  # DEFINES += -DDUMP_SCRIPTS  # Uncomment this to activate the MAD lib for compressed sound files -DEFINES += -DCOMPRESSED_SOUND_FILE +DEFINES += -DUSE_MAD  LIBS    += -lmad +# Uncomment this to activate the Ogg Vorbis lib for compressed sound files +# DEFINES += -DUSE_VORBIS +# LIBS    += -lvorbisfile -lvorbis +  # Uncomment this to activate the ALSA lib for midi  # DEFINES += -DUSE_ALSA  # LIBS    += -lasound diff --git a/common/scummsys.h b/common/scummsys.h index fda37b18bd..0067d62d60 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -31,6 +31,10 @@ typedef int bool;  const bool true(1), false(0);  #endif  /*  HAVE_NO_BOOL  */ +#if defined(USE_MAD) || defined(USE_VORBIS) +#define COMPRESSED_SOUND_FILE +#endif +  #if defined(_MSC_VER)    //#pragma warning (disable: 4244) diff --git a/scumm/sound.cpp b/scumm/sound.cpp index a8449a98c9..1c852fd016 100644 --- a/scumm/sound.cpp +++ b/scumm/sound.cpp @@ -738,7 +738,7 @@ int Sound::startSfxSound(File *file, int file_size) {  	int rate, comp;  	byte *data; -#ifdef COMPRESSED_SOUND_FILE +#ifdef USE_MAD  	if (file_size > 0) {  		data = (byte *)calloc(file_size + MAD_BUFFER_GUARD, 1); @@ -1116,7 +1116,7 @@ int Sound::playSfxSound(void *sound, uint32 size, uint rate, bool isUnsigned) {  }  int Sound::playSfxSound_MP3(void *sound, uint32 size) { -#ifdef COMPRESSED_SOUND_FILE +#ifdef USE_MAD  	if (_soundsPaused)  		return -1;  	return _scumm->_mixer->playMP3(NULL, sound, size, SoundMixer::FLAG_AUTOFREE); @@ -1219,16 +1219,11 @@ int Sound::getCachedTrack(int track) {  	char track_name[1024];  	File * file = new File();  	int current_index; -	struct mad_stream stream; -	struct mad_frame frame; -	unsigned char buffer[8192]; -	unsigned int buflen = 0; -	int count = 0;  	// See if we find the track in the cache  	for (i = 0; i < CACHE_TRACKS; i++)  		if (_cached_tracks[i] == track) { -			if (_mp3_tracks[i]) +			if (_track_info[i])  				return i;  			else  				return -1; @@ -1237,21 +1232,119 @@ int Sound::getCachedTrack(int track) {  	_current_cache %= CACHE_TRACKS;  	// Not found, see if it exists + +	// First, delete the previous track info object +	delete _track_info[current_index]; +	_track_info[current_index] = NULL; + +	_cached_tracks[current_index] = track; + +#ifdef USE_MAD  	sprintf(track_name, "track%d.mp3", track);  	file->open(track_name, _scumm->getGameDataPath()); -	_cached_tracks[current_index] = track; -	/* First, close the previous file */ -	if (_mp3_tracks[current_index]) -		_mp3_tracks[current_index]->close(); +	if (file->isOpen()) { +		_track_info[current_index] = new MP3TrackInfo(file); +		if (_track_info[current_index]->error()) { +			delete _track_info[current_index]; +			_track_info[current_index] = NULL; +			return -1; +		} +		return current_index; +	} +#endif + +#ifdef USE_VORBIS +	sprintf(track_name, "track%d.ogg", track); +	file->open(track_name, _scumm->getGameDataPath()); + +	if (file->isOpen()) { +		_track_info[current_index] = new VorbisTrackInfo(file); +		if (_track_info[current_index]->error()) { +			delete _track_info[current_index]; +			_track_info[current_index] = NULL; +			return -1; +		} +		return current_index; +	} +#endif + +	debug(1, "Track %d not available in compressed format", track); +	return -1; +} + +int Sound::playMP3CDTrack(int track, int num_loops, int start, int delay) { +	int index; +	_scumm->_vars[_scumm->VAR_MI1_TIMER] = 0; + +	if (_soundsPaused) +		return 0; + +	if ((num_loops == 0) && (start == 0)) { +		return 0; +	} + +	index = getCachedTrack(track); +	if (index < 0) +		return -1; + +	if (_dig_cd_playing) +		_scumm->_mixer->stop(_dig_cd_index); +	_dig_cd_index = _track_info[index]->play(_scumm->_mixer, start, delay); +	_dig_cd_playing = true; +	_dig_cd_track = track; +	_dig_cd_num_loops = num_loops; +	_dig_cd_start = start; +	_dig_cd_delay = delay; +	return 0; +} + +int Sound::stopMP3CD() { +	if (_dig_cd_playing == true) { +		_scumm->_mixer->stop(_dig_cd_index); +		_dig_cd_playing = false; +		_dig_cd_track = 0; +		_dig_cd_num_loops = 0; +		_dig_cd_start = 0; +		_dig_cd_delay = 0; +		return 0; +	} +	return -1; +} + +int Sound::pollMP3CD() { +	if (_dig_cd_playing == true) +		return 1; +	return 0; +} + +int Sound::updateMP3CD() { +	if (_dig_cd_playing == false) +		return -1; -	_mp3_tracks[current_index] = NULL; -	if (file->isOpen() == false) { -		// This warning is pretty pointless. -		debug(1, "Track %d not available in mp3 format", track); +	if (_scumm->_mixer->_channels[_dig_cd_index] == NULL) { +		warning("Error in MP3 decoding");  		return -1;  	} +	if (_scumm->_mixer->_channels[_dig_cd_index]->soundFinished()) { +		if (_dig_cd_num_loops == -1 || --_dig_cd_num_loops > 0) +			playMP3CDTrack(_dig_cd_track, _dig_cd_num_loops, _dig_cd_start, _dig_cd_delay); +		else +			stopMP3CD(); +	} + +	return 0; +} + +#ifdef USE_MAD +Sound::MP3TrackInfo::MP3TrackInfo(File *file) { +	struct mad_stream stream; +	struct mad_frame frame; +	unsigned char buffer[8192]; +	unsigned int buflen = 0; +	int count = 0; +  	// Check the format and bitrate  	mad_stream_init(&stream);  	mad_frame_init(&frame); @@ -1263,7 +1356,7 @@ int Sound::getCachedTrack(int track) {  			bytes = file->read(buffer + buflen, sizeof(buffer) - buflen);  			if (bytes <= 0) {  				if (bytes == -1) { -					warning("Invalid format for track %d", track); +					warning("Invalid file format");  					goto error;  				}  				break; @@ -1295,105 +1388,154 @@ int Sound::getCachedTrack(int track) {  	}  	if (count) -		memcpy(&_mad_header[current_index], &frame.header, sizeof(mad_header)); +		memcpy(&_mad_header, &frame.header, sizeof(mad_header));  	else { -		warning("Invalid format for track %d", track); +		warning("Invalid file format");  		goto error;  	}  	mad_frame_finish(&frame);  	mad_stream_finish(&stream);  	// Get file size -	_mp3_size[current_index] = file->size(); -	_mp3_tracks[current_index] = file; -	 -	return current_index; +	_size = file->size(); +	_file = file; +	_error_flag = false; +	return;   error:  	mad_frame_finish(&frame);  	mad_stream_finish(&stream); +	_error_flag = true;  	delete file; - -	return -1;  } -int Sound::playMP3CDTrack(int track, int num_loops, int start, int delay) { -	int index; +int Sound::MP3TrackInfo::play(SoundMixer *mixer, int start, int delay) {  	unsigned int offset;  	mad_timer_t duration; -	_scumm->_vars[_scumm->VAR_MI1_TIMER] = 0; - -	if (_soundsPaused) -		return 0; - -	if ((num_loops == 0) && (start == 0)) { -		return 0; -	} - -	index = getCachedTrack(track); -	if (index < 0) -		return -1;  	// Calc offset. As all bitrates are in kilobit per seconds, the division by 200 is always exact -	offset = (start * (_mad_header[index].bitrate / (8 * 25))) / 3; +	offset = (start * (_mad_header.bitrate / (8 * 25))) / 3;  	// Calc delay  	if (!delay) { -		mad_timer_set(&duration, (_mp3_size[index] * 8) / _mad_header[index].bitrate, -		              (_mp3_size[index] * 8) % _mad_header[index].bitrate, _mad_header[index].bitrate); +		mad_timer_set(&duration, (_size * 8) / _mad_header.bitrate, +		              (_size * 8) % _mad_header.bitrate, _mad_header.bitrate);  	} else {  		mad_timer_set(&duration, delay / 75, delay % 75, 75);  	}	  	// Go -	_mp3_tracks[index]->seek(offset, SEEK_SET); - -	if (_mp3_cd_playing == true) -		_scumm->_mixer->stop(_mp3_index);		 -	_mp3_index = _scumm->_mixer->playMP3CDTrack(NULL, _mp3_tracks[index], duration); -	_mp3_cd_playing = true; -	_mp3_cd_track = track; -	_mp3_cd_num_loops = num_loops; -	_mp3_cd_start = start; -	_mp3_cd_delay = delay; -	return 0; +	_file->seek(offset, SEEK_SET); + +	return mixer->playMP3CDTrack(NULL, _file, duration);  } -int Sound::stopMP3CD() { -	if (_mp3_cd_playing == true) { -		_scumm->_mixer->stop(_mp3_index); -		_mp3_cd_playing = false; -		_mp3_cd_track = 0; -		_mp3_cd_num_loops = 0; -		_mp3_cd_start = 0; -		_mp3_cd_delay = 0; -		return 0; +Sound::MP3TrackInfo::~MP3TrackInfo() { +	if (! _error_flag) +		_file->close(); +} + +#endif + +#ifdef USE_VORBIS +// These are wrapper functions to allow using a File object to +// provide data to the OggVorbis_File object. + +struct file_info { +	File *file; +	int start, curr_pos; +	size_t len; +}; + +static size_t read_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) { +	file_info *f = (file_info *) datasource; +	int result; + +	nmemb *= size; +	if (f->curr_pos > (int) f->len) +		nmemb = 0; +	else if (nmemb > f->len - f->curr_pos) +		nmemb = f->len - f->curr_pos; +	result = f->file->read(ptr, nmemb); +	if (result == -1) { +		f->curr_pos = f->file->pos() - f->start; +		return (size_t) -1; +	} +	else { +		f->curr_pos += result; +		return result / size;  	} -	return -1;  } -int Sound::pollMP3CD() { -	if (_mp3_cd_playing == true) -		return 1; +static int seek_wrap(void *datasource, ogg_int64_t offset, int whence) { +	file_info *f = (file_info *) datasource; + +	if (whence == SEEK_SET) +		offset += f->start; +	else if (whence == SEEK_END) { +		offset += f->start + f->len; +		whence = SEEK_SET; +	} + +	f->file->seek(offset, whence); +	f->curr_pos = f->file->pos() - f->start; +	return f->curr_pos; +} + +static int close_wrap(void *datasource) { +	file_info *f = (file_info *) datasource; + +	f->file->close(); +	delete f;  	return 0;  } -int Sound::updateMP3CD() { -	if (_mp3_cd_playing == false) -		return -1; +static long tell_wrap(void *datasource) { +	file_info *f = (file_info *) datasource; -	if (_scumm->_mixer->_channels[_mp3_index] == NULL) { -		warning("Error in MP3 decoding"); -		return -1; +	return f->file->pos(); +} + +static ov_callbacks File_wrap = { +	read_wrap, seek_wrap, close_wrap, tell_wrap +}; + +Sound::VorbisTrackInfo::VorbisTrackInfo(File *file) { +	file_info *f = new file_info; + +	f->file = file; +	f->start = 0; +	f->len = file->size(); +	f->curr_pos = file->pos(); + +	if (ov_open_callbacks((void *) f, &_ov_file, NULL, 0, File_wrap) < 0) { +		warning("Invalid file format"); +		_error_flag = true; +		delete f; +		delete file;  	} +	else { +		_error_flag = false; +		_file = file; -	if (_scumm->_mixer->_channels[_mp3_index]->soundFinished()) { -		if (_mp3_cd_num_loops == -1 || --_mp3_cd_num_loops > 0) -			playMP3CDTrack(_mp3_cd_track, _mp3_cd_num_loops, _mp3_cd_start, _mp3_cd_delay); -		else -			stopMP3CD(); +		// Check the file format +		if (ov_info(&_ov_file, -1)->rate != 22050) +			warning("Vorbis code currently only supports files encoded at 22050 Hz");  	} +} -	return 0; +int Sound::VorbisTrackInfo::play(SoundMixer *mixer, int start, int delay) { +	ov_time_seek(&_ov_file, start / 75.0); +	return mixer->playVorbisCDTrack(NULL, &_ov_file, delay / 75.0); +} + +Sound::VorbisTrackInfo::~VorbisTrackInfo() { +	if (! _error_flag) { +		ov_clear(&_ov_file); +		delete _file; +	}  } + +#endif +  #endif diff --git a/scumm/sound.h b/scumm/sound.h index 2f4ddec5c8..635fda5531 100644 --- a/scumm/sound.h +++ b/scumm/sound.h @@ -65,24 +65,62 @@ enum {  	uint16 _mouthSyncTimes[52];  	uint _curSoundPos; +#ifdef COMPRESSED_SOUND_FILE  	MP3OffsetTable *offset_table;	// SO3 MP3 compressed audio  	int num_sound_effects;		// SO3 MP3 compressed audio -#ifdef COMPRESSED_SOUND_FILE  	#define CACHE_TRACKS 10  	/* used for mp3 CD music */  	int _cached_tracks[CACHE_TRACKS]; -	struct mad_header _mad_header[CACHE_TRACKS]; -	long _mp3_size[CACHE_TRACKS]; -	File *_mp3_tracks[CACHE_TRACKS]; -	int _mp3_index; -	int _mp3_cd_track; -	int _mp3_cd_start; -	int _mp3_cd_delay; -	int _mp3_cd_num_loops; -	bool _mp3_cd_playing; +	int _dig_cd_index; +	int _dig_cd_track; +	int _dig_cd_start; +	int _dig_cd_delay; +	int _dig_cd_num_loops; +	bool _dig_cd_playing; + +	class DigitalTrackInfo { +	public: +		virtual bool error() = 0; +		virtual int play(SoundMixer *mixer, int start, int delay) = 0; +		virtual ~DigitalTrackInfo() { } +	}; + +	DigitalTrackInfo *_track_info[CACHE_TRACKS]; + +#ifdef USE_MAD +	class MP3TrackInfo : public DigitalTrackInfo { +	private: +		struct mad_header _mad_header; +		long _size; +		File *_file; +		bool _error_flag; + +	public: +		MP3TrackInfo(File *file); +		~MP3TrackInfo(); +		bool error() { return _error_flag; } +		int play(SoundMixer *mixer, int start, int delay); +	}; +#endif + +#ifdef USE_VORBIS +	class VorbisTrackInfo : public DigitalTrackInfo { +	private: +		File *_file; +		OggVorbis_File _ov_file; +		bool _error_flag; + +	public: +		VorbisTrackInfo(File *file); +		~VorbisTrackInfo(); +		bool error() { return _error_flag; } +		int play(SoundMixer *mixer, int start, int delay); +	}; +#endif +  #endif  	Scumm * _scumm; diff --git a/sound/mixer.cpp b/sound/mixer.cpp index c742fb153e..e79a91aa98 100644 --- a/sound/mixer.cpp +++ b/sound/mixer.cpp @@ -120,7 +120,7 @@ void SoundMixer::beginSlots(int index) {  	_beginSlots = index;  } -#ifdef COMPRESSED_SOUND_FILE +#ifdef USE_MAD  int SoundMixer::playMP3(PlayingSoundHandle * handle, void *sound, uint32 size, byte flags) {  	for (int i = _beginSlots; i != NUM_CHANNELS; i++) {  		if (_channels[i] == NULL) { @@ -144,6 +144,19 @@ int SoundMixer::playMP3CDTrack(PlayingSoundHandle * handle, File * file, mad_tim  }  #endif +#ifdef USE_VORBIS +int SoundMixer::playVorbisCDTrack(PlayingSoundHandle * handle, OggVorbis_File * ov_file, double duration) { +	for (int i = _beginSlots; i != NUM_CHANNELS; i++) { +		if (_channels[i] == NULL) { +			return insertAt(handle, i, new ChannelVorbis(this, ov_file, duration)); +		} +	} + +	warning("SoundMixer::out of mixer slots"); +	return -1; +} +#endif +  void SoundMixer::mix(int16 *buf, uint len) {  	if (_paused) {  		memset(buf, 0, 2 * len * sizeof(int16)); @@ -713,7 +726,7 @@ void SoundMixer::ChannelStream::realDestroy() {  	delete this;  } -#ifdef COMPRESSED_SOUND_FILE +#ifdef USE_MAD  SoundMixer::ChannelMP3::ChannelMP3(SoundMixer * mixer, void * sound, uint size, byte flags) {  	_mixer = mixer;  	_flags = flags; @@ -962,3 +975,86 @@ void SoundMixer::ChannelMP3CDMusic::realDestroy() {  }  #endif + +#ifdef USE_VORBIS +SoundMixer::ChannelVorbis::ChannelVorbis(SoundMixer * mixer, OggVorbis_File * ov_file, double duration) { +	_mixer = mixer; +	_ov_file = ov_file; + +	if (duration) +		_end_pos = ov_time_tell(ov_file) + duration; +	else +		_end_pos = 0; + +	_eof_flag = false; +	_toBeDestroyed = false; +} + +void SoundMixer::ChannelVorbis::mix(int16 * data, uint len) { +	if (_toBeDestroyed) { +		realDestroy(); +		return; +	} + +	if (_eof_flag) { +		memset(data, 0, sizeof(int16) * 2 * len); +		return; +	} + +	int channels = ov_info(_ov_file, -1)->channels; +	uint len_left = len * channels * 2; +	int16 *samples = new int16[len_left / 2]; +	char *read_pos = (char *) samples; +	int volume = _mixer->_musicVolume; + +	// Read the samples +	while (len_left > 0) { +		long result = ov_read(_ov_file, read_pos, len_left, +#ifdef SCUMM_BIG_ENDIAN +				      1, +#else +				      0, +#endif +				      2, 1, NULL); +		if (result == 0) { +			_eof_flag = true; +			memset(read_pos, 0, len_left); +			break; +		} +		else if (result < 0) { +			debug(1, "Decode error %d in Vorbis file", result); +			// Don't delete it yet, that causes problems in +			// the CD player emulation code. +			_eof_flag = true; +			memset(read_pos, 0, len_left); +			break; +		} +		else { +			len_left -= result; +			read_pos += result; +		} +	} + +	// Mix the samples in +	for (uint i = 0; i < len; i++) { +		int16 sample = (int16) ((int32) samples[i * channels] * volume / 256); +		*data++ += sample; +		if (channels > 1) +			sample = (int16) ((int32) samples[i * channels + 1] * volume / 256); +		*data++ += sample; +	} + +	delete [] samples; +} + +void SoundMixer::ChannelVorbis::realDestroy() { +	_mixer->unInsert(this); +	delete this; +} + +bool SoundMixer::ChannelVorbis::soundFinished() { +	return _eof_flag || (_end_pos > 0 && +			     ov_time_tell(_ov_file) >= _end_pos); +} + +#endif diff --git a/sound/mixer.h b/sound/mixer.h index fd947502ca..1fecef27a2 100644 --- a/sound/mixer.h +++ b/sound/mixer.h @@ -25,10 +25,14 @@  #include <stdio.h> -#ifdef COMPRESSED_SOUND_FILE +#ifdef USE_MAD  #include <mad.h>  #endif +#ifdef USE_VORBIS +#include <vorbis/vorbisfile.h> +#endif +  #include "common/scummsys.h"  #include "common/system.h" @@ -92,7 +96,7 @@ private:  		void realDestroy();  	}; -#ifdef COMPRESSED_SOUND_FILE +#ifdef USE_MAD  	class ChannelMP3 : public Channel {  		SoundMixer * _mixer; @@ -138,6 +142,22 @@ private:  #endif +#ifdef USE_VORBIS +	class ChannelVorbis : public Channel { +		SoundMixer * _mixer; +		OggVorbis_File * _ov_file; +		double _end_pos; +		bool _eof_flag; + +	public: +		ChannelVorbis(SoundMixer * mixer, OggVorbis_File * ov_file, double duration); + +		void mix(int16 * data, uint len); +		void realDestroy(); +		bool soundFinished(); +	}; +#endif +  	static void onGenerateSamples(void * s, byte * samples, int len);  public: @@ -187,10 +207,13 @@ public:  	int playRaw(PlayingSoundHandle * handle, void * sound, uint32 size, uint rate, byte flags, int id);  	int playStream(PlayingSoundHandle * handle, int index, void * sound, uint32 size, uint rate,  									byte flags, int32 timeout = 3, int32 buffer_size = 2000000); -#ifdef COMPRESSED_SOUND_FILE +#ifdef USE_MAD  	int playMP3(PlayingSoundHandle * handle, void * sound, uint32 size, byte flags);  	int playMP3CDTrack(PlayingSoundHandle * handle, File * file, mad_timer_t duration);  #endif +#ifdef USE_VORBIS +	int playVorbisCDTrack(PlayingSoundHandle * handle, OggVorbis_File * ov_file, double duration); +#endif  	/* Premix procedure, useful when using fmopl adlib */  	void setupPremix(void * param, PremixProc * proc);  | 
