src/audio/sun/SDL_sunaudio.c
author Sam Lantinga
Fri, 08 Apr 2011 13:03:26 -0700
changeset 5535 96594ac5fd1a
parent 5262 b530ef003506
child 6044 35448a5ea044
permissions -rw-r--r--
SDL 1.3 is now under the zlib license.
icculus@2049
     1
/* I'm gambling no one uses this audio backend...we'll see who emails.  :)  */
icculus@2049
     2
#error this code has not been updated for SDL 1.3.
icculus@2049
     3
#error if no one emails icculus at icculus.org and tells him that this
icculus@2049
     4
#error  code is needed, this audio backend will eventually be removed from SDL.
icculus@2049
     5
slouken@0
     6
/*
slouken@5535
     7
  Simple DirectMedia Layer
slouken@5535
     8
  Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
slouken@0
     9
slouken@5535
    10
  This software is provided 'as-is', without any express or implied
slouken@5535
    11
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
    12
  arising from the use of this software.
slouken@0
    13
slouken@5535
    14
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    15
  including commercial applications, and to alter it and redistribute it
slouken@5535
    16
  freely, subject to the following restrictions:
slouken@0
    17
slouken@5535
    18
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    19
     claim that you wrote the original software. If you use this software
slouken@5535
    20
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    21
     appreciated but is not required.
slouken@5535
    22
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    23
     misrepresented as being the original software.
slouken@5535
    24
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    25
*/
slouken@1402
    26
#include "SDL_config.h"
slouken@0
    27
slouken@0
    28
/* Allow access to a raw mixing buffer */
slouken@0
    29
slouken@0
    30
#include <fcntl.h>
slouken@0
    31
#include <errno.h>
slouken@1402
    32
#ifdef __NETBSD__
slouken@0
    33
#include <sys/ioctl.h>
slouken@0
    34
#include <sys/audioio.h>
slouken@0
    35
#endif
slouken@0
    36
#ifdef __SVR4
slouken@0
    37
#include <sys/audioio.h>
slouken@0
    38
#else
slouken@0
    39
#include <sys/time.h>
slouken@0
    40
#include <sys/types.h>
slouken@0
    41
#endif
slouken@0
    42
#include <unistd.h>
slouken@0
    43
slouken@1358
    44
#include "SDL_timer.h"
slouken@0
    45
#include "SDL_audio.h"
slouken@1361
    46
#include "../SDL_audiomem.h"
slouken@1361
    47
#include "../SDL_audio_c.h"
slouken@1361
    48
#include "../SDL_audiodev_c.h"
slouken@0
    49
#include "SDL_sunaudio.h"
slouken@0
    50
slouken@0
    51
/* Open the audio device for playback, and don't block if busy */
slouken@0
    52
#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
slouken@0
    53
slouken@0
    54
/* Audio driver functions */
slouken@1895
    55
static int DSP_OpenAudio(_THIS, SDL_AudioSpec * spec);
slouken@0
    56
static void DSP_WaitAudio(_THIS);
slouken@0
    57
static void DSP_PlayAudio(_THIS);
slouken@0
    58
static Uint8 *DSP_GetAudioBuf(_THIS);
slouken@0
    59
static void DSP_CloseAudio(_THIS);
slouken@0
    60
slouken@1464
    61
static Uint8 snd2au(int sample);
slouken@1464
    62
slouken@0
    63
/* Audio driver bootstrap functions */
slouken@0
    64
slouken@1895
    65
static int
slouken@1895
    66
Audio_Available(void)
slouken@0
    67
{
slouken@1895
    68
    int fd;
slouken@1895
    69
    int available;
slouken@0
    70
slouken@1895
    71
    available = 0;
slouken@1895
    72
    fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 1);
slouken@1895
    73
    if (fd >= 0) {
slouken@1895
    74
        available = 1;
slouken@1895
    75
        close(fd);
slouken@1895
    76
    }
slouken@1895
    77
    return (available);
slouken@0
    78
}
slouken@0
    79
