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