aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Horn2007-02-22 10:51:47 +0000
committerMax Horn2007-02-22 10:51:47 +0000
commit2c1445056df1bdfc065c33a5a103729e46a60e33 (patch)
tree393da20ae44ffa0ebb12946151b8fe41bb7c9b50
parent0c899f82ab32c4d9da3700bbf54201db620c2ec4 (diff)
downloadscummvm-rg350-2c1445056df1bdfc065c33a5a103729e46a60e33.tar.gz
scummvm-rg350-2c1445056df1bdfc065c33a5a103729e46a60e33.tar.bz2
scummvm-rg350-2c1445056df1bdfc065c33a5a103729e46a60e33.zip
Rewrote Ogg Vorbis code to be more flexible when it comes to seeking; also now playback from arbitrary SeekableReadStream data sources is possible
svn-id: r25784
-rw-r--r--sound/mp3.cpp4
-rw-r--r--sound/vorbis.cpp334
2 files changed, 147 insertions, 191 deletions
diff --git a/sound/mp3.cpp b/sound/mp3.cpp
index 7fbcb44b5f..d1613c13a9 100644
--- a/sound/mp3.cpp
+++ b/sound/mp3.cpp
@@ -299,10 +299,10 @@ AudioStream *makeMP3Stream(Common::File *file, uint32 size) {
if (!size)
size = file->size() - file->pos();
- // Read 'size' bytes of data (or until EOF is reached) into a MemoryReadStream
+ // Read 'size' bytes of data into a MemoryReadStream
Common::MemoryReadStream *stream = file->readStream(size);
- // .. and create a MP3InputStream from all this
+ // .. and create an MP3InputStream from all this
return new MP3InputStream(stream, true);
}
diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp
index 7df6fbee8b..485176a44e 100644
--- a/sound/vorbis.cpp
+++ b/sound/vorbis.cpp
@@ -46,166 +46,141 @@ using Common::File;
namespace Audio {
-// These are wrapper functions to allow using a File object to
+// These are wrapper functions to allow using a SeekableReadStream object to
// provide data to the OggVorbis_File object.
-struct file_info {
- File *file;
- int start, curr_pos;
- size_t len;
-};
-
-static size_t read_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) {
- file_info *f = (file_info *) datasource;
- int result;
-
-#ifdef __SYMBIAN32__
- // For symbian we must check that an alternative file pointer is created, see if its open
- // If not re-open file and seek to the last read position
- if (f->file && !f->file->isOpen()) {
- f->file->open(f->file->name());
- f->file->seek(f->curr_pos);
- }
-#endif
+static size_t read_stream_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) {
+ Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
- nmemb *= size;
- if (f->curr_pos > (int) f->len)
- nmemb = 0;
- else if (nmemb > f->len - f->curr_pos)
- nmemb = f->len - f->curr_pos;
- // There is no guarantee that the Vorbis stream is alone in accessing
- // the file, so make sure the current position is what we think it is.
- f->file->seek(f->start + f->curr_pos);
- result = f->file->read(ptr, nmemb);
-#ifdef __SYMBIAN32__
- // For symbian we now store the last read position and then close the file
- if (f->file) {
- f->file->close();
- }
-#endif
- if (result == -1) {
- f->curr_pos = f->file->pos() - f->start;
- return (size_t) -1;
- } else {
- f->curr_pos += result;
- return result / size;
- }
+ uint32 result = stream->read(ptr, size * nmemb);
+
+ return result / size;
}
-static int seek_wrap(void *datasource, ogg_int64_t offset, int whence) {
- file_info *f = (file_info *) datasource;
-
- if (whence == SEEK_SET)
- offset += f->start;
- else if (whence == SEEK_END) {
- offset += f->start + f->len;
- whence = SEEK_SET;
- }
-
-#ifdef __SYMBIAN32__
- // For symbian we must check that an alternative file pointer is created, see if its open
- // If not re-open file and seek to the last read position
- if (f->file && !f->file->isOpen()) {
- f->file->open(f->file->name());
- f->file->seek(f->curr_pos);
- }
-#endif
-
- f->file->seek(offset, whence);
- f->curr_pos = f->file->pos() - f->start;
-
-#ifdef __SYMBIAN32__
- // For symbian we now store the last read position and then close the file
- if (f->file) {
- f->file->close();
- }
-#endif
-
- return f->curr_pos;
+static int seek_stream_wrap(void *datasource, ogg_int64_t offset, int whence) {
+ Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
+ stream->seek(offset, whence);
+ return stream->pos();
}
-static int close_wrap(void *datasource) {
- file_info *f = (file_info *) datasource;
-
- delete f->file;
- delete f;
-
+static int close_stream_wrap(void *datasource) {
+ // Do nothing -- we leave it up to the VorbisInputStream to free memory as appropriate.
return 0;
}
-static long tell_wrap(void *datasource) {
- file_info *f = (file_info *) datasource;
-
- return f->curr_pos;
+static long tell_stream_wrap(void *datasource) {
+ Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
+ return stream->pos();
}
-static ov_callbacks g_File_wrap = {
- read_wrap, seek_wrap, close_wrap, tell_wrap
+static ov_callbacks g_stream_wrap = {
+ read_stream_wrap, seek_stream_wrap, close_stream_wrap, tell_stream_wrap
};
+
#pragma mark -
#pragma mark --- Ogg Vorbis stream ---
#pragma mark -
class VorbisInputStream : public AudioStream {
- OggVorbis_File *_ov_file;
- int _end_pos;
- int _numChannels;
+protected:
+ OggVorbis_File _ovFile;
+
+ Common::SeekableReadStream *_inStream;
+ bool _disposeAfterUse;
+
int16 _buffer[4096];
const int16 *_bufferEnd;
const int16 *_pos;
- bool _deleteFileAfterUse;
+
+ bool _isStereo;
+ int _rate;
+
+#ifdef USE_TREMOR
+ ogg_int64_t _startTime;
+ ogg_int64_t _endTime;
+#else
+ double _startTime;
+ double _endTime;
+#endif
void refill();
inline bool eosIntern() const;
public:
- VorbisInputStream(OggVorbis_File *file, int duration, bool deleteFileAfterUse);
+ // startTime / duration are in milliseconds
+ VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime = 0, uint endTime = 0);
~VorbisInputStream();
int readBuffer(int16 *buffer, const int numSamples);
bool endOfData() const { return eosIntern(); }
- bool isStereo() const { return _numChannels >= 2; }
+ bool isStereo() const { return _isStereo; }
- int getRate() const { return ov_info(_ov_file, -1)->rate; }
+ int getRate() const { return _rate; }
};
-VorbisInputStream::VorbisInputStream(OggVorbis_File *file, int duration, bool deleteFileAfterUse)
- : _ov_file(file),
- _bufferEnd(_buffer + ARRAYSIZE(_buffer)),
- _deleteFileAfterUse(deleteFileAfterUse) {
-//debug(5, "" __FILE__ ":%i", __LINE__);
+VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime, uint endTime) :
+ _inStream(inStream),
+ _disposeAfterUse(dispose),
+ _bufferEnd(_buffer + ARRAYSIZE(_buffer)) {
- // Check the header, determine if this is a stereo stream
- _numChannels = ov_info(_ov_file, -1)->channels;
+ bool err = (ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap) < 0);
+ // FIXME: proper error handling!
+ assert(!err);
- // Determine the end position
- if (duration)
- _end_pos = ov_pcm_tell(_ov_file) + duration;
- else
- _end_pos = ov_pcm_total(_ov_file, -1);
+#ifdef USE_TREMOR
+ /* TODO: Symbian may have to use scumm_fixdfdi here? To quote:
+ "SumthinWicked says: fixing "relocation truncated to fit: ARM_26 __fixdfdi" during linking on GCC, see portdefs.h"
+ */
+
+ // In Tremor, the ov_time_seek() and ov_time_seek_page() calls take seeking
+ // positions in milliseconds as 64 bit integers, rather than in seconds as
+ // doubles as in Vorbisfile.
+ ogg_int64_t totalTime;
+ _startTime = startTime;
+ _endTime = endTime;
+#else
+ double totalTime;
+ _startTime = startTime / 1000.0;
+ _endTime = endTime / 1000.0;
+#endif
+
+ // If endTime was 0, or is past the end of the file, set it to the maximal time possible
+ totalTime = ov_time_total(&_ovFile, -1);
+ if (_endTime == 0 || _endTime > totalTime)
+ _endTime = totalTime;
+
+ // If the specified time range is empty, abort early.
+ if (_startTime >= _endTime) {
+ _pos = _bufferEnd;
+ return;
+ }
+
+ // Seek to the start position
+ ov_time_seek(&_ovFile, _startTime);
// Read in initial data
refill();
+
+ // Setup some header information
+ _isStereo = ov_info(&_ovFile, -1)->channels >= 2;
+ _rate = ov_info(&_ovFile, -1)->rate;
}
VorbisInputStream::~VorbisInputStream() {
-//debug(5, "" __FILE__ ":%i", __LINE__);
- ov_clear(_ov_file);
- if (_deleteFileAfterUse)
- delete _ov_file;
+ ov_clear(&_ovFile);
+ if (_disposeAfterUse)
+ delete _inStream;
}
inline bool VorbisInputStream::eosIntern() const {
-//debug(5, "" __FILE__ ":%i", __LINE__);
return _pos >= _bufferEnd;
}
int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) {
-//debug(5, "" __FILE__ ":%i", __LINE__);
int samples = 0;
while (samples < numSamples && !eosIntern()) {
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
@@ -221,23 +196,33 @@ int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) {
}
void VorbisInputStream::refill() {
-//debug(5, "" __FILE__ ":%i", __LINE__);
// 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 USE_TREMOR // Tremor ov_read() always returns data as signed 16 bit interleaved PCM in host byte order. As such, it does not take arguments to request specific signedness, byte order or bit depth as in Vorbisfile.
+ while (len_left > 0 && ov_time_tell(&_ovFile) < _endTime) {
+ long result;
+#ifdef USE_TREMOR
+ // Tremor ov_read() always returns data as signed 16 bit interleaved PCM
+ // in host byte order. As such, it does not take arguments to request
+ // specific signedness, byte order or bit depth as in Vorbisfile.
+ result = ov_read(&_ovFile, read_pos, len_left,
+ NULL);
+#else
#ifdef SCUMM_BIG_ENDIAN
+ result = ov_read(&_ovFile, read_pos, len_left,
1,
+ 2, // 16 bit
+ 1, // signed
+ NULL);
#else
+ result = ov_read(&_ovFile, read_pos, len_left,
0,
-#endif
2, // 16 bit
1, // signed
-#endif
NULL);
+#endif
+#endif
if (result == OV_HOLE) {
// Possibly recoverable, just warn about it
warning("Corrupted data in Vorbis file");
@@ -259,34 +244,20 @@ void VorbisInputStream::refill() {
}
AudioStream *makeVorbisStream(File *file, uint32 size) {
-//debug(5, "" __FILE__ ":%i", __LINE__);
- OggVorbis_File *ov_file = new OggVorbis_File;
- file_info *f = new file_info;
-
-#if defined(__SYMBIAN32__)
- // Symbian can't share filehandles between different threads.
- // So create a new file and seek that to the other filehandles position
- f->file = new File;
- f->file->open(file->name());
- f->file->seek(file->pos());
-#else
- f->file = file;
-#endif
- f->start = file->pos();
- f->len = size;
- f->curr_pos = 0;
-
- if (ov_open_callbacks((void *) f, ov_file, NULL, 0, g_File_wrap) < 0) {
- warning("Invalid file format");
- delete ov_file;
- delete f;
- return 0;
- } else {
-#ifndef __SYMBIAN32__
- file->incRef();
-#endif
- return new VorbisInputStream(ov_file, 0, true);
- }
+ assert(file);
+
+ // FIXME: For now, just read the whole data into memory, and be done
+ // with it. Of course this is in general *not* a nice thing to do...
+
+ // If no size was specified, read the whole remainder of the file
+ if (!size)
+ size = file->size() - file->pos();
+
+ // Read 'size' bytes of data into a MemoryReadStream
+ Common::MemoryReadStream *stream = file->readStream(size);
+
+ // .. and create a VorbisInputStream from all this
+ return new VorbisInputStream(stream, true);
}
@@ -301,7 +272,6 @@ private:
public:
VorbisTrackInfo(const char *filename);
- bool openTrack(OggVorbis_File *ovFile);
bool error() { return _errorFlag; }
void play(Audio::Mixer *mixer, Audio::SoundHandle *handle, int startFrame, int duration);
};
@@ -310,60 +280,46 @@ public:
VorbisTrackInfo::VorbisTrackInfo(const char *filename) :
_filename(filename),
_errorFlag(false) {
-
- OggVorbis_File ov_file;
- _errorFlag = openTrack(&ov_file);
- if (!_errorFlag)
- ov_clear(&ov_file);
-}
-
-bool VorbisTrackInfo::openTrack(OggVorbis_File *ovFile) {
- bool err;
- file_info *f = new file_info;
- assert(f);
- f->file = new File;
- assert(f->file);
-
- err = !f->file->open(_filename);
-
- if (!err) {
- f->start = 0;
- f->len = f->file->size();
- f->curr_pos = 0;
-
- err = (ov_open_callbacks((void *) f, ovFile, NULL, 0, g_File_wrap) < 0);
- }
-
- if (err) {
- delete f->file;
- delete f;
+ // Try to open the file
+ Common::File file;
+ if (!file.open(_filename)) {
+ _errorFlag = true;
+ return;
}
+
+ // Next, try to create a VorbisInputStream from it
+ VorbisInputStream *tempStream = new VorbisInputStream(&file, false);
- return err;
+ // If an error occured...
+ // TODO: add an error or init method to VorbisInputStream
+ _errorFlag = tempStream->endOfData();
+
+ // Clean up again
+ delete tempStream;
}
void VorbisTrackInfo::play(Audio::Mixer *mixer, Audio::SoundHandle *handle, int startFrame, int duration) {
- OggVorbis_File *ovFile = new OggVorbis_File;
- bool err = openTrack(ovFile);
- assert(!err);
-
-#ifdef USE_TREMOR
- // In Tremor, the ov_time_seek() and ov_time_seek_page() calls take seeking
- // positions in milliseconds as 64 bit integers, rather than in seconds as
- // doubles as in Vorbisfile.
-#if defined(__SYMBIAN32__) && defined(__GCC32__)
- // SumthinWicked says: fixing "relocation truncated to fit: ARM_26 __fixdfdi" during linking on GCC, see portdefs.h
- ov_time_seek(ovFile, (ogg_int64_t)scumm_fixdfdi(startFrame / 75.0 * 1000));
-#else
- ov_time_seek(ovFile, (ogg_int64_t)(startFrame / 75.0 * 1000));
-#endif
-#else
- ov_time_seek(ovFile, startFrame / 75.0);
-#endif
+ assert(!_errorFlag);
+ uint start, end;
+
+ // Open the file
+ Common::File *file = new Common::File();
+ if (!file || !file->open(_filename)) {
+ warning("VorbisTrackInfo::play: failed to open '%s'", _filename.c_str());
+ delete file;
+ return;
+ }
+
+ // Convert startFrame & duration from frames (1/75 s) to milliseconds (1/1000s)
+ start = startFrame * 1000 / 75;
+ end = duration ? ((startFrame + duration) * 1000 / 75) : 0;
- AudioStream *input = new VorbisInputStream(ovFile, duration * ov_info(ovFile, -1)->rate / 75, true);
+ // ... create an AudioStream ...
+ VorbisInputStream *input = new VorbisInputStream(file, true, start, end);
+
+ // ... and play it
mixer->playInputStream(Audio::Mixer::kMusicSoundType, handle, input);
}