slouken@1895
    80
static void
slouken@1895
    81
Audio_DeleteDevice(SDL_AudioDevice * device)
slouken@0
    82
{
slouken@1895
    83
    SDL_free(device->hidden);
slouken@1895
    84
    SDL_free(device);
slouken@0
    85
}
slouken@0
    86
slouken@1895
    87
static SDL_AudioDevice *
slouken@1895
    88
Audio_CreateDevice(int devindex)
slouken@0
    89
{
slouken@1895
    90
    SDL_AudioDevice *this;
slouken@0
    91
slouken@1895
    92
    /* Initialize all variables that we clean on shutdown */
slouken@1895
    93
    this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
slouken@1895
    94
    if (this) {
slouken@1895
    95
        SDL_memset(this, 0, (sizeof *this));
slouken@1895
    96
        this->hidden = (struct SDL_PrivateAudioData *)
slouken@1895
    97
            SDL_malloc((sizeof *this->hidden));
slouken@1895
    98
    }
slouken@1895
    99
    if ((this == NULL) || (this->hidden == NULL)) {
slouken@1895
   100
        SDL_OutOfMemory();
slouken@1895
   101
        if (this) {
slouken@1895
   102
            SDL_free(this);
slouken@1895
   103
        }
slouken@1895
   104
        return (0);
slouken@1895
   105
    }
slouken@1895
   106
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@1895
   107
    audio_fd = -1;
slouken@0
   108
slouken@1895
   109
    /* Set the function pointers */
slouken@1895
   110
    this->OpenAudio = DSP_OpenAudio;
slouken@1895
   111
    this->WaitAudio = DSP_WaitAudio;
slouken@1895
   112
    this->PlayAudio = DSP_PlayAudio;
slouken@1895
   113
    this->GetAudioBuf = DSP_GetAudioBuf;
slouken@1895
   114
    this->CloseAudio = DSP_CloseAudio;
slouken@0
   115
slouken@1895
   116
    this->free = Audio_DeleteDevice;
slouken@0
   117
slouken@1895
   118
    return this;
slouken@0
   119
}
slouken@0
   120
slouken@148
   121
AudioBootStrap SUNAUDIO_bootstrap = {
slouken@1895
   122
    "audio", "UNIX /dev/audio interface",
icculus@2049
   123
    Audio_Available, Audio_CreateDevice, 0
slouken@0
   124
};
slouken@0
   125
slouken@0
   126
#ifdef DEBUG_AUDIO
slouken@1895
   127
void
slouken@1895
   128
CheckUnderflow(_THIS)
slouken@0
   129
{
slouken@0
   130
#ifdef AUDIO_GETINFO
slouken@1895
   131
    audio_info_t info;
slouken@1895
   132
    int left;
slouken@0
   133
slouken@1895
   134
    ioctl(audio_fd, AUDIO_GETINFO, &info);
slouken@1895
   135
    left = (written - info.play.samples);
slouken@1895
   136
    if (written && (left == 0)) {
slouken@1895
   137
        fprintf(stderr, "audio underflow!\n");
slouken@1895
   138
    }
slouken@0
   139
#endif
slouken@0
   140
}
slouken@0
   141
#endif
slouken@0
   142
slouken@1895
   143
void
slouken@1895
   144
DSP_WaitAudio(_THIS)
slouken@0
   145
{
slouken@0
   146
#ifdef AUDIO_GETINFO
slouken@1895
   147
#define SLEEP_FUDGE	10      /* 10 ms scheduling fudge factor */
slouken@1895
   148
    audio_info_t info;
slouken@1895
   149
    Sint32 left;
slouken@0
   150
slouken@1895
   151
    ioctl(audio_fd, AUDIO_GETINFO, &info);
slouken@1895
   152
    left = (written - info.play.samples);
slouken@1895
   153
    if (left > fragsize) {
slouken@1895
   154
        Sint32 sleepy;
slouken@0
   155
slouken@1895
   156
        sleepy = ((left - fragsize) / frequency);
slouken@1895
   157
        sleepy -= SLEEP_FUDGE;
slouken@1895
   158
        if (sleepy > 0) {
slouken@1895
   159
            SDL_Delay(sleepy);
slouken@1895
   160
        }
slouken@1895
   161
    }
slouken@0
   162
#else
slouken@1895
   163
    fd_set fdset;
slouken@0
   164
slouken@1895
   165
    FD_ZERO(&fdset);
slouken@1895
   166
    FD_SET(audio_fd, &fdset);
slouken@1895
   167
    select(audio_fd + 1, NULL, &fdset, NULL, NULL);
slouken@0
   168
#endif
slouken@0
   169
}
slouken@0
   170
