1 // Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
2 //
3 // Use, modification and distribution is allowed without limitation,
4 // warranty, or liability of any kind.
5 //
6
7
8 #include <qmmp/buffer.h>
9 #include <qmmp/output.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <QObject>
13 #include <QIODevice>
14 #include "decoder_vorbis.h"
15
16 // ic functions for OggVorbis
oggread(void * buf,size_t size,size_t nmemb,void * src)17 static size_t oggread (void *buf, size_t size, size_t nmemb, void *src)
18 {
19 DecoderVorbis *dogg = static_cast<DecoderVorbis *>(src);
20 int len = dogg->input()->read((char *) buf, (size * nmemb));
21 return len / size;
22 }
23
oggseek(void * src,ogg_int64_t offset,int whence)24 static int oggseek(void *src, ogg_int64_t offset, int whence)
25 {
26 DecoderVorbis *dogg = static_cast<DecoderVorbis *>(src);
27
28 if ( dogg->input()->isSequential ())
29 return -1;
30
31 long start = 0;
32 switch (whence)
33 {
34 case SEEK_END:
35 start = dogg->input()->size();
36 break;
37
38 case SEEK_CUR:
39 start = dogg->input()->pos();
40 break;
41
42 case SEEK_SET:
43 default:
44 start = 0;
45 }
46
47 if (dogg->input()->seek(start + offset))
48 return 0;
49 return -1;
50 }
51
52
oggclose(void *)53 static int oggclose(void *)
54 {
55 return 0;
56 }
57
58
oggtell(void * src)59 static long oggtell(void *src)
60 {
61 DecoderVorbis *dogg = static_cast<DecoderVorbis *>(src);
62 long t = dogg->input()->pos();
63 return t;
64 }
65
66
67 // Decoder class
68
DecoderVorbis(QIODevice * i)69 DecoderVorbis::DecoderVorbis(QIODevice *i) : Decoder(i)
70 {
71 memset(&oggfile, 0, sizeof(OggVorbis_File));
72 }
73
74
~DecoderVorbis()75 DecoderVorbis::~DecoderVorbis()
76 {
77 deinit();
78 }
79
initialize()80 bool DecoderVorbis::initialize()
81 {
82 qDebug("DecoderVorbis: initialize");
83 m_inited = false;
84 m_totalTime = 0;
85 if (!input())
86 {
87 qDebug("DecoderVorbis: cannot initialize. No input");
88 return false;
89 }
90
91 ov_callbacks oggcb =
92 {
93 oggread,
94 oggseek,
95 oggclose,
96 oggtell
97 };
98 if (ov_open_callbacks(this, &oggfile, nullptr, 0, oggcb) < 0)
99 {
100 qWarning("DecoderVorbis: cannot open stream");
101
102 return false;
103 }
104
105 quint32 freq = 0;
106 m_bitrate = ov_bitrate(&oggfile, -1) / 1000;
107 int chan = 0;
108
109 if((m_totalTime = ov_time_total(&oggfile, -1) * 1000) < 0)
110 m_totalTime = 0;
111
112 vorbis_info *ogginfo = ov_info(&oggfile, -1);
113 if (ogginfo)
114 {
115 freq = ogginfo->rate;
116 chan = ogginfo->channels;
117 setProperty(Qmmp::BITRATE, int(ogginfo->bitrate_nominal / 1000));
118 setProperty(Qmmp::FORMAT_NAME, "Ogg Vorbis");
119 }
120
121 ChannelMap chmap = findChannelMap(chan);
122 if(chmap.isEmpty())
123 {
124 qWarning("DecoderVorbis: unsupported number of channels: %d", chan);
125 return false;
126 }
127 configure(freq, chmap, Qmmp::PCM_FLOAT);
128 m_inited = true;
129 return true;
130 }
131
132
totalTime() const133 qint64 DecoderVorbis::totalTime() const
134 {
135 if (!m_inited)
136 return 0;
137 return m_totalTime;
138 }
139
bitrate() const140 int DecoderVorbis::bitrate() const
141 {
142 return m_bitrate;
143 }
144
deinit()145 void DecoderVorbis::deinit()
146 {
147 if (m_inited)
148 ov_clear(&oggfile);
149 len = 0;
150 }
151
updateTags()152 void DecoderVorbis::updateTags()
153 {
154 int i;
155 vorbis_comment *comments;
156
157 QMap <Qmmp::MetaData, QString> metaData;
158 comments = ov_comment (&oggfile, -1);
159 for (i = 0; i < comments->comments; i++)
160 {
161 if (!strncasecmp(comments->user_comments[i], "title=",
162 strlen ("title=")))
163 metaData.insert(Qmmp::TITLE, QString::fromUtf8(comments->user_comments[i]
164 + strlen ("title=")));
165 else if (!strncasecmp(comments->user_comments[i],
166 "artist=", strlen ("artist=")))
167 metaData.insert(Qmmp::ARTIST,
168 QString::fromUtf8(comments->user_comments[i]
169 + strlen ("artist=")));
170 else if (!strncasecmp(comments->user_comments[i],
171 "album=", strlen ("album=")))
172 metaData.insert(Qmmp::ALBUM,
173 QString::fromUtf8(comments->user_comments[i]
174 + strlen ("album=")));
175 else if (!strncasecmp(comments->user_comments[i],
176 "comment=", strlen ("comment=")))
177 metaData.insert(Qmmp::COMMENT,
178 QString::fromUtf8(comments->user_comments[i]
179 + strlen ("comment=")));
180 else if (!strncasecmp(comments->user_comments[i],
181 "genre=", strlen ("genre=")))
182 metaData.insert(Qmmp::GENRE, QString::fromUtf8 (comments->user_comments[i]
183 + strlen ("genre=")));
184 else if (!strncasecmp(comments->user_comments[i],
185 "tracknumber=",
186 strlen ("tracknumber=")))
187 metaData.insert(Qmmp::TRACK, QString::number(atoi(comments->user_comments[i]
188 + strlen ("tracknumber="))));
189 else if (!strncasecmp(comments->user_comments[i],
190 "track=", strlen ("track=")))
191 metaData.insert(Qmmp::TRACK, QString::number(atoi(comments->user_comments[i]
192 + strlen ("track="))));
193 else if (!strncasecmp(comments->user_comments[i],
194 "date=", strlen ("date=")))
195 metaData.insert(Qmmp::YEAR, QString::number(atoi(comments->user_comments[i]
196 + strlen ("date="))));
197 else if (!strncasecmp(comments->user_comments[i],
198 "composer=", strlen ("composer=")))
199 metaData.insert(Qmmp::COMPOSER, QString::fromUtf8 (comments->user_comments[i]
200 + strlen ("composer=")));
201 else if (!strncasecmp(comments->user_comments[i],
202 "discnumber=", strlen ("discnumber=")))
203 metaData.insert(Qmmp::DISCNUMBER, QString::number(atoi(comments->user_comments[i]
204 + strlen ("discnumber="))));
205 }
206 addMetaData(metaData);
207 }
208
209 //http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
findChannelMap(int channels)210 ChannelMap DecoderVorbis::findChannelMap(int channels)
211 {
212 ChannelMap map;
213 switch (channels)
214 {
215 case 1:
216 map << Qmmp::CHAN_FRONT_LEFT;
217 break;
218 case 2:
219 map << Qmmp::CHAN_FRONT_LEFT
220 << Qmmp::CHAN_FRONT_RIGHT;
221 break;
222 case 3:
223 map << Qmmp::CHAN_FRONT_LEFT
224 << Qmmp::CHAN_FRONT_CENTER
225 << Qmmp::CHAN_FRONT_RIGHT;
226 break;
227 case 4:
228 map << Qmmp::CHAN_FRONT_LEFT
229 << Qmmp::CHAN_FRONT_RIGHT
230 << Qmmp::CHAN_REAR_LEFT
231 << Qmmp::CHAN_REAR_RIGHT;
232 break;
233 case 5:
234 map << Qmmp::CHAN_FRONT_LEFT
235 << Qmmp::CHAN_FRONT_CENTER
236 << Qmmp::CHAN_FRONT_RIGHT
237 << Qmmp::CHAN_REAR_LEFT
238 << Qmmp::CHAN_REAR_RIGHT;
239 break;
240 case 6:
241 map << Qmmp::CHAN_FRONT_LEFT
242 << Qmmp::CHAN_FRONT_CENTER
243 << Qmmp::CHAN_FRONT_RIGHT
244 << Qmmp::CHAN_REAR_LEFT
245 << Qmmp::CHAN_REAR_RIGHT
246 << Qmmp::CHAN_LFE;
247 break;
248 case 7:
249 map << Qmmp::CHAN_FRONT_LEFT
250 << Qmmp::CHAN_FRONT_CENTER
251 << Qmmp::CHAN_FRONT_RIGHT
252 << Qmmp::CHAN_SIDE_LEFT
253 << Qmmp::CHAN_SIDE_RIGHT
254 << Qmmp::CHAN_REAR_CENTER
255 << Qmmp::CHAN_LFE;
256 break;
257 case 8:
258 map << Qmmp::CHAN_FRONT_LEFT
259 << Qmmp::CHAN_FRONT_CENTER
260 << Qmmp::CHAN_FRONT_RIGHT
261 << Qmmp::CHAN_SIDE_LEFT
262 << Qmmp::CHAN_SIDE_RIGHT
263 << Qmmp::CHAN_REAR_LEFT
264 << Qmmp::CHAN_REAR_RIGHT
265 << Qmmp::CHAN_LFE;
266 break;
267 default:
268 ;
269 }
270 return map;
271 }
272
seek(qint64 time)273 void DecoderVorbis::seek(qint64 time)
274 {
275 ov_time_seek(&oggfile, (double) time/1000);
276 }
277
read(unsigned char * data,qint64 maxSize)278 qint64 DecoderVorbis::read(unsigned char *data, qint64 maxSize)
279 {
280 len = -1;
281 float **pcm = nullptr;
282 int section = 0;
283 while (len < 0)
284 len = ov_read_float(&oggfile, &pcm, maxSize/sizeof(float), §ion);
285
286 if(len == 0)
287 return 0;
288
289 int channels = audioParameters().channels();
290
291 for(int i = 0; i < channels; ++i)
292 {
293 float *ptr = (float *) (data + i*sizeof(float));
294 for(int j = 0; j < len; ++j)
295 {
296 *ptr = pcm[i][j];
297 ptr += channels;
298 }
299 }
300
301 if (section != m_last_section)
302 {
303 updateTags();
304 m_last_section = section;
305 }
306
307 m_bitrate = ov_bitrate_instant(&oggfile) / 1000;
308 return len*sizeof(float)*channels;
309 }
310