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