view src/audio/ums/SDL_umsaudio.c @ 1662:782fd950bd46 SDL-1.3

Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API. WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid. The code is now run through a consistent indent format: indent -i4 -nut -nsc -br -ce The headers are being converted to automatically generate doxygen documentation.
author Sam Lantinga <slouken@libsdl.org>
date Sun, 28 May 2006 13:04:16 +0000
parents d910939febfa
children 4da1ee79c9af
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Carsten Griwodz
    griff@kom.tu-darmstadt.de

    based on linux/SDL_dspaudio.c by Sam Lantinga
*/
#include "SDL_config.h"

/* Allow access to a raw mixing buffer */

#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
#include "SDL_umsaudio.h"

/* The tag name used by UMS audio */
#define UMS_DRIVER_NAME         "ums"

#define DEBUG_AUDIO 1

/* Audio driver functions */
static int UMS_OpenAudio (_THIS, SDL_AudioSpec * spec);
static void UMS_PlayAudio (_THIS);
static Uint8 *UMS_GetAudioBuf (_THIS);
static void UMS_CloseAudio (_THIS);

static UMSAudioDevice_ReturnCode UADOpen (_THIS, string device, string mode,
                                          long flags);
static UMSAudioDevice_ReturnCode UADClose (_THIS);
static UMSAudioDevice_ReturnCode UADGetBitsPerSample (_THIS, long *bits);
static UMSAudioDevice_ReturnCode UADSetBitsPerSample (_THIS, long bits);
static UMSAudioDevice_ReturnCode UADSetSampleRate (_THIS, long rate,
                                                   long *set_rate);
static UMSAudioDevice_ReturnCode UADSetByteOrder (_THIS, string byte_order);
static UMSAudioDevice_ReturnCode UADSetAudioFormatType (_THIS, string fmt);
static UMSAudioDevice_ReturnCode UADSetNumberFormat (_THIS, string fmt);
static UMSAudioDevice_ReturnCode UADInitialize (_THIS);
static UMSAudioDevice_ReturnCode UADStart (_THIS);
static UMSAudioDevice_ReturnCode UADStop (_THIS);
static UMSAudioDevice_ReturnCode UADSetTimeFormat (_THIS,
                                                   UMSAudioTypes_TimeFormat
                                                   fmt);
static UMSAudioDevice_ReturnCode UADWriteBuffSize (_THIS, long *buff_size);
static UMSAudioDevice_ReturnCode UADWriteBuffRemain (_THIS, long *buff_size);
static UMSAudioDevice_ReturnCode UADWriteBuffUsed (_THIS, long *buff_size);
static UMSAudioDevice_ReturnCode UADSetDMABufferSize (_THIS, long bytes,
                                                      long *bytes_ret);
static UMSAudioDevice_ReturnCode UADSetVolume (_THIS, long volume);
static UMSAudioDevice_ReturnCode UADSetBalance (_THIS, long balance);
static UMSAudioDevice_ReturnCode UADSetChannels (_THIS, long channels);
static UMSAudioDevice_ReturnCode UADPlayRemainingData (_THIS, boolean block);
static UMSAudioDevice_ReturnCode UADEnableOutput (_THIS, string output,
                                                  long *left_gain,
                                                  long *right_gain);
static UMSAudioDevice_ReturnCode UADWrite (_THIS, UMSAudioTypes_Buffer * buff,
                                           long samples,
                                           long *samples_written);

/* Audio driver bootstrap functions */
static int
Audio_Available (void)
{
    return 1;
}

static void
Audio_DeleteDevice (_THIS)
{
    if (this->hidden->playbuf._buffer)
        SDL_free (this->hidden->playbuf._buffer);
    if (this->hidden->fillbuf._buffer)
        SDL_free (this->hidden->fillbuf._buffer);
    _somFree (this->hidden->umsdev);
    SDL_free (this->hidden);
    SDL_free (this);
}

