Disable unused options
[platform/upstream/alure.git] / src / codec_mpg123.cpp
1 /*
2  * ALURE  OpenAL utility library
3  * Copyright (c) 2009-2010 by Chris Robinson.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23
24 #include "config.h"
25
26 #include "main.h"
27
28 #include <string.h>
29 #include <assert.h>
30
31 #include <istream>
32
33 #include <mpg123.h>
34
35
36 #ifdef DYNLOAD
37 static void *mp123_handle;
38 #define MAKE_FUNC(x) static typeof(x)* p##x
39 MAKE_FUNC(mpg123_read);
40 MAKE_FUNC(mpg123_init);
41 MAKE_FUNC(mpg123_open_feed);
42 MAKE_FUNC(mpg123_new);
43 MAKE_FUNC(mpg123_delete);
44 MAKE_FUNC(mpg123_feed);
45 MAKE_FUNC(mpg123_exit);
46 MAKE_FUNC(mpg123_getformat);
47 MAKE_FUNC(mpg123_format_none);
48 MAKE_FUNC(mpg123_decode);
49 MAKE_FUNC(mpg123_format);
50 #undef MAKE_FUNC
51
52 #define mpg123_read pmpg123_read
53 #define mpg123_init pmpg123_init
54 #define mpg123_open_feed pmpg123_open_feed
55 #define mpg123_new pmpg123_new
56 #define mpg123_delete pmpg123_delete
57 #define mpg123_feed pmpg123_feed
58 #define mpg123_exit pmpg123_exit
59 #define mpg123_getformat pmpg123_getformat
60 #define mpg123_format_none pmpg123_format_none
61 #define mpg123_decode pmpg123_decode
62 #define mpg123_format pmpg123_format
63 #else
64 #define mp123_handle 1
65 #endif
66
67
68 struct mp3Stream : public alureStream {
69 private:
70     mpg123_handle *mp3File;
71     long samplerate;
72     int channels;
73     ALenum format;
74     std::ios::pos_type dataStart;
75     std::ios::pos_type dataEnd;
76
77 public:
78 #ifdef DYNLOAD
79     static void Init()
80     {
81 #ifdef _WIN32
82 #define MPG123_LIB "libmpg123.dll"
83 #elif defined(__APPLE__)
84 #define MPG123_LIB "libmpg123.0.dylib"
85 #else
86 #define MPG123_LIB "libmpg123.so.0"
87 #endif
88         mp123_handle = OpenLib(MPG123_LIB);
89         if(!mp123_handle) return;
90
91         LOAD_FUNC(mp123_handle, mpg123_read);
92         LOAD_FUNC(mp123_handle, mpg123_init);
93         LOAD_FUNC(mp123_handle, mpg123_open_feed);
94         LOAD_FUNC(mp123_handle, mpg123_new);
95         LOAD_FUNC(mp123_handle, mpg123_delete);
96         LOAD_FUNC(mp123_handle, mpg123_feed);
97         LOAD_FUNC(mp123_handle, mpg123_exit);
98         LOAD_FUNC(mp123_handle, mpg123_getformat);
99         LOAD_FUNC(mp123_handle, mpg123_format_none);
100         LOAD_FUNC(mp123_handle, mpg123_decode);
101         LOAD_FUNC(mp123_handle, mpg123_format);
102         pmpg123_init();
103     }
104     static void Deinit()
105     {
106         if(mp123_handle)
107         {
108             pmpg123_exit();
109             CloseLib(mp123_handle);
110         }
111         mp123_handle = NULL;
112     }
113 #else
114     static void Init() { }
115     static void Deinit() { }
116 #endif
117
118     virtual bool IsValid()
119     { return mp3File != NULL; }
120
121     virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
122     {
123         *fmt = format;
124         *frequency = samplerate;
125         *blockalign = channels*2;
126         return true;
127     }
128
129     virtual ALuint GetData(ALubyte *data, ALuint bytes)
130     {
131         if(!mp3File)
132             return 0;
133
134         ALuint amt = 0;
135         while(bytes > 0)
136         {
137             size_t got = 0;
138             int ret = mpg123_read(mp3File, data, bytes, &got);
139
140             bytes -= got;
141             data += got;
142             amt += got;
143
144             if(ret == MPG123_NEW_FORMAT)
145             {
146                 mpg123_delete(mp3File);
147                 mp3File = NULL;
148                 break;
149             }
150             if(ret == MPG123_NEED_MORE)
151             {
152                 unsigned char data[4096];
153                 ALint insize = std::min<ALint>(sizeof(data),
154                                                (dataEnd-fstream->tellg()));
155                 if(insize > 0)
156                 {
157                     fstream->read((char*)data, insize);
158                     insize = fstream->gcount();
159                 }
160                 if(insize > 0 && mpg123_feed(mp3File, data, insize) == MPG123_OK)
161                     continue;
162             }
163             if(got == 0)
164                 break;
165         }
166         return amt;
167     }
168
169     virtual bool Rewind()
170     {
171         fstream->clear();
172         std::ios::pos_type oldpos = fstream->tellg();
173         fstream->seekg(dataStart);
174
175         mpg123_handle *newFile = mpg123_new(NULL, NULL);
176         if(mpg123_open_feed(newFile) == MPG123_OK)
177         {
178             unsigned char data[4096];
179             long newrate;
180             int newchans;
181             int enc;
182
183             ALuint amt, total = 0;
184             int ret = MPG123_OK;
185             do {
186                 amt = std::min<ALint>(sizeof(data),
187                                       (dataEnd-fstream->tellg()));
188                 fstream->read((char*)data, amt);
189                 amt = fstream->gcount();
190                 if(amt == 0)  break;
191                 total += amt;
192                 ret = mpg123_decode(newFile, data, amt, NULL, 0, NULL);
193             } while(ret == MPG123_NEED_MORE && total < 64*1024);
194
195             if(ret == MPG123_NEW_FORMAT &&
196                mpg123_getformat(newFile, &newrate, &newchans, &enc) == MPG123_OK)
197             {
198                 if(mpg123_format_none(newFile) == MPG123_OK &&
199                    mpg123_format(newFile, samplerate, channels, MPG123_ENC_SIGNED_16) == MPG123_OK)
200                 {
201                     // All OK
202                     if(mp3File)
203                         mpg123_delete(mp3File);
204                     mp3File = newFile;
205                     return true;
206                 }
207             }
208             mpg123_delete(newFile);
209         }
210
211         fstream->seekg(oldpos);
212         SetError("Restart failed");
213         return false;
214     }
215
216     mp3Stream(std::istream *_fstream)
217       : alureStream(_fstream), mp3File(NULL), format(AL_NONE),
218         dataStart(0), dataEnd(0)
219     {
220         if(!mp123_handle) return;
221
222         if(!FindDataChunk())
223             return;
224
225         mp3File = mpg123_new(NULL, NULL);
226         if(mpg123_open_feed(mp3File) == MPG123_OK)
227         {
228             unsigned char data[4096];
229             int enc;
230
231             ALuint amt, total = 0;
232             int ret = MPG123_OK;
233             do {
234                 amt = std::min<ALint>(sizeof(data),
235                                       (dataEnd-fstream->tellg()));
236                 fstream->read((char*)data, amt);
237                 amt = fstream->gcount();
238                 if(amt == 0)  break;
239                 total += amt;
240                 ret = mpg123_decode(mp3File, data, amt, NULL, 0, NULL);
241             } while(ret == MPG123_NEED_MORE && total < 64*1024);
242
243             if(ret == MPG123_NEW_FORMAT &&
244                mpg123_getformat(mp3File, &samplerate, &channels, &enc) == MPG123_OK)
245             {
246                 format = GetSampleFormat(channels, 16, false);
247                 if(mpg123_format_none(mp3File) == MPG123_OK &&
248                    mpg123_format(mp3File, samplerate, channels, MPG123_ENC_SIGNED_16) == MPG123_OK)
249                 {
250                     // All OK
251                     return;
252                 }
253             }
254         }
255         mpg123_delete(mp3File);
256         mp3File = NULL;
257     }
258
259     virtual ~mp3Stream()
260     {
261         if(mp3File)
262             mpg123_delete(mp3File);
263         mp3File = NULL;
264     }
265
266 private:
267     bool FindDataChunk()
268     {
269         ALubyte buffer[25];
270         int length;
271
272         if(!fstream->read(reinterpret_cast<char*>(buffer), 12))
273             return false;
274
275         if(memcmp(buffer, "RIFF", 4) != 0 || memcmp(buffer+8, "WAVE", 4) != 0)
276         {
277             dataStart = 0;
278
279             // Check for an ID3v2 tag, and skip it
280             if(memcmp(buffer, "ID3", 3) == 0 &&
281                buffer[3] <= 4 && buffer[4] != 0xff &&
282                (buffer[5]&0x0f) == 0 && (buffer[6]&0x80) == 0 &&
283                (buffer[7]&0x80) == 0 && (buffer[8]&0x80) == 0 &&
284                (buffer[9]&0x80) == 0)
285             {
286                 dataStart = (buffer[6]<<21) | (buffer[7]<<14) |
287                             (buffer[8]<< 7) | (buffer[9]    );
288                 dataStart += ((buffer[5]&0x10) ? 20 : 10);
289             }
290
291             if(fstream->seekg(0, std::ios_base::end))
292             {
293                 dataEnd = fstream->tellg();
294                 fstream->seekg(dataStart);
295             }
296             return fstream->good();
297         }
298
299         int type = 0;
300         while(1)
301         {
302             char tag[4];
303             if(!fstream->read(tag, 4))
304                 break;
305
306             /* read chunk length */
307             length = read_le32(fstream);
308
309             if(memcmp(tag, "fmt ", 4) == 0 && length >= 16)
310             {
311                 /* Data type (should be 0x0050 or 0x0055 for MP3 data) */
312                 type = read_le16(fstream);
313                 if(type != 0x0050 && type != 0x0055)
314                     break;
315                 length -= 2;
316                 /* Ignore the rest of the chunk. Everything we need is in the
317                  * data stream */
318             }
319             else if(memcmp(tag, "data", 4) == 0)
320             {
321                 if(type == 0x0050 || type == 0x0055)
322                 {
323                     dataStart = fstream->tellg();
324                     dataEnd = dataStart;
325                     dataEnd += length;
326                     return fstream->good();
327                 }
328             }
329
330             fstream->seekg(length, std::ios_base::cur);
331         }
332
333         return false;
334     }
335 };
336 // Priority = -2, because mp3 loading can find false-positives, and interferes
337 // with mod files
338 static DecoderDecl<mp3Stream,-2> mp3Stream_decoder;
339 Decoder &alure_init_mpg123(void)
340 { return mp3Stream_decoder; }