src/audio/dsp/SDL_dspaudio.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
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@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@0
     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@0
     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@0
    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@0
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@0
    22
slouken@6044
    23
#if SDL_AUDIO_DRIVER_OSS
slouken@6044
    24
slouken@0
    25
/* Allow access to a raw mixing buffer */
slouken@0
    26
slouken@1895
    27
#include <stdio.h>              /* For perror() */
slouken@1895
    28
#include <string.h>             /* For strerror() */
slouken@0
    29
#include <errno.h>
slouken@0
    30
#include <unistd.h>
slouken@0
    31
#include <fcntl.h>
slouken@0
    32
#include <signal.h>
slouken@0
    33
#include <sys/time.h>
slouken@0
    34
#include <sys/ioctl.h>
slouken@0
    35
#include <sys/stat.h>
slouken@1361
    36
slouken@1361
    37
#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
slouken@94
    38
/* This is installed on some systems */
slouken@94
    39
#include <soundcard.h>
slouken@94
    40
#else
slouken@94
    41
/* This is recommended by OSS */
slouken@0
    42
#include <sys/soundcard.h>
slouken@94
    43
#endif
slouken@0
    44
slouken@1358
    45
#include "SDL_timer.h"
slouken@0
    46
#include "SDL_audio.h"
slouken@1361
    47
#include "../SDL_audio_c.h"
slouken@1361
    48
#include "../SDL_audiodev_c.h"
slouken@0
    49
#include "SDL_dspaudio.h"
slouken@0
    50
icculus@2049
    51
icculus@2049
    52
static void
icculus@9394
    53
DSP_DetectDevices(void)
slouken@0
    54
{
icculus@9394
    55
    SDL_EnumUnixAudioDevices(0, NULL);
slouken@0
    56
}
slouken@0
    57
icculus@2049
    58
slouken@1895
    59
static void
icculus@2049
    60
DSP_CloseDevice(_THIS)
slouken@0
    61
{
icculus@10255
    62
    if (this->hidden->audio_fd >= 0) {
icculus@10255
    63
        close(this->hidden->audio_fd);
slouken@1895
    64
    }
icculus@10256
    65
    SDL_free(this->hidden->mixbuf);
icculus@10255
    66
    SDL_free(this->hidden);
slouken@0
    67
}
slouken@0
    68
icculus@2049
    69
slouken@1895
    70
static int
icculus@9394
    71
DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
slouken@0
    72
{
icculus@2049
    73
    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
slouken@1895
    74
    int format;
slouken@1895
    75
    int value;
slouken@1895
    76
    int frag_spec;
icculus@1982
    77
    SDL_AudioFormat test_format;
slouken@0
    78
icculus@2049
    79
    /* We don't care what the devname is...we'll try to open anything. */
icculus@2049
    80
    /*  ...but default to first name in the list... */
icculus@2049
    81
    if (devname == NULL) {
icculus@5593
    82
        devname = SDL_GetAudioDeviceName(0, iscapture);
icculus@5593
    83
        if (devname == NULL) {
icculus@7038
    84
            return SDL_SetError("No such audio device");
icculus@2049
    85
        }
icculus@2049
    86
    }
icculus@2049
    87
icculus@2071
    88
    /* Make sure fragment size stays a power of 2, or OSS fails. */
icculus@2071
    89
    /* I don't know which of these are actually legal values, though... */
icculus@2071
    90
    if (this->spec.channels > 8)
icculus@2071
    91
        this->spec.channels = 8;
icculus@2071
    92
    else if (this->spec.channels > 4)
icculus@2071
    93
        this->spec.channels = 4;
icculus@2071
    94
    else if (this->spec.channels > 2)
icculus@2071
    95
        this->spec.channels = 2;
icculus@2071
    96
icculus@2049
    97
    /* Initialize all variables that we clean on shutdown */
icculus@2049
    98
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
    99
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   100
    if (this->hidden == NULL) {
icculus@7038
   101
        return SDL_OutOfMemory();
icculus@2049
   102
    }
icculus@10257
   103
    SDL_zerop(this->hidden);
icculus@2049
   104
slouken@1895
   105
    /* Open the audio device */
icculus@2049
   106
    this->hidden->audio_fd = open(devname, flags, 0);
icculus@2049
   107
    if (this->hidden->audio_fd < 0) {
icculus@7038
   108
        return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
slouken@1895
   109
    }
slouken@968
   110
icculus@10269
   111
    /* Make the file descriptor use blocking i/o with fcntl() */
slouken@1895
   112
    {
icculus@2049
   113
        long ctlflags;
icculus@2049
   114
        ctlflags = fcntl(this->hidden->audio_fd, F_GETFL);
icculus@2049
   115
        ctlflags &= ~O_NONBLOCK;
icculus@2049
   116
        if (fcntl(this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
icculus@7038
   117
            return SDL_SetError("Couldn't set audio blocking mode");
slouken@1895
   118
        }
slouken@1895
   119
    }
slouken@968
   120
slouken@1895
   121
    /* Get a list of supported hardware formats */
icculus@2049
   122
    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
slouken@1895
   123
        perror("SNDCTL_DSP_GETFMTS");
icculus@7038
   124
        return SDL_SetError("Couldn't get audio format list");
slouken@1895
   125
    }
slouken@968
   126
slouken@1895
   127
    /* Try for a closest match on audio format */
slouken@1895
   128
    format = 0;
icculus@2049
   129
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
slouken@1895
   130
         !format && test_format;) {
slouken@968
   131
#ifdef DEBUG_AUDIO
slouken@1895
   132
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
slouken@968
   133
#endif
slouken@1895
   134
        switch (test_format) {
slouken@1895
   135
        case AUDIO_U8:
slouken@1895
   136
            if (value & AFMT_U8) {
slouken@1895
   137
                format = AFMT_U8;
slouken@1895
   138
            }
slouken@1895
   139
            break;
slouken@1895
   140
        case AUDIO_S16LSB:
slouken@1895
   141
            if (value & AFMT_S16_LE) {
slouken@1895
   142
                format = AFMT_S16_LE;
slouken@1895
   143
            }
slouken@1895
   144
            break;
slouken@1895
   145
        case AUDIO_S16MSB:
slouken@1895
   146
            if (value & AFMT_S16_BE) {
slouken@1895
   147
                format = AFMT_S16_BE;
slouken@1895
   148
            }
slouken@1895
   149
            break;
slouken@968
   150
#if 0
slouken@968
   151
/*
slouken@7191
   152
 * These formats are not used by any real life systems so they are not
slouken@968
   153
 * needed here.
slouken@968
   154
 */
slouken@1895
   155
        case AUDIO_S8:
slouken@1895
   156
            if (value & AFMT_S8) {
slouken@1895
   157
                format = AFMT_S8;
slouken@1895
   158
            }
slouken@1895
   159
            break;
slouken@1895
   160
        case AUDIO_U16LSB:
slouken@1895
   161
            if (value & AFMT_U16_LE) {
slouken@1895
   162
                format = AFMT_U16_LE;
slouken@1895
   163
            }
slouken@1895
   164
            break;
slouken@1895
   165
        case AUDIO_U16MSB:
slouken@1895
   166
            if (value & AFMT_U16_BE) {
slouken@1895
   167
                format = AFMT_U16_BE;
slouken@1895
   168
            }
slouken@1895
   169
            break;
slouken@968
   170
#endif
slouken@1895
   171
        default:
slouken@1895
   172
            format = 0;
slouken@1895
   173
            break;
slouken@1895
   174
        }
slouken@1895
   175
        if (!format) {
slouken@1895
   176
            test_format = SDL_NextAudioFormat();
slouken@1895
   177
        }
slouken@1895
   178
    }
slouken@1895
   179
    if (format == 0) {
icculus@7038
   180
        return SDL_SetError("Couldn't find any hardware audio formats");
slouken@1895
   181
    }
icculus@2049
   182
    this->spec.format = test_format;
slouken@968
   183
slouken@1895
   184
    /* Set the audio format */
slouken@1895
   185
    value = format;
slouken@2060
   186
    if ((ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
slouken@2060
   187
        (value != format)) {
slouken@1895
   188
        perror("SNDCTL_DSP_SETFMT");
icculus@7038
   189
        return SDL_SetError("Couldn't set audio format");
slouken@1895
   190
    }
slouken@968
   191
slouken@1895
   192
    /* Set the number of channels of output */
icculus@2049
   193
    value = this->spec.channels;
icculus@2049
   194
    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
slouken@1895
   195
        perror("SNDCTL_DSP_CHANNELS");
icculus@7038
   196
        return SDL_SetError("Cannot set the number of channels");
slouken@1895
   197
    }
icculus@2049
   198
    this->spec.channels = value;
slouken@968
   199
slouken@1895
   200
    /* Set the DSP frequency */
icculus@2049
   201
    value = this->spec.freq;
icculus@2049
   202
    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
slouken@1895
   203
        perror("SNDCTL_DSP_SPEED");
icculus@7038
   204
        return SDL_SetError("Couldn't set audio frequency");
slouken@1895
   205
    }
icculus@2049
   206
    this->spec.freq = value;
slouken@0
   207
slouken@1895
   208
    /* Calculate the final parameters for this audio specification */
icculus@2049
   209
    SDL_CalculateAudioSpec(&this->spec);
slouken@0
   210
slouken@1895
   211
    /* Determine the power of two of the fragment size */
icculus@2049
   212
    for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
icculus@2049
   213
    if ((0x01U << frag_spec) != this->spec.size) {
icculus@7038
   214
        return SDL_SetError("Fragment size must be a power of two");
slouken@1895
   215
    }
slouken@1895
   216
    frag_spec |= 0x00020000;    /* two fragments, for low latency */
slouken@0
   217
slouken@1895
   218
    /* Set the audio buffering parameters */
slouken@0
   219
