Home | History | Annotate | Download | only in bsd
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Library General Public
      7     License as published by the Free Software Foundation; either
      8     version 2 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     Library General Public License for more details.
     14 
     15     You should have received a copy of the GNU Library General Public
     16     License along with this library; if not, write to the Free
     17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 /*
     25  * Driver for native OpenBSD/NetBSD audio(4).
     26  * vedge (at) vedge.com.ar.
     27  */
     28 
     29 #include <errno.h>
     30 #include <unistd.h>
     31 #include <fcntl.h>
     32 #include <sys/time.h>
     33 #include <sys/ioctl.h>
     34 #include <sys/stat.h>
     35 #include <sys/types.h>
     36 #include <sys/audioio.h>
     37 
     38 #include "SDL_timer.h"
     39 #include "SDL_audio.h"
     40 #include "../SDL_audiomem.h"
     41 #include "../SDL_audio_c.h"
     42 #include "../SDL_audiodev_c.h"
     43 #include "SDL_bsdaudio.h"
     44 
     45 /* The tag name used by NetBSD/OpenBSD audio */
     46 #ifdef __NetBSD__
     47 #define BSD_AUDIO_DRIVER_NAME         "netbsd"
     48 #define BSD_AUDIO_DRIVER_DESC         "Native NetBSD audio"
     49 #else
     50 #define BSD_AUDIO_DRIVER_NAME         "openbsd"
     51 #define BSD_AUDIO_DRIVER_DESC         "Native OpenBSD audio"
     52 #endif
     53 
     54 /* Open the audio device for playback, and don't block if busy */
     55 /* #define USE_BLOCKING_WRITES */
     56 
     57 /* Use timer for synchronization */
     58 /* #define USE_TIMER_SYNC */
     59 
     60 /* #define DEBUG_AUDIO */
     61 /* #define DEBUG_AUDIO_STREAM */
     62 
     63 #ifdef USE_BLOCKING_WRITES
     64 #define OPEN_FLAGS	O_WRONLY
     65 #else
     66 #define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
     67 #endif
     68 
     69 /* Audio driver functions */
     70 static void OBSD_WaitAudio(_THIS);
     71 static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec);
     72 static void OBSD_PlayAudio(_THIS);
     73 static Uint8 *OBSD_GetAudioBuf(_THIS);
     74 static void OBSD_CloseAudio(_THIS);
     75 
     76 #ifdef DEBUG_AUDIO
     77 static void OBSD_Status(_THIS);
     78 #endif
     79 
     80 /* Audio driver bootstrap functions */
     81 
     82 static int
     83 Audio_Available(void)
     84 {
     85     int fd;
     86     int available;
     87 
     88     available = 0;
     89     fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
     90     if(fd >= 0) {
     91 	available = 1;
     92 	close(fd);
     93     }
     94     return(available);
     95 }
     96 
     97 static void
     98 Audio_DeleteDevice(SDL_AudioDevice *device)
     99 {
    100     SDL_free(device->hidden);
    101     SDL_free(device);
    102 }
    103 
    104 static SDL_AudioDevice
    105 *Audio_CreateDevice(int devindex)
    106 {
    107     SDL_AudioDevice *this;
    108 
    109     /* Initialize all variables that we clean on shutdown */
    110     this = (SDL_AudioDevice*)SDL_malloc(sizeof(SDL_AudioDevice));
    111     if(this) {
    112 	SDL_memset(this, 0, (sizeof *this));
    113 	this->hidden =
    114 	    (struct SDL_PrivateAudioData*)SDL_malloc((sizeof *this->hidden));
    115     }
    116     if((this == NULL) || (this->hidden == NULL)) {
    117 	SDL_OutOfMemory();
    118 	if(this) SDL_free(this);
    119 	return(0);
    120     }
    121     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    122     audio_fd = -1;
    123 
    124     /* Set the function pointers */
    125     this->OpenAudio = OBSD_OpenAudio;
    126     this->WaitAudio = OBSD_WaitAudio;
    127     this->PlayAudio = OBSD_PlayAudio;
    128     this->GetAudioBuf = OBSD_GetAudioBuf;
    129     this->CloseAudio = OBSD_CloseAudio;
    130 
    131     this->free = Audio_DeleteDevice;
    132 
    133     return this;
    134 }
    135 
    136 AudioBootStrap BSD_AUDIO_bootstrap = {
    137 	BSD_AUDIO_DRIVER_NAME, BSD_AUDIO_DRIVER_DESC,
    138 	Audio_Available, Audio_CreateDevice
    139 };
    140 
    141 /* This function waits until it is possible to write a full sound buffer */
    142 static void
    143 OBSD_WaitAudio(_THIS)
    144 {
    145 #ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
    146 	/* See if we need to use timed audio synchronization */
    147 	if ( frame_ticks ) {
    148 		/* Use timer for general audio synchronization */
    149 		Sint32 ticks;
    150 
    151 		ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
    152 		if ( ticks > 0 ) {
    153 			SDL_Delay(ticks);
    154 		}
    155 	} else {
    156 		/* Use select() for audio synchronization */
    157 		fd_set fdset;
    158 		struct timeval timeout;
    159 
    160 		FD_ZERO(&fdset);
    161 		FD_SET(audio_fd, &fdset);
    162 		timeout.tv_sec = 10;
    163 		timeout.tv_usec = 0;
    164 #ifdef DEBUG_AUDIO
    165 		fprintf(stderr, "Waiting for audio to get ready\n");
    166 #endif
    167 		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
    168 			const char *message =
    169 			"Audio timeout - buggy audio driver? (disabled)";
    170 			/* In general we should never print to the screen,
    171 			   but in this case we have no other way of letting
    172 			   the user know what happened.
    173 			*/
    174 			fprintf(stderr, "SDL: %s\n", message);
    175 			this->enabled = 0;
    176 			/* Don't try to close - may hang */
    177 			audio_fd = -1;
    178 #ifdef DEBUG_AUDIO
    179 			fprintf(stderr, "Done disabling audio\n");
    180 #endif
    181 		}
    182 #ifdef DEBUG_AUDIO
    183 		fprintf(stderr, "Ready!\n");
    184 #endif
    185 	}
    186 #endif /* !USE_BLOCKING_WRITES */
    187 }
    188 
    189 static void
    190 OBSD_PlayAudio(_THIS)
    191 {
    192 	int written, p=0;
    193 
    194 	/* Write the audio data, checking for EAGAIN on broken audio drivers */
    195 	do {
    196 		written = write(audio_fd, &mixbuf[p], mixlen-p);
    197 		if (written>0)
    198 		   p += written;
    199 		if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR)
    200 		{
    201 		   /* Non recoverable error has occurred. It should be reported!!! */
    202 		   perror("audio");
    203 		   break;
    204 		}
    205 
    206 		if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) {
    207 			SDL_Delay(1);	/* Let a little CPU time go by */
    208 		}
    209 	} while ( p < written );
    210 
    211 	/* If timer synchronization is enabled, set the next write frame */
    212 	if ( frame_ticks ) {
    213 		next_frame += frame_ticks;
    214 	}
    215 
    216 	/* If we couldn't write, assume fatal error for now */
    217 	if ( written < 0 ) {
    218 		this->enabled = 0;
    219 	}
    220 #ifdef DEBUG_AUDIO
    221 	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
    222 #endif
    223 }
    224 
    225 static Uint8
    226 *OBSD_GetAudioBuf(_THIS)
    227 {
    228     return(mixbuf);
    229 }
    230 
    231 static void
    232 OBSD_CloseAudio(_THIS)
    233 {
    234     if(mixbuf != NULL) {
    235 	SDL_FreeAudioMem(mixbuf);
    236 	mixbuf = NULL;
    237     }
    238     if(audio_fd >= 0) {
    239 	close(audio_fd);
    240 	audio_fd = -1;
    241     }
    242 }
    243 
    244 #ifdef DEBUG_AUDIO
    245 void
    246 OBSD_Status(_THIS)
    247 {
    248     audio_info_t info;
    249 
    250     if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
    251 	fprintf(stderr,"AUDIO_GETINFO failed.\n");
    252 	return;
    253     }
    254 
    255     fprintf(stderr,"\n"
    256 "[play/record info]\n"
    257 "buffer size	:   %d bytes\n"
    258 "sample rate	:   %i Hz\n"
    259 "channels	:   %i\n"
    260 "precision	:   %i-bit\n"
    261 "encoding	:   0x%x\n"
    262 "seek		:   %i\n"
    263 "sample count	:   %i\n"
    264 "EOF count	:   %i\n"
    265 "paused		:   %s\n"
    266 "error occured	:   %s\n"
    267 "waiting		:   %s\n"
    268 "active		:   %s\n"
    269 "",
    270     info.play.buffer_size,
    271     info.play.sample_rate,
    272     info.play.channels,
    273     info.play.precision,
    274     info.play.encoding,
    275     info.play.seek,
    276     info.play.samples,
    277     info.play.eof,
    278     info.play.pause ? "yes" : "no",
    279     info.play.error ? "yes" : "no",
    280     info.play.waiting ? "yes" : "no",
    281     info.play.active ? "yes": "no");
    282 
    283     fprintf(stderr,"\n"
    284 "[audio info]\n"
    285 "monitor_gain	:   %i\n"
    286 "hw block size	:   %d bytes\n"
    287 "hi watermark	:   %i\n"
    288 "lo watermark	:   %i\n"
    289 "audio mode	:   %s\n"
    290 "",
    291     info.monitor_gain,
    292     info.blocksize,
    293     info.hiwat, info.lowat,
    294     (info.mode == AUMODE_PLAY) ? "PLAY"
    295     : (info.mode = AUMODE_RECORD) ? "RECORD"
    296     : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL"
    297     : "?"));
    298 }
    299 #endif /* DEBUG_AUDIO */
    300 
    301 static int
    302 OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec)
    303 {
    304     char audiodev[64];
    305     Uint16 format;
    306     audio_info_t info;
    307 
    308     AUDIO_INITINFO(&info);
    309 
    310     /* Calculate the final parameters for this audio specification */
    311     SDL_CalculateAudioSpec(spec);
    312 
    313 #ifdef USE_TIMER_SYNC
    314     frame_ticks = 0.0;
    315 #endif
    316 
    317     /* Open the audio device */
    318     audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
    319     if(audio_fd < 0) {
    320 	SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
    321 	return(-1);
    322     }
    323 
    324     /* Set to play mode */
    325     info.mode = AUMODE_PLAY;
    326     if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) {
    327 	SDL_SetError("Couldn't put device into play mode");
    328 	return(-1);
    329     }
    330 
    331     mixbuf = NULL;
    332     AUDIO_INITINFO(&info);
    333     for (format = SDL_FirstAudioFormat(spec->format);
    334     	format; format = SDL_NextAudioFormat())
    335     {
    336 	switch(format) {
    337 	case AUDIO_U8:
    338 	    info.play.encoding = AUDIO_ENCODING_ULINEAR;
    339 	    info.play.precision = 8;
    340 	    break;
    341 	case AUDIO_S8:
    342 	    info.play.encoding = AUDIO_ENCODING_SLINEAR;
    343 	    info.play.precision = 8;
    344 	    break;
    345 	case AUDIO_S16LSB:
    346 	    info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
    347 	    info.play.precision = 16;
    348 	    break;
    349 	case AUDIO_S16MSB:
    350 	    info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
    351 	    info.play.precision = 16;
    352 	    break;
    353 	case AUDIO_U16LSB:
    354 	    info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
    355 	    info.play.precision = 16;
    356 	    break;
    357 	case AUDIO_U16MSB:
    358 	    info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
    359 	    info.play.precision = 16;
    360 	    break;
    361 	default:
    362 	    continue;
    363 	}
    364 	if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0)
    365 	    break;
    366     }
    367 
    368     if(!format) {
    369 	SDL_SetError("No supported encoding for 0x%x", spec->format);
    370 	return(-1);
    371     }
    372 
    373     spec->format = format;
    374 
    375     AUDIO_INITINFO(&info);
    376     info.play.channels = spec->channels;
    377     if (ioctl(audio_fd, AUDIO_SETINFO, &info) == -1)
    378     	spec->channels = 1;
    379     AUDIO_INITINFO(&info);
    380     info.play.sample_rate = spec->freq;
    381     info.blocksize = spec->size;
    382     info.hiwat = 5;
    383     info.lowat = 3;
    384     (void)ioctl(audio_fd, AUDIO_SETINFO, &info);
    385     (void)ioctl(audio_fd, AUDIO_GETINFO, &info);
    386     spec->freq  = info.play.sample_rate;
    387     /* Allocate mixing buffer */
    388     mixlen = spec->size;
    389     mixbuf = (Uint8*)SDL_AllocAudioMem(mixlen);
    390     if(mixbuf == NULL) {
    391 	return(-1);
    392     }
    393     SDL_memset(mixbuf, spec->silence, spec->size);
    394 
    395     /* Get the parent process id (we're the parent of the audio thread) */
    396     parent = getpid();
    397 
    398 #ifdef DEBUG_AUDIO
    399     OBSD_Status(this);
    400 #endif
    401 
    402     /* We're ready to rock and roll. :-) */
    403     return(0);
    404 }
    405