src/audio/paudio/SDL_paudio.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Aug 2009 18:39:57 +0000
changeset 3227 458e53d8662c
parent 2942 1e431c2631ee
child 3697 f7b03b6838cb
permissions -rw-r--r--
Clarified API documentation
slouken@0
     1
/*
slouken@1312
     2
    SDL - Simple DirectMedia Layer
slouken@2859
     3
    Copyright (C) 1997-2009 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
    Carsten Griwodz
slouken@0
    20
    griff@kom.tu-darmstadt.de
slouken@0
    21
slouken@0
    22
    based on linux/SDL_dspaudio.c by Sam Lantinga
slouken@0
    23
*/
slouken@1402
    24
#include "SDL_config.h"
slouken@0
    25
slouken@0
    26
/* Allow access to a raw mixing buffer */
slouken@0
    27
slouken@0
    28
#include <errno.h>
slouken@0
    29
#include <unistd.h>
slouken@0
    30
#include <fcntl.h>
slouken@0
    31
#include <sys/time.h>
slouken@0
    32
#include <sys/ioctl.h>
icculus@2049
    33
#include <sys/types.h>
slouken@0
    34
#include <sys/stat.h>
slouken@0
    35
slouken@1358
    36
#include "SDL_timer.h"
slouken@0
    37
#include "SDL_audio.h"
icculus@2049
    38
#include "SDL_stdinc.h"
slouken@1361
    39
#include "../SDL_audiomem.h"
slouken@1361
    40
#include "../SDL_audio_c.h"
slouken@0
    41
#include "SDL_paudio.h"
slouken@0
    42
icculus@2049
    43
#define DEBUG_AUDIO 0
slouken@0
    44
slouken@0
    45
/* A conflict within AIX 4.3.3 <sys/> headers and probably others as well.
slouken@0
    46
 * I guess nobody ever uses audio... Shame over AIX header files.  */
slouken@0
    47
#include <sys/machine.h>
slouken@0
    48
#undef BIG_ENDIAN
slouken@0
    49
#include <sys/audio.h>
slouken@0
    50
slouken@0
    51
/* The tag name used by paud audio */
icculus@2049
    52
#define PAUDIO_DRIVER_NAME         "paud"
slouken@0
    53
slouken@0
    54
/* Open the audio device for playback, and don't block if busy */
slouken@0
    55
/* #define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK) */
slouken@0
    56
#define OPEN_FLAGS	O_WRONLY
slouken@0
    57
icculus@2049
    58
/* Get the name of the audio device we use for output */
icculus@2049
    59
icculus@2049
    60
#ifndef _PATH_DEV_DSP
icculus@2049
    61
#define _PATH_DEV_DSP	"/dev/%caud%c/%c"
icculus@2049
    62
#endif
slouken@0
    63
icculus@2049
    64
static char devsettings[][3] = {
icculus@2049
    65
    {'p', '0', '1'}, {'p', '0', '2'}, {'p', '0', '3'}, {'p', '0', '4'},
icculus@2049
    66
    {'p', '1', '1'}, {'p', '1', '2'}, {'p', '1', '3'}, {'p', '1', '4'},
icculus@2049
    67
    {'p', '2', '1'}, {'p', '2', '2'}, {'p', '2', '3'}, {'p', '2', '4'},
icculus@2049
    68
    {'p', '3', '1'}, {'p', '3', '2'}, {'p', '3', '3'}, {'p', '3', '4'},
icculus@2049
    69
    {'b', '0', '1'}, {'b', '0', '2'}, {'b', '0', '3'}, {'b', '0', '4'},
icculus@2049
    70
    {'b', '1', '1'}, {'b', '1', '2'}, {'b', '1', '3'}, {'b', '1', '4'},
icculus@2049
    71
    {'b', '2', '1'}, {'b', '2', '2'}, {'b', '2', '3'}, {'b', '2', '4'},
icculus@2049
    72
    {'b', '3', '1'}, {'b', '3', '2'}, {'b', '3', '3'}, {'b', '3', '4'},
icculus@2049
    73
    {'\0', '\0', '\0'}
icculus@2049
    74
};
slouken@0
    75
slouken@1895
    76
static int
icculus@2049
    77
