src/audio/qsa/SDL_qsa_audio.c
author Ozkan Sezer <sezeroz@gmail.com>
Wed, 31 Jul 2019 05:11:40 +0300
changeset 12968 0e3948762c96
parent 12503 806492103856
permissions -rw-r--r--
use SDL_zeroa at more places where the argument is an array.
slouken@3099
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@12503
     3
  Copyright (C) 1997-2019 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@11296
    48
#include "../../core/unix/SDL_poll.h"
slouken@3099
    49
#include "../SDL_audio_c.h"
slouken@3099
    50
#include "SDL_qsa_audio.h"
slouken@3099
    51
slouken@3099
    52
/* default channel communication parameters */
slouken@3099
    53
#define DEFAULT_CPARAMS_RATE   44100
slouken@3099
    54
#define DEFAULT_CPARAMS_VOICES 1
slouken@3099
    55
slouken@3099
    56
#define DEFAULT_CPARAMS_FRAG_SIZE 4096
slouken@3099
    57
#define DEFAULT_CPARAMS_FRAGS_MIN 1
slouken@3099
    58
#define DEFAULT_CPARAMS_FRAGS_MAX 1
slouken@3099
    59
slouken@3099
    60
/* List of found devices */
slouken@3099
    61
#define QSA_MAX_DEVICES       32
slouken@3139
    62
#define QSA_MAX_NAME_LENGTH   81+16     /* Hardcoded in QSA, can't be changed */
slouken@3099
    63
slouken@3099
    64
typedef struct _QSA_Device
slouken@3099
    65
{
slouken@3139
    66
    char name[QSA_MAX_NAME_LENGTH];     /* Long audio device name for SDL  */
slouken@3139
    67
    int cardno;
slouken@3139
    68
    int deviceno;
slouken@3099
    69
} QSA_Device;
slouken@3099
    70
slouken@3099
    71
QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
slouken@3139
    72
uint32_t qsa_playback_devices;
slouken@3099
    73
slouken@3099
    74
QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
slouken@3139
    75
uint32_t qsa_capture_devices;
slouken@3099
    76
slouken@7860
    77
static SDL_INLINE int
slouken@3139
    78
QSA_SetError(const char *fn, int status)
slouken@3099
    79
{
icculus@7038
    80
    return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
slouken@3099
    81
}
slouken@3099
    82
icculus@5590
    83
/* !!! FIXME: does this need to be here? Does the SDL version not work? */
slouken@3139
    84
static void
slouken@3139
    85
QSA_ThreadInit(_THIS)
slouken@3099
    86
{
icculus@11155
    87
    /* Increase default 10 priority to 25 to avoid jerky sound */
slouken@3139
    88
    struct sched_param param;
icculus@11155
    89
    if (SchedGet(0, 0, &param) != -1) {
icculus@11155
    90
        param.sched_priority = param.sched_curpriority + 15;
icculus@11155
    91
        SchedSet(0, 0, SCHED_NOCHANGE, &param);
icculus@11155
    92
    }
slouken@3099
    93
}
slouken@3099
    94
slouken@3099
    95
/* PCM channel parameters initialize function */
slouken@3139
    96
static void
slouken@3139
    97
QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
slouken@3099
    98
{
icculus@10257
    99
    SDL_zerop(cpars);
slouken@3139
   100
    cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
slouken@3139
   101
    cpars->mode = SND_PCM_MODE_BLOCK;
slouken@3139
   102
    cpars->start_mode = SND_PCM_START_DATA;
slouken@3139
   103
    cpars->stop_mode = SND_PCM_STOP_STOP;
slouken@3139
   104
    cpars->format.format = SND_PCM_SFMT_S16_LE;
slouken@3139
   105
    cpars->format.interleave = 1;
slouken@3139
   106
    cpars->format.rate = DEFAULT_CPARAMS_RATE;
slouken@3139
   107
    cpars->format.voices = DEFAULT_CPARAMS_VOICES;
slouken@3139
   108
    cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
slouken@3139
   109
    cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
slouken@3139
   110
    cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
slouken@3099
   111
}
slouken@3099
   112
slouken@3099
   113
/* This function waits until it is possible to write a full sound buffer */
slouken@3139
   114
static void
slouken@3139
   115
