1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 2005-2020 Warzone 2100 Project
4
5 Warzone 2100 is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 Warzone 2100 is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Warzone 2100; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "lib/framework/frame.h"
21 #include <physfs.h>
22 #include <limits>
23 #include "lib/framework/physfs_ext.h"
24
25 #include <vorbis/vorbisfile.h>
26 #include <vorbis/codec.h>
27
28 #ifdef __BIG_ENDIAN__
29 #define OGG_ENDIAN 1
30 #else
31 #define OGG_ENDIAN 0
32 #endif
33
34 #include "oggvorbis.h"
35
36 struct OggVorbisDecoderState
37 {
38 // Internal identifier towards PhysicsFS
39 PHYSFS_file *fileHandle;
40
41 // Wether to allow seeking or not
42 bool allowSeeking;
43
44 // Internal identifier towards libVorbisFile
45 OggVorbis_File oggVorbis_stream;
46
47 // Internal meta data
48 vorbis_info *VorbisInfo;
49
50 // Total time
51 double total_stream_time = 0.0;
52 };
53
wz_oggVorbis_getErrorStr(int error)54 static const char *wz_oggVorbis_getErrorStr(int error)
55 {
56 switch (error)
57 {
58 case OV_FALSE:
59 return "OV_FALSE";
60 case OV_HOLE:
61 return "OV_HOLE";
62 case OV_EREAD:
63 return "OV_EREAD";
64 case OV_EFAULT:
65 return "OV_EFAULT";
66 case OV_EIMPL:
67 return "OV_EIMPL";
68 case OV_EINVAL:
69 return "OV_EINVAL";
70 case OV_ENOTVORBIS:
71 return "OV_ENOTVORBIS";
72 case OV_EBADHEADER:
73 return "OV_EBADHEADER";
74 case OV_EVERSION:
75 return "OV_EVERSION";
76 case OV_EBADLINK:
77 return "OV_EBADLINK";
78 case OV_ENOSEEK:
79 return "OV_ENOSEEK";
80 default:
81 return "Unknown Ogg error.";
82 }
83 }
84
wz_oggVorbis_read(void * ptr,size_t size,size_t nmemb,void * datasource)85 static size_t wz_oggVorbis_read(void *ptr, size_t size, size_t nmemb, void *datasource)
86 {
87 PHYSFS_file *fileHandle;
88
89 ASSERT(datasource != nullptr, "NULL decoder passed!");
90
91 fileHandle = ((struct OggVorbisDecoderState *)datasource)->fileHandle;
92 ASSERT(fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
93
94 size_t readLen = size * nmemb;
95 ASSERT(readLen <= static_cast<size_t>(std::numeric_limits<PHYSFS_uint32>::max()), "readLen (%zu) exceeds PHYSFS_uint32::max", readLen);
96 return WZ_PHYSFS_readBytes(fileHandle, ptr, static_cast<PHYSFS_uint32>(readLen));
97 }
98
wz_oggVorbis_seek(void * datasource,ogg_int64_t offset,int whence)99 static int wz_oggVorbis_seek(void *datasource, ogg_int64_t offset, int whence)
100 {
101 PHYSFS_file *fileHandle;
102 bool allowSeeking;
103 int newPos;
104
105 ASSERT(datasource != nullptr, "NULL decoder passed!");
106
107 fileHandle = ((struct OggVorbisDecoderState *)datasource)->fileHandle;
108 ASSERT(fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
109
110 allowSeeking = ((struct OggVorbisDecoderState *)datasource)->allowSeeking;
111
112 if (!allowSeeking)
113 {
114 return -1;
115 }
116
117 switch (whence)
118 {
119 // Seek to absolute position
120 case SEEK_SET:
121 newPos = offset;
122 break;
123
124 // Seek `offset` ahead
125 case SEEK_CUR:
126 {
127 int curPos = PHYSFS_tell(fileHandle);
128 if (curPos == -1)
129 {
130 return -1;
131 }
132
133 newPos = curPos + offset;
134 break;
135 }
136
137 // Seek backwards from the end of the file
138 case SEEK_END:
139 {
140 int fileSize = PHYSFS_fileLength(fileHandle);
141 if (fileSize == -1)
142 {
143 return -1;
144 }
145
146 newPos = fileSize - 1 - offset;
147 break;
148 }
149
150 // unrecognized seek instruction
151 default:
152 // indicate failure
153 return -1;
154 }
155
156 // PHYSFS_seek return value of non-zero means success
157 if (PHYSFS_seek(fileHandle, newPos) != 0)
158 {
159 return newPos; // success
160 }
161 else
162 {
163 return -1; // failure
164 }
165 }
166
wz_oggVorbis_close(WZ_DECL_UNUSED void * datasource)167 static int wz_oggVorbis_close(WZ_DECL_UNUSED void *datasource)
168 {
169 return 0;
170 }
171
wz_oggVorbis_tell(void * datasource)172 static long wz_oggVorbis_tell(void *datasource)
173 {
174 PHYSFS_file *fileHandle;
175
176 ASSERT(datasource != nullptr, "NULL decoder passed!");
177
178 fileHandle = ((struct OggVorbisDecoderState *)datasource)->fileHandle;
179 ASSERT(fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
180
181 return PHYSFS_tell(fileHandle);
182 }
183
184 static const ov_callbacks wz_oggVorbis_callbacks =
185 {
186 wz_oggVorbis_read,
187 wz_oggVorbis_seek,
188 wz_oggVorbis_close,
189 wz_oggVorbis_tell
190 };
191
sound_CreateOggVorbisDecoder(PHYSFS_file * PHYSFS_fileHandle,bool allowSeeking)192 struct OggVorbisDecoderState *sound_CreateOggVorbisDecoder(PHYSFS_file *PHYSFS_fileHandle, bool allowSeeking)
193 {
194 int error;
195
196 struct OggVorbisDecoderState *decoder = (struct OggVorbisDecoderState *)malloc(sizeof(struct OggVorbisDecoderState));
197 if (decoder == nullptr)
198 {
199 debug(LOG_FATAL, "Out of memory");
200 abort();
201 return nullptr;
202 }
203
204 ASSERT(PHYSFS_fileHandle != nullptr, "Bad PhysicsFS file handle passed in");
205
206 decoder->fileHandle = PHYSFS_fileHandle;
207 decoder->allowSeeking = allowSeeking;
208
209 error = ov_open_callbacks(decoder, &decoder->oggVorbis_stream, nullptr, 0, wz_oggVorbis_callbacks);
210 if (error < 0)
211 {
212 debug(LOG_ERROR, "ov_open_callbacks failed with errorcode %s", wz_oggVorbis_getErrorStr(error));
213 free(decoder);
214 return nullptr;
215 }
216
217 // Aquire some info about the sound data
218 decoder->VorbisInfo = ov_info(&decoder->oggVorbis_stream, -1);
219 if (allowSeeking)
220 {
221 decoder->total_stream_time = ov_time_total(&decoder->oggVorbis_stream, -1);
222 }
223
224 return decoder;
225 }
226
sound_GetOggVorbisTotalTime(struct OggVorbisDecoderState * decoder)227 double sound_GetOggVorbisTotalTime(struct OggVorbisDecoderState *decoder)
228 {
229 ASSERT(decoder != nullptr, "NULL decoder passed!");
230 return decoder->total_stream_time;
231 }
232
sound_DestroyOggVorbisDecoder(struct OggVorbisDecoderState * decoder)233 void sound_DestroyOggVorbisDecoder(struct OggVorbisDecoderState *decoder)
234 {
235 ASSERT(decoder != nullptr, "NULL decoder passed!");
236
237 // Close the OggVorbis decoding stream
238 ov_clear(&decoder->oggVorbis_stream);
239
240 free(decoder);
241 }
242
getSampleCount(struct OggVorbisDecoderState * decoder)243 static inline unsigned int getSampleCount(struct OggVorbisDecoderState *decoder)
244 {
245 int numSamples;
246
247 ASSERT(decoder != nullptr, "NULL decoder passed!");
248
249 numSamples = ov_pcm_total(&decoder->oggVorbis_stream, -1);
250
251 if (numSamples == OV_EINVAL)
252 {
253 return 0;
254 }
255
256 return numSamples;
257 }
258
getCurrentSample(struct OggVorbisDecoderState * decoder)259 static inline unsigned int getCurrentSample(struct OggVorbisDecoderState *decoder)
260 {
261 int samplePos;
262
263 ASSERT(decoder != nullptr, "NULL decoder passed!");
264
265 samplePos = ov_pcm_tell(&decoder->oggVorbis_stream);
266
267 if (samplePos == OV_EINVAL)
268 {
269 return 0;
270 }
271
272 return samplePos;
273 }
274
sound_DecodeOggVorbis(struct OggVorbisDecoderState * decoder,size_t bufferSize)275 soundDataBuffer *sound_DecodeOggVorbis(struct OggVorbisDecoderState *decoder, size_t bufferSize)
276 {
277 size_t size = 0;
278 int result;
279
280 soundDataBuffer *buffer;
281
282 ASSERT(decoder != nullptr, "NULL decoder passed!");
283
284 if (decoder->allowSeeking)
285 {
286 unsigned int sampleCount = getSampleCount(decoder);
287
288 unsigned int sizeEstimate = sampleCount * decoder->VorbisInfo->channels * 2;
289
290 if (((bufferSize == 0) || (bufferSize > sizeEstimate)) && (sizeEstimate != 0))
291 {
292 bufferSize = static_cast<size_t>(sampleCount - getCurrentSample(decoder)) * static_cast<size_t>(decoder->VorbisInfo->channels) * 2;
293 }
294 }
295
296 // If we can't seek nor receive any suggested size for our buffer, just quit
297 if (bufferSize == 0)
298 {
299 debug(LOG_ERROR, "can't find a proper buffer size");
300 return nullptr;
301 }
302
303 buffer = (soundDataBuffer *)malloc(bufferSize + sizeof(soundDataBuffer));
304 if (buffer == nullptr)
305 {
306 debug(LOG_ERROR, "couldn't allocate memory (%lu bytes requested)", (unsigned long) bufferSize + sizeof(soundDataBuffer));
307 return nullptr;
308 }
309
310 buffer->data = (char *)(buffer + 1);
311 buffer->bufferSize = bufferSize;
312 buffer->bitsPerSample = 16;
313
314 buffer->channelCount = decoder->VorbisInfo->channels;
315 buffer->frequency = decoder->VorbisInfo->rate;
316
317 // Decode PCM data into the buffer until there is nothing to decode left
318 do
319 {
320 // Decode
321 int section;
322 size_t readLen = bufferSize - size;
323 ASSERT(readLen <= static_cast<size_t>(std::numeric_limits<int>::max()), "readLen (%zu) exceeds int::max", readLen);
324 result = ov_read(&decoder->oggVorbis_stream, &buffer->data[size], static_cast<int>(readLen), OGG_ENDIAN, 2, 1, §ion);
325
326 if (result < 0)
327 {
328 debug(LOG_ERROR, "error decoding from OggVorbis file; errorcode from ov_read: %s", wz_oggVorbis_getErrorStr(result));
329 free(buffer);
330 return nullptr;
331 }
332 else
333 {
334 size += result;
335 }
336
337 }
338 while ((result != 0 && size < bufferSize));
339
340 buffer->size = size;
341
342 return buffer;
343 }
344