[Scummvm-cvs-logs] CVS: scummvm/sound audiostream.cpp,1.46,1.47 audiostream.h,1.23,1.24 mixer.cpp,1.137,1.138 mixer.h,1.60,1.61 mp3.cpp,1.1,1.2 mp3.h,1.1,1.2 vorbis.cpp,1.1,1.2 vorbis.h,1.1,1.2

Max Horn fingolfin at users.sourceforge.net
Thu Dec 18 16:33:01 CET 2003


Update of /cvsroot/scummvm/scummvm/sound
In directory sc8-pr-cvs1:/tmp/cvs-serv18293

Modified Files:
	audiostream.cpp audiostream.h mixer.cpp mixer.h mp3.cpp mp3.h 
	vorbis.cpp vorbis.h 
Log Message:
o Moved MP3 and Vorbis input streams to mp3.* resp. vorbis.*
o Added SoundMixer::playInputStream and made some of the other play* methods use it
o Added ProcInputStream stub (not working yet) which one day may allow us to replace the premix code, and allow other fancy stuff
o Remove AudioInputStream::readBuffer default implementation (subclasses should always provide it for max. performance)
o Some minor cleanup


Index: audiostream.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/audiostream.cpp,v
retrieving revision 1.46
retrieving revision 1.47
diff -u -d -r1.46 -r1.47
--- audiostream.cpp	19 Dec 2003 00:09:34 -0000	1.46
+++ audiostream.cpp	19 Dec 2003 00:32:47 -0000	1.47
@@ -20,11 +20,10 @@
  */
 
 #include "stdafx.h"
-#include "audiostream.h"
-#include "mixer.h"
-#include "base/engine.h"
 #include "common/file.h"
 #include "common/util.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
 
 
 // This used to be an inline template function, but
@@ -225,362 +224,62 @@
 
 
 #pragma mark -
-#pragma mark --- MP3 (MAD) stream ---
+#pragma mark --- Procedural stream ---
 #pragma mark -
 
 
