1 #include <algorithm>
2 #include <string.h>
3 #include "input_ogg.h"
4 #include "types.h"
5 #include "utility.h"
6 
7 
8 namespace audiere {
9 
10   typedef ogg_int64_t int64_t;
11 
12 
OGGInputStream()13   OGGInputStream::OGGInputStream() {
14     m_file = 0;
15 
16     m_channel_count = 0;
17     m_sample_rate   = 0;
18     m_sample_format = SF_S16;
19   }
20 
21 
~OGGInputStream()22   OGGInputStream::~OGGInputStream() {
23     // were we initialized successfully?
24     if (m_file) {
25       ov_clear(&m_vorbis_file);
26     }
27   }
28 
29 
30   bool
initialize(FilePtr file)31   OGGInputStream::initialize(FilePtr file) {
32     m_file = file;
33 
34     // custom ogg vorbis callbacks
35     ov_callbacks callbacks;
36     callbacks.read_func  = FileRead;
37     callbacks.seek_func  = FileSeek;
38     callbacks.close_func = FileClose;
39     callbacks.tell_func  = FileTell;
40 
41     // open ogg vorbis stream
42     int result = ov_open_callbacks(file.get(), &m_vorbis_file, 0, 0, callbacks);
43     if (result) {
44       m_file = 0;
45       return false;
46     }
47 
48     // calculate stream type
49     vorbis_info* vi = ov_info(&m_vorbis_file, -1);
50     if (!vi) {
51       ov_clear(&m_vorbis_file);
52       m_file = 0;
53       return false;
54     }
55 
56     // read metadata
57     vorbis_comment* comments = ov_comment(&m_vorbis_file, -1);
58     if (comments) {
59       addTag("vendor", comments->vendor, "vorbis");
60       for (int i = 0; i < comments->comments; ++i) {
61         std::string kv = comments->user_comments[i];
62         std::string key;
63         std::string value;
64         std::string::iterator eq = std::find(kv.begin(), kv.end(), '=');
65         if (eq != kv.end()) {
66           key.assign(kv.begin(), eq);
67           value.assign(eq + 1, kv.end());
68         } else {
69           key = kv;
70         }
71         addTag(key, value, "vorbis");
72       }
73     }
74 
75     m_channel_count = vi->channels;
76     m_sample_rate   = vi->rate;
77     m_sample_format = SF_S16; // see constructor
78 
79     return true;
80   }
81 
82 
83   void
getFormat(int & channel_count,int & sample_rate,SampleFormat & sample_format)84   OGGInputStream::getFormat(
85     int& channel_count,
86     int& sample_rate,
87     SampleFormat& sample_format)
88   {
89     channel_count = m_channel_count;
90     sample_rate   = m_sample_rate;
91     sample_format = m_sample_format;
92   }
93 
94 
95   int
doRead(int frame_count,void * buffer)96   OGGInputStream::doRead(int frame_count, void* buffer) {
97     int sample_size = m_channel_count * GetSampleSize(m_sample_format);
98 
99     u8* out = (u8*)buffer;
100 
101     int samples_left = frame_count;
102     int total_read = 0;
103     while (samples_left > 0) {
104 
105       // check to see if the stream format has changed
106       // if so, treat it as an EndOfStream
107       vorbis_info* vi = ov_info(&m_vorbis_file, -1);
108       if (vi && (m_sample_rate != vi->rate || m_channel_count != vi->channels)) {
109         break;
110       }
111 
112 #ifdef WORDS_BIGENDIAN
113 #define ENDIANNESS 1
114 #else
115 #define ENDIANNESS 0
116 #endif
117 
118       int bitstream;
119       long result = ov_read(
120         &m_vorbis_file,
121         (char*)out,
122         samples_left * sample_size,
123         ENDIANNESS,
124         2,  // 16-bit
125         1,  // signed
126         &bitstream);
127 
128       if (result < 0) {
129         // if error, ignore it
130         continue;
131       } else if (result == 0) {
132         break;
133       }
134 
135       u32 samples_read = (u32)(result / sample_size);
136 
137       out += samples_read * sample_size;
138       samples_left -= samples_read;
139       total_read   += samples_read;
140     }
141 
142     return total_read;
143   }
144 
145 
146   void
reset()147   OGGInputStream::reset() {
148     ov_pcm_seek(&m_vorbis_file, 0);
149   }
150 
151 
152   bool
isSeekable()153   OGGInputStream::isSeekable() {
154     return (ov_seekable(&m_vorbis_file) != 0);
155   }
156 
157 
158   int
getLength()159   OGGInputStream::getLength() {
160     if (isSeekable()) {
161       return static_cast<int>(ov_pcm_total(&m_vorbis_file, -1));
162     } else {
163       return 0;
164     }
165   }
166 
167 
168   void
setPosition(int position)169   OGGInputStream::setPosition(int position) {
170     if (isSeekable()) {
171       ov_pcm_seek(&m_vorbis_file, position);
172     }
173   }
174 
175 
176   int
getPosition()177   OGGInputStream::getPosition() {
178     if (isSeekable()) {
179       return static_cast<int>(ov_pcm_tell(&m_vorbis_file));
180     } else {
181       return 0;
182     }
183   }
184 
185 
186   size_t
FileRead(void * buffer,size_t size,size_t n,void * opaque)187   OGGInputStream::FileRead(void* buffer, size_t size, size_t n, void* opaque) {
188     File* file = reinterpret_cast<File*>(opaque);
189     return file->read(buffer, size * n) / size;
190   }
191 
192 
193   int
FileSeek(void * opaque,ogg_int64_t offset,int whence)194   OGGInputStream::FileSeek(void* opaque, ogg_int64_t offset, int whence) {
195     File* file = reinterpret_cast<File*>(opaque);
196     File::SeekMode type;
197     switch (whence) {
198       case SEEK_SET: type = File::BEGIN;   break;
199       case SEEK_CUR: type = File::CURRENT; break;
200       case SEEK_END: type = File::END;     break;
201       default: return -1;
202     }
203     return (file->seek((int)offset, type) ? 0 : -1);
204   }
205 
206 
207   int
FileClose(void *)208   OGGInputStream::FileClose(void* /*opaque*/) {
209     // we don't have to do anything
210     // (read: don't trust ogg vorbis with handling file closes)
211     return 0;
212   }
213 
214 
215   long
FileTell(void * opaque)216   OGGInputStream::FileTell(void* opaque) {
217     File* file = reinterpret_cast<File*>(opaque);
218     return file->tell();
219   }
220 
221 }
222