OpenUserDefinedDevice(char *path, int maxlen, int flags)
slouken@0
    78
{
icculus@2049
    79
    const char *audiodev;
slouken@1895
    80
    int fd;
slouken@0
    81
icculus@2049
    82
    /* Figure out what our audio device is */
icculus@2049
    83
    if ((audiodev = SDL_getenv("SDL_PATH_DSP")) == NULL) {
icculus@2049
    84
        audiodev = SDL_getenv("AUDIODEV");
icculus@2049
    85
    }
icculus@2049
    86
    if (audiodev == NULL) {
icculus@2049
    87
        return -1;
slouken@1895
    88
    }
icculus@2049
    89
    fd = open(audiodev, flags, 0);
icculus@2049
    90
    if (path != NULL) {
icculus@2049
    91
        SDL_strlcpy(path, audiodev, maxlen);
icculus@2049
    92
        path[maxlen - 1] = '\0';
icculus@2049
    93
    }
icculus@2049
    94
    return fd;
slouken@0
    95
}
slouken@0
    96
icculus@2049
    97
static int
icculus@2049
    98
OpenAudioPath(char *path, int maxlen, int flags, int classic)
slouken@0
    99
{
icculus@2049
   100
    struct stat sb;
icculus@2049
   101
    int cycle = 0;
icculus@2049
   102
    int fd = OpenUserDefinedDevice(path, maxlen, flags);
slouken@0
   103
icculus@2049
   104
    if (fd != -1) {
icculus@2049
   105
        return fd;
slouken@1895
   106
    }
slouken@0
   107
icculus@2049
   108
    /* !!! FIXME: do we really need a table here? */
icculus@2049
   109
    while (devsettings[cycle][0] != '\0') {
icculus@2049
   110
        char audiopath[1024];
icculus@2049
   111
        SDL_snprintf(audiopath, SDL_arraysize(audiopath),
icculus@2049
   112
                     _PATH_DEV_DSP,
icculus@2049
   113
                     devsettings[cycle][0],
icculus@2049
   114
                     devsettings[cycle][1], devsettings[cycle][2]);
slouken@0
   115
icculus@2049
   116
        if (stat(audiopath, &sb) == 0) {
icculus@2049
   117
            fd = open(audiopath, flags, 0);
icculus@2049
   118
            if (fd > 0) {
icculus@2049
   119
                if (path != NULL) {
icculus@2049
   120
                    SDL_strlcpy(path, audiopath, maxlen);
icculus@2049
   121
                }
icculus@2049
   122
                return fd;
icculus@2049
   123
            }
icculus@2049
   124
        }
icculus@2049
   125
    }
icculus@2049
   126
    return -1;
slouken@0
   127
}
slouken@0
   128
slouken@0
   129
/* This function waits until it is possible to write a full sound buffer */
slouken@1895
   130
static void
icculus@2049
   131
PAUDIO_WaitDevice(_THIS)
slouken@0
   132
{
slouken@0
   133
    fd_set fdset;
slouken@0
   134
slouken@0
   135
    /* See if we need to use timed audio synchronization */
icculus@2049
   136
    if (this->hidden->frame_ticks) {
slouken@0
   137
        /* Use timer for general audio synchronization */
slouken@0
   138
        Sint32 ticks;
slouken@0
   139
slouken@2060
   140
        ticks =
slouken@2060
   141
            ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) -
slouken@2060
   142
            FUDGE_TICKS;
slouken@1895
   143
        if (ticks > 0) {
slouken@1895
   144
            SDL_Delay(ticks);
slouken@0
   145
        }
slouken@0
   146
    } else {
slouken@1895
   147
        audio_buffer paud_bufinfo;
slouken@0
   148
slouken@0
   149
        /* Use select() for audio synchronization */
slouken@0
   150
        struct timeval timeout;
slouken@0
   151
        FD_ZERO(&fdset);
icculus@2049
   152
        FD_SET(this->hidden->audio_fd, &fdset);
slouken@0
   153
icculus@2049
   154
        if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
slouken@0
   155
#ifdef DEBUG_AUDIO
slouken@0
   156
            fprintf(stderr, "Couldn't get audio buffer information\n");
slouken@0
   157
#endif
slouken@1895
   158
            timeout.tv_sec = 10;
slouken@0
   159
            timeout.tv_usec = 0;
slouken@0
   160
        } else {
slouken@1895
   161
            long ms_in_buf = paud_bufinfo.write_buf_time;
slouken@1895
   162
            timeout.tv_sec = ms_in_buf / 1000;
slouken@1895
   163
            ms_in_buf = ms_in_buf - timeout.tv_sec * 1000;
slouken@1895
   164
            timeout.tv_usec = ms_in_buf * 1000;
slouken@0
   165
#ifdef DEBUG_AUDIO
slouken@1895
   166
            fprintf(stderr,
slouken@1895
   167
                    "Waiting for write_buf_time=%ld,%ld\n",
slouken@1895
   168
                    timeout.tv_sec, timeout.tv_usec);
slouken@0
   169
#endif
slouken@1895
   170
        }
