playwave.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 17 Jun 2015 00:11:41 -0700
changeset 705 fe757163b8f7
parent 617 87116a42526e
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   PLAYWAVE:  A test application for the SDL mixer 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$ */
    23 
    24 #include <stdlib.h>
    25 #include <stdio.h>
    26 #include <string.h>
    27 
    28 #ifdef unix
    29 #include <unistd.h>
    30 #endif
    31 
    32 #include "SDL.h"
    33 #include "SDL_mixer.h"
    34 
    35 #ifdef HAVE_SIGNAL_H
    36 #include <signal.h>
    37 #endif
    38 
    39 
    40 /*
    41  * rcg06132001 various mixer tests. Define the ones you want.
    42  */
    43 /*#define TEST_MIX_DECODERS*/
    44 /*#define TEST_MIX_VERSIONS*/
    45 /*#define TEST_MIX_CHANNELFINISHED*/
    46 /*#define TEST_MIX_PANNING*/
    47 /*#define TEST_MIX_DISTANCE*/
    48 /*#define TEST_MIX_POSITION*/
    49 
    50 
    51 #if (defined TEST_MIX_POSITION)
    52 
    53 #if (defined TEST_MIX_PANNING)
    54 #error TEST_MIX_POSITION interferes with TEST_MIX_PANNING.
    55 #endif
    56 
    57 #if (defined TEST_MIX_DISTANCE)
    58 #error TEST_MIX_POSITION interferes with TEST_MIX_DISTANCE.
    59 #endif
    60 
    61 #endif
    62 
    63 
    64 /* rcg06192001 for debugging purposes. */
    65 static void output_test_warnings(void)
    66 {
    67 #if (defined TEST_MIX_CHANNELFINISHED)
    68     fprintf(stderr, "Warning: TEST_MIX_CHANNELFINISHED is enabled in this binary...\n");
    69 #endif
    70 #if (defined TEST_MIX_PANNING)
    71     fprintf(stderr, "Warning: TEST_MIX_PANNING is enabled in this binary...\n");
    72 #endif
    73 #if (defined TEST_MIX_VERSIONS)
    74     fprintf(stderr, "Warning: TEST_MIX_VERSIONS is enabled in this binary...\n");
    75 #endif
    76 #if (defined TEST_MIX_DISTANCE)
    77     fprintf(stderr, "Warning: TEST_MIX_DISTANCE is enabled in this binary...\n");
    78 #endif
    79 #if (defined TEST_MIX_POSITION)
    80     fprintf(stderr, "Warning: TEST_MIX_POSITION is enabled in this binary...\n");
    81 #endif
    82 }
    83 
    84 
    85 static int audio_open = 0;
    86 static Mix_Chunk *wave = NULL;
    87 
    88 /* rcg06042009 Report available decoders. */
    89 #if (defined TEST_MIX_DECODERS)
    90 static void report_decoders(void)
    91 {
    92     int i, total;
    93 
    94     printf("Supported decoders...\n");
    95     total = Mix_GetNumChunkDecoders();
    96     for (i = 0; i < total; i++) {
    97         fprintf(stderr, " - chunk decoder: %s\n", Mix_GetChunkDecoder(i));
    98     }
    99 
   100     total = Mix_GetNumMusicDecoders();
   101     for (i = 0; i < total; i++) {
   102         fprintf(stderr, " - music decoder: %s\n", Mix_GetMusicDecoder(i));
   103     }
   104 }
   105 #endif
   106 
   107 /* rcg06192001 Check new Mixer version API. */
   108 #if (defined TEST_MIX_VERSIONS)
   109 static void output_versions(const char *libname, const SDL_version *compiled,
   110                             const SDL_version *linked)
   111 {
   112     fprintf(stderr,
   113             "This program was compiled against %s %d.%d.%d,\n"
   114             " and is dynamically linked to %d.%d.%d.\n", libname,
   115             compiled->major, compiled->minor, compiled->patch,
   116             linked->major, linked->minor, linked->patch);
   117 }
   118 
   119 static void test_versions(void)
   120 {
   121     SDL_version compiled;
   122     const SDL_version *linked;
   123 
   124     SDL_VERSION(&compiled);
   125     linked = SDL_Linked_Version();
   126     output_versions("SDL", &compiled, linked);
   127 
   128     SDL_MIXER_VERSION(&compiled);
   129     linked = Mix_Linked_Version();
   130     output_versions("SDL_mixer", &compiled, linked);
   131 }
   132 #endif
   133 
   134 
   135 #ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
   136 static volatile int channel_is_done = 0;
   137 static void channel_complete_callback(int chan)
   138 {
   139     Mix_Chunk *done_chunk = Mix_GetChunk(chan);
   140     fprintf(stderr, "We were just alerted that Mixer channel #%d is done.\n", chan);
   141     fprintf(stderr, "Channel's chunk pointer is (%p).\n", done_chunk);
   142     fprintf(stderr, " Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT");
   143     channel_is_done = 1;
   144 }
   145 #endif
   146 
   147 
   148 /* rcg06192001 abstract this out for testing purposes. */
   149 static int still_playing(void)
   150 {
   151 #ifdef TEST_MIX_CHANNELFINISHED
   152     return(!channel_is_done);
   153 #else
   154     return(Mix_Playing(0));
   155 #endif
   156 }
   157 
   158 
   159 #if (defined TEST_MIX_PANNING)
   160 static void do_panning_update(void)
   161 {
   162     static Uint8 leftvol = 128;
   163     static Uint8 rightvol = 128;
   164     static Uint8 leftincr = -1;
   165     static Uint8 rightincr = 1;
   166     static int panningok = 1;
   167     static Uint32 next_panning_update = 0;
   168 
   169     if ((panningok) && (SDL_GetTicks() >= next_panning_update)) {
   170         panningok = Mix_SetPanning(0, leftvol, rightvol);
   171         if (!panningok) {
   172             fprintf(stderr, "Mix_SetPanning(0, %d, %d) failed!\n",
   173                     (int) leftvol, (int) rightvol);
   174             fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
   175         }
   176 
   177         if ((leftvol == 255) || (leftvol == 0)) {
   178             if (leftvol == 255)
   179                 printf("All the way in the left speaker.\n");
   180                 leftincr *= -1;
   181         }
   182 
   183         if ((rightvol == 255) || (rightvol == 0)) {
   184             if (rightvol == 255)
   185                 printf("All the way in the right speaker.\n");
   186             rightincr *= -1;
   187         }
   188 
   189         leftvol += leftincr;
   190         rightvol += rightincr;
   191         next_panning_update = SDL_GetTicks() + 10;
   192     }
   193 }
   194 #endif
   195 
   196 
   197 #if (defined TEST_MIX_DISTANCE)
   198 static void do_distance_update(void)
   199 {
   200     static Uint8 distance = 1;
   201     static Uint8 distincr = 1;
   202     static int distanceok = 1;
   203     static Uint32 next_distance_update = 0;
   204 
   205     if ((distanceok) && (SDL_GetTicks() >= next_distance_update)) {
   206         distanceok = Mix_SetDistance(0, distance);
   207         if (!distanceok) {
   208             fprintf(stderr, "Mix_SetDistance(0, %d) failed!\n", (int) distance);
   209             fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
   210         }
   211 
   212         if (distance == 0) {
   213             printf("Distance at nearest point.\n");
   214             distincr *= -1;
   215         }
   216         else if (distance == 255) {
   217             printf("Distance at furthest point.\n");
   218             distincr *= -1;
   219         }
   220 
   221         distance += distincr;
   222         next_distance_update = SDL_GetTicks() + 15;
   223     }
   224 }
   225 #endif
   226 
   227 
   228 #if (defined TEST_MIX_POSITION)
   229 static void do_position_update(void)
   230 {
   231     static Sint16 distance = 1;
   232     static Sint8 distincr = 1;
   233     static Uint16 angle = 0;
   234     static Sint8 angleincr = 1;
   235     static int positionok = 1;
   236     static Uint32 next_position_update = 0;
   237 
   238     if ((positionok) && (SDL_GetTicks() >= next_position_update)) {
   239         positionok = Mix_SetPosition(0, angle, distance);
   240         if (!positionok) {
   241             fprintf(stderr, "Mix_SetPosition(0, %d, %d) failed!\n",
   242                     (int) angle, (int) distance);
   243             fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
   244         }
   245 
   246         if (angle == 0) {
   247             printf("Due north; now rotating clockwise...\n");
   248             angleincr = 1;
   249         }
   250 
   251         else if (angle == 360) {
   252             printf("Due north; now rotating counter-clockwise...\n");
   253             angleincr = -1;
   254         }
   255 
   256         distance += distincr;
   257 
   258         if (distance < 0) {
   259             distance = 0;
   260             distincr = 3;
   261             printf("Distance is very, very near. Stepping away by threes...\n");
   262         } else if (distance > 255) {
   263             distance = 255;
   264             distincr = -3;
   265             printf("Distance is very, very far. Stepping towards by threes...\n");
   266         }
   267 
   268         angle += angleincr;
   269         next_position_update = SDL_GetTicks() + 30;
   270     }
   271 }
   272 #endif
   273 
   274 
   275 static void CleanUp(int exitcode)
   276 {
   277     if ( wave ) {
   278         Mix_FreeChunk(wave);
   279         wave = NULL;
   280     }
   281     if ( audio_open ) {
   282         Mix_CloseAudio();
   283         audio_open = 0;
   284     }
   285     SDL_Quit();
   286 
   287     exit(exitcode);
   288 }
   289 
   290 
   291 static void Usage(char *argv0)
   292 {
   293     fprintf(stderr, "Usage: %s [-8] [-r rate] [-c channels] [-f] [-F] [-l] [-m] <wavefile>\n", argv0);
   294 }
   295 
   296 
   297 /*
   298  * rcg06182001 This is sick, but cool.
   299  *
   300  *  Actually, it's meant to be an example of how to manipulate a voice
   301  *  without having to use the mixer effects API. This is more processing
   302  *  up front, but no extra during the mixing process. Also, in a case like
   303  *  this, when you need to touch the whole sample at once, it's the only
   304  *  option you've got. And, with the effects API, you are altering a copy of
   305  *  the original sample for each playback, and thus, your changes aren't
   306  *  permanent; here, you've got a reversed sample, and that's that until
   307  *  you either reverse it again, or reload it.
   308  */
   309 static void flip_sample(Mix_Chunk *wave)
   310 {
   311     Uint16 format;
   312     int channels, i, incr;
   313     Uint8 *start = wave->abuf;
   314     Uint8 *end = wave->abuf + wave->alen;
   315 
   316     Mix_QuerySpec(NULL, &format, &channels);
   317     incr = (format & 0xFF) * channels;
   318 
   319     end -= incr;
   320 
   321     switch (incr) {
   322         case 8:
   323             for (i = wave->alen / 2; i >= 0; i -= 1) {
   324                 Uint8 tmp = *start;
   325                 *start = *end;
   326                 *end = tmp;
   327                 start++;
   328                 end--;
   329             }
   330             break;
   331 
   332         case 16:
   333             for (i = wave->alen / 2; i >= 0; i -= 2) {
   334                 Uint16 tmp = *start;
   335                 *((Uint16 *) start) = *((Uint16 *) end);
   336                 *((Uint16 *) end) = tmp;
   337                 start += 2;
   338                 end -= 2;
   339             }
   340             break;
   341 
   342         case 32:
   343             for (i = wave->alen / 2; i >= 0; i -= 4) {
   344                 Uint32 tmp = *start;
   345                 *((Uint32 *) start) = *((Uint32 *) end);
   346                 *((Uint32 *) end) = tmp;
   347                 start += 4;
   348                 end -= 4;
   349             }
   350             break;
   351 
   352         default:
   353             fprintf(stderr, "Unhandled format in sample flipping.\n");
   354             return;
   355     }
   356 }
   357 
   358 
   359 int main(int argc, char *argv[])
   360 {
   361     int audio_rate;
   362     Uint16 audio_format;
   363     int audio_channels;
   364     int loops = 0;
   365     int i;
   366     int reverse_stereo = 0;
   367     int reverse_sample = 0;
   368 
   369 #ifdef HAVE_SETBUF
   370     setbuf(stdout, NULL);    /* rcg06132001 for debugging purposes. */
   371     setbuf(stderr, NULL);    /* rcg06192001 for debugging purposes, too. */
   372 #endif
   373     output_test_warnings();
   374 
   375     /* Initialize variables */
   376     audio_rate = MIX_DEFAULT_FREQUENCY;
   377     audio_format = MIX_DEFAULT_FORMAT;
   378     audio_channels = 2;
   379 
   380     /* Check command line usage */
   381     for ( i=1; argv[i] && (*argv[i] == '-'); ++i ) {
   382         if ( (strcmp(argv[i], "-r") == 0) && argv[i+1] ) {
   383             ++i;
   384             audio_rate = atoi(argv[i]);
   385         } else
   386         if ( strcmp(argv[i], "-m") == 0 ) {
   387             audio_channels = 1;
   388         } else
   389         if ( (strcmp(argv[i], "-c") == 0) && argv[i+1] ) {
   390             ++i;
   391             audio_channels = atoi(argv[i]);
   392         } else
   393         if ( strcmp(argv[i], "-l") == 0 ) {
   394             loops = -1;
   395         } else
   396         if ( strcmp(argv[i], "-8") == 0 ) {
   397             audio_format = AUDIO_U8;
   398         } else
   399         if ( strcmp(argv[i], "-f") == 0 ) { /* rcg06122001 flip stereo */
   400             reverse_stereo = 1;
   401         } else
   402         if ( strcmp(argv[i], "-F") == 0 ) { /* rcg06172001 flip sample */
   403             reverse_sample = 1;
   404         } else {
   405             Usage(argv[0]);
   406             return(1);
   407         }
   408     }
   409     if ( ! argv[i] ) {
   410         Usage(argv[0]);
   411         return(1);
   412     }
   413 
   414     /* Initialize the SDL library */
   415     if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) {
   416         fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
   417         return(255);
   418     }
   419 #ifdef HAVE_SIGNAL_H
   420     signal(SIGINT, CleanUp);
   421     signal(SIGTERM, CleanUp);
   422 #endif
   423 
   424     /* Open the audio device */
   425     if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) {
   426         fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
   427         CleanUp(2);
   428     } else {
   429         Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
   430         printf("Opened audio at %d Hz %d bit %s", audio_rate,
   431             (audio_format&0xFF),
   432             (audio_channels > 2) ? "surround" :
   433             (audio_channels > 1) ? "stereo" : "mono");
   434         if ( loops ) {
   435           printf(" (looping)\n");
   436         } else {
   437           putchar('\n');
   438         }
   439     }
   440     audio_open = 1;
   441 
   442 #if (defined TEST_MIX_VERSIONS)
   443     test_versions();
   444 #endif
   445 
   446 #if (defined TEST_MIX_DECODERS)
   447     report_decoders();
   448 #endif
   449 
   450     /* Load the requested wave file */
   451     wave = Mix_LoadWAV(argv[i]);
   452     if ( wave == NULL ) {
   453         fprintf(stderr, "Couldn't load %s: %s\n",
   454                         argv[i], SDL_GetError());
   455         CleanUp(2);
   456     }
   457 
   458     if (reverse_sample) {
   459         flip_sample(wave);
   460     }
   461 
   462 #ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
   463     Mix_ChannelFinished(channel_complete_callback);
   464 #endif
   465 
   466     if ( (!Mix_SetReverseStereo(MIX_CHANNEL_POST, reverse_stereo)) &&
   467          (reverse_stereo) )
   468     {
   469         printf("Failed to set up reverse stereo effect!\n");
   470         printf("Reason: [%s].\n", Mix_GetError());
   471     }
   472 
   473     /* Play and then exit */
   474     Mix_PlayChannel(0, wave, loops);
   475 
   476     while (still_playing()) {
   477 
   478 #if (defined TEST_MIX_PANNING)  /* rcg06132001 */
   479         do_panning_update();
   480 #endif
   481 
   482 #if (defined TEST_MIX_DISTANCE) /* rcg06192001 */
   483         do_distance_update();
   484 #endif
   485 
   486 #if (defined TEST_MIX_POSITION) /* rcg06202001 */
   487         do_position_update();
   488 #endif
   489 
   490         SDL_Delay(1);
   491 
   492     } /* while still_playing() loop... */
   493 
   494     CleanUp(0);
   495 
   496     /* Not reached, but fixes compiler warnings */
   497     return 0;
   498 }
   499 
   500 /* end of playwave.c ... */
   501