static SDL_AudioDevice *
Audio_CreateDevice (int devindex)
{
    SDL_AudioDevice *this;

    /*
     * Allocate and initialize management storage and private management
     * storage for this SDL-using library.
     */
    this = (SDL_AudioDevice *) SDL_malloc (sizeof (SDL_AudioDevice));
    if (this) {
        SDL_memset (this, 0, (sizeof *this));
        this->hidden = (struct SDL_PrivateAudioData *)
            SDL_malloc ((sizeof *this->hidden));
    }
    if ((this == NULL) || (this->hidden == NULL)) {
        SDL_OutOfMemory ();
        if (this) {
            SDL_free (this);
        }
        return (0);
    }
    SDL_memset (this->hidden, 0, (sizeof *this->hidden));
#ifdef DEBUG_AUDIO
    fprintf (stderr, "Creating UMS Audio device\n");
#endif

    /*
     * Calls for UMS env initialization and audio object construction.
     */
    this->hidden->ev = somGetGlobalEnvironment ();
    this->hidden->umsdev = UMSAudioDeviceNew ();

    /*
     * Set the function pointers.
     */
    this->OpenAudio = UMS_OpenAudio;
    this->WaitAudio = NULL;     /* we do blocking output */
    this->PlayAudio = UMS_PlayAudio;
    this->GetAudioBuf = UMS_GetAudioBuf;
    this->CloseAudio = UMS_CloseAudio;
    this->free = Audio_DeleteDevice;

#ifdef DEBUG_AUDIO
    fprintf (stderr, "done\n");
#endif
    return this;
}

AudioBootStrap UMS_bootstrap = {
    UMS_DRIVER_NAME, "AUX UMS audio",
    Audio_Available, Audio_CreateDevice
};

static Uint8 *
UMS_GetAudioBuf (_THIS)
{
#ifdef DEBUG_AUDIO
    fprintf (stderr, "enter UMS_GetAudioBuf\n");
#endif
    return this->hidden->fillbuf._buffer;
/*
    long                      bufSize;
    UMSAudioDevice_ReturnCode rc;

    rc = UADSetTimeFormat(this, UMSAudioTypes_Bytes );
    rc = UADWriteBuffSize(this,  bufSize );
*/
}

static void
UMS_CloseAudio (_THIS)
{
    UMSAudioDevice_ReturnCode rc;

#ifdef DEBUG_AUDIO
    fprintf (stderr, "enter UMS_CloseAudio\n");
#endif
    rc = UADPlayRemainingData (this, TRUE);
    rc = UADStop (this);
    rc = UADClose (this);
}

static void
UMS_PlayAudio (_THIS)
{
    UMSAudioDevice_ReturnCode rc;
    long samplesToWrite;
    long samplesWritten;
    UMSAudioTypes_Buffer swpbuf;

#ifdef DEBUG_AUDIO
    fprintf (stderr, "enter UMS_PlayAudio\n");
#endif
    samplesToWrite =
        this->hidden->playbuf._length / this->hidden->bytesPerSample;
    do {
        rc = UADWrite (this, &this->hidden->playbuf,
                       samplesToWrite, &samplesWritten);
        samplesToWrite -= samplesWritten;

        /* rc values: UMSAudioDevice_Success
         *            UMSAudioDevice_Failure
         *            UMSAudioDevice_Preempted
         *            UMSAudioDevice_Interrupted
         *            UMSAudioDevice_DeviceError
         */
        if (rc == UMSAudioDevice_DeviceError) {
#ifdef DEBUG_AUDIO
            fprintf (stderr, "Returning from PlayAudio with devices error\n");
#endif
            return;
        }
    }
    while (samplesToWrite > 0);

    SDL_LockAudio ();
    SDL_memcpy (&swpbuf, &this->hidden->playbuf,
                sizeof (UMSAudioTypes_Buffer));
    SDL_memcpy (&this->hidden->playbuf, &this->hidden->fillbuf,
                sizeof (UMSAudioTypes_Buffer));
    SDL_memcpy (&this->hidden->fillbuf, &swpbuf,
                sizeof (UMSAudioTypes_Buffer));
    SDL_UnlockAudio ();

#ifdef DEBUG_AUDIO
    fprintf (stderr, "Wrote audio data and swapped buffer\n");
#endif
}