slouken@0
   171
slouken@0
   172
#ifdef DEBUG_AUDIO
slouken@0
   173
        fprintf(stderr, "Waiting for audio to get ready\n");
slouken@0
   174
#endif
slouken@2060
   175
        if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
slouken@2060
   176
            <= 0) {
slouken@1895
   177
            const char *message =
slouken@1895
   178
                "Audio timeout - buggy audio driver? (disabled)";
slouken@0
   179
            /*
slouken@1895
   180
             * In general we should never print to the screen,
slouken@0
   181
             * but in this case we have no other way of letting
slouken@0
   182
             * the user know what happened.
slouken@0
   183
             */
slouken@0
   184
            fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
slouken@0
   185
            this->enabled = 0;
slouken@0
   186
            /* Don't try to close - may hang */
icculus@2049
   187
            this->hidden->audio_fd = -1;
slouken@0
   188
#ifdef DEBUG_AUDIO
slouken@0
   189
            fprintf(stderr, "Done disabling audio\n");
slouken@0
   190
#endif
slouken@0
   191
        }
slouken@0
   192
#ifdef DEBUG_AUDIO
slouken@0
   193
        fprintf(stderr, "Ready!\n");
slouken@0
   194
#endif
slouken@0
   195
    }
slouken@0
   196
}
slouken@0
   197
slouken@1895
   198
static void
icculus@2049
   199
PAUDIO_PlayDevice(_THIS)
slouken@0
   200
{
icculus@2049
   201
    int written = 0;
icculus@2049
   202
    const Uint8 *mixbuf = this->hidden->mixbuf;
icculus@2049
   203
    const size_t mixlen = this->hidden->mixlen;
slouken@0
   204
slouken@1895
   205
    /* Write the audio data, checking for EAGAIN on broken audio drivers */
slouken@1895
   206
    do {
icculus@2049
   207
        written = write(this->hidden->audio_fd, mixbuf, mixlen);
slouken@1895
   208
        if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
slouken@1895
   209
            SDL_Delay(1);       /* Let a little CPU time go by */
slouken@1895
   210
        }
slouken@2735
   211
    } while ((written < 0) &&
slouken@2735
   212
             ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
slouken@0
   213
slouken@1895
   214
    /* If timer synchronization is enabled, set the next write frame */
icculus@2049
   215
    if (this->hidden->frame_ticks) {
icculus@2049
   216
        this->hidden->next_frame += this->hidden->frame_ticks;
slouken@1895
   217
    }
slouken@0
   218
slouken@1895
   219
    /* If we couldn't write, assume fatal error for now */
slouken@1895
   220
    if (written < 0) {
slouken@1895
   221
        this->enabled = 0;
slouken@1895
   222
    }
slouken@0
   223
#ifdef DEBUG_AUDIO
slouken@1895
   224
    fprintf(stderr, "Wrote %d bytes of audio data\n", written);
slouken@0
   225
#endif
slouken@0
   226
}
slouken@0
   227
slouken@1895
   228
static Uint8 *
icculus@2049
   229
PAUDIO_GetDeviceBuf(_THIS)
slouken@0
   230
{
icculus@2049
   231
    return this->hidden->mixbuf;
slouken@0
   232
}
slouken@0
   233
slouken@1895
   234
static void
icculus@2049
   235
PAUDIO_CloseDevice(_THIS)
slouken@0
   236
{
icculus@2049
   237
    if (this->hidden != NULL) {
icculus@2049
   238
        if (this->hidden->mixbuf != NULL) {
icculus@2049
   239
            SDL_FreeAudioMem(this->hidden->mixbuf);
icculus@2049
   240
            this->hidden->mixbuf = NULL;
icculus@2049
   241
        }
icculus@2049
   242
        if (this->hidden->audio_fd >= 0) {
icculus@2049
   243
            close(this->hidden->audio_fd);
icculus@2049
   244
            this->hidden->audio_fd = -1;
icculus@2049
   245
        }
icculus@2049
   246
        SDL_free(this->hidden);
icculus@2049
   247
        this->hidden = NULL;
slouken@1895
   248
    }
slouken@0
   249
}
slouken@0
   250
