src/audio/bsd/SDL_bsdaudio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sat, 01 Jun 2013 21:09:36 +0200
changeset 7252 8ecb54eeaeec
parent 7038 7f22b9ba218f
child 7719 31b5f9ff36ca
permissions -rw-r--r--
Corrected indentation of license.
slouken@1567
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 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
*/
slouken@1567
    21
#include "SDL_config.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_audiomem.h"
slouken@1567
    42
#include "../SDL_audio_c.h"
slouken@1567
    43
#include "../SDL_audiodev_c.h"
slouken@1583
    44
#include "SDL_bsdaudio.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@5593
    54
BSDAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
slouken@1567
    55
{
icculus@5593
    56
    SDL_EnumUnixAudioDevices(iscapture, 0, NULL, addfn);
slouken@1567
    57
}
slouken@1567
    58
slouken@1567
    59
slouken@1567
    60
static void
icculus@2049
    61
BSDAUDIO_Status(_THIS)
slouken@1567
    62
{
slouken@1567
    63
#ifdef DEBUG_AUDIO
slouken@2065
    64
    /* *INDENT-OFF* */
slouken@1567
    65
    audio_info_t info;
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
    }
slouken@1895
    71
    fprintf(stderr, "\n"
slouken@1895
    72
            "[play/record info]\n"
slouken@1895
    73
            "buffer size	:   %d bytes\n"
slouken@1895
    74
            "sample rate	:   %i Hz\n"
slouken@1895
    75
            "channels	:   %i\n"
slouken@1895
    76
            "precision	:   %i-bit\n"
slouken@1895
    77
            "encoding	:   0x%x\n"
slouken@1895
    78
            "seek		:   %i\n"
slouken@1895
    79
            "sample count	:   %i\n"
slouken@1895
    80
            "EOF count	:   %i\n"
slouken@1895
    81
            "paused		:   %s\n"
slouken@1895
    82
            "error occured	:   %s\n"
slouken@1895
    83
            "waiting		:   %s\n"
slouken@1895
    84
            "active		:   %s\n"
slouken@1895
    85
            "",
icculus@2049
    86
            info.play.buffer_size,
icculus@2049
    87
            info.play.sample_rate,
icculus@2049
    88
            info.play.channels,
icculus@2049
    89
            info.play.precision,
icculus@2049
    90
            info.play.encoding,
icculus@2049
    91
            info.play.seek,
icculus@2049
    92
            info.play.samples,
icculus@2049
    93
            info.play.eof,
icculus@2049
    94
            info.play.pause ? "yes" : "no",
icculus@2049
    95
            info.play.error ? "yes" : "no",
icculus@2049
    96
            info.play.waiting ? "yes" : "no",
icculus@2049
    97
            info.play.active ? "yes" : "no");
slouken@1567
    98
slouken@1895
    99
    fprintf(stderr, "\n"
slouken@1895
   100
            "[audio info]\n"
slouken@1895
   101
            "monitor_gain	:   %i\n"
slouken@1895
   102
            "hw block size	:   %d bytes\n"
slouken@1895
   103
            "hi watermark	:   %i\n"
slouken@1895
   104
            "lo watermark	:   %i\n"
slouken@1895
   105
            "audio mode	:   %s\n"
slouken@1895
   106
            "",
slouken@1895
   107
            info.monitor_gain,
slouken@1895
   108
            info.blocksize,
slouken@1895
   109
            info.hiwat, info.lowat,
slouken@1895
   110
            (info.mode == AUMODE_PLAY) ? "PLAY"
slouken@1895
   111
            : (info.mode = AUMODE_RECORD) ? "RECORD"
slouken@1895
   112
            : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
slouken@2060
   113
    /* *INDENT-ON* */
icculus@2049
   114
#endif /* DEBUG_AUDIO */
slouken@1567
   115
}
icculus@2049
   116
icculus@2049
   117
icculus@2049
   118
/* This function waits until it is possible to write a full sound buffer */
icculus@2049
   119
static void
icculus@2049
   120
