src/audio/bsd/SDL_bsdaudio.c
author Ryan C. Gordon
Mon, 23 Jan 2017 12:06:10 -0500
changeset 10837 c2f241c2f6ad
parent 10737 3406a0f8b041
permissions -rw-r--r--
audio: Fix same bug as last commit, but for _mm_bslli_si128 vs _mm_slli_si128.
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: */