slouken@1895
   251
static int
icculus@2049
   252
PAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
slouken@0
   253
{
icculus@2049
   254
    const char *workaround = SDL_getenv("SDL_DSP_NOSELECT");
slouken@1895
   255
    char audiodev[1024];
icculus@2049
   256
    const char *err = NULL;
slouken@1895
   257
    int format;
slouken@1895
   258
    int bytes_per_sample;
icculus@1982
   259
    SDL_AudioFormat test_format;
slouken@1895
   260
    audio_init paud_init;
slouken@1895
   261
    audio_buffer paud_bufinfo;
slouken@1895
   262
    audio_status paud_status;
slouken@1895
   263
    audio_control paud_control;
slouken@1895
   264
    audio_change paud_change;
icculus@2049
   265
    int fd = -1;
slouken@0
   266
icculus@2049
   267
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   268
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   269
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   270
    if (this->hidden == NULL) {
icculus@2049
   271
        SDL_OutOfMemory();
icculus@2049
   272
        return 0;
icculus@2049
   273
    }
icculus@2049
   274
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@0
   275
slouken@1895
   276
    /* Open the audio device */
icculus@2049
   277
    fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
icculus@2049
   278
    this->hidden->audio_fd = fd;
icculus@2049
   279
    if (fd < 0) {
icculus@2049
   280
        PAUDIO_CloseDevice(this);
slouken@1895
   281
        SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
icculus@2049
   282
        return 0;
slouken@1895
   283
    }
slouken@0
   284
slouken@1895
   285
    /*
slouken@1895
   286
     * We can't set the buffer size - just ask the device for the maximum
slouken@1895
   287
     * that we can have.
slouken@1895
   288
     */
icculus@2049
   289
    if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
icculus@2049
   290
        PAUDIO_CloseDevice(this);
slouken@1895
   291
        SDL_SetError("Couldn't get audio buffer information");
icculus@2049
   292
        return 0;
slouken@1895
   293
    }
slouken@0
   294
icculus@2049
   295
    if (this->spec.channels > 1)
icculus@2049
   296
        this->spec.channels = 2;
slouken@1895
   297
    else
icculus@2049
   298
        this->spec.channels = 1;
slouken@0
   299
slouken@1895
   300
    /*
slouken@1895
   301
     * Fields in the audio_init structure:
slouken@1895
   302
     *
slouken@1895
   303
     * Ignored by us:
slouken@1895
   304
     *
slouken@1895
   305
     * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only?
slouken@1895
   306
     * paud.slot_number;         * slot number of the adapter
slouken@1895
   307
     * paud.device_id;           * adapter identification number
slouken@1895
   308
     *
slouken@1895
   309
     * Input:
slouken@1895
   310
     *
slouken@1895
   311
     * paud.srate;           * the sampling rate in Hz
slouken@1895
   312
     * paud.bits_per_sample; * 8, 16, 32, ...
slouken@1895
   313
     * paud.bsize;           * block size for this rate
slouken@1895
   314
     * paud.mode;            * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX
slouken@1895
   315
     * paud.channels;        * 1=mono, 2=stereo
slouken@1895
   316
     * paud.flags;           * FIXED - fixed length data
slouken@1895
   317
     *                       * LEFT_ALIGNED, RIGHT_ALIGNED (var len only)
slouken@1895
   318
     *                       * TWOS_COMPLEMENT - 2's complement data
slouken@1895
   319
     *                       * SIGNED - signed? comment seems wrong in sys/audio.h
slouken@1895
   320
     *                       * BIG_ENDIAN
slouken@1895
   321
     * paud.operation;       * PLAY, RECORD
slouken@1895
   322
     *
slouken@1895
   323
     * Output:
slouken@1895
   324
     *
slouken@1895
   325
     * paud.flags;           * PITCH            - pitch is supported
slouken@1895
   326
     *                       * INPUT            - input is supported
slouken@1895
   327
     *                       * OUTPUT           - output is supported
slouken@1895
   328
     *                       * MONITOR          - monitor is supported
slouken@1895
   329
     *                       * VOLUME           - volume is supported
slouken@1895
   330
     *                       * VOLUME_DELAY     - volume delay is supported
slouken@1895
   331
     *                       * BALANCE          - balance is supported
slouken@1895
   332
     *                       * BALANCE_DELAY    - balance delay is supported
slouken@1895
   333
     *                       * TREBLE           - treble control is supported
slouken@1895
   334
     *                       * BASS             - bass control is supported
slouken@1895
   335
     *                       * BESTFIT_PROVIDED - best fit returned
slouken@1895
   336
     *                       * LOAD_CODE        - DSP load needed
slouken@1895
   337
     * paud.rc;              * NO_PLAY         - DSP code can't do play requests
slouken@1895
   338
     *                       * NO_RECORD       - DSP code can't do record requests
slouken@1895
   339
     *                       * INVALID_REQUEST - request was invalid
slouken@1895
   340
     *                       * CONFLICT        - conflict with open's flags
slouken@1895
   341
     *                       * OVERLOADED      - out of DSP MIPS or memory
slouken@1895
   342
     * paud.position_resolution; * smallest increment for position
slouken@1895
   343
     */
