1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21#include "VorbisDecoder.h"
22
23#include <string.h>
24#include "common/config.h"
25#include "common/Exception.h"
26
27namespace love
28{
29namespace sound
30{
31namespace lullaby
32{
33
34/**
35 * CALLBACK FUNCTIONS
36 **/
37static int vorbisClose(void * /* ptr to the data that the vorbis files need*/)
38{
39 // Does nothing (handled elsewhere)
40 return 1;
41}
42
43static size_t vorbisRead(void *ptr /* ptr to the data that the vorbis files need*/,
44 size_t byteSize /* how big a byte is*/,
45 size_t sizeToRead /* How much we can read*/,
46 void *datasource /* this is a pointer to the data we passed into ov_open_callbacks (our SOggFile struct*/)
47{
48 size_t spaceToEOF; // How much more we can read till we hit the EOF marker
49 size_t actualSizeToRead; // How much data we are actually going to read from memory
50 SOggFile *vorbisData; // Our vorbis data, for the typecast
51
52 // Get the data in the right format
53 vorbisData = (SOggFile *)datasource;
54
55 // Calculate how much we need to read. This can be sizeToRead*byteSize or less depending on how near the EOF marker we are
56 spaceToEOF = vorbisData->dataSize - vorbisData->dataRead;
57 if ((sizeToRead*byteSize) < spaceToEOF)
58 actualSizeToRead = (sizeToRead*byteSize);
59 else
60 actualSizeToRead = spaceToEOF;
61
62 // A simple copy of the data from memory to the datastruct that the vorbis libs will use
63 if (actualSizeToRead)
64 {
65 // Copy the data from the start of the file PLUS how much we have already read in
66 memcpy(ptr, (const char *)vorbisData->dataPtr + vorbisData->dataRead, actualSizeToRead);
67 // Increase by how much we have read by
68 vorbisData->dataRead += (actualSizeToRead);
69 }
70
71 // Return how much we read (in the same way fread would)
72 return actualSizeToRead;
73}
74
75static int vorbisSeek(void *datasource /* ptr to the data that the vorbis files need*/,
76 ogg_int64_t offset /*offset from the point we wish to seek to*/,
77 int whence /*where we want to seek to*/)
78{
79 int64 spaceToEOF; // How much more we can read till we hit the EOF marker
80 int64 actualOffset; // How much we can actually offset it by
81 SOggFile *vorbisData; // The data we passed in (for the typecast)
82
83 // Get the data in the right format
84 vorbisData = (SOggFile *) datasource;
85
86 // Goto where we wish to seek to
87 switch (whence)
88 {
89 case SEEK_SET: // Seek to the start of the data file
90 // Make sure we are not going to the end of the file
91 if (vorbisData->dataSize >= offset)
92 actualOffset = offset;
93 else
94 actualOffset = vorbisData->dataSize;
95 // Set where we now are
96 vorbisData->dataRead = (int)actualOffset;
97 break;
98 case SEEK_CUR: // Seek from where we are
99 // Make sure we dont go past the end
100 spaceToEOF = vorbisData->dataSize - vorbisData->dataRead;
101 if (offset < spaceToEOF)
102 actualOffset = (offset);
103 else
104 actualOffset = spaceToEOF;
105 // Seek from our currrent location
106 vorbisData->dataRead += actualOffset;
107 break;
108 case SEEK_END: // Seek from the end of the file
109 if (offset < 0)
110 vorbisData->dataRead = vorbisData->dataSize + offset;
111 else
112 vorbisData->dataRead = vorbisData->dataSize;
113 break;
114 default:
115 break;
116 };
117
118 return 0;
119}
120
121static long vorbisTell(void *datasource /* ptr to the data that the vorbis files need*/)
122{
123 SOggFile *vorbisData;
124 vorbisData = (SOggFile *) datasource;
125 return vorbisData->dataRead;
126}
127/**
128 * END CALLBACK FUNCTIONS
129 **/
130
131VorbisDecoder::VorbisDecoder(Data *data, int bufferSize)
132 : Decoder(data, bufferSize)
133 , duration(-2.0)
134{
135 // Initialize callbacks
136 vorbisCallbacks.close_func = vorbisClose;
137 vorbisCallbacks.seek_func = vorbisSeek;
138 vorbisCallbacks.read_func = vorbisRead;
139 vorbisCallbacks.tell_func = vorbisTell;
140
141 // Check endianness
142#ifdef LOVE_BIG_ENDIAN
143 endian = 1;
144#else
145 endian = 0;
146#endif
147
148 // Initialize OGG file
149 oggFile.dataPtr = (const char *) data->getData();
150 oggFile.dataSize = data->getSize();
151 oggFile.dataRead = 0;
152
153 // Open Vorbis handle
154 if (ov_open_callbacks(&oggFile, &handle, NULL, 0, vorbisCallbacks) < 0)
155 throw love::Exception("Could not read Ogg bitstream");
156
157 // Get info and comments
158 vorbisInfo = ov_info(&handle, -1);
159 vorbisComment = ov_comment(&handle, -1);
160}
161
162VorbisDecoder::~VorbisDecoder()
163{
164 ov_clear(&handle);
165}
166
167bool VorbisDecoder::accepts(const std::string &ext)
168{
169 static const std::string supported[] =
170 {
171 "ogg", "oga", "ogv", ""
172 };
173
174 for (int i = 0; !(supported[i].empty()); i++)
175 {
176 if (supported[i].compare(ext) == 0)
177 return true;
178 }
179
180 return false;
181}
182
183love::sound::Decoder *VorbisDecoder::clone()
184{
185 return new VorbisDecoder(data.get(), bufferSize);
186}
187
188int VorbisDecoder::decode()
189{
190 int size = 0;
191
192 while (size < bufferSize)
193 {
194 long result = ov_read(&handle, (char *) buffer + size, bufferSize - size, endian, (getBitDepth() == 16 ? 2 : 1), 1, 0);
195
196 if (result == OV_HOLE)
197 continue;
198 else if (result <= OV_EREAD)
199 return -1;
200 else if (result == 0)
201 {
202 eof = true;
203 break;
204 }
205 else if (result > 0)
206 size += result;
207 }
208
209 return size;
210}
211
212bool VorbisDecoder::seek(double s)
213{
214 int result = 0;
215
216 // Avoid ov_time_seek (which calls ov_pcm_seek) when seeking to 0, to avoid
217 // a bug in libvorbis <= 1.3.4 when seeking to PCM 0 in multiplexed streams.
218 if (s <= 0.000001)
219 result = ov_raw_seek(&handle, 0);
220 else
221 result = ov_time_seek(&handle, s);
222
223 if (result == 0)
224 {
225 eof = false;
226 return true;
227 }
228
229 return false;
230}
231
232bool VorbisDecoder::rewind()
233{
234 // Avoid ov_time_seek to avoid a bug in libvorbis <= 1.3.4 when seeking to
235 // PCM 0 in multiplexed streams.
236 int result = ov_raw_seek(&handle, 0);
237
238 if (result == 0)
239 {
240 eof = false;
241 return true;
242 }
243
244 return false;
245}
246
247bool VorbisDecoder::isSeekable()
248{
249 long result = ov_seekable(&handle);
250 return (result != 0);
251}
252
253int VorbisDecoder::getChannelCount() const
254{
255 return vorbisInfo->channels;
256}
257
258int VorbisDecoder::getBitDepth() const
259{
260 return 16;
261}
262
263int VorbisDecoder::getSampleRate() const
264{
265 return (int) vorbisInfo->rate;
266}
267
268double VorbisDecoder::getDuration()
269{
270 // Only calculate the duration if we haven't done so already.
271 if (duration == -2.0)
272 {
273 duration = ov_time_total(&handle, -1);
274
275 if (duration == OV_EINVAL || duration < 0.0)
276 duration = -1.0;
277 }
278
279 return duration;
280}
281
282} // lullaby
283} // sound
284} // love
285