slouken@1895
   171
void
slouken@1895
   172
DSP_PlayAudio(_THIS)
slouken@0
   173
{
slouken@1895
   174
    /* Write the audio data */
slouken@1895
   175
    if (ulaw_only) {
slouken@1895
   176
        /* Assuming that this->spec.freq >= 8000 Hz */
slouken@1895
   177
        int accum, incr, pos;
slouken@1895
   178
        Uint8 *aubuf;
slouken@0
   179
slouken@1895
   180
        accum = 0;
slouken@1895
   181
        incr = this->spec.freq / 8;
slouken@1895
   182
        aubuf = ulaw_buf;
slouken@1895
   183
        switch (audio_fmt & 0xFF) {
slouken@1895
   184
        case 8:
slouken@1895
   185
            {
slouken@1895
   186
                Uint8 *sndbuf;
slouken@0
   187
slouken@1895
   188
                sndbuf = mixbuf;
slouken@1895
   189
                for (pos = 0; pos < fragsize; ++pos) {
slouken@1895
   190
                    *aubuf = snd2au((0x80 - *sndbuf) * 64);
slouken@1895
   191
                    accum += incr;
slouken@1895
   192
                    while (accum > 0) {
slouken@1895
   193
                        accum -= 1000;
slouken@1895
   194
                        sndbuf += 1;
slouken@1895
   195
                    }
slouken@1895
   196
                    aubuf += 1;
slouken@1895
   197
                }
slouken@1895
   198
            }
slouken@1895
   199
            break;
slouken@1895
   200
        case 16:
slouken@1895
   201
            {
slouken@1895
   202
                Sint16 *sndbuf;
slouken@1895
   203
slouken@1895
   204
                sndbuf = (Sint16 *) mixbuf;
slouken@1895
   205
                for (pos = 0; pos < fragsize; ++pos) {
slouken@1895
   206
                    *aubuf = snd2au(*sndbuf / 4);
slouken@1895
   207
                    accum += incr;
slouken@1895
   208
                    while (accum > 0) {
slouken@1895
   209
                        accum -= 1000;
slouken@1895
   210
                        sndbuf += 1;
slouken@1895
   211
                    }
slouken@1895
   212
                    aubuf += 1;
slouken@1895
   213
                }
slouken@1895
   214
            }
slouken@1895
   215
            break;
slouken@1895
   216
        }
slouken@0
   217
#ifdef DEBUG_AUDIO
slouken@1895
   218
        CheckUnderflow(this);
slouken@0
   219
#endif
slouken@1895
   220
        if (write(audio_fd, ulaw_buf, fragsize) < 0) {
slouken@1895
   221
            /* Assume fatal error, for now */
slouken@1895
   222
            this->enabled = 0;
slouken@1895
   223
        }
slouken@1895
   224
        written += fragsize;
slouken@1895
   225
    } else {
slouken@0
   226
#ifdef DEBUG_AUDIO
slouken@1895
   227
        CheckUnderflow(this);
slouken@0
   228
#endif
slouken@1895
   229
        if (write(audio_fd, mixbuf, this->spec.size) < 0) {
slouken@1895
   230
            /* Assume fatal error, for now */
slouken@1895
   231
            this->enabled = 0;
slouken@1895
   232
        }
slouken@1895
   233
        written += fragsize;
slouken@1895
   234
    }
slouken@0
   235
}
slouken@0
   236
