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), &section);
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