BSDAUDIO_WaitDevice(_THIS)
icculus@2049
   121
{
icculus@2049
   122
#ifndef USE_BLOCKING_WRITES     /* Not necessary when using blocking writes */
icculus@2049
   123
    /* See if we need to use timed audio synchronization */
icculus@2049
   124
    if (this->hidden->frame_ticks) {
icculus@2049
   125
        /* Use timer for general audio synchronization */
icculus@2049
   126
        Sint32 ticks;
icculus@2049
   127
slouken@2060
   128
        ticks =
slouken@2060
   129
            ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) -
slouken@2060
   130
            FUDGE_TICKS;
icculus@2049
   131
        if (ticks > 0) {
icculus@2049
   132
            SDL_Delay(ticks);
icculus@2049
   133
        }
icculus@2049
   134
    } else {
icculus@2049
   135
        /* Use select() for audio synchronization */
icculus@2049
   136
        fd_set fdset;
icculus@2049
   137
        struct timeval timeout;
icculus@2049
   138
icculus@2049
   139
        FD_ZERO(&fdset);
icculus@2049
   140
        FD_SET(this->hidden->audio_fd, &fdset);
icculus@2049
   141
        timeout.tv_sec = 10;
icculus@2049
   142
        timeout.tv_usec = 0;
icculus@2049
   143
#ifdef DEBUG_AUDIO
icculus@2049
   144
        fprintf(stderr, "Waiting for audio to get ready\n");
icculus@2049
   145
#endif
slouken@2060
   146
        if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
slouken@2060
   147
            <= 0) {
icculus@2049
   148
            const char *message =
icculus@2049
   149
                "Audio timeout - buggy audio driver? (disabled)";
icculus@2049
   150
            /* In general we should never print to the screen,
icculus@2049
   151
               but in this case we have no other way of letting
icculus@2049
   152
               the user know what happened.
icculus@2049
   153
             */
icculus@2049
   154
            fprintf(stderr, "SDL: %s\n", message);
icculus@2049
   155
            this->enabled = 0;
icculus@2049
   156
            /* Don't try to close - may hang */
icculus@2049
   157
            this->hidden->audio_fd = -1;
icculus@2049
   158
#ifdef DEBUG_AUDIO
icculus@2049
   159
            fprintf(stderr, "Done disabling audio\n");
icculus@2049
   160
#endif
icculus@2049
   161
        }
icculus@2049
   162
#ifdef DEBUG_AUDIO
icculus@2049
   163
        fprintf(stderr, "Ready!\n");
icculus@2049
   164
#endif
icculus@2049
   165
    }
icculus@2049
   166
#endif /* !USE_BLOCKING_WRITES */
icculus@2049
   167
}
icculus@2049
   168
icculus@2049
   169
static void
icculus@2049
   170
BSDAUDIO_PlayDevice(_THIS)
icculus@2049
   171
{
icculus@2049
   172
    int written, p = 0;
icculus@2049
   173
icculus@2049
   174
    /* Write the audio data, checking for EAGAIN on broken audio drivers */
icculus@2049
   175
    do {
icculus@2049
   176
        written = write(this->hidden->audio_fd,
slouken@2060
   177
                        &this->hidden->mixbuf[p], this->hidden->mixlen - p);
icculus@2049
   178
icculus@2049
   179
        if (written > 0)
icculus@2049
   180
            p += written;
icculus@2049
   181
        if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
icculus@2049
   182
            /* Non recoverable error has occurred. It should be reported!!! */
icculus@2049
   183
            perror("audio");
icculus@2049
   184
            break;
icculus@2049
   185
        }
icculus@2049
   186
icculus@2049
   187
        if (p < written
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
        }
slouken@2735
   191
    } while (p < written);
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@2049
   200
        this->enabled = 0;
icculus@2049
   201
    }
icculus@2049
   202
#ifdef DEBUG_AUDIO
icculus@2049
   203
    fprintf(stderr, "Wrote %d bytes of audio data\n", written);
icculus@2049
   204
#endif
icculus@2049
   205
}
icculus@2049
   206
