src/audio/nas/SDL_nasaudio.c
changeset 0 74212992fb08
child 252 e8157fcb3114
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/audio/nas/SDL_nasaudio.c	Thu Apr 26 16:45:43 2001 +0000
     1.3 @@ -0,0 +1,308 @@
     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 +    This driver was written by:
    1.26 +    Erik Inge Bolsų
    1.27 +    knan@mo.himolde.no
    1.28 +*/
    1.29 +
    1.30 +#ifdef SAVE_RCSID
    1.31 +static char rcsid =
    1.32 + "@(#) $Id$";
    1.33 +#endif
    1.34 +
    1.35 +/* Allow access to a raw mixing buffer */
    1.36 +
    1.37 +#include <stdlib.h>
    1.38 +#include <stdio.h>
    1.39 +#include <string.h>
    1.40 +#include <errno.h>
    1.41 +#include <signal.h>
    1.42 +#include <unistd.h>
    1.43 +
    1.44 +#include "SDL_audio.h"
    1.45 +#include "SDL_error.h"
    1.46 +#include "SDL_audiomem.h"
    1.47 +#include "SDL_audio_c.h"
    1.48 +#include "SDL_timer.h"
    1.49 +#include "SDL_audiodev_c.h"
    1.50 +#include "SDL_nasaudio.h"
    1.51 +
    1.52 +/* The tag name used by artsc audio */
    1.53 +#define NAS_DRIVER_NAME         "nas"
    1.54 +
    1.55 +static struct SDL_PrivateAudioData *this2 = NULL;
    1.56 +
    1.57 +/* Audio driver functions */
    1.58 +static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec);
    1.59 +static void NAS_WaitAudio(_THIS);
    1.60 +static void NAS_PlayAudio(_THIS);
    1.61 +static Uint8 *NAS_GetAudioBuf(_THIS);
    1.62 +static void NAS_CloseAudio(_THIS);
    1.63 +
    1.64 +/* Audio driver bootstrap functions */
    1.65 +
    1.66 +static int Audio_Available(void)
    1.67 +{
    1.68 +	AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
    1.69 +	if (!aud) return 0;
    1.70 +
    1.71 +	AuCloseServer(aud);
    1.72 +	return 1;
    1.73 +}
    1.74 +
    1.75 +static void Audio_DeleteDevice(SDL_AudioDevice *device)
    1.76 +{
    1.77 +	free(device->hidden);
    1.78 +	free(device);
    1.79 +}
    1.80 +
    1.81 +static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    1.82 +{
    1.83 +	SDL_AudioDevice *this;
    1.84 +
    1.85 +	/* Initialize all variables that we clean on shutdown */
    1.86 +	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
    1.87 +	if ( this ) {
    1.88 +		memset(this, 0, (sizeof *this));
    1.89 +		this->hidden = (struct SDL_PrivateAudioData *)
    1.90 +				malloc((sizeof *this->hidden));
    1.91 +	}
    1.92 +	if ( (this == NULL) || (this->hidden == NULL) ) {
    1.93 +		SDL_OutOfMemory();
    1.94 +		if ( this ) {
    1.95 +			free(this);
    1.96 +		}
    1.97 +		return(0);
    1.98 +	}
    1.99 +	memset(this->hidden, 0, (sizeof *this->hidden));
   1.100 +
   1.101 +	/* Set the function pointers */
   1.102 +	this->OpenAudio = NAS_OpenAudio;
   1.103 +	this->WaitAudio = NAS_WaitAudio;
   1.104 +	this->PlayAudio = NAS_PlayAudio;
   1.105 +	this->GetAudioBuf = NAS_GetAudioBuf;
   1.106 +	this->CloseAudio = NAS_CloseAudio;
   1.107 +
   1.108 +	this->free = Audio_DeleteDevice;
   1.109 +
   1.110 +	return this;
   1.111 +}
   1.112 +
   1.113 +AudioBootStrap NAS_bootstrap = {
   1.114 +	NAS_DRIVER_NAME, "Network Audio System",
   1.115 +	Audio_Available, Audio_CreateDevice
   1.116 +};
   1.117 +
   1.118 +/* This function waits until it is possible to write a full sound buffer */
   1.119 +static void NAS_WaitAudio(_THIS)
   1.120 +{
   1.121 +	while ( this->hidden->buf_free < this->hidden->mixlen ) {
   1.122 +		AuEvent ev;
   1.123 +		AuNextEvent(this->hidden->aud, AuTrue, &ev);
   1.124 +		AuDispatchEvent(this->hidden->aud, &ev);
   1.125 +	}
   1.126 +}
   1.127 +
   1.128 +static void NAS_PlayAudio(_THIS)
   1.129 +{
   1.130 +	while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events,
   1.131 +				    in the hope that some of them is LowWater events telling us more
   1.132 +				    of the buffer is free now than what we think. */
   1.133 +		AuEvent ev;
   1.134 +		AuNextEvent(this->hidden->aud, AuTrue, &ev);
   1.135 +		AuDispatchEvent(this->hidden->aud, &ev);
   1.136 +	}
   1.137 +	this->hidden->buf_free -= this->hidden->mixlen;
   1.138 +
   1.139 +	/* Write the audio data */
   1.140 +	AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
   1.141 +
   1.142 +	this->hidden->written += this->hidden->mixlen;
   1.143 +	
   1.144 +#ifdef DEBUG_AUDIO
   1.145 +	fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
   1.146 +#endif
   1.147 +}
   1.148 +
   1.149 +static Uint8 *NAS_GetAudioBuf(_THIS)
   1.150 +{
   1.151 +	return(this->hidden->mixbuf);
   1.152 +}
   1.153 +
   1.154 +static void NAS_CloseAudio(_THIS)
   1.155 +{
   1.156 +	if ( this->hidden->mixbuf != NULL ) {
   1.157 +		SDL_FreeAudioMem(this->hidden->mixbuf);
   1.158 +		this->hidden->mixbuf = NULL;
   1.159 +	}
   1.160 +	if ( this->hidden->aud ) {
   1.161 +		AuCloseServer(this->hidden->aud);
   1.162 +		this->hidden->aud = 0;
   1.163 +	}
   1.164 +}
   1.165 +
   1.166 +static unsigned char sdlformat_to_auformat(unsigned int fmt)
   1.167 +{
   1.168 +  switch (fmt)
   1.169 +    {
   1.170 +    case AUDIO_U8:
   1.171 +      return AuFormatLinearUnsigned8;
   1.172 +    case AUDIO_S8:
   1.173 +      return AuFormatLinearSigned8;
   1.174 +    case AUDIO_U16LSB:
   1.175 +      return AuFormatLinearUnsigned16LSB;
   1.176 +    case AUDIO_U16MSB:
   1.177 +      return AuFormatLinearUnsigned16MSB;
   1.178 +    case AUDIO_S16LSB:
   1.179 +      return AuFormatLinearSigned16LSB;
   1.180 +    case AUDIO_S16MSB:
   1.181 +      return AuFormatLinearSigned16MSB;
   1.182 +    }
   1.183 +  return AuNone;
   1.184 +}
   1.185 +
   1.186 +static AuBool
   1.187 +event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
   1.188 +{
   1.189 +	switch (ev->type) {
   1.190 +	case AuEventTypeElementNotify: {
   1.191 +		AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
   1.192 +
   1.193 +		switch (event->kind) {
   1.194 +		case AuElementNotifyKindLowWater:
   1.195 +			if (this2->buf_free >= 0) {
   1.196 +				this2->really += event->num_bytes;
   1.197 +				gettimeofday(&this2->last_tv, 0);
   1.198 +				this2->buf_free += event->num_bytes;
   1.199 +			} else {
   1.200 +				this2->buf_free = event->num_bytes;
   1.201 +			}
   1.202 +			break;
   1.203 +		case AuElementNotifyKindState:
   1.204 +			switch (event->cur_state) {
   1.205 +			case AuStatePause:
   1.206 +				if (event->reason != AuReasonUser) {
   1.207 +					if (this2->buf_free >= 0) {
   1.208 +						this2->really += event->num_bytes;
   1.209 +						gettimeofday(&this2->last_tv, 0);
   1.210 +						this2->buf_free += event->num_bytes;
   1.211 +					} else {
   1.212 +						this2->buf_free = event->num_bytes;
   1.213 +					}
   1.214 +				}
   1.215 +				break;
   1.216 +			}
   1.217 +		}
   1.218 +	}
   1.219 +	}
   1.220 +	return AuTrue;
   1.221 +}
   1.222 +
   1.223 +static AuDeviceID
   1.224 +find_device(_THIS, int nch)
   1.225 +{
   1.226 +	int i;
   1.227 +	for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
   1.228 +		if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
   1.229 +				AuComponentKindPhysicalOutput) &&
   1.230 +			AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
   1.231 +			return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
   1.232 +		}
   1.233 +	}
   1.234 +	return AuNone;
   1.235 +}
   1.236 +
   1.237 +static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
   1.238 +{
   1.239 +	AuElement elms[3];
   1.240 +	int buffer_size;
   1.241 +	Uint16 test_format, format;
   1.242 +
   1.243 +	this->hidden->mixbuf = NULL;
   1.244 +
   1.245 +	/* Try for a closest match on audio format */
   1.246 +	format = 0;
   1.247 +	for ( test_format = SDL_FirstAudioFormat(spec->format);
   1.248 +						! format && test_format; ) {
   1.249 +		format = sdlformat_to_auformat(test_format);
   1.250 +
   1.251 +		if (format == AuNone) {
   1.252 +			test_format = SDL_NextAudioFormat();
   1.253 +		}
   1.254 +	}
   1.255 +	if ( format == 0 ) {
   1.256 +		SDL_SetError("Couldn't find any hardware audio formats");
   1.257 +		return(-1);
   1.258 +	}
   1.259 +	spec->format = test_format;
   1.260 +
   1.261 +	this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
   1.262 +	if (this->hidden->aud == 0)
   1.263 +	{
   1.264 +		SDL_SetError("Couldn't open connection to NAS server");
   1.265 +		return (-1);
   1.266 +	}
   1.267 +	
   1.268 +	this->hidden->dev = find_device(this, spec->channels);
   1.269 +	if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
   1.270 +		AuCloseServer(this->hidden->aud);
   1.271 +		this->hidden->aud = 0;
   1.272 +		SDL_SetError("Couldn't find a fitting playback device on NAS server");
   1.273 +		return (-1);
   1.274 +	}
   1.275 +	
   1.276 +	buffer_size = spec->freq;
   1.277 +	if (buffer_size < 4096)
   1.278 +		buffer_size = 4096; 
   1.279 +
   1.280 +	if (buffer_size > 32768)
   1.281 +		buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
   1.282 +
   1.283 +	/* Calculate the final parameters for this audio specification */
   1.284 +	SDL_CalculateAudioSpec(spec);
   1.285 +
   1.286 +	this2 = this->hidden;
   1.287 +
   1.288 +	AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
   1.289 +				buffer_size, buffer_size / 4, 0, NULL);
   1.290 +	AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
   1.291 +				AuUnlimitedSamples, 0, NULL);
   1.292 +	AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
   1.293 +	AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
   1.294 +				event_handler, (AuPointer) NULL);
   1.295 +
   1.296 +	AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   1.297 +
   1.298 +	/* Allocate mixing buffer */
   1.299 +	this->hidden->mixlen = spec->size;
   1.300 +	this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
   1.301 +	if ( this->hidden->mixbuf == NULL ) {
   1.302 +		return(-1);
   1.303 +	}
   1.304 +	memset(this->hidden->mixbuf, spec->silence, spec->size);
   1.305 +
   1.306 +	/* Get the parent process id (we're the parent of the audio thread) */
   1.307 +	this->hidden->parent = getpid();
   1.308 +
   1.309 +	/* We're ready to rock and roll. :-) */
   1.310 +	return(0);
   1.311 +}