Home | History | Annotate | Download | only in dma
      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     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 /* Allow access to a raw mixing buffer */
     25 
     26 #include <stdio.h>
     27 #include <string.h>	/* For strerror() */
     28 #include <errno.h>
     29 #include <unistd.h>
     30 #include <fcntl.h>
     31 #include <signal.h>
     32 #include <sys/types.h>
     33 #include <sys/time.h>
     34 #include <sys/ioctl.h>
     35 #include <sys/stat.h>
     36 #include <sys/mman.h>
     37 
     38 #if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
     39 /* This is installed on some systems */
     40 #include <soundcard.h>
     41 #else
     42 /* This is recommended by OSS */
     43 #include <sys/soundcard.h>
     44 #endif
     45 
     46 #ifndef MAP_FAILED
     47 #define MAP_FAILED	((Uint8 *)-1)
     48 #endif
     49 
     50 #include "SDL_timer.h"
     51 #include "SDL_audio.h"
     52 #include "../SDL_audio_c.h"
     53 #include "../SDL_audiodev_c.h"
     54 #include "SDL_dmaaudio.h"
     55 
     56 /* The tag name used by DMA audio */
     57 #define DMA_DRIVER_NAME         "dma"
     58 
     59 /* Open the audio device for playback, and don't block if busy */
     60 #define OPEN_FLAGS	(O_RDWR|O_NONBLOCK)
     61 
     62 /* Audio driver functions */
     63 static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec);
     64 static void DMA_WaitAudio(_THIS);
     65 static void DMA_PlayAudio(_THIS);
     66 static Uint8 *DMA_GetAudioBuf(_THIS);
     67 static void DMA_CloseAudio(_THIS);
     68 
     69 /* Audio driver bootstrap functions */
     70 
     71 static int Audio_Available(void)
     72 {
     73 	int available;
     74 	int fd;
     75 
     76 	available = 0;
     77 
     78 	fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
     79 	if ( fd >= 0 ) {
     80 		int caps;
     81 		struct audio_buf_info info;
     82 
     83 		if ( (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) &&
     84 	             (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) &&
     85 		     (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0) ) {
     86 			available = 1;
     87 		}
     88 		close(fd);
     89 	}
     90 	return(available);
     91 }
     92 
     93 static void Audio_DeleteDevice(SDL_AudioDevice *device)
     94 {
     95 	SDL_free(device->hidden);
     96 	SDL_free(device);
     97 }
     98 
     99 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    100 {
    101 	SDL_AudioDevice *this;
    102 
    103 	/* Initialize all variables that we clean on shutdown */
    104 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
    105 	if ( this ) {
    106 		SDL_memset(this, 0, (sizeof *this));
    107 		this->hidden = (struct SDL_PrivateAudioData *)
    108 				SDL_malloc((sizeof *this->hidden));
    109 	}
    110 	if ( (this == NULL) || (this->hidden == NULL) ) {
    111 		SDL_OutOfMemory();
    112 		if ( this ) {
    113 			SDL_free(this);
    114 		}
    115 		return(0);
    116 	}
    117 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    118 	audio_fd = -1;
    119 
    120 	/* Set the function pointers */
    121 	this->OpenAudio = DMA_OpenAudio;
    122 	this->WaitAudio = DMA_WaitAudio;
    123 	this->PlayAudio = DMA_PlayAudio;
    124 	this->GetAudioBuf = DMA_GetAudioBuf;
    125 	this->CloseAudio = DMA_CloseAudio;
    126 
    127 	this->free = Audio_DeleteDevice;
    128 
    129 	return this;
    130 }
    131 
    132 AudioBootStrap DMA_bootstrap = {
    133 	DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio",
    134 	Audio_Available, Audio_CreateDevice
    135 };
    136 
    137 /* This function waits until it is possible to write a full sound buffer */
    138 static void DMA_WaitAudio(_THIS)
    139 {
    140 	fd_set fdset;
    141 
    142 	/* Check to see if the thread-parent process is still alive */
    143 	{ static int cnt = 0;
    144 		/* Note that this only works with thread implementations
    145 		   that use a different process id for each thread.
    146 		*/
    147 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
    148 			if ( kill(parent, 0) < 0 ) {
    149 				this->enabled = 0;
    150 			}
    151 		}
    152 	}
    153 
    154 	/* See if we need to use timed audio synchronization */
    155 	if ( frame_ticks ) {
    156 		/* Use timer for general audio synchronization */
    157 		Sint32 ticks;
    158 
    159 		ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
    160 		if ( ticks > 0 ) {
    161 			SDL_Delay(ticks);
    162 		}
    163 	} else {
    164 		/* Use select() for audio synchronization */
    165 		struct timeval timeout;
    166 		FD_ZERO(&fdset);
    167 		FD_SET(audio_fd, &fdset);
    168 		timeout.tv_sec = 10;
    169 		timeout.tv_usec = 0;
    170 #ifdef DEBUG_AUDIO
    171 		fprintf(stderr, "Waiting for audio to get ready\n");
    172 #endif
    173 		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
    174 			const char *message =
    175 #ifdef AUDIO_OSPACE_HACK
    176 			"Audio timeout - buggy audio driver? (trying ospace)";
    177 #else
    178 			"Audio timeout - buggy audio driver? (disabled)";
    179 #endif
    180 			/* In general we should never print to the screen,
    181 			   but in this case we have no other way of letting
    182 			   the user know what happened.
    183 			*/
    184 			fprintf(stderr, "SDL: %s\n", message);
    185 #ifdef AUDIO_OSPACE_HACK
    186 			/* We may be able to use GET_OSPACE trick */
    187 			frame_ticks = (float)(this->spec->samples*1000) /
    188 			                      this->spec->freq;
    189 			next_frame = SDL_GetTicks()+frame_ticks;
    190 #else
    191 			this->enabled = 0;
    192 			/* Don't try to close - may hang */
    193 			audio_fd = -1;
    194 #ifdef DEBUG_AUDIO
    195 			fprintf(stderr, "Done disabling audio\n");
    196 #endif
    197 #endif /* AUDIO_OSPACE_HACK */
    198 		}
    199 #ifdef DEBUG_AUDIO
    200 		fprintf(stderr, "Ready!\n");
    201 #endif
    202 	}
    203 }
    204 
    205 static void DMA_PlayAudio(_THIS)
    206 {
    207 	/* If timer synchronization is enabled, set the next write frame */
    208 	if ( frame_ticks ) {
    209 		next_frame += frame_ticks;
    210 	}
    211 	return;
    212 }
    213 
    214 static Uint8 *DMA_GetAudioBuf(_THIS)
    215 {
    216 	count_info info;
    217 	int playing;
    218 	int filling;
    219 
    220 	/* Get number of blocks, looping if we're not using select() */
    221 	do {
    222 		if ( ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &info) < 0 ) {
    223 			/* Uh oh... */
    224 			this->enabled = 0;
    225 			return(NULL);
    226 		}
    227 	} while ( frame_ticks && (info.blocks < 1) );
    228 #ifdef DEBUG_AUDIO
    229 	if ( info.blocks > 1 ) {
    230 		printf("Warning: audio underflow (%d frags)\n", info.blocks-1);
    231 	}
    232 #endif
    233 	playing = info.ptr / this->spec.size;
    234 	filling = (playing + 1)%num_buffers;
    235 	return (dma_buf + (filling * this->spec.size));
    236 }
    237 
    238 static void DMA_CloseAudio(_THIS)
    239 {
    240 	if ( dma_buf != NULL ) {
    241 		munmap(dma_buf, dma_len);
    242 		dma_buf = NULL;
    243 	}
    244 	if ( audio_fd >= 0 ) {
    245 		close(audio_fd);
    246 		audio_fd = -1;
    247 	}
    248 }
    249 
    250 static int DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo,
    251 							SDL_AudioSpec *spec)
    252 {
    253 	int frag_spec;
    254 	int value;
    255 
    256 	/* Close and then reopen the audio device */
    257 	close(audio_fd);
    258 	audio_fd = open(audiodev, O_RDWR, 0);
    259 	if ( audio_fd < 0 ) {
    260 		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
    261 		return(-1);
    262 	}
    263 
    264 	/* Calculate the final parameters for this audio specification */
    265 	SDL_CalculateAudioSpec(spec);
    266 
    267 	/* Determine the power of two of the fragment size */
    268 	for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
    269 	if ( (0x01<<frag_spec) != spec->size ) {
    270 		SDL_SetError("Fragment size must be a power of two");
    271 		return(-1);
    272 	}
    273 
    274 	/* Set the audio buffering parameters */
    275 	if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
    276 		SDL_SetError("Couldn't set audio fragment spec");
    277 		return(-1);
    278 	}
    279 
    280 	/* Set the audio format */
    281 	value = format;
    282 	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
    283 						(value != format) ) {
    284 		SDL_SetError("Couldn't set audio format");
    285 		return(-1);
    286 	}
    287 
    288 	/* Set mono or stereo audio */
    289 	value = (spec->channels > 1);
    290 	if ( (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) ||
    291 						(value != stereo) ) {
    292 		SDL_SetError("Couldn't set audio channels");
    293 		return(-1);
    294 	}
    295 
    296 	/* Set the DSP frequency */
    297 	value = spec->freq;
    298 	if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
    299 		SDL_SetError("Couldn't set audio frequency");
    300 		return(-1);
    301 	}
    302 	spec->freq = value;
    303 
    304 	/* We successfully re-opened the audio */
    305 	return(0);
    306 }
    307 
    308 static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec)
    309 {
    310 	char audiodev[1024];
    311 	int format;
    312 	int stereo;
    313 	int value;
    314 	Uint16 test_format;
    315 	struct audio_buf_info info;
    316 
    317 	/* Reset the timer synchronization flag */
    318 	frame_ticks = 0.0;
    319 
    320 	/* Open the audio device */
    321 	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
    322 	if ( audio_fd < 0 ) {
    323 		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
    324 		return(-1);
    325 	}
    326 	dma_buf = NULL;
    327 	ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
    328 
    329 	/* Get a list of supported hardware formats */
    330 	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
    331 		SDL_SetError("Couldn't get audio format list");
    332 		return(-1);
    333 	}
    334 
    335 	/* Try for a closest match on audio format */
    336 	format = 0;
    337 	for ( test_format = SDL_FirstAudioFormat(spec->format);
    338 						! format && test_format; ) {
    339 #ifdef DEBUG_AUDIO
    340 		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
    341 #endif
    342 		switch ( test_format ) {
    343 			case AUDIO_U8:
    344 				if ( value & AFMT_U8 ) {
    345 					format = AFMT_U8;
    346 				}
    347 				break;
    348 			case AUDIO_S8:
    349 				if ( value & AFMT_S8 ) {
    350 					format = AFMT_S8;
    351 				}
    352 				break;
    353 			case AUDIO_S16LSB:
    354 				if ( value & AFMT_S16_LE ) {
    355 					format = AFMT_S16_LE;
    356 				}
    357 				break;
    358 			case AUDIO_S16MSB:
    359 				if ( value & AFMT_S16_BE ) {
    360 					format = AFMT_S16_BE;
    361 				}
    362 				break;
    363 			case AUDIO_U16LSB:
    364 				if ( value & AFMT_U16_LE ) {
    365 					format = AFMT_U16_LE;
    366 				}
    367 				break;
    368 			case AUDIO_U16MSB:
    369 				if ( value & AFMT_U16_BE ) {
    370 					format = AFMT_U16_BE;
    371 				}
    372 				break;
    373 			default:
    374 				format = 0;
    375 				break;
    376 		}
    377 		if ( ! format ) {
    378 			test_format = SDL_NextAudioFormat();
    379 		}
    380 	}
    381 	if ( format == 0 ) {
    382 		SDL_SetError("Couldn't find any hardware audio formats");
    383 		return(-1);
    384 	}
    385 	spec->format = test_format;
    386 
    387 	/* Set the audio format */
    388 	value = format;
    389 	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
    390 						(value != format) ) {
    391 		SDL_SetError("Couldn't set audio format");
    392 		return(-1);
    393 	}
    394 
    395 	/* Set mono or stereo audio (currently only two channels supported) */
    396 	stereo = (spec->channels > 1);
    397 	ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
    398 	if ( stereo ) {
    399 		spec->channels = 2;
    400 	} else {
    401 		spec->channels = 1;
    402 	}
    403 
    404 	/* Because some drivers don't allow setting the buffer size
    405 	   after setting the format, we must re-open the audio device
    406 	   once we know what format and channels are supported
    407 	 */
    408 	if ( DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0 ) {
    409 		/* Error is set by DMA_ReopenAudio() */
    410 		return(-1);
    411 	}
    412 
    413 	/* Memory map the audio buffer */
    414 	if ( ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0 ) {
    415 		SDL_SetError("Couldn't get OSPACE parameters");
    416 		return(-1);
    417 	}
    418 	spec->size = info.fragsize;
    419 	spec->samples = spec->size / ((spec->format & 0xFF) / 8);
    420 	spec->samples /= spec->channels;
    421 	num_buffers = info.fragstotal;
    422 	dma_len = num_buffers*spec->size;
    423 	dma_buf = (Uint8 *)mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED,
    424 							audio_fd, 0);
    425 	if ( dma_buf == MAP_FAILED ) {
    426 		SDL_SetError("DMA memory map failed");
    427 		dma_buf = NULL;
    428 		return(-1);
    429 	}
    430 	SDL_memset(dma_buf, spec->silence, dma_len);
    431 
    432 	/* Check to see if we need to use select() workaround */
    433 	{ char *workaround;
    434 		workaround = SDL_getenv("SDL_DSP_NOSELECT");
    435 		if ( workaround ) {
    436 			frame_ticks = (float)(spec->samples*1000)/spec->freq;
    437 			next_frame = SDL_GetTicks()+frame_ticks;
    438 		}
    439 	}
    440 
    441 	/* Trigger audio playback */
    442 	value = 0;
    443 	ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value);
    444 	value = PCM_ENABLE_OUTPUT;
    445 	if ( ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0 ) {
    446 		SDL_SetError("Couldn't trigger audio output");
    447 		return(-1);
    448 	}
    449 
    450 	/* Get the parent process id (we're the parent of the audio thread) */
    451 	parent = getpid();
    452 
    453 	/* We're ready to rock and roll. :-) */
    454 	return(0);
    455 }
    456