src/audio/paudio/SDL_paudio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Wed, 29 Oct 2014 20:20:47 +0100
changeset 9201 21d9f9babb30
parent 8149 681eb46b8ac4
child 9393 ed79a66e57e5
permissions -rw-r--r--
Fixed bug 2647 - Memory leak in SDL_AddHintCallback function - SDL_hints.c

Nitz

Variable entry going out of scope leaks the storage it points to, at:

/* Need to add a hint entry for this watcher */
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
if (!hint) {
return;
}

Patch is attached.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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 #include "../../SDL_internal.h"
    22 
    23 #if SDL_AUDIO_DRIVER_PAUDIO
    24 
    25 /* Allow access to a raw mixing buffer */
    26 
    27 #include <errno.h>
    28 #include <unistd.h>
    29 #include <fcntl.h>
    30 #include <sys/time.h>
    31 #include <sys/ioctl.h>
    32 #include <sys/types.h>
    33 #include <sys/stat.h>
    34 
    35 #include "SDL_timer.h"
    36 #include "SDL_audio.h"
    37 #include "SDL_stdinc.h"
    38 #include "../SDL_audiomem.h"
    39 #include "../SDL_audio_c.h"
    40 #include "SDL_paudio.h"
    41 
    42 #define DEBUG_AUDIO 0
    43 
    44 /* A conflict within AIX 4.3.3 <sys/> headers and probably others as well.
    45  * I guess nobody ever uses audio... Shame over AIX header files.  */
    46 #include <sys/machine.h>
    47 #undef BIG_ENDIAN
    48 #include <sys/audio.h>
    49 
    50 /* Open the audio device for playback, and don't block if busy */
    51 /* #define OPEN_FLAGS   (O_WRONLY|O_NONBLOCK) */
    52 #define OPEN_FLAGS  O_WRONLY
    53 
    54 /* Get the name of the audio device we use for output */
    55 
    56 #ifndef _PATH_DEV_DSP
    57 #define _PATH_DEV_DSP   "/dev/%caud%c/%c"
    58 #endif
    59 
    60 static char devsettings[][3] = {
    61     {'p', '0', '1'}, {'p', '0', '2'}, {'p', '0', '3'}, {'p', '0', '4'},
    62     {'p', '1', '1'}, {'p', '1', '2'}, {'p', '1', '3'}, {'p', '1', '4'},
    63     {'p', '2', '1'}, {'p', '2', '2'}, {'p', '2', '3'}, {'p', '2', '4'},
    64     {'p', '3', '1'}, {'p', '3', '2'}, {'p', '3', '3'}, {'p', '3', '4'},
    65     {'b', '0', '1'}, {'b', '0', '2'}, {'b', '0', '3'}, {'b', '0', '4'},
    66     {'b', '1', '1'}, {'b', '1', '2'}, {'b', '1', '3'}, {'b', '1', '4'},
    67     {'b', '2', '1'}, {'b', '2', '2'}, {'b', '2', '3'}, {'b', '2', '4'},
    68     {'b', '3', '1'}, {'b', '3', '2'}, {'b', '3', '3'}, {'b', '3', '4'},
    69     {'\0', '\0', '\0'}
    70 };
    71 
    72 static int
    73 OpenUserDefinedDevice(char *path, int maxlen, int flags)
    74 {
    75     const char *audiodev;
    76     int fd;
    77 
    78     /* Figure out what our audio device is */
    79     if ((audiodev = SDL_getenv("SDL_PATH_DSP")) == NULL) {
    80         audiodev = SDL_getenv("AUDIODEV");
    81     }
    82     if (audiodev == NULL) {
    83         return -1;
    84     }
    85     fd = open(audiodev, flags, 0);
    86     if (path != NULL) {
    87         SDL_strlcpy(path, audiodev, maxlen);
    88         path[maxlen - 1] = '\0';
    89     }
    90     return fd;
    91 }
    92 
    93 static int
    94 OpenAudioPath(char *path, int maxlen, int flags, int classic)
    95 {
    96     struct stat sb;
    97     int cycle = 0;
    98     int fd = OpenUserDefinedDevice(path, maxlen, flags);
    99 
   100     if (fd != -1) {
   101         return fd;
   102     }
   103 
   104     /* !!! FIXME: do we really need a table here? */
   105     while (devsettings[cycle][0] != '\0') {
   106         char audiopath[1024];
   107         SDL_snprintf(audiopath, SDL_arraysize(audiopath),
   108                      _PATH_DEV_DSP,
   109                      devsettings[cycle][0],
   110                      devsettings[cycle][1], devsettings[cycle][2]);
   111 
   112         if (stat(audiopath, &sb) == 0) {
   113             fd = open(audiopath, flags, 0);
   114             if (fd > 0) {
   115                 if (path != NULL) {
   116                     SDL_strlcpy(path, audiopath, maxlen);
   117                 }
   118                 return fd;
   119             }
   120         }
   121     }
   122     return -1;
   123 }
   124 
   125 /* This function waits until it is possible to write a full sound buffer */
   126 static void
   127 PAUDIO_WaitDevice(_THIS)
   128 {
   129     fd_set fdset;
   130 
   131     /* See if we need to use timed audio synchronization */
   132     if (this->hidden->frame_ticks) {
   133         /* Use timer for general audio synchronization */
   134         Sint32 ticks;
   135 
   136         ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
   137         if (ticks > 0) {
   138             SDL_Delay(ticks);
   139         }
   140     } else {
   141         audio_buffer paud_bufinfo;
   142 
   143         /* Use select() for audio synchronization */
   144         struct timeval timeout;
   145         FD_ZERO(&fdset);
   146         FD_SET(this->hidden->audio_fd, &fdset);
   147 
   148         if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
   149 #ifdef DEBUG_AUDIO
   150             fprintf(stderr, "Couldn't get audio buffer information\n");
   151 #endif
   152             timeout.tv_sec = 10;
   153             timeout.tv_usec = 0;
   154         } else {
   155             long ms_in_buf = paud_bufinfo.write_buf_time;
   156             timeout.tv_sec = ms_in_buf / 1000;
   157             ms_in_buf = ms_in_buf - timeout.tv_sec * 1000;
   158             timeout.tv_usec = ms_in_buf * 1000;
   159 #ifdef DEBUG_AUDIO
   160             fprintf(stderr,
   161                     "Waiting for write_buf_time=%ld,%ld\n",
   162                     timeout.tv_sec, timeout.tv_usec);
   163 #endif
   164         }
   165 
   166 #ifdef DEBUG_AUDIO
   167         fprintf(stderr, "Waiting for audio to get ready\n");
   168 #endif
   169         if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
   170             <= 0) {
   171             const char *message =
   172                 "Audio timeout - buggy audio driver? (disabled)";
   173             /*
   174              * In general we should never print to the screen,
   175              * but in this case we have no other way of letting
   176              * the user know what happened.
   177              */
   178             fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
   179             this->enabled = 0;
   180             /* Don't try to close - may hang */
   181             this->hidden->audio_fd = -1;
   182 #ifdef DEBUG_AUDIO
   183             fprintf(stderr, "Done disabling audio\n");
   184 #endif
   185         }
   186 #ifdef DEBUG_AUDIO
   187         fprintf(stderr, "Ready!\n");
   188 #endif
   189     }
   190 }
   191 
   192 static void
   193 PAUDIO_PlayDevice(_THIS)
   194 {
   195     int written = 0;
   196     const Uint8 *mixbuf = this->hidden->mixbuf;
   197     const size_t mixlen = this->hidden->mixlen;
   198 
   199     /* Write the audio data, checking for EAGAIN on broken audio drivers */
   200     do {
   201         written = write(this->hidden->audio_fd, mixbuf, mixlen);
   202         if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
   203             SDL_Delay(1);       /* Let a little CPU time go by */
   204         }
   205     } while ((written < 0) &&
   206              ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
   207 
   208     /* If timer synchronization is enabled, set the next write frame */
   209     if (this->hidden->frame_ticks) {
   210         this->hidden->next_frame += this->hidden->frame_ticks;
   211     }
   212 
   213     /* If we couldn't write, assume fatal error for now */
   214     if (written < 0) {
   215         this->enabled = 0;
   216     }
   217 #ifdef DEBUG_AUDIO
   218     fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   219 #endif
   220 }
   221 
   222 static Uint8 *
   223 PAUDIO_GetDeviceBuf(_THIS)
   224 {
   225     return this->hidden->mixbuf;
   226 }
   227 
   228 static void
   229 PAUDIO_CloseDevice(_THIS)
   230 {
   231     if (this->hidden != NULL) {
   232         SDL_FreeAudioMem(this->hidden->mixbuf);
   233         this->hidden->mixbuf = NULL;
   234         if (this->hidden->audio_fd >= 0) {
   235             close(this->hidden->audio_fd);
   236             this->hidden->audio_fd = -1;
   237         }
   238         SDL_free(this->hidden);
   239         this->hidden = NULL;
   240     }
   241 }
   242 
   243 static int
   244 PAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
   245 {
   246     const char *workaround = SDL_getenv("SDL_DSP_NOSELECT");
   247     char audiodev[1024];
   248     const char *err = NULL;
   249     int format;
   250     int bytes_per_sample;
   251     SDL_AudioFormat test_format;
   252     audio_init paud_init;
   253     audio_buffer paud_bufinfo;
   254     audio_status paud_status;
   255     audio_control paud_control;
   256     audio_change paud_change;
   257     int fd = -1;
   258 
   259     /* Initialize all variables that we clean on shutdown */
   260     this->hidden = (struct SDL_PrivateAudioData *)
   261         SDL_malloc((sizeof *this->hidden));
   262     if (this->hidden == NULL) {
   263         return SDL_OutOfMemory();
   264     }
   265     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   266 
   267     /* Open the audio device */
   268     fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
   269     this->hidden->audio_fd = fd;
   270     if (fd < 0) {
   271         PAUDIO_CloseDevice(this);
   272         return SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
   273     }
   274 
   275     /*
   276      * We can't set the buffer size - just ask the device for the maximum
   277      * that we can have.
   278      */
   279     if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
   280         PAUDIO_CloseDevice(this);
   281         return SDL_SetError("Couldn't get audio buffer information");
   282     }
   283 
   284     if (this->spec.channels > 1)
   285         this->spec.channels = 2;
   286     else
   287         this->spec.channels = 1;
   288 
   289     /*
   290      * Fields in the audio_init structure:
   291      *
   292      * Ignored by us:
   293      *
   294      * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only?
   295      * paud.slot_number;         * slot number of the adapter
   296      * paud.device_id;           * adapter identification number
   297      *
   298      * Input:
   299      *
   300      * paud.srate;           * the sampling rate in Hz
   301      * paud.bits_per_sample; * 8, 16, 32, ...
   302      * paud.bsize;           * block size for this rate
   303      * paud.mode;            * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX
   304      * paud.channels;        * 1=mono, 2=stereo
   305      * paud.flags;           * FIXED - fixed length data
   306      *                       * LEFT_ALIGNED, RIGHT_ALIGNED (var len only)
   307      *                       * TWOS_COMPLEMENT - 2's complement data
   308      *                       * SIGNED - signed? comment seems wrong in sys/audio.h
   309      *                       * BIG_ENDIAN
   310      * paud.operation;       * PLAY, RECORD
   311      *
   312      * Output:
   313      *
   314      * paud.flags;           * PITCH            - pitch is supported
   315      *                       * INPUT            - input is supported
   316      *                       * OUTPUT           - output is supported
   317      *                       * MONITOR          - monitor is supported
   318      *                       * VOLUME           - volume is supported
   319      *                       * VOLUME_DELAY     - volume delay is supported
   320      *                       * BALANCE          - balance is supported
   321      *                       * BALANCE_DELAY    - balance delay is supported
   322      *                       * TREBLE           - treble control is supported
   323      *                       * BASS             - bass control is supported
   324      *                       * BESTFIT_PROVIDED - best fit returned
   325      *                       * LOAD_CODE        - DSP load needed
   326      * paud.rc;              * NO_PLAY         - DSP code can't do play requests
   327      *                       * NO_RECORD       - DSP code can't do record requests
   328      *                       * INVALID_REQUEST - request was invalid
   329      *                       * CONFLICT        - conflict with open's flags
   330      *                       * OVERLOADED      - out of DSP MIPS or memory
   331      * paud.position_resolution; * smallest increment for position
   332      */
   333 
   334     paud_init.srate = this->spec.freq;
   335     paud_init.mode = PCM;
   336     paud_init.operation = PLAY;
   337     paud_init.channels = this->spec.channels;
   338 
   339     /* Try for a closest match on audio format */
   340     format = 0;
   341     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   342          !format && test_format;) {
   343 #ifdef DEBUG_AUDIO
   344         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   345 #endif
   346         switch (test_format) {
   347         case AUDIO_U8:
   348             bytes_per_sample = 1;
   349             paud_init.bits_per_sample = 8;
   350             paud_init.flags = TWOS_COMPLEMENT | FIXED;
   351             format = 1;
   352             break;
   353         case AUDIO_S8:
   354             bytes_per_sample = 1;
   355             paud_init.bits_per_sample = 8;
   356             paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
   357             format = 1;
   358             break;
   359         case AUDIO_S16LSB:
   360             bytes_per_sample = 2;
   361             paud_init.bits_per_sample = 16;
   362             paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
   363             format = 1;
   364             break;
   365         case AUDIO_S16MSB:
   366             bytes_per_sample = 2;
   367             paud_init.bits_per_sample = 16;
   368             paud_init.flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED;
   369             format = 1;
   370             break;
   371         case AUDIO_U16LSB:
   372             bytes_per_sample = 2;
   373             paud_init.bits_per_sample = 16;
   374             paud_init.flags = TWOS_COMPLEMENT | FIXED;
   375             format = 1;
   376             break;
   377         case AUDIO_U16MSB:
   378             bytes_per_sample = 2;
   379             paud_init.bits_per_sample = 16;
   380             paud_init.flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED;
   381             format = 1;
   382             break;
   383         default:
   384             break;
   385         }
   386         if (!format) {
   387             test_format = SDL_NextAudioFormat();
   388         }
   389     }
   390     if (format == 0) {
   391 #ifdef DEBUG_AUDIO
   392         fprintf(stderr, "Couldn't find any hardware audio formats\n");
   393 #endif
   394         PAUDIO_CloseDevice(this);
   395         return SDL_SetError("Couldn't find any hardware audio formats");
   396     }
   397     this->spec.format = test_format;
   398 
   399     /*
   400      * We know the buffer size and the max number of subsequent writes
   401      *  that can be pending. If more than one can pend, allow the application
   402      *  to do something like double buffering between our write buffer and
   403      *  the device's own buffer that we are filling with write() anyway.
   404      *
   405      * We calculate this->spec.samples like this because
   406      *  SDL_CalculateAudioSpec() will give put paud_bufinfo.write_buf_cap
   407      *  (or paud_bufinfo.write_buf_cap/2) into this->spec.size in return.
   408      */
   409     if (paud_bufinfo.request_buf_cap == 1) {
   410         this->spec.samples = paud_bufinfo.write_buf_cap
   411             / bytes_per_sample / this->spec.channels;
   412     } else {
   413         this->spec.samples = paud_bufinfo.write_buf_cap
   414             / bytes_per_sample / this->spec.channels / 2;
   415     }
   416     paud_init.bsize = bytes_per_sample * this->spec.channels;
   417 
   418     SDL_CalculateAudioSpec(&this->spec);
   419 
   420     /*
   421      * The AIX paud device init can't modify the values of the audio_init
   422      * structure that we pass to it. So we don't need any recalculation
   423      * of this stuff and no reinit call as in linux dsp code.
   424      *
   425      * /dev/paud supports all of the encoding formats, so we don't need
   426      * to do anything like reopening the device, either.
   427      */
   428     if (ioctl(fd, AUDIO_INIT, &paud_init) < 0) {
   429         switch (paud_init.rc) {
   430         case 1:
   431             err = "Couldn't set audio format: DSP can't do play requests";
   432             break;
   433         case 2:
   434             err = "Couldn't set audio format: DSP can't do record requests";
   435             break;
   436         case 4:
   437             err = "Couldn't set audio format: request was invalid";
   438             break;
   439         case 5:
   440             err = "Couldn't set audio format: conflict with open's flags";
   441             break;
   442         case 6:
   443             err = "Couldn't set audio format: out of DSP MIPS or memory";
   444             break;
   445         default:
   446             err = "Couldn't set audio format: not documented in sys/audio.h";
   447             break;
   448         }
   449     }
   450 
   451     if (err != NULL) {
   452         PAUDIO_CloseDevice(this);
   453         return SDL_SetError("Paudio: %s", err);
   454     }
   455 
   456     /* Allocate mixing buffer */
   457     this->hidden->mixlen = this->spec.size;
   458     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   459     if (this->hidden->mixbuf == NULL) {
   460         PAUDIO_CloseDevice(this);
   461         return SDL_OutOfMemory();
   462     }
   463     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   464 
   465     /*
   466      * Set some paramters: full volume, first speaker that we can find.
   467      * Ignore the other settings for now.
   468      */
   469     paud_change.input = AUDIO_IGNORE;   /* the new input source */
   470     paud_change.output = OUTPUT_1;      /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */
   471     paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
   472     paud_change.volume = 0x7fffffff;    /* volume level [0-0x7fffffff] */
   473     paud_change.volume_delay = AUDIO_IGNORE;    /* the new volume delay */
   474     paud_change.balance = 0x3fffffff;   /* the new balance */
   475     paud_change.balance_delay = AUDIO_IGNORE;   /* the new balance delay */
   476     paud_change.treble = AUDIO_IGNORE;  /* the new treble state */
   477     paud_change.bass = AUDIO_IGNORE;    /* the new bass state */
   478     paud_change.pitch = AUDIO_IGNORE;   /* the new pitch state */
   479 
   480     paud_control.ioctl_request = AUDIO_CHANGE;
   481     paud_control.request_info = (char *) &paud_change;
   482     if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
   483 #ifdef DEBUG_AUDIO
   484         fprintf(stderr, "Can't change audio display settings\n");
   485 #endif
   486     }
   487 
   488     /*
   489      * Tell the device to expect data. Actual start will wait for
   490      * the first write() call.
   491      */
   492     paud_control.ioctl_request = AUDIO_START;
   493     paud_control.position = 0;
   494     if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
   495         PAUDIO_CloseDevice(this);
   496 #ifdef DEBUG_AUDIO
   497         fprintf(stderr, "Can't start audio play\n");
   498 #endif
   499         return SDL_SetError("Can't start audio play");
   500     }
   501 
   502     /* Check to see if we need to use select() workaround */
   503     if (workaround != NULL) {
   504         this->hidden->frame_ticks = (float) (this->spec.samples * 1000) /
   505             this->spec.freq;
   506         this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
   507     }
   508 
   509     /* We're ready to rock and roll. :-) */
   510     return 0;
   511 }
   512 
   513 static int
   514 PAUDIO_Init(SDL_AudioDriverImpl * impl)
   515 {
   516     /* !!! FIXME: not right for device enum? */
   517     int fd = OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
   518     if (fd < 0) {
   519         SDL_SetError("PAUDIO: Couldn't open audio device");
   520         return 0;
   521     }
   522     close(fd);
   523 
   524     /* Set the function pointers */
   525     impl->OpenDevice = DSP_OpenDevice;
   526     impl->PlayDevice = DSP_PlayDevice;
   527     impl->PlayDevice = DSP_WaitDevice;
   528     impl->GetDeviceBuf = DSP_GetDeviceBuf;
   529     impl->CloseDevice = DSP_CloseDevice;
   530     impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: add device enum! */
   531 
   532     return 1;   /* this audio target is available. */
   533 }
   534 
   535 AudioBootStrap PAUDIO_bootstrap = {
   536     "paud", "AIX Paudio", PAUDIO_Init, 0
   537 };
   538 
   539 #endif /* SDL_AUDIO_DRIVER_PAUDIO */
   540 
   541 /* vi: set ts=4 sw=4 expandtab: */