Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ogg.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 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 
25 
26 #include "lib/byte_order.h"
27 #include "lib/file/io/io.h"
28 #include "lib/file/file_system.h"
29 
30 #include "lib/file/vfs/vfs_util.h"
31 #include "ps/CLogger.h"
32 #include "ps/Filesystem.h"
33 
34 
35 static Status LibErrorFromVorbis(int err)
36 {
37  switch(err)
38  {
39  case 0:
40  return INFO::OK;
41  case OV_HOLE:
42  return ERR::AGAIN;
43  case OV_EREAD:
44  return ERR::IO;
45  case OV_EFAULT:
46  return ERR::LOGIC;
47  case OV_EIMPL:
48  return ERR::NOT_SUPPORTED;
49  case OV_EINVAL:
50  return ERR::INVALID_PARAM;
51  case OV_ENOTVORBIS:
52  return ERR::NOT_SUPPORTED;
53  case OV_EBADHEADER:
54  return ERR::CORRUPTED;
55  case OV_EVERSION:
56  return ERR::INVALID_VERSION;
57  case OV_ENOTAUDIO:
58  return ERR::_1;
59  case OV_EBADPACKET:
60  return ERR::_2;
61  case OV_EBADLINK:
62  return ERR::_3;
63  case OV_ENOSEEK:
64  return ERR::_4;
65  default:
66  return ERR::FAIL;
67  }
68 }
69 
70 
71 //-----------------------------------------------------------------------------
72 
74 {
75 public:
76  VorbisFileAdapter(const PFile& openedFile)
77  : file(openedFile)
78  , size(FileSize(openedFile->Pathname()))
79  , offset(0)
80  {
81  }
82 
83  static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
84  {
85  VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
86  const off_t sizeRequested = numItems*itemSize;
87  const off_t sizeRemaining = adapter->size - adapter->offset;
88  const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
89 
90  io::Operation op(*adapter->file.get(), bufferToFill, sizeToRead, adapter->offset);
91  if(io::Run(op) == INFO::OK)
92  {
93  adapter->offset += sizeToRead;
94  return sizeToRead;
95  }
96 
97  errno = EIO;
98  return 0;
99  }
100 
101  static int Seek(void* context, ogg_int64_t offset, int whence)
102  {
103  VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
104 
105  off_t origin = 0;
106  switch(whence)
107  {
108  case SEEK_SET:
109  origin = 0;
110  break;
111  case SEEK_CUR:
112  origin = adapter->offset;
113  break;
114  case SEEK_END:
115  origin = adapter->size+1;
116  break;
117  NODEFAULT;
118  }
119 
120  adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
121  return 0;
122  }
123 
124  static int Close(void* context)
125  {
126  VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
127  adapter->file.reset();
128  return 0; // return value is ignored
129  }
130 
131  static long Tell(void* context)
132  {
133  VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
134  return adapter->offset;
135  }
136 
137 private:
141 };
142 
143 //-----------------------------------------------------------------------------
144 
146 {
147 public:
148  VorbisBufferAdapter(const shared_ptr<u8>& buffer, size_t size)
149  : buffer(buffer)
150  , size(size)
151  , offset(0)
152  {
153  }
154 
155  static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
156  {
157  VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
158 
159  const off_t sizeRequested = numItems*itemSize;
160  const off_t sizeRemaining = adapter->size - adapter->offset;
161  const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
162 
163  memcpy(bufferToFill, adapter->buffer.get() + adapter->offset, sizeToRead);
164 
165  adapter->offset += sizeToRead;
166  return sizeToRead;
167  }
168 
169  static int Seek(void* context, ogg_int64_t offset, int whence)
170  {
171  VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
172 
173  off_t origin = 0;
174  switch(whence)
175  {
176  case SEEK_SET:
177  origin = 0;
178  break;
179  case SEEK_CUR:
180  origin = adapter->offset;
181  break;
182  case SEEK_END:
183  origin = adapter->size+1;
184  break;
185  NODEFAULT;
186  }
187 
188  adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
189  return 0;
190  }
191 
192  static int Close(void* context)
193  {
194  VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
195  adapter->buffer.reset();
196  return 0; // return value is ignored
197  }
198 
199  static long Tell(void* context)
200  {
201  VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
202  return adapter->offset;
203  }
204 
205 private:
206  shared_ptr<u8> buffer;
209 };
210 
211 
212 //-----------------------------------------------------------------------------
213 
214 template <typename Adapter>
215 class OggStreamImpl : public OggStream
216 {
217 public:
218  OggStreamImpl(const Adapter& adapter)
219  : adapter(adapter)
220  {
221  m_fileEOF = false;
222  info = NULL;
223  }
224 
226  {
227  ov_clear( &vf );
228 
229  return 0;
230  }
231 
233  {
234  ov_callbacks callbacks;
235  callbacks.read_func = Adapter::Read;
236  callbacks.close_func = Adapter::Close;
237  callbacks.seek_func = Adapter::Seek;
238  callbacks.tell_func = Adapter::Tell;
239  const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks);
240  if(ret != 0)
242 
243  const int link = -1; // retrieve info for current bitstream
244  info = ov_info(&vf, link);
245  if(!info)
247 
248  return INFO::OK;
249  }
250 
251  virtual ALenum Format()
252  {
253  return (info->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
254  }
255 
256  virtual ALsizei SamplingRate()
257  {
258  return info->rate;
259  }
260  virtual bool atFileEOF()
261  {
262  return m_fileEOF;
263  }
264 
265  virtual Status ResetFile()
266  {
267  ov_time_seek( &vf, 0 );
268  m_fileEOF = false;
269  return INFO::OK;
270  }
271 
272  virtual Status GetNextChunk(u8* buffer, size_t size)
273  {
274  // we may have to call ov_read multiple times because it
275  // treats the buffer size "as a limit and not a request"
276  size_t bytesRead = 0;
277  for(;;)
278  {
279  const int isBigEndian = (BYTE_ORDER == BIG_ENDIAN);
280  const int wordSize = sizeof(i16);
281  const int isSigned = 1;
282  int bitstream; // unused
283  const int ret = ov_read(&vf, (char*)buffer+bytesRead, int(size-bytesRead), isBigEndian, wordSize, isSigned, &bitstream);
284  if(ret == 0) { // EOF
285  m_fileEOF = true;
286  return (Status)bytesRead;
287  }
288  else if(ret < 0)
290  else // success
291  {
292  bytesRead += ret;
293  if(bytesRead == size)
294  return (Status)bytesRead;
295  }
296  }
297  }
298 
299 private:
300  Adapter adapter;
301  OggVorbis_File vf;
302  vorbis_info* info;
303  bool m_fileEOF;
304 };
305 
306 
307 //-----------------------------------------------------------------------------
308 
309 Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream)
310 {
311  PFile file(new File);
312  RETURN_STATUS_IF_ERR(file->Open(pathname, L'r'));
313 
314  shared_ptr<OggStreamImpl<VorbisFileAdapter> > tmp(new OggStreamImpl<VorbisFileAdapter>(VorbisFileAdapter(file)));
315  RETURN_STATUS_IF_ERR(tmp->Open());
316  stream = tmp;
317  return INFO::OK;
318 }
319 
320 Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream)
321 {
322  shared_ptr<u8> contents;
323  size_t size;
324  RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, contents, size));
325 
326  shared_ptr<OggStreamImpl<VorbisBufferAdapter> > tmp(new OggStreamImpl<VorbisBufferAdapter>(VorbisBufferAdapter(contents, size)));
327  RETURN_STATUS_IF_ERR(tmp->Open());
328  stream = tmp;
329  return INFO::OK;
330 }
331 
332 #endif // CONFIG2_AUDIO
333 
virtual Status GetNextChunk(u8 *buffer, size_t size)
Definition: ogg.cpp:272
VorbisBufferAdapter(const shared_ptr< u8 > &buffer, size_t size)
Definition: ogg.cpp:148
#define u8
Definition: types.h:39
static int Seek(void *context, ogg_int64_t offset, int whence)
Definition: ogg.cpp:101
const Status LOGIC
Definition: status.h:409
static long Tell(void *context)
Definition: ogg.cpp:131
const Status _4
Definition: status.h:444
const Status _1
Definition: status.h:441
static Status LibErrorFromVorbis(int err)
Definition: ogg.cpp:35
#define BIG_ENDIAN
Definition: byte_order.h:35
virtual bool atFileEOF()
Definition: ogg.cpp:260
const Status OK
Definition: status.h:386
Status Close()
Definition: ogg.cpp:225
const Status IO
Definition: io.h:43
shared_ptr< IVFS > PIVFS
Definition: vfs.h:226
vorbis_info * info
Definition: ogg.cpp:302
off_t offset
Definition: ogg.cpp:140
const Status _3
Definition: status.h:443
const Status CORRUPTED
Definition: status.h:413
T Clamp(T val, T min, T max)
low-level aka &quot;lib&quot;
Definition: lib.h:68
#define BYTE_ORDER
Definition: byte_order.h:39
const Status INVALID_HANDLE
Definition: status.h:419
#define i16
Definition: types.h:35
VorbisFileAdapter(const PFile &openedFile)
Definition: ogg.cpp:76
static size_t Read(void *bufferToFill, size_t itemSize, size_t numItems, void *context)
Definition: ogg.cpp:83
const Status AGAIN
Definition: status.h:427
const Status NOT_SUPPORTED
Definition: status.h:429
OggVorbis_File vf
Definition: ogg.cpp:301
const Status INVALID_VERSION
Definition: status.h:424
shared_ptr< File > PFile
Definition: file.h:99
__int64 off_t
Definition: wposix_types.h:91
Definition: path.h:75
virtual Status ResetFile()
Definition: ogg.cpp:265
shared_ptr< OggStream > OggStreamPtr
Definition: ogg.h:43
bool m_fileEOF
Definition: ogg.cpp:303
u64 FileSize(const OsPath &pathname)
Definition: file_system.cpp:57
const Status INVALID_PARAM
Definition: status.h:423
Adapter adapter
Definition: ogg.cpp:300
Status Open()
Definition: ogg.cpp:232
i64 Status
Error handling system.
Definition: status.h:171
shared_ptr< u8 > buffer
Definition: ogg.cpp:206
Definition: ogg.h:27
OggStreamImpl(const Adapter &adapter)
Definition: ogg.cpp:218
Status OpenOggStream(const OsPath &pathname, OggStreamPtr &stream)
Definition: ogg.cpp:309
virtual ALsizei SamplingRate()
Definition: ogg.cpp:256
u64 Read(u64 reg)
Definition: msr.cpp:130
static int Close(void *context)
Definition: ogg.cpp:124
Status OpenOggNonstream(const PIVFS &vfs, const VfsPath &pathname, OggStreamPtr &stream)
A non-streaming OggStream (reading the whole file in advance) that can cope with archived/compressed ...
Definition: ogg.cpp:320
#define WARN_RETURN(status)
Definition: status.h:255
const Status FAIL
Definition: status.h:406
#define NODEFAULT
convenient specialization of UNREACHABLE for switch statements whose default can never be reached...
const Status _2
Definition: status.h:442
virtual ALenum Format()
Definition: ogg.cpp:251
static int Seek(void *context, ogg_int64_t offset, int whence)
Definition: ogg.cpp:169
Definition: file.h:45
static int Close(void *context)
Definition: ogg.cpp:192
static long Tell(void *context)
Definition: ogg.cpp:199
static size_t Read(void *bufferToFill, size_t itemSize, size_t numItems, void *context)
Definition: ogg.cpp:155
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276
static Status Run(const Operation &op, const Parameters &p=Parameters(), const CompletedHook &completedHook=CompletedHook(), const IssueHook &issueHook=IssueHook())
Definition: io.h:233