Move decoder (de)initialization into their respective classes
[alure.git] / src / codec_mpg123.cpp
blobf77d7bd6d63de2e66d3dbc71848f0bf4de72f719
1 /*
2 * ALURE OpenAL utility library
3 * Copyright (c) 2009-2010 by Chris Robinson.
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
24 #include "config.h"
26 #include "main.h"
28 #include <string.h>
29 #include <assert.h>
31 #include <istream>
33 #include <mpg123.h>
36 #ifdef _WIN32
37 #define MPG123_LIB "libmpg123.dll"
38 #elif defined(__APPLE__)
39 #define MPG123_LIB "libmpg123.0.dylib"
40 #else
41 #define MPG123_LIB "libmpg123.so.0"
42 #endif
44 static void *mp123_handle;
45 #define MAKE_FUNC(x) static typeof(x)* p##x
46 MAKE_FUNC(mpg123_read);
47 MAKE_FUNC(mpg123_init);
48 MAKE_FUNC(mpg123_open_feed);
49 MAKE_FUNC(mpg123_new);
50 MAKE_FUNC(mpg123_delete);
51 MAKE_FUNC(mpg123_feed);
52 MAKE_FUNC(mpg123_exit);
53 MAKE_FUNC(mpg123_getformat);
54 MAKE_FUNC(mpg123_format_none);
55 MAKE_FUNC(mpg123_decode);
56 MAKE_FUNC(mpg123_format);
57 #undef MAKE_FUNC
60 struct mp3Stream : public alureStream {
61 private:
62 mpg123_handle *mp3File;
63 long samplerate;
64 int channels;
65 ALenum format;
66 std::ios::pos_type dataStart;
67 std::ios::pos_type dataEnd;
69 public:
70 static void Init()
72 mp123_handle = OpenLib(MPG123_LIB);
73 if(!mp123_handle) return;
75 LOAD_FUNC(mp123_handle, mpg123_read);
76 LOAD_FUNC(mp123_handle, mpg123_init);
77 LOAD_FUNC(mp123_handle, mpg123_open_feed);
78 LOAD_FUNC(mp123_handle, mpg123_new);
79 LOAD_FUNC(mp123_handle, mpg123_delete);
80 LOAD_FUNC(mp123_handle, mpg123_feed);
81 LOAD_FUNC(mp123_handle, mpg123_exit);
82 LOAD_FUNC(mp123_handle, mpg123_getformat);
83 LOAD_FUNC(mp123_handle, mpg123_format_none);
84 LOAD_FUNC(mp123_handle, mpg123_decode);
85 LOAD_FUNC(mp123_handle, mpg123_format);
86 pmpg123_init();
88 static void Deinit()
90 if(mp123_handle)
92 pmpg123_exit();
93 CloseLib(mp123_handle);
95 mp123_handle = NULL;
98 virtual bool IsValid()
99 { return mp3File != NULL; }
101 virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
103 *fmt = format;
104 *frequency = samplerate;
105 *blockalign = channels*2;
106 return true;
109 virtual ALuint GetData(ALubyte *data, ALuint bytes)
111 ALuint amt = 0;
112 while(bytes > 0)
114 size_t got = 0;
115 int ret = pmpg123_read(mp3File, data, bytes, &got);
117 bytes -= got;
118 data += got;
119 amt += got;
121 if(ret == MPG123_NEW_FORMAT)
123 long newrate;
124 int newchans, enc;
125 pmpg123_getformat(mp3File, &newrate, &newchans, &enc);
126 continue;
128 if(ret == MPG123_NEED_MORE)
130 unsigned char data[4096];
131 ALint insize = std::min<ALint>(sizeof(data),
132 (dataEnd-fstream->tellg()));
133 if(insize > 0)
135 fstream->read((char*)data, insize);
136 insize = fstream->gcount();
138 if(insize > 0 && pmpg123_feed(mp3File, data, insize) == MPG123_OK)
139 continue;
141 if(got == 0)
142 break;
144 return amt;
147 virtual bool Rewind()
149 fstream->clear();
150 std::istream::pos_type oldpos = fstream->tellg();
151 fstream->seekg(dataStart);
153 mpg123_handle *newFile = pmpg123_new(NULL, NULL);
154 if(pmpg123_open_feed(newFile) == MPG123_OK)
156 unsigned char data[4096];
157 long newrate;
158 int newchans;
159 int enc;
161 ALuint amt, total = 0;
162 int ret = MPG123_OK;
163 do {
164 amt = std::min<ALint>(sizeof(data),
165 (dataEnd-fstream->tellg()));
166 fstream->read((char*)data, amt);
167 amt = fstream->gcount();
168 if(amt == 0) break;
169 total += amt;
170 ret = pmpg123_decode(newFile, data, amt, NULL, 0, NULL);
171 } while(ret == MPG123_NEED_MORE && total < 64*1024);
173 if(ret == MPG123_NEW_FORMAT &&
174 pmpg123_getformat(newFile, &newrate, &newchans, &enc) == MPG123_OK)
176 if(pmpg123_format_none(newFile) == MPG123_OK &&
177 pmpg123_format(newFile, samplerate, channels, MPG123_ENC_SIGNED_16) == MPG123_OK)
179 // All OK
180 pmpg123_delete(mp3File);
181 mp3File = newFile;
182 return true;
185 pmpg123_delete(newFile);
188 fstream->seekg(oldpos);
189 SetError("Restart failed");
190 return false;
193 mp3Stream(std::istream *_fstream)
194 : alureStream(_fstream), mp3File(NULL), format(AL_NONE),
195 dataStart(0), dataEnd(0)
197 if(!mp123_handle) return;
199 if(!FindDataChunk())
200 return;
202 mp3File = pmpg123_new(NULL, NULL);
203 if(pmpg123_open_feed(mp3File) == MPG123_OK)
205 unsigned char data[4096];
206 int enc;
208 ALuint amt, total = 0;
209 int ret = MPG123_OK;
210 do {
211 amt = std::min<ALint>(sizeof(data),
212 (dataEnd-fstream->tellg()));
213 fstream->read((char*)data, amt);
214 amt = fstream->gcount();
215 if(amt == 0) break;
216 total += amt;
217 ret = pmpg123_decode(mp3File, data, amt, NULL, 0, NULL);
218 } while(ret == MPG123_NEED_MORE && total < 64*1024);
220 if(ret == MPG123_NEW_FORMAT &&
221 pmpg123_getformat(mp3File, &samplerate, &channels, &enc) == MPG123_OK)
223 format = GetSampleFormat(channels, 16, false);
224 if(pmpg123_format_none(mp3File) == MPG123_OK &&
225 pmpg123_format(mp3File, samplerate, channels, MPG123_ENC_SIGNED_16) == MPG123_OK)
227 // All OK
228 return;
232 pmpg123_delete(mp3File);
233 mp3File = NULL;
236 virtual ~mp3Stream()
238 if(mp3File)
239 pmpg123_delete(mp3File);
240 mp3File = NULL;
243 private:
244 bool FindDataChunk()
246 ALubyte buffer[25];
247 int length;
249 if(!fstream->read(reinterpret_cast<char*>(buffer), 12))
250 return false;
252 if(memcmp(buffer, "RIFF", 4) != 0 || memcmp(buffer+8, "WAVE", 4) != 0)
254 dataStart = 0;
256 // Check for an ID3v2 tag, and skip it
257 if(memcmp(buffer, "ID3", 3) == 0 &&
258 buffer[3] <= 4 && buffer[4] != 0xff &&
259 (buffer[5]&0x0f) == 0 && (buffer[6]&0x80) == 0 &&
260 (buffer[7]&0x80) == 0 && (buffer[8]&0x80) == 0 &&
261 (buffer[9]&0x80) == 0)
263 dataStart = (buffer[6]<<21) | (buffer[7]<<14) |
264 (buffer[8]<< 7) | (buffer[9] );
265 dataStart += ((buffer[5]&0x10) ? 20 : 10);
268 if(fstream->seekg(0, std::ios_base::end))
270 dataEnd = fstream->tellg();
271 fstream->seekg(dataStart);
273 return true;
276 int type = 0;
277 while(1)
279 char tag[4];
280 if(!fstream->read(tag, 4))
281 break;
283 /* read chunk length */
284 length = read_le32(fstream);
286 if(memcmp(tag, "fmt ", 4) == 0 && length >= 16)
288 /* Data type (should be 0x0050 or 0x0055 for MP3 data) */
289 type = read_le16(fstream);
290 if(type != 0x0050 && type != 0x0055)
291 break;
292 length -= 2;
293 /* Ignore the rest of the chunk. Everything we need is in the
294 * data stream */
296 else if(memcmp(tag, "data", 4) == 0)
298 if(type == 0x0050 || type == 0x0055)
300 dataStart = fstream->tellg();
301 dataEnd = dataStart;
302 dataEnd += length;
303 return true;
307 fstream->seekg(length, std::ios_base::cur);
310 return false;
313 static DecoderDecl<mp3Stream> mp3Stream_decoder;