-#ifdef USE_MAD
-class MP3InputStream : public AudioInputStream {
-	struct mad_stream _stream;
-	struct mad_frame _frame;
-	struct mad_synth _synth;
-	mad_timer_t _duration;
-	uint32 _posInFrame;
-	uint32 _bufferSize;
-	int _size;
-	bool _isStereo;
-	int _curChannel;
-	File *_file;
-	byte *_ptr;
+#if 0
+// Work in progress!!! Not yet usable/finished/working/anything :-)
 
-	bool init();
-	void refill(bool first = false);
-	inline bool eosIntern() const;
+class ProcInputStream : public AudioInputStream {
 public:
-	MP3InputStream(File *file, mad_timer_t duration, uint size = 0);
-	~MP3InputStream();
-	int readBuffer(int16 *buffer, const int numSamples);
-
-	int16 read();
-	bool eos() const			{ return eosIntern(); }
-	bool isStereo() const		{ return _isStereo; }
-	
-	int getRate() const			{ return _frame.header.samplerate; }
-};
-
-
-/**
- * Playback the MP3 data in the given file for the specified duration.
- *
- * @param file		file containing the MP3 data
- * @param duration	playback duration in frames (1/75th of a second), 0 means
- *					playback until EOF
- * @param size		optional, if non-zero this limits playback based on the
- * 					number of input bytes rather then a duration
- */
-MP3InputStream::MP3InputStream(File *file, mad_timer_t duration, uint size) {
-	// duration == 0 means: play everything till end of file
-
-	mad_stream_init(&_stream);
-	mad_frame_init(&_frame);
-	mad_synth_init(&_synth);
-
-	_duration = duration;
-
-	_posInFrame = 0;
-	_bufferSize = size ? size : (128 * 1024);	// Default buffer size is 128K
-
-	_isStereo = false;
-	_curChannel = 0;
-	_file = file;
-	_ptr = (byte *)malloc(_bufferSize + MAD_BUFFER_GUARD);
-
-	init();
-
-	// If a size is specified, we do not perform any further read operations
-	if (size) {
-		_file = 0;
-	}
-}
-
-MP3InputStream::~MP3InputStream() {
-	mad_synth_finish(&_synth);
-	mad_frame_finish(&_frame);
-	mad_stream_finish(&_stream);
-
-	free(_ptr);
-}
-
-bool MP3InputStream::init() {
-	// TODO
-
-	// Read in the first chunk of the MP3 file
-	_size = _file->read(_ptr, _bufferSize);
-	if (_size <= 0) {
-		warning("MP3InputStream: Failed to read MP3 data");
-		return false;
-	}
-
-	// Feed the data we just read into the stream decoder
-	mad_stream_buffer(&_stream, _ptr, _size);
-
-	// Read in initial data
-	refill(true);
-
-	// Check the header, determine if this is a stereo stream
-	int num;
-	switch(_frame.header.mode) {
-		case MAD_MODE_SINGLE_CHANNEL:
-		case MAD_MODE_DUAL_CHANNEL:
-		case MAD_MODE_JOINT_STEREO:
-		case MAD_MODE_STEREO:
-			num = MAD_NCHANNELS(&_frame.header);
-			assert(num == 1 || num == 2);
-			_isStereo = (num == 2);
-			break;
-		default:
-			warning("MP3InputStream: Cannot determine number of channels");
-			return false;
-	}
-	
-	return true;
-}
-
-void MP3InputStream::refill(bool first) {
-
-	// Read the next frame (may have to retry several times, e.g.
-	// to skip over ID3 information).
-	while (mad_frame_decode(&_frame, &_stream)) {
-		if (_stream.error == MAD_ERROR_BUFLEN) {
-			int offset;
-
-			if (!_file)
-				_size = -1;
-
-			// Give up immediately if we are at the EOF already
-			if (_size <= 0)
-				return;
-
-			if (!_stream.next_frame) {
-				offset = 0;
-				memset(_ptr, 0, _bufferSize + MAD_BUFFER_GUARD);
-			} else {
-				offset = _stream.bufend - _stream.next_frame;
-				memcpy(_ptr, _stream.next_frame, offset);
-			}
-			// Read in more data from the input file
-			_size = _file->read(_ptr + offset, _bufferSize - offset);
-
-			// Nothing read -> EOF -> bail out
-			if (_size <= 0) {
-				return;
-			}
-			_stream.error = (enum mad_error)0;
+	typedef void InputProc (void *refCon, int16 *data, uint len);
 
-			// Feed the data we just read into the stream decoder
-			mad_stream_buffer(&_stream, _ptr, _size + offset);
+private:
+	const int _rate;
+	const bool _isStereo;
+	InputProc *_proc;
+	void *_refCon;
+	int16 _buffer[2048];
+	const int16 *_pos;
+	int _len;
 
-		} else if (MAD_RECOVERABLE(_stream.error)) {
-			// FIXME: should we do anything here?
-			debug(6, "MP3InputStream: Recoverable error...");
-		} else {
-			error("MP3InputStream: Unrecoverable error");
-		}
+	void refill() {
+		// Fill the buffer
+		(_proc)(_refCon, _buffer, 2048);
+		_pos = _buffer;
+		_len = 2048;
 	}
 
-	// Subtract the duration of this frame from the time left to play
-	mad_timer_t frame_duration = _frame.header.duration;
-	mad_timer_negate(&frame_duration);
-	mad_timer_add(&_duration, frame_duration);
-
-	if (!first && _file && mad_timer_compare(_duration, mad_timer_zero) <= 0)
-		_size = -1;	// Mark for EOF
-
-	// Synthesise the frame into PCM samples and reset the buffer position
-	mad_synth_frame(&_synth, &_frame);
-	_posInFrame = 0;
-}
-
-inline bool MP3InputStream::eosIntern() const {
-	return (_size < 0 || _posInFrame >= _synth.pcm.length);
-}
-
-static inline int scale_sample(mad_fixed_t sample) {
-	// round
-	sample += (1L << (MAD_F_FRACBITS - 16));
-
-	// clip
-	if (sample > MAD_F_ONE - 1)
-		sample = MAD_F_ONE - 1;
-	else if (sample < -MAD_F_ONE)
-		sample = -MAD_F_ONE;
-
-	// quantize and scale to not saturate when mixing a lot of channels
-	return sample >> (MAD_F_FRACBITS + 1 - 16);
-}
-
-inline int16 MP3InputStream::read() {
-	assert(!eosIntern());
-
-	int16 sample;
-	if (_isStereo) {
-		sample = (int16)scale_sample(_synth.pcm.samples[_curChannel][_posInFrame]);
-		if (_curChannel == 0) {
-			_curChannel = 1;
-		} else {
-			_posInFrame++;
-			_curChannel = 0;
+public:
+	ProcInputStream(int rate, bool stereo, InputProc *proc, void *refCon)
+		: _rate(rate), _isStereo(stereo), _proc(proc), _refCon(refCon), _len(0) { }
+	int readBuffer(int16 *buffer, const int numSamples) {
+		int remSamples = numSamples;
+		while (remSamples > 0) {
+			if (_len == 0)
+				refill();
+			// Copy data to the output
+			int samples = MIN(_len, remSamples);
+			memcpy(buffer, _pos, samples * sizeof(int16));
+			_pos += samples;
+			_len -= samples;
+			buffer += samples;
+			remSamples -= samples;
 		}
-	} else {
-		sample = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
-		_posInFrame++;
-	}
-
-	if (_posInFrame >= _synth.pcm.length) {
-		refill();
+		return numSamples;
 	}
-
-	return sample;
-}
-
-int MP3InputStream::readBuffer(int16 *buffer, const int numSamples) {
-	int samples = 0;
-	assert(_curChannel == 0);	// Paranoia check
-	while (samples < numSamples && !eosIntern()) {
-		const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * (_isStereo ? 2 : 1));
-		while (samples < len) {
-			*buffer++ = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
-			samples++;
-			if (_isStereo) {
-				*buffer++ = (int16)scale_sample(_synth.pcm.samples[1][_posInFrame]);
-				samples++;
-			}
-			_posInFrame++;
-		}
-		if (_posInFrame >= _synth.pcm.length) {
+	int16 read() {
+		if (_len == 0)
 			refill();
-		}
+		_len--;
+		return *_pos++;
 	}
-	return samples;
-}
-
-AudioInputStream *makeMP3Stream(File *file, mad_timer_t duration, uint size) {
-	return new MP3InputStream(file, duration, size);
-}
-
-#endif
-
-
-#pragma mark -
-#pragma mark --- Ogg Vorbis stream ---
-#pragma mark -
-
-
-#ifdef USE_VORBIS
-
-class VorbisInputStream : public AudioInputStream {
-	OggVorbis_File *_ov_file;
-	int _end_pos;
-	int _numChannels;
-	int16 _buffer[4096];
-	const int16 *_bufferEnd;
-	const int16 *_pos;
-	
-	void refill();
-	inline bool eosIntern() const;
-public:
-	VorbisInputStream(OggVorbis_File *file, int duration);
-	int readBuffer(int16 *buffer, const int numSamples);
-
-	int16 read();
-	bool eos() const			{ return eosIntern(); }
-	bool isStereo() const		{ return _numChannels >= 2; }
+	bool isStereo() const { return _isStereo; }
+	bool eos() const { return false; }
 	
-	int getRate() const			{ return ov_info(_ov_file, -1)->rate; }
+	int getRate() const { return _rate; }
 };
-
-
-#ifdef CHUNKSIZE
-#define VORBIS_TREMOR
-#endif
-
-
-VorbisInputStream::VorbisInputStream(OggVorbis_File *file, int duration) 
-	: _ov_file(file), _bufferEnd(_buffer + ARRAYSIZE(_buffer)) {
-
-	// Check the header, determine if this is a stereo stream
-	_numChannels = ov_info(_ov_file, -1)->channels;
-
-	// Determine the end position
-	if (duration)
-		_end_pos = ov_pcm_tell(_ov_file) + duration;
-	else
-		_end_pos = ov_pcm_total(_ov_file, -1);
-
-	// Read in initial data
-	refill();
-}
-
-inline int16 VorbisInputStream::read() {
-	assert(!eosIntern());
-
-	int16 sample = *_pos++;
-	if (_pos >= _bufferEnd) {
-		refill();
-	}
-	return sample;
-}
-
-inline bool VorbisInputStream::eosIntern() const {
-	return _pos >= _bufferEnd;
-}
-
-int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) {
-	int samples = 0;
-	while (samples < numSamples && !eosIntern()) {
-		const int len = MIN(numSamples, samples + (int)(_bufferEnd - _pos));
-		memcpy(buffer, _pos, len * 2);
-		buffer += len;
-		_pos += len;
-		samples += len;
-		if (_pos >= _bufferEnd) {
-			refill();
-		}
-	}
-	return samples;
-}
-
-void VorbisInputStream::refill() {
-	// Read the samples
-	uint len_left = sizeof(_buffer);
-	char *read_pos = (char *)_buffer;
-
-	while (len_left > 0 && _end_pos > ov_pcm_tell(_ov_file)) {
-		long result = ov_read(_ov_file, read_pos, len_left,
-#ifndef VORBIS_TREMOR
-#ifdef SCUMM_BIG_ENDIAN
-						1,
-#else
-						0,
-#endif
-						2,	// 16 bit
-						1,	// signed
-#endif
-						NULL);
-		if (result == OV_HOLE) {
-			// Possibly recoverable, just warn about it
-			warning("Corrupted data in Vorbis file");
-		} else if (result <= 0) {
-			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.
-			memset(read_pos, 0, len_left);
-			break;
-		} else {
-			len_left -= result;
-			read_pos += result;
-		}
-	}
-
-	_pos = _buffer;
-	_bufferEnd = (int16 *)read_pos;
-}
-
-AudioInputStream *makeVorbisStream(OggVorbis_File *file, int duration) {
-	return new VorbisInputStream(file, duration);
-}
-
 #endif
 
 

Index: audiostream.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/audiostream.h,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -d -r1.23 -r1.24
--- audiostream.h	17 Dec 2003 02:19:24 -0000	1.23
+++ audiostream.h	19 Dec 2003 00:32:47 -0000	1.24
@@ -25,22 +25,8 @@
 #include "stdafx.h"
 #include "common/scummsys.h"
 #include "common/util.h"
-#ifdef USE_MAD
-#include <mad.h>
-#endif
-#ifdef USE_VORBIS
-#include <vorbis/vorbisfile.h>
-#endif
-
-class File;
 
 
-// TODO:
-// * maybe make readIntern return 16.16 or 24.8 fixed point values
-//   since MAD (and maybe OggVorbis?) gives us those -> higher quality.
-//   The rate converters should be able to deal with those just fine, too.
-// * possibly add MADInputStream and VorbisInputStream
-
 /**
  * Generic input stream for the resampling code.
  */
@@ -56,19 +42,12 @@
 	 * happen when the stream is fully used up).
 	 * For stereo stream, buffer will be filled with interleaved
 	 * left and right channel samples.
-	 *
-	 * For maximum efficency, subclasses should always override
-	 * the default implementation!
 	 */
-	virtual int readBuffer(int16 *buffer, const int numSamples) {
-		int samples;
-		for (samples = 0; samples < numSamples && !eos(); samples++) {
-			*buffer++ = read();
-		}
-		return samples;
-	}
+	virtual int readBuffer(int16 *buffer, const int numSamples) = 0;
 
-	/** Read a single (16 bit signed) sample from the stream. */
+	/**
+	 * Read a single (16 bit signed) sample from the stream.
+	 */
 	virtual int16 read() = 0;
 	
 	/** Is this a stereo stream? */
@@ -88,7 +67,7 @@
 };
 
 class ZeroInputStream : public AudioInputStream {
-protected:
+private:
 	int _len;
 public:
 	ZeroInputStream(uint len) : _len(len) { }
@@ -99,7 +78,6 @@
 		return samples;
 	}
 	int16 read() { assert(_len > 0); _len--; return 0; }
-	int size() const { return _len; }
 	bool isStereo() const { return false; }
 	bool eos() const { return _len <= 0; }
 	
@@ -108,14 +86,5 @@
 
 AudioInputStream *makeLinearInputStream(int rate, byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen);
 WrappedAudioInputStream *makeWrappedInputStream(int rate, byte _flags, uint32 len);
-
-#ifdef USE_MAD
-AudioInputStream *makeMP3Stream(File *file, mad_timer_t duration, uint size = 0);
-#endif
-
-#ifdef USE_VORBIS
-AudioInputStream *makeVorbisStream(OggVorbis_File *file, int duration);
-#endif
-
 
 #endif

Index: mixer.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/mixer.cpp,v
retrieving revision 1.137
retrieving revision 1.138
diff -u -d -r1.137 -r1.138
--- mixer.cpp	18 Dec 2003 11:51:58 -0000	1.137
+++ mixer.cpp	19 Dec 2003 00:32:47 -0000	1.138
@@ -27,6 +27,8 @@
 #include "sound/mixer.h"
 #include "sound/rate.h"
 #include "sound/audiostream.h"
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
 
 
 #pragma mark -
@@ -242,52 +244,51 @@
 				return -1;
 	}
 
