src/audio/qsa/SDL_qsa_audio.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
child 11046 8dc8b2ed306e
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

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