Atlas - SDL_nasaudio.c

Home / ext / SDL2 / src / audio / nas Lines: 4 | Size: 13964 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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_audio_c.h" 34#include "SDL_nasaudio.h" 35 36static void (*NAS_AuCloseServer) (AuServer *); 37static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *); 38static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *); 39static void (*NAS_AuHandleEvents) (AuServer *); 40static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *); 41static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *); 42static void (*NAS_AuSetElements) 43 (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *); 44static void (*NAS_AuWriteElement) 45 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *); 46static AuUint32 (*NAS_AuReadElement) 47 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuStatus *); 48static AuServer *(*NAS_AuOpenServer) 49 (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **); 50static AuEventHandlerRec *(*NAS_AuRegisterEventHandler) 51 (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer); 52 53 54#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC 55 56static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC; 57static void *nas_handle = NULL; 58 59static int 60load_nas_sym(const char *fn, void **addr) 61{ 62 *addr = SDL_LoadFunction(nas_handle, fn); 63 if (*addr == NULL) { 64 return 0; 65 } 66 return 1; 67} 68 69/* cast funcs to char* first, to please GCC's strict aliasing rules. */ 70#define SDL_NAS_SYM(x) \ 71 if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1 72#else 73#define SDL_NAS_SYM(x) NAS_##x = x 74#endif 75 76static int 77load_nas_syms(void) 78{ 79 SDL_NAS_SYM(AuCloseServer); 80 SDL_NAS_SYM(AuNextEvent); 81 SDL_NAS_SYM(AuDispatchEvent); 82 SDL_NAS_SYM(AuHandleEvents); 83 SDL_NAS_SYM(AuCreateFlow); 84 SDL_NAS_SYM(AuStartFlow); 85 SDL_NAS_SYM(AuSetElements); 86 SDL_NAS_SYM(AuWriteElement); 87 SDL_NAS_SYM(AuReadElement); 88 SDL_NAS_SYM(AuOpenServer); 89 SDL_NAS_SYM(AuRegisterEventHandler); 90 return 0; 91} 92 93#undef SDL_NAS_SYM 94 95#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC 96 97static void 98UnloadNASLibrary(void) 99{ 100 if (nas_handle != NULL) { 101 SDL_UnloadObject(nas_handle); 102 nas_handle = NULL; 103 } 104} 105 106static int 107LoadNASLibrary(void) 108{ 109 int retval = 0; 110 if (nas_handle == NULL) { 111 nas_handle = SDL_LoadObject(nas_library); 112 if (nas_handle == NULL) { 113 /* Copy error string so we can use it in a new SDL_SetError(). */ 114 const char *origerr = SDL_GetError(); 115 const size_t len = SDL_strlen(origerr) + 1; 116 char *err = (char *) alloca(len); 117 SDL_strlcpy(err, origerr, len); 118 retval = -1; 119 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s", 120 nas_library, err); 121 } else { 122 retval = load_nas_syms(); 123 if (retval < 0) { 124 UnloadNASLibrary(); 125 } 126 } 127 } 128 return retval; 129} 130 131#else 132 133static void 134UnloadNASLibrary(void) 135{ 136} 137 138static int 139LoadNASLibrary(void) 140{ 141 load_nas_syms(); 142 return 0; 143} 144 145#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */ 146 147/* This function waits until it is possible to write a full sound buffer */ 148static void 149NAS_WaitDevice(_THIS) 150{ 151 while (this->hidden->buf_free < this->hidden->mixlen) { 152 AuEvent ev; 153 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); 154 NAS_AuDispatchEvent(this->hidden->aud, &ev); 155 } 156} 157 158static void 159NAS_PlayDevice(_THIS) 160{ 161 while (this->hidden->mixlen > this->hidden->buf_free) { 162 /* 163 * We think the buffer is full? Yikes! Ask the server for events, 164 * in the hope that some of them is LowWater events telling us more 165 * of the buffer is free now than what we think. 166 */ 167 AuEvent ev; 168 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); 169 NAS_AuDispatchEvent(this->hidden->aud, &ev); 170 } 171 this->hidden->buf_free -= this->hidden->mixlen; 172 173 /* Write the audio data */ 174 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0, 175 this->hidden->mixlen, this->hidden->mixbuf, AuFalse, 176 NULL); 177 178 this->hidden->written += this->hidden->mixlen; 179 180#ifdef DEBUG_AUDIO 181 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen); 182#endif 183} 184 185static Uint8 * 186NAS_GetDeviceBuf(_THIS) 187{ 188 return (this->hidden->mixbuf); 189} 190 191static int 192NAS_CaptureFromDevice(_THIS, void *buffer, int buflen) 193{ 194 struct SDL_PrivateAudioData *h = this->hidden; 195 int retval; 196 197 while (SDL_TRUE) { 198 /* just keep the event queue moving and the server chattering. */ 199 NAS_AuHandleEvents(h->aud); 200 201 retval = (int) NAS_AuReadElement(h->aud, h->flow, 1, buflen, buffer, NULL); 202 /*printf("read %d capture bytes\n", (int) retval);*/ 203 if (retval == 0) { 204 SDL_Delay(10); /* don't burn the CPU if we're waiting for data. */ 205 } else { 206 break; 207 } 208 } 209 210 return retval; 211} 212 213static void 214NAS_FlushCapture(_THIS) 215{ 216 struct SDL_PrivateAudioData *h = this->hidden; 217 AuUint32 total = 0; 218 AuUint32 br; 219 Uint8 buf[512]; 220 221 do { 222 /* just keep the event queue moving and the server chattering. */ 223 NAS_AuHandleEvents(h->aud); 224 br = NAS_AuReadElement(h->aud, h->flow, 1, sizeof (buf), buf, NULL); 225 /*printf("flushed %d capture bytes\n", (int) br);*/ 226 total += br; 227 } while ((br == sizeof (buf)) && (total < this->spec.size)); 228} 229 230static void 231NAS_CloseDevice(_THIS) 232{ 233 if (this->hidden->aud) { 234 NAS_AuCloseServer(this->hidden->aud); 235 } 236 SDL_free(this->hidden->mixbuf); 237 SDL_free(this->hidden); 238} 239 240static unsigned char 241sdlformat_to_auformat(unsigned int fmt) 242{ 243 switch (fmt) { 244 case AUDIO_U8: 245 return AuFormatLinearUnsigned8; 246 case AUDIO_S8: 247 return AuFormatLinearSigned8; 248 case AUDIO_U16LSB: 249 return AuFormatLinearUnsigned16LSB; 250 case AUDIO_U16MSB: 251 return AuFormatLinearUnsigned16MSB; 252 case AUDIO_S16LSB: 253 return AuFormatLinearSigned16LSB; 254 case AUDIO_S16MSB: 255 return AuFormatLinearSigned16MSB; 256 } 257 return AuNone; 258} 259 260static AuBool 261event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd) 262{ 263 SDL_AudioDevice *this = (SDL_AudioDevice *) hnd->data; 264 struct SDL_PrivateAudioData *h = this->hidden; 265 if (this->iscapture) { 266 return AuTrue; /* we don't (currently) care about any of this for capture devices */ 267 } 268 269 switch (ev->type) { 270 case AuEventTypeElementNotify: 271 { 272 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev; 273 274 switch (event->kind) { 275 case AuElementNotifyKindLowWater: 276 if (h->buf_free >= 0) { 277 h->really += event->num_bytes; 278 gettimeofday(&h->last_tv, 0); 279 h->buf_free += event->num_bytes; 280 } else { 281 h->buf_free = event->num_bytes; 282 } 283 break; 284 case AuElementNotifyKindState: 285 switch (event->cur_state) { 286 case AuStatePause: 287 if (event->reason != AuReasonUser) { 288 if (h->buf_free >= 0) { 289 h->really += event->num_bytes; 290 gettimeofday(&h->last_tv, 0); 291 h->buf_free += event->num_bytes; 292 } else { 293 h->buf_free = event->num_bytes; 294 } 295 } 296 break; 297 } 298 } 299 } 300 } 301 return AuTrue; 302} 303 304static AuDeviceID 305find_device(_THIS) 306{ 307 /* These "Au" things are all macros, not functions... */ 308 struct SDL_PrivateAudioData *h = this->hidden; 309 const unsigned int devicekind = this->iscapture ? AuComponentKindPhysicalInput : AuComponentKindPhysicalOutput; 310 const int numdevs = AuServerNumDevices(h->aud); 311 const int nch = this->spec.channels; 312 int i; 313 314 /* Try to find exact match on channels first... */ 315 for (i = 0; i < numdevs; i++) { 316 const AuDeviceAttributes *dev = AuServerDevice(h->aud, i); 317 if ((AuDeviceKind(dev) == devicekind) && (AuDeviceNumTracks(dev) == nch)) { 318 return AuDeviceIdentifier(dev); 319 } 320 } 321 322 /* Take anything, then... */ 323 for (i = 0; i < numdevs; i++) { 324 const AuDeviceAttributes *dev = AuServerDevice(h->aud, i); 325 if (AuDeviceKind(dev) == devicekind) { 326 this->spec.channels = AuDeviceNumTracks(dev); 327 return AuDeviceIdentifier(dev); 328 } 329 } 330 return AuNone; 331} 332 333static int 334NAS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) 335{ 336 AuElement elms[3]; 337 int buffer_size; 338 SDL_AudioFormat test_format, format; 339 340 /* Initialize all variables that we clean on shutdown */ 341 this->hidden = (struct SDL_PrivateAudioData *) 342 SDL_malloc((sizeof *this->hidden)); 343 if (this->hidden == NULL) { 344 return SDL_OutOfMemory(); 345 } 346 SDL_zerop(this->hidden); 347 348 /* Try for a closest match on audio format */ 349 format = 0; 350 for (test_format = SDL_FirstAudioFormat(this->spec.format); 351 !format && test_format;) { 352 format = sdlformat_to_auformat(test_format); 353 if (format == AuNone) { 354 test_format = SDL_NextAudioFormat(); 355 } 356 } 357 if (format == 0) { 358 return SDL_SetError("NAS: Couldn't find any hardware audio formats"); 359 } 360 this->spec.format = test_format; 361 362 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); 363 if (this->hidden->aud == 0) { 364 return SDL_SetError("NAS: Couldn't open connection to NAS server"); 365 } 366 367 this->hidden->dev = find_device(this); 368 if ((this->hidden->dev == AuNone) 369 || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) { 370 return SDL_SetError("NAS: Couldn't find a fitting device on NAS server"); 371 } 372 373 buffer_size = this->spec.freq; 374 if (buffer_size < 4096) 375 buffer_size = 4096; 376 377 if (buffer_size > 32768) 378 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */ 379 380 /* Calculate the final parameters for this audio specification */ 381 SDL_CalculateAudioSpec(&this->spec); 382 383 if (iscapture) { 384 AuMakeElementImportDevice(elms, this->spec.freq, this->hidden->dev, 385 AuUnlimitedSamples, 0, NULL); 386 AuMakeElementExportClient(elms + 1, 0, this->spec.freq, format, 387 this->spec.channels, AuTrue, buffer_size, 388 buffer_size, 0, NULL); 389 } else { 390 AuMakeElementImportClient(elms, this->spec.freq, format, 391 this->spec.channels, AuTrue, buffer_size, 392 buffer_size / 4, 0, NULL); 393 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq, 394 AuUnlimitedSamples, 0, NULL); 395 } 396 397 NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 398 2, elms, NULL); 399 400 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, 401 this->hidden->flow, event_handler, 402 (AuPointer) this); 403 404 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL); 405 406 /* Allocate mixing buffer */ 407 if (!iscapture) { 408 this->hidden->mixlen = this->spec.size; 409 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen); 410 if (this->hidden->mixbuf == NULL) { 411 return SDL_OutOfMemory(); 412 } 413 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 414 } 415 416 /* We're ready to rock and roll. :-) */ 417 return 0; 418} 419 420static void 421NAS_Deinitialize(void) 422{ 423 UnloadNASLibrary(); 424} 425 426static int 427NAS_Init(SDL_AudioDriverImpl * impl) 428{ 429 if (LoadNASLibrary() < 0) { 430 return 0; 431 } else { 432 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); 433 if (aud == NULL) { 434 SDL_SetError("NAS: AuOpenServer() failed (no audio server?)"); 435 return 0; 436 } 437 NAS_AuCloseServer(aud); 438 } 439 440 /* Set the function pointers */ 441 impl->OpenDevice = NAS_OpenDevice; 442 impl->PlayDevice = NAS_PlayDevice; 443 impl->WaitDevice = NAS_WaitDevice; 444 impl->GetDeviceBuf = NAS_GetDeviceBuf; 445 impl->CaptureFromDevice = NAS_CaptureFromDevice; 446 impl->FlushCapture = NAS_FlushCapture; 447 impl->CloseDevice = NAS_CloseDevice; 448 impl->Deinitialize = NAS_Deinitialize; 449 450 impl->OnlyHasDefaultOutputDevice = 1; 451 impl->OnlyHasDefaultCaptureDevice = 1; 452 impl->HasCaptureSupport = SDL_TRUE; 453 454 return 1; /* this audio target is available. */ 455} 456 457AudioBootStrap NAS_bootstrap = { 458 "nas", "Network Audio System", NAS_Init, 0 459}; 460 461#endif /* SDL_AUDIO_DRIVER_NAS */ 462 463/* vi: set ts=4 sw=4 expandtab: */ 464
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.