src/audio/qsa/SDL_qsa_audio.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Aug 2009 18:39:57 +0000
changeset 3227 458e53d8662c
parent 3139 7f684f249ec9
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 Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 
    22     QNX Sound Architecture (QSA) SDL driver
    23     Copyright (C) 2009 Mike Gorchak
    24     (mike@malva.ua, lestat@i.com.ua)
    25 */
    26 
    27 #include "SDL_config.h"
    28 
    29 #include <errno.h>
    30 #include <unistd.h>
    31 #include <fcntl.h>
    32 #include <signal.h>
    33 #include <sys/types.h>
    34 #include <sys/time.h>
    35 #include <sched.h>
    36 #include <sys/select.h>
    37 #include <sys/neutrino.h>
    38 #include <sys/asoundlib.h>
    39 
    40 #include "SDL_timer.h"
    41 #include "SDL_audio.h"
    42 #include "../SDL_audiomem.h"
    43 #include "../SDL_audio_c.h"
    44 #include "SDL_qsa_audio.h"
    45 
    46 /* The tag name used by QSA audio framework */
    47 #define DRIVER_NAME "qsa"
    48 
    49 /* default channel communication parameters */
    50 #define DEFAULT_CPARAMS_RATE   44100
    51 #define DEFAULT_CPARAMS_VOICES 1
    52 
    53 #define DEFAULT_CPARAMS_FRAG_SIZE 4096
    54 #define DEFAULT_CPARAMS_FRAGS_MIN 1
    55 #define DEFAULT_CPARAMS_FRAGS_MAX 1
    56 
    57 #define QSA_NO_WORKAROUNDS  0x00000000
    58 #define QSA_MMAP_WORKAROUND 0x00000001
    59 
    60 struct BuggyCards
    61 {
    62     char *cardname;
    63     unsigned long bugtype;
    64 };
    65 
    66 #define QSA_WA_CARDS             3
    67 #define QSA_MAX_CARD_NAME_LENGTH 33
    68 
    69 struct BuggyCards buggycards[QSA_WA_CARDS] = {
    70     {"Sound Blaster Live!", QSA_MMAP_WORKAROUND},
    71     {"Vortex 8820", QSA_MMAP_WORKAROUND},
    72     {"Vortex 8830", QSA_MMAP_WORKAROUND},
    73 };
    74 
    75 /* List of found devices */
    76 #define QSA_MAX_DEVICES       32
    77 #define QSA_MAX_NAME_LENGTH   81+16     /* Hardcoded in QSA, can't be changed */
    78 
    79 typedef struct _QSA_Device
    80 {
    81     char name[QSA_MAX_NAME_LENGTH];     /* Long audio device name for SDL  */
    82     int cardno;
    83     int deviceno;
    84 } QSA_Device;
    85 
    86 QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
    87 uint32_t qsa_playback_devices;
    88 
    89 QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
    90 uint32_t qsa_capture_devices;
    91 
    92 static inline void
    93 QSA_SetError(const char *fn, int status)
    94 {
    95     SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
    96 }
    97 
    98 /* card names check to apply the workarounds */
    99 static int
   100 QSA_CheckBuggyCards(_THIS, unsigned long checkfor)
   101 {
   102     char scardname[QSA_MAX_CARD_NAME_LENGTH];
   103     int it;
   104 
   105     if (snd_card_get_name
   106         (this->hidden->cardno, scardname, QSA_MAX_CARD_NAME_LENGTH - 1) < 0) {
   107         return 0;
   108     }
   109 
   110     for (it = 0; it < QSA_WA_CARDS; it++) {
   111         if (SDL_strcmp(buggycards[it].cardname, scardname) == 0) {
   112             if (buggycards[it].bugtype == checkfor) {
   113                 return 1;
   114             }
   115         }
   116     }
   117 
   118     return 0;
   119 }
   120 
   121 static void
   122 QSA_ThreadInit(_THIS)
   123 {
   124     struct sched_param param;
   125     int status;
   126 
   127     /* Increase default 10 priority to 25 to avoid jerky sound */
   128     status = SchedGet(0, 0, &param);
   129     param.sched_priority = param.sched_curpriority + 15;
   130     status = SchedSet(0, 0, SCHED_NOCHANGE, &param);
   131 }
   132 
   133 /* PCM channel parameters initialize function */
   134 static void
   135 QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
   136 {
   137     SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t));
   138 
   139     cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
   140     cpars->mode = SND_PCM_MODE_BLOCK;
   141     cpars->start_mode = SND_PCM_START_DATA;
   142     cpars->stop_mode = SND_PCM_STOP_STOP;
   143     cpars->format.format = SND_PCM_SFMT_S16_LE;
   144     cpars->format.interleave = 1;
   145     cpars->format.rate = DEFAULT_CPARAMS_RATE;
   146     cpars->format.voices = DEFAULT_CPARAMS_VOICES;
   147     cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
   148     cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
   149     cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
   150 }
   151 
   152 /* This function waits until it is possible to write a full sound buffer */
   153 static void
   154 QSA_WaitDevice(_THIS)
   155 {
   156     fd_set wfds;
   157     fd_set rfds;
   158     int selectret;
   159     struct timeval timeout;
   160 
   161     if (!this->hidden->iscapture) {
   162         FD_ZERO(&wfds);
   163         FD_SET(this->hidden->audio_fd, &wfds);
   164     } else {
   165         FD_ZERO(&rfds);
   166         FD_SET(this->hidden->audio_fd, &rfds);
   167     }
   168 
   169     do {
   170         /* Setup timeout for playing one fragment equal to 2 seconds          */
   171         /* If timeout occured than something wrong with hardware or driver    */
   172         /* For example, Vortex 8820 audio driver stucks on second DAC because */
   173         /* it doesn't exist !                                                 */
   174         timeout.tv_sec = 2;
   175         timeout.tv_usec = 0;
   176         this->hidden->timeout_on_wait = 0;
   177 
   178         if (!this->hidden->iscapture) {
   179             selectret =
   180                 select(this->hidden->audio_fd + 1, NULL, &wfds, NULL,
   181                        &timeout);
   182         } else {
   183             selectret =
   184                 select(this->hidden->audio_fd + 1, &rfds, NULL, NULL,
   185                        &timeout);
   186         }
   187 
   188         switch (selectret) {
   189         case -1:
   190             {
   191                 SDL_SetError("QSA: select() failed: %s\n", strerror(errno));
   192                 return;
   193             }
   194             break;
   195         case 0:
   196             {
   197                 SDL_SetError("QSA: timeout on buffer waiting occured\n");
   198                 this->hidden->timeout_on_wait = 1;
   199                 return;
   200             }
   201             break;
   202         default:
   203             {
   204                 if (!this->hidden->iscapture) {
   205                     if (FD_ISSET(this->hidden->audio_fd, &wfds)) {
   206                         return;
   207                     }
   208                 } else {
   209                     if (FD_ISSET(this->hidden->audio_fd, &rfds)) {
   210                         return;
   211                     }
   212                 }
   213             }
   214             break;
   215         }
   216     } while (1);
   217 }
   218 
   219 static void
   220 QSA_PlayDevice(_THIS)
   221 {
   222     snd_pcm_channel_status_t cstatus;
   223     int written;
   224     int status;
   225     int towrite;
   226     void *pcmbuffer;
   227 
   228     if ((!this->enabled) || (!this->hidden)) {
   229         return;
   230     }
   231 
   232     towrite = this->spec.size;
   233     pcmbuffer = this->hidden->pcm_buf;
   234 
   235     /* Write the audio data, checking for EAGAIN (buffer full) and underrun */
   236     do {
   237         written =
   238             snd_pcm_plugin_write(this->hidden->audio_handle, pcmbuffer,
   239                                  towrite);
   240         if (written != towrite) {
   241             /* Check if samples playback got stuck somewhere in hardware or in */
   242             /* the audio device driver */
   243             if ((errno == EAGAIN) && (written == 0)) {
   244                 if (this->hidden->timeout_on_wait != 0) {
   245                     SDL_SetError("QSA: buffer playback timeout\n");
   246                     return;
   247                 }
   248             }
   249 
   250             /* Check for errors or conditions */
   251             if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
   252                 /* Let a little CPU time go by and try to write again */
   253                 SDL_Delay(1);
   254 
   255                 /* if we wrote some data */
   256                 towrite -= written;
   257                 pcmbuffer += written * this->spec.channels;
   258                 continue;
   259             } else {
   260                 if ((errno == EINVAL) || (errno == EIO)) {
   261                     SDL_memset(&cstatus, 0, sizeof(cstatus));
   262                     if (!this->hidden->iscapture) {
   263                         cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
   264                     } else {
   265                         cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
   266                     }
   267 
   268                     status =
   269                         snd_pcm_plugin_status(this->hidden->audio_handle,
   270                                               &cstatus);
   271                     if (status < 0) {
   272                         QSA_SetError("snd_pcm_plugin_status", status);
   273                         return;
   274                     }
   275 
   276                     if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
   277                         (cstatus.status == SND_PCM_STATUS_READY)) {
   278                         if (!this->hidden->iscapture) {
   279                             status =
   280                                 snd_pcm_plugin_prepare(this->hidden->
   281                                                        audio_handle,
   282                                                        SND_PCM_CHANNEL_PLAYBACK);
   283                         } else {
   284                             status =
   285                                 snd_pcm_plugin_prepare(this->hidden->
   286                                                        audio_handle,
   287                                                        SND_PCM_CHANNEL_CAPTURE);
   288                         }
   289                         if (status < 0) {
   290                             QSA_SetError("snd_pcm_plugin_prepare", status);
   291                             return;
   292                         }
   293                     }
   294                     continue;
   295                 } else {
   296                     return;
   297                 }
   298             }
   299         } else {
   300             /* we wrote all remaining data */
   301             towrite -= written;
   302             pcmbuffer += written * this->spec.channels;
   303         }
   304     } while ((towrite > 0) && (this->enabled));
   305 
   306     /* If we couldn't write, assume fatal error for now */
   307     if (towrite != 0) {
   308         this->enabled = 0;
   309     }
   310 }
   311 
   312 static Uint8 *
   313 QSA_GetDeviceBuf(_THIS)
   314 {
   315     return this->hidden->pcm_buf;
   316 }
   317 
   318 static void
   319 QSA_CloseDevice(_THIS)
   320 {
   321     if (this->hidden != NULL) {
   322         if (this->hidden->audio_handle != NULL) {
   323             if (!this->hidden->iscapture) {
   324                 /* Finish playing available samples */
   325                 snd_pcm_plugin_flush(this->hidden->audio_handle,
   326                                      SND_PCM_CHANNEL_PLAYBACK);
   327             } else {
   328                 /* Cancel unread samples during capture */
   329                 snd_pcm_plugin_flush(this->hidden->audio_handle,
   330                                      SND_PCM_CHANNEL_CAPTURE);
   331             }
   332             snd_pcm_close(this->hidden->audio_handle);
   333             this->hidden->audio_handle = NULL;
   334         }
   335 
   336         if (this->hidden->pcm_buf != NULL) {
   337             SDL_FreeAudioMem(this->hidden->pcm_buf);
   338             this->hidden->pcm_buf = NULL;
   339         }
   340 
   341         SDL_free(this->hidden);
   342         this->hidden = NULL;
   343     }
   344 }
   345 
   346 static int
   347 QSA_OpenDevice(_THIS, const char *devname, int iscapture)
   348 {
   349     int status = 0;
   350     int format = 0;
   351     SDL_AudioFormat test_format = 0;
   352     int found = 0;
   353     snd_pcm_channel_setup_t csetup;
   354     snd_pcm_channel_params_t cparams;
   355 
   356     /* Initialize all variables that we clean on shutdown */
   357     this->hidden =
   358         (struct SDL_PrivateAudioData *) SDL_calloc(1,
   359                                                    (sizeof
   360                                                     (struct
   361                                                      SDL_PrivateAudioData)));
   362     if (this->hidden == NULL) {
   363         SDL_OutOfMemory();
   364         return 0;
   365     }
   366     SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData));
   367 
   368     /* Initialize channel transfer parameters to default */
   369     QSA_InitAudioParams(&cparams);
   370 
   371     /* Initialize channel direction: capture or playback */
   372     this->hidden->iscapture = iscapture;
   373 
   374     /* Find deviceid and cardid by device name for playback */
   375     if ((!this->hidden->iscapture) && (devname != NULL)) {
   376         uint32_t device;
   377         int32_t status;
   378 
   379         /* Search in the playback devices */
   380         device = 0;
   381         do {
   382             status = SDL_strcmp(qsa_playback_device[device].name, devname);
   383             if (status == 0) {
   384                 /* Found requested device */
   385                 this->hidden->deviceno = qsa_playback_device[device].deviceno;
   386                 this->hidden->cardno = qsa_playback_device[device].cardno;
   387                 break;
   388             }
   389             device++;
   390             if (device >= qsa_playback_devices) {
   391                 QSA_CloseDevice(this);
   392                 SDL_SetError("No such playback device");
   393                 return 0;
   394             }
   395         } while (1);
   396     }
   397 
   398     /* Find deviceid and cardid by device name for capture */
   399     if ((this->hidden->iscapture) && (devname != NULL)) {
   400         /* Search in the capture devices */
   401         uint32_t device;
   402         int32_t status;
   403 
   404         /* Searching in the playback devices */
   405         device = 0;
   406         do {
   407             status = SDL_strcmp(qsa_capture_device[device].name, devname);
   408             if (status == 0) {
   409                 /* Found requested device */
   410                 this->hidden->deviceno = qsa_capture_device[device].deviceno;
   411                 this->hidden->cardno = qsa_capture_device[device].cardno;
   412                 break;
   413             }
   414             device++;
   415             if (device >= qsa_capture_devices) {
   416                 QSA_CloseDevice(this);
   417                 SDL_SetError("No such capture device");
   418                 return 0;
   419             }
   420         } while (1);
   421     }
   422 
   423     /* Check if SDL requested default audio device */
   424     if (devname == NULL) {
   425         /* Open system default audio device */
   426         if (!this->hidden->iscapture) {
   427             status = snd_pcm_open_preferred(&this->hidden->audio_handle,
   428                                             &this->hidden->cardno,
   429                                             &this->hidden->deviceno,
   430                                             SND_PCM_OPEN_PLAYBACK);
   431         } else {
   432             status = snd_pcm_open_preferred(&this->hidden->audio_handle,
   433                                             &this->hidden->cardno,
   434                                             &this->hidden->deviceno,
   435                                             SND_PCM_OPEN_CAPTURE);
   436         }
   437     } else {
   438         /* Open requested audio device */
   439         if (!this->hidden->iscapture) {
   440             status =
   441                 snd_pcm_open(&this->hidden->audio_handle,
   442                              this->hidden->cardno, this->hidden->deviceno,
   443                              SND_PCM_OPEN_PLAYBACK);
   444         } else {
   445             status =
   446                 snd_pcm_open(&this->hidden->audio_handle,
   447                              this->hidden->cardno, this->hidden->deviceno,
   448                              SND_PCM_OPEN_CAPTURE);
   449         }
   450     }
   451 
   452     /* Check if requested device is opened */
   453     if (status < 0) {
   454         this->hidden->audio_handle = NULL;
   455         QSA_CloseDevice(this);
   456         QSA_SetError("snd_pcm_open", status);
   457         return 0;
   458     }
   459 
   460     if (!QSA_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) {
   461         /* Disable QSA MMAP plugin for buggy audio drivers */
   462         status =
   463             snd_pcm_plugin_set_disable(this->hidden->audio_handle,
   464                                        PLUGIN_DISABLE_MMAP);
   465         if (status < 0) {
   466             QSA_CloseDevice(this);
   467             QSA_SetError("snd_pcm_plugin_set_disable", status);
   468             return 0;
   469         }
   470     }
   471 
   472     /* Try for a closest match on audio format */
   473     format = 0;
   474     /* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */
   475     found = 0;
   476 
   477     for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) {
   478         /* if match found set format to equivalent QSA format */
   479         switch (test_format) {
   480         case AUDIO_U8:
   481             {
   482                 format = SND_PCM_SFMT_U8;
   483                 found = 1;
   484             }
   485             break;
   486         case AUDIO_S8:
   487             {
   488                 format = SND_PCM_SFMT_S8;
   489                 found = 1;
   490             }
   491             break;
   492         case AUDIO_S16LSB:
   493             {
   494                 format = SND_PCM_SFMT_S16_LE;
   495                 found = 1;
   496             }
   497             break;
   498         case AUDIO_S16MSB:
   499             {
   500                 format = SND_PCM_SFMT_S16_BE;
   501                 found = 1;
   502             }
   503             break;
   504         case AUDIO_U16LSB:
   505             {
   506                 format = SND_PCM_SFMT_U16_LE;
   507                 found = 1;
   508             }
   509             break;
   510         case AUDIO_U16MSB:
   511             {
   512                 format = SND_PCM_SFMT_U16_BE;
   513                 found = 1;
   514             }
   515             break;
   516         case AUDIO_S32LSB:
   517             {
   518                 format = SND_PCM_SFMT_S32_LE;
   519                 found = 1;
   520             }
   521             break;
   522         case AUDIO_S32MSB:
   523             {
   524                 format = SND_PCM_SFMT_S32_BE;
   525                 found = 1;
   526             }
   527             break;
   528         case AUDIO_F32LSB:
   529             {
   530                 format = SND_PCM_SFMT_FLOAT_LE;
   531                 found = 1;
   532             }
   533             break;
   534         case AUDIO_F32MSB:
   535             {
   536                 format = SND_PCM_SFMT_FLOAT_BE;
   537                 found = 1;
   538             }
   539             break;
   540         default:
   541             {
   542                 break;
   543             }
   544         }
   545 
   546         if (!found) {
   547             test_format = SDL_NextAudioFormat();
   548         }
   549     }
   550 
   551     /* assumes test_format not 0 on success */
   552     if (test_format == 0) {
   553         QSA_CloseDevice(this);
   554         SDL_SetError("QSA: Couldn't find any hardware audio formats");
   555         return 0;
   556     }
   557 
   558     this->spec.format = test_format;
   559 
   560     /* Set the audio format */
   561     cparams.format.format = format;
   562 
   563     /* Set mono/stereo/4ch/6ch/8ch audio */
   564     cparams.format.voices = this->spec.channels;
   565 
   566     /* Set rate */
   567     cparams.format.rate = this->spec.freq;
   568 
   569     /* Setup the transfer parameters according to cparams */
   570     status = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams);
   571     if (status < 0) {
   572         QSA_CloseDevice(this);
   573         QSA_SetError("snd_pcm_channel_params", status);
   574         return 0;
   575     }
   576 
   577     /* Make sure channel is setup right one last time */
   578     SDL_memset(&csetup, '\0', sizeof(csetup));
   579     if (!this->hidden->iscapture) {
   580         csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
   581     } else {
   582         csetup.channel = SND_PCM_CHANNEL_CAPTURE;
   583     }
   584 
   585     /* Setup an audio channel */
   586     if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
   587         QSA_CloseDevice(this);
   588         SDL_SetError("QSA: Unable to setup channel\n");
   589         return 0;
   590     }
   591 
   592     /* Calculate the final parameters for this audio specification */
   593     SDL_CalculateAudioSpec(&this->spec);
   594 
   595     this->hidden->pcm_len = this->spec.size;
   596 
   597     if (this->hidden->pcm_len == 0) {
   598         this->hidden->pcm_len =
   599             csetup.buf.block.frag_size * this->spec.channels *
   600             (snd_pcm_format_width(format) / 8);
   601     }
   602 
   603     /*
   604      * Allocate memory to the audio buffer and initialize with silence
   605      *  (Note that buffer size must be a multiple of fragment size, so find
   606      *  closest multiple)
   607      */
   608     this->hidden->pcm_buf =
   609         (Uint8 *) SDL_AllocAudioMem(this->hidden->pcm_len);
   610     if (this->hidden->pcm_buf == NULL) {
   611         QSA_CloseDevice(this);
   612         SDL_OutOfMemory();
   613         return 0;
   614     }
   615     SDL_memset(this->hidden->pcm_buf, this->spec.silence,
   616                this->hidden->pcm_len);
   617 
   618     /* get the file descriptor */
   619     if (!this->hidden->iscapture) {
   620         this->hidden->audio_fd =
   621             snd_pcm_file_descriptor(this->hidden->audio_handle,
   622                                     SND_PCM_CHANNEL_PLAYBACK);
   623     } else {
   624         this->hidden->audio_fd =
   625             snd_pcm_file_descriptor(this->hidden->audio_handle,
   626                                     SND_PCM_CHANNEL_CAPTURE);
   627     }
   628 
   629     if (this->hidden->audio_fd < 0) {
   630         QSA_CloseDevice(this);
   631         QSA_SetError("snd_pcm_file_descriptor", status);
   632         return 0;
   633     }
   634 
   635     /* Prepare an audio channel */
   636     if (!this->hidden->iscapture) {
   637         /* Prepare audio playback */
   638         status =
   639             snd_pcm_plugin_prepare(this->hidden->audio_handle,
   640                                    SND_PCM_CHANNEL_PLAYBACK);
   641     } else {
   642         /* Prepare audio capture */
   643         status =
   644             snd_pcm_plugin_prepare(this->hidden->audio_handle,
   645                                    SND_PCM_CHANNEL_CAPTURE);
   646     }
   647 
   648     if (status < 0) {
   649         QSA_CloseDevice(this);
   650         QSA_SetError("snd_pcm_plugin_prepare", status);
   651         return 0;
   652     }
   653 
   654     /* We're really ready to rock and roll. :-) */
   655     return 1;
   656 }
   657 
   658 int
   659 QSA_DetectDevices(int iscapture)
   660 {
   661     uint32_t it;
   662     uint32_t cards;
   663     uint32_t devices;
   664     int32_t status;
   665 
   666     /* Detect amount of available devices       */
   667     /* this value can be changed in the runtime */
   668     cards = snd_cards();
   669 
   670     /* If io-audio manager is not running we will get 0 as number */
   671     /* of available audio devices                                 */
   672     if (cards == 0) {
   673         /* We have no any available audio devices */
   674         return 0;
   675     }
   676 
   677     /* Find requested devices by type */
   678     if (!iscapture) {
   679         /* Playback devices enumeration requested */
   680         for (it = 0; it < cards; it++) {
   681             devices = 0;
   682             do {
   683                 status =
   684                     snd_card_get_longname(it,
   685                                           qsa_playback_device
   686                                           [qsa_playback_devices].name,
   687                                           QSA_MAX_NAME_LENGTH);
   688                 if (status == EOK) {
   689                     snd_pcm_t *handle;
   690 
   691                     /* Add device number to device name */
   692                     sprintf(qsa_playback_device[qsa_playback_devices].name +
   693                             SDL_strlen(qsa_playback_device
   694                                        [qsa_playback_devices].name), " d%d",
   695                             devices);
   696 
   697                     /* Store associated card number id */
   698                     qsa_playback_device[qsa_playback_devices].cardno = it;
   699 
   700                     /* Check if this device id could play anything */
   701                     status =
   702                         snd_pcm_open(&handle, it, devices,
   703                                      SND_PCM_OPEN_PLAYBACK);
   704                     if (status == EOK) {
   705                         qsa_playback_device[qsa_playback_devices].deviceno =
   706                             devices;
   707                         status = snd_pcm_close(handle);
   708                         if (status == EOK) {
   709                             qsa_playback_devices++;
   710                         }
   711                     } else {
   712                         /* Check if we got end of devices list */
   713                         if (status == -ENOENT) {
   714                             break;
   715                         }
   716                     }
   717                 } else {
   718                     break;
   719                 }
   720 
   721                 /* Check if we reached maximum devices count */
   722                 if (qsa_playback_devices >= QSA_MAX_DEVICES) {
   723                     break;
   724                 }
   725                 devices++;
   726             } while (1);
   727 
   728             /* Check if we reached maximum devices count */
   729             if (qsa_playback_devices >= QSA_MAX_DEVICES) {
   730                 break;
   731             }
   732         }
   733     } else {
   734         /* Capture devices enumeration requested */
   735         for (it = 0; it < cards; it++) {
   736             devices = 0;
   737             do {
   738                 status =
   739                     snd_card_get_longname(it,
   740                                           qsa_capture_device
   741                                           [qsa_capture_devices].name,
   742                                           QSA_MAX_NAME_LENGTH);
   743                 if (status == EOK) {
   744                     snd_pcm_t *handle;
   745 
   746                     /* Add device number to device name */
   747                     sprintf(qsa_capture_device[qsa_capture_devices].name +
   748                             SDL_strlen(qsa_capture_device
   749                                        [qsa_capture_devices].name), " d%d",
   750                             devices);
   751 
   752                     /* Store associated card number id */
   753                     qsa_capture_device[qsa_capture_devices].cardno = it;
   754 
   755                     /* Check if this device id could play anything */
   756                     status =
   757                         snd_pcm_open(&handle, it, devices,
   758                                      SND_PCM_OPEN_CAPTURE);
   759                     if (status == EOK) {
   760                         qsa_capture_device[qsa_capture_devices].deviceno =
   761                             devices;
   762                         status = snd_pcm_close(handle);
   763                         if (status == EOK) {
   764                             qsa_capture_devices++;
   765                         }
   766                     } else {
   767                         /* Check if we got end of devices list */
   768                         if (status == -ENOENT) {
   769                             break;
   770                         }
   771                     }
   772 
   773                     /* Check if we reached maximum devices count */
   774                     if (qsa_capture_devices >= QSA_MAX_DEVICES) {
   775                         break;
   776                     }
   777                 } else {
   778                     break;
   779                 }
   780                 devices++;
   781             } while (1);
   782 
   783             /* Check if we reached maximum devices count */
   784             if (qsa_capture_devices >= QSA_MAX_DEVICES) {
   785                 break;
   786             }
   787         }
   788     }
   789 
   790     /* Return amount of available playback or capture devices */
   791     if (!iscapture) {
   792         return qsa_playback_devices;
   793     } else {
   794         return qsa_capture_devices;
   795     }
   796 }
   797 
   798 const char *
   799 QSA_GetDeviceName(int index, int iscapture)
   800 {
   801     if (!iscapture) {
   802         if (index >= qsa_playback_devices) {
   803             return "No such playback device";
   804         }
   805 
   806         return qsa_playback_device[index].name;
   807     } else {
   808         if (index >= qsa_capture_devices) {
   809             return "No such capture device";
   810         }
   811 
   812         return qsa_capture_device[index].name;
   813     }
   814 }
   815 
   816 void
   817 QSA_WaitDone(_THIS)
   818 {
   819     if (!this->hidden->iscapture) {
   820         if (this->hidden->audio_handle != NULL) {
   821             /* Wait till last fragment is played and stop channel */
   822             snd_pcm_plugin_flush(this->hidden->audio_handle,
   823                                  SND_PCM_CHANNEL_PLAYBACK);
   824         }
   825     } else {
   826         if (this->hidden->audio_handle != NULL) {
   827             /* Discard all unread data and stop channel */
   828             snd_pcm_plugin_flush(this->hidden->audio_handle,
   829                                  SND_PCM_CHANNEL_CAPTURE);
   830         }
   831     }
   832 }
   833 
   834 void
   835 QSA_Deinitialize(void)
   836 {
   837     /* Clear devices array on shutdown */
   838     SDL_memset(qsa_playback_device, 0x00,
   839                sizeof(QSA_Device) * QSA_MAX_DEVICES);
   840     SDL_memset(qsa_capture_device, 0x00,
   841                sizeof(QSA_Device) * QSA_MAX_DEVICES);
   842     qsa_playback_devices = 0;
   843     qsa_capture_devices = 0;
   844 }
   845 
   846 static int
   847 QSA_Init(SDL_AudioDriverImpl * impl)
   848 {
   849     snd_pcm_t *handle = NULL;
   850     int32_t status = 0;
   851 
   852     /* Clear devices array */
   853     SDL_memset(qsa_playback_device, 0x00,
   854                sizeof(QSA_Device) * QSA_MAX_DEVICES);
   855     SDL_memset(qsa_capture_device, 0x00,
   856                sizeof(QSA_Device) * QSA_MAX_DEVICES);
   857     qsa_playback_devices = 0;
   858     qsa_capture_devices = 0;
   859 
   860     /* Set function pointers                                     */
   861     /* DeviceLock and DeviceUnlock functions are used default,   */
   862     /* provided by SDL, which uses pthread_mutex for lock/unlock */
   863     impl->DetectDevices = QSA_DetectDevices;
   864     impl->GetDeviceName = QSA_GetDeviceName;
   865     impl->OpenDevice = QSA_OpenDevice;
   866     impl->ThreadInit = QSA_ThreadInit;
   867     impl->WaitDevice = QSA_WaitDevice;
   868     impl->PlayDevice = QSA_PlayDevice;
   869     impl->GetDeviceBuf = QSA_GetDeviceBuf;
   870     impl->CloseDevice = QSA_CloseDevice;
   871     impl->WaitDone = QSA_WaitDone;
   872     impl->Deinitialize = QSA_Deinitialize;
   873     impl->LockDevice = NULL;
   874     impl->UnlockDevice = NULL;
   875 
   876     impl->OnlyHasDefaultOutputDevice = 0;
   877     impl->ProvidesOwnCallbackThread = 0;
   878     impl->SkipMixerLock = 0;
   879     impl->HasCaptureSupport = 1;
   880     impl->OnlyHasDefaultOutputDevice = 0;
   881     impl->OnlyHasDefaultInputDevice = 0;
   882 
   883     /* Check if io-audio manager is running or not */
   884     status = snd_cards();
   885     if (status == 0) {
   886         /* if no, return immediately */
   887         return 1;
   888     }
   889 
   890     /* At this point we are definitely has an audio device */
   891     return 2;
   892 }
   893 
   894 AudioBootStrap QSAAUDIO_bootstrap = {
   895     DRIVER_NAME, "QNX QSA Audio", QSA_Init, 0
   896 };
   897 
   898 /* vi: set ts=4 sw=4 expandtab: */