src/audio/dsp/SDL_dspaudio.c
author Ryan C. Gordon
Thu, 24 Aug 2006 12:10:46 +0000
changeset 1982 3b4ce57c6215
parent 1895 c121d94672cb
child 2049 5f6550e5184f
child 3795 589bc3d060cd
permissions -rw-r--r--
First shot at new audio data types (int32 and float32).

Notable changes:
- Converters between types are autogenerated. Instead of making multiple
passes over the data with seperate filters for endianess, size, signedness,
etc, converting between data types is always one specialized filter. This
simplifies SDL_BuildAudioCVT(), which otherwise had a million edge cases
with the new types, and makes the actually conversions more CPU cache
friendly. Left a stub for adding specific optimized versions of these
routines (SSE/MMX/Altivec, assembler, etc)
- Autogenerated converters are built by SDL/src/audio/sdlgenaudiocvt.pl. This
does not need to be run unless tweaking the code, and thus doesn't need
integration into the build system.
- Went through all the drivers and tried to weed out all the "Uint16"
references that are better specified with the new SDL_AudioFormat typedef.
- Cleaned out a bunch of hardcoded bitwise magic numbers and replaced them
with new SDL_AUDIO_* macros.
- Added initial float32 and int32 support code. Theoretically, existing
drivers will push these through converters to get the data they want to
feed to the hardware.

Still TODO:
- Optimize and debug new converters.
- Update the CoreAudio backend to accept float32 data directly.
- Other backends, too?
- SDL_LoadWAV() needs to be updated to support int32 and float32 .wav files
(both of which exist and can be generated by 'sox' for testing purposes).
- Update the mixer to handle new datatypes.
- Optionally update SDL_sound and SDL_mixer, etc.
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@968
    21
slouken@968
    22
    Modified in Oct 2004 by Hannu Savolainen 
slouken@968
    23
    hannu@opensound.com
slouken@0
    24
*/
slouken@1402
    25
#include "SDL_config.h"
slouken@0
    26
slouken@0
    27
/* Allow access to a raw mixing buffer */
slouken@0
    28
slouken@1895
    29
#include <stdio.h>              /* For perror() */
slouken@1895
    30
#include <string.h>             /* For strerror() */
slouken@0
    31
#include <errno.h>
slouken@0
    32
#include <unistd.h>
slouken@0
    33
#include <fcntl.h>
slouken@0
    34
#include <signal.h>
slouken@0
    35
#include <sys/time.h>
slouken@0
    36
#include <sys/ioctl.h>
slouken@0
    37
#include <sys/stat.h>
slouken@1361
    38
slouken@1361
    39
#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
slouken@94
    40
/* This is installed on some systems */
slouken@94
    41
#include <soundcard.h>
slouken@94
    42
#else
slouken@94
    43
/* This is recommended by OSS */
slouken@0
    44
#include <sys/soundcard.h>
slouken@94
    45
#endif
slouken@0
    46
slouken@1358
    47
#include "SDL_timer.h"
slouken@0
    48
#include "SDL_audio.h"
slouken@1361
    49
#include "../SDL_audiomem.h"
slouken@1361
    50
#include "../SDL_audio_c.h"
slouken@1361
    51
#include "../SDL_audiodev_c.h"
slouken@0
    52
#include "SDL_dspaudio.h"
slouken@0
    53
slouken@0
    54
/* The tag name used by DSP audio */
slouken@0
    55
#define DSP_DRIVER_NAME         "dsp"
slouken@0
    56
slouken@0
    57
/* Open the audio device for playback, and don't block if busy */
slouken@0
    58
#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
slouken@0
    59
slouken@0
    60
/* Audio driver functions */
slouken@1895
    61
static int DSP_OpenAudio(_THIS, SDL_AudioSpec * spec);
slouken@0
    62
static void DSP_WaitAudio(_THIS);
slouken@0
    63
static void DSP_PlayAudio(_THIS);
slouken@0
    64
static Uint8 *DSP_GetAudioBuf(_THIS);
slouken@0
    65
static void DSP_CloseAudio(_THIS);
slouken@0
    66
slouken@0
    67
/* Audio driver bootstrap functions */
slouken@0
    68
slouken@1895
    69
static int
slouken@1895
    70
