src/audio/nas/SDL_nasaudio.c
author Sam Lantinga
Sun, 04 Jan 2004 16:49:27 +0000
changeset 769 b8d311d90021
parent 297 f6ffac90895c
child 1312 c9b51268668f
permissions -rw-r--r--
Updated copyright information for 2004 (Happy New Year!)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 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@libsdl.org
    21 
    22     This driver was written by:
    23     Erik Inge Bolsų
    24     knan@mo.himolde.no
    25 */
    26 
    27 #ifdef SAVE_RCSID
    28 static char rcsid =
    29  "@(#) $Id$";
    30 #endif
    31 
    32 /* Allow access to a raw mixing buffer */
    33 
    34 #include <stdlib.h>
    35 #include <stdio.h>
    36 #include <string.h>
    37 #include <errno.h>
    38 #include <signal.h>
    39 #include <unistd.h>
    40 
    41 #include "SDL_audio.h"
    42 #include "SDL_error.h"
    43 #include "SDL_audiomem.h"
    44 #include "SDL_audio_c.h"
    45 #include "SDL_timer.h"
    46 #include "SDL_audiodev_c.h"
    47 #include "SDL_nasaudio.h"
    48 
    49 /* The tag name used by artsc audio */
    50 #define NAS_DRIVER_NAME         "nas"
    51 
    52 static struct SDL_PrivateAudioData *this2 = NULL;
    53 
    54 /* Audio driver functions */
    55 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec);
    56 static void NAS_WaitAudio(_THIS);
    57 static void NAS_PlayAudio(_THIS);
    58 static Uint8 *NAS_GetAudioBuf(_THIS);
    59 static void NAS_CloseAudio(_THIS);
    60 
    61 /* Audio driver bootstrap functions */
    62 
    63 static int Audio_Available(void)
    64 {
    65 	AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
    66 	if (!aud) return 0;
    67 
    68 	AuCloseServer(aud);
    69 	return 1;
    70 }
    71 
    72 static void Audio_DeleteDevice(SDL_AudioDevice *device)
    73 {
    74 	free(device->hidden);
    75 	free(device);
    76 }
    77 
    78 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    79 {
    80 	SDL_AudioDevice *this;
    81 
    82 	/* Initialize all variables that we clean on shutdown */
    83 	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
    84 	if ( this ) {
    85 		memset(this, 0, (sizeof *this));
    86 		this->hidden = (struct SDL_PrivateAudioData *)
    87 				malloc((sizeof *this->hidden));
    88 	}
    89 	if ( (this == NULL) || (this->hidden == NULL) ) {
    90 		SDL_OutOfMemory();
    91 		if ( this ) {
    92 			free(this);
    93 		}
    94 		return(0);
    95 	}
    96 	memset(this->hidden, 0, (sizeof *this->hidden));
    97 
    98 	/* Set the function pointers */
    99 	this->OpenAudio = NAS_OpenAudio;
   100 	this->WaitAudio = NAS_WaitAudio;
   101 	this->PlayAudio = NAS_PlayAudio;
   102 	this->GetAudioBuf = NAS_GetAudioBuf;
   103 	this->CloseAudio = NAS_CloseAudio;
   104 
   105 	this->free = Audio_DeleteDevice;
   106 
   107 	return this;
   108 }
   109 
   110 AudioBootStrap NAS_bootstrap = {
   111 	NAS_DRIVER_NAME, "Network Audio System",
   112 	Audio_Available, Audio_CreateDevice
   113 };
   114 
   115 /* This function waits until it is possible to write a full sound buffer */
   116 static void NAS_WaitAudio(_THIS)
   117 {
   118 	while ( this->hidden->buf_free < this->hidden->mixlen ) {
   119 		AuEvent ev;
   120 		AuNextEvent(this->hidden->aud, AuTrue, &ev);
   121 		AuDispatchEvent(this->hidden->aud, &ev);
   122 	}
   123 }
   124 
   125 static void NAS_PlayAudio(_THIS)
   126 {
   127 	while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events,
   128 				    in the hope that some of them is LowWater events telling us more
   129 				    of the buffer is free now than what we think. */
   130 		AuEvent ev;
   131 		AuNextEvent(this->hidden->aud, AuTrue, &ev);
   132 		AuDispatchEvent(this->hidden->aud, &ev);
   133 	}
   134 	this->hidden->buf_free -= this->hidden->mixlen;
   135 
   136 	/* Write the audio data */
   137 	AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
   138 
   139 	this->hidden->written += this->hidden->mixlen;
   140 	
   141 #ifdef DEBUG_AUDIO
   142 	fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
   143 #endif
   144 }
   145 
   146 static Uint8 *NAS_GetAudioBuf(_THIS)
   147 {
   148 	return(this->hidden->mixbuf);
   149 }
   150 
   151 static void NAS_CloseAudio(_THIS)
   152 {
   153 	if ( this->hidden->mixbuf != NULL ) {
   154 		SDL_FreeAudioMem(this->hidden->mixbuf);
   155 		this->hidden->mixbuf = NULL;
   156 	}
   157 	if ( this->hidden->aud ) {
   158 		AuCloseServer(this->hidden->aud);
   159 		this->hidden->aud = 0;
   160 	}
   161 }
   162 
   163 static unsigned char sdlformat_to_auformat(unsigned int fmt)
   164 {
   165   switch (fmt)
   166     {
   167     case AUDIO_U8:
   168       return AuFormatLinearUnsigned8;
   169     case AUDIO_S8:
   170       return AuFormatLinearSigned8;
   171     case AUDIO_U16LSB:
   172       return AuFormatLinearUnsigned16LSB;
   173     case AUDIO_U16MSB:
   174       return AuFormatLinearUnsigned16MSB;
   175     case AUDIO_S16LSB:
   176       return AuFormatLinearSigned16LSB;
   177     case AUDIO_S16MSB:
   178       return AuFormatLinearSigned16MSB;
   179     }
   180   return AuNone;
   181 }
   182 
   183 static AuBool
   184 event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
   185 {
   186 	switch (ev->type) {
   187 	case AuEventTypeElementNotify: {
   188 		AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
   189 
   190 		switch (event->kind) {
   191 		case AuElementNotifyKindLowWater:
   192 			if (this2->buf_free >= 0) {
   193 				this2->really += event->num_bytes;
   194 				gettimeofday(&this2->last_tv, 0);
   195 				this2->buf_free += event->num_bytes;
   196 			} else {
   197 				this2->buf_free = event->num_bytes;
   198 			}
   199 			break;
   200 		case AuElementNotifyKindState:
   201 			switch (event->cur_state) {
   202 			case AuStatePause:
   203 				if (event->reason != AuReasonUser) {
   204 					if (this2->buf_free >= 0) {
   205 						this2->really += event->num_bytes;
   206 						gettimeofday(&this2->last_tv, 0);
   207 						this2->buf_free += event->num_bytes;
   208 					} else {
   209 						this2->buf_free = event->num_bytes;
   210 					}
   211 				}
   212 				break;
   213 			}
   214 		}
   215 	}
   216 	}
   217 	return AuTrue;
   218 }
   219 
   220 static AuDeviceID
   221 find_device(_THIS, int nch)
   222 {
   223 	int i;
   224 	for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
   225 		if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
   226 				AuComponentKindPhysicalOutput) &&
   227 			AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
   228 			return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
   229 		}
   230 	}
   231 	return AuNone;
   232 }
   233 
   234 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
   235 {
   236 	AuElement elms[3];
   237 	int buffer_size;
   238 	Uint16 test_format, format;
   239 
   240 	this->hidden->mixbuf = NULL;
   241 
   242 	/* Try for a closest match on audio format */
   243 	format = 0;
   244 	for ( test_format = SDL_FirstAudioFormat(spec->format);
   245 						! format && test_format; ) {
   246 		format = sdlformat_to_auformat(test_format);
   247 
   248 		if (format == AuNone) {
   249 			test_format = SDL_NextAudioFormat();
   250 		}
   251 	}
   252 	if ( format == 0 ) {
   253 		SDL_SetError("Couldn't find any hardware audio formats");
   254 		return(-1);
   255 	}
   256 	spec->format = test_format;
   257 
   258 	this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
   259 	if (this->hidden->aud == 0)
   260 	{
   261 		SDL_SetError("Couldn't open connection to NAS server");
   262 		return (-1);
   263 	}
   264 	
   265 	this->hidden->dev = find_device(this, spec->channels);
   266 	if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
   267 		AuCloseServer(this->hidden->aud);
   268 		this->hidden->aud = 0;
   269 		SDL_SetError("Couldn't find a fitting playback device on NAS server");
   270 		return (-1);
   271 	}
   272 	
   273 	buffer_size = spec->freq;
   274 	if (buffer_size < 4096)
   275 		buffer_size = 4096; 
   276 
   277 	if (buffer_size > 32768)
   278 		buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
   279 
   280 	/* Calculate the final parameters for this audio specification */
   281 	SDL_CalculateAudioSpec(spec);
   282 
   283 	this2 = this->hidden;
   284 
   285 	AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
   286 				buffer_size, buffer_size / 4, 0, NULL);
   287 	AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
   288 				AuUnlimitedSamples, 0, NULL);
   289 	AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
   290 	AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
   291 				event_handler, (AuPointer) NULL);
   292 
   293 	AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   294 
   295 	/* Allocate mixing buffer */
   296 	this->hidden->mixlen = spec->size;
   297 	this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
   298 	if ( this->hidden->mixbuf == NULL ) {
   299 		return(-1);
   300 	}
   301 	memset(this->hidden->mixbuf, spec->silence, spec->size);
   302 
   303 	/* Get the parent process id (we're the parent of the audio thread) */
   304 	this->hidden->parent = getpid();
   305 
   306 	/* We're ready to rock and roll. :-) */
   307 	return(0);
   308 }