src/audio/bsd/SDL_bsdaudio.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
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@1567
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@1567
     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@1567
     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@1567
    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@1567
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@1567
    22
slouken@6044
    23
#if SDL_AUDIO_DRIVER_BSD
slouken@6044
    24
slouken@1567
    25
/*
slouken@1567
    26
 * Driver for native OpenBSD/NetBSD audio(4).
slouken@1567
    27
 * vedge@vedge.com.ar.
slouken@1567
    28
 */
slouken@1567
    29
slouken@1567
    30
#include <errno.h>
slouken@1567
    31
#include <unistd.h>
slouken@1567
    32
#include <fcntl.h>
slouken@1567
    33
#include <sys/time.h>
slouken@1567
    34
#include <sys/ioctl.h>
slouken@1567
    35
#include <sys/stat.h>
slouken@1567
    36
#include <sys/types.h>
slouken@1567
    37
#include <sys/audioio.h>
slouken@1567
    38
slouken@1567
    39
#include "SDL_timer.h"
slouken@1567
    40
#include "SDL_audio.h"
slouken@1567
    41
#include "../SDL_audio_c.h"
slouken@1567
    42
#include "../SDL_audiodev_c.h"
slouken@1583
    43
#include "SDL_bsdaudio.h"
slouken@1567
    44
slouken@1567
    45
/* Use timer for synchronization */
slouken@1567
    46
/* #define USE_TIMER_SYNC */
slouken@1567
    47
slouken@1567
    48
/* #define DEBUG_AUDIO */
slouken@1567
    49
/* #define DEBUG_AUDIO_STREAM */
slouken@1567
    50
slouken@1567
    51
icculus@5593
    52
static void
icculus@9394
    53
BSDAUDIO_DetectDevices(void)
icculus@2049
    54
{
icculus@9394
    55
    SDL_EnumUnixAudioDevices(0, NULL);
slouken@1567
    56
}
slouken@1567
    57
icculus@2049
    58
slouken@1567
    59
static void
icculus@2049
    60
BSDAUDIO_Status(_THIS)
slouken@1567
    61
{
slouken@1567
    62
#ifdef DEBUG_AUDIO
slouken@2065
    63
    /* *INDENT-OFF* */
slouken@1567
    64
    audio_info_t info;
icculus@10251
    65
    const audio_prinfo *prinfo;
slouken@1567
    66
icculus@2049
    67
    if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
slouken@1895
    68
        fprintf(stderr, "AUDIO_GETINFO failed.\n");
slouken@1895
    69
        return;
slouken@1567
    70
    }
icculus@10251
    71
icculus@10251
    72
    prinfo = this->iscapture ? &info.play : &info.record;
icculus@10251
    73
slouken@1895
    74
    fprintf(stderr, "\n"
icculus@10251
    75
            "[%s info]\n"
slouken@1895
    76
            "buffer size	:   %d bytes\n"
slouken@1895
    77
            "sample rate	:   %i Hz\n"
slouken@1895
    78
            "channels	:   %i\n"
slouken@1895
    79
            "precision	:   %i-bit\n"
slouken@1895
    80
            "encoding	:   0x%x\n"
slouken@1895
    81
            "seek		:   %i\n"
slouken@1895
    82
            "sample count	:   %i\n"
slouken@1895
    83
            "EOF count	:   %i\n"
slouken@1895
    84
            "paused		:   %s\n"
slouken@1895
    85
            "error occured	:   %s\n"
slouken@1895
    86
            "waiting		:   %s\n"
slouken@1895
    87
            "active		:   %s\n"
slouken@1895
    88
            "",
icculus@10251
    89
            this->iscapture ? "record" : "play",
icculus@10251
    90
            prinfo->buffer_size,
icculus@10251
    91
            prinfo->sample_rate,
icculus@10251
    92
            prinfo->channels,
icculus@10251
    93
            prinfo->precision,
icculus@10251
    94
            prinfo->encoding,
icculus@10251
    95
            prinfo->seek,
icculus@10251
    96
            prinfo->samples,
icculus@10251
    97
            prinfo->eof,
icculus@10251
    98
            prinfo->pause ? "yes" : "no",
icculus@10251
    99
            prinfo->error ? "yes" : "no",
icculus@10251
   100
            prinfo->waiting ? "yes" : "no",
icculus@10251
   101
            prinfo->active ? "yes" : "no");