Audio_Available(void)
slouken@0
    71
{
slouken@1895
    72
    int fd;
slouken@1895
    73
    int available;
slouken@0
    74
slouken@1895
    75
    available = 0;
slouken@1895
    76
    fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
slouken@1895
    77
    if (fd >= 0) {
slouken@1895
    78
        available = 1;
slouken@1895
    79
        close(fd);
slouken@1895
    80
    }
slouken@1895
    81
    return (available);
slouken@0
    82
}
slouken@0
    83
slouken@1895
    84
static void
slouken@1895
    85
Audio_DeleteDevice(SDL_AudioDevice * device)
slouken@0
    86
{
slouken@1895
    87
    SDL_free(device->hidden);
slouken@1895
    88
    SDL_free(device);
slouken@0
    89
}
slouken@0
    90
slouken@1895
    91
static SDL_AudioDevice *
slouken@1895
    92
Audio_CreateDevice(int devindex)
slouken@0
    93
{
slouken@1895
    94
    SDL_AudioDevice *this;
slouken@0
    95
slouken@1895
    96
    /* Initialize all variables that we clean on shutdown */
slouken@1895
    97
    this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
slouken@1895
    98
    if (this) {
slouken@1895
    99
        SDL_memset(this, 0, (sizeof *this));
slouken@1895
   100
        this->hidden = (struct SDL_PrivateAudioData *)
slouken@1895
   101
            SDL_malloc((sizeof *this->hidden));
slouken@1895
   102
    }
slouken@1895
   103
    if ((this == NULL) || (this->hidden == NULL)) {
slouken@1895
   104
        SDL_OutOfMemory();
slouken@1895
   105
        if (this) {
slouken@1895
   106
            SDL_free(this);
slouken@1895
   107
        }
slouken@1895
   108
        return (0);
slouken@1895
   109
    }
slouken@1895
   110
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@1895
   111
    audio_fd = -1;
slouken@0
   112
slouken@1895
   113
    /* Set the function pointers */
slouken@1895
   114
    this->OpenAudio = DSP_OpenAudio;
slouken@1895
   115
    this->WaitAudio = DSP_WaitAudio;
slouken@1895
   116
    this->PlayAudio = DSP_PlayAudio;
slouken@1895
   117
    this->GetAudioBuf = DSP_GetAudioBuf;
slouken@1895
   118
    this->CloseAudio = DSP_CloseAudio;
slouken@0
   119
slouken@1895
   120
    this->free = Audio_DeleteDevice;
slouken@0
   121
slouken@1895
   122
    return this;
slouken@0
   123
}
slouken@0
   124
slouken@0
   125
AudioBootStrap DSP_bootstrap = {
slouken@1895
   126
    DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
slouken@1895
   127
    Audio_Available, Audio_CreateDevice
slouken@0
   128
};
slouken@0
   129
slouken@0
   130
/* This function waits until it is possible to write a full sound buffer */
slouken@1895
   131
static void
slouken@1895
   132
DSP_WaitAudio(_THIS)
slouken@0
   133
{
slouken@1895
   134
    /* Not needed at all since OSS handles waiting automagically */
slouken@0
   135
}
slouken@0
   136
slouken@1895
   137
static void
slouken@1895
   138
DSP_PlayAudio(_THIS)
slouken@0
   139
{
slouken@1895
   140
    if (write(audio_fd, mixbuf, mixlen) == -1) {
slouken@1895
   141
        perror("Audio write");
slouken@1895
   142
        this->enabled = 0;
slouken@1895
   143
    }
slouken@0
   144
#ifdef DEBUG_AUDIO
slouken@1895
   145
    fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
slouken@0
   146
#endif
slouken@0
   147
}
slouken@0
   148
slouken@1895
   149
static Uint8 *
slouken@1895
   150
DSP_GetAudioBuf(_THIS)
slouken@0
   151
{
slouken@1895
   152
    return (mixbuf);
slouken@0
   153
}
slouken@0
   154
slouken@1895
   155
static void
slouken@1895
   156
DSP_CloseAudio(_THIS)
slouken@0
   157
{
slouken@1895
   158
    if (mixbuf != NULL) {
slouken@1895
   159
        SDL_FreeAudioMem(mixbuf);
slouken@1895
   160
        mixbuf = NULL;
slouken@1895
   161
    }
slouken@1895
   162
    if (audio_fd >= 0) {
slouken@1895
   163
        close(audio_fd);
slouken@1895
   164
        audio_fd = -1;
slouken@1895
   165
    }
slouken@0
   166
}
slouken@0
   167
slouken@1895
   168
static int
slouken@1895
   169