icculus@2049
   207
static Uint8 *
icculus@2049
   208
BSDAUDIO_GetDeviceBuf(_THIS)
icculus@2049
   209
{
icculus@2049
   210
    return (this->hidden->mixbuf);
icculus@2049
   211
}
icculus@2049
   212
icculus@2049
   213
static void
icculus@2049
   214
BSDAUDIO_CloseDevice(_THIS)
icculus@2049
   215
{
icculus@2049
   216
    if (this->hidden != NULL) {
icculus@2049
   217
        if (this->hidden->mixbuf != NULL) {
icculus@2049
   218
            SDL_FreeAudioMem(this->hidden->mixbuf);
icculus@2049
   219
            this->hidden->mixbuf = NULL;
icculus@2049
   220
        }
icculus@2049
   221
        if (this->hidden->audio_fd >= 0) {
icculus@2049
   222
            close(this->hidden->audio_fd);
icculus@2049
   223
            this->hidden->audio_fd = -1;
icculus@2049
   224
        }
icculus@2049
   225
        SDL_free(this->hidden);
icculus@2049
   226
        this->hidden = NULL;
icculus@2049
   227
    }
icculus@2049
   228
}
slouken@1567
   229
slouken@1567
   230
static int
icculus@2049
   231
BSDAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
slouken@1567
   232
{
icculus@2049
   233
    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
icculus@2049
   234
    SDL_AudioFormat format = 0;
slouken@1567
   235
    audio_info_t info;
slouken@1567
   236
icculus@2049
   237
    /* We don't care what the devname is...we'll try to open anything. */
icculus@2049
   238
    /*  ...but default to first name in the list... */
icculus@2049
   239
    if (devname == NULL) {
icculus@5593
   240
        devname = SDL_GetAudioDeviceName(0, iscapture);
icculus@5593
   241
        if (devname == NULL) {
icculus@7038
   242
            return SDL_SetError("No such audio device");
icculus@2049
   243
        }
icculus@2049
   244
    }
icculus@2049
   245
icculus@2049
   246
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   247
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   248
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   249
    if (this->hidden == NULL) {
icculus@7038
   250
        return SDL_OutOfMemory();
icculus@2049
   251
    }
icculus@2049
   252
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
icculus@2049
   253
icculus@2049
   254
    /* Open the audio device */
icculus@2049
   255
    this->hidden->audio_fd = open(devname, flags, 0);
icculus@2049
   256
    if (this->hidden->audio_fd < 0) {
icculus@7038
   257
        return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
icculus@2049
   258
    }
icculus@2049
   259
slouken@1567
   260
    AUDIO_INITINFO(&info);
slouken@1895
   261
slouken@1567
   262
    /* Calculate the final parameters for this audio specification */
icculus@2049
   263
    SDL_CalculateAudioSpec(&this->spec);
slouken@1895
   264
slouken@1567
   265
    /* Set to play mode */
slouken@1567
   266
    info.mode = AUMODE_PLAY;
icculus@2049
   267
    if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
icculus@2049
   268
        BSDAUDIO_CloseDevice(this);
icculus@7038
   269
        return SDL_SetError("Couldn't put device into play mode");
slouken@1567
   270
    }
slouken@1895
   271
slouken@1567
   272
    AUDIO_INITINFO(&info);
