Adjusted default choice of audio driver.
If a driver can definitely see available devices, it is chosen. Otherwise,
we'll take the first driver that initializes but saw no devices...this might
be because it can't enumerate them, or there really aren't any available.
This prevents the dsp driver from hogging control when there are no /dev/dsp*
nodes (for example, on a Linux box with ALSA and no OSS emulation).
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: */