From d21fc5845dc77dd977df22fd9ddc3206bf23a920 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 19 Dec 2003 00:32:47 +0000 Subject: 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 --- sound/audiostream.cpp | 389 ++++++-------------------------------------------- sound/audiostream.h | 41 +----- sound/mixer.cpp | 39 ++--- sound/mixer.h | 4 + sound/mp3.cpp | 239 ++++++++++++++++++++++++++++++- sound/mp3.h | 11 +- sound/vorbis.cpp | 126 +++++++++++++++- sound/vorbis.h | 11 +- 8 files changed, 450 insertions(+), 410 deletions(-) (limited to 'sound') 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::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 -#endif -#ifdef USE_VORBIS -#include -#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 +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 +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 -- cgit v1.2.3