#if 0
//      /* Set the DSP frequency */
//      value = spec->freq;
//      if ( ioctl(this->hidden->audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) {
//              SDL_SetError("Couldn't set audio frequency");
//              return(-1);
//      }
//      spec->freq = value;
#endif

static int
UMS_OpenAudio (_THIS, SDL_AudioSpec * spec)
{
    char *audiodev = "/dev/paud0";
    long lgain;
    long rgain;
    long outRate;
    long outBufSize;
    long bitsPerSample;
    long samplesPerSec;
    long success;
    Uint16 test_format;
    int frag_spec;
    UMSAudioDevice_ReturnCode rc;

#ifdef DEBUG_AUDIO
    fprintf (stderr, "enter UMS_OpenAudio\n");
#endif
    rc = UADOpen (this, audiodev, "PLAY", UMSAudioDevice_BlockingIO);
    if (rc != UMSAudioDevice_Success) {
        SDL_SetError ("Couldn't open %s: %s", audiodev, strerror (errno));
        return -1;
    }

    rc = UADSetAudioFormatType (this, "PCM");

    success = 0;
    test_format = SDL_FirstAudioFormat (spec->format);
    do {
#ifdef DEBUG_AUDIO
        fprintf (stderr, "Trying format 0x%4.4x\n", test_format);
#endif
        switch (test_format) {
        case AUDIO_U8:
/* from the mac code: better ? */
/* sample_bits = spec->size / spec->samples / spec->channels * 8; */
            success = 1;
            bitsPerSample = 8;
            rc = UADSetSampleRate (this, spec->freq << 16, &outRate);
            rc = UADSetByteOrder (this, "MSB"); /* irrelevant */
            rc = UADSetNumberFormat (this, "UNSIGNED");
            break;
        case AUDIO_S8:
            success = 1;
            bitsPerSample = 8;
            rc = UADSetSampleRate (this, spec->freq << 16, &outRate);
            rc = UADSetByteOrder (this, "MSB"); /* irrelevant */
            rc = UADSetNumberFormat (this, "SIGNED");
            break;
        case AUDIO_S16LSB:
            success = 1;
            bitsPerSample = 16;
            rc = UADSetSampleRate (this, spec->freq << 16, &outRate);
            rc = UADSetByteOrder (this, "LSB");
            rc = UADSetNumberFormat (this, "SIGNED");
            break;
        case AUDIO_S16MSB:
            success = 1;
            bitsPerSample = 16;
            rc = UADSetSampleRate (this, spec->freq << 16, &outRate);
            rc = UADSetByteOrder (this, "MSB");
            rc = UADSetNumberFormat (this, "SIGNED");
            break;
        case AUDIO_U16LSB:
            success = 1;
            bitsPerSample = 16;
            rc = UADSetSampleRate (this, spec->freq << 16, &outRate);
            rc = UADSetByteOrder (this, "LSB");
            rc = UADSetNumberFormat (this, "UNSIGNED");
            break;
        case AUDIO_U16MSB:
            success = 1;
            bitsPerSample = 16;
            rc = UADSetSampleRate (this, spec->freq << 16, &outRate);
            rc = UADSetByteOrder (this, "MSB");
            rc = UADSetNumberFormat (this, "UNSIGNED");
            break;
        default:
            break;
        }
        if (!success) {
            test_format = SDL_NextAudioFormat ();
        }
    }
    while (!success && test_format);

    if (success == 0) {
        SDL_SetError ("Couldn't find any hardware audio formats");
        return -1;
    }

    spec->format = test_format;

    for (frag_spec = 0; (0x01 << frag_spec) < spec->size; ++frag_spec);
    if ((0x01 << frag_spec) != spec->size) {
        SDL_SetError ("Fragment size must be a power of two");
        return -1;
    }
    if (frag_spec > 2048)
        frag_spec = 2048;

    this->hidden->bytesPerSample = (bitsPerSample / 8) * spec->channels;
    samplesPerSec = this->hidden->bytesPerSample * outRate;

    this->hidden->playbuf._length = 0;
    this->hidden->playbuf._maximum = spec->size;
    this->hidden->playbuf._buffer = (unsigned char *) SDL_malloc (spec->size);
    this->hidden->fillbuf._length = 0;
    this->hidden->fillbuf._maximum = spec->size;
    this->hidden->fillbuf._buffer = (unsigned char *) SDL_malloc (spec->size);

    rc = UADSetBitsPerSample (this, bitsPerSample);
    rc = UADSetDMABufferSize (this, frag_spec, &outBufSize);
    rc = UADSetChannels (this, spec->channels); /* functions reduces to mono or stereo */

    lgain = 100;                /*maximum left input gain */
    rgain = 100;                /*maimum right input gain */
    rc = UADEnableOutput (this, "LINE_OUT", &lgain, &rgain);
    rc = UADInitialize (this);
    rc = UADStart (this);
    rc = UADSetVolume (this, 100);
    rc = UADSetBalance (this, 0);

    /* We're ready to rock and roll. :-) */
    return 0;
}


