SDL 1.3 is now under the zlib license.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2011 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_config.h"
23 /* Allow access to a raw mixing buffer */
28 #include "SDL_timer.h"
29 #include "SDL_audio.h"
30 #include "SDL_loadso.h"
31 #include "../SDL_audiomem.h"
32 #include "../SDL_audio_c.h"
33 #include "SDL_nasaudio.h"
35 /* The tag name used by nas audio */
36 #define NAS_DRIVER_NAME "nas"
38 static struct SDL_PrivateAudioData *this2 = NULL;
41 static void (*NAS_AuCloseServer) (AuServer *);
42 static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
43 static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
44 static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
45 static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
46 static void (*NAS_AuSetElements)
47 (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
48 static void (*NAS_AuWriteElement)
49 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
50 static AuServer *(*NAS_AuOpenServer)
51 (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
52 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
53 (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
56 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
58 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
59 static void *nas_handle = NULL;
62 load_nas_sym(const char *fn, void **addr)
64 *addr = SDL_LoadFunction(nas_handle, fn);
71 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
72 #define SDL_NAS_SYM(x) \
73 if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
75 #define SDL_NAS_SYM(x) NAS_##x = x
81 SDL_NAS_SYM(AuCloseServer);
82 SDL_NAS_SYM(AuNextEvent);
83 SDL_NAS_SYM(AuDispatchEvent);
84 SDL_NAS_SYM(AuCreateFlow);
85 SDL_NAS_SYM(AuStartFlow);
86 SDL_NAS_SYM(AuSetElements);
87 SDL_NAS_SYM(AuWriteElement);
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 char *origerr = SDL_GetError();
115 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\n",
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_CloseDevice(_THIS)
194 if (this->hidden != NULL) {
195 if (this->hidden->mixbuf != NULL) {
196 SDL_FreeAudioMem(this->hidden->mixbuf);
197 this->hidden->mixbuf = NULL;
199 if (this->hidden->aud) {
200 NAS_AuCloseServer(this->hidden->aud);
201 this->hidden->aud = 0;
203 SDL_free(this->hidden);
204 this2 = this->hidden = NULL;
209 sdlformat_to_auformat(unsigned int fmt)
213 return AuFormatLinearUnsigned8;
215 return AuFormatLinearSigned8;
217 return AuFormatLinearUnsigned16LSB;
219 return AuFormatLinearUnsigned16MSB;
221 return AuFormatLinearSigned16LSB;
223 return AuFormatLinearSigned16MSB;
229 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
232 case AuEventTypeElementNotify:
234 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
236 switch (event->kind) {
237 case AuElementNotifyKindLowWater:
238 if (this2->buf_free >= 0) {
239 this2->really += event->num_bytes;
240 gettimeofday(&this2->last_tv, 0);
241 this2->buf_free += event->num_bytes;
243 this2->buf_free = event->num_bytes;
246 case AuElementNotifyKindState:
247 switch (event->cur_state) {
249 if (event->reason != AuReasonUser) {
250 if (this2->buf_free >= 0) {
251 this2->really += event->num_bytes;
252 gettimeofday(&this2->last_tv, 0);
253 this2->buf_free += event->num_bytes;
255 this2->buf_free = event->num_bytes;
267 find_device(_THIS, int nch)
269 /* These "Au" things are all macros, not functions... */
271 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
272 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
273 AuComponentKindPhysicalOutput) &&
274 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
275 return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
282 NAS_OpenDevice(_THIS, const char *devname, int iscapture)
286 SDL_AudioFormat test_format, format;
288 /* Initialize all variables that we clean on shutdown */
289 this->hidden = (struct SDL_PrivateAudioData *)
290 SDL_malloc((sizeof *this->hidden));
291 if (this->hidden == NULL) {
295 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
297 /* Try for a closest match on audio format */
299 for (test_format = SDL_FirstAudioFormat(this->spec.format);
300 !format && test_format;) {
301 format = sdlformat_to_auformat(test_format);
302 if (format == AuNone) {
303 test_format = SDL_NextAudioFormat();
307 NAS_CloseDevice(this);
308 SDL_SetError("NAS: Couldn't find any hardware audio formats");
311 this->spec.format = test_format;
313 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
314 if (this->hidden->aud == 0) {
315 NAS_CloseDevice(this);
316 SDL_SetError("NAS: Couldn't open connection to NAS server");
320 this->hidden->dev = find_device(this, this->spec.channels);
321 if ((this->hidden->dev == AuNone)
322 || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
323 NAS_CloseDevice(this);
324 SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
328 buffer_size = this->spec.freq;
329 if (buffer_size < 4096)
332 if (buffer_size > 32768)
333 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
335 /* Calculate the final parameters for this audio specification */
336 SDL_CalculateAudioSpec(&this->spec);
338 this2 = this->hidden;
340 AuMakeElementImportClient(elms, this->spec.freq, format,
341 this->spec.channels, AuTrue, buffer_size,
342 buffer_size / 4, 0, NULL);
343 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
344 AuUnlimitedSamples, 0, NULL);
345 NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
347 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
348 this->hidden->flow, event_handler,
351 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
353 /* Allocate mixing buffer */
354 this->hidden->mixlen = this->spec.size;
355 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
356 if (this->hidden->mixbuf == NULL) {
357 NAS_CloseDevice(this);
361 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
363 /* We're ready to rock and roll. :-) */
368 NAS_Deinitialize(void)
374 NAS_Init(SDL_AudioDriverImpl * impl)
376 if (LoadNASLibrary() < 0) {
379 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
381 SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
384 NAS_AuCloseServer(aud);
387 /* Set the function pointers */
388 impl->OpenDevice = NAS_OpenDevice;
389 impl->PlayDevice = NAS_PlayDevice;
390 impl->WaitDevice = NAS_WaitDevice;
391 impl->GetDeviceBuf = NAS_GetDeviceBuf;
392 impl->CloseDevice = NAS_CloseDevice;
393 impl->Deinitialize = NAS_Deinitialize;
394 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */
396 return 1; /* this audio target is available. */
399 AudioBootStrap NAS_bootstrap = {
400 NAS_DRIVER_NAME, "Network Audio System", NAS_Init, 0
403 /* vi: set ts=4 sw=4 expandtab: */