comparison src/audio/nas/SDL_nasaudio.c @ 2049:5f6550e5184f

Merged SDL-ryan-multiple-audio-device branch r2803:2871 into the trunk.
author Ryan C. Gordon <icculus@icculus.org>
date Tue, 17 Oct 2006 09:15:21 +0000
parents 3b4ce57c6215
children 866052b01ee5
comparison
equal deleted inserted replaced
2048:6067c7f9a672 2049:5f6550e5184f
30 #include <signal.h> 30 #include <signal.h>
31 #include <unistd.h> 31 #include <unistd.h>
32 32
33 #include "SDL_timer.h" 33 #include "SDL_timer.h"
34 #include "SDL_audio.h" 34 #include "SDL_audio.h"
35 #include "SDL_loadso.h"
35 #include "../SDL_audiomem.h" 36 #include "../SDL_audiomem.h"
36 #include "../SDL_audio_c.h" 37 #include "../SDL_audio_c.h"
37 #include "../SDL_audiodev_c.h"
38 #include "SDL_nasaudio.h" 38 #include "SDL_nasaudio.h"
39 39
40 /* The tag name used by artsc audio */ 40 /* The tag name used by nas audio */
41 #define NAS_DRIVER_NAME "nas" 41 #define NAS_DRIVER_NAME "nas"
42 42
43 static struct SDL_PrivateAudioData *this2 = NULL; 43 static struct SDL_PrivateAudioData *this2 = NULL;
44 44
45 /* Audio driver functions */ 45
46 static int NAS_OpenAudio(_THIS, SDL_AudioSpec * spec); 46 static void (*NAS_AuCloseServer)(AuServer *);
47 static void NAS_WaitAudio(_THIS); 47 static void (*NAS_AuNextEvent)(AuServer *, AuBool, AuEvent *);
48 static void NAS_PlayAudio(_THIS); 48 static AuBool (*NAS_AuDispatchEvent)(AuServer *, AuEvent *);
49 static Uint8 *NAS_GetAudioBuf(_THIS); 49 static AuFlowID (*NAS_AuCreateFlow)(AuServer *, AuStatus *);
50 static void NAS_CloseAudio(_THIS); 50 static void (*NAS_AuStartFlow)(AuServer *, AuFlowID, AuStatus *);
51 51 static void (*NAS_AuSetElements)
52 /* Audio driver bootstrap functions */ 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);
59
60
61 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
62
63 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
64 static void *nas_handle = NULL;
53 65
54 static int 66 static int
55 Audio_Available(void) 67 load_nas_sym(const char *fn, void **addr)
56 { 68 {
57 AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL); 69 *addr = SDL_LoadFunction(nas_handle, fn);
58 if (!aud) 70 if (*addr == NULL) {
59 return 0; 71 return 0;
60 72 }
61 AuCloseServer(aud);
62 return 1; 73 return 1;
63 } 74 }
64 75
65 static void 76 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
66 Audio_DeleteDevice(SDL_AudioDevice * device) 77 #define SDL_NAS_SYM(x) \
67 { 78 if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
68 SDL_free(device->hidden); 79 #else
69 SDL_free(device); 80 #define SDL_NAS_SYM(x) NAS_##x = x
70 } 81 #endif
71 82
72 static SDL_AudioDevice * 83 static int load_nas_syms(void)
73 Audio_CreateDevice(int devindex) 84 {
74 { 85 SDL_NAS_SYM(AuCloseServer);
75 SDL_AudioDevice *this; 86 SDL_NAS_SYM(AuNextEvent);
76 87 SDL_NAS_SYM(AuDispatchEvent);
77 /* Initialize all variables that we clean on shutdown */ 88 SDL_NAS_SYM(AuCreateFlow);
78 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); 89 SDL_NAS_SYM(AuStartFlow);
79 if (this) { 90 SDL_NAS_SYM(AuSetElements);
80 SDL_memset(this, 0, (sizeof *this)); 91 SDL_NAS_SYM(AuWriteElement);
81 this->hidden = (struct SDL_PrivateAudioData *) 92 SDL_NAS_SYM(AuOpenServer);
82 SDL_malloc((sizeof *this->hidden)); 93 SDL_NAS_SYM(AuRegisterEventHandler);
83 } 94 return 0;
84 if ((this == NULL) || (this->hidden == NULL)) { 95 }
85 SDL_OutOfMemory(); 96 #undef SDL_NAS_SYM
86 if (this) { 97
87 SDL_free(this); 98 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
88 } 99
89 return (0); 100 static void
90 } 101 UnloadNASLibrary(void)
91 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 102 {
92 103 if (nas_handle != NULL) {
93 /* Set the function pointers */ 104 SDL_UnloadObject(nas_handle);
94 this->OpenAudio = NAS_OpenAudio; 105 nas_handle = NULL;
95 this->WaitAudio = NAS_WaitAudio; 106 }
96 this->PlayAudio = NAS_PlayAudio; 107 }
97 this->GetAudioBuf = NAS_GetAudioBuf; 108
98 this->CloseAudio = NAS_CloseAudio; 109 static int
99 110 LoadNASLibrary(void)
100 this->free = Audio_DeleteDevice; 111 {
101 112 int retval = 0;
102 return this; 113 if (nas_handle == NULL) {
103 } 114 nas_handle = SDL_LoadObject(nas_library);
104 115 if (nas_handle == NULL) {
105 AudioBootStrap NAS_bootstrap = { 116 /* Copy error string so we can use it in a new SDL_SetError(). */
106 NAS_DRIVER_NAME, "Network Audio System", 117 char *origerr = SDL_GetError();
107 Audio_Available, Audio_CreateDevice 118 size_t len = SDL_strlen(origerr) + 1;
108 }; 119 char *err = (char *) alloca(len);
120 SDL_strlcpy(err, origerr, len);
121 retval = -1;
122 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
123 nas_library, err);
124 } else {
125 retval = load_nas_syms();
126 if (retval < 0) {
127 UnloadNASLibrary();
128 }
129 }
130 }
131 return retval;
132 }
133
134 #else
135
136 static void
137 UnloadNASLibrary(void)
138 {
139 }
140
141 static int
142 LoadNASLibrary(void)
143 {
144 load_nas_syms();
145 return 0;
146 }
147
148 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
109 149
110 /* This function waits until it is possible to write a full sound buffer */ 150 /* This function waits until it is possible to write a full sound buffer */
111 static void 151 static void
112 NAS_WaitAudio(_THIS) 152 NAS_WaitDevice(_THIS)
113 { 153 {
114 while (this->hidden->buf_free < this->hidden->mixlen) { 154 while (this->hidden->buf_free < this->hidden->mixlen) {
115 AuEvent ev; 155 AuEvent ev;
116 AuNextEvent(this->hidden->aud, AuTrue, &ev); 156 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
117 AuDispatchEvent(this->hidden->aud, &ev); 157 NAS_AuDispatchEvent(this->hidden->aud, &ev);
118 } 158 }
119 } 159 }
120 160
121 static void 161 static void
122 NAS_PlayAudio(_THIS) 162 NAS_PlayDevice(_THIS)
123 { 163 {
124 while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events, 164 while (this->hidden->mixlen > this->hidden->buf_free) {
125 in the hope that some of them is LowWater events telling us more 165 /*
126 of the buffer is free now than what we think. */ 166 * We think the buffer is full? Yikes! Ask the server for events,
167 * in the hope that some of them is LowWater events telling us more
168 * of the buffer is free now than what we think.
169 */
127 AuEvent ev; 170 AuEvent ev;
128 AuNextEvent(this->hidden->aud, AuTrue, &ev); 171 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
129 AuDispatchEvent(this->hidden->aud, &ev); 172 NAS_AuDispatchEvent(this->hidden->aud, &ev);
130 } 173 }
131 this->hidden->buf_free -= this->hidden->mixlen; 174 this->hidden->buf_free -= this->hidden->mixlen;
132 175
133 /* Write the audio data */ 176 /* Write the audio data */
134 AuWriteElement(this->hidden->aud, this->hidden->flow, 0, 177 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
135 this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL); 178 this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
136 179
137 this->hidden->written += this->hidden->mixlen; 180 this->hidden->written += this->hidden->mixlen;
138 181
139 #ifdef DEBUG_AUDIO 182 #ifdef DEBUG_AUDIO
140 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen); 183 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
141 #endif 184 #endif
142 } 185 }
143 186
144 static Uint8 * 187 static Uint8 *
145 NAS_GetAudioBuf(_THIS) 188 NAS_GetDeviceBuf(_THIS)
146 { 189 {
147 return (this->hidden->mixbuf); 190 return (this->hidden->mixbuf);
148 } 191 }
149 192
150 static void 193 static void
151 NAS_CloseAudio(_THIS) 194 NAS_CloseDevice(_THIS)
152 { 195 {
153 if (this->hidden->mixbuf != NULL) { 196 if (this->hidden != NULL) {
154 SDL_FreeAudioMem(this->hidden->mixbuf); 197 if (this->hidden->mixbuf != NULL) {
155 this->hidden->mixbuf = NULL; 198 SDL_FreeAudioMem(this->hidden->mixbuf);
156 } 199 this->hidden->mixbuf = NULL;
157 if (this->hidden->aud) { 200 }
158 AuCloseServer(this->hidden->aud); 201 if (this->hidden->aud) {
159 this->hidden->aud = 0; 202 NAS_AuCloseServer(this->hidden->aud);
203 this->hidden->aud = 0;
204 }
205 SDL_free(this->hidden);
206 this2 = this->hidden = NULL;
160 } 207 }
161 } 208 }
162 209
163 static unsigned char 210 static unsigned char
164 sdlformat_to_auformat(unsigned int fmt) 211 sdlformat_to_auformat(unsigned int fmt)
219 } 266 }
220 267
221 static AuDeviceID 268 static AuDeviceID
222 find_device(_THIS, int nch) 269 find_device(_THIS, int nch)
223 { 270 {
271 /* These "Au" things are all macros, not functions... */
224 int i; 272 int i;
225 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) { 273 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
226 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) == 274 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
227 AuComponentKindPhysicalOutput) && 275 AuComponentKindPhysicalOutput) &&
228 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) { 276 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
231 } 279 }
232 return AuNone; 280 return AuNone;
233 } 281 }
234 282
235 static int 283 static int
236 NAS_OpenAudio(_THIS, SDL_AudioSpec * spec) 284 NAS_OpenDevice(_THIS, const char *devname, int iscapture)
237 { 285 {
238 AuElement elms[3]; 286 AuElement elms[3];
239 int buffer_size; 287 int buffer_size;
240 SDL_AudioFormat test_format, format; 288 SDL_AudioFormat test_format, format;
241 289
242 this->hidden->mixbuf = NULL; 290 /* Initialize all variables that we clean on shutdown */
291 this->hidden = (struct SDL_PrivateAudioData *)
292 SDL_malloc((sizeof *this->hidden));
293 if (this->hidden == NULL) {
294 SDL_OutOfMemory();
295 return 0;
296 }
297 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
243 298
244 /* Try for a closest match on audio format */ 299 /* Try for a closest match on audio format */
245 format = 0; 300 format = 0;
246 for (test_format = SDL_FirstAudioFormat(spec->format); 301 for (test_format = SDL_FirstAudioFormat(this->spec.format);
247 !format && test_format;) { 302 !format && test_format;) {
248 format = sdlformat_to_auformat(test_format); 303 format = sdlformat_to_auformat(test_format);
249
250 if (format == AuNone) { 304 if (format == AuNone) {
251 test_format = SDL_NextAudioFormat(); 305 test_format = SDL_NextAudioFormat();
252 } 306 }
253 } 307 }
254 if (format == 0) { 308 if (format == 0) {
255 SDL_SetError("Couldn't find any hardware audio formats"); 309 NAS_CloseDevice(this);
256 return (-1); 310 SDL_SetError("NAS: Couldn't find any hardware audio formats");
257 } 311 return 0;
258 spec->format = test_format; 312 }
259 313 this->spec.format = test_format;
260 this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL); 314
315 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
261 if (this->hidden->aud == 0) { 316 if (this->hidden->aud == 0) {
262 SDL_SetError("Couldn't open connection to NAS server"); 317 NAS_CloseDevice(this);
263 return (-1); 318 SDL_SetError("NAS: Couldn't open connection to NAS server");
264 } 319 return 0;
265 320 }
266 this->hidden->dev = find_device(this, spec->channels); 321
322 this->hidden->dev = find_device(this, this->spec.channels);
267 if ((this->hidden->dev == AuNone) 323 if ((this->hidden->dev == AuNone)
268 || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) { 324 || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
269 AuCloseServer(this->hidden->aud); 325 NAS_CloseDevice(this);
270 this->hidden->aud = 0; 326 SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
271 SDL_SetError("Couldn't find a fitting playback device on NAS server"); 327 return 0;
272 return (-1); 328 }
273 } 329
274 330 buffer_size = this->spec.freq;
275 buffer_size = spec->freq;
276 if (buffer_size < 4096) 331 if (buffer_size < 4096)
277 buffer_size = 4096; 332 buffer_size = 4096;
278 333
279 if (buffer_size > 32768) 334 if (buffer_size > 32768)
280 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */ 335 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
281 336
282 /* Calculate the final parameters for this audio specification */ 337 /* Calculate the final parameters for this audio specification */
283 SDL_CalculateAudioSpec(spec); 338 SDL_CalculateAudioSpec(&this->spec);
284 339
285 this2 = this->hidden; 340 this2 = this->hidden;
286 341
287 AuMakeElementImportClient(elms, spec->freq, format, spec->channels, 342 AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels,
288 AuTrue, buffer_size, buffer_size / 4, 0, NULL); 343 AuTrue, buffer_size, buffer_size / 4, 0, NULL);
289 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, spec->freq, 344 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
290 AuUnlimitedSamples, 0, NULL); 345 AuUnlimitedSamples, 0, NULL);
291 AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, 346 NAS_AuSetElements(this->hidden->aud, this->hidden->flow,
292 NULL); 347 AuTrue, 2, elms, NULL);
293 AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, 348 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
294 this->hidden->flow, event_handler, 349 this->hidden->flow, event_handler,
295 (AuPointer) NULL); 350 (AuPointer) NULL);
296 351
297 AuStartFlow(this->hidden->aud, this->hidden->flow, NULL); 352 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
298 353
299 /* Allocate mixing buffer */ 354 /* Allocate mixing buffer */
300 this->hidden->mixlen = spec->size; 355 this->hidden->mixlen = this->spec.size;
301 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 356 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
302 if (this->hidden->mixbuf == NULL) { 357 if (this->hidden->mixbuf == NULL) {
303 return (-1); 358 NAS_CloseDevice(this);
304 } 359 SDL_OutOfMemory();
305 SDL_memset(this->hidden->mixbuf, spec->silence, spec->size); 360 return 0;
306 361 }
307 /* Get the parent process id (we're the parent of the audio thread) */ 362 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
308 this->hidden->parent = getpid();
309 363
310 /* We're ready to rock and roll. :-) */ 364 /* We're ready to rock and roll. :-) */
311 return (0); 365 return 1;
312 } 366 }
367
368 static void
369 NAS_Deinitialize(void)
370 {
371 UnloadNASLibrary();
372 }
373
374 static int
375 NAS_Init(SDL_AudioDriverImpl *impl)
376 {
377 if (LoadNASLibrary() < 0) {
378 return 0;
379 } else {
380 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
381 if (aud == NULL) {
382 SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
383 return 0;
384 }
385 NAS_AuCloseServer(aud);
386 }
387
388 /* Set the function pointers */
389 impl->OpenDevice = NAS_OpenDevice;
390 impl->PlayDevice = NAS_PlayDevice;
391 impl->WaitDevice = NAS_WaitDevice;
392 impl->GetDeviceBuf = NAS_GetDeviceBuf;
393 impl->CloseDevice = NAS_CloseDevice;
394 impl->Deinitialize = NAS_Deinitialize;
395 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */
396
397 return 1;
398 }
399
400 AudioBootStrap NAS_bootstrap = {
401 NAS_DRIVER_NAME, "Network Audio System", NAS_Init, 0
402 };
313 403
314 /* vi: set ts=4 sw=4 expandtab: */ 404 /* vi: set ts=4 sw=4 expandtab: */