slouken@1567
   102
slouken@1895
   103
    fprintf(stderr, "\n"
slouken@1895
   104
            "[audio info]\n"
slouken@1895
   105
            "monitor_gain	:   %i\n"
slouken@1895
   106
            "hw block size	:   %d bytes\n"
slouken@1895
   107
            "hi watermark	:   %i\n"
slouken@1895
   108
            "lo watermark	:   %i\n"
slouken@1895
   109
            "audio mode	:   %s\n"
slouken@1895
   110
            "",
slouken@1895
   111
            info.monitor_gain,
slouken@1895
   112
            info.blocksize,
slouken@1895
   113
            info.hiwat, info.lowat,
slouken@1895
   114
            (info.mode == AUMODE_PLAY) ? "PLAY"
slouken@1895
   115
            : (info.mode = AUMODE_RECORD) ? "RECORD"
slouken@1895
   116
            : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
slouken@2060
   117
    /* *INDENT-ON* */
icculus@2049
   118
#endif /* DEBUG_AUDIO */
slouken@1567
   119
}
icculus@2049
   120
icculus@2049
   121
icculus@2049
   122
/* This function waits until it is possible to write a full sound buffer */
icculus@2049
   123
static void
icculus@2049
   124
BSDAUDIO_WaitDevice(_THIS)
icculus@2049
   125
{
icculus@2049
   126
#ifndef USE_BLOCKING_WRITES     /* Not necessary when using blocking writes */
icculus@2049
   127
    /* See if we need to use timed audio synchronization */
icculus@2049
   128
    if (this->hidden->frame_ticks) {
icculus@2049
   129
        /* Use timer for general audio synchronization */
icculus@2049
   130
        Sint32 ticks;
icculus@2049
   131
slouken@7857
   132
        ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
icculus@2049
   133
        if (ticks > 0) {
icculus@2049
   134
            SDL_Delay(ticks);
icculus@2049
   135
        }
icculus@2049
   136
    } else {
icculus@2049
   137
        /* Use select() for audio synchronization */
icculus@2049
   138
        fd_set fdset;
icculus@2049
   139
        struct timeval timeout;
icculus@2049
   140
icculus@2049
   141
        FD_ZERO(&fdset);
icculus@2049
   142
        FD_SET(this->hidden->audio_fd, &fdset);
icculus@2049
   143
        timeout.tv_sec = 10;
icculus@2049
   144
        timeout.tv_usec = 0;
icculus@2049
   145
#ifdef DEBUG_AUDIO
icculus@2049
   146
        fprintf(stderr, "Waiting for audio to get ready\n");
icculus@2049
   147
#endif
slouken@2060
   148
        if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
slouken@2060
   149
            <= 0) {
icculus@2049
   150
            const char *message =
icculus@2049
   151
                "Audio timeout - buggy audio driver? (disabled)";
icculus@2049
   152
            /* In general we should never print to the screen,
icculus@2049
   153
               but in this case we have no other way of letting
icculus@2049
   154
               the user know what happened.
icculus@2049
   155
             */
icculus@2049
   156
            fprintf(stderr, "SDL: %s\n", message);
icculus@9394
   157
            SDL_OpenedAudioDeviceDisconnected(this);
icculus@2049
   158
            /* Don't try to close - may hang */
icculus@2049
   159
            this->hidden->audio_fd = -1;
icculus@2049
   160
#ifdef DEBUG_AUDIO
icculus@2049
   161
            fprintf(stderr, "Done disabling audio\n");
icculus@2049
   162
#endif
icculus@2049
   163
        }
icculus@2049
   164
#ifdef DEBUG_AUDIO
icculus@2049
   165
        fprintf(stderr, "Ready!\n");
icculus@2049
   166
#endif
icculus@2049
   167
    }
icculus@2049
   168
#endif /* !USE_BLOCKING_WRITES */
icculus@2049
   169
}
icculus@2049
   170
icculus@2049
   171
static void
icculus@2049
   172
