diff options
| -rw-r--r-- | scumm/sound.cpp | 387 | ||||
| -rw-r--r-- | scumm/sound.h | 23 | ||||
| -rw-r--r-- | sound/audiocd.cpp | 152 | ||||
| -rw-r--r-- | sound/audiocd.h | 74 | ||||
| -rw-r--r-- | sound/module.mk | 5 | ||||
| -rw-r--r-- | sound/mp3.cpp | 127 | ||||
| -rw-r--r-- | sound/mp3.h | 47 | ||||
| -rw-r--r-- | sound/vorbis.cpp | 150 | ||||
| -rw-r--r-- | sound/vorbis.h | 49 | 
9 files changed, 614 insertions, 400 deletions
| diff --git a/scumm/sound.cpp b/scumm/sound.cpp index 6e63cb17c1..80857d1bf6 100644 --- a/scumm/sound.cpp +++ b/scumm/sound.cpp @@ -32,10 +32,12 @@  #include "common/timer.h"  #include "common/util.h" +#include "sound/audiocd.h"  #include "sound/mididrv.h"  #include "sound/midiparser.h"  #include "sound/mixer.h"  #include "sound/voc.h" +#include "sound/vorbis.h"  namespace Scumm { @@ -52,45 +54,6 @@ struct MP3OffsetTable {					/* Compressed Sound (.SO3) */  	int compressed_size;  }; -class DigitalTrackInfo { -public: -	virtual bool error() = 0; -	virtual int play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration) = 0; -	virtual ~DigitalTrackInfo() { } -}; - -#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, PlayingSoundHandle *handle, int startFrame, int duration); -}; -#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, PlayingSoundHandle *handle, int startFrame, int duration); -}; -#endif - -  Sound::Sound(ScummEngine *parent) {  	memset(this,0,sizeof(Sound));	// palmos @@ -101,7 +64,6 @@ Sound::Sound(ScummEngine *parent) {  	_musicBundleBufOutput = NULL;  	_musicDisk = 0;  	_talkChannelHandle = 0; -	_current_cache = 0;  	_currentCDSound = 0;  	_sfxFile = 0; @@ -880,7 +842,9 @@ void Sound::startSfxSound(File *file, int file_size, PlayingSoundHandle *handle)  	if (file_size > 0) {  		if (_vorbis_mode) { -			playSfxSound_Vorbis(file, file_size, handle); +#ifdef USE_VORBIS +			playSfxSound_Vorbis(_scumm->_mixer, file, file_size, handle); +#endif  		} else {  #ifdef USE_MAD  			_scumm->_mixer->playMP3(handle, file, file_size); @@ -1360,89 +1324,6 @@ void Sound::playSfxSound(void *sound, uint32 size, uint rate, bool isUnsigned, P  	_scumm->_mixer->playRaw(handle, sound, size, rate, flags);  } -#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; -	} -} - -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; -} - -static long tell_wrap(void *datasource) { -	file_info *f = (file_info *) datasource; - -	return f->curr_pos; -} - -static ov_callbacks g_File_wrap = { -	read_wrap, seek_wrap, close_wrap, tell_wrap -}; -#endif - -void Sound::playSfxSound_Vorbis(File *file, uint32 size, PlayingSoundHandle *handle) { -#ifdef USE_VORBIS - -	OggVorbis_File *ov_file = new OggVorbis_File; -	file_info *f = new file_info; - -	f->file = file; -	f->start = file->pos(); -	f->len = size; -	f->curr_pos = 0; - -	if (ov_open_callbacks((void *) f, ov_file, NULL, 0, g_File_wrap) < 0) { -		warning("Invalid file format"); -		delete ov_file; -		delete f; -	} else -		_scumm->_mixer->playVorbis(handle, ov_file, 0, false); -#endif -} -  // We use a real timer in an attempt to get better sync with CD tracks. This is  // necessary for games like Loom CD. @@ -1481,23 +1362,9 @@ void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {  	// Reset the music timer variable at the start of a new track  	_scumm->VAR(_scumm->VAR_MUSIC_TIMER) = 0; -	if (!_soundsPaused && (numLoops != 0 || startFrame != 0)) { -		// Try to load the track from a .mp3/.ogg file, and if found, use -		// that. If not found, attempt to do regular Audio CD playback of -		// the requested track. -		int index = getCachedTrack(track); -		if (index >= 0) { -			_scumm->_mixer->stopHandle(_dig_cd.handle); -			_dig_cd.playing = true; -			_dig_cd.track = track; -			_dig_cd.numLoops = numLoops; -			_dig_cd.start = startFrame; -			_dig_cd.duration = duration; -			_track_info[index]->play(_scumm->_mixer, &_dig_cd.handle, _dig_cd.start, _dig_cd.duration); -		} else { -			_scumm->_system->play_cdrom(track, numLoops, startFrame, duration); -		} -	} +	// Play it +	if (!_soundsPaused) +		AudioCD.playCDTrack(track, numLoops, startFrame, duration);  	// Start the timer after starting the track. Starting an MP3 track is  	// almost instantaneous, but a CD player may take some time. Hopefully @@ -1506,247 +1373,15 @@ void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {  }  void Sound::stopCD() { - -	if (_dig_cd.playing) { -		_scumm->_mixer->stopHandle(_dig_cd.handle); -		_dig_cd.playing = false; -	} else { -		_scumm->_system->stop_cdrom(); -	} +	AudioCD.stopCD();  }  int Sound::pollCD() const { -	return _dig_cd.playing || _scumm->_system->poll_cdrom(); +	return AudioCD.pollCD();  }  void Sound::updateCD() { -	if (_dig_cd.playing) { -		// If the sound handle is 0, then playback stopped. -		if (!_dig_cd.handle) { -			// If playback just stopped, check if the current track is supposed -			// to be repeated, and if that's the case, play it again. Else, stop -			// the CD explicitly. -			if (_dig_cd.numLoops == -1 || --_dig_cd.numLoops > 0) { -				_scumm->VAR(_scumm->VAR_MUSIC_TIMER) = 0; -				if (!_soundsPaused) { -					int index = getCachedTrack(_dig_cd.track); -					assert(index >= 0); -					_track_info[index]->play(_scumm->_mixer, &_dig_cd.handle, _dig_cd.start, _dig_cd.duration); -				} -			} else { -				_scumm->_mixer->stopHandle(_dig_cd.handle); -				_dig_cd.playing = false; -			} -		} -	} else { -		_scumm->_system->update_cdrom(); -	} -} - -int Sound::getCachedTrack(int track) { -	int i; -#if defined(USE_MAD) || defined(USE_VORBIS) -	char track_name[1024]; -	File *file = new File(); -#endif -	int current_index; - -	// See if we find the track in the cache -	for (i = 0; i < CACHE_TRACKS; i++) -		if (_cached_tracks[i] == track) { -			if (_track_info[i]) -				return i; -			else -				return -1; -		} -	current_index = _current_cache++; -	_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()); - -	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(2, "Track %d not available in compressed format", track); -	return -1; -} - -#ifdef USE_MAD -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); - -	while (1) { -		if (buflen < sizeof(buffer)) { -			int bytes; - -			bytes = file->read(buffer + buflen, sizeof(buffer) - buflen); -			if (bytes <= 0) { -				if (bytes == -1) { -					warning("Invalid file format"); -					goto error; -				} -				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++) -				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 { -		warning("Invalid file format"); -		goto error; -	} - -	mad_frame_finish(&frame); -	mad_stream_finish(&stream); -	// Get file size -	_size = file->size(); -	_file = file; -	_error_flag = false; -	return; - -error: -	mad_frame_finish(&frame); -	mad_stream_finish(&stream); -	_error_flag = true; -	delete file; +	AudioCD.updateCD();  } -int MP3TrackInfo::play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration) { -	unsigned int offset; -	mad_timer_t durationTime; - -	// Calc offset. As all bitrates are in kilobit per seconds, the division by 200 is always exact -	offset = (startFrame * (_mad_header.bitrate / (8 * 25))) / 3; -	_file->seek(offset, SEEK_SET); - -	// Calc delay -	if (!duration) { -		// FIXME: Using _size here is a problem if offset (or equivalently -		// startFrame) is non-zero. -		mad_timer_set(&durationTime, (_size * 8) / _mad_header.bitrate, -					(_size * 8) % _mad_header.bitrate, _mad_header.bitrate); -	} else { -		mad_timer_set(&durationTime, duration / 75, duration % 75, 75); -	} - -	// Play it -	return mixer->playMP3CDTrack(handle, _file, durationTime); -} - -MP3TrackInfo::~MP3TrackInfo() { -	if (! _error_flag) -		_file->close(); -} - -#endif - -#ifdef USE_VORBIS - -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, g_File_wrap) < 0) { -		warning("Invalid file format"); -		_error_flag = true; -		delete f; -		delete file; -	} else { -		_error_flag = false; -		_file = file; -	} -} - -#ifdef CHUNKSIZE -#define VORBIS_TREMOR -#endif - -int VorbisTrackInfo::play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration) { -#ifdef VORBIS_TREMOR -	ov_time_seek(&_ov_file, (ogg_int64_t)(startFrame / 75.0 * 1000)); -#else -	ov_time_seek(&_ov_file, startFrame / 75.0); -#endif -	return mixer->playVorbis(handle, &_ov_file, -							duration * ov_info(&_ov_file, -1)->rate / 75, true); -} - -VorbisTrackInfo::~VorbisTrackInfo() { -	if (! _error_flag) { -		ov_clear(&_ov_file); -		delete _file; -	} -} - -#endif -  } // End of namespace Scumm diff --git a/scumm/sound.h b/scumm/sound.h index 28e9d135a6..e1b6c7c64d 100644 --- a/scumm/sound.h +++ b/scumm/sound.h @@ -29,7 +29,6 @@ class File;  namespace Scumm {  class Bundle; -class DigitalTrackInfo;  class ScummEngine;  struct MP3OffsetTable; @@ -74,27 +73,8 @@ protected:  	int num_sound_effects;		// SO3 MP3 compressed audio  	bool _vorbis_mode;	// true if using SOG, false if using SO3 -	enum { -		CACHE_TRACKS = 10 -	}; - -	/* used for mp3 CD music */ -  	int _currentCDSound; -	int _cached_tracks[CACHE_TRACKS]; -	struct { -		PlayingSoundHandle handle; -		int track; -		int start; -		int duration; -		int numLoops; -		bool playing; -	} _dig_cd; - -	DigitalTrackInfo *_track_info[CACHE_TRACKS]; -	int _current_cache; -  	ScummEngine *_scumm;  public: @@ -147,9 +127,6 @@ protected:  	void startSfxSound(File *file, int file_size, PlayingSoundHandle *handle);  	bool isSfxFinished() const;  	void playSfxSound(void *sound, uint32 size, uint rate, bool isUnsigned, PlayingSoundHandle *handle); -	void playSfxSound_Vorbis(File *file, uint32 size, PlayingSoundHandle *handle); - -	int getCachedTrack(int track);  };  } // End of namespace Scumm diff --git a/sound/audiocd.cpp b/sound/audiocd.cpp new file mode 100644 index 0000000000..6da8cf28aa --- /dev/null +++ b/sound/audiocd.cpp @@ -0,0 +1,152 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" + +#include "sound/audiocd.h" +#include "sound/mp3.h" +#include "sound/vorbis.h" +#include "base/engine.h" +#include "common/file.h" +#include "common/util.h" + + +AudioCDManager::AudioCDManager() { +	_current_cache = 0; +} + +void AudioCDManager::playCDTrack(int track, int numLoops, int startFrame, int duration) { +	if (numLoops != 0 || startFrame != 0) { +		// Try to load the track from a .mp3/.ogg file, and if found, use +		// that. If not found, attempt to do regular Audio CD playback of +		// the requested track. +		int index = getCachedTrack(track); +		if (index >= 0) { +			g_engine->_mixer->stopHandle(_cd.handle); +			_cd.playing = true; +			_cd.track = track; +			_cd.numLoops = numLoops; +			_cd.start = startFrame; +			_cd.duration = duration; +			_track_info[index]->play(g_engine->_mixer, &_cd.handle, _cd.start, _cd.duration); +		} else { +			g_system->play_cdrom(track, numLoops, startFrame, duration); +		} +	} +} + +void AudioCDManager::stopCD() { +	if (_cd.playing) { +		g_engine->_mixer->stopHandle(_cd.handle); +		_cd.playing = false; +	} else { +		g_system->stop_cdrom(); +	} +} + +int AudioCDManager::pollCD() const { +	return _cd.playing || g_system->poll_cdrom(); +} + +void AudioCDManager::updateCD() { +	if (_cd.playing) { +		// If the sound handle is 0, then playback stopped. +		if (!_cd.handle) { +			// If playback just stopped, check if the current track is supposed +			// to be repeated, and if that's the case, play it again. Else, stop +			// the CD explicitly. +			if (_cd.numLoops == -1 || --_cd.numLoops > 0) { +//FIXME				_scumm->VAR(_scumm->VAR_MUSIC_TIMER) = 0; +//FIXME				if (!_soundsPaused) { +					int index = getCachedTrack(_cd.track); +					assert(index >= 0); +					_track_info[index]->play(g_engine->_mixer, &_cd.handle, _cd.start, _cd.duration); +//FIXME				} +			} else { +				g_engine->_mixer->stopHandle(_cd.handle); +				_cd.playing = false; +			} +		} +	} else { +		g_system->update_cdrom(); +	} +} + +int AudioCDManager::getCachedTrack(int track) { +	int i; +#if defined(USE_MAD) || defined(USE_VORBIS) +	char track_name[1024]; +	File *file = new File(); +#endif +	int current_index; + +	// See if we find the track in the cache +	for (i = 0; i < CACHE_TRACKS; i++) +		if (_cached_tracks[i] == track) { +			if (_track_info[i]) +				return i; +			else +				return -1; +		} +	current_index = _current_cache++; +	_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, g_engine->getGameDataPath()); + +	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, g_engine->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(2, "Track %d not available in compressed format", track); +	return -1; +} diff --git a/sound/audiocd.h b/sound/audiocd.h new file mode 100644 index 0000000000..c02e50f4f6 --- /dev/null +++ b/sound/audiocd.h @@ -0,0 +1,74 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef SOUND_AUDIOCD_H +#define SOUND_AUDIOCD_H + +#include "stdafx.h" +#include "common/scummsys.h" +#include "common/singleton.h" +#include "sound/mixer.h" + +class DigitalTrackInfo { +public: +	virtual bool error() = 0; +	virtual int play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration) = 0; +	virtual ~DigitalTrackInfo() { } +}; + + +class AudioCDManager : public Common::Singleton<AudioCDManager> { +public: +	void playCDTrack(int track, int numLoops, int startFrame, int duration); +	void stopCD(); +	int pollCD() const; +	void updateCD(); + +private: +	friend class Common::Singleton<AudioCDManager>; +	AudioCDManager(); + +	int getCachedTrack(int track); + +private: +	/* used for emulated CD music */ +	enum { +		CACHE_TRACKS = 10 +	}; +	int _cached_tracks[CACHE_TRACKS]; +	DigitalTrackInfo *_track_info[CACHE_TRACKS]; +	int _current_cache; + +	struct { +		PlayingSoundHandle handle; +		int track; +		int start; +		int duration; +		int numLoops; +		bool playing; +	} _cd; + +}; + +/** Shortcut for accessing the audio CD manager. */ +#define AudioCD		AudioCDManager::instance() + +#endif diff --git a/sound/module.mk b/sound/module.mk index e0974162a5..859a8b9975 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -1,6 +1,7 @@  MODULE := sound  MODULE_OBJS := \ +	sound/audiocd.o \  	sound/audiostream.o \  	sound/fmopl.o \  	sound/mididrv.o \ @@ -8,9 +9,11 @@ MODULE_OBJS := \  	sound/midiparser_smf.o \  	sound/midiparser_xmidi.o \  	sound/mixer.o \ +	sound/mp3.o \  	sound/mpu401.o \  	sound/rate.o \ -	sound/voc.o +	sound/voc.o \ +	sound/vorbis.o  #	sound/resample.o \  MODULE_DIRS += \ diff --git a/sound/mp3.cpp b/sound/mp3.cpp new file mode 100644 index 0000000000..db73f7f394 --- /dev/null +++ b/sound/mp3.cpp @@ -0,0 +1,127 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" + +#include "sound/mp3.h" +#include "common/file.h" +#include "common/util.h" + +#ifdef USE_MAD +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); + +	while (1) { +		if (buflen < sizeof(buffer)) { +			int bytes; + +			bytes = file->read(buffer + buflen, sizeof(buffer) - buflen); +			if (bytes <= 0) { +				if (bytes == -1) { +					warning("Invalid file format"); +					goto error; +				} +				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++) +				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 { +		warning("Invalid file format"); +		goto error; +	} + +	mad_frame_finish(&frame); +	mad_stream_finish(&stream); +	// Get file size +	_size = file->size(); +	_file = file; +	_error_flag = false; +	return; + +error: +	mad_frame_finish(&frame); +	mad_stream_finish(&stream); +	_error_flag = true; +	delete file; +} + +int MP3TrackInfo::play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration) { +	unsigned int offset; +	mad_timer_t durationTime; + +	// Calc offset. As all bitrates are in kilobit per seconds, the division by 200 is always exact +	offset = (startFrame * (_mad_header.bitrate / (8 * 25))) / 3; +	_file->seek(offset, SEEK_SET); + +	// Calc delay +	if (!duration) { +		// FIXME: Using _size here is a problem if offset (or equivalently +		// startFrame) is non-zero. +		mad_timer_set(&durationTime, (_size * 8) / _mad_header.bitrate, +					(_size * 8) % _mad_header.bitrate, _mad_header.bitrate); +	} else { +		mad_timer_set(&durationTime, duration / 75, duration % 75, 75); +	} + +	// Play it +	return mixer->playMP3CDTrack(handle, _file, durationTime); +} + +MP3TrackInfo::~MP3TrackInfo() { +	if (! _error_flag) +		_file->close(); +} + +#endif diff --git a/sound/mp3.h b/sound/mp3.h new file mode 100644 index 0000000000..fd5be63387 --- /dev/null +++ b/sound/mp3.h @@ -0,0 +1,47 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef SOUND_MP3_H +#define SOUND_MP3_H + +#include "sound/audiocd.h" + +class File; + +#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, PlayingSoundHandle *handle, int startFrame, int duration); +}; +#endif + + + +#endif diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp new file mode 100644 index 0000000000..cd1a84ad08 --- /dev/null +++ b/sound/vorbis.cpp @@ -0,0 +1,150 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" + +#include "sound/vorbis.h" +#include "common/file.h" +#include "common/util.h" + +#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; +	} +} + +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; +} + +static long tell_wrap(void *datasource) { +	file_info *f = (file_info *) datasource; + +	return f->curr_pos; +} + +static ov_callbacks g_File_wrap = { +	read_wrap, seek_wrap, close_wrap, tell_wrap +}; +#endif + +#ifdef USE_VORBIS + +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, g_File_wrap) < 0) { +		warning("Invalid file format"); +		_error_flag = true; +		delete f; +		delete file; +	} else { +		_error_flag = false; +		_file = file; +	} +} + +#ifdef CHUNKSIZE +#define VORBIS_TREMOR +#endif + +int VorbisTrackInfo::play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration) { +#ifdef VORBIS_TREMOR +	ov_time_seek(&_ov_file, (ogg_int64_t)(startFrame / 75.0 * 1000)); +#else +	ov_time_seek(&_ov_file, startFrame / 75.0); +#endif +	return mixer->playVorbis(handle, &_ov_file, +							duration * ov_info(&_ov_file, -1)->rate / 75, true); +} + +VorbisTrackInfo::~VorbisTrackInfo() { +	if (! _error_flag) { +		ov_clear(&_ov_file); +		delete _file; +	} +} + +void playSfxSound_Vorbis(SoundMixer *mixer, File *file, uint32 size, PlayingSoundHandle *handle) { +	OggVorbis_File *ov_file = new OggVorbis_File; +	file_info *f = new file_info; + +	f->file = file; +	f->start = file->pos(); +	f->len = size; +	f->curr_pos = 0; + +	if (ov_open_callbacks((void *) f, ov_file, NULL, 0, g_File_wrap) < 0) { +		warning("Invalid file format"); +		delete ov_file; +		delete f; +	} else +		mixer->playVorbis(handle, ov_file, 0, false); +} + +#endif diff --git a/sound/vorbis.h b/sound/vorbis.h new file mode 100644 index 0000000000..fcffc1c50d --- /dev/null +++ b/sound/vorbis.h @@ -0,0 +1,49 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef SOUND_VORBIS_H +#define SOUND_VORBIS_H + +#include "sound/audiocd.h" + +class File; + +#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, PlayingSoundHandle *handle, int startFrame, int duration); +}; + + +void playSfxSound_Vorbis(SoundMixer *mixer, File *file, uint32 size, PlayingSoundHandle *handle); + +#endif + +#endif | 