static UMSAudioDevice_ReturnCode
UADGetBitsPerSample (_THIS, long *bits)
{
    return UMSAudioDevice_get_bits_per_sample (this->hidden->umsdev,
                                               this->hidden->ev, bits);
}

static UMSAudioDevice_ReturnCode
UADSetBitsPerSample (_THIS, long bits)
{
    return UMSAudioDevice_set_bits_per_sample (this->hidden->umsdev,
                                               this->hidden->ev, bits);
}

static UMSAudioDevice_ReturnCode
UADSetSampleRate (_THIS, long rate, long *set_rate)
{
    /* from the mac code: sample rate = spec->freq << 16; */
    return UMSAudioDevice_set_sample_rate (this->hidden->umsdev,
                                           this->hidden->ev, rate, set_rate);
}

static UMSAudioDevice_ReturnCode
UADSetByteOrder (_THIS, string byte_order)
{
    return UMSAudioDevice_set_byte_order (this->hidden->umsdev,
                                          this->hidden->ev, byte_order);
}

static UMSAudioDevice_ReturnCode
UADSetAudioFormatType (_THIS, string fmt)
{
    /* possible PCM, A_LAW or MU_LAW */
    return UMSAudioDevice_set_audio_format_type (this->hidden->umsdev,
                                                 this->hidden->ev, fmt);
}

static UMSAudioDevice_ReturnCode
UADSetNumberFormat (_THIS, string fmt)
{
    /* possible SIGNED, UNSIGNED, or TWOS_COMPLEMENT */
    return UMSAudioDevice_set_number_format (this->hidden->umsdev,
                                             this->hidden->ev, fmt);
}

static UMSAudioDevice_ReturnCode
UADInitialize (_THIS)
{
    return UMSAudioDevice_initialize (this->hidden->umsdev, this->hidden->ev);
}

static UMSAudioDevice_ReturnCode
UADStart (_THIS)
{
    return UMSAudioDevice_start (this->hidden->umsdev, this->hidden->ev);
}

static UMSAudioDevice_ReturnCode
UADSetTimeFormat (_THIS, UMSAudioTypes_TimeFormat fmt)
{
    /*
     * Switches the time format to the new format, immediately.
     * possible UMSAudioTypes_Msecs, UMSAudioTypes_Bytes or UMSAudioTypes_Samples
     */
    return UMSAudioDevice_set_time_format (this->hidden->umsdev,
                                           this->hidden->ev, fmt);
}

