2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 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
86 SDL_NAS_SYM(AuCloseServer);
87 SDL_NAS_SYM(AuNextEvent);
88 SDL_NAS_SYM(AuDispatchEvent);
89 SDL_NAS_SYM(AuCreateFlow);
90 SDL_NAS_SYM(AuStartFlow);
91 SDL_NAS_SYM(AuSetElements);
92 SDL_NAS_SYM(AuWriteElement);
93 SDL_NAS_SYM(AuOpenServer);
94 SDL_NAS_SYM(AuRegisterEventHandler);
100 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
103 UnloadNASLibrary(void)
105 if (nas_handle != NULL) {
106 SDL_UnloadObject(nas_handle);
115 if (nas_handle == NULL) {
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 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
127 retval = load_nas_syms();
139 UnloadNASLibrary(void)
150 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
152 /* This function waits until it is possible to write a full sound buffer */
154 NAS_WaitDevice(_THIS)
156 while (this->hidden->buf_free < this->hidden->mixlen) {
158 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
159 NAS_AuDispatchEvent(this->hidden->aud, &ev);
164 NAS_PlayDevice(_THIS)
166 while (this->hidden->mixlen > this->hidden->buf_free) {
168 * We think the buffer is full? Yikes! Ask the server for events,
169 * in the hope that some of them is LowWater events telling us more
170 * of the buffer is free now than what we think.
173 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
174 NAS_AuDispatchEvent(this->hidden->aud, &ev);
176 this->hidden->buf_free -= this->hidden->mixlen;
178 /* Write the audio data */
179 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
180 this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
183 this->hidden->written += this->hidden->mixlen;
186 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
191 NAS_GetDeviceBuf(_THIS)
193 return (this->hidden->mixbuf);
197 NAS_CloseDevice(_THIS)
199 if (this->hidden != NULL) {
200 if (this->hidden->mixbuf != NULL) {
201 SDL_FreeAudioMem(this->hidden->mixbuf);
202 this->hidden->mixbuf = NULL;
204 if (this->hidden->aud) {
205 NAS_AuCloseServer(this->hidden->aud);
206 this->hidden->aud = 0;
208 SDL_free(this->hidden);
209 this2 = this->hidden = NULL;
214 sdlformat_to_auformat(unsigned int fmt)
218 return AuFormatLinearUnsigned8;
220 return AuFormatLinearSigned8;
222 return AuFormatLinearUnsigned16LSB;
224 return AuFormatLinearUnsigned16MSB;
226 return AuFormatLinearSigned16LSB;
228 return AuFormatLinearSigned16MSB;
234 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
237 case AuEventTypeElementNotify:
239 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
241 switch (event->kind) {
242 case AuElementNotifyKindLowWater:
243 if (this2->buf_free >= 0) {
244 this2->really += event->num_bytes;
245 gettimeofday(&this2->last_tv, 0);
246 this2->buf_free += event->num_bytes;
248 this2->buf_free = event->num_bytes;
251 case AuElementNotifyKindState:
252 switch (event->cur_state) {
254 if (event->reason != AuReasonUser) {
255 if (this2->buf_free >= 0) {
256 this2->really += event->num_bytes;
257 gettimeofday(&this2->last_tv, 0);
258 this2->buf_free += event->num_bytes;
260 this2->buf_free = event->num_bytes;
272 find_device(_THIS, int nch)
274 /* These "Au" things are all macros, not functions... */
276 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
277 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
278 AuComponentKindPhysicalOutput) &&
279 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
280 return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
287 NAS_OpenDevice(_THIS, const char *devname, int iscapture)
291 SDL_AudioFormat test_format, format;
293 /* Initialize all variables that we clean on shutdown */
294 this->hidden = (struct SDL_PrivateAudioData *)
295 SDL_malloc((sizeof *this->hidden));
296 if (this->hidden == NULL) {
300 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
302 /* Try for a closest match on audio format */
304 for (test_format = SDL_FirstAudioFormat(this->spec.format);
305 !format && test_format;) {
306 format = sdlformat_to_auformat(test_format);
307 if (format == AuNone) {
308 test_format = SDL_NextAudioFormat();
312 NAS_CloseDevice(this);
313 SDL_SetError("NAS: Couldn't find any hardware audio formats");
316 this->spec.format = test_format;
318 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
319 if (this->hidden->aud == 0) {
320 NAS_CloseDevice(this);
321 SDL_SetError("NAS: Couldn't open connection to NAS server");
325 this->hidden->dev = find_device(this, this->spec.channels);
326 if ((this->hidden->dev == AuNone)
327 || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
328 NAS_CloseDevice(this);
329 SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
333 buffer_size = this->spec.freq;
334 if (buffer_size < 4096)
337 if (buffer_size > 32768)
338 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
340 /* Calculate the final parameters for this audio specification */
341 SDL_CalculateAudioSpec(&this->spec);
343 this2 = this->hidden;
345 AuMakeElementImportClient(elms, this->spec.freq, format,
346 this->spec.channels, AuTrue, buffer_size,
347 buffer_size / 4, 0, NULL);
348 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
349 AuUnlimitedSamples, 0, NULL);
350 NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
352 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
353 this->hidden->flow, event_handler,
356 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
358 /* Allocate mixing buffer */
359 this->hidden->mixlen = this->spec.size;
360 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
361 if (this->hidden->mixbuf == NULL) {
362 NAS_CloseDevice(this);
366 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
368 /* We're ready to rock and roll. :-) */
373 NAS_Deinitialize(void)
379 NAS_Init(SDL_AudioDriverImpl * impl)
381 if (LoadNASLibrary() < 0) {
384 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
386 SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
389 NAS_AuCloseServer(aud);
392 /* Set the function pointers */
393 impl->OpenDevice = NAS_OpenDevice;
394 impl->PlayDevice = NAS_PlayDevice;
395 impl->WaitDevice = NAS_WaitDevice;
396 impl->GetDeviceBuf = NAS_GetDeviceBuf;
397 impl->CloseDevice = NAS_CloseDevice;
398 impl->Deinitialize = NAS_Deinitialize;
399 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */
401 return 2; /* 2 == definitely has an audio device. */
404 AudioBootStrap NAS_bootstrap = {
405 NAS_DRIVER_NAME, "Network Audio System", NAS_Init, 0
408 /* vi: set ts=4 sw=4 expandtab: */