]> git.tdb.fi Git - libs/al.git/blob - source/oggdecoder.cpp
Add dedicated exception classes for decoding sounds
[libs/al.git] / source / oggdecoder.cpp
1 #include <vorbis/vorbisfile.h>
2 #include <msp/strings/format.h>
3 #include "oggdecoder.h"
4
5 using namespace std;
6
7 namespace {
8
9 size_t read(void *ptr, size_t size, size_t nmemb, void *src)
10 {
11         Msp::IO::Base *in = reinterpret_cast<Msp::IO::Base *>(src);
12         unsigned len = in->read(reinterpret_cast<char *>(ptr), size*nmemb);
13         return len/size;
14 }
15
16 int seek(void *src, ogg_int64_t offset, int whence)
17 {
18         Msp::IO::SeekType type;
19         if(whence==SEEK_SET)
20                 type = Msp::IO::S_BEG;
21         else if(whence==SEEK_CUR)
22                 type = Msp::IO::S_CUR;
23         else if(whence==SEEK_END)
24                 type = Msp::IO::S_END;
25         else
26                 return -1;
27
28         Msp::IO::Seekable *in = reinterpret_cast<Msp::IO::Seekable *>(src);
29         return in->seek(offset, type);
30 }
31
32 long tell(void *src)
33 {
34         return reinterpret_cast<Msp::IO::Seekable *>(src)->tell();
35 }
36
37 ov_callbacks io_callbacks =
38 {
39         &read,
40         &seek,
41         0,
42         &tell
43 };
44
45 } // namespace
46
47
48 namespace Msp {
49 namespace AL {
50
51 ogg_error::ogg_error(const std::string &func, int code):
52         runtime_error(format("%s: %s", func, get_message(code)))
53 { }
54
55 string ogg_error::get_message(int code)
56 {
57         switch(code)
58         {
59         case OV_FALSE: return "No data available";
60         case OV_HOLE: return "Missing or corrupt data";
61         case OV_EREAD: return "Read error";
62         case OV_EFAULT: return "Internal inconsistency";
63         case OV_EIMPL: return "Not implemented";
64         case OV_EINVAL: return "Invalid argument";
65         case OV_ENOTVORBIS: return "Not Vorbis data";
66         case OV_EBADHEADER: return "Corrupt Vorbis header";
67         case OV_EVERSION: return "Unsupported version";
68         case OV_EBADLINK: return "Bad link";
69         case OV_ENOSEEK: return "Stream is not seekable";
70         default: return format("Unknown error (%d)", code);
71         }
72 }
73
74
75 struct OggDecoder::Private
76 {
77         OggVorbis_File ovfile;  
78 };
79
80 OggDecoder::OggDecoder(IO::Seekable &io):
81         priv(new Private)
82 {
83         int ret = ov_open_callbacks(&io, &priv->ovfile, 0, 0, io_callbacks);
84         if(ret<0)
85                 throw ogg_error("ov_open_callbacks", ret);
86
87         vorbis_info *info = ov_info(&priv->ovfile, -1);
88         freq = info->rate;
89
90         size = ov_pcm_total(&priv->ovfile, 0)*info->channels*2;
91
92         switch(info->channels)
93         {
94         case 1: format = MONO16; break;
95         case 2: format = STEREO16; break;
96         default: throw unsupported_sound(Msp::format("%d channels", info->channels));
97         }
98 }
99
100 OggDecoder::~OggDecoder()
101 {
102         if(priv->ovfile.datasource)
103                 ov_clear(&priv->ovfile);
104         delete priv;
105 }
106
107 void OggDecoder::rewind()
108 {
109         ov_pcm_seek(&priv->ovfile, 0);
110 }
111
112 unsigned OggDecoder::read(char *buf, unsigned len)
113 {
114         int section = 0;
115         int res = ov_read(&priv->ovfile, buf, len, 0, 2, 1, &section);
116         if(res<0)
117                 throw ogg_error("ov_read", res);
118         else if(res==0)
119                 eof_flag = true;
120         return res;
121 }
122
123 } // namespace AL
124 } // namespace Msp