QSA_WaitDevice(_THIS)
slouken@3099
   116
{
slouken@11296
   117
    int result;
slouken@3099
   118
slouken@11296
   119
    /* Setup timeout for playing one fragment equal to 2 seconds          */
slouken@11296
   120
    /* If timeout occured than something wrong with hardware or driver    */
slouken@11296
   121
    /* For example, Vortex 8820 audio driver stucks on second DAC because */
slouken@11296
   122
    /* it doesn't exist !                                                 */
slouken@11296
   123
    result = SDL_IOReady(this->hidden->audio_fd, !this->hidden->iscapture, 2 * 1000);
slouken@11296
   124
    switch (result) {
slouken@11296
   125
    case -1:
slouken@11296
   126
        SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno));
slouken@11296
   127
        break;
slouken@11296
   128
    case 0:
slouken@11296
   129
        SDL_SetError("QSA: timeout on buffer waiting occured");
slouken@11296
   130
        this->hidden->timeout_on_wait = 1;
slouken@11296
   131
        break;
slouken@11296
   132
    default:
slouken@11296
   133
        this->hidden->timeout_on_wait = 0;
slouken@11296
   134
        break;
slouken@3139
   135
    }
slouken@3099
   136
}
slouken@3099
   137
slouken@3139
   138
static void
slouken@3139
   139
QSA_PlayDevice(_THIS)
slouken@3099
   140
{
slouken@3139
   141
    snd_pcm_channel_status_t cstatus;
slouken@3139
   142
    int written;
slouken@3139
   143
    int status;
slouken@3139
   144
    int towrite;
slouken@3139
   145
    void *pcmbuffer;
slouken@3099
   146
icculus@10238
   147
    if (!SDL_AtomicGet(&this->enabled) || !this->hidden) {
slouken@3139
   148
        return;
slouken@3139
   149
    }
slouken@3099
   150
slouken@3139
   151
    towrite = this->spec.size;
slouken@3139
   152
    pcmbuffer = this->hidden->pcm_buf;
slouken@3099
   153
slouken@3139
   154
    /* Write the audio data, checking for EAGAIN (buffer full) and underrun */
slouken@3139
   155
    do {
slouken@3139
   156
        written =
slouken@3139
   157
            snd_pcm_plugin_write(this->hidden->audio_handle, pcmbuffer,
slouken@3139
   158
                                 towrite);
slouken@3139
   159
        if (written != towrite) {
slouken@3139
   160
            /* Check if samples playback got stuck somewhere in hardware or in */
slouken@3139
   161
            /* the audio device driver */
slouken@3139
   162
            if ((errno == EAGAIN) && (written == 0)) {
slouken@3139
   163
                if (this->hidden->timeout_on_wait != 0) {
icculus@7038
   164
                    SDL_SetError("QSA: buffer playback timeout");
slouken@3139
   165
                    return;
slouken@3139
   166
                }
slouken@3099
   167
            }
slouken@3099
   168
slouken@3139
   169
            /* Check for errors or conditions */
slouken@3139
   170
            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
slouken@3139
   171
                /* Let a little CPU time go by and try to write again */
slouken@3139
   172
                SDL_Delay(1);
slouken@3099
   173
slouken@3139
   174
                /* if we wrote some data */
slouken@3139
   175
                towrite -= written;
slouken@3139
   176
                pcmbuffer += written * this->spec.channels;
slouken@3139
   177
                continue;
slouken@3139
   178
            } else {
slouken@3139
   179
                if ((errno == EINVAL) || (errno == EIO)) {
icculus@10257
   180
                    SDL_zero(cstatus);
slouken@3139
   181
                    if (!this->hidden->iscapture) {
slouken@3139
   182
                        cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
slouken@3139
   183
                    } else {
slouken@3139
   184
                        cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
slouken@3139
   185
                    }
slouken@3099
   186
slouken@3139
   187
                    status =
slouken@3139
   188
                        snd_pcm_plugin_status(this->hidden->audio_handle,
slouken@3139
   189
                                              &cstatus);
slouken@3139
   190
                    if (status < 0) {
slouken@3139
   191
                        QSA_SetError("snd_pcm_plugin_status", status);
slouken@3139
   192
                        return;
slouken@3139
   193
                    }
slouken@3099
   194
slouken@3139
   195
                    if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
slouken@3139
   196
                        (cstatus.status == SND_PCM_STATUS_READY)) {
slouken@3139
   197
                        if (!this->hidden->iscapture) {
slouken@3139
   198
                            status =
slouken@3139
   199
                                snd_pcm_plugin_prepare(this->hidden->
slouken@3139
   200
                                                       audio_handle,
slouken@3139
   201
                                                       SND_PCM_CHANNEL_PLAYBACK);
slouken@3139
   202
                        } else {
slouken@3139
   203
                            status =
slouken@3139
   204
                                snd_pcm_plugin_prepare(this->hidden->
slouken@3139
   205
                                                       audio_handle,
slouken@3139
   206
                                                       SND_PCM_CHANNEL_CAPTURE);
slouken@3139
   207
                        }
slouken@3139
   208
                        if (status < 0) {
slouken@3139
   209
                            QSA_SetError("snd_pcm_plugin_prepare", status);
slouken@3139
   210
                            return;
slouken@3139
   211
                        }
slouken@3139
   212
                    }
slouken@3139
   213
                    continue;
slouken@3139
   214
                } else {
slouken@3139
   215
                    return;
slouken@3139
   216
                }
slouken@3099
   217
            }
slouken@3139
   218
        } else {
slouken@3139
   219
            /* we wrote all remaining data */
slouken@3139
   220
            towrite -= written;
slouken@3139
   221
            pcmbuffer += written * this->spec.channels;
slouken@3139
   222
        }
