music_modplug.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 04 Jan 2019 22:02:19 -0800
changeset 926 d6c9518fb5ee
parent 848 3907db698eb5
permissions -rw-r--r--
Updated copyright for 2019
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2019 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_MODPLUG
    23 
    24 #include "SDL_loadso.h"
    25 
    26 #include "music_modplug.h"
    27 
    28 #ifdef MODPLUG_HEADER
    29 #include MODPLUG_HEADER
    30 #else
    31 #include <libmodplug/modplug.h>
    32 #endif
    33 
    34 typedef struct {
    35     int loaded;
    36     void *handle;
    37 
    38     ModPlugFile* (*ModPlug_Load)(const void* data, int size);
    39     void (*ModPlug_Unload)(ModPlugFile* file);
    40     int  (*ModPlug_Read)(ModPlugFile* file, void* buffer, int size);
    41     void (*ModPlug_Seek)(ModPlugFile* file, int millisecond);
    42     void (*ModPlug_GetSettings)(ModPlug_Settings* settings);
    43     void (*ModPlug_SetSettings)(const ModPlug_Settings* settings);
    44     void (*ModPlug_SetMasterVolume)(ModPlugFile* file,unsigned int cvol) ;
    45 } modplug_loader;
    46 
    47 static modplug_loader modplug = {
    48     0, NULL
    49 };
    50 
    51 
    52 static ModPlug_Settings settings;
    53 
    54 #ifdef MODPLUG_DYNAMIC
    55 #define FUNCTION_LOADER(FUNC, SIG) \
    56     modplug.FUNC = (SIG) SDL_LoadFunction(modplug.handle, #FUNC); \
    57     if (modplug.FUNC == NULL) { SDL_UnloadObject(modplug.handle); return -1; }
    58 #else
    59 #define FUNCTION_LOADER(FUNC, SIG) \
    60     modplug.FUNC = FUNC;
    61 #endif
    62 
    63 static int MODPLUG_Load(void)
    64 {
    65     if (modplug.loaded == 0) {
    66 #ifdef MODPLUG_DYNAMIC
    67         modplug.handle = SDL_LoadObject(MODPLUG_DYNAMIC);
    68         if (modplug.handle == NULL) {
    69             return -1;
    70         }
    71 #elif defined(__MACOSX__)
    72         extern ModPlugFile* ModPlug_Load(const void* data, int size) __attribute__((weak_import));
    73         if (ModPlug_Load == NULL)
    74         {
    75             /* Missing weakly linked framework */
    76             Mix_SetError("Missing modplug.framework");
    77             return -1;
    78         }
    79 #endif
    80         FUNCTION_LOADER(ModPlug_Load, ModPlugFile* (*)(const void* data, int size))
    81         FUNCTION_LOADER(ModPlug_Unload, void (*)(ModPlugFile* file))
    82         FUNCTION_LOADER(ModPlug_Read, int  (*)(ModPlugFile* file, void* buffer, int size))
    83         FUNCTION_LOADER(ModPlug_Seek, void (*)(ModPlugFile* file, int millisecond))
    84         FUNCTION_LOADER(ModPlug_GetSettings, void (*)(ModPlug_Settings* settings))
    85         FUNCTION_LOADER(ModPlug_SetSettings, void (*)(const ModPlug_Settings* settings))
    86         FUNCTION_LOADER(ModPlug_SetMasterVolume, void (*)(ModPlugFile* file,unsigned int cvol))
    87     }
    88     ++modplug.loaded;
    89 
    90     return 0;
    91 }
    92 
    93 static void MODPLUG_Unload(void)
    94 {
    95     if (modplug.loaded == 0) {
    96         return;
    97     }
    98     if (modplug.loaded == 1) {
    99 #ifdef MODPLUG_DYNAMIC
   100         SDL_UnloadObject(modplug.handle);
   101 #endif
   102     }
   103     --modplug.loaded;
   104 }
   105 
   106 
   107 typedef struct
   108 {
   109     int play_count;
   110     ModPlugFile *file;
   111     SDL_AudioStream *stream;
   112     void *buffer;
   113     int buffer_size;
   114 } MODPLUG_Music;
   115 
   116 
   117 static int MODPLUG_Seek(void *context, double position);
   118 static void MODPLUG_Delete(void *context);
   119 
   120 static int MODPLUG_Open(const SDL_AudioSpec *spec)
   121 {
   122     /* ModPlug supports U8 or S16 audio output */
   123     modplug.ModPlug_GetSettings(&settings);
   124     settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING;
   125     if (spec->channels == 1) {
   126         settings.mChannels = 1;
   127     } else {
   128         settings.mChannels = 2;
   129     }
   130     if (SDL_AUDIO_BITSIZE(spec->format) == 8) {
   131         settings.mBits = 8;
   132     } else {
   133         settings.mBits = 16;
   134     }
   135     if (spec->freq >= 44100) {
   136         settings.mFrequency = 44100;
   137     } else if (spec->freq >= 22050) {
   138         settings.mFrequency = 22050;
   139     } else {
   140         settings.mFrequency = 11025;
   141     }
   142     settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
   143     settings.mReverbDepth = 0;
   144     settings.mReverbDelay = 100;
   145     settings.mBassAmount = 0;
   146     settings.mBassRange = 50;
   147     settings.mSurroundDepth = 0;
   148     settings.mSurroundDelay = 10;
   149     settings.mLoopCount = 0;
   150     modplug.ModPlug_SetSettings(&settings);
   151     return 0;
   152 }
   153 
   154 /* Load a modplug stream from an SDL_RWops object */
   155 void *MODPLUG_CreateFromRW(SDL_RWops *src, int freesrc)
   156 {
   157     MODPLUG_Music *music;
   158     void *buffer;
   159     size_t size;
   160 
   161     music = (MODPLUG_Music *)SDL_calloc(1, sizeof(*music));
   162     if (!music) {
   163         SDL_OutOfMemory();
   164         return NULL;
   165     }
   166 
   167     music->stream = SDL_NewAudioStream((settings.mBits == 8) ? AUDIO_U8 : AUDIO_S16SYS, settings.mChannels, settings.mFrequency,
   168                                        music_spec.format, music_spec.channels, music_spec.freq);
   169     if (!music->stream) {
   170         MODPLUG_Delete(music);
   171         return NULL;
   172     }
   173 
   174     music->buffer_size = music_spec.samples * (settings.mBits / 8) * settings.mChannels;
   175     music->buffer = SDL_malloc(music->buffer_size);
   176     if (!music->buffer) {
   177         MODPLUG_Delete(music);
   178         return NULL;
   179     }
   180 
   181     buffer = SDL_LoadFile_RW(src, &size, SDL_FALSE);
   182     if (buffer) {
   183         music->file = modplug.ModPlug_Load(buffer, (int)size);
   184         if (!music->file) {
   185             Mix_SetError("ModPlug_Load failed");
   186         }
   187         SDL_free(buffer);
   188     }
   189 
   190     if (!music->file) {
   191         MODPLUG_Delete(music);
   192         return NULL;
   193     }
   194 
   195     if (freesrc) {
   196         SDL_RWclose(src);
   197     }
   198     return music;
   199 }
   200 
   201 /* Set the volume for a modplug stream */
   202 static void MODPLUG_SetVolume(void *context, int volume)
   203 {
   204     MODPLUG_Music *music = (MODPLUG_Music *)context;
   205     modplug.ModPlug_SetMasterVolume(music->file, volume*4);
   206 }
   207 
   208 /* Start playback of a given modplug stream */
   209 static int MODPLUG_Play(void *context, int play_count)
   210 {
   211     MODPLUG_Music *music = (MODPLUG_Music *)context;
   212     music->play_count = play_count;
   213     return MODPLUG_Seek(music, 0.0);
   214 }
   215 
   216 /* Play some of a stream previously started with modplug_play() */
   217 static int MODPLUG_GetSome(void *context, void *data, int bytes, SDL_bool *done)
   218 {
   219     MODPLUG_Music *music = (MODPLUG_Music *)context;
   220     int filled, amount;
   221 
   222     filled = SDL_AudioStreamGet(music->stream, data, bytes);
   223     if (filled != 0) {
   224         return filled;
   225     }
   226 
   227     if (!music->play_count) {
   228         /* All done */
   229         *done = SDL_TRUE;
   230         return 0;
   231     }
   232 
   233     amount = modplug.ModPlug_Read(music->file, music->buffer, music->buffer_size);
   234     if (amount > 0) {
   235         if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) {
   236             return -1;
   237         }
   238     } else {
   239         if (music->play_count == 1) {
   240             music->play_count = 0;
   241             SDL_AudioStreamFlush(music->stream);
   242         } else {
   243             int play_count = -1;
   244             if (music->play_count > 0) {
   245                 play_count = (music->play_count - 1);
   246             }
   247             if (MODPLUG_Play(music, play_count) < 0) {
   248                 return -1;
   249             }
   250         }
   251     }
   252     return 0;
   253 }
   254 static int MODPLUG_GetAudio(void *context, void *data, int bytes)
   255 {
   256     return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, MODPLUG_GetSome);
   257 }
   258 
   259 /* Jump (seek) to a given position */
   260 static int MODPLUG_Seek(void *context, double position)
   261 {
   262     MODPLUG_Music *music = (MODPLUG_Music *)context;
   263     modplug.ModPlug_Seek(music->file, (int)(position*1000));
   264     return 0;
   265 }
   266 
   267 /* Close the given modplug stream */
   268 static void MODPLUG_Delete(void *context)
   269 {
   270     MODPLUG_Music *music = (MODPLUG_Music *)context;
   271     if (music->file) {
   272         modplug.ModPlug_Unload(music->file);
   273     }
   274     if (music->stream) {
   275         SDL_FreeAudioStream(music->stream);
   276     }
   277     if (music->buffer) {
   278         SDL_free(music->buffer);
   279     }
   280     SDL_free(music);
   281 }
   282 
   283 Mix_MusicInterface Mix_MusicInterface_MODPLUG =
   284 {
   285     "MODPLUG",
   286     MIX_MUSIC_MODPLUG,
   287     MUS_MOD,
   288     SDL_FALSE,
   289     SDL_FALSE,
   290 
   291     MODPLUG_Load,
   292     MODPLUG_Open,
   293     MODPLUG_CreateFromRW,
   294     NULL,   /* CreateFromFile */
   295     MODPLUG_SetVolume,
   296     MODPLUG_Play,
   297     NULL,   /* IsPlaying */
   298     MODPLUG_GetAudio,
   299     MODPLUG_Seek,
   300     NULL,   /* Pause */
   301     NULL,   /* Resume */
   302     NULL,   /* Stop */
   303     MODPLUG_Delete,
   304     NULL,   /* Close */
   305     MODPLUG_Unload,
   306 };
   307 
   308 #endif /* MUSIC_MOD_MODPLUG */
   309 
   310 /* vi: set ts=4 sw=4 expandtab: */