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