music_mikmod.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 07 Nov 2018 07:47:50 -0800
changeset 924 9be60a9582a6
parent 852 b86f9bd104c8
child 926 d6c9518fb5ee
permissions -rw-r--r--
The Debian maintainers aren't using these rules, so enable dynamic loading of shared libraries by default for the Steam Linux Runtime
     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 
    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 
   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  */
   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 */
   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 
   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 }
   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 }
   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 }
   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 }
   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 }
   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 */
   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 */
   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 */
   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 */
   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() */
   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 }
   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) */
   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() */
   439 static void MIKMOD_Stop(void *context)
   440 {
   441     mikmod.Player_Stop();
   442 }
   443 
   444 /* Close the given MOD stream */
   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: */