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