-	return insertChannel(handle, new ChannelRaw(this, handle, sound, size, rate, flags, volume, pan, id, loopStart, loopEnd));
+	Channel *chan = new ChannelRaw(this, handle, sound, size, rate, flags, volume, pan, id, loopStart, loopEnd);
+	return insertChannel(handle, chan);
 }
 
 #ifdef USE_MAD
 int SoundMixer::playMP3(PlayingSoundHandle *handle, File *file, uint32 size, byte volume, int8 pan) {
-	Common::StackLock lock(_mutex);
-
 	// Create the input stream
 	AudioInputStream *input = makeMP3Stream(file, mad_timer_zero, size);
-	Channel *chan = new Channel(this, handle, input, false, volume, pan);
-	return insertChannel(handle, chan);
+	return playInputStream(handle, input, false, volume, pan);
 }
 int SoundMixer::playMP3CDTrack(PlayingSoundHandle *handle, File *file, mad_timer_t duration, byte volume, int8 pan) {
-	Common::StackLock lock(_mutex);
-
 	// Create the input stream
 	AudioInputStream *input = makeMP3Stream(file, duration, 0);
-	Channel *chan = new Channel(this, handle, input, true, volume, pan);
-	return insertChannel(handle, chan);
+	return playInputStream(handle, input, true, volume, pan);
 }
 #endif
 
 #ifdef USE_VORBIS
 int SoundMixer::playVorbis(PlayingSoundHandle *handle, OggVorbis_File *ov_file, int duration, bool is_cd_track, byte volume, int8 pan) {
-	Common::StackLock lock(_mutex);
-
 	// Create the input stream
 	AudioInputStream *input = makeVorbisStream(ov_file, duration);
-	Channel *chan = new Channel(this, handle, input, is_cd_track, volume, pan);
-	return insertChannel(handle, chan);
+	return playInputStream(handle, input, is_cd_track, volume, pan);
 }
 #endif
 
