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