Dynamic loading for NAS audio driver.
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2006 Sam Lantinga
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.
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.
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
22 This driver was written by:
26 #include "SDL_config.h"
28 /* Allow access to a raw mixing buffer */
33 #include "SDL_timer.h"
34 #include "SDL_audio.h"
35 #include "SDL_loadso.h"
36 #include "../SDL_audiomem.h"
37 #include "../SDL_audio_c.h"
38 #include "SDL_nasaudio.h"
40 /* The tag name used by nas audio */
41 #define NAS_DRIVER_NAME "nas"
43 static struct SDL_PrivateAudioData *this2 = NULL;
46 static void (*NAS_AuCloseServer)(AuServer *);
47 static void (*NAS_AuNextEvent)(AuServer *, AuBool, AuEvent *);
48 static AuBool (*NAS_AuDispatchEvent)(AuServer *, AuEvent *);
49 static AuFlowID (*NAS_AuCreateFlow)(AuServer *, AuStatus *);
50 static void (*NAS_AuStartFlow)(AuServer *, AuFlowID, AuStatus *);
51 static void (*NAS_AuSetElements)
52 (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
53 static void (*NAS_AuWriteElement)
54 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
55 static AuServer *(*NAS_AuOpenServer)
56 (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
57 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
58 (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
61 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
63 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
64 static void *nas_handle = NULL;
67 load_nas_sym(const char *fn, void **addr)
69 *addr = SDL_LoadFunction(nas_handle, fn);
76 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
77 #define SDL_NAS_SYM(x) \
78 if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
80 #define SDL_NAS_SYM(x) NAS_##x = x
83 static int load_nas_syms(void)
85 SDL_NAS_SYM(AuCloseServer);
86 SDL_NAS_SYM(AuNextEvent);
87 SDL_NAS_SYM(AuDispatchEvent);
88 SDL_NAS_SYM(AuCreateFlow);
89 SDL_NAS_SYM(AuStartFlow);
90 SDL_NAS_SYM(AuSetElements);
91 SDL_NAS_SYM(AuWriteElement);
92 SDL_NAS_SYM(AuOpenServer);
93 SDL_NAS_SYM(AuRegisterEventHandler);
98 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
100 static int library_load_count = 0;
103 UnloadNASLibrary(void)
105 if ((nas_handle != NULL) && (--library_load_count == 0)) {
106 SDL_UnloadObject(nas_handle);
115 if (library_load_count++ == 0) {
116 nas_handle = SDL_LoadObject(nas_library);
117 if (nas_handle == NULL) {
118 /* Copy error string so we can use it in a new SDL_SetError(). */
119 char *origerr = SDL_GetError();
120 size_t len = SDL_strlen(origerr) + 1;
121 char *err = (char *) alloca(len);
122 SDL_strlcpy(err, origerr, len);
124 library_load_count--;
126 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
129 retval = load_nas_syms();
141 UnloadNASLibrary(void)
152 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
158 if (LoadNASLibrary() >= 0) {
159 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
162 NAS_AuCloseServer(aud);
169 /* This function waits until it is possible to write a full sound buffer */
171 NAS_WaitDevice(_THIS)
173 while (this->hidden->buf_free < this->hidden->mixlen) {
175 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
176 NAS_AuDispatchEvent(this->hidden->aud, &ev);
181 NAS_PlayDevice(_THIS)
183 while (this->hidden->mixlen > this->hidden->buf_free) {
185 * We think the buffer is full? Yikes! Ask the server for events,
186 * in the hope that some of them is LowWater events telling us more
187 * of the buffer is free now than what we think.
190 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
191 NAS_AuDispatchEvent(this->hidden->aud, &ev);
193 this->hidden->buf_free -= this->hidden->mixlen;
195 /* Write the audio data */
196 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
197 this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
199 this->hidden->written += this->hidden->mixlen;
202 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
207 NAS_GetDeviceBuf(_THIS)
209 return (this->hidden->mixbuf);
213 NAS_CloseDevice(_THIS)
215 if (this->hidden != NULL) {
216 if (this->hidden->mixbuf != NULL) {
217 SDL_FreeAudioMem(this->hidden->mixbuf);
218 this->hidden->mixbuf = NULL;
220 if (this->hidden->aud) {
221 NAS_AuCloseServer(this->hidden->aud);
222 this->hidden->aud = 0;
224 SDL_free(this->hidden);
225 this2 = this->hidden = NULL;
231 sdlformat_to_auformat(unsigned int fmt)
235 return AuFormatLinearUnsigned8;
237 return AuFormatLinearSigned8;
239 return AuFormatLinearUnsigned16LSB;
241 return AuFormatLinearUnsigned16MSB;
243 return AuFormatLinearSigned16LSB;
245 return AuFormatLinearSigned16MSB;
251 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
254 case AuEventTypeElementNotify:
256 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
258 switch (event->kind) {
259 case AuElementNotifyKindLowWater:
260 if (this2->buf_free >= 0) {
261 this2->really += event->num_bytes;
262 gettimeofday(&this2->last_tv, 0);
263 this2->buf_free += event->num_bytes;
265 this2->buf_free = event->num_bytes;
268 case AuElementNotifyKindState:
269 switch (event->cur_state) {
271 if (event->reason != AuReasonUser) {
272 if (this2->buf_free >= 0) {
273 this2->really += event->num_bytes;
274 gettimeofday(&this2->last_tv, 0);
275 this2->buf_free += event->num_bytes;
277 this2->buf_free = event->num_bytes;
289 find_device(_THIS, int nch)
291 /* These "Au" things are all macros, not functions... */
293 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
294 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
295 AuComponentKindPhysicalOutput) &&
296 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
297 return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
304 NAS_OpenDevice(_THIS, const char *devname, int iscapture)
308 SDL_AudioFormat test_format, format;
310 /* Initialize all variables that we clean on shutdown */
311 this->hidden = (struct SDL_PrivateAudioData *)
312 SDL_malloc((sizeof *this->hidden));
313 if (this->hidden == NULL) {
317 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
319 if (LoadNASLibrary() < 0) {
320 NAS_CloseDevice(this);
324 /* Try for a closest match on audio format */
326 for (test_format = SDL_FirstAudioFormat(this->spec.format);
327 !format && test_format;) {
328 format = sdlformat_to_auformat(test_format);
329 if (format == AuNone) {
330 test_format = SDL_NextAudioFormat();
334 NAS_CloseDevice(this);
335 SDL_SetError("NAS: Couldn't find any hardware audio formats");
338 this->spec.format = test_format;
340 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
341 if (this->hidden->aud == 0) {
342 NAS_CloseDevice(this);
343 SDL_SetError("NAS: Couldn't open connection to NAS server");
347 this->hidden->dev = find_device(this, this->spec.channels);
348 if ((this->hidden->dev == AuNone)
349 || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
350 NAS_CloseDevice(this);
351 SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
355 buffer_size = this->spec.freq;
356 if (buffer_size < 4096)
359 if (buffer_size > 32768)
360 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
362 /* Calculate the final parameters for this audio specification */
363 SDL_CalculateAudioSpec(&this->spec);
365 this2 = this->hidden;
367 AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels,
368 AuTrue, buffer_size, buffer_size / 4, 0, NULL);
369 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
370 AuUnlimitedSamples, 0, NULL);
371 NAS_AuSetElements(this->hidden->aud, this->hidden->flow,
372 AuTrue, 2, elms, NULL);
373 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
374 this->hidden->flow, event_handler,
377 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
379 /* Allocate mixing buffer */
380 this->hidden->mixlen = this->spec.size;
381 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
382 if (this->hidden->mixbuf == NULL) {
383 NAS_CloseDevice(this);
387 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
389 /* We're ready to rock and roll. :-) */
394 NAS_Init(SDL_AudioDriverImpl *impl)
396 /* Set the function pointers */
397 impl->OpenDevice = NAS_OpenDevice;
398 impl->PlayDevice = NAS_PlayDevice;
399 impl->WaitDevice = NAS_WaitDevice;
400 impl->GetDeviceBuf = NAS_GetDeviceBuf;
401 impl->CloseDevice = NAS_CloseDevice;
402 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */
407 AudioBootStrap NAS_bootstrap = {
408 NAS_DRIVER_NAME, "Network Audio System",
409 NAS_Available, NAS_Init, 0
412 /* vi: set ts=4 sw=4 expandtab: */