icculus@10238
   223
    } while ((towrite > 0) && SDL_AtomicGet(&this->enabled));
slouken@3099
   224
slouken@3139
   225
    /* If we couldn't write, assume fatal error for now */
slouken@3139
   226
    if (towrite != 0) {
icculus@9394
   227
        SDL_OpenedAudioDeviceDisconnected(this);
slouken@3139
   228
    }
slouken@3099
   229
}
slouken@3099
   230
slouken@3139
   231
static Uint8 *
slouken@3139
   232
QSA_GetDeviceBuf(_THIS)
slouken@3099
   233
{
slouken@3139
   234
    return this->hidden->pcm_buf;
slouken@3099
   235
}
slouken@3099
   236
slouken@3139
   237
static void
slouken@3139
   238
QSA_CloseDevice(_THIS)
slouken@3099
   239
{
icculus@10255
   240
    if (this->hidden->audio_handle != NULL) {
icculus@10255
   241
        if (!this->hidden->iscapture) {
icculus@10255
   242
            /* Finish playing available samples */
icculus@10255
   243
            snd_pcm_plugin_flush(this->hidden->audio_handle,
icculus@10255
   244
                                 SND_PCM_CHANNEL_PLAYBACK);
icculus@10255
   245
        } else {
icculus@10255
   246
            /* Cancel unread samples during capture */
icculus@10255
   247
            snd_pcm_plugin_flush(this->hidden->audio_handle,
icculus@10255
   248
                                 SND_PCM_CHANNEL_CAPTURE);
slouken@3139
   249
        }
icculus@10255
   250
        snd_pcm_close(this->hidden->audio_handle);
icculus@10255
   251
    }
slouken@3099
   252
icculus@10256
   253
    SDL_free(this->hidden->pcm_buf);
icculus@10255
   254
    SDL_free(this->hidden);
slouken@3099
   255
}
slouken@3099
   256
slouken@3139
   257
static int
icculus@9394
   258
QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
slouken@3099
   259
{
icculus@9394
   260
    const QSA_Device *device = (const QSA_Device *) handle;
slouken@3139
   261
    int status = 0;
slouken@3139
   262
    int format = 0;
slouken@3139
   263
    SDL_AudioFormat test_format = 0;
slouken@3139
   264
    int found = 0;
slouken@3139
   265
    snd_pcm_channel_setup_t csetup;
slouken@3139
   266
    snd_pcm_channel_params_t cparams;
slouken@3099
   267
slouken@3139
   268
    /* Initialize all variables that we clean on shutdown */
slouken@3139
   269
    this->hidden =
slouken@3139
   270
        (struct SDL_PrivateAudioData *) SDL_calloc(1,
slouken@3139
   271
                                                   (sizeof
slouken@3139
   272
                                                    (struct
slouken@3139
   273
                                                     SDL_PrivateAudioData)));
slouken@3139
   274
    if (this->hidden == NULL) {
icculus@7038
   275
        return SDL_OutOfMemory();
slouken@3139
   276
    }
slouken@3099
   277
slouken@3139
   278
    /* Initialize channel transfer parameters to default */
slouken@3139
   279
    QSA_InitAudioParams(&cparams);
slouken@3099
   280
slouken@3139
   281
    /* Initialize channel direction: capture or playback */
icculus@10235
   282
    this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
slouken@3099
   283
icculus@9394
   284
    if (device != NULL) {
icculus@9394
   285
        /* Open requested audio device */
icculus@9394
   286
        this->hidden->deviceno = device->deviceno;
icculus@9394
   287
        this->hidden->cardno = device->cardno;
icculus@9394
   288
        status = snd_pcm_open(&this->hidden->audio_handle,
icculus@9394
   289
                              device->cardno, device->deviceno,
icculus@11119
   290
                              iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
icculus@9394
   291
    } else {
slouken@3139
   292
        /* Open system default audio device */
icculus@9394
   293
        status = snd_pcm_open_preferred(&this->hidden->audio_handle,
icculus@9394
   294
                                        &this->hidden->cardno,
icculus@9394
   295
                                        &this->hidden->deviceno,
icculus@11119
   296
                                        iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
slouken@3139
   297
    }
slouken@3139
   298
slouken@3139
   299
    /* Check if requested device is opened */
slouken@3139
   300
    if (status < 0) {
slouken@3139
   301
        this->hidden->audio_handle = NULL;
icculus@7038
   302
        return QSA_SetError("snd_pcm_open", status);
slouken@3139
   303
    }
slouken@3139
   304
slouken@3139
   305
    /* Try for a closest match on audio format */
slouken@3139
   306
    format = 0;
slouken@3139
   307
    /* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */
slouken@3139
   308
    found = 0;
slouken@3139
   309
slouken@3139
   310
    for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) {
slouken@3139
   311
        /* if match found set format to equivalent QSA format */
slouken@3139
   312
        switch (test_format) {
slouken@3139
   313
        case AUDIO_U8:
slouken@3139
   314
            {
slouken@3139
   315
                format = SND_PCM_SFMT_U8;
slouken@3139
   316
                found = 1;
slouken@3139
   317
            }
slouken@3099
   318
            break;
slouken@3139
   319
        case AUDIO_S8:
slouken@3139
   320
            {
slouken@3139
   321
                format = SND_PCM_SFMT_S8;
slouken@3139
   322
                found = 1;
slouken@3139
   323
            }
slouken@3139
   324
            break;
slouken@3139
   325
        case AUDIO_S16LSB:
slouken@3139
   326
            {
slouken@3139
   327
                format = SND_PCM_SFMT_S16_LE;
slouken@3139
   328
                found = 1;
slouken@3139
   329
            }
slouken@3139
   330
            break;
slouken@3139
   331
        case AUDIO_S16MSB:
slouken@3139
   332
            {
slouken@3139
   333
                format = SND_PCM_SFMT_S16_BE;
slouken@3139
   334
                found = 1;
slouken@3139
   335
            }
slouken@3139
   336
            break;
slouken@3139
   337
        case AUDIO_U16LSB:
slouken@3139
   338
            {
slouken@3139
   339
                format = SND_PCM_SFMT_U16_LE;
slouken@3139
   340
                found = 1;
slouken@3139
   341
            }
slouken@3139
   342
            break;
slouken@3139
   343
        case AUDIO_U16MSB:
slouken@3139
   344
            {
slouken@3139
   345
                format = SND_PCM_SFMT_U16_BE;
slouken@3139
   346
                found = 1;
slouken@3139
   347
            }
slouken@3139
   348
            break;
slouken@3139
   349
        case AUDIO_S32LSB:
slouken@3139
   350
            {
slouken@3139
   351
                format = SND_PCM_SFMT_S32_LE;
slouken@3139
   352
                found = 1;
slouken@3139
   353
            }
slouken@3139
   354
            break;
slouken@3139
   355
        case AUDIO_S32MSB:
slouken@3139
   356
            {
slouken@3139
   357
                format = SND_PCM_SFMT_S32_BE;
slouken@3139
   358
                found = 1;
slouken@3139
   359
            }
slouken@3139
   360
            break;
slouken@3139
   361
        case AUDIO_F32LSB:
slouken@3139
   362
            {
slouken@3139
   363
                format = SND_PCM_SFMT_FLOAT_LE;
slouken@3139
   364
                found = 1;
slouken@3139
   365
            }
slouken@3139
   366
            break;
slouken@3139
   367
        case AUDIO_F32MSB:
slouken@3139
   368
            {
slouken@3139
   369
                format = SND_PCM_SFMT_FLOAT_BE;
slouken@3139
   370
                found = 1;
slouken@3139
   371
            }
slouken@3139
   372
            break;
slouken@3139
   373
        default:
slouken@3139
   374
            {
slouken@3139
   375
                break;
slouken@3139
   376
            }
slouken@3139
   377
        }
slouken@3099
   378
slouken@3139
   379
        if (!found) {
slouken@3139
   380
            test_format = SDL_NextAudioFormat();
slouken@3139
   381
        }
slouken@3139
   382
    }
slouken@3099
   383
slouken@3139
   384
    /* assumes test_format not 0 on success */
slouken@3139
   385
    if (test_format == 0) {
icculus@7038
   386
        return SDL_SetError("QSA: Couldn't find any hardware audio formats");
slouken@3139
   387
    }
slouken@3099
   388
slouken@3139
   389
    this->spec.format = test_format;
slouken@3099
   390
slouken@3139
   391
    /* Set the audio format */
slouken@3139
   392
    cparams.format.format = format;
slouken@3099
   393
slouken@3139
   394
    /* Set mono/stereo/4ch/6ch/8ch audio */
slouken@3139
   395
    cparams.format.voices = this->spec.channels;
slouken@3099
   396
slouken@3139
   397
    /* Set rate */
slouken@3139
   398
    cparams.format.rate = this->spec.freq;
slouken@3099
   399
slouken@3139
   400
    /* Setup the transfer parameters according to cparams */
slouken@3139
   401
    status = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams);
slouken@3139
   402
    if (status < 0) {
philipp@11166
   403
        return QSA_SetError("snd_pcm_plugin_params", status);
slouken@3139
   404
    }
slouken@3099
   405
slouken@3139
   406
    /* Make sure channel is setup right one last time */
icculus@10257
   407
    SDL_zero(csetup);
slouken@3139
   408
    if (!this->hidden->iscapture) {
slouken@3139
   409
        csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
slouken@3139
   410
    } else {
slouken@3139
   411
        csetup.channel = SND_PCM_CHANNEL_CAPTURE;
slouken@3139
   412
    }
slouken@3099
   413
slouken@3139
   414
    /* Setup an audio channel */
slouken@3139
   415
    if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
icculus@7038
   416
        return SDL_SetError("QSA: Unable to setup channel");
slouken@3139
   417
    }
