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