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