Imported Upstream version 1.2
[platform/upstream/alure.git] / src / codec_vorbisfile.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 #ifdef HAS_VORBISIDEC
34 #include <tremor/ivorbiscodec.h>
35 #include <tremor/ivorbisfile.h>
36 #else
37 #include <vorbis/vorbisfile.h>
38 #endif
39
40
41 #ifdef DYNLOAD
42 static void *vorbisfile_handle;
43 #define MAKE_FUNC(x) static typeof(x)* p##x
44 MAKE_FUNC(ov_clear);
45 MAKE_FUNC(ov_info);
46 MAKE_FUNC(ov_open_callbacks);
47 MAKE_FUNC(ov_pcm_seek);
48 MAKE_FUNC(ov_pcm_total);
49 MAKE_FUNC(ov_read);
50 #undef MAKE_FUNC
51
52 #define ov_clear pov_clear
53 #define ov_info pov_info
54 #define ov_open_callbacks pov_open_callbacks
55 #define ov_pcm_seek pov_pcm_seek
56 #define ov_pcm_total pov_pcm_total
57 #define ov_read pov_read
58 #else
59 #define vorbisfile_handle 1
60 #endif
61
62
63 struct oggStream : public alureStream {
64 private:
65     OggVorbis_File oggFile;
66     vorbis_info *oggInfo;
67     int oggBitstream;
68     ALenum format;
69
70 public:
71 #ifdef DYNLOAD
72     static void Init()
73     {
74 #ifdef HAS_VORBISIDEC
75 #ifdef _WIN32
76 #define VORBISFILE_LIB "vorbisidec.dll"
77 #elif defined(__APPLE__)
78 #define VORBISFILE_LIB "libvorbisidec.1.dylib"
79 #else
80 #define VORBISFILE_LIB "libvorbisidec.so.1"
81 #endif
82 #else
83 #ifdef _WIN32
84 #define VORBISFILE_LIB "vorbisfile.dll"
85 #elif defined(__APPLE__)
86 #define VORBISFILE_LIB "libvorbisfile.3.dylib"
87 #else
88 #define VORBISFILE_LIB "libvorbisfile.so.3"
89 #endif
90 #endif
91         vorbisfile_handle = OpenLib(VORBISFILE_LIB);
92         if(!vorbisfile_handle) return;
93
94         LOAD_FUNC(vorbisfile_handle, ov_clear);
95         LOAD_FUNC(vorbisfile_handle, ov_info);
96         LOAD_FUNC(vorbisfile_handle, ov_open_callbacks);
97         LOAD_FUNC(vorbisfile_handle, ov_pcm_seek);
98         LOAD_FUNC(vorbisfile_handle, ov_pcm_total);
99         LOAD_FUNC(vorbisfile_handle, ov_read);
100     }
101     static void Deinit()
102     {
103         if(vorbisfile_handle)
104             CloseLib(vorbisfile_handle);
105         vorbisfile_handle = NULL;
106     }
107 #else
108     static void Init() { }
109     static void Deinit() { }
110 #endif
111
112     virtual bool IsValid()
113     { return oggInfo != NULL; }
114
115     virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
116     {
117         if(format == AL_NONE)
118             format = GetSampleFormat(oggInfo->channels, 16, false);
119         *fmt = format;
120         *frequency = oggInfo->rate;
121         *blockalign = oggInfo->channels*2;
122         return true;
123     }
124
125     virtual ALuint GetData(ALubyte *data, ALuint bytes)
126     {
127         ALuint got = 0;
128         while(bytes > 0)
129         {
130 #ifdef HAS_VORBISIDEC
131             int res = ov_read(&oggFile, (char*)&data[got], bytes, &oggBitstream);
132 #else
133             int res = ov_read(&oggFile, (char*)&data[got], bytes, BigEndian?1:0, 2, 1, &oggBitstream);
134 #endif
135             if(res <= 0)
136                 break;
137             bytes -= res;
138             got += res;
139         }
140         // 1, 2, and 4 channel files decode into the same channel order as
141         // OpenAL, however 6(5.1), 7(6.1), and 8(7.1) channel files need to be
142         // re-ordered
143         if(oggInfo->channels == 6)
144         {
145             ALshort *samples = (ALshort*)data;
146             for(ALuint i = 0;i < got/sizeof(ALshort);i+=6)
147             {
148                 // OpenAL : FL, FR, FC, LFE, RL, RR
149                 // Vorbis : FL, FC, FR,  RL, RR, LFE
150                 swap(samples[i+1], samples[i+2]);
151                 swap(samples[i+3], samples[i+5]);
152                 swap(samples[i+4], samples[i+5]);
153             }
154         }
155         else if(oggInfo->channels == 7)
156         {
157             ALshort *samples = (ALshort*)data;
158             for(ALuint i = 0;i < got/sizeof(ALshort);i+=7)
159             {
160                 // OpenAL : FL, FR, FC, LFE, RC, SL, SR
161                 // Vorbis : FL, FC, FR,  SL, SR, RC, LFE
162                 swap(samples[i+1], samples[i+2]);
163                 swap(samples[i+3], samples[i+6]);
164                 swap(samples[i+4], samples[i+6]);
165                 swap(samples[i+5], samples[i+6]);
166             }
167         }
168         else if(oggInfo->channels == 8)
169         {
170             ALshort *samples = (ALshort*)data;
171             for(ALuint i = 0;i < got/sizeof(ALshort);i+=8)
172             {
173                 // OpenAL : FL, FR, FC, LFE, RL, RR, SL, SR
174                 // Vorbis : FL, FC, FR,  SL, SR, RL, RR, LFE
175                 swap(samples[i+1], samples[i+2]);
176                 swap(samples[i+3], samples[i+7]);
177                 swap(samples[i+4], samples[i+5]);
178                 swap(samples[i+5], samples[i+6]);
179                 swap(samples[i+6], samples[i+7]);
180             }
181         }
182         return got;
183     }
184
185     virtual bool Rewind()
186     {
187         if(ov_pcm_seek(&oggFile, 0) == 0)
188             return true;
189
190         SetError("Seek failed");
191         return false;
192     }
193
194     virtual alureInt64 GetLength()
195     {
196         ogg_int64_t len = ov_pcm_total(&oggFile, oggBitstream);
197         if(len < 0) return 0;
198         return len;
199     }
200
201     oggStream(std::istream *_fstream)
202       : alureStream(_fstream), oggInfo(NULL), oggBitstream(0), format(AL_NONE)
203     {
204         if(!vorbisfile_handle) return;
205
206         const ov_callbacks streamIO = {
207             read, seek, close, tell
208         };
209
210         if(ov_open_callbacks(this, &oggFile, NULL, 0, streamIO) == 0)
211         {
212             oggInfo = ov_info(&oggFile, -1);
213             if(!oggInfo)
214                 ov_clear(&oggFile);
215         }
216     }
217
218     virtual ~oggStream()
219     {
220         if(oggInfo)
221             ov_clear(&oggFile);
222         oggInfo = NULL;
223     }
224
225 private:
226     // libVorbisFile iostream callbacks
227     static int seek(void *user_data, ogg_int64_t offset, int whence)
228     {
229         std::istream *stream = static_cast<oggStream*>(user_data)->fstream;
230         stream->clear();
231
232         if(whence == SEEK_CUR)
233             stream->seekg(offset, std::ios_base::cur);
234         else if(whence == SEEK_SET)
235             stream->seekg(offset, std::ios_base::beg);
236         else if(whence == SEEK_END)
237             stream->seekg(offset, std::ios_base::end);
238         else
239             return -1;
240
241         return stream->tellg();
242     }
243
244     static size_t read(void *ptr, size_t size, size_t nmemb, void *user_data)
245     {
246         std::istream *stream = static_cast<oggStream*>(user_data)->fstream;
247         stream->clear();
248
249         stream->read(static_cast<char*>(ptr), nmemb*size);
250         size_t ret = stream->gcount();
251         return ret/size;
252     }
253
254     static long tell(void *user_data)
255     {
256         std::istream *stream = static_cast<oggStream*>(user_data)->fstream;
257         stream->clear();
258         return stream->tellg();
259     }
260
261     static int close(void*)
262     {
263         return 0;
264     }
265 };
266 // Priority = 2, so it's preferred over libsndfile and libFLAC
267 static DecoderDecl<oggStream,2> oggStream_decoder;
268 Decoder &alure_init_vorbisfile(void)
269 { return oggStream_decoder; }