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_audiomem.h"
36 #include "../SDL_audio_c.h"
37 #include "../SDL_audiodev_c.h"
38 #include "SDL_nasaudio.h"
40 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
41 #include "SDL_loadso.h"
44 /* The tag name used by artsc audio */
45 #define NAS_DRIVER_NAME "nas"
47 static struct SDL_PrivateAudioData *this2 = NULL;
49 static void (*NAS_AuCloseServer) (AuServer *);
50 static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
51 static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
52 static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
53 static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
54 static void (*NAS_AuSetElements)
55 (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
56 static void (*NAS_AuWriteElement)
57 (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
58 static AuServer *(*NAS_AuOpenServer)
59 (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
60 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
61 (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
64 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
66 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
67 static void *nas_handle = NULL;
70 load_nas_sym(const char *fn, void **addr)
72 *addr = SDL_LoadFunction(nas_handle, fn);
79 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
80 #define SDL_NAS_SYM(x) \
81 if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
83 #define SDL_NAS_SYM(x) NAS_##x = x
89 SDL_NAS_SYM(AuCloseServer);
90 SDL_NAS_SYM(AuNextEvent);
91 SDL_NAS_SYM(AuDispatchEvent);
92 SDL_NAS_SYM(AuCreateFlow);
93 SDL_NAS_SYM(AuStartFlow);
94 SDL_NAS_SYM(AuSetElements);
95 SDL_NAS_SYM(AuWriteElement);
96 SDL_NAS_SYM(AuOpenServer);
97 SDL_NAS_SYM(AuRegisterEventHandler);
103 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
106 UnloadNASLibrary(void)
108 if (nas_handle != NULL) {
109 SDL_UnloadObject(nas_handle);
118 if (nas_handle == NULL) {
119 nas_handle = SDL_LoadObject(nas_library);
120 if (nas_handle == NULL) {
121 /* Copy error string so we can use it in a new SDL_SetError(). */
122 char *origerr = SDL_GetError();
123 size_t len = SDL_strlen(origerr) + 1;
124 char *err = (char *) alloca(len);
125 SDL_strlcpy(err, origerr, len);
127 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
130 retval = load_nas_syms();
142 UnloadNASLibrary(void)
153 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
156 /* Audio driver functions */
157 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec);
158 static void NAS_WaitAudio(_THIS);
159 static void NAS_PlayAudio(_THIS);
160 static Uint8 *NAS_GetAudioBuf(_THIS);
161 static void NAS_CloseAudio(_THIS);
163 /* Audio driver bootstrap functions */
165 static int Audio_Available(void)
167 if (LoadNASLibrary() == 0) {
168 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
173 NAS_AuCloseServer(aud);
180 static void Audio_DeleteDevice(SDL_AudioDevice *device)
183 SDL_free(device->hidden);
187 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
189 SDL_AudioDevice *this;
191 if (LoadNASLibrary() < 0) {
195 /* Initialize all variables that we clean on shutdown */
196 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
198 SDL_memset(this, 0, (sizeof *this));
199 this->hidden = (struct SDL_PrivateAudioData *)
200 SDL_malloc((sizeof *this->hidden));
202 if ( (this == NULL) || (this->hidden == NULL) ) {
209 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
211 /* Set the function pointers */
212 this->OpenAudio = NAS_OpenAudio;
213 this->WaitAudio = NAS_WaitAudio;
214 this->PlayAudio = NAS_PlayAudio;
215 this->GetAudioBuf = NAS_GetAudioBuf;
216 this->CloseAudio = NAS_CloseAudio;
218 this->free = Audio_DeleteDevice;
223 AudioBootStrap NAS_bootstrap = {
224 NAS_DRIVER_NAME, "Network Audio System",
225 Audio_Available, Audio_CreateDevice
228 /* This function waits until it is possible to write a full sound buffer */
229 static void NAS_WaitAudio(_THIS)
231 while ( this->hidden->buf_free < this->hidden->mixlen ) {
233 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
234 NAS_AuDispatchEvent(this->hidden->aud, &ev);
238 static void NAS_PlayAudio(_THIS)
240 while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events,
241 in the hope that some of them is LowWater events telling us more
242 of the buffer is free now than what we think. */
244 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
245 NAS_AuDispatchEvent(this->hidden->aud, &ev);
247 this->hidden->buf_free -= this->hidden->mixlen;
249 /* Write the audio data */
250 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
252 this->hidden->written += this->hidden->mixlen;
255 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
259 static Uint8 *NAS_GetAudioBuf(_THIS)
261 return(this->hidden->mixbuf);
264 static void NAS_CloseAudio(_THIS)
266 if ( this->hidden->mixbuf != NULL ) {
267 SDL_FreeAudioMem(this->hidden->mixbuf);
268 this->hidden->mixbuf = NULL;
270 if ( this->hidden->aud ) {
271 NAS_AuCloseServer(this->hidden->aud);
272 this->hidden->aud = 0;
276 static unsigned char sdlformat_to_auformat(unsigned int fmt)
281 return AuFormatLinearUnsigned8;
283 return AuFormatLinearSigned8;
285 return AuFormatLinearUnsigned16LSB;
287 return AuFormatLinearUnsigned16MSB;
289 return AuFormatLinearSigned16LSB;
291 return AuFormatLinearSigned16MSB;
297 event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
300 case AuEventTypeElementNotify: {
301 AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
303 switch (event->kind) {
304 case AuElementNotifyKindLowWater:
305 if (this2->buf_free >= 0) {
306 this2->really += event->num_bytes;
307 gettimeofday(&this2->last_tv, 0);
308 this2->buf_free += event->num_bytes;
310 this2->buf_free = event->num_bytes;
313 case AuElementNotifyKindState:
314 switch (event->cur_state) {
316 if (event->reason != AuReasonUser) {
317 if (this2->buf_free >= 0) {
318 this2->really += event->num_bytes;
319 gettimeofday(&this2->last_tv, 0);
320 this2->buf_free += event->num_bytes;
322 this2->buf_free = event->num_bytes;
334 find_device(_THIS, int nch)
336 /* These "Au" things are all macros, not functions... */
338 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
339 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
340 AuComponentKindPhysicalOutput) &&
341 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
342 return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
348 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
352 Uint16 test_format, format;
354 this->hidden->mixbuf = NULL;
356 /* Try for a closest match on audio format */
358 for ( test_format = SDL_FirstAudioFormat(spec->format);
359 ! format && test_format; ) {
360 format = sdlformat_to_auformat(test_format);
362 if (format == AuNone) {
363 test_format = SDL_NextAudioFormat();
367 SDL_SetError("Couldn't find any hardware audio formats");
370 spec->format = test_format;
372 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
373 if (this->hidden->aud == 0)
375 SDL_SetError("Couldn't open connection to NAS server");
379 this->hidden->dev = find_device(this, spec->channels);
380 if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, NULL)))) {
381 NAS_AuCloseServer(this->hidden->aud);
382 this->hidden->aud = 0;
383 SDL_SetError("Couldn't find a fitting playback device on NAS server");
387 buffer_size = spec->freq;
388 if (buffer_size < 4096)
391 if (buffer_size > 32768)
392 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
394 /* Calculate the final parameters for this audio specification */
395 SDL_CalculateAudioSpec(spec);
397 this2 = this->hidden;
399 /* These "Au" things without a NAS_ prefix are macros, not functions... */
400 AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
401 buffer_size, buffer_size / 4, 0, NULL);
402 AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
403 AuUnlimitedSamples, 0, NULL);
404 NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
405 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
406 event_handler, (AuPointer) NULL);
408 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
410 /* Allocate mixing buffer */
411 this->hidden->mixlen = spec->size;
412 this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
413 if ( this->hidden->mixbuf == NULL ) {
416 SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
418 /* Get the parent process id (we're the parent of the audio thread) */
419 this->hidden->parent = getpid();
421 /* We're ready to rock and roll. :-) */