slouken@1895
   237
Uint8 *
slouken@1895
   238
DSP_GetAudioBuf(_THIS)
slouken@0
   239
{
slouken@1895
   240
    return (mixbuf);
slouken@0
   241
}
slouken@0
   242
slouken@1895
   243
void
slouken@1895
   244
DSP_CloseAudio(_THIS)
slouken@0
   245
{
slouken@1895
   246
    if (mixbuf != NULL) {
slouken@1895
   247
        SDL_FreeAudioMem(mixbuf);
slouken@1895
   248
        mixbuf = NULL;
slouken@1895
   249
    }
slouken@1895
   250
    if (ulaw_buf != NULL) {
slouken@1895
   251
        SDL_free(ulaw_buf);
slouken@1895
   252
        ulaw_buf = NULL;
slouken@1895
   253
    }
slouken@1895
   254
    close(audio_fd);
slouken@0
   255
}
slouken@0
   256
slouken@1895
   257
int
slouken@1895
   258
DSP_OpenAudio(_THIS, SDL_AudioSpec * spec)
slouken@0
   259
{
slouken@1895
   260
    char audiodev[1024];
slouken@0
   261
#ifdef AUDIO_SETINFO
slouken@1895
   262
    int enc;
slouken@0
   263
#endif
slouken@1895
   264
    int desired_freq = spec->freq;
slouken@0
   265
slouken@1895
   266
    /* Initialize our freeable variables, in case we fail */
slouken@1895
   267
    audio_fd = -1;
slouken@1895
   268
    mixbuf = NULL;
slouken@1895
   269
    ulaw_buf = NULL;
slouken@0
   270
slouken@1895
   271
    /* Determine the audio parameters from the AudioSpec */
icculus@2015
   272
    switch (SDL_AUDIO_BITSIZE(spec->format)) {
slouken@1895
   273
slouken@1895
   274
    case 8:
slouken@1895
   275
        {                       /* Unsigned 8 bit audio data */
slouken@1895
   276
            spec->format = AUDIO_U8;
slouken@0
   277
#ifdef AUDIO_SETINFO
slouken@1895
   278
            enc = AUDIO_ENCODING_LINEAR8;
slouken@0
   279
#endif
slouken@1895
   280
        }
slouken@1895
   281
        break;
slouken@0
   282
slouken@1895
   283
    case 16:
slouken@1895
   284
        {                       /* Signed 16 bit audio data */
slouken@1895
   285
            spec->format = AUDIO_S16SYS;
slouken@1895
   286
#ifdef AUDIO_SETINFO
slouken@1895
   287
            enc = AUDIO_ENCODING_LINEAR;
slouken@1895
   288
#endif
slouken@1895
   289
        }
slouken@1895
   290
        break;
slouken@0
   291
slouken@1895
   292
    default:
slouken@1895
   293
        {
icculus@2015
   294
            /* !!! FIXME: fallback to conversion on unsupported types! */
slouken@1895
   295
            SDL_SetError("Unsupported audio format");
slouken@1895
   296
            return (-1);
slouken@1895
   297
        }
slouken@1895
   298
    }
slouken@1895
   299
    audio_fmt = spec->format;
slouken@0
   300
slouken@1895
   301
    /* Open the audio device */
slouken@1895
   302
    audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 1);
slouken@1895
   303
    if (audio_fd < 0) {
slouken@1895
   304
        SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
slouken@1895
   305
        return (-1);
slouken@1895
   306
    }
slouken@1895
   307
slouken@1895
   308
    ulaw_only = 0;              /* modern Suns do support linear audio */
slouken@0
   309