slouken@3099
   418
slouken@3139
   419
    /* Calculate the final parameters for this audio specification */
slouken@3139
   420
    SDL_CalculateAudioSpec(&this->spec);
slouken@3099
   421
slouken@3139
   422
    this->hidden->pcm_len = this->spec.size;
slouken@3099
   423
slouken@3139
   424
    if (this->hidden->pcm_len == 0) {
slouken@3139
   425
        this->hidden->pcm_len =
slouken@3139
   426
            csetup.buf.block.frag_size * this->spec.channels *
slouken@3139
   427
            (snd_pcm_format_width(format) / 8);
slouken@3139
   428
    }
slouken@3099
   429
slouken@3139
   430
    /*
slouken@3139
   431
     * Allocate memory to the audio buffer and initialize with silence
slouken@3139
   432
     *  (Note that buffer size must be a multiple of fragment size, so find
slouken@3139
   433
     *  closest multiple)
slouken@3139
   434
     */
slouken@3139
   435
    this->hidden->pcm_buf =
icculus@10256
   436
        (Uint8 *) SDL_malloc(this->hidden->pcm_len);
slouken@3139
   437
    if (this->hidden->pcm_buf == NULL) {
icculus@7038
   438
        return SDL_OutOfMemory();
slouken@3139
   439
    }
