SDL_nasaudio.c (11799B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#if SDL_AUDIO_DRIVER_NAS 24 25/* Allow access to a raw mixing buffer */ 26 27#include <signal.h> 28#include <unistd.h> 29 30#include "SDL_timer.h" 31#include "SDL_audio.h" 32#include "SDL_loadso.h" 33#include "../SDL_audiomem.h" 34#include "../SDL_audio_c.h" 35#include "SDL_nasaudio.h" 36 37static struct SDL_PrivateAudioData *this2 = NULL; 38 39 40static void (*NAS_AuCloseServer) (AuServer *); 41static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *); 42static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *); 43static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *); 44static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *); 45static void (*NAS_AuSetElements) 46 (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *); 47static void (*NAS_AuWriteElement) 48 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *); 49static AuServer *(*NAS_AuOpenServer) 50 (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **); 51static AuEventHandlerRec *(*NAS_AuRegisterEventHandler) 52 (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer); 53 54 55#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC 56 57static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC; 58static void *nas_handle = NULL; 59 60static int 61load_nas_sym(const char *fn, void **addr) 62{ 63 *addr = SDL_LoadFunction(nas_handle, fn); 64 if (*addr == NULL) { 65 return 0; 66 } 67 return 1; 68} 69 70/* cast funcs to char* first, to please GCC's strict aliasing rules. */ 71#define SDL_NAS_SYM(x) \ 72 if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1 73#else 74#define SDL_NAS_SYM(x) NAS_##x = x 75#endif 76 77static int 78load_nas_syms(void) 79{ 80 SDL_NAS_SYM(AuCloseServer); 81 SDL_NAS_SYM(AuNextEvent); 82 SDL_NAS_SYM(AuDispatchEvent); 83 SDL_NAS_SYM(AuCreateFlow); 84 SDL_NAS_SYM(AuStartFlow); 85 SDL_NAS_SYM(AuSetElements); 86 SDL_NAS_SYM(AuWriteElement); 87 SDL_NAS_SYM(AuOpenServer); 88 SDL_NAS_SYM(AuRegisterEventHandler); 89 return 0; 90} 91 92#undef SDL_NAS_SYM 93 94#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC 95 96static void 97UnloadNASLibrary(void) 98{ 99 if (nas_handle != NULL) { 100 SDL_UnloadObject(nas_handle); 101 nas_handle = NULL; 102 } 103} 104 105static int 106LoadNASLibrary(void) 107{ 108 int retval = 0; 109 if (nas_handle == NULL) { 110 nas_handle = SDL_LoadObject(nas_library); 111 if (nas_handle == NULL) { 112 /* Copy error string so we can use it in a new SDL_SetError(). */ 113 const char *origerr = SDL_GetError(); 114 const size_t len = SDL_strlen(origerr) + 1; 115 char *err = (char *) alloca(len); 116 SDL_strlcpy(err, origerr, len); 117 retval = -1; 118 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n", 119 nas_library, err); 120 } else { 121 retval = load_nas_syms(); 122 if (retval < 0) { 123 UnloadNASLibrary(); 124 } 125 } 126 } 127 return retval; 128} 129 130#else 131 132static void 133UnloadNASLibrary(void) 134{ 135} 136 137static int 138LoadNASLibrary(void) 139{ 140 load_nas_syms(); 141 return 0; 142} 143 144#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */ 145 146/* This function waits until it is possible to write a full sound buffer */ 147static void 148NAS_WaitDevice(_THIS) 149{ 150 while (this->hidden->buf_free < this->hidden->mixlen) { 151 AuEvent ev; 152 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); 153 NAS_AuDispatchEvent(this->hidden->aud, &ev); 154 } 155} 156 157static void 158NAS_PlayDevice(_THIS) 159{ 160 while (this->hidden->mixlen > this->hidden->buf_free) { 161 /* 162 * We think the buffer is full? Yikes! Ask the server for events, 163 * in the hope that some of them is LowWater events telling us more 164 * of the buffer is free now than what we think. 165 */ 166 AuEvent ev; 167 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); 168 NAS_AuDispatchEvent(this->hidden->aud, &ev); 169 } 170 this->hidden->buf_free -= this->hidden->mixlen; 171 172 /* Write the audio data */ 173 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0, 174 this->hidden->mixlen, this->hidden->mixbuf, AuFalse, 175 NULL); 176 177 this->hidden->written += this->hidden->mixlen; 178 179#ifdef DEBUG_AUDIO 180 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen); 181#endif 182} 183 184static Uint8 * 185NAS_GetDeviceBuf(_THIS) 186{ 187 return (this->hidden->mixbuf); 188} 189 190static void 191NAS_CloseDevice(_THIS) 192{ 193 if (this->hidden != NULL) { 194 SDL_FreeAudioMem(this->hidden->mixbuf); 195 this->hidden->mixbuf = NULL; 196 if (this->hidden->aud) { 197 NAS_AuCloseServer(this->hidden->aud); 198 this->hidden->aud = 0; 199 } 200 SDL_free(this->hidden); 201 this2 = this->hidden = NULL; 202 } 203} 204 205static unsigned char 206sdlformat_to_auformat(unsigned int fmt) 207{ 208 switch (fmt) { 209 case AUDIO_U8: 210 return AuFormatLinearUnsigned8; 211 case AUDIO_S8: 212 return AuFormatLinearSigned8; 213 case AUDIO_U16LSB: 214 return AuFormatLinearUnsigned16LSB; 215 case AUDIO_U16MSB: 216 return AuFormatLinearUnsigned16MSB; 217 case AUDIO_S16LSB: 218 return AuFormatLinearSigned16LSB; 219 case AUDIO_S16MSB: 220 return AuFormatLinearSigned16MSB; 221 } 222 return AuNone; 223} 224 225static AuBool 226event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd) 227{ 228 switch (ev->type) { 229 case AuEventTypeElementNotify: 230 { 231 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev; 232 233 switch (event->kind) { 234 case AuElementNotifyKindLowWater: 235 if (this2->buf_free >= 0) { 236 this2->really += event->num_bytes; 237 gettimeofday(&this2->last_tv, 0); 238 this2->buf_free += event->num_bytes; 239 } else { 240 this2->buf_free = event->num_bytes; 241 } 242 break; 243 case AuElementNotifyKindState: 244 switch (event->cur_state) { 245 case AuStatePause: 246 if (event->reason != AuReasonUser) { 247 if (this2->buf_free >= 0) { 248 this2->really += event->num_bytes; 249 gettimeofday(&this2->last_tv, 0); 250 this2->buf_free += event->num_bytes; 251 } else { 252 this2->buf_free = event->num_bytes; 253 } 254 } 255 break; 256 } 257 } 258 } 259 } 260 return AuTrue; 261} 262 263static AuDeviceID 264find_device(_THIS, int nch) 265{ 266 /* These "Au" things are all macros, not functions... */ 267 int i; 268 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) { 269 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) == 270 AuComponentKindPhysicalOutput) && 271 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) { 272 return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i)); 273 } 274 } 275 return AuNone; 276} 277 278static int 279NAS_OpenDevice(_THIS, const char *devname, int iscapture) 280{ 281 AuElement elms[3]; 282 int buffer_size; 283 SDL_AudioFormat test_format, format; 284 285 /* Initialize all variables that we clean on shutdown */ 286 this->hidden = (struct SDL_PrivateAudioData *) 287 SDL_malloc((sizeof *this->hidden)); 288 if (this->hidden == NULL) { 289 return SDL_OutOfMemory(); 290 } 291 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 292 293 /* Try for a closest match on audio format */ 294 format = 0; 295 for (test_format = SDL_FirstAudioFormat(this->spec.format); 296 !format && test_format;) { 297 format = sdlformat_to_auformat(test_format); 298 if (format == AuNone) { 299 test_format = SDL_NextAudioFormat(); 300 } 301 } 302 if (format == 0) { 303 NAS_CloseDevice(this); 304 return SDL_SetError("NAS: Couldn't find any hardware audio formats"); 305 } 306 this->spec.format = test_format; 307 308 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); 309 if (this->hidden->aud == 0) { 310 NAS_CloseDevice(this); 311 return SDL_SetError("NAS: Couldn't open connection to NAS server"); 312 } 313 314 this->hidden->dev = find_device(this, this->spec.channels); 315 if ((this->hidden->dev == AuNone) 316 || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) { 317 NAS_CloseDevice(this); 318 return SDL_SetError("NAS: Couldn't find a fitting device on NAS server"); 319 } 320 321 buffer_size = this->spec.freq; 322 if (buffer_size < 4096) 323 buffer_size = 4096; 324 325 if (buffer_size > 32768) 326 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */ 327 328 /* Calculate the final parameters for this audio specification */ 329 SDL_CalculateAudioSpec(&this->spec); 330 331 this2 = this->hidden; 332 333 AuMakeElementImportClient(elms, this->spec.freq, format, 334 this->spec.channels, AuTrue, buffer_size, 335 buffer_size / 4, 0, NULL); 336 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq, 337 AuUnlimitedSamples, 0, NULL); 338 NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, 339 NULL); 340 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, 341 this->hidden->flow, event_handler, 342 (AuPointer) NULL); 343 344 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL); 345 346 /* Allocate mixing buffer */ 347 this->hidden->mixlen = this->spec.size; 348 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 349 if (this->hidden->mixbuf == NULL) { 350 NAS_CloseDevice(this); 351 return SDL_OutOfMemory(); 352 } 353 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 354 355 /* We're ready to rock and roll. :-) */ 356 return 0; 357} 358 359static void 360NAS_Deinitialize(void) 361{ 362 UnloadNASLibrary(); 363} 364 365static int 366NAS_Init(SDL_AudioDriverImpl * impl) 367{ 368 if (LoadNASLibrary() < 0) { 369 return 0; 370 } else { 371 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); 372 if (aud == NULL) { 373 SDL_SetError("NAS: AuOpenServer() failed (no audio server?)"); 374 return 0; 375 } 376 NAS_AuCloseServer(aud); 377 } 378 379 /* Set the function pointers */ 380 impl->OpenDevice = NAS_OpenDevice; 381 impl->PlayDevice = NAS_PlayDevice; 382 impl->WaitDevice = NAS_WaitDevice; 383 impl->GetDeviceBuf = NAS_GetDeviceBuf; 384 impl->CloseDevice = NAS_CloseDevice; 385 impl->Deinitialize = NAS_Deinitialize; 386 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */ 387 388 return 1; /* this audio target is available. */ 389} 390 391AudioBootStrap NAS_bootstrap = { 392 "nas", "Network Audio System", NAS_Init, 0 393}; 394 395#endif /* SDL_AUDIO_DRIVER_NAS */ 396 397/* vi: set ts=4 sw=4 expandtab: */