aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorMax Horn2003-12-19 00:32:47 +0000
committerMax Horn2003-12-19 00:32:47 +0000
commitd21fc5845dc77dd977df22fd9ddc3206bf23a920 (patch)
treeeed9abe12e4a103c469f875cb6dfebab746c74a4 /sound
parent97ee61963c896e48a0acc5b4169ce96163e76b06 (diff)
downloadscummvm-rg350-d21fc5845dc77dd977df22fd9ddc3206bf23a920.tar.gz
scummvm-rg350-d21fc5845dc77dd977df22fd9ddc3206bf23a920.tar.bz2
scummvm-rg350-d21fc5845dc77dd977df22fd9ddc3206bf23a920.zip
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 svn-id: r11754
Diffstat (limited to 'sound')
-rw-r--r--sound/audiostream.cpp389
-rw-r--r--sound/audiostream.h41
-rw-r--r--sound/mixer.cpp39
-rw-r--r--sound/mixer.h4
-rw-r--r--sound/mp3.cpp239
-rw-r--r--sound/mp3.h11
-rw-r--r--sound/vorbis.cpp126
-rw-r--r--sound/vorbis.h11
8 files changed, 450 insertions, 410 deletions
diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp
index 3fcdb64051..8622255ff9 100644
--- a/sound/audiostream.cpp
+++ b/sound/audiostream.cpp
@@ -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 @@ void WrappedMemoryStream<stereo, is16Bit, isUnsigned>::append(const byte *data,
#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;
-
- 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
+#if 0
+// Work in progress!!! Not yet usable/finished/working/anything :-)
- _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;
- }
+class ProcInputStream : public AudioInputStream {
+public:
+ 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);
-
- // 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;
-}
+private:
+ const int _rate;
+ const bool _isStereo;
+ InputProc *_proc;
+ void *_refCon;
+ int16 _buffer[2048];
+ const int16 *_pos;
+ int _len;
-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");
- }
+ 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
diff --git a/sound/audiostream.h b/sound/audiostream.h
index ef702d3259..2a5934fb80 100644
--- a/sound/audiostream.h
+++ b/sound/audiostream.h
@@ -25,21 +25,7 @@
#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 @@ public:
* 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 @@ public:
};
class ZeroInputStream : public AudioInputStream {
-protected:
+private:
int _len;
public:
ZeroInputStream(uint len) : _len(len) { }
@@ -99,7 +78,6 @@ public:
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; }
@@ -109,13 +87,4 @@ public:
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
diff --git a/sound/mixer.cpp b/sound/mixer.cpp
index 271cf84393..5278071c1e 100644
--- a/sound/mixer.cpp
+++ b/sound/mixer.cpp
@@ -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 @@ int SoundMixer::playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, ui
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())
diff --git a/sound/mixer.h b/sound/mixer.h
index dcea973826..387883ce59 100644
--- a/sound/mixer.h
+++ b/sound/mixer.h
@@ -37,6 +37,7 @@
typedef uint32 PlayingSoundHandle;
+class AudioInputStream;
class Channel;
class File;
@@ -106,6 +107,9 @@ public:
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);
diff --git a/sound/mp3.cpp b/sound/mp3.cpp
index db73f7f394..f23a4e4248 100644
--- a/sound/mp3.cpp
+++ b/sound/mp3.cpp
@@ -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;
@@ -124,4 +124,239 @@ MP3TrackInfo::~MP3TrackInfo() {
_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
diff --git a/sound/mp3.h b/sound/mp3.h
index fd5be63387..bc19deca24 100644
--- a/sound/mp3.h
+++ b/sound/mp3.h
@@ -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 @@ public:
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
diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp
index cd1a84ad08..fb8ec9274a 100644
--- a/sound/vorbis.cpp
+++ b/sound/vorbis.cpp
@@ -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 long tell_wrap(void *datasource) {
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;
@@ -147,4 +145,124 @@ void playSfxSound_Vorbis(SoundMixer *mixer, File *file, uint32 size, PlayingSoun
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
diff --git a/sound/vorbis.h b/sound/vorbis.h
index fcffc1c50d..21d17f3667 100644
--- a/sound/vorbis.h
+++ b/sound/vorbis.h
@@ -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;
@@ -44,6 +49,8 @@ public:
void playSfxSound_Vorbis(SoundMixer *mixer, File *file, uint32 size, PlayingSoundHandle *handle);
+AudioInputStream *makeVorbisStream(OggVorbis_File *file, int duration);
+
#endif
#endif