slouken@3139
   440
    SDL_memset(this->hidden->pcm_buf, this->spec.silence,
slouken@3139
   441
               this->hidden->pcm_len);
slouken@3099
   442
slouken@3139
   443
    /* get the file descriptor */
slouken@3139
   444
    if (!this->hidden->iscapture) {
slouken@3139
   445
        this->hidden->audio_fd =
slouken@3139
   446
            snd_pcm_file_descriptor(this->hidden->audio_handle,
slouken@3139
   447
                                    SND_PCM_CHANNEL_PLAYBACK);
slouken@3139
   448
    } else {
slouken@3139
   449
        this->hidden->audio_fd =
slouken@3139
   450
            snd_pcm_file_descriptor(this->hidden->audio_handle,
slouken@3139
   451
                                    SND_PCM_CHANNEL_CAPTURE);
slouken@3139
   452
    }
slouken@3099
   453
slouken@3139
   454
    if (this->hidden->audio_fd < 0) {
icculus@7038
   455
        return QSA_SetError("snd_pcm_file_descriptor", status);
slouken@3139
   456
    }
slouken@3099
   457
slouken@3139
   458
    /* Prepare an audio channel */
slouken@3139
   459
    if (!this->hidden->iscapture) {
slouken@3139
   460
        /* Prepare audio playback */
slouken@3139
   461
        status =
slouken@3139
   462
            snd_pcm_plugin_prepare(this->hidden->audio_handle,
slouken@3139
   463
                                   SND_PCM_CHANNEL_PLAYBACK);
slouken@3139
   464
    } else {
slouken@3139
   465
        /* Prepare audio capture */
slouken@3139
   466
        status =
slouken@3139
   467
            snd_pcm_plugin_prepare(this->hidden->audio_handle,
slouken@3139
   468
                                   SND_PCM_CHANNEL_CAPTURE);
slouken@3139
   469
    }
slouken@3099
   470
slouken@3139
   471
    if (status < 0) {
icculus@7038
   472
        return QSA_SetError("snd_pcm_plugin_prepare", status);
slouken@3139
   473
    }
slouken@3099
   474
slouken@3139
   475
    /* We're really ready to rock and roll. :-) */
icculus@7038
   476
    return 0;
slouken@3099
   477
}
slouken@3099
   478
icculus@5593
   479
static void
icculus@9394
   480
