Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 : #include "precompiled.h"
18 :
19 : #include "ogg.h"
20 :
21 : #if CONFIG2_AUDIO
22 :
23 : #include "lib/byte_order.h"
24 : #include "lib/external_libraries/openal.h"
25 : #include "lib/external_libraries/vorbis.h"
26 : #include "lib/file/file_system.h"
27 : #include "lib/file/io/io.h"
28 : #include "lib/file/vfs/vfs_util.h"
29 : #include "maths/MathUtil.h"
30 : #include "ps/CLogger.h"
31 : #include "ps/Filesystem.h"
32 :
33 :
34 0 : static Status LibErrorFromVorbis(int err)
35 : {
36 0 : switch(err)
37 : {
38 0 : case 0:
39 0 : return INFO::OK;
40 0 : case OV_HOLE:
41 0 : return ERR::AGAIN;
42 0 : case OV_EREAD:
43 0 : return ERR::IO;
44 0 : case OV_EFAULT:
45 0 : return ERR::LOGIC;
46 0 : case OV_EIMPL:
47 0 : return ERR::NOT_SUPPORTED;
48 0 : case OV_EINVAL:
49 0 : return ERR::INVALID_PARAM;
50 0 : case OV_ENOTVORBIS:
51 0 : return ERR::NOT_SUPPORTED;
52 0 : case OV_EBADHEADER:
53 0 : return ERR::CORRUPTED;
54 0 : case OV_EVERSION:
55 0 : return ERR::INVALID_VERSION;
56 0 : case OV_ENOTAUDIO:
57 0 : return ERR::_1;
58 0 : case OV_EBADPACKET:
59 0 : return ERR::_2;
60 0 : case OV_EBADLINK:
61 0 : return ERR::_3;
62 0 : case OV_ENOSEEK:
63 0 : return ERR::_4;
64 0 : default:
65 0 : return ERR::FAIL;
66 : }
67 : }
68 :
69 :
70 : //-----------------------------------------------------------------------------
71 :
72 0 : class VorbisFileAdapter
73 : {
74 : public:
75 0 : VorbisFileAdapter(const PFile& openedFile)
76 0 : : file(openedFile)
77 0 : , size(FileSize(openedFile->Pathname()))
78 0 : , offset(0)
79 : {
80 0 : }
81 :
82 0 : static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
83 : {
84 0 : VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
85 0 : const off_t sizeRequested = numItems*itemSize;
86 0 : const off_t sizeRemaining = adapter->size - adapter->offset;
87 0 : const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
88 :
89 0 : io::Operation op(*adapter->file.get(), bufferToFill, sizeToRead, adapter->offset);
90 0 : if(io::Run(op) == INFO::OK)
91 : {
92 0 : adapter->offset += sizeToRead;
93 0 : return sizeToRead;
94 : }
95 :
96 0 : errno = EIO;
97 0 : return 0;
98 : }
99 :
100 0 : static int Seek(void* context, ogg_int64_t offset, int whence)
101 : {
102 0 : VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
103 :
104 0 : off_t origin = 0;
105 0 : switch(whence)
106 : {
107 0 : case SEEK_SET:
108 0 : origin = 0;
109 0 : break;
110 0 : case SEEK_CUR:
111 0 : origin = adapter->offset;
112 0 : break;
113 0 : case SEEK_END:
114 0 : origin = adapter->size+1;
115 0 : break;
116 0 : NODEFAULT;
117 : }
118 :
119 0 : adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
120 0 : return 0;
121 : }
122 :
123 0 : static int Close(void* context)
124 : {
125 0 : VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
126 0 : adapter->file.reset();
127 0 : return 0; // return value is ignored
128 : }
129 :
130 0 : static long Tell(void* context)
131 : {
132 0 : VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
133 0 : return adapter->offset;
134 : }
135 :
136 : private:
137 : PFile file;
138 : off_t size;
139 : off_t offset;
140 : };
141 :
142 : //-----------------------------------------------------------------------------
143 :
144 0 : class VorbisBufferAdapter
145 : {
146 : public:
147 0 : VorbisBufferAdapter(const std::shared_ptr<u8>& buffer, size_t size)
148 0 : : buffer(buffer)
149 : , size(size)
150 0 : , offset(0)
151 : {
152 0 : }
153 :
154 0 : static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
155 : {
156 0 : VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
157 :
158 0 : const off_t sizeRequested = numItems*itemSize;
159 0 : const off_t sizeRemaining = adapter->size - adapter->offset;
160 0 : const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
161 :
162 0 : memcpy(bufferToFill, adapter->buffer.get() + adapter->offset, sizeToRead);
163 :
164 0 : adapter->offset += sizeToRead;
165 0 : return sizeToRead;
166 : }
167 :
168 0 : static int Seek(void* context, ogg_int64_t offset, int whence)
169 : {
170 0 : VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
171 :
172 0 : off_t origin = 0;
173 0 : switch(whence)
174 : {
175 0 : case SEEK_SET:
176 0 : origin = 0;
177 0 : break;
178 0 : case SEEK_CUR:
179 0 : origin = adapter->offset;
180 0 : break;
181 0 : case SEEK_END:
182 0 : origin = adapter->size+1;
183 0 : break;
184 0 : NODEFAULT;
185 : }
186 :
187 0 : adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
188 0 : return 0;
189 : }
190 :
191 0 : static int Close(void* context)
192 : {
193 0 : VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
194 0 : adapter->buffer.reset();
195 0 : return 0; // return value is ignored
196 : }
197 :
198 0 : static long Tell(void* context)
199 : {
200 0 : VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
201 0 : return adapter->offset;
202 : }
203 :
204 : private:
205 : std::shared_ptr<u8> buffer;
206 : off_t size;
207 : off_t offset;
208 : };
209 :
210 :
211 : //-----------------------------------------------------------------------------
212 :
213 : template <typename Adapter>
214 0 : class OggStreamImpl : public OggStream
215 : {
216 : public:
217 0 : OggStreamImpl(const Adapter& adapter)
218 0 : : adapter(adapter)
219 : {
220 0 : m_fileEOF = false;
221 0 : info = NULL;
222 0 : }
223 :
224 0 : Status Close()
225 : {
226 0 : ov_clear( &vf );
227 :
228 0 : return 0;
229 : }
230 :
231 0 : Status Open()
232 : {
233 : ov_callbacks callbacks;
234 0 : callbacks.read_func = Adapter::Read;
235 0 : callbacks.close_func = Adapter::Close;
236 0 : callbacks.seek_func = Adapter::Seek;
237 0 : callbacks.tell_func = Adapter::Tell;
238 0 : const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks);
239 0 : if(ret != 0)
240 0 : WARN_RETURN(LibErrorFromVorbis(ret));
241 :
242 0 : const int link = -1; // retrieve info for current bitstream
243 0 : info = ov_info(&vf, link);
244 0 : if(!info)
245 0 : WARN_RETURN(ERR::INVALID_HANDLE);
246 :
247 0 : return INFO::OK;
248 : }
249 :
250 0 : virtual ALenum Format()
251 : {
252 0 : return (info->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
253 : }
254 :
255 0 : virtual ALsizei SamplingRate()
256 : {
257 0 : return info->rate;
258 : }
259 0 : virtual bool atFileEOF()
260 : {
261 0 : return m_fileEOF;
262 : }
263 :
264 0 : virtual Status ResetFile()
265 : {
266 0 : ov_time_seek( &vf, 0 );
267 0 : m_fileEOF = false;
268 0 : return INFO::OK;
269 : }
270 :
271 0 : virtual Status GetNextChunk(u8* buffer, size_t size)
272 : {
273 : // we may have to call ov_read multiple times because it
274 : // treats the buffer size "as a limit and not a request"
275 0 : size_t bytesRead = 0;
276 0 : for(;;)
277 : {
278 0 : const int isBigEndian = (BYTE_ORDER == BIG_ENDIAN);
279 0 : const int wordSize = sizeof(i16);
280 0 : const int isSigned = 1;
281 : int bitstream; // unused
282 0 : const int ret = ov_read(&vf, (char*)buffer+bytesRead, int(size-bytesRead), isBigEndian, wordSize, isSigned, &bitstream);
283 0 : if(ret == 0) { // EOF
284 0 : m_fileEOF = true;
285 0 : return (Status)bytesRead;
286 : }
287 0 : else if(ret < 0)
288 0 : WARN_RETURN(LibErrorFromVorbis(ret));
289 : else // success
290 : {
291 0 : bytesRead += ret;
292 0 : if(bytesRead == size)
293 0 : return (Status)bytesRead;
294 : }
295 : }
296 : }
297 :
298 : private:
299 : Adapter adapter;
300 : OggVorbis_File vf;
301 : vorbis_info* info;
302 : bool m_fileEOF;
303 : };
304 :
305 :
306 : //-----------------------------------------------------------------------------
307 :
308 0 : Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream)
309 : {
310 0 : PFile file(new File);
311 0 : RETURN_STATUS_IF_ERR(file->Open(pathname, L'r'));
312 :
313 0 : std::shared_ptr<OggStreamImpl<VorbisFileAdapter>> tmp = std::make_shared<OggStreamImpl<VorbisFileAdapter>>(VorbisFileAdapter(file));
314 0 : RETURN_STATUS_IF_ERR(tmp->Open());
315 0 : stream = tmp;
316 0 : return INFO::OK;
317 : }
318 :
319 0 : Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream)
320 : {
321 0 : std::shared_ptr<u8> contents;
322 : size_t size;
323 0 : RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, contents, size));
324 :
325 0 : std::shared_ptr<OggStreamImpl<VorbisBufferAdapter>> tmp = std::make_shared<OggStreamImpl<VorbisBufferAdapter>>(VorbisBufferAdapter(contents, size));
326 0 : RETURN_STATUS_IF_ERR(tmp->Open());
327 0 : stream = tmp;
328 0 : return INFO::OK;
329 3 : }
330 :
331 : #endif // CONFIG2_AUDIO
332 :
|