slouken@0
   344
icculus@2049
   345
    paud_init.srate = this->spec.freq;
slouken@1895
   346
    paud_init.mode = PCM;
slouken@1895
   347
    paud_init.operation = PLAY;
icculus@2049
   348
    paud_init.channels = this->spec.channels;
slouken@0
   349
slouken@1895
   350
    /* Try for a closest match on audio format */
slouken@1895
   351
    format = 0;
icculus@2049
   352
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
slouken@1895
   353
         !format && test_format;) {
slouken@0
   354
#ifdef DEBUG_AUDIO
slouken@1895
   355
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
slouken@0
   356
#endif
slouken@1895
   357
        switch (test_format) {
slouken@1895
   358
        case AUDIO_U8:
slouken@1895
   359
            bytes_per_sample = 1;
slouken@1895
   360
            paud_init.bits_per_sample = 8;
slouken@1895
   361
            paud_init.flags = TWOS_COMPLEMENT | FIXED;
slouken@1895
   362
            format = 1;
slouken@1895
   363
            break;
slouken@1895
   364
        case AUDIO_S8:
slouken@1895
   365
            bytes_per_sample = 1;
slouken@1895
   366
            paud_init.bits_per_sample = 8;
slouken@1895
   367
            paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
slouken@1895
   368
            format = 1;
slouken@1895
   369
            break;
slouken@1895
   370
        case AUDIO_S16LSB:
slouken@1895
   371
            bytes_per_sample = 2;
slouken@1895
   372
            paud_init.bits_per_sample = 16;
slouken@1895
   373
            paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
slouken@1895
   374
            format = 1;
slouken@1895
   375
            break;
slouken@1895
   376
        case AUDIO_S16MSB:
slouken@1895
   377
            bytes_per_sample = 2;
slouken@1895
   378
            paud_init.bits_per_sample = 16;
slouken@1895
   379
            paud_init.flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED;
slouken@1895
   380
            format = 1;
slouken@1895
   381
            break;
slouken@1895
   382
        case AUDIO_U16LSB:
slouken@1895
   383
            bytes_per_sample = 2;
slouken@1895
   384
            paud_init.bits_per_sample = 16;
slouken@1895
   385
            paud_init.flags = TWOS_COMPLEMENT | FIXED;
slouken@1895
   386
            format = 1;
slouken@1895
   387
            break;
slouken@1895
   388
        case AUDIO_U16MSB:
slouken@1895
   389
            bytes_per_sample = 2;
slouken@1895
   390
            paud_init.bits_per_sample = 16;
slouken@1895
   391
            paud_init.flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED;
slouken@1895
   392
            format = 1;
slouken@1895
   393
            break;
slouken@1895
   394
        default:
slouken@1895
   395
            break;
slouken@1895
   396
        }
slouken@1895
   397
        if (!format) {
slouken@1895
   398
            test_format = SDL_NextAudioFormat();
slouken@1895
   399
        }
slouken@1895
   400
    }
slouken@1895
   401
    if (format == 0) {
slouken@0
   402
#ifdef DEBUG_AUDIO
slouken@1895
   403
        fprintf(stderr, "Couldn't find any hardware audio formats\n");
slouken@0
   404
#endif
icculus@2049
   405
        PAUDIO_CloseDevice(this);
slouken@1895
   406
        SDL_SetError("Couldn't find any hardware audio formats");
icculus@2049
   407
        return 0;
slouken@1895
   408
    }
