]> git.tdb.fi Git - libs/al.git/blobdiff - source/sounddecoder.cpp
Split Sound into SoundDecoder and Waveform parts
[libs/al.git] / source / sounddecoder.cpp
diff --git a/source/sounddecoder.cpp b/source/sounddecoder.cpp
new file mode 100644 (file)
index 0000000..d4d1767
--- /dev/null
@@ -0,0 +1,142 @@
+#include <cstring>
+#include <stdexcept>
+#include "sounddecoder.h"
+
+using namespace std;
+
+namespace {
+
+struct MemorySource
+{
+       const void *data;
+       unsigned length;
+       unsigned pos;
+
+       MemorySource(const void *d, unsigned l): data(d), length(l), pos(0) { }
+};
+
+size_t memory_read(void *ptr, size_t size, size_t nmemb, void *src)
+{
+       MemorySource &memsrc = *reinterpret_cast<MemorySource *>(src);
+       unsigned len = min<unsigned>(size*nmemb, memsrc.length-memsrc.pos);
+       memcpy(ptr, reinterpret_cast<const char *>(memsrc.data)+memsrc.pos, len);
+       memsrc.pos += len;
+
+       return len/size;
+}
+
+int memory_seek(void *src, ogg_int64_t offset, int whence)
+{
+       MemorySource &memsrc = *reinterpret_cast<MemorySource *>(src);
+       if(whence==SEEK_SET)
+               memsrc.pos = offset;
+       else if(whence==SEEK_CUR)
+               memsrc.pos += offset;
+       else if(whence==SEEK_END)
+               memsrc.pos = memsrc.length-offset;
+       memsrc.pos = min(memsrc.pos, memsrc.length);
+
+       return memsrc.pos;
+}
+
+int memory_close(void *src)
+{
+       delete reinterpret_cast<MemorySource *>(src);
+       return 0;
+}
+
+long memory_tell(void *src)
+{
+       MemorySource &memsrc = *reinterpret_cast<MemorySource *>(src);
+       return memsrc.pos;
+}
+
+ov_callbacks memory_callbacks=
+{
+       &memory_read,
+       &memory_seek,
+       &memory_close,
+       &memory_tell
+};
+
+} // namespace
+
+namespace Msp {
+namespace AL {
+
+SoundDecoder::SoundDecoder():
+       eof_flag(false)
+{
+       ovfile.datasource = 0;
+}
+
+SoundDecoder::~SoundDecoder()
+{
+       if(ovfile.datasource)
+               ov_clear(&ovfile);
+}
+
+void SoundDecoder::open_file(const string &fn)
+{
+       if(ovfile.datasource)
+               throw logic_error("Sound has already been opened");
+       if(ov_fopen(const_cast<char *>(fn.c_str()), &ovfile)<0)
+               throw runtime_error("Could not open ogg vorbis file "+fn);
+
+       open_common();
+}
+
+void SoundDecoder::open_memory(const void *d, unsigned len)
+{
+       if(ovfile.datasource)
+               throw logic_error("Sound has already been opened");
+
+       MemorySource *src = new MemorySource(d, len);
+       if(ov_open_callbacks(src, &ovfile, 0, 0, memory_callbacks)<0)
+       {
+               delete src;
+               throw runtime_error("Could not open ogg vorbis memory block");
+       }
+
+       open_common();
+}
+
+void SoundDecoder::open_common()
+{
+       vorbis_info *info = ov_info(&ovfile, -1);
+       freq = info->rate;
+
+       size = ov_pcm_total(&ovfile, 0)*info->channels*2;
+
+       switch(info->channels)
+       {
+       case 1: format = MONO16; break;
+       case 2: format = STEREO16; break;
+       default: throw runtime_error("Unsupported number of channels");
+       }
+}
+
+void SoundDecoder::close()
+{
+       if(ovfile.datasource)
+               ov_clear(&ovfile);
+}
+
+void SoundDecoder::rewind()
+{
+       ov_pcm_seek(&ovfile, 0);
+}
+
+unsigned SoundDecoder::read(char *buf, unsigned len)
+{
+       int section = 0;
+       int res = ov_read(&ovfile, buf, len, 0, 2, 1, &section);
+       if(res<0)
+               throw runtime_error("Error reading ogg vorbis file");
+       else if(res==0)
+               eof_flag = true;
+       return res;
+}
+
+} // namespace AL
+} // namespace Msp