diff options
author | Max Horn | 2003-12-19 00:32:47 +0000 |
---|---|---|
committer | Max Horn | 2003-12-19 00:32:47 +0000 |
commit | d21fc5845dc77dd977df22fd9ddc3206bf23a920 (patch) | |
tree | eed9abe12e4a103c469f875cb6dfebab746c74a4 /sound/audiostream.cpp | |
parent | 97ee61963c896e48a0acc5b4169ce96163e76b06 (diff) | |
download | scummvm-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/audiostream.cpp')
-rw-r--r-- | sound/audiostream.cpp | 389 |
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 |