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_MOD_MIKMOD
23
24 /* This file supports MOD tracker music streams */
25
26 #include "SDL_loadso.h"
27
28 #include "music_mikmod.h"
29
30 #include "mikmod.h"
31
32
33 /* libmikmod >= 3.3.2 constified several funcs */
34 #if (LIBMIKMOD_VERSION < 0x030302)
35 #define MIKMOD3_CONST
36 #else
37 #define MIKMOD3_CONST const
38 #endif
39
40 typedef struct {
41 int loaded;
42 void *handle;
43
44 void (*MikMod_Exit)(void);
45 CHAR* (*MikMod_InfoDriver)(void);
46 CHAR* (*MikMod_InfoLoader)(void);
47 int (*MikMod_Init)(MIKMOD3_CONST CHAR*);
48 void (*MikMod_RegisterAllLoaders)(void);
49 void (*MikMod_RegisterDriver)(struct MDRIVER*);
50 int* MikMod_errno;
51 MIKMOD3_CONST char* (*MikMod_strerror)(int);
52 void (*MikMod_free)(void*);
53 BOOL (*Player_Active)(void);
54 void (*Player_Free)(MODULE*);
55 MODULE* (*Player_LoadGeneric)(MREADER*,int,BOOL);
56 void (*Player_SetPosition)(UWORD);
57 void (*Player_SetVolume)(SWORD);
58 void (*Player_Start)(MODULE*);
59 void (*Player_Stop)(void);
60 ULONG (*VC_WriteBytes)(SBYTE*,ULONG);
61 struct MDRIVER* drv_nos;
62 UWORD* md_device;
63 UWORD* md_mixfreq;
64 UWORD* md_mode;
65 UBYTE* md_musicvolume;
66 UBYTE* md_pansep;
67 UBYTE* md_reverb;
68 UBYTE* md_sndfxvolume;
69 UBYTE* md_volume;
70 } mikmod_loader;
71
72 static mikmod_loader mikmod = {
73 0, NULL
74 };
75
76 #ifdef MIKMOD_DYNAMIC
77 #define FUNCTION_LOADER(FUNC, SIG) \
78 mikmod.FUNC = (SIG) SDL_LoadFunction(mikmod.handle, #FUNC); \
79 if (mikmod.FUNC == NULL) { SDL_UnloadObject(mikmod.handle); return -1; }
80 #define VARIABLE_LOADER(NAME, SIG) \
81 mikmod.NAME = (SIG) SDL_LoadFunction(mikmod.handle, #NAME); \
82 if (mikmod.NAME == NULL) { SDL_UnloadObject(mikmod.handle); return -1; }
83 #else
84 #define FUNCTION_LOADER(FUNC, SIG) \
85 mikmod.FUNC = FUNC;
86 #define VARIABLE_LOADER(NAME, SIG) \
87 mikmod.NAME = &NAME;
88 #endif
89
MIKMOD_Load()90 static int MIKMOD_Load()
91 {
92 if (mikmod.loaded == 0) {
93 #ifdef MIKMOD_DYNAMIC
94 mikmod.handle = SDL_LoadObject(MIKMOD_DYNAMIC);
95 if (mikmod.handle == NULL) {
96 return -1;
97 }
98 #elif defined(__MACOSX__)
99 extern void Player_Start(MODULE*) __attribute__((weak_import));
100 if (Player_Start == NULL)
101 {
102 /* Missing weakly linked framework */
103 Mix_SetError("Missing mikmod.framework");
104 return -1;
105 }
106 #endif
107 FUNCTION_LOADER(MikMod_Exit, void (*)(void))
108 FUNCTION_LOADER(MikMod_InfoDriver, CHAR* (*)(void))
109 FUNCTION_LOADER(MikMod_InfoLoader, CHAR* (*)(void))
110 FUNCTION_LOADER(MikMod_Init, int (*)(MIKMOD3_CONST CHAR*))
111 FUNCTION_LOADER(MikMod_RegisterAllLoaders, void (*)(void))
112 FUNCTION_LOADER(MikMod_RegisterDriver, void (*)(struct MDRIVER*))
113 VARIABLE_LOADER(MikMod_errno, int*)
114 FUNCTION_LOADER(MikMod_strerror, MIKMOD3_CONST char* (*)(int))
115 #ifdef MIKMOD_DYNAMIC
116 mikmod.MikMod_free = (void (*)(void*)) SDL_LoadFunction(mikmod.handle, "MikMod_free");
117 if (!mikmod.MikMod_free) {
118 /* libmikmod 3.1 and earlier doesn't have it */
119 mikmod.MikMod_free = free;
120 }
121 #else
122 #if (LIBMIKMOD_VERSION < 0x030200) || !defined(DMODE_NOISEREDUCTION)
123 /* libmikmod 3.2.0-beta2 or older */
124 mikmod.MikMod_free = free;
125 #else
126 mikmod.MikMod_free = MikMod_free;
127 #endif
128 #endif /* MIKMOD_DYNAMIC */
129 FUNCTION_LOADER(Player_Active, BOOL (*)(void))
130 FUNCTION_LOADER(Player_Free, void (*)(MODULE*))
131 FUNCTION_LOADER(Player_LoadGeneric, MODULE* (*)(MREADER*,int,BOOL))
132 FUNCTION_LOADER(Player_SetPosition, void (*)(UWORD))
133 FUNCTION_LOADER(Player_SetVolume, void (*)(SWORD))
134 FUNCTION_LOADER(Player_Start, void (*)(MODULE*))
135 FUNCTION_LOADER(Player_Stop, void (*)(void))
136 FUNCTION_LOADER(VC_WriteBytes, ULONG (*)(SBYTE*,ULONG))
137 VARIABLE_LOADER(drv_nos, MDRIVER*)
138 VARIABLE_LOADER(md_device, UWORD*)
139 VARIABLE_LOADER(md_mixfreq, UWORD*)
140 VARIABLE_LOADER(md_mode, UWORD*)
141 VARIABLE_LOADER(md_musicvolume, UBYTE*)
142 VARIABLE_LOADER(md_pansep, UBYTE*)
143 VARIABLE_LOADER(md_reverb, UBYTE*)
144 VARIABLE_LOADER(md_sndfxvolume, UBYTE*)
145 VARIABLE_LOADER(md_volume, UBYTE*)
146 }
147 ++mikmod.loaded;
148
149 return 0;
150 }
151
MIKMOD_Unload()152 static void MIKMOD_Unload()
153 {
154 if (mikmod.loaded == 0) {
155 return;
156 }
157 if (mikmod.loaded == 1) {
158 #ifdef MIKMOD_DYNAMIC
159 SDL_UnloadObject(mikmod.handle);
160 #endif
161 }
162 --mikmod.loaded;
163 }
164
165
166 typedef struct
167 {
168 int play_count;
169 int volume;
170 MODULE *module;
171 SDL_AudioStream *stream;
172 SBYTE *buffer;
173 ULONG buffer_size;
174 } MIKMOD_Music;
175
176
177 static int MIKMOD_Seek(void *context, double position);
178 static void MIKMOD_Delete(void *context);
179
180 /* Initialize the MOD player, with the given mixer settings
181 This function returns 0, or -1 if there was an error.
182 */
MIKMOD_Open(const SDL_AudioSpec * spec)183 static int MIKMOD_Open(const SDL_AudioSpec *spec)
184 {
185 CHAR *list;
186
187 /* Set the MikMod music format */
188 if (spec->format == AUDIO_S8 || spec->format == AUDIO_U8) {
189 /* MIKMOD audio format is AUDIO_U8 */
190 *mikmod.md_mode = 0;
191 } else {
192 /* MIKMOD audio format is AUDIO_S16SYS */
193 *mikmod.md_mode = DMODE_16BITS;
194 }
195 if (spec->channels > 1) {
196 *mikmod.md_mode |= DMODE_STEREO;
197 }
198 *mikmod.md_mixfreq = spec->freq;
199 *mikmod.md_device = 0;
200 *mikmod.md_volume = 96;
201 *mikmod.md_musicvolume = 128;
202 *mikmod.md_sndfxvolume = 128;
203 *mikmod.md_pansep = 128;
204 *mikmod.md_reverb = 0;
205 *mikmod.md_mode |= DMODE_HQMIXER|DMODE_SOFT_MUSIC|DMODE_SURROUND;
206
207 list = mikmod.MikMod_InfoDriver();
208 if (list) {
209 mikmod.MikMod_free(list);
210 } else {
211 mikmod.MikMod_RegisterDriver(mikmod.drv_nos);
212 }
213
214 list = mikmod.MikMod_InfoLoader();
215 if (list) {
216 mikmod.MikMod_free(list);
217 } else {
218 mikmod.MikMod_RegisterAllLoaders();
219 }
220
221 if (mikmod.MikMod_Init(NULL)) {
222 Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
223 return -1;
224 }
225 return 0;
226 }
227
228 /* Uninitialize the music players */
MIKMOD_Close(void)229 static void MIKMOD_Close(void)
230 {
231 if (mikmod.MikMod_Exit) {
232 mikmod.MikMod_Exit();
233 }
234 }
235
236 typedef struct
237 {
238 MREADER mr;
239 /* struct MREADER in libmikmod <= 3.2.0-beta2
240 * doesn't have iobase members. adding them here
241 * so that if we compile against 3.2.0-beta2, we
242 * can still run OK against 3.2.0b3 and newer. */
243 long iobase, prev_iobase;
244 Sint64 offset;
245 Sint64 eof;
246 SDL_RWops *src;
247 } LMM_MREADER;
248
LMM_Seek(struct MREADER * mr,long to,int dir)249 int LMM_Seek(struct MREADER *mr,long to,int dir)
250 {
251 Sint64 offset = to;
252 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
253 if (dir == SEEK_SET) {
254 offset += lmmmr->offset;
255 if (offset < lmmmr->offset)
256 return -1;
257 }
258 return (SDL_RWseek(lmmmr->src, offset, dir) < lmmmr->offset)? -1 : 0;
259 }
LMM_Tell(struct MREADER * mr)260 long LMM_Tell(struct MREADER *mr)
261 {
262 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
263 return (long)(SDL_RWtell(lmmmr->src) - lmmmr->offset);
264 }
LMM_Read(struct MREADER * mr,void * buf,size_t sz)265 BOOL LMM_Read(struct MREADER *mr,void *buf,size_t sz)
266 {
267 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
268 return SDL_RWread(lmmmr->src, buf, sz, 1);
269 }
LMM_Get(struct MREADER * mr)270 int LMM_Get(struct MREADER *mr)
271 {
272 unsigned char c;
273 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
274 if (SDL_RWread(lmmmr->src, &c, 1, 1)) {
275 return c;
276 }
277 return EOF;
278 }
LMM_Eof(struct MREADER * mr)279 BOOL LMM_Eof(struct MREADER *mr)
280 {
281 Sint64 offset;
282 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
283 offset = LMM_Tell(mr);
284 return offset >= lmmmr->eof;
285 }
MikMod_LoadSongRW(SDL_RWops * src,int maxchan)286 MODULE *MikMod_LoadSongRW(SDL_RWops *src, int maxchan)
287 {
288 LMM_MREADER lmmmr = {
289 { LMM_Seek, LMM_Tell, LMM_Read, LMM_Get, LMM_Eof },
290 0,
291 0,
292 0
293 };
294 lmmmr.offset = SDL_RWtell(src);
295 SDL_RWseek(src, 0, RW_SEEK_END);
296 lmmmr.eof = SDL_RWtell(src);
297 SDL_RWseek(src, lmmmr.offset, RW_SEEK_SET);
298 lmmmr.src = src;
299 return mikmod.Player_LoadGeneric((MREADER*)&lmmmr, maxchan, 0);
300 }
301
302 /* Load a MOD stream from an SDL_RWops object */
MIKMOD_CreateFromRW(SDL_RWops * src,int freesrc)303 void *MIKMOD_CreateFromRW(SDL_RWops *src, int freesrc)
304 {
305 MIKMOD_Music *music;
306 SDL_AudioFormat format;
307 Uint8 channels;
308
309 music = (MIKMOD_Music *)SDL_calloc(1, sizeof(*music));
310 if (!music) {
311 SDL_OutOfMemory();
312 return NULL;
313 }
314 music->volume = MIX_MAX_VOLUME;
315
316 music->module = MikMod_LoadSongRW(src, 64);
317 if (!music->module) {
318 Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
319 MIKMOD_Delete(music);
320 return NULL;
321 }
322
323 /* Allow implicit looping, disable fade out and other flags. */
324 music->module->extspd = 1;
325 music->module->panflag = 1;
326 music->module->wrap = 0;
327 music->module->loop = 1;
328 music->module->fadeout = 0;
329
330 if ((*mikmod.md_mode & DMODE_16BITS) == DMODE_16BITS) {
331 format = AUDIO_S16SYS;
332 } else {
333 format = AUDIO_U8;
334 }
335 if ((*mikmod.md_mode & DMODE_STEREO) == DMODE_STEREO) {
336 channels = 2;
337 } else {
338 channels = 1;
339 }
340 music->stream = SDL_NewAudioStream(format, channels, *mikmod.md_mixfreq,
341 music_spec.format, music_spec.channels, music_spec.freq);
342 if (!music->stream) {
343 MIKMOD_Delete(music);
344 return NULL;
345 }
346
347 music->buffer_size = music_spec.samples * (SDL_AUDIO_BITSIZE(format) / 8) * channels;
348 music->buffer = (SBYTE *)SDL_malloc(music->buffer_size);
349 if (!music->buffer) {
350 SDL_OutOfMemory();
351 MIKMOD_Delete(music);
352 return NULL;
353 }
354
355 if (freesrc) {
356 SDL_RWclose(src);
357 }
358 return music;
359 }
360
361 /* Set the volume for a MOD stream */
MIKMOD_SetVolume(void * context,int volume)362 static void MIKMOD_SetVolume(void *context, int volume)
363 {
364 MIKMOD_Music *music = (MIKMOD_Music *)context;
365 music->volume = volume;
366 mikmod.Player_SetVolume((SWORD)volume);
367 }
368
369 /* Start playback of a given MOD stream */
MIKMOD_Play(void * context,int play_count)370 static int MIKMOD_Play(void *context, int play_count)
371 {
372 MIKMOD_Music *music = (MIKMOD_Music *)context;
373 music->play_count = play_count;
374 music->module->initvolume = music->volume;
375 mikmod.Player_Start(music->module);
376 return MIKMOD_Seek(music, 0.0);
377 }
378
379 /* Return non-zero if a stream is currently playing */
MIKMOD_IsPlaying(void * context)380 static SDL_bool MIKMOD_IsPlaying(void *context)
381 {
382 return mikmod.Player_Active() ? SDL_TRUE : SDL_FALSE;
383 }
384
385 /* Play some of a stream previously started with MOD_play() */
MIKMOD_GetSome(void * context,void * data,int bytes,SDL_bool * done)386 static int MIKMOD_GetSome(void *context, void *data, int bytes, SDL_bool *done)
387 {
388 MIKMOD_Music *music = (MIKMOD_Music *)context;
389 int filled;
390
391 filled = SDL_AudioStreamGet(music->stream, data, bytes);
392 if (filled != 0) {
393 return filled;
394 }
395
396 if (!music->play_count) {
397 /* All done */
398 *done = SDL_TRUE;
399 return 0;
400 }
401
402 /* This never fails, and always writes a full buffer */
403 mikmod.VC_WriteBytes(music->buffer, music->buffer_size);
404
405 if (SDL_AudioStreamPut(music->stream, music->buffer, music->buffer_size) < 0) {
406 return -1;
407 }
408
409 /* Check to see if we're done now */
410 if (!mikmod.Player_Active()) {
411 if (music->play_count == 1) {
412 music->play_count = 0;
413 SDL_AudioStreamFlush(music->stream);
414 } else {
415 int play_count = -1;
416 if (music->play_count > 0) {
417 play_count = (music->play_count - 1);
418 }
419 if (MIKMOD_Play(music, play_count) < 0) {
420 return -1;
421 }
422 }
423 }
424 return 0;
425 }
MIKMOD_GetAudio(void * context,void * data,int bytes)426 static int MIKMOD_GetAudio(void *context, void *data, int bytes)
427 {
428 return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, MIKMOD_GetSome);
429 }
430
431 /* Jump (seek) to a given position (time is in seconds) */
MIKMOD_Seek(void * context,double position)432 static int MIKMOD_Seek(void *context, double position)
433 {
434 mikmod.Player_SetPosition((UWORD)position);
435 return 0;
436 }
437
438 /* Stop playback of a stream previously started with MOD_play() */
MIKMOD_Stop(void * context)439 static void MIKMOD_Stop(void *context)
440 {
441 mikmod.Player_Stop();
442 }
443
444 /* Close the given MOD stream */
MIKMOD_Delete(void * context)445 static void MIKMOD_Delete(void *context)
446 {
447 MIKMOD_Music *music = (MIKMOD_Music *)context;
448
449 if (music->module) {
450 mikmod.Player_Free(music->module);
451 }
452 if (music->stream) {
453 SDL_FreeAudioStream(music->stream);
454 }
455 if (music->buffer) {
456 SDL_free(music->buffer);
457 }
458 SDL_free(music);
459 }
460
461 Mix_MusicInterface Mix_MusicInterface_MIKMOD =
462 {
463 "MIKMOD",
464 MIX_MUSIC_MIKMOD,
465 MUS_MOD,
466 SDL_FALSE,
467 SDL_FALSE,
468
469 MIKMOD_Load,
470 MIKMOD_Open,
471 MIKMOD_CreateFromRW,
472 NULL, /* CreateFromFile */
473 MIKMOD_SetVolume,
474 MIKMOD_Play,
475 MIKMOD_IsPlaying,
476 MIKMOD_GetAudio,
477 MIKMOD_Seek,
478 NULL, /* Pause */
479 NULL, /* Resume */
480 MIKMOD_Stop,
481 MIKMOD_Delete,
482 MIKMOD_Close,
483 MIKMOD_Unload,
484 };
485
486 #endif /* MUSIC_MOD_MIKMOD */
487
488 /* vi: set ts=4 sw=4 expandtab: */
489