QSA_DetectDevices(void)
slouken@3099
   481
{
slouken@3139
   482
    uint32_t it;
slouken@3139
   483
    uint32_t cards;
slouken@3139
   484
    uint32_t devices;
slouken@3139
   485
    int32_t status;
slouken@3099
   486
slouken@3139
   487
    /* Detect amount of available devices       */
slouken@3139
   488
    /* this value can be changed in the runtime */
slouken@3139
   489
    cards = snd_cards();
slouken@3099
   490
slouken@3139
   491
    /* If io-audio manager is not running we will get 0 as number */
slouken@3139
   492
    /* of available audio devices                                 */
slouken@3139
   493
    if (cards == 0) {
slouken@3139
   494
        /* We have no any available audio devices */
icculus@5593
   495
        return;
slouken@3139
   496
    }
slouken@3099
   497
icculus@9394
   498
    /* !!! FIXME: code duplication */
slouken@3139
   499
    /* Find requested devices by type */
icculus@9394
   500
    {  /* output devices */
slouken@3139
   501
        /* Playback devices enumeration requested */
slouken@3139
   502
        for (it = 0; it < cards; it++) {
slouken@3139
   503
            devices = 0;
slouken@3139
   504
            do {
slouken@3139
   505
                status =
slouken@3139
   506
                    snd_card_get_longname(it,
slouken@3139
   507
                                          qsa_playback_device
slouken@3139
   508
                                          [qsa_playback_devices].name,
slouken@3139
   509
                                          QSA_MAX_NAME_LENGTH);
slouken@3139
   510
                if (status == EOK) {
slouken@3139
   511
                    snd_pcm_t *handle;
slouken@3099
   512
slouken@3139
   513
                    /* Add device number to device name */
slouken@3139
   514
                    sprintf(qsa_playback_device[qsa_playback_devices].name +
slouken@3139
   515
                            SDL_strlen(qsa_playback_device
slouken@3139
   516
                                       [qsa_playback_devices].name), " d%d",
slouken@3139
   517
                            devices);
slouken@3099
   518
slouken@3139
   519
                    /* Store associated card number id */
slouken@3139
   520
                    qsa_playback_device[qsa_playback_devices].cardno = it;
slouken@3099
   521
slouken@3139
   522
                    /* Check if this device id could play anything */
slouken@3139
   523
                    status =
slouken@3139
   524
                        snd_pcm_open(&handle, it, devices,
slouken@3139
   525
                                     SND_PCM_OPEN_PLAYBACK);
slouken@3139
   526
                    if (status == EOK) {
slouken@3139
   527
                        qsa_playback_device[qsa_playback_devices].deviceno =
slouken@3139
   528
                            devices;
slouken@3139
   529
                        status = snd_pcm_close(handle);
slouken@3139
   530
                        if (status == EOK) {
icculus@9394
   531
                            SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, &qsa_playback_device[qsa_playback_devices]);
slouken@3139
   532
                            qsa_playback_devices++;
slouken@3139
   533
                        }
slouken@3139
   534
                    } else {
slouken@3139
   535
                        /* Check if we got end of devices list */
slouken@3139
   536
                        if (status == -ENOENT) {
slouken@3139
   537
                            break;
slouken@3139
   538
                        }
slouken@3139
   539
                    }
slouken@3139
   540
                } else {
slouken@3139
   541
                    break;
slouken@3139
   542
                }
slouken@3139
   543
slouken@3139
   544
                /* Check if we reached maximum devices count */
slouken@3139
   545
                if (qsa_playback_devices >= QSA_MAX_DEVICES) {
slouken@3139
   546
                    break;
slouken@3139
   547
                }
slouken@3139
   548
                devices++;
slouken@3139
   549
            } while (1);
slouken@3099
   550
slouken@3099
   551
            /* Check if we reached maximum devices count */
slouken@3139
   552
            if (qsa_playback_devices >= QSA_MAX_DEVICES) {
slouken@3139
   553
                break;
slouken@3099
   554
            }
slouken@3139
   555
        }
icculus@9394
   556
    }
icculus@9394
   557
icculus@9394
   558
    {  /* capture devices */
slouken@3139
   559
        /* Capture devices enumeration requested */
slouken@3139
   560
        for (it = 0; it < cards; it++) {
slouken@3139
   561
            devices = 0;
slouken@3139
   562
            do {
slouken@3139
   563
                status =
slouken@3139
   564
                    snd_card_get_longname(it,
slouken@3139
   565
                                          qsa_capture_device
slouken@3139
   566
                                          [qsa_capture_devices].name,
slouken@3139
   567
                                          QSA_MAX_NAME_LENGTH);
slouken@3139
   568
                if (status == EOK) {
slouken@3139
   569
                    snd_pcm_t *handle;
slouken@3099
   570
slouken@3139
   571
                    /* Add device number to device name */
slouken@3139
   572
                    sprintf(qsa_capture_device[qsa_capture_devices].name +
slouken@3139
   573
                            SDL_strlen(qsa_capture_device
slouken@3139
   574
                                       [qsa_capture_devices].name), " d%d",
slouken@3139
   575
                            devices);
slouken@3099
   576
slouken@3139
   577
                    /* Store associated card number id */
slouken@3139
   578
                    qsa_capture_device[qsa_capture_devices].cardno = it;
slouken@3099
   579
slouken@3139
   580
                    /* Check if this device id could play anything */
slouken@3139
   581
                    status =
slouken@3139
   582
                        snd_pcm_open(&handle, it, devices,
slouken@3139
   583
                                     SND_PCM_OPEN_CAPTURE);
slouken@3139
   584
                    if (status == EOK) {
slouken@3139
   585
                        qsa_capture_device[qsa_capture_devices].deviceno =
slouken@3139
   586
                            devices;
slouken@3139
   587
                        status = snd_pcm_close(handle);
slouken@3139
   588
                        if (status == EOK) {
icculus@9394
   589
                            SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, &qsa_capture_device[qsa_capture_devices]);
slouken@3139
   590
                            qsa_capture_devices++;
slouken@3139
   591
                        }
slouken@3139
   592
                    } else {
slouken@3139
   593
                        /* Check if we got end of devices list */
slouken@3139
   594
                        if (status == -ENOENT) {
slouken@3139
   595
                            break;
slouken@3139
   596
                        }
slouken@3139
   597
                    }
slouken@3099
   598
slouken@3139
   599
                    /* Check if we reached maximum devices count */
slouken@3139
   600
                    if (qsa_capture_devices >= QSA_MAX_DEVICES) {
slouken@3139
   601
                        break;
slouken@3139
   602
                    }
slouken@3139
   603
                } else {
slouken@3139
   604
                    break;
slouken@3139
   605
                }
slouken@3139
   606
                devices++;
slouken@3139
   607
            } while (1);
slouken@3099
   608
slouken@3139
   609
            /* Check if we reached maximum devices count */
slouken@3139
   610
            if (qsa_capture_devices >= QSA_MAX_DEVICES) {
slouken@3139
   611
                break;
slouken@3099
   612
            }
slouken@3139
   613
        }