+int SoundMixer::playInputStream(PlayingSoundHandle *handle, AudioInputStream *input, bool isMusic, byte volume, int8 pan) {
+	Common::StackLock lock(_mutex);
+
+	// Create the channel
+	Channel *chan = new Channel(this, handle, input, isMusic, volume, pan);
+	return insertChannel(handle, chan);
+}
+
 void SoundMixer::mix(int16 *buf, uint len) {
 #ifndef __PALM_OS__
 	Common::StackLock lock(_mutex);
 #endif
 
-	if (_premixProc && !_paused) {
-		_premixProc(_premixParam, buf, len);
-	} else {
-		//  zero the buf out
-		memset(buf, 0, 2 * len * sizeof(int16));
-	}
+	//  zero the buf
+	memset(buf, 0, 2 * len * sizeof(int16));
 
 	if (!_paused) {
+		if (_premixProc)
+			_premixProc(_premixParam, buf, len);
+
 		// now mix all channels
 		for (int i = 0; i != NUM_CHANNELS; i++)
 			if (_channels[i] && !_channels[i]->isPaused())

Index: mixer.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/mixer.h,v
retrieving revision 1.60
retrieving revision 1.61
diff -u -d -r1.60 -r1.61
--- mixer.h	16 Dec 2003 15:34:17 -0000	1.60
+++ mixer.h	19 Dec 2003 00:32:47 -0000	1.61
@@ -37,6 +37,7 @@
 
 typedef uint32 PlayingSoundHandle;
 
+class AudioInputStream;
 class Channel;
 class File;
 
@@ -105,6 +106,9 @@
 #ifdef USE_VORBIS
 	int playVorbis(PlayingSoundHandle *handle, OggVorbis_File *ov_file, int duration, bool is_cd_track, byte volume = 255, int8 pan = 0);
 #endif
+
+	int playInputStream(PlayingSoundHandle *handle, AudioInputStream *input, bool isMusic, byte volume = 255, int8 pan = 0);
+
 
 	/** Start a new stream. */
 	int newStream(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume = 255, int8 pan = 0);

Index: mp3.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/mp3.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- mp3.cpp	29 Nov 2003 12:09:51 -0000	1.1
+++ mp3.cpp	19 Dec 2003 00:32:47 -0000	1.2
@@ -19,13 +19,13 @@
  *
  */
 
-#include "stdafx.h"
-
 #include "sound/mp3.h"
+#include "sound/audiostream.h"
 #include "common/file.h"
 #include "common/util.h"
 
 #ifdef USE_MAD
+
 MP3TrackInfo::MP3TrackInfo(File *file) {
 	struct mad_stream stream;
 	struct mad_frame frame;
@@ -123,5 +123,240 @@
 	if (! _error_flag)
 		_file->close();
 }
+
+
+#pragma mark -
+#pragma mark --- MP3 (MAD) stream ---
+#pragma mark -
+
+
+class MP3InputStream : public AudioInputStream {
+	struct mad_stream _stream;
+	struct mad_frame _frame;
+	struct mad_synth _synth;
+	mad_timer_t _duration;
+	uint32 _posInFrame;
+	uint32 _bufferSize;
+	int _size;
+	bool _isStereo;
+	int _curChannel;
+	File *_file;
+	byte *_ptr;
+
+	bool init();
+	void refill(bool first = false);
+	inline bool eosIntern() const;
+public:
+	MP3InputStream(File *file, mad_timer_t duration, uint size = 0);
+	~MP3InputStream();
+	int readBuffer(int16 *buffer, const int numSamples);
+
+	int16 read();
+	bool eos() const			{ return eosIntern(); }
+	bool isStereo() const		{ return _isStereo; }
+	
+	int getRate() const			{ return _frame.header.samplerate; }
+};
+
+
+/**
+ * Playback the MP3 data in the given file for the specified duration.
+ *
+ * @param file		file containing the MP3 data
+ * @param duration	playback duration in frames (1/75th of a second), 0 means
+ *					playback until EOF
+ * @param size		optional, if non-zero this limits playback based on the
+ * 					number of input bytes rather then a duration
+ */
+MP3InputStream::MP3InputStream(File *file, mad_timer_t duration, uint size) {
+	// duration == 0 means: play everything till end of file
+
+	mad_stream_init(&_stream);
+	mad_frame_init(&_frame);
+	mad_synth_init(&_synth);
+
+	_duration = duration;
+
+	_posInFrame = 0;
+	_bufferSize = size ? size : (128 * 1024);	// Default buffer size is 128K
+
+	_isStereo = false;
+	_curChannel = 0;
+	_file = file;
+	_ptr = (byte *)malloc(_bufferSize + MAD_BUFFER_GUARD);
+
+	init();
+
+	// If a size is specified, we do not perform any further read operations
+	if (size) {
+		_file = 0;
+	}
+}
+
+MP3InputStream::~MP3InputStream() {
+	mad_synth_finish(&_synth);
+	mad_frame_finish(&_frame);
+	mad_stream_finish(&_stream);
+
+	free(_ptr);
+}
+
+bool MP3InputStream::init() {
+	// TODO
+
+	// Read in the first chunk of the MP3 file
+	_size = _file->read(_ptr, _bufferSize);
+	if (_size <= 0) {
+		warning("MP3InputStream: Failed to read MP3 data");
+		return false;
+	}
+
+	// Feed the data we just read into the stream decoder
+	mad_stream_buffer(&_stream, _ptr, _size);
+
+	// Read in initial data
+	refill(true);
+
+	// Check the header, determine if this is a stereo stream
+	int num;
+	switch(_frame.header.mode) {
+		case MAD_MODE_SINGLE_CHANNEL:
+		case MAD_MODE_DUAL_CHANNEL:
+		case MAD_MODE_JOINT_STEREO:
+		case MAD_MODE_STEREO:
+			num = MAD_NCHANNELS(&_frame.header);
+			assert(num == 1 || num == 2);
+			_isStereo = (num == 2);
+			break;
+		default:
+			warning("MP3InputStream: Cannot determine number of channels");
+			return false;
+	}
+	
+	return true;
+}
+
+void MP3InputStream::refill(bool first) {
+
+	// Read the next frame (may have to retry several times, e.g.
+	// to skip over ID3 information).
+	while (mad_frame_decode(&_frame, &_stream)) {
+		if (_stream.error == MAD_ERROR_BUFLEN) {
+			int offset;
+
+			if (!_file)
+				_size = -1;
+
+			// Give up immediately if we are at the EOF already
+			if (_size <= 0)
+				return;
+
+			if (!_stream.next_frame) {
+				offset = 0;
+				memset(_ptr, 0, _bufferSize + MAD_BUFFER_GUARD);
+			} else {
+				offset = _stream.bufend - _stream.next_frame;
+				memcpy(_ptr, _stream.next_frame, offset);
+			}
+			// Read in more data from the input file
+			_size = _file->read(_ptr + offset, _bufferSize - offset);
+
+			// Nothing read -> EOF -> bail out
+			if (_size <= 0) {
+				return;
+			}
+			_stream.error = (enum mad_error)0;
+
+			// Feed the data we just read into the stream decoder
+			mad_stream_buffer(&_stream, _ptr, _size + offset);
+
+		} else if (MAD_RECOVERABLE(_stream.error)) {
+			// FIXME: should we do anything here?
+			debug(6, "MP3InputStream: Recoverable error...");
+		} else {
+			error("MP3InputStream: Unrecoverable error");
+		}
+	}
+
+	// Subtract the duration of this frame from the time left to play
+	mad_timer_t frame_duration = _frame.header.duration;
+	mad_timer_negate(&frame_duration);
+	mad_timer_add(&_duration, frame_duration);
+
+	if (!first && _file && mad_timer_compare(_duration, mad_timer_zero) <= 0)
+		_size = -1;	// Mark for EOF
+
+	// Synthesise the frame into PCM samples and reset the buffer position
+	mad_synth_frame(&_synth, &_frame);
+	_posInFrame = 0;
+}
+
+inline bool MP3InputStream::eosIntern() const {
+	return (_size < 0 || _posInFrame >= _synth.pcm.length);
+}
+
+static inline int scale_sample(mad_fixed_t sample) {
+	// round
+	sample += (1L << (MAD_F_FRACBITS - 16));
+
+	// clip
+	if (sample > MAD_F_ONE - 1)
+		sample = MAD_F_ONE - 1;
+	else if (sample < -MAD_F_ONE)
+		sample = -MAD_F_ONE;
+
+	// quantize and scale to not saturate when mixing a lot of channels
+	return sample >> (MAD_F_FRACBITS + 1 - 16);
+}
+
+inline int16 MP3InputStream::read() {
+	assert(!eosIntern());
+
+	int16 sample;
+	if (_isStereo) {
+		sample = (int16)scale_sample(_synth.pcm.samples[_curChannel][_posInFrame]);
+		if (_curChannel == 0) {
+			_curChannel = 1;
+		} else {
+			_posInFrame++;
+			_curChannel = 0;
+		}
+	} else {
+		sample = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
+		_posInFrame++;
+	}
+
+	if (_posInFrame >= _synth.pcm.length) {
+		refill();
+	}
+
+	return sample;
+}
+
+int MP3InputStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samples = 0;
+	assert(_curChannel == 0);	// Paranoia check
+	while (samples < numSamples && !eosIntern()) {
+		const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * (_isStereo ? 2 : 1));
+		while (samples < len) {
+			*buffer++ = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
+			samples++;
+			if (_isStereo) {
+				*buffer++ = (int16)scale_sample(_synth.pcm.samples[1][_posInFrame]);
+				samples++;
+			}
+			_posInFrame++;
+		}
+		if (_posInFrame >= _synth.pcm.length) {
+			refill();
+		}
+	}
+	return samples;
+}
+
+AudioInputStream *makeMP3Stream(File *file, mad_timer_t duration, uint size) {
+	return new MP3InputStream(file, duration, size);
+}
+
 
 #endif

