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