slouken@3139
   614
    }
slouken@3099
   615
}
slouken@3099
   616
icculus@5584
   617
static void
slouken@3139
   618
QSA_Deinitialize(void)
slouken@3099
   619
{
slouken@3139
   620
    /* Clear devices array on shutdown */
icculus@10257
   621
    /* !!! FIXME: we zero these on init...any reason to do it here? */
sezeroz@12968
   622
    SDL_zeroa(qsa_playback_device);
sezeroz@12968
   623
    SDL_zeroa(qsa_capture_device);
slouken@3139
   624
    qsa_playback_devices = 0;
slouken@3139
   625
    qsa_capture_devices = 0;
slouken@3099
   626
}
slouken@3099
   627
slouken@3139
   628
static int
slouken@3139
   629
QSA_Init(SDL_AudioDriverImpl * impl)
slouken@3099
   630
{
slouken@3139
   631
    /* Clear devices array */
sezeroz@12968
   632
    SDL_zeroa(qsa_playback_device);
sezeroz@12968
   633
    SDL_zeroa(qsa_capture_device);
slouken@3139
   634
    qsa_playback_devices = 0;
slouken@3139
   635
    qsa_capture_devices = 0;
slouken@3099
   636
slouken@3139
   637
    /* Set function pointers                                     */
slouken@3139
   638
    /* DeviceLock and DeviceUnlock functions are used default,   */
slouken@3139
   639
    /* provided by SDL, which uses pthread_mutex for lock/unlock */
slouken@3139
   640
    impl->DetectDevices = QSA_DetectDevices;
slouken@3139
   641
    impl->OpenDevice = QSA_OpenDevice;
slouken@3139
   642
    impl->ThreadInit = QSA_ThreadInit;
slouken@3139
   643
    impl->WaitDevice = QSA_WaitDevice;
slouken@3139
   644
    impl->PlayDevice = QSA_PlayDevice;
slouken@3139
   645
    impl->GetDeviceBuf = QSA_GetDeviceBuf;
slouken@3139
   646
    impl->CloseDevice = QSA_CloseDevice;
slouken@3139
   647
    impl->Deinitialize = QSA_Deinitialize;
slouken@3139
   648
    impl->LockDevice = NULL;
slouken@3139
   649
    impl->UnlockDevice = NULL;
slouken@3099
   650
slouken@3139
   651
    impl->ProvidesOwnCallbackThread = 0;
slouken@3139
   652
    impl->SkipMixerLock = 0;
slouken@3139
   653
    impl->HasCaptureSupport = 1;
slouken@3139
   654
    impl->OnlyHasDefaultOutputDevice = 0;
icculus@10258
   655
    impl->OnlyHasDefaultCaptureDevice = 0;
slouken@3099
   656
icculus@3699
   657
    return 1;   /* this audio target is available. */
slouken@3099
   658
}
slouken@3099
   659
slouken@3139
   660
AudioBootStrap QSAAUDIO_bootstrap = {
icculus@5594
   661
    "qsa", "QNX QSA Audio", QSA_Init, 0
slouken@3099
   662
};
slouken@3099
   663
slouken@6044
   664
#endif /* SDL_AUDIO_DRIVER_QSA */
slouken@6044
   665
slouken@3099
   666
/* vi: set ts=4 sw=4 expandtab: */