blob: e873b56365839e17c32b2c30ed598e3d64aa606a [file] [log] [blame]
/*
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, "AIX 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 );
}