Index: mp3.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/mp3.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- mp3.h	29 Nov 2003 12:09:51 -0000	1.1
+++ mp3.h	19 Dec 2003 00:32:47 -0000	1.2
@@ -22,11 +22,17 @@
 #ifndef SOUND_MP3_H
 #define SOUND_MP3_H
 
+#include "stdafx.h"
+#include "common/scummsys.h"
+
+#ifdef USE_MAD
+
 #include "sound/audiocd.h"
+#include <mad.h>
 
+class AudioInputStream;
 class File;
 
-#ifdef USE_MAD
 class MP3TrackInfo : public DigitalTrackInfo {
 private:
 	struct mad_header _mad_header;
@@ -40,8 +46,9 @@
 	bool error() { return _error_flag; }
 	int play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration);
 };
-#endif
 
+AudioInputStream *makeMP3Stream(File *file, mad_timer_t duration, uint size = 0);
 
+#endif
 
 #endif

Index: vorbis.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/vorbis.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- vorbis.cpp	29 Nov 2003 12:09:51 -0000	1.1
+++ vorbis.cpp	19 Dec 2003 00:32:47 -0000	1.2
@@ -19,13 +19,13 @@
  *
  */
 
-#include "stdafx.h"
-
 #include "sound/vorbis.h"