icculus@2049
   409
    this->spec.format = test_format;
slouken@0
   410
slouken@1895
   411
    /*
slouken@1895
   412
     * We know the buffer size and the max number of subsequent writes
icculus@2049
   413
     *  that can be pending. If more than one can pend, allow the application
icculus@2049
   414
     *  to do something like double buffering between our write buffer and
icculus@2049
   415
     *  the device's own buffer that we are filling with write() anyway.
slouken@1895
   416
     *
icculus@2049
   417
     * We calculate this->spec.samples like this because
icculus@2049
   418
     *  SDL_CalculateAudioSpec() will give put paud_bufinfo.write_buf_cap
icculus@2049
   419
     *  (or paud_bufinfo.write_buf_cap/2) into this->spec.size in return.
slouken@1895
   420
     */
slouken@1895
   421
    if (paud_bufinfo.request_buf_cap == 1) {
icculus@2049
   422
        this->spec.samples = paud_bufinfo.write_buf_cap
icculus@2049
   423
            / bytes_per_sample / this->spec.channels;
slouken@1895
   424
    } else {
icculus@2049
   425
        this->spec.samples = paud_bufinfo.write_buf_cap
icculus@2049
   426
            / bytes_per_sample / this->spec.channels / 2;
slouken@1895
   427
    }
icculus@2049
   428
    paud_init.bsize = bytes_per_sample * this->spec.channels;
slouken@0
   429
icculus@2049
   430
    SDL_CalculateAudioSpec(&this->spec);
slouken@0
   431
slouken@1895
   432
    /*
slouken@1895
   433
     * The AIX paud device init can't modify the values of the audio_init
slouken@1895
   434
     * structure that we pass to it. So we don't need any recalculation
slouken@1895
   435
     * of this stuff and no reinit call as in linux dsp and dma code.
slouken@1895
   436
     *
slouken@1895
   437
     * /dev/paud supports all of the encoding formats, so we don't need
slouken@1895
   438
     * to do anything like reopening the device, either.
slouken@1895
   439
     */
icculus@2049
   440
    if (ioctl(fd, AUDIO_INIT, &paud_init) < 0) {
slouken@1895
   441
        switch (paud_init.rc) {
slouken@1895
   442
        case 1:
icculus@2049
   443
            err = "Couldn't set audio format: DSP can't do play requests";
slouken@1895
   444
            break;
slouken@1895
   445
        case 2:
icculus@2049
   446
            err = "Couldn't set audio format: DSP can't do record requests";
slouken@1895
   447
            break;
slouken@1895
   448
        case 4:
icculus@2049
   449
            err = "Couldn't set audio format: request was invalid";
slouken@1895
   450
            break;
slouken@1895
   451
        case 5:
icculus@2049
   452
            err = "Couldn't set audio format: conflict with open's flags";
slouken@1895
   453
            break;
slouken@1895
   454
        case 6:
icculus@2049
   455
            err = "Couldn't set audio format: out of DSP MIPS or memory";
slouken@1895
   456
            break;
slouken@1895
   457
        default:
icculus@2049
   458
            err = "Couldn't set audio format: not documented in sys/audio.h";
slouken@1895
   459
            break;
slouken@1895
   460
        }
slouken@1895
   461
    }
slouken@0
   462
icculus@2049
   463
    if (err != NULL) {
icculus@2049
   464
        PAUDIO_CloseDevice(this);
icculus@2049
   465
        SDL_SetError("Paudio: %s", err);
icculus@2049
   466
        return 0;
icculus@2049
   467
    }
icculus@2049
   468
slouken@1895
   469
    /* Allocate mixing buffer */
icculus@2049
   470
    this->hidden->mixlen = this->spec.size;
icculus@2049
   471
    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
icculus@2049
   472
    if (this->hidden->mixbuf == NULL) {
icculus@2049
   473
        PAUDIO_CloseDevice(this);
icculus@2049
   474
        SDL_OutOfMemory();
icculus@2049
   475
        return 0;
slouken@1895
   476
    }
icculus@2049
   477
    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
slouken@0
   478
slouken@1895
   479
    /*
slouken@1895
   480
     * Set some paramters: full volume, first speaker that we can find.
slouken@1895
   481
     * Ignore the other settings for now.
slouken@1895
   482
     */
