Release 1.1
[alure.git] / src / codec_vorbisfile.cpp
blob188a2b658714809f9cb47be7a63cd3ce9a87e06a
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 <vorbis/vorbisfile.h>
36 #ifdef _WIN32
37 #define VORBISFILE_LIB "vorbisfile.dll"
38 #elif defined(__APPLE__)
39 #define VORBISFILE_LIB "libvorbisfile.3.dylib"
40 #else
41 #define VORBISFILE_LIB "libvorbisfile.so.3"
42 #endif
44 static void *vorbisfile_handle;
45 #define MAKE_FUNC(x) static typeof(x)* p##x
46 MAKE_FUNC(ov_clear);
47 MAKE_FUNC(ov_info);
48 MAKE_FUNC(ov_open_callbacks);
49 MAKE_FUNC(ov_pcm_seek);
50 MAKE_FUNC(ov_read);
51 #undef MAKE_FUNC
54 struct oggStream : public alureStream {
55 private:
56 OggVorbis_File oggFile;
57 vorbis_info *oggInfo;
58 int oggBitstream;
59 ALenum format;
61 public:
62 static void Init()
64 vorbisfile_handle = OpenLib(VORBISFILE_LIB);
65 if(!vorbisfile_handle) return;
67 LOAD_FUNC(vorbisfile_handle, ov_clear);
68 LOAD_FUNC(vorbisfile_handle, ov_info);
69 LOAD_FUNC(vorbisfile_handle, ov_open_callbacks);
70 LOAD_FUNC(vorbisfile_handle, ov_pcm_seek);
71 LOAD_FUNC(vorbisfile_handle, ov_read);
73 static void Deinit()
75 if(vorbisfile_handle)
76 CloseLib(vorbisfile_handle);
77 vorbisfile_handle = NULL;
80 virtual bool IsValid()
81 { return oggInfo != NULL; }
83 virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
85 if(format == AL_NONE)
86 format = GetSampleFormat(oggInfo->channels, 16, false);
87 *fmt = format;
88 *frequency = oggInfo->rate;
89 *blockalign = oggInfo->channels*2;
90 return true;
93 virtual ALuint GetData(ALubyte *data, ALuint bytes)
95 ALuint got = 0;
96 while(bytes > 0)
98 int res = pov_read(&oggFile, (char*)&data[got], bytes, BigEndian?1:0, 2, 1, &oggBitstream);
99 if(res <= 0)
100 break;
101 bytes -= res;
102 got += res;
104 // 1, 2, and 4 channel files decode into the same channel order as
105 // OpenAL, however 6(5.1), 7(6.1), and 8(7.1) channel files need to be
106 // re-ordered
107 if(oggInfo->channels == 6)
109 ALshort *samples = (ALshort*)data;
110 for(ALuint i = 0;i < got/sizeof(ALshort);i+=6)
112 // OpenAL : FL, FR, FC, LFE, RL, RR
113 // Vorbis : FL, FC, FR, RL, RR, LFE
114 swap(samples[i+1], samples[i+2]);
115 swap(samples[i+3], samples[i+5]);
116 swap(samples[i+4], samples[i+5]);
119 else if(oggInfo->channels == 7)
121 ALshort *samples = (ALshort*)data;
122 for(ALuint i = 0;i < got/sizeof(ALshort);i+=7)
124 // OpenAL : FL, FR, FC, LFE, RC, SL, SR
125 // Vorbis : FL, FC, FR, SL, SR, RC, LFE
126 swap(samples[i+1], samples[i+2]);
127 swap(samples[i+3], samples[i+6]);
128 swap(samples[i+4], samples[i+6]);
129 swap(samples[i+5], samples[i+6]);
132 else if(oggInfo->channels == 8)
134 ALshort *samples = (ALshort*)data;
135 for(ALuint i = 0;i < got/sizeof(ALshort);i+=8)
137 // OpenAL : FL, FR, FC, LFE, RL, RR, SL, SR
138 // Vorbis : FL, FC, FR, SL, SR, RL, RR, LFE
139 swap(samples[i+1], samples[i+2]);
140 swap(samples[i+3], samples[i+7]);
141 swap(samples[i+4], samples[i+5]);
142 swap(samples[i+5], samples[i+6]);
143 swap(samples[i+6], samples[i+7]);
146 return got;
149 virtual bool Rewind()
151 if(pov_pcm_seek(&oggFile, 0) == 0)
152 return true;
154 SetError("Seek failed");
155 return false;
158 oggStream(std::istream *_fstream)
159 : alureStream(_fstream), oggInfo(NULL), oggBitstream(0), format(AL_NONE)
161 if(!vorbisfile_handle) return;
163 const ov_callbacks streamIO = {
164 read, seek, close, tell
167 if(pov_open_callbacks(this, &oggFile, NULL, 0, streamIO) == 0)
169 oggInfo = pov_info(&oggFile, -1);
170 if(!oggInfo)
171 pov_clear(&oggFile);
175 virtual ~oggStream()
177 if(oggInfo)
178 pov_clear(&oggFile);
179 oggInfo = NULL;
182 private:
183 // libVorbisFile iostream callbacks
184 static int seek(void *user_data, ogg_int64_t offset, int whence)
186 std::istream *stream = static_cast<oggStream*>(user_data)->fstream;
187 stream->clear();
189 if(whence == SEEK_CUR)
190 stream->seekg(offset, std::ios_base::cur);
191 else if(whence == SEEK_SET)
192 stream->seekg(offset, std::ios_base::beg);
193 else if(whence == SEEK_END)
194 stream->seekg(offset, std::ios_base::end);
195 else
196 return -1;
198 return stream->tellg();
201 static size_t read(void *ptr, size_t size, size_t nmemb, void *user_data)
203 std::istream *stream = static_cast<oggStream*>(user_data)->fstream;
204 stream->clear();
206 stream->read(static_cast<char*>(ptr), nmemb*size);
207 size_t ret = stream->gcount();
208 return ret/size;
211 static long tell(void *user_data)
213 std::istream *stream = static_cast<oggStream*>(user_data)->fstream;
214 stream->clear();
215 return stream->tellg();
218 static int close(void*)
220 return 0;
223 // Priority = 2, so it's preferred over libsndfile and libFLAC
224 static DecoderDecl<oggStream,2> oggStream_decoder;
225 Decoder &alure_init_vorbisfile(void)
226 { return oggStream_decoder; }