BSDAUDIO_PlayDevice(_THIS)
icculus@2049
   173
{
icculus@2049
   174
    int written, p = 0;
icculus@2049
   175
icculus@2049
   176
    /* Write the audio data, checking for EAGAIN on broken audio drivers */
icculus@2049
   177
    do {
icculus@2049
   178
        written = write(this->hidden->audio_fd,
slouken@2060
   179
                        &this->hidden->mixbuf[p], this->hidden->mixlen - p);
icculus@2049
   180
icculus@2049
   181
        if (written > 0)
icculus@2049
   182
            p += written;
icculus@2049
   183
        if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
icculus@2049
   184
            /* Non recoverable error has occurred. It should be reported!!! */
icculus@2049
   185
            perror("audio");
icculus@2049
   186
            break;
icculus@2049
   187
        }
icculus@2049
   188
icculus@10250
   189
#ifdef DEBUG_AUDIO
icculus@10250
   190
        fprintf(stderr, "Wrote %d bytes of audio data\n", written);
icculus@10250
   191
#endif
icculus@10250
   192
icculus@10250
   193
        if (p < this->hidden->mixlen
icculus@2049
   194
            || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
icculus@2049
   195
            SDL_Delay(1);       /* Let a little CPU time go by */
icculus@2049
   196
        }
icculus@10250
   197
    } while (p < this->hidden->mixlen);
icculus@2049
   198
icculus@2049
   199
    /* If timer synchronization is enabled, set the next write frame */
icculus@2049
   200
    if (this->hidden->frame_ticks) {
icculus@2049
   201
        this->hidden->next_frame += this->hidden->frame_ticks;
icculus@2049
   202
    }
icculus@2049
   203
icculus@2049
   204
    /* If we couldn't write, assume fatal error for now */
icculus@2049
   205
    if (written < 0) {
icculus@9394
   206
        SDL_OpenedAudioDeviceDisconnected(this);
icculus@2049
   207
    }
icculus@2049
   208
}
icculus@2049
   209
icculus@2049
   210
static Uint8 *
icculus@2049
   211
BSDAUDIO_GetDeviceBuf(_THIS)
icculus@2049
   212
{
icculus@2049
   213
    return (this->hidden->mixbuf);
icculus@2049
   214
}
icculus@2049
   215
icculus@10251
   216
icculus@10251
   217
static int
icculus@10251
   218
BSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen)
icculus@10251
   219
{
icculus@10251
   220
    Uint8 *buffer = (Uint8 *) _buffer;
icculus@10251
   221
    int br, p = 0;
icculus@10251
   222
icculus@10251
   223
    /* Write the audio data, checking for EAGAIN on broken audio drivers */
icculus@10251
   224
    do {
icculus@10251
   225
        br = read(this->hidden->audio_fd, buffer + p, buflen - p);
icculus@10251
   226
        if (br > 0)
icculus@10251
   227
            p += br;
icculus@10251
   228
        if (br == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
icculus@10251
   229
            /* Non recoverable error has occurred. It should be reported!!! */
icculus@10251
   230
            perror("audio");
icculus@10251
   231
            return p ? p : -1;
icculus@10251
   232
        }
icculus@10251
   233
icculus@10251
   234
#ifdef DEBUG_AUDIO
icculus@10251
   235
        fprintf(stderr, "Captured %d bytes of audio data\n", br);
icculus@10251
   236
#endif
icculus@10251
   237
icculus@10251
   238
        if (p < buflen
icculus@10251
   239
            || ((br < 0) && ((errno == 0) || (errno == EAGAIN)))) {
icculus@10251
   240
            SDL_Delay(1);       /* Let a little CPU time go by */
icculus@10251
   241
        }
icculus@10251
   242
    } while (p < buflen);
icculus@10251
   243
}
icculus@10251
   244
icculus@10251
   245
static void
icculus@10251
   246
BSDAUDIO_FlushCapture(_THIS)
icculus@10251
   247
{
icculus@10251
   248
    audio_info_t info;
icculus@10251
   249
    size_t remain;
icculus@10251
   250
    Uint8 buf[512];
icculus@10251
   251
icculus@10251
   252
    if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
icculus@10251
   253
        return;  /* oh well. */
icculus@10251
   254
    }
icculus@10251
   255
