aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorMax Horn2003-07-31 01:21:38 +0000
committerMax Horn2003-07-31 01:21:38 +0000
commit9b2d4f92aab4ecf52afd49865a3b7efa879391b8 (patch)
treec7ccfc9ddf1c880c9cdf4764a824a17390ee852a /sound
parent5225597d85089935eb25ed57c0e8d7d94681ad68 (diff)
downloadscummvm-rg350-9b2d4f92aab4ecf52afd49865a3b7efa879391b8.tar.gz
scummvm-rg350-9b2d4f92aab4ecf52afd49865a3b7efa879391b8.tar.bz2
scummvm-rg350-9b2d4f92aab4ecf52afd49865a3b7efa879391b8.zip
removed the AudioInputStream::size method -> only eof() is really needed, and this can be implemented more efficiently stand-alone; implemented MP3InputStream (work in progress)
svn-id: r9313
Diffstat (limited to 'sound')
-rw-r--r--sound/audiostream.cpp188
-rw-r--r--sound/audiostream.h53
-rw-r--r--sound/rate.cpp3
3 files changed, 197 insertions, 47 deletions
diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp
index ea4c0534f9..0d769e3df7 100644
--- a/sound/audiostream.cpp
+++ b/sound/audiostream.cpp
@@ -23,6 +23,7 @@
#include "audiostream.h"
#include "mixer.h"
#include "common/engine.h"
+#include "common/file.h"
#include "common/util.h"
@@ -57,8 +58,8 @@ public:
_ptr += (is16Bit ? 2 : 1);
return val;
}
- int size() const {
- return (_end - _ptr) / (is16Bit ? 2 : 1);
+ bool eof() const {
+ return _end <= _ptr;
}
bool isStereo() const {
return stereo;
@@ -84,7 +85,7 @@ public:
WrappedMemoryStream(uint bufferSize);
~WrappedMemoryStream() { free(_bufferStart); }
int16 read();
- int size() const;
+ bool eof() const;
bool isStereo() const {
return stereo;
}
@@ -116,11 +117,8 @@ int16 WrappedMemoryStream<stereo, is16Bit, isUnsigned>::read() {
}
template<bool stereo, bool is16Bit, bool isUnsigned>
-int WrappedMemoryStream<stereo, is16Bit, isUnsigned>::size() const {
- int len = _end - _pos;
- if (len < 0)
- len += (_bufferEnd - _bufferStart);
- return len / (is16Bit ? 2 : 1);
+bool WrappedMemoryStream<stereo, is16Bit, isUnsigned>::eof() const {
+ return _end == _pos;
}
template<bool stereo, bool is16Bit, bool isUnsigned>
@@ -152,27 +150,144 @@ void WrappedMemoryStream<stereo, is16Bit, isUnsigned>::append(const byte *data,
#pragma mark -
-/*
#ifdef USE_MAD
-class MP3InputStream : public AudioInputStream {
- struct mad_stream _stream;
- struct mad_frame _frame;
- struct mad_synth _synth;
- uint32 _posInFrame;
- int _chan;
-
- void refill();
-public:
+
+#define MP3_BUFFER_SIZE 131072
+
+/**
+ * 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
+ */
+MP3InputStream::MP3InputStream(File *file, mad_timer_t duration) {
+ // duration == 0 means: play everything till end of file
+
+ _isStereo = false;
+ _curChannel = 0;
+ _file = file;
+ _rate = 0;
+ _posInFrame = 0;
+
+ _duration = duration;
+
+ mad_stream_init(&_stream);
+ mad_frame_init(&_frame);
+ mad_synth_init(&_synth);
+
+ _ptr = (byte *)malloc(MP3_BUFFER_SIZE + MAD_BUFFER_GUARD);
+
+ _initialized = init();
+}
+
+MP3InputStream::~MP3InputStream() {
+ mad_synth_finish(&_synth);
+ mad_frame_finish(&_frame);
+ mad_stream_finish(&_stream);
+
+ free(_ptr);
+}
+
+bool MP3InputStream::init() {
// TODO
- MP3InputStream();
-};
+
+ // Read in the first chunk of the MP3 file
+ _size = _file->read(_ptr, MP3_BUFFER_SIZE);
+ 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();
+
+ // 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;
+ }
+
+ // Determine the sample rate
+ _rate = _frame.header.samplerate;
-MP3InputStream::MP3InputStream() {
- _chan = 0;
+ return true;
}
void MP3InputStream::refill() {
- // TODO
+
+ // 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;
+
+ // Give up immediately if we are at the EOF already
+ if (_size <= 0)
+ return;
+
+ if (!_stream.next_frame) {
+ offset = 0;
+ memset(_ptr, 0, MP3_BUFFER_SIZE + 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, MP3_BUFFER_SIZE - 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?
+ warning("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.header.duration);
+
+ // Synthesise the frame into PCM samples and reset the buffer position
+ mad_synth_frame(&_synth, &_frame);
+ _posInFrame = 0;
+}
+
+bool MP3InputStream::eof() const {
+ // Time over -> input steam ends
+ if (mad_timer_compare(_duration, mad_timer_zero) <= 0)
+ return true;
+ // Data left in the PCM buffer -> we are not yet done!
+ if (_posInFrame < _synth.pcm.length)
+ return false;
+ // EOF of the input file, we are done
+ if (_size < 0)
+ return true;
+ // Otherwise, we are still good to go
+ return false;
}
static inline int scale_sample(mad_fixed_t sample) {
@@ -192,19 +307,22 @@ static inline int scale_sample(mad_fixed_t sample) {
int16 MP3InputStream::read() {
if (_posInFrame >= _synth.pcm.length) {
refill();
+ if (_size < 0) // EOF
+ return 0;
}
+
int16 sample;
- if (stereo) {
- sample = (int16)scale_sample(_synth.pcm.samples[_chan][_posInFrame];
- if (_chan == 0) {
- _chan = 1;
+ if (_isStereo) {
+ sample = (int16)scale_sample(_synth.pcm.samples[_curChannel][_posInFrame]);
+ if (_curChannel == 0) {
+ _curChannel = 1;
} else {
_posInFrame++;
- _chan = 0;
+ _curChannel = 0;
}
} else {
- sample = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame];
+ sample = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
_posInFrame++;
}
@@ -212,7 +330,7 @@ int16 MP3InputStream::read() {
}
#endif
-*/
+
#pragma mark -
#pragma mark --- Ogg Vorbis stream ---
@@ -229,7 +347,7 @@ int16 MP3InputStream::read() {
VorbisInputStream::VorbisInputStream(OggVorbis_File *file, int duration)
: _ov_file(file) {
_pos = _buffer + ARRAYSIZE(_buffer);
- _channels = ov_info(_ov_file, -1)->channels;
+ _numChannels = ov_info(_ov_file, -1)->channels;
if (duration)
_end_pos = ov_pcm_tell(_ov_file) + duration;
@@ -246,10 +364,12 @@ int16 VorbisInputStream::read() {
return *_pos++;
}
-int VorbisInputStream::size() const {
+bool VorbisInputStream::eof() const {
if (_eof_flag)
- return 0;
- return (_end_pos - ov_pcm_tell(_ov_file)) + (_buffer + ARRAYSIZE(_buffer) - _pos);
+ return true;
+ if (_pos < _buffer + ARRAYSIZE(_buffer))
+ return false;
+ return (_end_pos <= ov_pcm_tell(_ov_file));
}
void VorbisInputStream::refill() {
diff --git a/sound/audiostream.h b/sound/audiostream.h
index c7c2614230..a40e27aaef 100644
--- a/sound/audiostream.h
+++ b/sound/audiostream.h
@@ -24,10 +24,16 @@
#include "scummsys.h"
#include <assert.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.
@@ -42,10 +48,9 @@ public:
virtual ~AudioInputStream() {}
virtual int16 read() = 0;
- virtual int size() const = 0;
+ //virtual int size() const = 0;
virtual bool isStereo() const = 0;
-
- bool eof() const { return size() <= 0; }
+ virtual bool eof() const = 0;
};
class WrappedAudioInputStream : public AudioInputStream {
@@ -61,11 +66,36 @@ public:
int16 read() { assert(_len > 0); _len--; return 0; }
int size() const { return _len; }
bool isStereo() const { return false; }
+ bool eof() const { return _len <= 0; }
};
-AudioInputStream *makeLinearInputStream(byte _flags, const byte *ptr, uint32 len);
-WrappedAudioInputStream *makeWrappedInputStream(byte _flags, uint32 len);
-
+#ifdef USE_MAD
+class MP3InputStream : public AudioInputStream {
+ struct mad_stream _stream;
+ struct mad_frame _frame;
+ struct mad_synth _synth;
+ uint32 _posInFrame;
+ int _size;
+ bool _isStereo;
+ int _curChannel;
+ File *_file;
+ byte *_ptr;
+ int _rate;
+ bool _initialized;
+ mad_timer_t _duration;
+
+ bool init();
+ void refill();
+public:
+ MP3InputStream(File *file, mad_timer_t duration);
+ ~MP3InputStream();
+ int16 read();
+ bool eof() const;
+ bool isStereo() const { return _isStereo; }
+
+ int getRate() const { return _rate; }
+};
+#endif
#ifdef USE_VORBIS
@@ -73,20 +103,23 @@ class VorbisInputStream : public AudioInputStream {
OggVorbis_File *_ov_file;
int _end_pos;
bool _eof_flag;
- int _channels;
+ int _numChannels;
int16 _buffer[4096];
int16 *_pos;
void refill();
public:
- // TODO
VorbisInputStream(OggVorbis_File *file, int duration);
int16 read();
- int size() const;
- bool isStereo() const { return _channels >= 2; }
+ bool eof() const;
+ bool isStereo() const { return _numChannels >= 2; }
};
#endif
+AudioInputStream *makeLinearInputStream(byte _flags, const byte *ptr, uint32 len);
+WrappedAudioInputStream *makeWrappedInputStream(byte _flags, uint32 len);
+
+
#endif
diff --git a/sound/rate.cpp b/sound/rate.cpp
index 89751029f7..9b79e824ba 100644
--- a/sound/rate.cpp
+++ b/sound/rate.cpp
@@ -108,9 +108,6 @@ int st_rate_flow(eff_t effp, AudioInputStream &input, st_sample_t *obuf, st_size
ostart = obuf;
oend = obuf + *osamp * 2;
- if (stereo)
- assert(input.size() % 2 == 0); // Stereo code assumes even number of input samples
-
// If the input position exceeds the output position, then we aborted the
// previous conversion run because the output buffer was full. Resume!
if (rate->ipos > rate->opos)