icculus@2049
   273
    for (format = SDL_FirstAudioFormat(this->spec.format);
slouken@1895
   274
         format; format = SDL_NextAudioFormat()) {
slouken@1895
   275
        switch (format) {
slouken@1895
   276
        case AUDIO_U8:
slouken@1895
   277
            info.play.encoding = AUDIO_ENCODING_ULINEAR;
slouken@1895
   278
            info.play.precision = 8;
slouken@1895
   279
            break;
slouken@1895
   280
        case AUDIO_S8:
slouken@1895
   281
            info.play.encoding = AUDIO_ENCODING_SLINEAR;
slouken@1895
   282
            info.play.precision = 8;
slouken@1895
   283
            break;
slouken@1895
   284
        case AUDIO_S16LSB:
slouken@1895
   285
            info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
slouken@1895
   286
            info.play.precision = 16;
slouken@1895
   287
            break;
slouken@1895
   288
        case AUDIO_S16MSB:
slouken@1895
   289
            info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
slouken@1895
   290
            info.play.precision = 16;
slouken@1895
   291
            break;
slouken@1895
   292
        case AUDIO_U16LSB:
slouken@1895
   293
            info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
slouken@1895
   294
            info.play.precision = 16;
slouken@1895
   295
            break;
slouken@1895
   296
        case AUDIO_U16MSB:
slouken@1895
   297
            info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
slouken@1895
   298
            info.play.precision = 16;
slouken@1895
   299
            break;
slouken@1895
   300
        default:
slouken@1895
   301
            continue;
slouken@1895
   302
        }
icculus@2049
   303
icculus@2049
   304
        if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
slouken@1895
   305
            break;
icculus@2049
   306
        }
slouken@1567
   307
    }
slouken@1567
   308
slouken@1895
   309
    if (!format) {
icculus@2049
   310
        BSDAUDIO_CloseDevice(this);
icculus@7038
   311
        return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
slouken@1567
   312
    }
slouken@1567
   313
icculus@2049
   314
    this->spec.format = format;
slouken@1567
   315
slouken@1567
   316
    AUDIO_INITINFO(&info);
icculus@2049
   317
    info.play.channels = this->spec.channels;
icculus@2049
   318
    if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
icculus@2049
   319
        this->spec.channels = 1;
icculus@2049
   320
    }
slouken@1567
   321
    AUDIO_INITINFO(&info);
icculus@2049
   322
    info.play.sample_rate = this->spec.freq;
icculus@2049
   323
    info.blocksize = this->spec.size;
slouken@1567
   324
    info.hiwat = 5;
slouken@1567
   325
    info.lowat = 3;
icculus@2049
   326
    (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
icculus@2049
   327
    (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
icculus@2049
   328
    this->spec.freq = info.play.sample_rate;
slouken@1567
   329
    /* Allocate mixing buffer */
icculus@2049
   330
    this->hidden->mixlen = this->spec.size;
icculus@2049
   331
    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
icculus@2049
   332
    if (this->hidden->mixbuf == NULL) {
icculus@2049
   333
        BSDAUDIO_CloseDevice(this);
icculus@7038
   334
        return SDL_OutOfMemory();
slouken@1567
   335
    }
icculus@2049
   336
    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
slouken@1895
   337
icculus@2049
   338
    BSDAUDIO_Status(this);
slouken@1567
   339
slouken@1567
   340
    /* We're ready to rock and roll. :-) */
icculus@7038
   341
    return 0;
slouken@1567
   342
}
slouken@1895
   343
icculus@2049
   344
static int
slouken@2060
   345
BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   346
{
icculus@2049
   347
    /* Set the function pointers */
icculus@2049
   348
    impl->DetectDevices = BSDAUDIO_DetectDevices;
icculus@2049
   349
    impl->OpenDevice = BSDAUDIO_OpenDevice;
icculus@2049
   350
    impl->PlayDevice = BSDAUDIO_PlayDevice;
icculus@2049
   351
    impl->WaitDevice = BSDAUDIO_WaitDevice;
icculus@2049
   352
    impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
icculus@2049
   353
    impl->CloseDevice = BSDAUDIO_CloseDevice;
icculus@2049
   354
icculus@3699
   355
    return 1;   /* this audio target is available. */
icculus@2049
   356
}
icculus@2049
   357
icculus@2049
   358
icculus@2049
   359
AudioBootStrap BSD_AUDIO_bootstrap = {
icculus@5594
   360
    "bsd", "BSD audio", BSDAUDIO_Init, 0
icculus@2049
   361
};
icculus@2049
   362
slouken@6044
   363
#endif /* SDL_AUDIO_DRIVER_BSD */
slouken@6044
   364
slouken@1895
   365
/* vi: set ts=4 sw=4 expandtab: */