icculus@10251
   256
    remain = (size_t) (info.record.samples * (SDL_AUDIO_BITSIZE(this->spec.format) / 8));
icculus@10251
   257
    while (remain > 0) {
icculus@10251
   258
        const size_t len = SDL_min(sizeof (buf), remain);
icculus@10251
   259
        const int br = read(this->hidden->audio_fd, buf, len);
icculus@10251
   260
        if (br <= 0) {
icculus@10251
   261
            return;  /* oh well. */
icculus@10251
   262
        }
icculus@10251
   263
        remain -= br;
icculus@10251
   264
    }
icculus@10251
   265
}
icculus@10251
   266
icculus@2049
   267
static void
icculus@2049
   268
BSDAUDIO_CloseDevice(_THIS)
icculus@2049
   269
{
icculus@10255
   270
    if (this->hidden->audio_fd >= 0) {
icculus@10255
   271
        close(this->hidden->audio_fd);
icculus@2049
   272
    }
icculus@10256
   273
    SDL_free(this->hidden->mixbuf);
icculus@10255
   274
    SDL_free(this->hidden);
icculus@2049
   275
}
slouken@1567
   276
slouken@1567
   277
static int
icculus@9394
   278
BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
slouken@1567
   279
{
icculus@10251
   280
    const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
icculus@2049
   281
    SDL_AudioFormat format = 0;
slouken@1567
   282
    audio_info_t info;
icculus@10251
   283
    audio_prinfo *prinfo = iscapture ? &info.play : &info.record;
slouken@1567
   284
icculus@2049
   285
    /* We don't care what the devname is...we'll try to open anything. */
icculus@2049
   286
    /*  ...but default to first name in the list... */
icculus@2049
   287
    if (devname == NULL) {
icculus@5593
   288
        devname = SDL_GetAudioDeviceName(0, iscapture);
icculus@5593
   289
        if (devname == NULL) {
icculus@7038
   290
            return SDL_SetError("No such audio device");
icculus@2049
   291
        }
icculus@2049
   292
    }
icculus@2049
   293
icculus@2049
   294
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   295
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   296
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   297
    if (this->hidden == NULL) {
icculus@7038
   298
        return SDL_OutOfMemory();
icculus@2049
   299
    }
icculus@10257
   300
    SDL_zerop(this->hidden);
icculus@2049
   301
icculus@2049
   302
    /* Open the audio device */
icculus@2049
   303
    this->hidden->audio_fd = open(devname, flags, 0);
icculus@2049
   304
    if (this->hidden->audio_fd < 0) {
icculus@7038
   305
        return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
icculus@2049
   306
    }
icculus@2049
   307
slouken@1567
   308
    AUDIO_INITINFO(&info);
slouken@1895
   309
slouken@1567
   310
    /* Calculate the final parameters for this audio specification */
icculus@2049
   311
    SDL_CalculateAudioSpec(&this->spec);
slouken@1895
   312
slouken@1567
   313
    /* Set to play mode */
icculus@10251
   314
    info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY;
icculus@2049
   315
    if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
icculus@7038
   316
        return SDL_SetError("Couldn't put device into play mode");
slouken@1567
   317
    }
slouken@1895
   318
slouken@1567
   319
    AUDIO_INITINFO(&info);
icculus@2049
   320
    for (format = SDL_FirstAudioFormat(this->spec.format);
slouken@1895
   321
         format; format = SDL_NextAudioFormat()) {
slouken@1895
   322
        switch (format) {
slouken@1895
   323
        case AUDIO_U8:
icculus@10251
   324
            prinfo->encoding = AUDIO_ENCODING_ULINEAR;
icculus@10251
   325
            prinfo->precision = 8;
slouken@1895
   326
            break;
slouken@1895
   327
        case AUDIO_S8:
icculus@10251
   328
            prinfo->encoding = AUDIO_ENCODING_SLINEAR;
icculus@10251
   329
            prinfo->precision = 8;
slouken@1895
   330
            break;
slouken@1895
   331
        case AUDIO_S16LSB:
icculus@10251
   332
            prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
icculus@10251
   333
            prinfo->precision = 16;
slouken@1895
   334
            break;
slouken@1895
   335
        case AUDIO_S16MSB:
icculus@10251
   336
            prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
icculus@10251
   337
            prinfo->precision = 16;
slouken@1895
   338
            break;
slouken@1895
   339
        case AUDIO_U16LSB:
icculus@10251
   340
            prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE;
icculus@10251
   341
            prinfo->precision = 16;
slouken@1895
   342
            break;
slouken@1895
   343
        case AUDIO_U16MSB:
icculus@10251
   344
            prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE;
icculus@10251
   345
            prinfo->precision = 16;
slouken@1895
   346
            break;
slouken@1895
   347
        default:
slouken@1895
   348
            continue;
slouken@1895
   349
        }
icculus@2049
   350
icculus@2049
   351
        if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
slouken@1895
   352
            break;
icculus@2049
   353
        }
