src/audio/dsp/SDL_dspaudio.c
changeset 0 74212992fb08
child 1 cf2af46e9e2a
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/audio/dsp/SDL_dspaudio.c	Thu Apr 26 16:45:43 2001 +0000
     1.3 @@ -0,0 +1,450 @@
     1.4 +/*
     1.5 +    SDL - Simple DirectMedia Layer
     1.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
     1.7 +
     1.8 +    This library is free software; you can redistribute it and/or
     1.9 +    modify it under the terms of the GNU Library General Public
    1.10 +    License as published by the Free Software Foundation; either
    1.11 +    version 2 of the License, or (at your option) any later version.
    1.12 +
    1.13 +    This library is distributed in the hope that it will be useful,
    1.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.16 +    Library General Public License for more details.
    1.17 +
    1.18 +    You should have received a copy of the GNU Library General Public
    1.19 +    License along with this library; if not, write to the Free
    1.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.21 +
    1.22 +    Sam Lantinga
    1.23 +    slouken@devolution.com
    1.24 +*/
    1.25 +
    1.26 +#ifdef SAVE_RCSID
    1.27 +static char rcsid =
    1.28 + "@(#) $Id$";
    1.29 +#endif
    1.30 +
    1.31 +/* Allow access to a raw mixing buffer */
    1.32 +
    1.33 +#include <stdlib.h>
    1.34 +#include <stdio.h>
    1.35 +#include <string.h>
    1.36 +#include <errno.h>
    1.37 +#include <unistd.h>
    1.38 +#include <fcntl.h>
    1.39 +#include <signal.h>
    1.40 +#include <sys/time.h>
    1.41 +#include <sys/ioctl.h>
    1.42 +#include <sys/stat.h>
    1.43 +#ifdef linux
    1.44 +#include <linux/soundcard.h>
    1.45 +#endif
    1.46 +#ifdef __bsdi__
    1.47 +#include <sys/soundcard.h>
    1.48 +#endif
    1.49 +#ifdef __FreeBSD__
    1.50 +#include <machine/soundcard.h>
    1.51 +#endif
    1.52 +#ifdef __USLC__
    1.53 +#include <sys/soundcard.h>
    1.54 +#endif
    1.55 +
    1.56 +#include "SDL_audio.h"
    1.57 +#include "SDL_error.h"
    1.58 +#include "SDL_audiomem.h"
    1.59 +#include "SDL_audio_c.h"
    1.60 +#include "SDL_timer.h"
    1.61 +#include "SDL_audiodev_c.h"
    1.62 +#include "SDL_dspaudio.h"
    1.63 +
    1.64 +/* The tag name used by DSP audio */
    1.65 +#define DSP_DRIVER_NAME         "dsp"
    1.66 +
    1.67 +/* Open the audio device for playback, and don't block if busy */
    1.68 +/*#define USE_BLOCKING_WRITES*/
    1.69 +#ifdef USE_BLOCKING_WRITES
    1.70 +#define OPEN_FLAGS	O_WRONLY
    1.71 +#else
    1.72 +#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
    1.73 +#endif
    1.74 +
    1.75 +/* Audio driver functions */
    1.76 +static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
    1.77 +static void DSP_WaitAudio(_THIS);
    1.78 +static void DSP_PlayAudio(_THIS);
    1.79 +static Uint8 *DSP_GetAudioBuf(_THIS);
    1.80 +static void DSP_CloseAudio(_THIS);
    1.81 +
    1.82 +/* Audio driver bootstrap functions */
    1.83 +
    1.84 +static int Audio_Available(void)
    1.85 +{
    1.86 +	int fd;
    1.87 +	int available;
    1.88 +
    1.89 +	available = 0;
    1.90 +	fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
    1.91 +	if ( fd >= 0 ) {
    1.92 +		available = 1;
    1.93 +		close(fd);
    1.94 +	}
    1.95 +	return(available);
    1.96 +}
    1.97 +
    1.98 +static void Audio_DeleteDevice(SDL_AudioDevice *device)
    1.99 +{
   1.100 +	free(device->hidden);
   1.101 +	free(device);
   1.102 +}
   1.103 +
   1.104 +static SDL_AudioDevice *Audio_CreateDevice(int devindex)
   1.105 +{
   1.106 +	SDL_AudioDevice *this;
   1.107 +
   1.108 +	/* Initialize all variables that we clean on shutdown */
   1.109 +	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
   1.110 +	if ( this ) {
   1.111 +		memset(this, 0, (sizeof *this));
   1.112 +		this->hidden = (struct SDL_PrivateAudioData *)
   1.113 +				malloc((sizeof *this->hidden));
   1.114 +	}
   1.115 +	if ( (this == NULL) || (this->hidden == NULL) ) {
   1.116 +		SDL_OutOfMemory();
   1.117 +		if ( this ) {
   1.118 +			free(this);
   1.119 +		}
   1.120 +		return(0);
   1.121 +	}
   1.122 +	memset(this->hidden, 0, (sizeof *this->hidden));
   1.123 +	audio_fd = -1;
   1.124 +
   1.125 +	/* Set the function pointers */
   1.126 +	this->OpenAudio = DSP_OpenAudio;
   1.127 +	this->WaitAudio = DSP_WaitAudio;
   1.128 +	this->PlayAudio = DSP_PlayAudio;
   1.129 +	this->GetAudioBuf = DSP_GetAudioBuf;
   1.130 +	this->CloseAudio = DSP_CloseAudio;
   1.131 +
   1.132 +	this->free = Audio_DeleteDevice;
   1.133 +
   1.134 +	return this;
   1.135 +}
   1.136 +
   1.137 +AudioBootStrap DSP_bootstrap = {
   1.138 +	DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
   1.139 +	Audio_Available, Audio_CreateDevice
   1.140 +};
   1.141 +
   1.142 +/* This function waits until it is possible to write a full sound buffer */
   1.143 +static void DSP_WaitAudio(_THIS)
   1.144 +{
   1.145 +#ifndef USE_BLOCKING_WRITES /* Not necessary because of blocking writes */
   1.146 +	fd_set fdset;
   1.147 +
   1.148 +	/* Check to see if the thread-parent process is still alive */
   1.149 +	{ static int cnt = 0;
   1.150 +		/* Note that this only works with thread implementations 
   1.151 +		   that use a different process id for each thread.
   1.152 +		*/
   1.153 +		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
   1.154 +			if ( kill(parent, 0) < 0 ) {
   1.155 +				this->enabled = 0;
   1.156 +			}
   1.157 +		}
   1.158 +	}
   1.159 +
   1.160 +	/* See if we need to use timed audio synchronization */
   1.161 +	if ( frame_ticks ) {
   1.162 +		/* Use timer for general audio synchronization */
   1.163 +		Sint32 ticks;
   1.164 +
   1.165 +		ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
   1.166 +		if ( ticks > 0 ) {
   1.167 +			SDL_Delay(ticks);
   1.168 +		}
   1.169 +	} else {
   1.170 +		/* Use select() for audio synchronization */
   1.171 +		struct timeval timeout;
   1.172 +		FD_ZERO(&fdset);
   1.173 +		FD_SET(audio_fd, &fdset);
   1.174 +		timeout.tv_sec = 10;
   1.175 +		timeout.tv_usec = 0;
   1.176 +#ifdef DEBUG_AUDIO
   1.177 +		fprintf(stderr, "Waiting for audio to get ready\n");
   1.178 +#endif
   1.179 +		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
   1.180 +			const char *message =
   1.181 +			"Audio timeout - buggy audio driver? (disabled)";
   1.182 +			/* In general we should never print to the screen,
   1.183 +			   but in this case we have no other way of letting
   1.184 +			   the user know what happened.
   1.185 +			*/
   1.186 +			fprintf(stderr, "SDL: %s\n", message);
   1.187 +			this->enabled = 0;
   1.188 +			/* Don't try to close - may hang */
   1.189 +			audio_fd = -1;
   1.190 +#ifdef DEBUG_AUDIO
   1.191 +			fprintf(stderr, "Done disabling audio\n");
   1.192 +#endif
   1.193 +		}
   1.194 +#ifdef DEBUG_AUDIO
   1.195 +		fprintf(stderr, "Ready!\n");
   1.196 +#endif
   1.197 +	}
   1.198 +#endif /* !USE_BLOCKING_WRITES */
   1.199 +}
   1.200 +
   1.201 +static void DSP_PlayAudio(_THIS)
   1.202 +{
   1.203 +	int written;
   1.204 +
   1.205 +	/* Write the audio data, checking for EAGAIN on broken audio drivers */
   1.206 +	do {
   1.207 +		written = write(audio_fd, mixbuf, mixlen);
   1.208 +		if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) {
   1.209 +			SDL_Delay(1);	/* Let a little CPU time go by */
   1.210 +		}
   1.211 +	} while ( (written < 0) && 
   1.212 +	          ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) );
   1.213 +
   1.214 +	/* If timer synchronization is enabled, set the next write frame */
   1.215 +	if ( frame_ticks ) {
   1.216 +		next_frame += frame_ticks;
   1.217 +	}
   1.218 +
   1.219 +	/* If we couldn't write, assume fatal error for now */
   1.220 +	if ( written < 0 ) {
   1.221 +		this->enabled = 0;
   1.222 +	}
   1.223 +#ifdef DEBUG_AUDIO
   1.224 +	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   1.225 +#endif
   1.226 +}
   1.227 +
   1.228 +static Uint8 *DSP_GetAudioBuf(_THIS)
   1.229 +{
   1.230 +	return(mixbuf);
   1.231 +}
   1.232 +
   1.233 +static void DSP_CloseAudio(_THIS)
   1.234 +{
   1.235 +	if ( mixbuf != NULL ) {
   1.236 +		SDL_FreeAudioMem(mixbuf);
   1.237 +		mixbuf = NULL;
   1.238 +	}
   1.239 +	if ( audio_fd >= 0 ) {
   1.240 +		close(audio_fd);
   1.241 +		audio_fd = -1;
   1.242 +	}
   1.243 +}
   1.244 +
   1.245 +static int DSP_ReopenAudio(_THIS, const char *audiodev, int format,
   1.246 +						SDL_AudioSpec *spec)
   1.247 +{
   1.248 +	int frag_spec;
   1.249 +	int value;
   1.250 +
   1.251 +	/* Close and then reopen the audio device */
   1.252 +	close(audio_fd);
   1.253 +	audio_fd = open(audiodev, O_WRONLY, 0);
   1.254 +	if ( audio_fd < 0 ) {
   1.255 +		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
   1.256 +		return(-1);
   1.257 +	}
   1.258 +
   1.259 +	/* Calculate the final parameters for this audio specification */
   1.260 +	SDL_CalculateAudioSpec(spec);
   1.261 +
   1.262 +	/* Determine the power of two of the fragment size */
   1.263 +	for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
   1.264 +	if ( (0x01<<frag_spec) != spec->size ) {
   1.265 +		SDL_SetError("Fragment size must be a power of two");
   1.266 +		return(-1);
   1.267 +	}
   1.268 +	frag_spec |= 0x00020000;	/* two fragments, for low latency */
   1.269 +
   1.270 +	/* Set the audio buffering parameters */
   1.271 +#ifdef DEBUG_AUDIO
   1.272 +	fprintf(stderr, "Requesting %d fragments of size %d\n",
   1.273 +		(frag_spec >> 16), 1<<(frag_spec&0xFFFF));
   1.274 +#endif
   1.275 +	if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
   1.276 +		fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
   1.277 +	}
   1.278 +#ifdef DEBUG_AUDIO
   1.279 +	{ audio_buf_info info;
   1.280 +	  ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
   1.281 +	  fprintf(stderr, "fragments = %d\n", info.fragments);
   1.282 +	  fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
   1.283 +	  fprintf(stderr, "fragsize = %d\n", info.fragsize);
   1.284 +	  fprintf(stderr, "bytes = %d\n", info.bytes);
   1.285 +	}
   1.286 +#endif
   1.287 +
   1.288 +	/* Set the audio format */
   1.289 +	value = format;
   1.290 +	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
   1.291 +						(value != format) ) {
   1.292 +		SDL_SetError("Couldn't set audio format");
   1.293 +		return(-1);
   1.294 +	}
   1.295 +
   1.296 +	/* Set the number of channels of output */
   1.297 +	value = spec->channels;
   1.298 +#ifdef SNDCTL_DSP_CHANNELS
   1.299 +	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
   1.300 +#endif
   1.301 +		value = (spec->channels > 1);
   1.302 +		ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
   1.303 +		value = (value ? 2 : 1);
   1.304 +#ifdef SNDCTL_DSP_CHANNELS
   1.305 +	}
   1.306 +#endif
   1.307 +	if ( value != spec->channels ) {
   1.308 +		SDL_SetError("Couldn't set audio channels");
   1.309 +		return(-1);
   1.310 +	}
   1.311 +
   1.312 +	/* Set the DSP frequency */
   1.313 +	value = spec->freq;
   1.314 +	if ( ioctl(audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) {
   1.315 +		SDL_SetError("Couldn't set audio frequency");
   1.316 +		return(-1);
   1.317 +	}
   1.318 +	spec->freq = value;
   1.319 +
   1.320 +	/* We successfully re-opened the audio */
   1.321 +	return(0);
   1.322 +}
   1.323 +
   1.324 +static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
   1.325 +{
   1.326 +	char audiodev[1024];
   1.327 +	int format;
   1.328 +	int value;
   1.329 +	Uint16 test_format;
   1.330 +
   1.331 +	/* Reset the timer synchronization flag */
   1.332 +	frame_ticks = 0.0;
   1.333 +
   1.334 +	/* Open the audio device */
   1.335 +	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
   1.336 +	if ( audio_fd < 0 ) {
   1.337 +		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
   1.338 +		return(-1);
   1.339 +	}
   1.340 +	mixbuf = NULL;
   1.341 +
   1.342 +	/* Get a list of supported hardware formats */
   1.343 +	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
   1.344 +		SDL_SetError("Couldn't get audio format list");
   1.345 +		return(-1);
   1.346 +	}
   1.347 +
   1.348 +	/* Try for a closest match on audio format */
   1.349 +	format = 0;
   1.350 +	for ( test_format = SDL_FirstAudioFormat(spec->format);
   1.351 +						! format && test_format; ) {
   1.352 +#ifdef DEBUG_AUDIO
   1.353 +		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   1.354 +#endif
   1.355 +		switch ( test_format ) {
   1.356 +			case AUDIO_U8:
   1.357 +				if ( value & AFMT_U8 ) {
   1.358 +					format = AFMT_U8;
   1.359 +				}
   1.360 +				break;
   1.361 +			case AUDIO_S8:
   1.362 +				if ( value & AFMT_S8 ) {
   1.363 +					format = AFMT_S8;
   1.364 +				}
   1.365 +				break;
   1.366 +			case AUDIO_S16LSB:
   1.367 +				if ( value & AFMT_S16_LE ) {
   1.368 +					format = AFMT_S16_LE;
   1.369 +				}
   1.370 +				break;
   1.371 +			case AUDIO_S16MSB:
   1.372 +				if ( value & AFMT_S16_BE ) {
   1.373 +					format = AFMT_S16_BE;
   1.374 +				}
   1.375 +				break;
   1.376 +			case AUDIO_U16LSB:
   1.377 +				if ( value & AFMT_U16_LE ) {
   1.378 +					format = AFMT_U16_LE;
   1.379 +				}
   1.380 +				break;
   1.381 +			case AUDIO_U16MSB:
   1.382 +				if ( value & AFMT_U16_BE ) {
   1.383 +					format = AFMT_U16_BE;
   1.384 +				}
   1.385 +				break;
   1.386 +			default:
   1.387 +				break;
   1.388 +		}
   1.389 +		if ( ! format ) {
   1.390 +			test_format = SDL_NextAudioFormat();
   1.391 +		}
   1.392 +	}
   1.393 +	if ( format == 0 ) {
   1.394 +		SDL_SetError("Couldn't find any hardware audio formats");
   1.395 +		return(-1);
   1.396 +	}
   1.397 +	spec->format = test_format;
   1.398 +
   1.399 +	/* Set the audio format */
   1.400 +	value = format;
   1.401 +	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
   1.402 +						(value != format) ) {
   1.403 +		SDL_SetError("Couldn't set audio format");
   1.404 +		return(-1);
   1.405 +	}
   1.406 +
   1.407 +	/* Set the number of channels of output */
   1.408 +	value = spec->channels;
   1.409 +#ifdef SNDCTL_DSP_CHANNELS
   1.410 +	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
   1.411 +#endif
   1.412 +		value = (spec->channels > 1);
   1.413 +		ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
   1.414 +		value = (value ? 2 : 1);
   1.415 +#ifdef SNDCTL_DSP_CHANNELS
   1.416 +	}
   1.417 +#endif
   1.418 +	spec->channels = value;
   1.419 +
   1.420 +	/* Because some drivers don't allow setting the buffer size
   1.421 +	   after setting the format, we must re-open the audio device
   1.422 +	   once we know what format and channels are supported
   1.423 +	 */
   1.424 +	if ( DSP_ReopenAudio(this, audiodev, format, spec) < 0 ) {
   1.425 +		/* Error is set by DSP_ReopenAudio() */
   1.426 +		return(-1);
   1.427 +	}
   1.428 +
   1.429 +	/* Allocate mixing buffer */
   1.430 +	mixlen = spec->size;
   1.431 +	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   1.432 +	if ( mixbuf == NULL ) {
   1.433 +		return(-1);
   1.434 +	}
   1.435 +	memset(mixbuf, spec->silence, spec->size);
   1.436 +
   1.437 +#ifndef USE_BLOCKING_WRITES
   1.438 +	/* Check to see if we need to use select() workaround */
   1.439 +	{ char *workaround;
   1.440 +		workaround = getenv("SDL_DSP_NOSELECT");
   1.441 +		if ( workaround ) {
   1.442 +			frame_ticks = (float)(spec->samples*1000)/spec->freq;
   1.443 +			next_frame = SDL_GetTicks()+frame_ticks;
   1.444 +		}
   1.445 +	}
   1.446 +#endif /* !USE_BLOCKING_WRITES */
   1.447 +
   1.448 +	/* Get the parent process id (we're the parent of the audio thread) */
   1.449 +	parent = getpid();
   1.450 +
   1.451 +	/* We're ready to rock and roll. :-) */
   1.452 +	return(0);
   1.453 +}