+#include "sound/audiostream.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.
 
@@ -86,9 +86,7 @@
 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;
@@ -146,5 +144,125 @@
 	} else
 		mixer->playVorbis(handle, ov_file, 0, false);
 }
+
+
+#pragma mark -
+#pragma mark --- Ogg Vorbis stream ---
+#pragma mark -
+
+
+class VorbisInputStream : public AudioInputStream {
+	OggVorbis_File *_ov_file;
+	int _end_pos;
+	int _numChannels;
+	int16 _buffer[4096];
+	const int16 *_bufferEnd;
+	const int16 *_pos;
+	
+	void refill();
+	inline bool eosIntern() const;
+public:
+	VorbisInputStream(OggVorbis_File *file, int duration);
+	int readBuffer(int16 *buffer, const int numSamples);
+
+	int16 read();
+	bool eos() const			{ return eosIntern(); }
+	bool isStereo() const		{ return _numChannels >= 2; }
+	
+	int getRate() const			{ return ov_info(_ov_file, -1)->rate; }
+};
+
+
+#ifdef CHUNKSIZE
+#define VORBIS_TREMOR
+#endif
+
+
+VorbisInputStream::VorbisInputStream(OggVorbis_File *file, int duration) 
+	: _ov_file(file), _bufferEnd(_buffer + ARRAYSIZE(_buffer)) {
+
+	// Check the header, determine if this is a stereo stream
+	_numChannels = ov_info(_ov_file, -1)->channels;
+
+	// Determine the end position
+	if (duration)
+		_end_pos = ov_pcm_tell(_ov_file) + duration;
+	else
+		_end_pos = ov_pcm_total(_ov_file, -1);
+
+	// Read in initial data
+	refill();
+}
+
+inline int16 VorbisInputStream::read() {
+	assert(!eosIntern());
+
+	int16 sample = *_pos++;
+	if (_pos >= _bufferEnd) {
+		refill();
+	}
+	return sample;
+}
+
+inline bool VorbisInputStream::eosIntern() const {
+	return _pos >= _bufferEnd;
+}
+
+int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samples = 0;
+	while (samples < numSamples && !eosIntern()) {
+		const int len = MIN(numSamples, samples + (int)(_bufferEnd - _pos));
+		memcpy(buffer, _pos, len * 2);
+		buffer += len;
+		_pos += len;
+		samples += len;
+		if (_pos >= _bufferEnd) {
+			refill();
+		}
+	}
+	return samples;
+}
+
+void VorbisInputStream::refill() {
+	// Read the samples
+	uint len_left = sizeof(_buffer);
+	char *read_pos = (char *)_buffer;
+
+	while (len_left > 0 && _end_pos > ov_pcm_tell(_ov_file)) {
+		long result = ov_read(_ov_file, read_pos, len_left,
+#ifndef VORBIS_TREMOR
+#ifdef SCUMM_BIG_ENDIAN
+						1,
+#else
+						0,
+#endif
+						2,	// 16 bit
+						1,	// signed
+#endif
+						NULL);
+		if (result == OV_HOLE) {
+			// Possibly recoverable, just warn about it
+			warning("Corrupted data in Vorbis file");
+		} else if (result <= 0) {
+			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.
+			memset(read_pos, 0, len_left);
+			break;
+		} else {
+			len_left -= result;
+			read_pos += result;
+		}
+	}
+
+	_pos = _buffer;
+	_bufferEnd = (int16 *)read_pos;
+}
+
+AudioInputStream *makeVorbisStream(OggVorbis_File *file, int duration) {
+	return new VorbisInputStream(file, duration);
+}
+
 
 #endif

Index: vorbis.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/vorbis.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- vorbis.h	29 Nov 2003 12:09:51 -0000	1.1
+++ vorbis.h	19 Dec 2003 00:32:47 -0000	1.2
@@ -22,12 +22,17 @@
 #ifndef SOUND_VORBIS_H
 #define SOUND_VORBIS_H
 
+#include "stdafx.h"
+#include "common/scummsys.h"
+
+#ifdef USE_VORBIS
+
 #include "sound/audiocd.h"
+#include <vorbis/vorbisfile.h>
 
+class AudioInputStream;
 class File;
 
-#ifdef USE_VORBIS
-
 class VorbisTrackInfo : public DigitalTrackInfo {
 private:
 	File *_file;
@@ -43,6 +48,8 @@
 
 
 void playSfxSound_Vorbis(SoundMixer *mixer, File *file, uint32 size, PlayingSoundHandle *handle);
+
+AudioInputStream *makeVorbisStream(OggVorbis_File *file, int duration);
 
 #endif
 





More information about the Scummvm-git-logs mailing list