DSP_OpenAudio(_THIS, SDL_AudioSpec * spec)
slouken@0
   170
{
slouken@1895
   171
    char audiodev[1024];
slouken@1895
   172
    int format;
slouken@1895
   173
    int value;
slouken@1895
   174
    int frag_spec;
icculus@1982
   175
    SDL_AudioFormat test_format;
slouken@0
   176
slouken@1895
   177
    /* Open the audio device */
slouken@1895
   178
    audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
slouken@1895
   179
    if (audio_fd < 0) {
slouken@1895
   180
        SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
slouken@1895
   181
        return (-1);
slouken@1895
   182
    }
slouken@1895
   183
    mixbuf = NULL;
slouken@968
   184
slouken@1895
   185
    /* Make the file descriptor use blocking writes with fcntl() */
slouken@1895
   186
    {
slouken@1895
   187
        long flags;
slouken@1895
   188
        flags = fcntl(audio_fd, F_GETFL);
slouken@1895
   189
        flags &= ~O_NONBLOCK;
slouken@1895
   190
        if (fcntl(audio_fd, F_SETFL, flags) < 0) {
slouken@1895
   191
            SDL_SetError("Couldn't set audio blocking mode");
slouken@1895
   192
            DSP_CloseAudio(this);
slouken@1895
   193
            return (-1);
slouken@1895
   194
        }
slouken@1895
   195
    }
slouken@968
   196
slouken@1895
   197
    /* Get a list of supported hardware formats */
slouken@1895
   198
    if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
slouken@1895
   199
        perror("SNDCTL_DSP_GETFMTS");
slouken@1895
   200
        SDL_SetError("Couldn't get audio format list");
slouken@1895
   201
        DSP_CloseAudio(this);
slouken@1895
   202
        return (-1);
slouken@1895
   203
    }
slouken@968
   204
slouken@1895
   205
    /* Try for a closest match on audio format */
slouken@1895
   206
    format = 0;
slouken@1895
   207
    for (test_format = SDL_FirstAudioFormat(spec->format);
slouken@1895
   208
         !format && test_format;) {
slouken@968
   209
#ifdef DEBUG_AUDIO
slouken@1895
   210
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
slouken@968
   211
#endif
slouken@1895
   212
        switch (test_format) {
slouken@1895
   213
        case AUDIO_U8:
slouken@1895
   214
            if (value & AFMT_U8) {
slouken@1895
   215
                format = AFMT_U8;
slouken@1895
   216
            }
slouken@1895
   217
            break;
slouken@1895
   218
        case AUDIO_S16LSB:
slouken@1895
   219
            if (value & AFMT_S16_LE) {
slouken@1895
   220
                format = AFMT_S16_LE;
slouken@1895
   221
            }
slouken@1895
   222
            break;
slouken@1895
   223
        case AUDIO_S16MSB:
slouken@1895
   224
            if (value & AFMT_S16_BE) {
slouken@1895
   225
                format = AFMT_S16_BE;
slouken@1895
   226
            }
slouken@1895
   227
            break;
slouken@968
   228
#if 0
slouken@968
   229
/*
slouken@968
   230
 * These formats are not used by any real life systems so they are not 
slouken@968
   231
 * needed here.
slouken@968
   232
 */
slouken@1895
   233
        case AUDIO_S8:
slouken@1895
   234
            if (value & AFMT_S8) {
slouken@1895
   235
                format = AFMT_S8;
slouken@1895
   236
            }
slouken@1895
   237
            break;
slouken@1895
   238
        case AUDIO_U16LSB:
slouken@1895
   239
            if (value & AFMT_U16_LE) {
slouken@1895
   240
                format = AFMT_U16_LE;
slouken@1895
   241
            }
slouken@1895
   242
            break;
slouken@1895
   243
        case AUDIO_U16MSB:
slouken@1895
   244
            if (value & AFMT_U16_BE) {
slouken@1895
   245
                format = AFMT_U16_BE;
slouken@1895
   246
            }
slouken@1895
   247
            break;
slouken@968
   248
#endif
slouken@1895
   249
        default:
slouken@1895
   250
            format = 0;
slouken@1895
   251
            break;
slouken@1895
   252
        }
slouken@1895
   253
        if (!format) {
slouken@1895
   254
            test_format = SDL_NextAudioFormat();
slouken@1895
   255
        }
slouken@1895
   256
    }
