aboutsummaryrefslogtreecommitdiff
path: root/sound/vorbis.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sound/vorbis.cpp')
-rw-r--r--sound/vorbis.cpp126
1 files changed, 122 insertions, 4 deletions
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