#ifdef AUDIO_SETINFO
slouken@1895
   310
    for (;;) {
slouken@1895
   311
        audio_info_t info;
slouken@1895
   312
        AUDIO_INITINFO(&info);  /* init all fields to "no change" */
slouken@0
   313
slouken@1895
   314
        /* Try to set the requested settings */
slouken@1895
   315
        info.play.sample_rate = spec->freq;
slouken@1895
   316
        info.play.channels = spec->channels;
slouken@1895
   317
        info.play.precision = (enc == AUDIO_ENCODING_ULAW)
slouken@1895
   318
            ? 8 : spec->format & 0xff;
slouken@1895
   319
        info.play.encoding = enc;
slouken@1895
   320
        if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0) {
slouken@0
   321
slouken@1895
   322
            /* Check to be sure we got what we wanted */
slouken@1895
   323
            if (ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
slouken@1895
   324
                SDL_SetError("Error getting audio parameters: %s",
slouken@1895
   325
                             strerror(errno));
slouken@1895
   326
                return -1;
slouken@1895
   327
            }
slouken@1895
   328
            if (info.play.encoding == enc
slouken@1895
   329
                && info.play.precision == (spec->format & 0xff)
slouken@1895
   330
                && info.play.channels == spec->channels) {
slouken@1895
   331
                /* Yow! All seems to be well! */
slouken@1895
   332
                spec->freq = info.play.sample_rate;
slouken@1895
   333
                break;
slouken@1895
   334
            }
slouken@1895
   335
        }
slouken@0
   336
slouken@1895
   337
        switch (enc) {
slouken@1895
   338
        case AUDIO_ENCODING_LINEAR8:
slouken@1895
   339
            /* unsigned 8bit apparently not supported here */
slouken@1895
   340
            enc = AUDIO_ENCODING_LINEAR;
slouken@1895
   341
            spec->format = AUDIO_S16SYS;
slouken@1895
   342
            break;              /* try again */
slouken@0
   343
slouken@1895
   344
        case AUDIO_ENCODING_LINEAR:
slouken@1895
   345
            /* linear 16bit didn't work either, resort to -law */
slouken@1895
   346
            enc = AUDIO_ENCODING_ULAW;
slouken@1895
   347
            spec->channels = 1;
slouken@1895
   348
            spec->freq = 8000;
slouken@1895
   349
            spec->format = AUDIO_U8;
slouken@1895
   350
            ulaw_only = 1;
slouken@1895
   351
            break;
slouken@1895
   352
slouken@1895
   353
        default:
slouken@1895
   354
            /* oh well... */
slouken@1895
   355
            SDL_SetError("Error setting audio parameters: %s",
slouken@1895
   356
                         strerror(errno));
slouken@1895
   357
            return -1;
slouken@1895
   358
        }
slouken@1895
   359
    }
slouken@0
   360
#endif /* AUDIO_SETINFO */
slouken@1895
   361
    written = 0;
slouken@0
   362
slouken@1895
   363
    /* We can actually convert on-the-fly to U-Law */
slouken@1895
   364
    if (ulaw_only) {
slouken@1895
   365
        spec->freq = desired_freq;
slouken@1895
   366
        fragsize = (spec->samples * 1000) / (spec->freq / 8);
slouken@1895
   367
        frequency = 8;
slouken@1895
   368
        ulaw_buf = (Uint8 *) SDL_malloc(fragsize);
slouken@1895
   369
        if (ulaw_buf == NULL) {
slouken@1895
   370
            SDL_OutOfMemory();
slouken@1895
   371
            return (-1);
slouken@1895
   372
        }
slouken@1895
   373
        spec->channels = 1;
slouken@1895
   374
    } else {
slouken@1895
   375
        fragsize = spec->samples;
slouken@1895
   376
        frequency = spec->freq / 1000;
slouken@1895
   377
    }
slouken@0
   378
#ifdef DEBUG_AUDIO
slouken@1895
   379
    fprintf(stderr, "Audio device %s U-Law only\n",
slouken@1895
   380
            ulaw_only ? "is" : "is not");
slouken@1895
   381
    fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
slouken@1895
   382
            spec->format, spec->channels, spec->freq);
slouken@0
   383
#endif
slouken@0
   384
slouken@1895
   385
    /* Update the fragment size as size in bytes */