#ifdef DEBUG_AUDIO
slouken@1895
   220
    fprintf(stderr, "Requesting %d fragments of size %d\n",
slouken@1895
   221
            (frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
slouken@0
   222
#endif
icculus@2049
   223
    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
slouken@1895
   224
        perror("SNDCTL_DSP_SETFRAGMENT");
slouken@1895
   225
    }
slouken@0
   226
#ifdef DEBUG_AUDIO
slouken@1895
   227
    {
slouken@1895
   228
        audio_buf_info info;
icculus@2049
   229
        ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
slouken@1895
   230
        fprintf(stderr, "fragments = %d\n", info.fragments);
slouken@1895
   231
        fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
slouken@1895
   232
        fprintf(stderr, "fragsize = %d\n", info.fragsize);
slouken@1895
   233
        fprintf(stderr, "bytes = %d\n", info.bytes);
slouken@1895
   234
    }
slouken@0
   235
#endif
slouken@0
   236
slouken@1895
   237
    /* Allocate mixing buffer */
icculus@10269
   238
    if (!iscapture) {
icculus@10269
   239
        this->hidden->mixlen = this->spec.size;
icculus@10269
   240
        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
icculus@10269
   241
        if (this->hidden->mixbuf == NULL) {
icculus@10269
   242
            return SDL_OutOfMemory();
icculus@10269
   243
        }
icculus@10269
   244
        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
slouken@1895
   245
    }
slouken@0
   246
slouken@1895
   247
    /* We're ready to rock and roll. :-) */
icculus@7038
   248
    return 0;
icculus@2049
   249
}
icculus@2049
   250
icculus@2049
   251
icculus@2049
   252
static void
icculus@2049
   253
DSP_PlayDevice(_THIS)
icculus@2049
   254
{
icculus@10269
   255
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@10269
   256
    if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) {
icculus@2049
   257
        perror("Audio write");
icculus@9394
   258
        SDL_OpenedAudioDeviceDisconnected(this);
icculus@2049
   259
    }
icculus@2049
   260
#ifdef DEBUG_AUDIO
icculus@10269
   261
    fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
icculus@2049
   262
#endif
slouken@0
   263
}
slouken@1895
   264
icculus@2049
   265
static Uint8 *
icculus@2049
   266
DSP_GetDeviceBuf(_THIS)
icculus@2049
   267
{
icculus@2049
   268
    return (this->hidden->mixbuf);
icculus@2049
   269
}
icculus@2049
   270
icculus@2049
   271
static int
icculus@10269
   272
DSP_CaptureFromDevice(_THIS, void *buffer, int buflen)
icculus@10269
   273
{
icculus@10269
   274
    return (int) read(this->hidden->audio_fd, buffer, buflen);
icculus@10269
   275
}
icculus@10269
   276
icculus@10269
   277
static void
icculus@10269
   278
DSP_FlushCapture(_THIS)
icculus@10269
   279
{
icculus@10269
   280
    struct SDL_PrivateAudioData *h = this->hidden;
icculus@10269
   281
    audio_buf_info info;
icculus@10269
   282
    if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
icculus@10269
   283
        while (info.bytes > 0) {
icculus@10269
   284
            char buf[512];
icculus@10269
   285
            const size_t len = SDL_min(sizeof (buf), info.bytes);
icculus@10269
   286
            const ssize_t br = read(h->audio_fd, buf, len);
icculus@10269
   287
            if (br <= 0) {
icculus@10269
   288
                break;
icculus@10269
   289
            }
icculus@10269
   290
            info.bytes -= br;
icculus@10269
   291
        }
icculus@10269
   292
    }
icculus@10269
   293
}
icculus@10269
   294
icculus@10269
   295
static int
slouken@2060
   296
DSP_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   297
{
icculus@2049
   298
    /* Set the function pointers */
icculus@2049
   299
    impl->DetectDevices = DSP_DetectDevices;
icculus@2049
   300
    impl->OpenDevice = DSP_OpenDevice;
icculus@2049
   301
    impl->PlayDevice = DSP_PlayDevice;
icculus@2049
   302
    impl->GetDeviceBuf = DSP_GetDeviceBuf;
icculus@2049
   303
    impl->CloseDevice = DSP_CloseDevice;
icculus@10269
   304
    impl->CaptureFromDevice = DSP_CaptureFromDevice;
icculus@10269
   305
    impl->FlushCapture = DSP_FlushCapture;
icculus@2049
   306
icculus@9394
   307
    impl->AllowsArbitraryDeviceNames = 1;
icculus@10269
   308
    impl->HasCaptureSupport = SDL_TRUE;
icculus@9394
   309
icculus@3699
   310
    return 1;   /* this audio target is available. */
icculus@2049
   311
}
icculus@2049
   312
icculus@2049
   313
icculus@2049
   314
AudioBootStrap DSP_bootstrap = {
icculus@5594
   315
    "dsp", "OSS /dev/dsp standard audio", DSP_Init, 0
icculus@2049
   316
};
icculus@2049
   317
slouken@6044
   318
#endif /* SDL_AUDIO_DRIVER_OSS */
slouken@6044
   319
slouken@1895
   320
/* vi: set ts=4 sw=4 expandtab: */