music_mod.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 29 Nov 2014 14:42:02 -0800
changeset 697 b28b41b93ba7
parent 628 461e2724372b
child 711 f40c5ac95b12
permissions -rw-r--r--
Fixed bug 2795 - SDL library detection selects the wrong lib

Chris Beck

When creating a homebrew recipe for wesnoth, I discovered that the SDL image configuration routine does not detect libpng properly -- if you have multiple instances of libpng on your system, and you use environment variables to select an instance which is not in your system directory, the build can be broken, because it will run configuration tests against the system installed version, but deduce that it should use the filename of the system-installed version. In a vanilla build of wesnoth using homebrew, this results in segfaults at runtime, because you end up linking against two different versions of libpng, which is also needed independently of SDL.

The problem is essentially in the "find_lib" routine in the configure file:



find_lib()
{
gcc_bin_path=[`$CC -print-search-dirs 2>/dev/null | fgrep programs: | sed 's/[^=]*=\(.*\)/\1/' | sed 's/:/ /g'`]
gcc_lib_path=[`$CC -print-search-dirs 2>/dev/null | fgrep libraries: | sed 's/[^=]*=\(.*\)/\1/' | sed 's/:/ /g'`]
env_lib_path=[`echo $LIBS $LDFLAGS | sed 's/-L[ ]*//g'`]
for path in $gcc_bin_path $gcc_lib_path $env_lib_path /usr/lib /usr/local/lib; do
lib=[`ls -- $path/$1 2>/dev/null | sort -r | sed 's/.*\/\(.*\)/\1/; q'`]
if test x$lib != x; then
echo $lib
return
fi
done
}



Because the for loop goes over the system directories before the environment directories, any system-installed lib will shadow the lib selected via environment variables. This is contrary to the behavior of the configuration tests earlier in the script, which prefers the environment variable libs over the system-installed libs. The 'for' loop should instead be:



for path in $env_lib_path $gcc_bin_path $gcc_lib_path /usr/lib /usr/local/lib; do



You can see the full discussion on the Homebrew / linuxbrew issue tracker here: https://github.com/Homebrew/linuxbrew/issues/172

I have checked that this bug also affects SDL 1.2.15, SDL_mixer and SDL_ttf 1.2, which all use this same "find_lib" routine. I have not determined if the bug affects SDL 2.0, which seems not to use this exact routine.
     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 */