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