static UMSAudioDevice_ReturnCode
UADWriteBuffSize (_THIS, long *buff_size)
{
    /*
     * returns write buffer size in the current time format
     */
    return UMSAudioDevice_write_buff_size (this->hidden->umsdev,
                                           this->hidden->ev, buff_size);
}

static UMSAudioDevice_ReturnCode
UADWriteBuffRemain (_THIS, long *buff_size)
{
    /*
     * returns amount of available space in the write buffer
     * in the current time format
     */
    return UMSAudioDevice_write_buff_remain (this->hidden->umsdev,
                                             this->hidden->ev, buff_size);
}

static UMSAudioDevice_ReturnCode
UADWriteBuffUsed (_THIS, long *buff_size)
{
    /*
     * returns amount of filled space in the write buffer
     * in the current time format
     */
    return UMSAudioDevice_write_buff_used (this->hidden->umsdev,
                                           this->hidden->ev, buff_size);
}

static UMSAudioDevice_ReturnCode
UADSetDMABufferSize (_THIS, long bytes, long *bytes_ret)
{
    /*
     * Request a new DMA buffer size, maximum requested size 2048.
     * Takes effect with next initialize() call.
     * Devices may or may not support DMA.
     */
    return UMSAudioDevice_set_DMA_buffer_size (this->hidden->umsdev,
                                               this->hidden->ev,
                                               bytes, bytes_ret);
}

static UMSAudioDevice_ReturnCode
UADSetVolume (_THIS, long volume)
{
    /*
     * Set the volume.
     * Takes effect immediately.
     */
    return UMSAudioDevice_set_volume (this->hidden->umsdev,
                                      this->hidden->ev, volume);
}

static UMSAudioDevice_ReturnCode
UADSetBalance (_THIS, long balance)
{
    /*
     * Set the balance.
     * Takes effect immediately.
     */
    return UMSAudioDevice_set_balance (this->hidden->umsdev,
                                       this->hidden->ev, balance);
}

static UMSAudioDevice_ReturnCode
UADSetChannels (_THIS, long channels)
{
    /*
     * Set mono or stereo.
     * Takes effect with next initialize() call.
     */
    if (channels != 1)
        channels = 2;
    return UMSAudioDevice_set_number_of_channels (this->hidden->umsdev,
                                                  this->hidden->ev, channels);
}

static UMSAudioDevice_ReturnCode
UADOpen (_THIS, string device, string mode, long flags)
{
    return UMSAudioDevice_open (this->hidden->umsdev,
                                this->hidden->ev, device, mode, flags);
}

static UMSAudioDevice_ReturnCode
UADWrite (_THIS, UMSAudioTypes_Buffer * buff,
          long samples, long *samples_written)
{
    return UMSAudioDevice_write (this->hidden->umsdev,
                                 this->hidden->ev,
                                 buff, samples, samples_written);
}

static UMSAudioDevice_ReturnCode
UADPlayRemainingData (_THIS, boolean block)
{
    return UMSAudioDevice_play_remaining_data (this->hidden->umsdev,
                                               this->hidden->ev, block);
}

static UMSAudioDevice_ReturnCode
UADStop (_THIS)
{
    return UMSAudioDevice_stop (this->hidden->umsdev, this->hidden->ev);
}

static UMSAudioDevice_ReturnCode
UADClose (_THIS)
{
    return UMSAudioDevice_close (this->hidden->umsdev, this->hidden->ev);
}

static UMSAudioDevice_ReturnCode
UADEnableOutput (_THIS, string output, long *left_gain, long *right_gain)
{
    return UMSAudioDevice_enable_output (this->hidden->umsdev,
                                         this->hidden->ev,
                                         output, left_gain, right_gain);
}

/* vi: set ts=4 sw=4 expandtab: */