slouken@1895
   386
    SDL_CalculateAudioSpec(spec);
slouken@0
   387
slouken@1895
   388
    /* Allocate mixing buffer */
slouken@1895
   389
    mixbuf = (Uint8 *) SDL_AllocAudioMem(spec->size);
slouken@1895
   390
    if (mixbuf == NULL) {
slouken@1895
   391
        SDL_OutOfMemory();
slouken@1895
   392
        return (-1);
slouken@1895
   393
    }
slouken@1895
   394
    SDL_memset(mixbuf, spec->silence, spec->size);
slouken@0
   395
slouken@1895
   396
    /* We're ready to rock and roll. :-) */
slouken@1895
   397
    return (0);
slouken@0
   398
}
slouken@0
   399
slouken@0
   400
/************************************************************************/
slouken@0
   401
/* This function (snd2au()) copyrighted:                                */
slouken@0
   402
/************************************************************************/
slouken@0
   403
/*      Copyright 1989 by Rich Gopstein and Harris Corporation          */
slouken@0
   404
/*                                                                      */
slouken@0
   405
/*      Permission to use, copy, modify, and distribute this software   */
slouken@0
   406
/*      and its documentation for any purpose and without fee is        */
slouken@0
   407
/*      hereby granted, provided that the above copyright notice        */
slouken@0
   408
/*      appears in all copies and that both that copyright notice and   */
slouken@0
   409
/*      this permission notice appear in supporting documentation, and  */
slouken@0
   410
/*      that the name of Rich Gopstein and Harris Corporation not be    */
slouken@0
   411
/*      used in advertising or publicity pertaining to distribution     */
slouken@0
   412
/*      of the software without specific, written prior permission.     */
slouken@0
   413
/*      Rich Gopstein and Harris Corporation make no representations    */
slouken@0
   414
/*      about the suitability of this software for any purpose.  It     */
slouken@0
   415
/*      provided "as is" without express or implied warranty.           */
slouken@0
   416
/************************************************************************/
slouken@0
   417
slouken@1895
   418
static Uint8
slouken@1895
   419
snd2au(int sample)
slouken@0
   420
{
slouken@0
   421
slouken@1895
   422
    int mask;
slouken@0
   423
slouken@1895
   424
    if (sample < 0) {
slouken@1895
   425
        sample = -sample;
slouken@1895
   426
        mask = 0x7f;
slouken@1895
   427
    } else {
slouken@1895
   428
        mask = 0xff;
slouken@1895
   429
    }
slouken@0
   430
slouken@1895
   431
    if (sample < 32) {
slouken@1895
   432
        sample = 0xF0 | (15 - sample / 2);
slouken@1895
   433
    } else if (sample < 96) {
slouken@1895
   434
        sample = 0xE0 | (15 - (sample - 32) / 4);
slouken@1895
   435
    } else if (sample < 224) {
slouken@1895
   436
        sample = 0xD0 | (15 - (sample - 96) / 8);
slouken@1895
   437
    } else if (sample < 480) {
slouken@1895
   438
        sample = 0xC0 | (15 - (sample - 224) / 16);
slouken@1895
   439
    } else if (sample < 992) {
slouken@1895
   440
        sample = 0xB0 | (15 - (sample - 480) / 32);
slouken@1895
   441
    } else if (sample < 2016) {
slouken@1895
   442
        sample = 0xA0 | (15 - (sample - 992) / 64);
slouken@1895
   443
    } else if (sample < 4064) {
slouken@1895
   444
        sample = 0x90 | (15 - (sample - 2016) / 128);
slouken@1895
   445
    } else if (sample < 8160) {
slouken@1895
   446
        sample = 0x80 | (15 - (sample - 4064) / 256);
slouken@1895
   447
    } else {
slouken@1895
   448
        sample = 0x80;
slouken@1895
   449
    }
slouken@1895
   450
    return (mask & sample);
slouken@0
   451
}
slouken@1895
   452
slouken@1895
   453
/* vi: set ts=4 sw=4 expandtab: */