src/audio/paudio/SDL_paudio.c
author Sam Lantinga
Sat, 06 Oct 2012 11:23:47 -0700
changeset 6565 1f3c0df426dc
parent 6138 4c64952a58fb
child 6885 700f1b25f77f
permissions -rw-r--r--
When using Xinerama, XVidMode always works on screen 0. Otherwise use the real X11 screen.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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_config.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 =
   137             ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) -
   138             FUDGE_TICKS;
   139         if (ticks > 0) {
   140             SDL_Delay(ticks);
   141         }
   142     } else {
   143         audio_buffer paud_bufinfo;
   144 
   145         /* Use select() for audio synchronization */
   146         struct timeval timeout;
   147         FD_ZERO(&fdset);
   148         FD_SET(this->hidden->audio_fd, &fdset);
   149 
   150         if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
   151 #ifdef DEBUG_AUDIO
   152             fprintf(stderr, "Couldn't get audio buffer information\n");
   153 #endif
   154             timeout.tv_sec = 10;
   155             timeout.tv_usec = 0;
   156         } else {
   157             long ms_in_buf = paud_bufinfo.write_buf_time;
   158             timeout.tv_sec = ms_in_buf / 1000;
   159             ms_in_buf = ms_in_buf - timeout.tv_sec * 1000;
   160             timeout.tv_usec = ms_in_buf * 1000;
   161 #ifdef DEBUG_AUDIO
   162             fprintf(stderr,
   163                     "Waiting for write_buf_time=%ld,%ld\n",
   164                     timeout.tv_sec, timeout.tv_usec);
   165 #endif
   166         }
   167 
   168 #ifdef DEBUG_AUDIO
   169         fprintf(stderr, "Waiting for audio to get ready\n");
   170 #endif
   171         if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
   172             <= 0) {
   173             const char *message =
   174                 "Audio timeout - buggy audio driver? (disabled)";
   175             /*
   176              * In general we should never print to the screen,
   177              * but in this case we have no other way of letting
   178              * the user know what happened.
   179              */
   180             fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
   181             this->enabled = 0;
   182             /* Don't try to close - may hang */
   183             this->hidden->audio_fd = -1;
   184 #ifdef DEBUG_AUDIO
   185             fprintf(stderr, "Done disabling audio\n");
   186 #endif
   187         }
   188 #ifdef DEBUG_AUDIO
   189         fprintf(stderr, "Ready!\n");
   190 #endif
   191     }
   192 }
   193 
   194 static void
   195 PAUDIO_PlayDevice(_THIS)
   196 {
   197     int written = 0;
   198     const Uint8 *mixbuf = this->hidden->mixbuf;
   199     const size_t mixlen = this->hidden->mixlen;
   200 
   201     /* Write the audio data, checking for EAGAIN on broken audio drivers */
   202     do {
   203         written = write(this->hidden->audio_fd, mixbuf, mixlen);
   204         if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
   205             SDL_Delay(1);       /* Let a little CPU time go by */
   206         }
   207     } while ((written < 0) &&
   208              ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
   209 
   210     /* If timer synchronization is enabled, set the next write frame */
   211     if (this->hidden->frame_ticks) {
   212         this->hidden->next_frame += this->hidden->frame_ticks;
   213     }
   214 
   215     /* If we couldn't write, assume fatal error for now */
   216     if (written < 0) {
   217         this->enabled = 0;
   218     }
   219 #ifdef DEBUG_AUDIO
   220     fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   221 #endif
   222 }
   223 
   224 static Uint8 *
   225 PAUDIO_GetDeviceBuf(_THIS)
   226 {
   227     return this->hidden->mixbuf;
   228 }
   229 
   230 static void
   231 PAUDIO_CloseDevice(_THIS)
   232 {
   233     if (this->hidden != NULL) {
   234         if (this->hidden->mixbuf != NULL) {
   235             SDL_FreeAudioMem(this->hidden->mixbuf);
   236             this->hidden->mixbuf = NULL;
   237         }
   238         if (this->hidden->audio_fd >= 0) {
   239             close(this->hidden->audio_fd);
   240             this->hidden->audio_fd = -1;
   241         }
   242         SDL_free(this->hidden);
   243         this->hidden = NULL;
   244     }
   245 }
   246 
   247 static int
   248 PAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
   249 {
   250     const char *workaround = SDL_getenv("SDL_DSP_NOSELECT");
   251     char audiodev[1024];
   252     const char *err = NULL;
   253     int format;
   254     int bytes_per_sample;
   255     SDL_AudioFormat test_format;
   256     audio_init paud_init;
   257     audio_buffer paud_bufinfo;
   258     audio_status paud_status;
   259     audio_control paud_control;
   260     audio_change paud_change;
   261     int fd = -1;
   262 
   263     /* Initialize all variables that we clean on shutdown */
   264     this->hidden = (struct SDL_PrivateAudioData *)
   265         SDL_malloc((sizeof *this->hidden));
   266     if (this->hidden == NULL) {
   267         SDL_OutOfMemory();
   268         return 0;
   269     }
   270     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   271 
   272     /* Open the audio device */
   273     fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
   274     this->hidden->audio_fd = fd;
   275     if (fd < 0) {
   276         PAUDIO_CloseDevice(this);
   277         SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
   278         return 0;
   279     }
   280 
   281     /*
   282      * We can't set the buffer size - just ask the device for the maximum
   283      * that we can have.
   284      */
   285     if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
   286         PAUDIO_CloseDevice(this);
   287         SDL_SetError("Couldn't get audio buffer information");
   288         return 0;
   289     }
   290 
   291     if (this->spec.channels > 1)
   292         this->spec.channels = 2;
   293     else
   294         this->spec.channels = 1;
   295 
   296     /*
   297      * Fields in the audio_init structure:
   298      *
   299      * Ignored by us:
   300      *
   301      * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only?
   302      * paud.slot_number;         * slot number of the adapter
   303      * paud.device_id;           * adapter identification number
   304      *
   305      * Input:
   306      *
   307      * paud.srate;           * the sampling rate in Hz
   308      * paud.bits_per_sample; * 8, 16, 32, ...
   309      * paud.bsize;           * block size for this rate
   310      * paud.mode;            * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX
   311      * paud.channels;        * 1=mono, 2=stereo
   312      * paud.flags;           * FIXED - fixed length data
   313      *                       * LEFT_ALIGNED, RIGHT_ALIGNED (var len only)
   314      *                       * TWOS_COMPLEMENT - 2's complement data
   315      *                       * SIGNED - signed? comment seems wrong in sys/audio.h
   316      *                       * BIG_ENDIAN
   317      * paud.operation;       * PLAY, RECORD
   318      *
   319      * Output:
   320      *
   321      * paud.flags;           * PITCH            - pitch is supported
   322      *                       * INPUT            - input is supported
   323      *                       * OUTPUT           - output is supported
   324      *                       * MONITOR          - monitor is supported
   325      *                       * VOLUME           - volume is supported
   326      *                       * VOLUME_DELAY     - volume delay is supported
   327      *                       * BALANCE          - balance is supported
   328      *                       * BALANCE_DELAY    - balance delay is supported
   329      *                       * TREBLE           - treble control is supported
   330      *                       * BASS             - bass control is supported
   331      *                       * BESTFIT_PROVIDED - best fit returned
   332      *                       * LOAD_CODE        - DSP load needed
   333      * paud.rc;              * NO_PLAY         - DSP code can't do play requests
   334      *                       * NO_RECORD       - DSP code can't do record requests
   335      *                       * INVALID_REQUEST - request was invalid
   336      *                       * CONFLICT        - conflict with open's flags
   337      *                       * OVERLOADED      - out of DSP MIPS or memory
   338      * paud.position_resolution; * smallest increment for position
   339      */
   340 
   341     paud_init.srate = this->spec.freq;
   342     paud_init.mode = PCM;
   343     paud_init.operation = PLAY;
   344     paud_init.channels = this->spec.channels;
   345 
   346     /* Try for a closest match on audio format */
   347     format = 0;
   348     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   349          !format && test_format;) {
   350 #ifdef DEBUG_AUDIO
   351         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   352 #endif
   353         switch (test_format) {
   354         case AUDIO_U8:
   355             bytes_per_sample = 1;
   356             paud_init.bits_per_sample = 8;
   357             paud_init.flags = TWOS_COMPLEMENT | FIXED;
   358             format = 1;
   359             break;
   360         case AUDIO_S8:
   361             bytes_per_sample = 1;
   362             paud_init.bits_per_sample = 8;
   363             paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
   364             format = 1;
   365             break;
   366         case AUDIO_S16LSB:
   367             bytes_per_sample = 2;
   368             paud_init.bits_per_sample = 16;
   369             paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
   370             format = 1;
   371             break;
   372         case AUDIO_S16MSB:
   373             bytes_per_sample = 2;
   374             paud_init.bits_per_sample = 16;
   375             paud_init.flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED;
   376             format = 1;
   377             break;
   378         case AUDIO_U16LSB:
   379             bytes_per_sample = 2;
   380             paud_init.bits_per_sample = 16;
   381             paud_init.flags = TWOS_COMPLEMENT | FIXED;
   382             format = 1;
   383             break;
   384         case AUDIO_U16MSB:
   385             bytes_per_sample = 2;
   386             paud_init.bits_per_sample = 16;
   387             paud_init.flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED;
   388             format = 1;
   389             break;
   390         default:
   391             break;
   392         }
   393         if (!format) {
   394             test_format = SDL_NextAudioFormat();
   395         }
   396     }
   397     if (format == 0) {
   398 #ifdef DEBUG_AUDIO
   399         fprintf(stderr, "Couldn't find any hardware audio formats\n");
   400 #endif
   401         PAUDIO_CloseDevice(this);
   402         SDL_SetError("Couldn't find any hardware audio formats");
   403         return 0;
   404     }
   405     this->spec.format = test_format;
   406 
   407     /*
   408      * We know the buffer size and the max number of subsequent writes
   409      *  that can be pending. If more than one can pend, allow the application
   410      *  to do something like double buffering between our write buffer and
   411      *  the device's own buffer that we are filling with write() anyway.
   412      *
   413      * We calculate this->spec.samples like this because
   414      *  SDL_CalculateAudioSpec() will give put paud_bufinfo.write_buf_cap
   415      *  (or paud_bufinfo.write_buf_cap/2) into this->spec.size in return.
   416      */
   417     if (paud_bufinfo.request_buf_cap == 1) {
   418         this->spec.samples = paud_bufinfo.write_buf_cap
   419             / bytes_per_sample / this->spec.channels;
   420     } else {
   421         this->spec.samples = paud_bufinfo.write_buf_cap
   422             / bytes_per_sample / this->spec.channels / 2;
   423     }
   424     paud_init.bsize = bytes_per_sample * this->spec.channels;
   425 
   426     SDL_CalculateAudioSpec(&this->spec);
   427 
   428     /*
   429      * The AIX paud device init can't modify the values of the audio_init
   430      * structure that we pass to it. So we don't need any recalculation
   431      * of this stuff and no reinit call as in linux dsp code.
   432      *
   433      * /dev/paud supports all of the encoding formats, so we don't need
   434      * to do anything like reopening the device, either.
   435      */
   436     if (ioctl(fd, AUDIO_INIT, &paud_init) < 0) {
   437         switch (paud_init.rc) {
   438         case 1:
   439             err = "Couldn't set audio format: DSP can't do play requests";
   440             break;
   441         case 2:
   442             err = "Couldn't set audio format: DSP can't do record requests";
   443             break;
   444         case 4:
   445             err = "Couldn't set audio format: request was invalid";
   446             break;
   447         case 5:
   448             err = "Couldn't set audio format: conflict with open's flags";
   449             break;
   450         case 6:
   451             err = "Couldn't set audio format: out of DSP MIPS or memory";
   452             break;
   453         default:
   454             err = "Couldn't set audio format: not documented in sys/audio.h";
   455             break;
   456         }
   457     }
   458 
   459     if (err != NULL) {
   460         PAUDIO_CloseDevice(this);
   461         SDL_SetError("Paudio: %s", err);
   462         return 0;
   463     }
   464 
   465     /* Allocate mixing buffer */
   466     this->hidden->mixlen = this->spec.size;
   467     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   468     if (this->hidden->mixbuf == NULL) {
   469         PAUDIO_CloseDevice(this);
   470         SDL_OutOfMemory();
   471         return 0;
   472     }
   473     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   474 
   475     /*
   476      * Set some paramters: full volume, first speaker that we can find.
   477      * Ignore the other settings for now.
   478      */
   479     paud_change.input = AUDIO_IGNORE;   /* the new input source */
   480     paud_change.output = OUTPUT_1;      /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */
   481     paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
   482     paud_change.volume = 0x7fffffff;    /* volume level [0-0x7fffffff] */
   483     paud_change.volume_delay = AUDIO_IGNORE;    /* the new volume delay */
   484     paud_change.balance = 0x3fffffff;   /* the new balance */
   485     paud_change.balance_delay = AUDIO_IGNORE;   /* the new balance delay */
   486     paud_change.treble = AUDIO_IGNORE;  /* the new treble state */
   487     paud_change.bass = AUDIO_IGNORE;    /* the new bass state */
   488     paud_change.pitch = AUDIO_IGNORE;   /* the new pitch state */
   489 
   490     paud_control.ioctl_request = AUDIO_CHANGE;
   491     paud_control.request_info = (char *) &paud_change;
   492     if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
   493 #ifdef DEBUG_AUDIO
   494         fprintf(stderr, "Can't change audio display settings\n");
   495 #endif
   496     }
   497 
   498     /*
   499      * Tell the device to expect data. Actual start will wait for
   500      * the first write() call.
   501      */
   502     paud_control.ioctl_request = AUDIO_START;
   503     paud_control.position = 0;
   504     if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
   505         PAUDIO_CloseDevice(this);
   506 #ifdef DEBUG_AUDIO
   507         fprintf(stderr, "Can't start audio play\n");
   508 #endif
   509         SDL_SetError("Can't start audio play");
   510         return 0;
   511     }
   512 
   513     /* Check to see if we need to use select() workaround */
   514     if (workaround != NULL) {
   515         this->hidden->frame_ticks = (float) (this->spec.samples * 1000) /
   516             this->spec.freq;
   517         this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
   518     }
   519 
   520     /* We're ready to rock and roll. :-) */
   521     return 1;
   522 }
   523 
   524 static int
   525 PAUDIO_Init(SDL_AudioDriverImpl * impl)
   526 {
   527     /* !!! FIXME: not right for device enum? */
   528     int fd = OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
   529     if (fd < 0) {
   530         SDL_SetError("PAUDIO: Couldn't open audio device");
   531         return 0;
   532     }
   533     close(fd);
   534 
   535     /* Set the function pointers */
   536     impl->OpenDevice = DSP_OpenDevice;
   537     impl->PlayDevice = DSP_PlayDevice;
   538     impl->PlayDevice = DSP_WaitDevice;
   539     impl->GetDeviceBuf = DSP_GetDeviceBuf;
   540     impl->CloseDevice = DSP_CloseDevice;
   541     impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: add device enum! */
   542 
   543     return 1;   /* this audio target is available. */
   544 }
   545 
   546 AudioBootStrap PAUDIO_bootstrap = {
   547     "paud", "AIX Paudio", PAUDIO_Init, 0
   548 };
   549 
   550 #endif /* SDL_AUDIO_DRIVER_PAUDIO */
   551 
   552 /* vi: set ts=4 sw=4 expandtab: */