music_mikmod.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 21 Oct 2017 14:40:31 -0700
changeset 823 bcd59adacdcc
parent 800 cc4aef7fef64
child 825 4359e193d93f
permissions -rw-r--r--
Fixed linker order so -lmingw32 comes before -lSDL2main
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2017 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 
    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 < ((3<<16)|(2<<8))
   123         mikmod.MikMod_free = free;
   124 #else
   125         mikmod.MikMod_free = MikMod_free;
   126 #endif
   127 #endif /* MIKMOD_DYNAMIC */
   128         FUNCTION_LOADER(Player_Active, BOOL (*)(void))
   129         FUNCTION_LOADER(Player_Free, void (*)(MODULE*))
   130         FUNCTION_LOADER(Player_LoadGeneric, MODULE* (*)(MREADER*,int,BOOL))
   131         FUNCTION_LOADER(Player_SetPosition, void (*)(UWORD))
   132         FUNCTION_LOADER(Player_SetVolume, void (*)(SWORD))
   133         FUNCTION_LOADER(Player_Start, void (*)(MODULE*))
   134         FUNCTION_LOADER(Player_Stop, void (*)(void))
   135         FUNCTION_LOADER(VC_WriteBytes, ULONG (*)(SBYTE*,ULONG))
   136         VARIABLE_LOADER(drv_nos, MDRIVER*)
   137         VARIABLE_LOADER(md_device, UWORD*)
   138         VARIABLE_LOADER(md_mixfreq, UWORD*)
   139         VARIABLE_LOADER(md_mode, UWORD*)
   140         VARIABLE_LOADER(md_musicvolume, UBYTE*)
   141         VARIABLE_LOADER(md_pansep, UBYTE*)
   142         VARIABLE_LOADER(md_reverb, UBYTE*)
   143         VARIABLE_LOADER(md_sndfxvolume, UBYTE*)
   144         VARIABLE_LOADER(md_volume, UBYTE*)
   145     }
   146     ++mikmod.loaded;
   147 
   148     return 0;
   149 }
   150 
   151 static void MIKMOD_Unload()
   152 {
   153     if (mikmod.loaded == 0) {
   154         return;
   155     }
   156     if (mikmod.loaded == 1) {
   157 #ifdef MIKMOD_DYNAMIC
   158         SDL_UnloadObject(mikmod.handle);
   159 #endif
   160     }
   161     --mikmod.loaded;
   162 }
   163 
   164 
   165 typedef struct
   166 {
   167     int play_count;
   168     int volume;
   169     MODULE *module;
   170     SDL_AudioStream *stream;
   171     SBYTE *buffer;
   172     ULONG buffer_size;
   173 } MIKMOD_Music;
   174 
   175 
   176 static int MIKMOD_Seek(void *context, double position);
   177 static void MIKMOD_Delete(void *context);
   178 
   179 /* Initialize the MOD player, with the given mixer settings
   180    This function returns 0, or -1 if there was an error.
   181  */
   182 static int MIKMOD_Open(const SDL_AudioSpec *spec)
   183 {
   184     CHAR *list;
   185 
   186     /* Set the MikMod music format */
   187     if (spec->format == AUDIO_S8 || spec->format == AUDIO_U8) {
   188         /* MIKMOD audio format is AUDIO_U8 */
   189         *mikmod.md_mode = 0;
   190     } else {
   191         /* MIKMOD audio format is AUDIO_S16SYS */
   192         *mikmod.md_mode = DMODE_16BITS;
   193     }
   194     if (spec->channels > 1) {
   195         *mikmod.md_mode |= DMODE_STEREO;
   196     }
   197     *mikmod.md_mixfreq = spec->freq;
   198     *mikmod.md_device  = 0;
   199     *mikmod.md_volume  = 96;
   200     *mikmod.md_musicvolume = 128;
   201     *mikmod.md_sndfxvolume = 128;
   202     *mikmod.md_pansep  = 128;
   203     *mikmod.md_reverb  = 0;
   204     *mikmod.md_mode    |= DMODE_HQMIXER|DMODE_SOFT_MUSIC|DMODE_SURROUND;
   205 
   206     list = mikmod.MikMod_InfoDriver();
   207     if (list) {
   208       mikmod.MikMod_free(list);
   209     } else {
   210       mikmod.MikMod_RegisterDriver(mikmod.drv_nos);
   211     }
   212 
   213     list = mikmod.MikMod_InfoLoader();
   214     if (list) {
   215       mikmod.MikMod_free(list);
   216     } else {
   217       mikmod.MikMod_RegisterAllLoaders();
   218     }
   219 
   220     if (mikmod.MikMod_Init(NULL)) {
   221         Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
   222         return -1;
   223     }
   224     return 0;
   225 }
   226 
   227 /* Uninitialize the music players */
   228 static void MIKMOD_Close(void)
   229 {
   230     if (mikmod.MikMod_Exit) {
   231         mikmod.MikMod_Exit();
   232     }
   233 }
   234 
   235 typedef struct
   236 {
   237     MREADER mr;
   238     /* struct MREADER in libmikmod <= 3.2.0-beta2
   239      * doesn't have iobase members. adding them here
   240      * so that if we compile against 3.2.0-beta2, we
   241      * can still run OK against 3.2.0b3 and newer. */
   242     long iobase, prev_iobase;
   243     Sint64 offset;
   244     Sint64 eof;
   245     SDL_RWops *src;
   246 } LMM_MREADER;
   247 
   248 int LMM_Seek(struct MREADER *mr,long to,int dir)
   249 {
   250     Sint64 offset = to;
   251     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   252     if (dir == SEEK_SET) {
   253         offset += lmmmr->offset;
   254         if (offset < lmmmr->offset)
   255             return -1;
   256     }
   257     return (int)(SDL_RWseek(lmmmr->src, offset, dir));
   258 }
   259 long LMM_Tell(struct MREADER *mr)
   260 {
   261     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   262     return (long)(SDL_RWtell(lmmmr->src) - lmmmr->offset);
   263 }
   264 BOOL LMM_Read(struct MREADER *mr,void *buf,size_t sz)
   265 {
   266     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   267     return SDL_RWread(lmmmr->src, buf, sz, 1);
   268 }
   269 int LMM_Get(struct MREADER *mr)
   270 {
   271     unsigned char c;
   272     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   273     if (SDL_RWread(lmmmr->src, &c, 1, 1)) {
   274         return c;
   275     }
   276     return EOF;
   277 }
   278 BOOL LMM_Eof(struct MREADER *mr)
   279 {
   280     Sint64 offset;
   281     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   282     offset = LMM_Tell(mr);
   283     return offset >= lmmmr->eof;
   284 }
   285 MODULE *MikMod_LoadSongRW(SDL_RWops *src, int maxchan)
   286 {
   287     LMM_MREADER lmmmr = {
   288         { LMM_Seek, LMM_Tell, LMM_Read, LMM_Get, LMM_Eof },
   289         0,
   290         0,
   291         0
   292     };
   293     lmmmr.offset = SDL_RWtell(src);
   294     SDL_RWseek(src, 0, RW_SEEK_END);
   295     lmmmr.eof = SDL_RWtell(src);
   296     SDL_RWseek(src, lmmmr.offset, RW_SEEK_SET);
   297     lmmmr.src = src;
   298     return mikmod.Player_LoadGeneric((MREADER*)&lmmmr, maxchan, 0);
   299 }
   300 
   301 /* Load a MOD stream from an SDL_RWops object */
   302 void *MIKMOD_CreateFromRW(SDL_RWops *src, int freesrc)
   303 {
   304     MIKMOD_Music *music;
   305     SDL_AudioFormat format;
   306     Uint8 channels;
   307 
   308     music = (MIKMOD_Music *)SDL_calloc(1, sizeof(*music));
   309     if (!music) {
   310         SDL_OutOfMemory();
   311         return NULL;
   312     }
   313     music->volume = MIX_MAX_VOLUME;
   314 
   315     music->module = MikMod_LoadSongRW(src, 64);
   316     if (!music->module) {
   317         Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
   318         MIKMOD_Delete(music);
   319         return NULL;
   320     }
   321 
   322     /* Stop implicit looping, fade out and other flags. */
   323     music->module->extspd  = 1;
   324     music->module->panflag = 1;
   325     music->module->wrap    = 0;
   326     music->module->loop    = 0;
   327 #if 0 /* Don't set fade out by default - unfortunately there's no real way
   328 to query the status of the song or set trigger actions.  Hum. */
   329     music->module->fadeout = 1;
   330 #endif
   331 
   332     if ((*mikmod.md_mode & DMODE_16BITS) == DMODE_16BITS) {
   333         format = AUDIO_S16SYS;
   334     } else {
   335         format = AUDIO_U8;
   336     }
   337     if ((*mikmod.md_mode & DMODE_STEREO) == DMODE_STEREO) {
   338         channels = 2;
   339     } else {
   340         channels = 1;
   341     }
   342     music->stream = SDL_NewAudioStream(format, channels, *mikmod.md_mixfreq,
   343                                        music_spec.format, music_spec.channels, music_spec.freq);
   344     if (!music->stream) {
   345         MIKMOD_Delete(music);
   346         return NULL;
   347     }
   348 
   349     music->buffer_size = music_spec.samples * (SDL_AUDIO_BITSIZE(format) / 8) * channels;
   350     music->buffer = (SBYTE *)SDL_malloc(music->buffer_size);
   351     if (!music->buffer) {
   352         SDL_OutOfMemory();
   353         MIKMOD_Delete(music);
   354         return NULL;
   355     }
   356         
   357     if (freesrc) {
   358         SDL_RWclose(src);
   359     }
   360     return music;
   361 }
   362 
   363 /* Set the volume for a MOD stream */
   364 static void MIKMOD_SetVolume(void *context, int volume)
   365 {
   366     MIKMOD_Music *music = (MIKMOD_Music *)context;
   367     music->volume = volume;
   368     mikmod.Player_SetVolume((SWORD)volume);
   369 }
   370 
   371 /* Start playback of a given MOD stream */
   372 static int MIKMOD_Play(void *context, int play_count)
   373 {
   374     MIKMOD_Music *music = (MIKMOD_Music *)context;
   375     music->play_count = play_count;
   376     mikmod.Player_Start(music->module);
   377     mikmod.Player_SetVolume((SWORD)music->volume);
   378     return MIKMOD_Seek(music, 0.0);
   379 }
   380 
   381 /* Return non-zero if a stream is currently playing */
   382 static SDL_bool MIKMOD_IsPlaying(void *context)
   383 {
   384     return mikmod.Player_Active() ? SDL_TRUE : SDL_FALSE;
   385 }
   386 
   387 /* Play some of a stream previously started with MOD_play() */
   388 static int MIKMOD_GetSome(void *context, void *data, int bytes, SDL_bool *done)
   389 {
   390     MIKMOD_Music *music = (MIKMOD_Music *)context;
   391     int filled;
   392 
   393     filled = SDL_AudioStreamGet(music->stream, data, bytes);
   394     if (filled != 0) {
   395         return filled;
   396     }
   397 
   398     if (!music->play_count) {
   399         /* All done */
   400         *done = SDL_TRUE;
   401         return 0;
   402     }
   403 
   404     /* This never fails, and always writes a full buffer */
   405     mikmod.VC_WriteBytes(music->buffer, music->buffer_size);
   406 
   407     if (SDL_AudioStreamPut(music->stream, music->buffer, music->buffer_size) < 0) {
   408         return -1;
   409     }
   410 
   411     /* Check to see if we're done now */
   412     if (!mikmod.Player_Active()) {
   413         if (music->play_count == 1) {
   414             music->play_count = 0;
   415             SDL_AudioStreamFlush(music->stream);
   416         } else {
   417             int play_count = -1;
   418             if (music->play_count > 0) {
   419                 play_count = (music->play_count - 1);
   420             }
   421             if (MIKMOD_Play(music, play_count) < 0) {
   422                 return -1;
   423             }
   424         }
   425     }
   426     return 0;
   427 }
   428 static int MIKMOD_GetAudio(void *context, void *data, int bytes)
   429 {
   430     return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, MIKMOD_GetSome);
   431 }
   432 
   433 /* Jump (seek) to a given position (time is in seconds) */
   434 static int MIKMOD_Seek(void *context, double position)
   435 {
   436     mikmod.Player_SetPosition((UWORD)position);
   437     return 0;
   438 }
   439 
   440 /* Stop playback of a stream previously started with MOD_play() */
   441 static void MIKMOD_Stop(void *context)
   442 {
   443     mikmod.Player_Stop();
   444 }
   445 
   446 /* Close the given MOD stream */
   447 static void MIKMOD_Delete(void *context)
   448 {
   449     MIKMOD_Music *music = (MIKMOD_Music *)context;
   450 
   451     if (music->module) {
   452         mikmod.Player_Free(music->module);
   453     }
   454     if (music->stream) {
   455         SDL_FreeAudioStream(music->stream);
   456     }
   457     if (music->buffer) {
   458         SDL_free(music->buffer);
   459     }
   460     SDL_free(music);
   461 }
   462 
   463 Mix_MusicInterface Mix_MusicInterface_MIKMOD =
   464 {
   465     "MIKMOD",
   466     MIX_MUSIC_MIKMOD,
   467     MUS_MOD,
   468     SDL_FALSE,
   469     SDL_FALSE,
   470 
   471     MIKMOD_Load,
   472     MIKMOD_Open,
   473     MIKMOD_CreateFromRW,
   474     NULL,   /* CreateFromFile */
   475     MIKMOD_SetVolume,
   476     MIKMOD_Play,
   477     MIKMOD_IsPlaying,
   478     MIKMOD_GetAudio,
   479     MIKMOD_Seek,
   480     NULL,   /* Pause */
   481     NULL,   /* Resume */
   482     MIKMOD_Stop,
   483     MIKMOD_Delete,
   484     MIKMOD_Close,
   485     MIKMOD_Unload,
   486 };
   487 
   488 #endif /* MUSIC_MOD_MIKMOD */
   489 
   490 /* vi: set ts=4 sw=4 expandtab: */