slouken@1567
   354
    }
slouken@1567
   355
slouken@1895
   356
    if (!format) {
icculus@7038
   357
        return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
slouken@1567
   358
    }
slouken@1567
   359
icculus@2049
   360
    this->spec.format = format;
slouken@1567
   361
slouken@1567
   362
    AUDIO_INITINFO(&info);
icculus@10251
   363
    prinfo->channels = this->spec.channels;
icculus@2049
   364
    if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
icculus@2049
   365
        this->spec.channels = 1;
icculus@2049
   366
    }
slouken@1567
   367
    AUDIO_INITINFO(&info);
icculus@10251
   368
    prinfo->sample_rate = this->spec.freq;
icculus@2049
   369
    info.blocksize = this->spec.size;
slouken@1567
   370
    info.hiwat = 5;
slouken@1567
   371
    info.lowat = 3;
icculus@2049
   372
    (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
icculus@2049
   373
    (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
icculus@10251
   374
    this->spec.freq = prinfo->sample_rate;
icculus@10251
   375
icculus@10251
   376
    if (!iscapture) {
icculus@10251
   377
        /* Allocate mixing buffer */
icculus@10251
   378
        this->hidden->mixlen = this->spec.size;
icculus@10256
   379
        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
icculus@10251
   380
        if (this->hidden->mixbuf == NULL) {
icculus@10251
   381
            return SDL_OutOfMemory();
icculus@10251
   382
        }
icculus@10251
   383
        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
slouken@1567
   384
    }
slouken@1895
   385
icculus@2049
   386
    BSDAUDIO_Status(this);
slouken@1567
   387
slouken@1567
   388
    /* We're ready to rock and roll. :-) */
icculus@7038
   389
    return 0;
slouken@1567
   390
}
slouken@1895
   391
icculus@2049
   392
static int
slouken@2060
   393
BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   394
{
icculus@2049
   395
    /* Set the function pointers */
icculus@2049
   396
    impl->DetectDevices = BSDAUDIO_DetectDevices;
icculus@2049
   397
    impl->OpenDevice = BSDAUDIO_OpenDevice;
icculus@2049
   398
    impl->PlayDevice = BSDAUDIO_PlayDevice;
icculus@2049
   399
    impl->WaitDevice = BSDAUDIO_WaitDevice;
icculus@2049
   400
    impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
icculus@2049
   401
    impl->CloseDevice = BSDAUDIO_CloseDevice;
icculus@10251
   402
    impl->CaptureFromDevice = BSDAUDIO_CaptureFromDevice;
icculus@10251
   403
    impl->FlushCapture = BSDAUDIO_FlushCapture;
icculus@2049
   404
icculus@10251
   405
    impl->HasCaptureSupport = SDL_TRUE;
icculus@9394
   406
    impl->AllowsArbitraryDeviceNames = 1;
icculus@9394
   407
icculus@3699
   408
    return 1;   /* this audio target is available. */
icculus@2049
   409
}
icculus@2049
   410
icculus@2049
   411
icculus@2049
   412
AudioBootStrap BSD_AUDIO_bootstrap = {
icculus@5594
   413
    "bsd", "BSD audio", BSDAUDIO_Init, 0
icculus@2049
   414
};
icculus@2049
   415
slouken@6044
   416
#endif /* SDL_AUDIO_DRIVER_BSD */
slouken@6044
   417
slouken@1895
   418
/* vi: set ts=4 sw=4 expandtab: */