aboutsummaryrefslogtreecommitdiff
path: root/sound/audiostream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sound/audiostream.cpp')
-rw-r--r--sound/audiostream.cpp389
1 files changed, 44 insertions, 345 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