1 /*
2 SDL_mixer: An audio mixer library based on the SDL library
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #ifdef MUSIC_OGG
23
24 /* This file supports Ogg Vorbis music streams */
25
26 #include "SDL_loadso.h"
27
28 #include "music_ogg.h"
29
30 #define OV_EXCLUDE_STATIC_CALLBACKS
31 #if defined(OGG_HEADER)
32 #include OGG_HEADER
33 #elif defined(OGG_USE_TREMOR)
34 #include <tremor/ivorbisfile.h>
35 #else
36 #include <vorbis/vorbisfile.h>
37 #endif
38
39 typedef struct {
40 int loaded;
41 void *handle;
42 int (*ov_clear)(OggVorbis_File *vf);
43 vorbis_info *(*ov_info)(OggVorbis_File *vf,int link);
44 vorbis_comment *(*ov_comment)(OggVorbis_File *vf,int link);
45 int (*ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks);
46 ogg_int64_t (*ov_pcm_total)(OggVorbis_File *vf,int i);
47 #ifdef OGG_USE_TREMOR
48 long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int *bitstream);
49 #else
50 long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream);
51 #endif
52 #ifdef OGG_USE_TREMOR
53 int (*ov_time_seek)(OggVorbis_File *vf,ogg_int64_t pos);
54 #else
55 int (*ov_time_seek)(OggVorbis_File *vf,double pos);
56 #endif
57 int (*ov_pcm_seek)(OggVorbis_File *vf, ogg_int64_t pos);
58 ogg_int64_t (*ov_pcm_tell)(OggVorbis_File *vf);
59 } vorbis_loader;
60
61 static vorbis_loader vorbis = {
62 0, NULL
63 };
64
65 #ifdef OGG_DYNAMIC
66 #define FUNCTION_LOADER(FUNC, SIG) \
67 vorbis.FUNC = (SIG) SDL_LoadFunction(vorbis.handle, #FUNC); \
68 if (vorbis.FUNC == NULL) { SDL_UnloadObject(vorbis.handle); return -1; }
69 #else
70 #define FUNCTION_LOADER(FUNC, SIG) \
71 vorbis.FUNC = FUNC;
72 #endif
73
OGG_Load(void)74 static int OGG_Load(void)
75 {
76 if (vorbis.loaded == 0) {
77 #ifdef OGG_DYNAMIC
78 vorbis.handle = SDL_LoadObject(OGG_DYNAMIC);
79 if (vorbis.handle == NULL) {
80 return -1;
81 }
82 #elif defined(__MACOSX__)
83 extern int ov_open_callbacks(void*, OggVorbis_File*, const char*, long, ov_callbacks) __attribute__((weak_import));
84 if (ov_open_callbacks == NULL)
85 {
86 /* Missing weakly linked framework */
87 Mix_SetError("Missing Vorbis.framework");
88 return -1;
89 }
90 #endif
91 FUNCTION_LOADER(ov_clear, int (*)(OggVorbis_File *))
92 FUNCTION_LOADER(ov_info, vorbis_info *(*)(OggVorbis_File *,int))
93 FUNCTION_LOADER(ov_comment, vorbis_comment *(*)(OggVorbis_File *,int))
94 FUNCTION_LOADER(ov_open_callbacks, int (*)(void *, OggVorbis_File *, const char *, long, ov_callbacks))
95 FUNCTION_LOADER(ov_pcm_total, ogg_int64_t (*)(OggVorbis_File *,int))
96 #ifdef OGG_USE_TREMOR
97 FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int *))
98 FUNCTION_LOADER(ov_time_seek, long (*)(OggVorbis_File *,ogg_int64_t))
99 #else
100 FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int,int,int,int *))
101 FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,double))
102 #endif
103 FUNCTION_LOADER(ov_pcm_seek, int (*)(OggVorbis_File *,ogg_int64_t))
104 FUNCTION_LOADER(ov_pcm_tell, ogg_int64_t (*)(OggVorbis_File *))
105 }
106 ++vorbis.loaded;
107
108 return 0;
109 }
110
OGG_Unload(void)111 static void OGG_Unload(void)
112 {
113 if (vorbis.loaded == 0) {
114 return;
115 }
116 if (vorbis.loaded == 1) {
117 #ifdef OGG_DYNAMIC
118 SDL_UnloadObject(vorbis.handle);
119 #endif
120 }
121 --vorbis.loaded;
122 }
123
124
125 typedef struct {
126 SDL_RWops *src;
127 int freesrc;
128 int play_count;
129 int volume;
130 OggVorbis_File vf;
131 vorbis_info vi;
132 int section;
133 SDL_AudioStream *stream;
134 char *buffer;
135 int buffer_size;
136 int loop;
137 ogg_int64_t loop_start;
138 ogg_int64_t loop_end;
139 ogg_int64_t loop_len;
140 ogg_int64_t channels;
141 } OGG_music;
142
143
set_ov_error(const char * function,int error)144 static int set_ov_error(const char *function, int error)
145 {
146 #define HANDLE_ERROR_CASE(X) case X: Mix_SetError("%s: %s", function, #X); break;
147 switch (error) {
148 HANDLE_ERROR_CASE(OV_FALSE);
149 HANDLE_ERROR_CASE(OV_EOF);
150 HANDLE_ERROR_CASE(OV_HOLE);
151 HANDLE_ERROR_CASE(OV_EREAD);
152 HANDLE_ERROR_CASE(OV_EFAULT);
153 HANDLE_ERROR_CASE(OV_EIMPL);
154 HANDLE_ERROR_CASE(OV_EINVAL);
155 HANDLE_ERROR_CASE(OV_ENOTVORBIS);
156 HANDLE_ERROR_CASE(OV_EBADHEADER);
157 HANDLE_ERROR_CASE(OV_EVERSION);
158 HANDLE_ERROR_CASE(OV_ENOTAUDIO);
159 HANDLE_ERROR_CASE(OV_EBADPACKET);
160 HANDLE_ERROR_CASE(OV_EBADLINK);
161 HANDLE_ERROR_CASE(OV_ENOSEEK);
162 default:
163 Mix_SetError("%s: unknown error %d\n", function, error);
164 break;
165 }
166 return -1;
167 }
168
sdl_read_func(void * ptr,size_t size,size_t nmemb,void * datasource)169 static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
170 {
171 return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
172 }
173
sdl_seek_func(void * datasource,ogg_int64_t offset,int whence)174 static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
175 {
176 return (int)SDL_RWseek((SDL_RWops*)datasource, offset, whence);
177 }
178
sdl_tell_func(void * datasource)179 static long sdl_tell_func(void *datasource)
180 {
181 return (long)SDL_RWtell((SDL_RWops*)datasource);
182 }
183
184 static int OGG_Seek(void *context, double time);
185 static void OGG_Delete(void *context);
186
OGG_UpdateSection(OGG_music * music)187 static int OGG_UpdateSection(OGG_music *music)
188 {
189 vorbis_info *vi;
190
191 vi = vorbis.ov_info(&music->vf, -1);
192 if (!vi) {
193 Mix_SetError("ov_info returned NULL");
194 return -1;
195 }
196
197 if (vi->channels == music->vi.channels && vi->rate == music->vi.rate) {
198 return 0;
199 }
200 SDL_memcpy(&music->vi, vi, sizeof(*vi));
201
202 if (music->buffer) {
203 SDL_free(music->buffer);
204 music->buffer = NULL;
205 }
206
207 if (music->stream) {
208 SDL_FreeAudioStream(music->stream);
209 music->stream = NULL;
210 }
211
212 music->stream = SDL_NewAudioStream(AUDIO_S16, vi->channels, (int)vi->rate,
213 music_spec.format, music_spec.channels, music_spec.freq);
214 if (!music->stream) {
215 return -1;
216 }
217
218 music->buffer_size = music_spec.samples * sizeof(Sint16) * vi->channels;
219 music->buffer = (char *)SDL_malloc(music->buffer_size);
220 if (!music->buffer) {
221 return -1;
222 }
223 return 0;
224 }
225
226 /* Load an OGG stream from an SDL_RWops object */
OGG_CreateFromRW(SDL_RWops * src,int freesrc)227 static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc)
228 {
229 OGG_music *music;
230 ov_callbacks callbacks;
231 vorbis_comment *vc;
232 int isLoopLength = 0, i;
233 ogg_int64_t fullLength;
234
235 music = (OGG_music *)SDL_calloc(1, sizeof *music);
236 if (!music) {
237 SDL_OutOfMemory();
238 return NULL;
239 }
240 music->src = src;
241 music->volume = MIX_MAX_VOLUME;
242 music->section = -1;
243 music->loop = -1;
244 music->loop_start = -1;
245 music->loop_end = 0;
246 music->loop_len = 0;
247
248 SDL_zero(callbacks);
249 callbacks.read_func = sdl_read_func;
250 callbacks.seek_func = sdl_seek_func;
251 callbacks.tell_func = sdl_tell_func;
252
253 if (vorbis.ov_open_callbacks(src, &music->vf, NULL, 0, callbacks) < 0) {
254 SDL_SetError("Not an Ogg Vorbis audio stream");
255 SDL_free(music);
256 return NULL;
257 }
258
259 if (OGG_UpdateSection(music) < 0) {
260 OGG_Delete(music);
261 return NULL;
262 }
263
264 vc = vorbis.ov_comment(&music->vf, -1);
265 for (i = 0; i < vc->comments; i++) {
266 char *param = SDL_strdup(vc->user_comments[i]);
267 char *argument = param;
268 char *value = SDL_strchr(param, '=');
269 if (value == NULL) {
270 value = param + SDL_strlen(param);
271 } else {
272 *(value++) = '\0';
273 }
274
275 if (SDL_strcasecmp(argument, "LOOPSTART") == 0)
276 music->loop_start = SDL_strtoull(value, NULL, 0);
277 else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) {
278 music->loop_len = SDL_strtoull(value, NULL, 0);
279 isLoopLength = 1;
280 } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) {
281 isLoopLength = 0;
282 music->loop_end = SDL_strtoull(value, NULL, 0);
283 }
284 SDL_free(param);
285 }
286
287 if (isLoopLength == 1) {
288 music->loop_end = music->loop_start + music->loop_len;
289 } else {
290 music->loop_len = music->loop_end - music->loop_start;
291 }
292
293 fullLength = vorbis.ov_pcm_total(&music->vf, -1);
294 if (((music->loop_start >= 0) || (music->loop_end > 0)) &&
295 ((music->loop_start < music->loop_end) || (music->loop_end == 0)) &&
296 (music->loop_start < fullLength) &&
297 (music->loop_end <= fullLength)) {
298 if (music->loop_start < 0) music->loop_start = 0;
299 if (music->loop_end == 0) music->loop_end = fullLength;
300 music->loop = 1;
301 }
302
303 music->freesrc = freesrc;
304 return music;
305 }
306
307 /* Set the volume for an OGG stream */
OGG_SetVolume(void * context,int volume)308 static void OGG_SetVolume(void *context, int volume)
309 {
310 OGG_music *music = (OGG_music *)context;
311 music->volume = volume;
312 }
313
314 /* Start playback of a given OGG stream */
OGG_Play(void * context,int play_count)315 static int OGG_Play(void *context, int play_count)
316 {
317 OGG_music *music = (OGG_music *)context;
318 music->play_count = play_count;
319 return OGG_Seek(music, 0.0);
320 }
321
322 /* Play some of a stream previously started with OGG_play() */
OGG_GetSome(void * context,void * data,int bytes,SDL_bool * done)323 static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done)
324 {
325 OGG_music *music = (OGG_music *)context;
326 SDL_bool looped = SDL_FALSE;
327 int filled, amount, result;
328 int section;
329 ogg_int64_t pcmPos;
330
331 filled = SDL_AudioStreamGet(music->stream, data, bytes);
332 if (filled != 0) {
333 return filled;
334 }
335
336 if (!music->play_count) {
337 /* All done */
338 *done = SDL_TRUE;
339 return 0;
340 }
341
342 section = music->section;
343 #ifdef OGG_USE_TREMOR
344 amount = vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, §ion);
345 #else
346 amount = (int)vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, 0, 2, 1, §ion);
347 #endif
348 if (amount < 0) {
349 set_ov_error("ov_read", amount);
350 return -1;
351 }
352
353 if (section != music->section) {
354 music->section = section;
355 if (OGG_UpdateSection(music) < 0) {
356 return -1;
357 }
358 }
359
360 pcmPos = vorbis.ov_pcm_tell(&music->vf);
361 if ((music->loop == 1) && (pcmPos >= music->loop_end)) {
362 amount -= (int)((pcmPos - music->loop_end) * music->channels) * sizeof(Sint16);
363 result = vorbis.ov_pcm_seek(&music->vf, music->loop_start);
364 if (result < 0) {
365 set_ov_error("ov_pcm_seek", result);
366 return -1;
367 }
368 looped = SDL_TRUE;
369 }
370
371 if (amount > 0) {
372 if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) {
373 return -1;
374 }
375 } else if (!looped) {
376 if (music->play_count == 1) {
377 music->play_count = 0;
378 SDL_AudioStreamFlush(music->stream);
379 } else {
380 int play_count = -1;
381 if (music->play_count > 0) {
382 play_count = (music->play_count - 1);
383 }
384 if (OGG_Play(music, play_count) < 0) {
385 return -1;
386 }
387 }
388 }
389 return 0;
390 }
OGG_GetAudio(void * context,void * data,int bytes)391 static int OGG_GetAudio(void *context, void *data, int bytes)
392 {
393 OGG_music *music = (OGG_music *)context;
394 return music_pcm_getaudio(context, data, bytes, music->volume, OGG_GetSome);
395 }
396
397 /* Jump (seek) to a given position (time is in seconds) */
OGG_Seek(void * context,double time)398 static int OGG_Seek(void *context, double time)
399 {
400 OGG_music *music = (OGG_music *)context;
401 int result;
402 #ifdef OGG_USE_TREMOR
403 result = vorbis.ov_time_seek(&music->vf, (ogg_int64_t)(time * 1000.0));
404 #else
405 result = vorbis.ov_time_seek(&music->vf, time);
406 #endif
407 if (result < 0) {
408 return set_ov_error("ov_time_seek", result);
409 }
410 return 0;
411 }
412
413 /* Close the given OGG stream */
OGG_Delete(void * context)414 static void OGG_Delete(void *context)
415 {
416 OGG_music *music = (OGG_music *)context;
417 vorbis.ov_clear(&music->vf);
418 if (music->stream) {
419 SDL_FreeAudioStream(music->stream);
420 }
421 if (music->buffer) {
422 SDL_free(music->buffer);
423 }
424 if (music->freesrc) {
425 SDL_RWclose(music->src);
426 }
427 SDL_free(music);
428 }
429
430 Mix_MusicInterface Mix_MusicInterface_OGG =
431 {
432 "OGG",
433 MIX_MUSIC_OGG,
434 MUS_OGG,
435 SDL_FALSE,
436 SDL_FALSE,
437
438 OGG_Load,
439 NULL, /* Open */
440 OGG_CreateFromRW,
441 NULL, /* CreateFromFile */
442 OGG_SetVolume,
443 OGG_Play,
444 NULL, /* IsPlaying */
445 OGG_GetAudio,
446 OGG_Seek,
447 NULL, /* Pause */
448 NULL, /* Resume */
449 NULL, /* Stop */
450 OGG_Delete,
451 NULL, /* Close */
452 OGG_Unload,
453 };
454
455 #endif /* MUSIC_OGG */
456
457 /* vi: set ts=4 sw=4 expandtab: */
458