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