diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index 8428de2d78d..64dceb1cd6c 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -161,15 +161,16 @@ void WrappedMemoryStream::append(const byte *data, #ifdef USE_MAD -#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 + * @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) { +MP3InputStream::MP3InputStream(File *file, mad_timer_t duration, uint size) { // duration == 0 means: play everything till end of file _isStereo = false; @@ -177,6 +178,7 @@ MP3InputStream::MP3InputStream(File *file, mad_timer_t duration) { _file = file; _rate = 0; _posInFrame = 0; + _bufferSize = size ? size : (128 * 1024); // Default buffer size is 128K _duration = duration; @@ -184,9 +186,15 @@ MP3InputStream::MP3InputStream(File *file, mad_timer_t duration) { mad_frame_init(&_frame); mad_synth_init(&_synth); - _ptr = (byte *)malloc(MP3_BUFFER_SIZE + MAD_BUFFER_GUARD); - + _ptr = (byte *)malloc(_bufferSize + MAD_BUFFER_GUARD); + _initialized = init(); + + // If a size is specified, we do not perform any further read operations + if (size) { + _file = 0; + } + } MP3InputStream::~MP3InputStream() { @@ -201,7 +209,7 @@ bool MP3InputStream::init() { // TODO // Read in the first chunk of the MP3 file - _size = _file->read(_ptr, MP3_BUFFER_SIZE); + _size = _file->read(_ptr, _bufferSize); if (_size <= 0) { warning("MP3InputStream: Failed to read MP3 data"); return false; @@ -244,19 +252,22 @@ void MP3InputStream::refill() { 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, MP3_BUFFER_SIZE + MAD_BUFFER_GUARD); + 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, MP3_BUFFER_SIZE - offset); + _size = _file->read(_ptr + offset, _bufferSize - offset); // Nothing read -> EOF -> bail out if (_size <= 0) { @@ -286,8 +297,10 @@ void MP3InputStream::refill() { } bool MP3InputStream::eof() const { - // Time over -> input steam ends - if (mad_timer_compare(_duration, mad_timer_zero) <= 0) + // Time over -> input steam ends. Unless _file is 0, which + // means that playback is based on the number of input bytes + // rather than a duration. + if (_file && mad_timer_compare(_duration, mad_timer_zero) <= 0) return true; return (_posInFrame >= _synth.pcm.length); } @@ -307,8 +320,9 @@ static inline int scale_sample(mad_fixed_t sample) { } int16 MP3InputStream::read() { - if (_size < 0 || _posInFrame >= _synth.pcm.length) // EOF + if (_size < 0 || _posInFrame >= _synth.pcm.length) { // EOF return 0; + } int16 sample; if (_isStereo) { diff --git a/sound/audiostream.h b/sound/audiostream.h index 722006e6258..110dee789dc 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -51,6 +51,8 @@ public: //virtual int size() const = 0; virtual bool isStereo() const = 0; virtual bool eof() const = 0; + + virtual int getRate() const { return -1; } }; class WrappedAudioInputStream : public AudioInputStream { @@ -75,6 +77,7 @@ class MP3InputStream : public AudioInputStream { struct mad_frame _frame; struct mad_synth _synth; uint32 _posInFrame; + uint32 _bufferSize; int _size; bool _isStereo; int _curChannel; @@ -87,7 +90,7 @@ class MP3InputStream : public AudioInputStream { bool init(); void refill(); public: - MP3InputStream(File *file, mad_timer_t duration); + MP3InputStream(File *file, mad_timer_t duration, uint size = 0); ~MP3InputStream(); int16 read(); bool eof() const; diff --git a/sound/mixer.cpp b/sound/mixer.cpp index 18111ad9719..1a978c33169 100644 --- a/sound/mixer.cpp +++ b/sound/mixer.cpp @@ -37,6 +37,10 @@ class Channel { protected: SoundMixer *_mixer; PlayingSoundHandle *_handle; +#ifdef SOX_HACK + RateConverter *_converter; + AudioInputStream *_input; +#endif public: int _id; Channel(SoundMixer *mixer, PlayingSoundHandle *handle) @@ -44,6 +48,8 @@ public: assert(mixer); } virtual ~Channel() { + delete _converter; + delete _input; if (_handle) *_handle = 0; } @@ -65,10 +71,7 @@ public: class ChannelRaw : public Channel { byte *_ptr; byte _flags; -#ifdef SOX_HACK - RateConverter *_converter; - AudioInputStream *_input; -#else +#ifndef SOX_HACK uint32 _pos; uint32 _size; uint32 _fpSpeed; @@ -89,10 +92,7 @@ public: }; class ChannelStream : public Channel { -#ifdef SOX_HACK - RateConverter *_converter; - WrappedAudioInputStream *_input; -#else +#ifndef SOX_HACK byte *_ptr; byte *_endOfData; byte *_endOfBuffer; @@ -134,8 +134,12 @@ public: ~ChannelMP3Common(); }; +#ifdef SOX_HACK +class ChannelMP3 : public Channel { +#else class ChannelMP3 : public ChannelMP3Common { uint32 _position; +#endif public: ChannelMP3(SoundMixer *mixer, PlayingSoundHandle *handle, File *file, uint size); @@ -146,8 +150,6 @@ public: #ifdef SOX_HACK class ChannelMP3CDMusic : public Channel { - RateConverter *_converter; - MP3InputStream *_input; #else class ChannelMP3CDMusic : public ChannelMP3Common { uint32 _bufferSize; @@ -157,7 +159,6 @@ class ChannelMP3CDMusic : public ChannelMP3Common { public: ChannelMP3CDMusic(SoundMixer *mixer, PlayingSoundHandle *handle, File *file, mad_timer_t duration); - ~ChannelMP3CDMusic(); void mix(int16 *data, uint len); bool isMusicChannel() { return true; } @@ -167,10 +168,7 @@ public: #ifdef USE_VORBIS class ChannelVorbis : public Channel { -#ifdef SOX_HACK - RateConverter *_converter; - AudioInputStream *_input; -#else +#ifndef SOX_HACK OggVorbis_File *_ov_file; int _end_pos; #endif @@ -178,7 +176,6 @@ class ChannelVorbis : public Channel { public: ChannelVorbis(SoundMixer *mixer, PlayingSoundHandle *handle, OggVorbis_File *ov_file, int duration, bool is_cd_track); - ~ChannelVorbis(); void mix(int16 *data, uint len); bool isMusicChannel() { @@ -702,10 +699,6 @@ ChannelRaw::ChannelRaw(SoundMixer *mixer, PlayingSoundHandle *handle, void *soun } ChannelRaw::~ChannelRaw() { -#ifdef SOX_HACK - delete _converter; - delete _input; -#endif if (_flags & SoundMixer::FLAG_AUTOFREE) free(_ptr); } @@ -762,7 +755,7 @@ ChannelStream::ChannelStream(SoundMixer *mixer, PlayingSoundHandle *handle, void // Create the input stream _input = makeWrappedInputStream(flags, buffer_size); - _input->append((const byte *)sound, size); + ((WrappedAudioInputStream *)_input)->append((const byte *)sound, size); // TODO: add support for SoundMixer::FLAG_REVERSE_STEREO // Get a rate converter instance @@ -791,17 +784,14 @@ ChannelStream::ChannelStream(SoundMixer *mixer, PlayingSoundHandle *handle, void } ChannelStream::~ChannelStream() { -#ifdef SOX_HACK - delete _converter; - delete _input; -#else +#ifndef SOX_HACK free(_ptr); #endif } void ChannelStream::append(void *data, uint32 len) { #ifdef SOX_HACK - _input->append((const byte *)data, len); + ((WrappedAudioInputStream *)_input)->append((const byte *)data, len); #else if (_endOfData + len > _endOfBuffer) { /* Wrap-around case */ @@ -936,6 +926,17 @@ static inline int scale_sample(mad_fixed_t sample) { return sample >> (MAD_F_FRACBITS + 1 - 16); } +#ifdef SOX_HACK +ChannelMP3::ChannelMP3(SoundMixer *mixer, PlayingSoundHandle *handle, File *file, uint size) + : Channel(mixer, handle) { + // Create the input stream + _input = new MP3InputStream(file, mad_timer_zero, size); + + // Get a rate converter instance +//printf("ChannelMP3: inrate %d, outrate %d, stereo %d\n", _input->getRate(), mixer->getOutputRate(), _input->isStereo()); + _converter = makeRateConverter(_input->getRate(), mixer->getOutputRate(), _input->isStereo()); +} +#else ChannelMP3::ChannelMP3(SoundMixer *mixer, PlayingSoundHandle *handle, File *file, uint size) : ChannelMP3Common(mixer, handle) { _posInFrame = 0xFFFFFFFF; @@ -944,8 +945,23 @@ ChannelMP3::ChannelMP3(SoundMixer *mixer, PlayingSoundHandle *handle, File *file _size = file->read(_ptr, size); } +#endif void ChannelMP3::mix(int16 *data, uint len) { +#ifdef SOX_HACK + assert(_input); + assert(_converter); + + if (_input->eof()) { + // TODO: call drain method + destroy(); + return; + } + + const int volume = _mixer->getVolume(); + st_size_t tmpLen = len; + _converter->flow(*_input, data, &tmpLen, volume); +#else const int volume = _mixer->getVolume(); // Exit if all data is used up (this also covers the case were reading from the file failed). @@ -990,6 +1006,7 @@ void ChannelMP3::mix(int16 *data, uint len) { _posInFrame = 0; _position = _stream.next_frame - _ptr; } +#endif } #define MP3CD_BUFFERING_SIZE 131072 @@ -998,7 +1015,7 @@ void ChannelMP3::mix(int16 *data, uint len) { ChannelMP3CDMusic::ChannelMP3CDMusic(SoundMixer *mixer, PlayingSoundHandle *handle, File *file, mad_timer_t duration) : Channel(mixer, handle) { // Create the input stream - _input = new MP3InputStream(file, duration); + _input = new MP3InputStream(file, duration, 0); // Get a rate converter instance //printf("ChannelMP3CDMusic: inrate %d, outrate %d, stereo %d\n", _input->getRate(), mixer->getOutputRate(), _input->isStereo()); @@ -1014,13 +1031,6 @@ ChannelMP3CDMusic::ChannelMP3CDMusic(SoundMixer *mixer, PlayingSoundHandle *hand } #endif -ChannelMP3CDMusic::~ChannelMP3CDMusic() { -#ifdef SOX_HACK - delete _converter; - delete _input; -#endif -} - void ChannelMP3CDMusic::mix(int16 *data, uint len) { #ifdef SOX_HACK assert(_input); @@ -1171,13 +1181,6 @@ ChannelVorbis::ChannelVorbis(SoundMixer *mixer, PlayingSoundHandle *handle, OggV _is_cd_track = is_cd_track; } -ChannelVorbis::~ChannelVorbis() { -#ifdef SOX_HACK - delete _converter; - delete _input; -#endif -} - #ifdef CHUNKSIZE #define VORBIS_TREMOR #endif