slouken@1895
   483
    paud_change.input = AUDIO_IGNORE;   /* the new input source */
slouken@1895
   484
    paud_change.output = OUTPUT_1;      /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */
slouken@1895
   485
    paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
slouken@1895
   486
    paud_change.volume = 0x7fffffff;    /* volume level [0-0x7fffffff] */
slouken@1895
   487
    paud_change.volume_delay = AUDIO_IGNORE;    /* the new volume delay */
slouken@1895
   488
    paud_change.balance = 0x3fffffff;   /* the new balance */
slouken@1895
   489
    paud_change.balance_delay = AUDIO_IGNORE;   /* the new balance delay */
slouken@1895
   490
    paud_change.treble = AUDIO_IGNORE;  /* the new treble state */
slouken@1895
   491
    paud_change.bass = AUDIO_IGNORE;    /* the new bass state */
slouken@1895
   492
    paud_change.pitch = AUDIO_IGNORE;   /* the new pitch state */
slouken@0
   493
slouken@1895
   494
    paud_control.ioctl_request = AUDIO_CHANGE;
slouken@1895
   495
    paud_control.request_info = (char *) &paud_change;
icculus@2049
   496
    if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
slouken@0
   497
#ifdef DEBUG_AUDIO
slouken@1895
   498
        fprintf(stderr, "Can't change audio display settings\n");
slouken@0
   499
#endif
slouken@1895
   500
    }
slouken@0
   501
slouken@1895
   502
    /*
slouken@1895
   503
     * Tell the device to expect data. Actual start will wait for
slouken@1895
   504
     * the first write() call.
slouken@1895
   505
     */
slouken@1895
   506
    paud_control.ioctl_request = AUDIO_START;
slouken@1895
   507
    paud_control.position = 0;
icculus@2049
   508
    if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
icculus@2049
   509
        PAUDIO_CloseDevice(this);
slouken@0
   510
#ifdef DEBUG_AUDIO
slouken@1895
   511
        fprintf(stderr, "Can't start audio play\n");
slouken@0
   512
#endif
slouken@1895
   513
        SDL_SetError("Can't start audio play");
icculus@2049
   514
        return 0;
slouken@1895
   515
    }
slouken@0
   516
slouken@1895
   517
    /* Check to see if we need to use select() workaround */
icculus@2049
   518
    if (workaround != NULL) {
icculus@2049
   519
        this->hidden->frame_ticks = (float) (this->spec.samples * 1000) /
slouken@2060
   520
            this->spec.freq;
icculus@2049
   521
        this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
slouken@1895
   522
    }
slouken@0
   523
slouken@1895
   524
    /* We're ready to rock and roll. :-) */
icculus@2049
   525
    return 1;
slouken@0
   526
}
slouken@0
   527
icculus@2049
   528
static int
slouken@2060
   529
PAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   530
{
icculus@2938
   531
    /* !!! FIXME: not right for device enum? */
icculus@2049
   532
    int fd = OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
icculus@2049
   533
    if (fd < 0) {
icculus@2049
   534
        SDL_SetError("PAUDIO: Couldn't open audio device");
icculus@2049
   535
        return 0;
icculus@2049
   536
    }
icculus@2049
   537
    close(fd);
icculus@2049
   538
icculus@2049
   539
    /* Set the function pointers */
icculus@2049
   540
    impl->OpenDevice = DSP_OpenDevice;
icculus@2049
   541
    impl->PlayDevice = DSP_PlayDevice;
icculus@2049
   542
    impl->PlayDevice = DSP_WaitDevice;
icculus@2049
   543
    impl->GetDeviceBuf = DSP_GetDeviceBuf;
icculus@2049
   544
    impl->CloseDevice = DSP_CloseDevice;
slouken@2060
   545
    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: add device enum! */
icculus@2049
   546
icculus@2938
   547
    /* !!! FIXME: device enum might make this 1. */
slouken@2942
   548
    return 2;                   /* 2 == definitely has an audio device. */
icculus@2049
   549
}
icculus@2049
   550
icculus@2049
   551
AudioBootStrap PAUDIO_bootstrap = {
icculus@2049
   552
    PAUDIO_DRIVER_NAME, "AIX Paudio", PAUDIO_Init, 0
icculus@2049
   553
};
icculus@2049
   554
slouken@1895
   555
/* vi: set ts=4 sw=4 expandtab: */