cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_qsa_audio.c (27766B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
      4
      5  This software is provided 'as-is', without any express or implied
      6  warranty.  In no event will the authors be held liable for any damages
      7  arising from the use of this software.
      8
      9  Permission is granted to anyone to use this software for any purpose,
     10  including commercial applications, and to alter it and redistribute it
     11  freely, subject to the following restrictions:
     12
     13  1. The origin of this software must not be misrepresented; you must not
     14     claim that you wrote the original software. If you use this software
     15     in a product, an acknowledgment in the product documentation would be
     16     appreciated but is not required.
     17  2. Altered source versions must be plainly marked as such, and must not be
     18     misrepresented as being the original software.
     19  3. This notice may not be removed or altered from any source distribution.
     20*/
     21
     22#include "../../SDL_internal.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
     54struct 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
     63struct 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
     73typedef 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
     80QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
     81uint32_t qsa_playback_devices;
     82
     83QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
     84uint32_t qsa_capture_devices;
     85
     86static SDL_INLINE int
     87QSA_SetError(const char *fn, int status)
     88{
     89    return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
     90}
     91
     92/* card names check to apply the workarounds */
     93static int
     94QSA_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? */
    116static void
    117QSA_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 */
    129static void
    130QSA_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 */
    148static void
    149QSA_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", strerror(errno));
    187                return;
    188            }
    189            break;
    190        case 0:
    191            {
    192                SDL_SetError("QSA: timeout on buffer waiting occured");
    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
    214static void
    215QSA_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");
    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
    307static Uint8 *
    308QSA_GetDeviceBuf(_THIS)
    309{
    310    return this->hidden->pcm_buf;
    311}
    312
    313static void
    314QSA_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        SDL_FreeAudioMem(this->hidden->pcm_buf);
    332        this->hidden->pcm_buf = NULL;
    333
    334        SDL_free(this->hidden);
    335        this->hidden = NULL;
    336    }
    337}
    338
    339static int
    340QSA_OpenDevice(_THIS, const char *devname, int iscapture)
    341{
    342    int status = 0;
    343    int format = 0;
    344    SDL_AudioFormat test_format = 0;
    345    int found = 0;
    346    snd_pcm_channel_setup_t csetup;
    347    snd_pcm_channel_params_t cparams;
    348
    349    /* Initialize all variables that we clean on shutdown */
    350    this->hidden =
    351        (struct SDL_PrivateAudioData *) SDL_calloc(1,
    352                                                   (sizeof
    353                                                    (struct
    354                                                     SDL_PrivateAudioData)));
    355    if (this->hidden == NULL) {
    356        return SDL_OutOfMemory();
    357    }
    358    SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData));
    359
    360    /* Initialize channel transfer parameters to default */
    361    QSA_InitAudioParams(&cparams);
    362
    363    /* Initialize channel direction: capture or playback */
    364    this->hidden->iscapture = iscapture;
    365
    366    /* Find deviceid and cardid by device name for playback */
    367    if ((!this->hidden->iscapture) && (devname != NULL)) {
    368        uint32_t device;
    369        int32_t status;
    370
    371        /* Search in the playback devices */
    372        device = 0;
    373        do {
    374            status = SDL_strcmp(qsa_playback_device[device].name, devname);
    375            if (status == 0) {
    376                /* Found requested device */
    377                this->hidden->deviceno = qsa_playback_device[device].deviceno;
    378                this->hidden->cardno = qsa_playback_device[device].cardno;
    379                break;
    380            }
    381            device++;
    382            if (device >= qsa_playback_devices) {
    383                QSA_CloseDevice(this);
    384                return SDL_SetError("No such playback device");
    385            }
    386        } while (1);
    387    }
    388
    389    /* Find deviceid and cardid by device name for capture */
    390    if ((this->hidden->iscapture) && (devname != NULL)) {
    391        /* Search in the capture devices */
    392        uint32_t device;
    393        int32_t status;
    394
    395        /* Searching in the playback devices */
    396        device = 0;
    397        do {
    398            status = SDL_strcmp(qsa_capture_device[device].name, devname);
    399            if (status == 0) {
    400                /* Found requested device */
    401                this->hidden->deviceno = qsa_capture_device[device].deviceno;
    402                this->hidden->cardno = qsa_capture_device[device].cardno;
    403                break;
    404            }
    405            device++;
    406            if (device >= qsa_capture_devices) {
    407                QSA_CloseDevice(this);
    408                return SDL_SetError("No such capture device");
    409            }
    410        } while (1);
    411    }
    412
    413    /* Check if SDL requested default audio device */
    414    if (devname == NULL) {
    415        /* Open system default audio device */
    416        if (!this->hidden->iscapture) {
    417            status = snd_pcm_open_preferred(&this->hidden->audio_handle,
    418                                            &this->hidden->cardno,
    419                                            &this->hidden->deviceno,
    420                                            SND_PCM_OPEN_PLAYBACK);
    421        } else {
    422            status = snd_pcm_open_preferred(&this->hidden->audio_handle,
    423                                            &this->hidden->cardno,
    424                                            &this->hidden->deviceno,
    425                                            SND_PCM_OPEN_CAPTURE);
    426        }
    427    } else {
    428        /* Open requested audio device */
    429        if (!this->hidden->iscapture) {
    430            status =
    431                snd_pcm_open(&this->hidden->audio_handle,
    432                             this->hidden->cardno, this->hidden->deviceno,
    433                             SND_PCM_OPEN_PLAYBACK);
    434        } else {
    435            status =
    436                snd_pcm_open(&this->hidden->audio_handle,
    437                             this->hidden->cardno, this->hidden->deviceno,
    438                             SND_PCM_OPEN_CAPTURE);
    439        }
    440    }
    441
    442    /* Check if requested device is opened */
    443    if (status < 0) {
    444        this->hidden->audio_handle = NULL;
    445        QSA_CloseDevice(this);
    446        return QSA_SetError("snd_pcm_open", status);
    447    }
    448
    449    if (!QSA_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) {
    450        /* Disable QSA MMAP plugin for buggy audio drivers */
    451        status =
    452            snd_pcm_plugin_set_disable(this->hidden->audio_handle,
    453                                       PLUGIN_DISABLE_MMAP);
    454        if (status < 0) {
    455            QSA_CloseDevice(this);
    456            return QSA_SetError("snd_pcm_plugin_set_disable", status);
    457        }
    458    }
    459
    460    /* Try for a closest match on audio format */
    461    format = 0;
    462    /* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */
    463    found = 0;
    464
    465    for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) {
    466        /* if match found set format to equivalent QSA format */
    467        switch (test_format) {
    468        case AUDIO_U8:
    469            {
    470                format = SND_PCM_SFMT_U8;
    471                found = 1;
    472            }
    473            break;
    474        case AUDIO_S8:
    475            {
    476                format = SND_PCM_SFMT_S8;
    477                found = 1;
    478            }
    479            break;
    480        case AUDIO_S16LSB:
    481            {
    482                format = SND_PCM_SFMT_S16_LE;
    483                found = 1;
    484            }
    485            break;
    486        case AUDIO_S16MSB:
    487            {
    488                format = SND_PCM_SFMT_S16_BE;
    489                found = 1;
    490            }
    491            break;
    492        case AUDIO_U16LSB:
    493            {
    494                format = SND_PCM_SFMT_U16_LE;
    495                found = 1;
    496            }
    497            break;
    498        case AUDIO_U16MSB:
    499            {
    500                format = SND_PCM_SFMT_U16_BE;
    501                found = 1;
    502            }
    503            break;
    504        case AUDIO_S32LSB:
    505            {
    506                format = SND_PCM_SFMT_S32_LE;
    507                found = 1;
    508            }
    509            break;
    510        case AUDIO_S32MSB:
    511            {
    512                format = SND_PCM_SFMT_S32_BE;
    513                found = 1;
    514            }
    515            break;
    516        case AUDIO_F32LSB:
    517            {
    518                format = SND_PCM_SFMT_FLOAT_LE;
    519                found = 1;
    520            }
    521            break;
    522        case AUDIO_F32MSB:
    523            {
    524                format = SND_PCM_SFMT_FLOAT_BE;
    525                found = 1;
    526            }
    527            break;
    528        default:
    529            {
    530                break;
    531            }
    532        }
    533
    534        if (!found) {
    535            test_format = SDL_NextAudioFormat();
    536        }
    537    }
    538
    539    /* assumes test_format not 0 on success */
    540    if (test_format == 0) {
    541        QSA_CloseDevice(this);
    542        return SDL_SetError("QSA: Couldn't find any hardware audio formats");
    543    }
    544
    545    this->spec.format = test_format;
    546
    547    /* Set the audio format */
    548    cparams.format.format = format;
    549
    550    /* Set mono/stereo/4ch/6ch/8ch audio */
    551    cparams.format.voices = this->spec.channels;
    552
    553    /* Set rate */
    554    cparams.format.rate = this->spec.freq;
    555
    556    /* Setup the transfer parameters according to cparams */
    557    status = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams);
    558    if (status < 0) {
    559        QSA_CloseDevice(this);
    560        return QSA_SetError("snd_pcm_channel_params", status);
    561    }
    562
    563    /* Make sure channel is setup right one last time */
    564    SDL_memset(&csetup, 0, sizeof(csetup));
    565    if (!this->hidden->iscapture) {
    566        csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
    567    } else {
    568        csetup.channel = SND_PCM_CHANNEL_CAPTURE;
    569    }
    570
    571    /* Setup an audio channel */
    572    if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
    573        QSA_CloseDevice(this);
    574        return SDL_SetError("QSA: Unable to setup channel");
    575    }
    576
    577    /* Calculate the final parameters for this audio specification */
    578    SDL_CalculateAudioSpec(&this->spec);
    579
    580    this->hidden->pcm_len = this->spec.size;
    581
    582    if (this->hidden->pcm_len == 0) {
    583        this->hidden->pcm_len =
    584            csetup.buf.block.frag_size * this->spec.channels *
    585            (snd_pcm_format_width(format) / 8);
    586    }
    587
    588    /*
    589     * Allocate memory to the audio buffer and initialize with silence
    590     *  (Note that buffer size must be a multiple of fragment size, so find
    591     *  closest multiple)
    592     */
    593    this->hidden->pcm_buf =
    594        (Uint8 *) SDL_AllocAudioMem(this->hidden->pcm_len);
    595    if (this->hidden->pcm_buf == NULL) {
    596        QSA_CloseDevice(this);
    597        return SDL_OutOfMemory();
    598    }
    599    SDL_memset(this->hidden->pcm_buf, this->spec.silence,
    600               this->hidden->pcm_len);
    601
    602    /* get the file descriptor */
    603    if (!this->hidden->iscapture) {
    604        this->hidden->audio_fd =
    605            snd_pcm_file_descriptor(this->hidden->audio_handle,
    606                                    SND_PCM_CHANNEL_PLAYBACK);
    607    } else {
    608        this->hidden->audio_fd =
    609            snd_pcm_file_descriptor(this->hidden->audio_handle,
    610                                    SND_PCM_CHANNEL_CAPTURE);
    611    }
    612
    613    if (this->hidden->audio_fd < 0) {
    614        QSA_CloseDevice(this);
    615        return QSA_SetError("snd_pcm_file_descriptor", status);
    616    }
    617
    618    /* Prepare an audio channel */
    619    if (!this->hidden->iscapture) {
    620        /* Prepare audio playback */
    621        status =
    622            snd_pcm_plugin_prepare(this->hidden->audio_handle,
    623                                   SND_PCM_CHANNEL_PLAYBACK);
    624    } else {
    625        /* Prepare audio capture */
    626        status =
    627            snd_pcm_plugin_prepare(this->hidden->audio_handle,
    628                                   SND_PCM_CHANNEL_CAPTURE);
    629    }
    630
    631    if (status < 0) {
    632        QSA_CloseDevice(this);
    633        return QSA_SetError("snd_pcm_plugin_prepare", status);
    634    }
    635
    636    /* We're really ready to rock and roll. :-) */
    637    return 0;
    638}
    639
    640static void
    641QSA_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
    642{
    643    uint32_t it;
    644    uint32_t cards;
    645    uint32_t devices;
    646    int32_t status;
    647
    648    /* Detect amount of available devices       */
    649    /* this value can be changed in the runtime */
    650    cards = snd_cards();
    651
    652    /* If io-audio manager is not running we will get 0 as number */
    653    /* of available audio devices                                 */
    654    if (cards == 0) {
    655        /* We have no any available audio devices */
    656        return;
    657    }
    658
    659    /* Find requested devices by type */
    660    if (!iscapture) {
    661        /* Playback devices enumeration requested */
    662        for (it = 0; it < cards; it++) {
    663            devices = 0;
    664            do {
    665                status =
    666                    snd_card_get_longname(it,
    667                                          qsa_playback_device
    668                                          [qsa_playback_devices].name,
    669                                          QSA_MAX_NAME_LENGTH);
    670                if (status == EOK) {
    671                    snd_pcm_t *handle;
    672
    673                    /* Add device number to device name */
    674                    sprintf(qsa_playback_device[qsa_playback_devices].name +
    675                            SDL_strlen(qsa_playback_device
    676                                       [qsa_playback_devices].name), " d%d",
    677                            devices);
    678
    679                    /* Store associated card number id */
    680                    qsa_playback_device[qsa_playback_devices].cardno = it;
    681
    682                    /* Check if this device id could play anything */
    683                    status =
    684                        snd_pcm_open(&handle, it, devices,
    685                                     SND_PCM_OPEN_PLAYBACK);
    686                    if (status == EOK) {
    687                        qsa_playback_device[qsa_playback_devices].deviceno =
    688                            devices;
    689                        status = snd_pcm_close(handle);
    690                        if (status == EOK) {
    691                            addfn(qsa_playback_device[qsa_playback_devices].name);
    692                            qsa_playback_devices++;
    693                        }
    694                    } else {
    695                        /* Check if we got end of devices list */
    696                        if (status == -ENOENT) {
    697                            break;
    698                        }
    699                    }
    700                } else {
    701                    break;
    702                }
    703
    704                /* Check if we reached maximum devices count */
    705                if (qsa_playback_devices >= QSA_MAX_DEVICES) {
    706                    break;
    707                }
    708                devices++;
    709            } while (1);
    710
    711            /* Check if we reached maximum devices count */
    712            if (qsa_playback_devices >= QSA_MAX_DEVICES) {
    713                break;
    714            }
    715        }
    716    } else {
    717        /* Capture devices enumeration requested */
    718        for (it = 0; it < cards; it++) {
    719            devices = 0;
    720            do {
    721                status =
    722                    snd_card_get_longname(it,
    723                                          qsa_capture_device
    724                                          [qsa_capture_devices].name,
    725                                          QSA_MAX_NAME_LENGTH);
    726                if (status == EOK) {
    727                    snd_pcm_t *handle;
    728
    729                    /* Add device number to device name */
    730                    sprintf(qsa_capture_device[qsa_capture_devices].name +
    731                            SDL_strlen(qsa_capture_device
    732                                       [qsa_capture_devices].name), " d%d",
    733                            devices);
    734
    735                    /* Store associated card number id */
    736                    qsa_capture_device[qsa_capture_devices].cardno = it;
    737
    738                    /* Check if this device id could play anything */
    739                    status =
    740                        snd_pcm_open(&handle, it, devices,
    741                                     SND_PCM_OPEN_CAPTURE);
    742                    if (status == EOK) {
    743                        qsa_capture_device[qsa_capture_devices].deviceno =
    744                            devices;
    745                        status = snd_pcm_close(handle);
    746                        if (status == EOK) {
    747                            addfn(qsa_capture_device[qsa_capture_devices].name);
    748                            qsa_capture_devices++;
    749                        }
    750                    } else {
    751                        /* Check if we got end of devices list */
    752                        if (status == -ENOENT) {
    753                            break;
    754                        }
    755                    }
    756
    757                    /* Check if we reached maximum devices count */
    758                    if (qsa_capture_devices >= QSA_MAX_DEVICES) {
    759                        break;
    760                    }
    761                } else {
    762                    break;
    763                }
    764                devices++;
    765            } while (1);
    766
    767            /* Check if we reached maximum devices count */
    768            if (qsa_capture_devices >= QSA_MAX_DEVICES) {
    769                break;
    770            }
    771        }
    772    }
    773}
    774
    775static void
    776QSA_WaitDone(_THIS)
    777{
    778    if (!this->hidden->iscapture) {
    779        if (this->hidden->audio_handle != NULL) {
    780            /* Wait till last fragment is played and stop channel */
    781            snd_pcm_plugin_flush(this->hidden->audio_handle,
    782                                 SND_PCM_CHANNEL_PLAYBACK);
    783        }
    784    } else {
    785        if (this->hidden->audio_handle != NULL) {
    786            /* Discard all unread data and stop channel */
    787            snd_pcm_plugin_flush(this->hidden->audio_handle,
    788                                 SND_PCM_CHANNEL_CAPTURE);
    789        }
    790    }
    791}
    792
    793static void
    794QSA_Deinitialize(void)
    795{
    796    /* Clear devices array on shutdown */
    797    SDL_memset(qsa_playback_device, 0x00,
    798               sizeof(QSA_Device) * QSA_MAX_DEVICES);
    799    SDL_memset(qsa_capture_device, 0x00,
    800               sizeof(QSA_Device) * QSA_MAX_DEVICES);
    801    qsa_playback_devices = 0;
    802    qsa_capture_devices = 0;
    803}
    804
    805static int
    806QSA_Init(SDL_AudioDriverImpl * impl)
    807{
    808    snd_pcm_t *handle = NULL;
    809    int32_t status = 0;
    810
    811    /* Clear devices array */
    812    SDL_memset(qsa_playback_device, 0x00,
    813               sizeof(QSA_Device) * QSA_MAX_DEVICES);
    814    SDL_memset(qsa_capture_device, 0x00,
    815               sizeof(QSA_Device) * QSA_MAX_DEVICES);
    816    qsa_playback_devices = 0;
    817    qsa_capture_devices = 0;
    818
    819    /* Set function pointers                                     */
    820    /* DeviceLock and DeviceUnlock functions are used default,   */
    821    /* provided by SDL, which uses pthread_mutex for lock/unlock */
    822    impl->DetectDevices = QSA_DetectDevices;
    823    impl->OpenDevice = QSA_OpenDevice;
    824    impl->ThreadInit = QSA_ThreadInit;
    825    impl->WaitDevice = QSA_WaitDevice;
    826    impl->PlayDevice = QSA_PlayDevice;
    827    impl->GetDeviceBuf = QSA_GetDeviceBuf;
    828    impl->CloseDevice = QSA_CloseDevice;
    829    impl->WaitDone = QSA_WaitDone;
    830    impl->Deinitialize = QSA_Deinitialize;
    831    impl->LockDevice = NULL;
    832    impl->UnlockDevice = NULL;
    833
    834    impl->OnlyHasDefaultOutputDevice = 0;
    835    impl->ProvidesOwnCallbackThread = 0;
    836    impl->SkipMixerLock = 0;
    837    impl->HasCaptureSupport = 1;
    838    impl->OnlyHasDefaultOutputDevice = 0;
    839    impl->OnlyHasDefaultInputDevice = 0;
    840
    841    /* Check if io-audio manager is running or not */
    842    status = snd_cards();
    843    if (status == 0) {
    844        /* if no, return immediately */
    845        return 1;
    846    }
    847
    848    return 1;   /* this audio target is available. */
    849}
    850
    851AudioBootStrap QSAAUDIO_bootstrap = {
    852    "qsa", "QNX QSA Audio", QSA_Init, 0
    853};
    854
    855#endif /* SDL_AUDIO_DRIVER_QSA */
    856
    857/* vi: set ts=4 sw=4 expandtab: */