src/audio/sun/SDL_sunaudio.c
changeset 0 74212992fb08
child 148 8758b8d42cd9
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/audio/sun/SDL_sunaudio.c	Thu Apr 26 16:45:43 2001 +0000
     1.3 @@ -0,0 +1,439 @@
     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 <fcntl.h>
    1.36 +#include <errno.h>
    1.37 +#include <string.h>
    1.38 +#ifdef __NetBSD__
    1.39 +#include <sys/ioctl.h>
    1.40 +#include <sys/audioio.h>
    1.41 +#endif
    1.42 +#ifdef __SVR4
    1.43 +#include <sys/audioio.h>
    1.44 +#else
    1.45 +#include <sys/time.h>
    1.46 +#include <sys/types.h>
    1.47 +#endif
    1.48 +#include <unistd.h>
    1.49 +
    1.50 +#include "SDL_endian.h"
    1.51 +#include "SDL_audio.h"
    1.52 +#include "SDL_audiomem.h"
    1.53 +#include "SDL_audiodev_c.h"
    1.54 +#include "SDL_sunaudio.h"
    1.55 +#include "SDL_audio_c.h"
    1.56 +#include "SDL_timer.h"
    1.57 +
    1.58 +/* Open the audio device for playback, and don't block if busy */
    1.59 +#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
    1.60 +
    1.61 +/* Audio driver functions */
    1.62 +static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
    1.63 +static void DSP_WaitAudio(_THIS);
    1.64 +static void DSP_PlayAudio(_THIS);
    1.65 +static Uint8 *DSP_GetAudioBuf(_THIS);
    1.66 +static void DSP_CloseAudio(_THIS);
    1.67 +
    1.68 +/* Audio driver bootstrap functions */
    1.69 +
    1.70 +static int Audio_Available(void)
    1.71 +{
    1.72 +	int fd;
    1.73 +	int available;
    1.74 +
    1.75 +	available = 0;
    1.76 +	fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 1);
    1.77 +	if ( fd >= 0 ) {
    1.78 +		available = 1;
    1.79 +		close(fd);
    1.80 +	}
    1.81 +	return(available);
    1.82 +}
    1.83 +
    1.84 +static void Audio_DeleteDevice(SDL_AudioDevice *device)
    1.85 +{
    1.86 +	free(device->hidden);
    1.87 +	free(device);
    1.88 +}
    1.89 +
    1.90 +static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    1.91 +{
    1.92 +	SDL_AudioDevice *this;
    1.93 +
    1.94 +	/* Initialize all variables that we clean on shutdown */
    1.95 +	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
    1.96 +	if ( this ) {
    1.97 +		memset(this, 0, (sizeof *this));
    1.98 +		this->hidden = (struct SDL_PrivateAudioData *)
    1.99 +				malloc((sizeof *this->hidden));
   1.100 +	}
   1.101 +	if ( (this == NULL) || (this->hidden == NULL) ) {
   1.102 +		SDL_OutOfMemory();
   1.103 +		if ( this ) {
   1.104 +			free(this);
   1.105 +		}
   1.106 +		return(0);
   1.107 +	}
   1.108 +	memset(this->hidden, 0, (sizeof *this->hidden));
   1.109 +	audio_fd = -1;
   1.110 +
   1.111 +	/* Set the function pointers */
   1.112 +	this->OpenAudio = DSP_OpenAudio;
   1.113 +	this->WaitAudio = DSP_WaitAudio;
   1.114 +	this->PlayAudio = DSP_PlayAudio;
   1.115 +	this->GetAudioBuf = DSP_GetAudioBuf;
   1.116 +	this->CloseAudio = DSP_CloseAudio;
   1.117 +
   1.118 +	this->free = Audio_DeleteDevice;
   1.119 +
   1.120 +	return this;
   1.121 +}
   1.122 +
   1.123 +AudioBootStrap AUDIO_bootstrap = {
   1.124 +	"audio", "UNIX /dev/audio interface",
   1.125 +	Audio_Available, Audio_CreateDevice
   1.126 +};
   1.127 +
   1.128 +#ifdef DEBUG_AUDIO
   1.129 +void CheckUnderflow(_THIS)
   1.130 +{
   1.131 +#ifdef AUDIO_GETINFO
   1.132 +	audio_info_t info;
   1.133 +	int left;
   1.134 +
   1.135 +	ioctl(audio_fd, AUDIO_GETINFO, &info);
   1.136 +	left = (written - info.play.samples);
   1.137 +	if ( written && (left == 0) ) {
   1.138 +		fprintf(stderr, "audio underflow!\n");
   1.139 +	}
   1.140 +#endif
   1.141 +}
   1.142 +#endif
   1.143 +
   1.144 +void DSP_WaitAudio(_THIS)
   1.145 +{
   1.146 +#ifdef AUDIO_GETINFO
   1.147 +#define SLEEP_FUDGE	10		/* 10 ms scheduling fudge factor */
   1.148 +	audio_info_t info;
   1.149 +	Sint32 left;
   1.150 +
   1.151 +	ioctl(audio_fd, AUDIO_GETINFO, &info);
   1.152 +	left = (written - info.play.samples);
   1.153 +	if ( left > fragsize ) {
   1.154 +		Sint32 sleepy;
   1.155 +
   1.156 +		sleepy = ((left - fragsize)/frequency);
   1.157 +		sleepy -= SLEEP_FUDGE;
   1.158 +		if ( sleepy > 0 ) {
   1.159 +			SDL_Delay(sleepy);
   1.160 +		}
   1.161 +	}
   1.162 +#else
   1.163 +	fd_set fdset;
   1.164 +
   1.165 +	FD_ZERO(&fdset);
   1.166 +	FD_SET(audio_fd, &fdset);
   1.167 +	select(audio_fd+1, NULL, &fdset, NULL, NULL);
   1.168 +#endif
   1.169 +}
   1.170 +
   1.171 +void DSP_PlayAudio(_THIS)
   1.172 +{
   1.173 +	static Uint8 snd2au(int sample);
   1.174 +	/* Write the audio data */
   1.175 +	if ( ulaw_only ) {
   1.176 +		/* Assuming that this->spec.freq >= 8000 Hz */
   1.177 +		int accum, incr, pos;
   1.178 +		Uint8 *aubuf;
   1.179 +
   1.180 +		accum = 0;
   1.181 +		incr  = this->spec.freq/8;
   1.182 +		aubuf = ulaw_buf;
   1.183 +		switch (audio_fmt & 0xFF) {
   1.184 +			case 8: {
   1.185 +				Uint8 *sndbuf;
   1.186 +
   1.187 +				sndbuf = mixbuf;
   1.188 +				for ( pos=0; pos < fragsize; ++pos ) {
   1.189 +					*aubuf = snd2au((0x80-*sndbuf)*64);
   1.190 +					accum += incr;
   1.191 +					while ( accum > 0 ) {
   1.192 +						accum -= 1000;
   1.193 +						sndbuf += 1;
   1.194 +					}
   1.195 +					aubuf += 1;
   1.196 +				}
   1.197 +			}
   1.198 +			break;
   1.199 +			case 16: {
   1.200 +				Sint16 *sndbuf;
   1.201 +
   1.202 +				sndbuf = (Sint16 *)mixbuf;
   1.203 +				for ( pos=0; pos < fragsize; ++pos ) {
   1.204 +					*aubuf = snd2au(*sndbuf/4);
   1.205 +					accum += incr;
   1.206 +					while ( accum > 0 ) {
   1.207 +						accum -= 1000;
   1.208 +						sndbuf += 1;
   1.209 +					}
   1.210 +					aubuf += 1;
   1.211 +				}
   1.212 +			}
   1.213 +			break;
   1.214 +		}
   1.215 +#ifdef DEBUG_AUDIO
   1.216 +		CheckUnderflow(this);
   1.217 +#endif
   1.218 +		if ( write(audio_fd, ulaw_buf, fragsize) < 0 ) {
   1.219 +			/* Assume fatal error, for now */
   1.220 +			this->enabled = 0;
   1.221 +		}
   1.222 +		written += fragsize;
   1.223 +	} else {
   1.224 +#ifdef DEBUG_AUDIO
   1.225 +		CheckUnderflow(this);
   1.226 +#endif
   1.227 +		if ( write(audio_fd, mixbuf, this->spec.size) < 0 ) {
   1.228 +			/* Assume fatal error, for now */
   1.229 +			this->enabled = 0;
   1.230 +		}
   1.231 +		written += fragsize;
   1.232 +	}
   1.233 +}
   1.234 +
   1.235 +Uint8 *DSP_GetAudioBuf(_THIS)
   1.236 +{
   1.237 +	return(mixbuf);
   1.238 +}
   1.239 +
   1.240 +void DSP_CloseAudio(_THIS)
   1.241 +{
   1.242 +	if ( mixbuf != NULL ) {
   1.243 +		SDL_FreeAudioMem(mixbuf);
   1.244 +		mixbuf = NULL;
   1.245 +	}
   1.246 +	if ( ulaw_buf != NULL ) {
   1.247 +		free(ulaw_buf);
   1.248 +		ulaw_buf = NULL;
   1.249 +	}
   1.250 +	close(audio_fd);
   1.251 +}
   1.252 +
   1.253 +int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
   1.254 +{
   1.255 +	char audiodev[1024];
   1.256 +#ifdef AUDIO_SETINFO
   1.257 +	int enc;
   1.258 +#endif
   1.259 +	int desired_freq = spec->freq;
   1.260 +
   1.261 +	/* Initialize our freeable variables, in case we fail*/
   1.262 +	audio_fd = -1;
   1.263 +	mixbuf = NULL;
   1.264 +	ulaw_buf = NULL;
   1.265 +
   1.266 +	/* Determine the audio parameters from the AudioSpec */
   1.267 +	switch ( spec->format & 0xFF ) {
   1.268 +
   1.269 +		case 8: { /* Unsigned 8 bit audio data */
   1.270 +			spec->format = AUDIO_U8;
   1.271 +#ifdef AUDIO_SETINFO
   1.272 +			enc = AUDIO_ENCODING_LINEAR8;
   1.273 +#endif
   1.274 +		}
   1.275 +		break;
   1.276 +
   1.277 +		case 16: { /* Signed 16 bit audio data */
   1.278 +		        spec->format = AUDIO_S16SYS;
   1.279 +#ifdef AUDIO_SETINFO
   1.280 +			enc = AUDIO_ENCODING_LINEAR;
   1.281 +#endif
   1.282 +		}
   1.283 +		break;
   1.284 +
   1.285 +		default: {
   1.286 +			SDL_SetError("Unsupported audio format");
   1.287 +			return(-1);
   1.288 +		}
   1.289 +	}
   1.290 +	audio_fmt = spec->format;
   1.291 +
   1.292 +	/* Open the audio device */
   1.293 +	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 1);
   1.294 +	if ( audio_fd < 0 ) {
   1.295 +		SDL_SetError("Couldn't open %s: %s", audiodev,
   1.296 +			     strerror(errno));
   1.297 +		return(-1);
   1.298 +	}
   1.299 +
   1.300 +	ulaw_only = 0;		/* modern Suns do support linear audio */
   1.301 +#ifdef AUDIO_SETINFO
   1.302 +	for(;;) {
   1.303 +	    audio_info_t info;
   1.304 +	    AUDIO_INITINFO(&info); /* init all fields to "no change" */
   1.305 +
   1.306 +	    /* Try to set the requested settings */
   1.307 +	    info.play.sample_rate = spec->freq;
   1.308 +	    info.play.channels = spec->channels;
   1.309 +	    info.play.precision = (enc == AUDIO_ENCODING_ULAW)
   1.310 +		                  ? 8 : spec->format & 0xff;
   1.311 +	    info.play.encoding = enc;
   1.312 +	    if( ioctl(audio_fd, AUDIO_SETINFO, &info) == 0 ) {
   1.313 +
   1.314 +		/* Check to be sure we got what we wanted */
   1.315 +		if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
   1.316 +		    SDL_SetError("Error getting audio parameters: %s",
   1.317 +				 strerror(errno));
   1.318 +		    return -1;
   1.319 +		}
   1.320 +		if(info.play.encoding == enc
   1.321 +		   && info.play.precision == (spec->format & 0xff)
   1.322 +		   && info.play.channels == spec->channels) {
   1.323 +		    /* Yow! All seems to be well! */
   1.324 +		    spec->freq = info.play.sample_rate;
   1.325 +		    break;
   1.326 +		}
   1.327 +	    }
   1.328 +
   1.329 +	    switch(enc) {
   1.330 +	    case AUDIO_ENCODING_LINEAR8:
   1.331 +		/* unsigned 8bit apparently not supported here */
   1.332 +		enc = AUDIO_ENCODING_LINEAR;
   1.333 +		spec->format = AUDIO_S16SYS;
   1.334 +		break;	/* try again */
   1.335 +
   1.336 +	    case AUDIO_ENCODING_LINEAR:
   1.337 +		/* linear 16bit didn't work either, resort to -law */
   1.338 +		enc = AUDIO_ENCODING_ULAW;
   1.339 +		spec->channels = 1;
   1.340 +		spec->freq = 8000;
   1.341 +		spec->format = AUDIO_U8;
   1.342 +		ulaw_only = 1;
   1.343 +		break;
   1.344 +
   1.345 +	    default:
   1.346 +		/* oh well... */
   1.347 +		SDL_SetError("Error setting audio parameters: %s",
   1.348 +			     strerror(errno));
   1.349 +		return -1;
   1.350 +	    }
   1.351 +	}
   1.352 +#endif /* AUDIO_SETINFO */
   1.353 +	written = 0;
   1.354 +
   1.355 +	/* We can actually convert on-the-fly to U-Law */
   1.356 +	if ( ulaw_only ) {
   1.357 +	        spec->freq = desired_freq;
   1.358 +		fragsize = (spec->samples*1000)/(spec->freq/8);
   1.359 +		frequency = 8;
   1.360 +		ulaw_buf = (Uint8 *)malloc(fragsize);
   1.361 +		if ( ulaw_buf == NULL ) {
   1.362 +			SDL_OutOfMemory();
   1.363 +			return(-1);
   1.364 +		}
   1.365 +		spec->channels = 1;
   1.366 +	} else {
   1.367 +		fragsize = spec->samples;
   1.368 +		frequency = spec->freq/1000;
   1.369 +	}
   1.370 +#ifdef DEBUG_AUDIO
   1.371 +	fprintf(stderr, "Audio device %s U-Law only\n", 
   1.372 +				ulaw_only ? "is" : "is not");
   1.373 +	fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
   1.374 +		spec->format, spec->channels, spec->freq);
   1.375 +#endif
   1.376 +
   1.377 +	/* Update the fragment size as size in bytes */
   1.378 +	SDL_CalculateAudioSpec(spec);
   1.379 +
   1.380 +	/* Allocate mixing buffer */
   1.381 +	mixbuf = (Uint8 *)SDL_AllocAudioMem(spec->size);
   1.382 +	if ( mixbuf == NULL ) {
   1.383 +		SDL_OutOfMemory();
   1.384 +		return(-1);
   1.385 +	}
   1.386 +	memset(mixbuf, spec->silence, spec->size);
   1.387 +
   1.388 +	/* We're ready to rock and roll. :-) */
   1.389 +	return(0);
   1.390 +}
   1.391 +
   1.392 +/************************************************************************/
   1.393 +/* This function (snd2au()) copyrighted:                                */
   1.394 +/************************************************************************/
   1.395 +/*      Copyright 1989 by Rich Gopstein and Harris Corporation          */
   1.396 +/*                                                                      */
   1.397 +/*      Permission to use, copy, modify, and distribute this software   */
   1.398 +/*      and its documentation for any purpose and without fee is        */
   1.399 +/*      hereby granted, provided that the above copyright notice        */
   1.400 +/*      appears in all copies and that both that copyright notice and   */
   1.401 +/*      this permission notice appear in supporting documentation, and  */
   1.402 +/*      that the name of Rich Gopstein and Harris Corporation not be    */
   1.403 +/*      used in advertising or publicity pertaining to distribution     */
   1.404 +/*      of the software without specific, written prior permission.     */
   1.405 +/*      Rich Gopstein and Harris Corporation make no representations    */
   1.406 +/*      about the suitability of this software for any purpose.  It     */
   1.407 +/*      provided "as is" without express or implied warranty.           */
   1.408 +/************************************************************************/
   1.409 +
   1.410 +static Uint8 snd2au(int sample)
   1.411 +{
   1.412 +
   1.413 +	int mask;
   1.414 +
   1.415 +	if (sample < 0) {
   1.416 +		sample = -sample;
   1.417 +		mask = 0x7f;
   1.418 +	} else {
   1.419 +		mask = 0xff;
   1.420 +	}
   1.421 +
   1.422 +	if (sample < 32) {
   1.423 +		sample = 0xF0 | (15 - sample / 2);
   1.424 +	} else if (sample < 96) {
   1.425 +		sample = 0xE0 | (15 - (sample - 32) / 4);
   1.426 +	} else if (sample < 224) {
   1.427 +		sample = 0xD0 | (15 - (sample - 96) / 8);
   1.428 +	} else if (sample < 480) {
   1.429 +		sample = 0xC0 | (15 - (sample - 224) / 16);
   1.430 +	} else if (sample < 992) {
   1.431 +		sample = 0xB0 | (15 - (sample - 480) / 32);
   1.432 +	} else if (sample < 2016) {
   1.433 +		sample = 0xA0 | (15 - (sample - 992) / 64);
   1.434 +	} else if (sample < 4064) {
   1.435 +		sample = 0x90 | (15 - (sample - 2016) / 128);
   1.436 +	} else if (sample < 8160) {
   1.437 +		sample = 0x80 | (15 - (sample - 4064) /  256);
   1.438 +	} else {
   1.439 +		sample = 0x80;
   1.440 +	}
   1.441 +	return (mask & sample);
   1.442 +}