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