2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_AUDIO_DRIVER_NAS
25 /* Allow access to a raw mixing buffer */
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"
36 static void (*NAS_AuCloseServer) (AuServer *);
37 static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
38 static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
39 static void (*NAS_AuHandleEvents) (AuServer *);
40 static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
41 static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
42 static void (*NAS_AuSetElements)
43 (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
44 static void (*NAS_AuWriteElement)
45 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
46 static AuUint32 (*NAS_AuReadElement)
47 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuStatus *);
48 static AuServer *(*NAS_AuOpenServer)
49 (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
50 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
51 (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
54 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
56 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
57 static void *nas_handle = NULL;
60 load_nas_sym(const char *fn, void **addr)
62 *addr = SDL_LoadFunction(nas_handle, fn);
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
73 #define SDL_NAS_SYM(x) NAS_##x = x
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);
95 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
98 UnloadNASLibrary(void)
100 if (nas_handle != NULL) {
101 SDL_UnloadObject(nas_handle);
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);
119 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s",
122 retval = load_nas_syms();
134 UnloadNASLibrary(void)
145 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
147 /* This function waits until it is possible to write a full sound buffer */
149 NAS_WaitDevice(_THIS)
151 while (this->hidden->buf_free < this->hidden->mixlen) {
153 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
154 NAS_AuDispatchEvent(this->hidden->aud, &ev);
159 NAS_PlayDevice(_THIS)
161 while (this->hidden->mixlen > this->hidden->buf_free) {
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.
168 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
169 NAS_AuDispatchEvent(this->hidden->aud, &ev);
171 this->hidden->buf_free -= this->hidden->mixlen;
173 /* Write the audio data */
174 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
175 this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
178 this->hidden->written += this->hidden->mixlen;
181 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
186 NAS_GetDeviceBuf(_THIS)
188 return (this->hidden->mixbuf);
192 NAS_CaptureFromDevice(_THIS, void *buffer, int buflen)
194 struct SDL_PrivateAudioData *h = this->hidden;
198 /* just keep the event queue moving and the server chattering. */
199 NAS_AuHandleEvents(h->aud);
201 retval = (int) NAS_AuReadElement(h->aud, h->flow, 1, buflen, buffer, NULL);
202 /*printf("read %d capture bytes\n", (int) retval);*/
204 SDL_Delay(10); /* don't burn the CPU if we're waiting for data. */
214 NAS_FlushCapture(_THIS)
216 struct SDL_PrivateAudioData *h = this->hidden;
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);*/
227 } while ((br == sizeof (buf)) && (total < this->spec.size));
231 NAS_CloseDevice(_THIS)
233 if (this->hidden->aud) {
234 NAS_AuCloseServer(this->hidden->aud);
236 SDL_free(this->hidden->mixbuf);
237 SDL_free(this->hidden);
241 sdlformat_to_auformat(unsigned int fmt)
245 return AuFormatLinearUnsigned8;
247 return AuFormatLinearSigned8;
249 return AuFormatLinearUnsigned16LSB;
251 return AuFormatLinearUnsigned16MSB;
253 return AuFormatLinearSigned16LSB;
255 return AuFormatLinearSigned16MSB;
261 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
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 */
270 case AuEventTypeElementNotify:
272 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
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;
281 h->buf_free = event->num_bytes;
284 case AuElementNotifyKindState:
285 switch (event->cur_state) {
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;
293 h->buf_free = event->num_bytes;
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;
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);
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);
334 NAS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
338 SDL_AudioFormat test_format, format;
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();
346 SDL_zerop(this->hidden);
348 /* Try for a closest match on audio format */
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();
358 return SDL_SetError("NAS: Couldn't find any hardware audio formats");
360 this->spec.format = test_format;
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");
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");
373 buffer_size = this->spec.freq;
374 if (buffer_size < 4096)
377 if (buffer_size > 32768)
378 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
380 /* Calculate the final parameters for this audio specification */
381 SDL_CalculateAudioSpec(&this->spec);
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);
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);
397 NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue,
400 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
401 this->hidden->flow, event_handler,
404 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
406 /* Allocate mixing buffer */
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();
413 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
416 /* We're ready to rock and roll. :-) */
421 NAS_Deinitialize(void)
427 NAS_Init(SDL_AudioDriverImpl * impl)
429 if (LoadNASLibrary() < 0) {
432 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
434 SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
437 NAS_AuCloseServer(aud);
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;
450 impl->OnlyHasDefaultOutputDevice = 1;
451 impl->OnlyHasDefaultCaptureDevice = 1;
452 impl->HasCaptureSupport = SDL_TRUE;
454 return 1; /* this audio target is available. */
457 AudioBootStrap NAS_bootstrap = {
458 "nas", "Network Audio System", NAS_Init, 0
461 #endif /* SDL_AUDIO_DRIVER_NAS */
463 /* vi: set ts=4 sw=4 expandtab: */