music_mod.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 17 Jun 2015 00:11:41 -0700
changeset 705 fe757163b8f7
parent 628 461e2724372b
child 711 f40c5ac95b12
permissions -rw-r--r--
Fixed bug 3018 - Loading MIDI music using FluidSynth leaks memory.

Philipp Wiesemann

There is a memory leak in fluidsynth.c and fluidsynth_loadsong_RW_internal(). The allocated temporary buffer is not deleted if fluid_player_add_mem() returns FLUID_OK.
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2013 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 /* $Id: music_mod.c 4211 2008-12-08 00:27:32Z slouken $ */
    23 
    24 #ifdef MOD_MUSIC
    25 
    26 /* This file supports MOD tracker music streams */
    27 
    28 #include "SDL_mixer.h"
    29 #include "dynamic_mod.h"
    30 #include "music_mod.h"
    31 
    32 #include "mikmod.h"
    33 
    34 #define SDL_SURROUND
    35 #ifdef SDL_SURROUND
    36 #define MAX_OUTPUT_CHANNELS 6
    37 #else
    38 #define MAX_OUTPUT_CHANNELS 2
    39 #endif
    40 
    41 /* Reference for converting mikmod output to 4/6 channels */
    42 static int current_output_channels;
    43 static Uint16 current_output_format;
    44 
    45 static int music_swap8;
    46 static int music_swap16;
    47 
    48 /* Initialize the MOD player, with the given mixer settings
    49    This function returns 0, or -1 if there was an error.
    50  */
    51 int MOD_init(SDL_AudioSpec *mixerfmt)
    52 {
    53     CHAR *list;
    54 
    55     if ( !Mix_Init(MIX_INIT_MOD) ) {
    56         return -1;
    57     }
    58 
    59     /* Set the MikMod music format */
    60     music_swap8 = 0;
    61     music_swap16 = 0;
    62     switch (mixerfmt->format) {
    63 
    64         case AUDIO_U8:
    65         case AUDIO_S8: {
    66             if ( mixerfmt->format == AUDIO_S8 ) {
    67                 music_swap8 = 1;
    68             }
    69             *mikmod.md_mode = 0;
    70         }
    71         break;
    72 
    73         case AUDIO_S16LSB:
    74         case AUDIO_S16MSB: {
    75             /* See if we need to correct MikMod mixing */
    76 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
    77             if ( mixerfmt->format == AUDIO_S16MSB ) {
    78 #else
    79             if ( mixerfmt->format == AUDIO_S16LSB ) {
    80 #endif
    81                 music_swap16 = 1;
    82             }
    83             *mikmod.md_mode = DMODE_16BITS;
    84         }
    85         break;
    86 
    87         default: {
    88             Mix_SetError("Unknown hardware audio format");
    89             return -1;
    90         }
    91     }
    92     current_output_channels = mixerfmt->channels;
    93     current_output_format = mixerfmt->format;
    94     if ( mixerfmt->channels > 1 ) {
    95         if ( mixerfmt->channels > MAX_OUTPUT_CHANNELS ) {
    96             Mix_SetError("Hardware uses more channels than mixerfmt");
    97             return -1;
    98         }
    99         *mikmod.md_mode |= DMODE_STEREO;
   100     }
   101     *mikmod.md_mixfreq = mixerfmt->freq;
   102     *mikmod.md_device  = 0;
   103     *mikmod.md_volume  = 96;
   104     *mikmod.md_musicvolume = 128;
   105     *mikmod.md_sndfxvolume = 128;
   106     *mikmod.md_pansep  = 128;
   107     *mikmod.md_reverb  = 0;
   108     *mikmod.md_mode    |= DMODE_HQMIXER|DMODE_SOFT_MUSIC|DMODE_SURROUND;
   109 
   110     list = mikmod.MikMod_InfoDriver();
   111     if ( list )
   112       mikmod.MikMod_free(list);
   113     else
   114       mikmod.MikMod_RegisterDriver(mikmod.drv_nos);
   115 
   116     list = mikmod.MikMod_InfoLoader();
   117     if ( list )
   118       mikmod.MikMod_free(list);
   119     else
   120       mikmod.MikMod_RegisterAllLoaders();
   121 
   122     if ( mikmod.MikMod_Init(NULL) ) {
   123         Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
   124         return -1;
   125     }
   126 
   127     return 0;
   128 }
   129 
   130 /* Uninitialize the music players */
   131 void MOD_exit(void)
   132 {
   133     if (mikmod.MikMod_Exit) {
   134         mikmod.MikMod_Exit();
   135     }
   136 }
   137 
   138 /* Set the volume for a MOD stream */
   139 void MOD_setvolume(MODULE *music, int volume)
   140 {
   141     mikmod.Player_SetVolume((SWORD)volume);
   142 }
   143 
   144 typedef struct
   145 {
   146     MREADER mr;
   147     Sint64 offset;
   148     Sint64 eof;
   149     SDL_RWops *src;
   150 } LMM_MREADER;
   151 
   152 BOOL LMM_Seek(struct MREADER *mr,long to,int dir)
   153 {
   154 	Sint64 offset = to;
   155     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   156     if ( dir == SEEK_SET ) {
   157         offset += lmmmr->offset;
   158     }
   159     return (SDL_RWseek(lmmmr->src, offset, dir) < lmmmr->offset);
   160 }
   161 long LMM_Tell(struct MREADER *mr)
   162 {
   163     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   164     return (long)(SDL_RWtell(lmmmr->src) - lmmmr->offset);
   165 }
   166 BOOL LMM_Read(struct MREADER *mr,void *buf,size_t sz)
   167 {
   168     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   169     return SDL_RWread(lmmmr->src, buf, sz, 1);
   170 }
   171 int LMM_Get(struct MREADER *mr)
   172 {
   173     unsigned char c;
   174     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   175     if ( SDL_RWread(lmmmr->src, &c, 1, 1) ) {
   176         return c;
   177     }
   178     return EOF;
   179 }
   180 BOOL LMM_Eof(struct MREADER *mr)
   181 {
   182     Sint64 offset;
   183     LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   184     offset = LMM_Tell(mr);
   185     return offset >= lmmmr->eof;
   186 }
   187 MODULE *MikMod_LoadSongRW(SDL_RWops *src, int maxchan)
   188 {
   189     LMM_MREADER lmmmr = {
   190         { LMM_Seek, LMM_Tell, LMM_Read, LMM_Get, LMM_Eof },
   191         0,
   192         0,
   193         0
   194     };
   195     lmmmr.offset = SDL_RWtell(src);
   196     SDL_RWseek(src, 0, RW_SEEK_END);
   197     lmmmr.eof = SDL_RWtell(src);
   198     SDL_RWseek(src, lmmmr.offset, RW_SEEK_SET);
   199     lmmmr.src = src;
   200     return mikmod.Player_LoadGeneric((MREADER*)&lmmmr, maxchan, 0);
   201 }
   202 
   203 /* Load a MOD stream from an SDL_RWops object */
   204 MODULE *MOD_new_RW(SDL_RWops *src, int freesrc)
   205 {
   206     MODULE *module;
   207 
   208     /* Make sure the mikmod library is loaded */
   209     if ( !Mix_Init(MIX_INIT_MOD) ) {
   210         return NULL;
   211     }
   212 
   213     module = MikMod_LoadSongRW(src, 64);
   214     if (!module) {
   215         Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
   216         return NULL;
   217     }
   218 
   219     /* Stop implicit looping, fade out and other flags. */
   220     module->extspd  = 1;
   221     module->panflag = 1;
   222     module->wrap    = 0;
   223     module->loop    = 0;
   224 #if 0 /* Don't set fade out by default - unfortunately there's no real way
   225 to query the status of the song or set trigger actions.  Hum. */
   226     module->fadeout = 1;
   227 #endif
   228 
   229     if ( freesrc ) {
   230         SDL_RWclose(src);
   231     }
   232     return module;
   233 }
   234 
   235 /* Start playback of a given MOD stream */
   236 void MOD_play(MODULE *music)
   237 {
   238     mikmod.Player_Start(music);
   239 }
   240 
   241 /* Return non-zero if a stream is currently playing */
   242 int MOD_playing(MODULE *music)
   243 {
   244     return mikmod.Player_Active();
   245 }
   246 
   247 /* Play some of a stream previously started with MOD_play() */
   248 int MOD_playAudio(MODULE *music, Uint8 *stream, int len)
   249 {
   250     if (current_output_channels > 2) {
   251         int small_len = 2 * len / current_output_channels;
   252         int i;
   253         Uint8 *src, *dst;
   254 
   255         mikmod.VC_WriteBytes((SBYTE *)stream, small_len);
   256         /* and extend to len by copying channels */
   257         src = stream + small_len;
   258         dst = stream + len;
   259 
   260         switch (current_output_format & 0xFF) {
   261             case 8:
   262                 for ( i=small_len/2; i; --i ) {
   263                     src -= 2;
   264                     dst -= current_output_channels;
   265                     dst[0] = src[0];
   266                     dst[1] = src[1];
   267                     dst[2] = src[0];
   268                     dst[3] = src[1];
   269                     if (current_output_channels == 6) {
   270                         dst[4] = src[0];
   271                         dst[5] = src[1];
   272                     }
   273                 }
   274                 break;
   275             case 16:
   276                 for ( i=small_len/4; i; --i ) {
   277                     src -= 4;
   278                     dst -= 2 * current_output_channels;
   279                     dst[0] = src[0];
   280                     dst[1] = src[1];
   281                     dst[2] = src[2];
   282                     dst[3] = src[3];
   283                     dst[4] = src[0];
   284                     dst[5] = src[1];
   285                     dst[6] = src[2];
   286                     dst[7] = src[3];
   287                     if (current_output_channels == 6) {
   288                         dst[8] = src[0];
   289                         dst[9] = src[1];
   290                         dst[10] = src[2];
   291                         dst[11] = src[3];
   292                     }
   293                 }
   294                 break;
   295         }
   296     } else {
   297         mikmod.VC_WriteBytes((SBYTE *)stream, len);
   298     }
   299     if ( music_swap8 ) {
   300         Uint8 *dst;
   301         int i;
   302 
   303         dst = stream;
   304         for ( i=len; i; --i ) {
   305             *dst++ ^= 0x80;
   306         }
   307     } else
   308     if ( music_swap16 ) {
   309         Uint8 *dst, tmp;
   310         int i;
   311 
   312         dst = stream;
   313         for ( i=(len/2); i; --i ) {
   314             tmp = dst[0];
   315             dst[0] = dst[1];
   316             dst[1] = tmp;
   317             dst += 2;
   318         }
   319     }
   320     return 0;
   321 }
   322 
   323 /* Stop playback of a stream previously started with MOD_play() */
   324 void MOD_stop(MODULE *music)
   325 {
   326     mikmod.Player_Stop();
   327 }
   328 
   329 /* Close the given MOD stream */
   330 void MOD_delete(MODULE *music)
   331 {
   332     mikmod.Player_Free(music);
   333 }
   334 
   335 /* Jump (seek) to a given position (time is in seconds) */
   336 void MOD_jump_to_time(MODULE *music, double time)
   337 {
   338     mikmod.Player_SetPosition((UWORD)time);
   339 }
   340 
   341 #endif /* MOD_MUSIC */