/* 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 */ /* Allow access to a raw mixing buffer */ #include #include #include #include #include #include #include #include #include #include #include #include "SDL_audio.h" #include "SDL_error.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) free(this->hidden->playbuf._buffer); if(this->hidden->fillbuf._buffer) free(this->hidden->fillbuf._buffer); _somFree( this->hidden->umsdev ); free(this->hidden); 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 *)malloc(sizeof(SDL_AudioDevice)); if ( this ) { memset(this, 0, (sizeof *this)); this->hidden = (struct SDL_PrivateAudioData *)malloc((sizeof *this->hidden)); } if ( (this == NULL) || (this->hidden == NULL) ) { SDL_OutOfMemory(); if ( this ) { free(this); } return(0); } 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(); memcpy( &swpbuf, &this->hidden->playbuf, sizeof(UMSAudioTypes_Buffer) ); memcpy( &this->hidden->playbuf, &this->hidden->fillbuf, sizeof(UMSAudioTypes_Buffer) ); 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<size; ++frag_spec ); if ( (0x01<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*)malloc(spec->size); this->hidden->fillbuf._length = 0; this->hidden->fillbuf._maximum = spec->size; this->hidden->fillbuf._buffer = (unsigned char*)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 ); }