slouken@1895
   257
    if (format == 0) {
slouken@1895
   258
        SDL_SetError("Couldn't find any hardware audio formats");
slouken@1895
   259
        DSP_CloseAudio(this);
slouken@1895
   260
        return (-1);
slouken@1895
   261
    }
slouken@1895
   262
    spec->format = test_format;
slouken@968
   263
slouken@1895
   264
    /* Set the audio format */
slouken@1895
   265
    value = format;
slouken@1895
   266
    if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) {
slouken@1895
   267
        perror("SNDCTL_DSP_SETFMT");
slouken@1895
   268
        SDL_SetError("Couldn't set audio format");
slouken@1895
   269
        DSP_CloseAudio(this);
slouken@1895
   270
        return (-1);
slouken@1895
   271
    }
slouken@968
   272
slouken@1895
   273
    /* Set the number of channels of output */
slouken@1895
   274
    value = spec->channels;
slouken@1895
   275
    if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
slouken@1895
   276
        perror("SNDCTL_DSP_CHANNELS");
slouken@1895
   277
        SDL_SetError("Cannot set the number of channels");
slouken@1895
   278
        DSP_CloseAudio(this);
slouken@1895
   279
        return (-1);
slouken@1895
   280
    }
slouken@1895
   281
    spec->channels = value;
slouken@968
   282
slouken@1895
   283
    /* Set the DSP frequency */
slouken@1895
   284
    value = spec->freq;
slouken@1895
   285
    if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
slouken@1895
   286
        perror("SNDCTL_DSP_SPEED");
slouken@1895
   287
        SDL_SetError("Couldn't set audio frequency");
slouken@1895
   288
        DSP_CloseAudio(this);
slouken@1895
   289
        return (-1);
slouken@1895
   290
    }
slouken@1895
   291
    spec->freq = value;
slouken@0
   292
slouken@1895
   293
    /* Calculate the final parameters for this audio specification */
slouken@1895
   294
    SDL_CalculateAudioSpec(spec);
slouken@0
   295
slouken@1895
   296
    /* Determine the power of two of the fragment size */
slouken@1895
   297
    for (frag_spec = 0; (0x01U << frag_spec) < spec->size; ++frag_spec);
slouken@1895
   298
    if ((0x01U << frag_spec) != spec->size) {
slouken@1895
   299
        SDL_SetError("Fragment size must be a power of two");
slouken@1895
   300
        DSP_CloseAudio(this);
slouken@1895
   301
        return (-1);
slouken@1895
   302
    }
slouken@1895
   303
    frag_spec |= 0x00020000;    /* two fragments, for low latency */
slouken@0
   304
slouken@1895
   305
    /* Set the audio buffering parameters */
slouken@0
   306
#ifdef DEBUG_AUDIO
slouken@1895
   307
    fprintf(stderr, "Requesting %d fragments of size %d\n",
slouken@1895
   308
            (frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
slouken@0
   309
#endif
slouken@1895
   310
    if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
slouken@1895
   311
        perror("SNDCTL_DSP_SETFRAGMENT");
slouken@1895
   312
    }
slouken@0
   313
#ifdef DEBUG_AUDIO
slouken@1895
   314
    {
slouken@1895
   315
        audio_buf_info info;
slouken@1895
   316
        ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
slouken@1895
   317
        fprintf(stderr, "fragments = %d\n", info.fragments);
slouken@1895
   318
        fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
slouken@1895
   319
        fprintf(stderr, "fragsize = %d\n", info.fragsize);
slouken@1895
   320
        fprintf(stderr, "bytes = %d\n", info.bytes);
slouken@1895
   321
    }
slouken@0
   322
#endif
slouken@0
   323
slouken@1895
   324
    /* Allocate mixing buffer */
slouken@1895
   325
    mixlen = spec->size;
slouken@1895
   326
    mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen);
slouken@1895
   327
    if (mixbuf == NULL) {
slouken@1895
   328
        DSP_CloseAudio(this);
slouken@1895
   329
        return (-1);
slouken@1895
   330
    }
slouken@1895
   331
    SDL_memset(mixbuf, spec->silence, spec->size);
slouken@0
   332
slouken@1895
   333
    /* Get the parent process id (we're the parent of the audio thread) */
slouken@1895
   334
    parent = getpid();
slouken@0
   335
slouken@1895
   336
    /* We're ready to rock and roll. :-) */
slouken@1895
   337
    return (0);
slouken@0
   338
}
slouken@1895
   339
slouken@1895
   340
/* vi: set ts=4 sw=4 expandtab: */