comparison src/audio/nas/SDL_nasaudio.c @ 3825:76c5a414b996 SDL-ryan-multiple-audio-device

Dynamic loading for NAS audio driver.
author Ryan C. Gordon <icculus@icculus.org>
date Sat, 07 Oct 2006 07:25:30 +0000
parents 748707e2ddd1
children 66fb40445587
comparison
equal deleted inserted replaced
3824:25052dd25810 3825:76c5a414b996
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_nasaudio.h" 38 #include "SDL_nasaudio.h"
38 39
39 /* The tag name used by nas audio */ 40 /* The tag name used by nas audio */
40 #define NAS_DRIVER_NAME "nas" 41 #define NAS_DRIVER_NAME "nas"
41 42
42 static struct SDL_PrivateAudioData *this2 = NULL; 43 static struct SDL_PrivateAudioData *this2 = NULL;
43 44
44 /* !!! FIXME: dynamic loading? */ 45
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);
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;
65
66 static int
67 load_nas_sym(const char *fn, void **addr)
68 {
69 *addr = SDL_LoadFunction(nas_handle, fn);
70 if (*addr == NULL) {
71 return 0;
72 }
73 return 1;
74 }
75
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
79 #else
80 #define SDL_NAS_SYM(x) NAS_##x = x
81 #endif
82
83 static int load_nas_syms(void)
84 {
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);
94 return 0;
95 }
96 #undef SDL_NAS_SYM
97
98 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
99
100 static int library_load_count = 0;
101
102 static void
103 UnloadNASLibrary(void)
104 {
105 if ((nas_handle != NULL) && (--library_load_count == 0)) {
106 SDL_UnloadObject(nas_handle);
107 nas_handle = NULL;
108 }
109 }
110
111 static int
112 LoadNASLibrary(void)
113 {
114 int retval = 0;
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);
123
124 library_load_count--;
125 retval = -1;
126 SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
127 nas_library, err);
128 } else {
129 retval = load_nas_syms();
130 if (retval < 0) {
131 UnloadNASLibrary();
132 }
133 }
134 }
135 return retval;
136 }
137
138 #else
139
140 static void
141 UnloadNASLibrary(void)
142 {
143 }
144
145 static int
146 LoadNASLibrary(void)
147 {
148 load_nas_syms();
149 return 0;
150 }
151
152 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
45 153
46 static int 154 static int
47 NAS_Available(void) 155 NAS_Available(void)
48 { 156 {
49 AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL); 157 int available = 0;
50 if (!aud) 158 if (LoadNASLibrary() >= 0) {
51 return 0; 159 AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
52 160 if (aud != NULL) {
53 AuCloseServer(aud); 161 available = 1;
54 return 1; 162 NAS_AuCloseServer(aud);
163 }
164 UnloadNASLibrary();
165 }
166 return available;
55 } 167 }
56 168
57 /* This function waits until it is possible to write a full sound buffer */ 169 /* This function waits until it is possible to write a full sound buffer */
58 static void 170 static void
59 NAS_WaitDevice(_THIS) 171 NAS_WaitDevice(_THIS)
60 { 172 {
61 while (this->hidden->buf_free < this->hidden->mixlen) { 173 while (this->hidden->buf_free < this->hidden->mixlen) {
62 AuEvent ev; 174 AuEvent ev;
63 AuNextEvent(this->hidden->aud, AuTrue, &ev); 175 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
64 AuDispatchEvent(this->hidden->aud, &ev); 176 NAS_AuDispatchEvent(this->hidden->aud, &ev);
65 } 177 }
66 } 178 }
67 179
68 static void 180 static void
69 NAS_PlayDevice(_THIS) 181 NAS_PlayDevice(_THIS)
73 * We think the buffer is full? Yikes! Ask the server for events, 185 * We think the buffer is full? Yikes! Ask the server for events,
74 * in the hope that some of them is LowWater events telling us more 186 * in the hope that some of them is LowWater events telling us more
75 * of the buffer is free now than what we think. 187 * of the buffer is free now than what we think.
76 */ 188 */
77 AuEvent ev; 189 AuEvent ev;
78 AuNextEvent(this->hidden->aud, AuTrue, &ev); 190 NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
79 AuDispatchEvent(this->hidden->aud, &ev); 191 NAS_AuDispatchEvent(this->hidden->aud, &ev);
80 } 192 }
81 this->hidden->buf_free -= this->hidden->mixlen; 193 this->hidden->buf_free -= this->hidden->mixlen;
82 194
83 /* Write the audio data */ 195 /* Write the audio data */
84 AuWriteElement(this->hidden->aud, this->hidden->flow, 0, 196 NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
85 this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL); 197 this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
86 198
87 this->hidden->written += this->hidden->mixlen; 199 this->hidden->written += this->hidden->mixlen;
88 200
89 #ifdef DEBUG_AUDIO 201 #ifdef DEBUG_AUDIO
104 if (this->hidden->mixbuf != NULL) { 216 if (this->hidden->mixbuf != NULL) {
105 SDL_FreeAudioMem(this->hidden->mixbuf); 217 SDL_FreeAudioMem(this->hidden->mixbuf);
106 this->hidden->mixbuf = NULL; 218 this->hidden->mixbuf = NULL;
107 } 219 }
108 if (this->hidden->aud) { 220 if (this->hidden->aud) {
109 AuCloseServer(this->hidden->aud); 221 NAS_AuCloseServer(this->hidden->aud);
110 this->hidden->aud = 0; 222 this->hidden->aud = 0;
111 } 223 }
112 SDL_free(this->hidden); 224 SDL_free(this->hidden);
113 this2 = this->hidden = NULL; 225 this2 = this->hidden = NULL;
226 UnloadNASLibrary();
114 } 227 }
115 } 228 }
116 229
117 static unsigned char 230 static unsigned char
118 sdlformat_to_auformat(unsigned int fmt) 231 sdlformat_to_auformat(unsigned int fmt)
173 } 286 }
174 287
175 static AuDeviceID 288 static AuDeviceID
176 find_device(_THIS, int nch) 289 find_device(_THIS, int nch)
177 { 290 {
291 /* These "Au" things are all macros, not functions... */
178 int i; 292 int i;
179 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) { 293 for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
180 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) == 294 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
181 AuComponentKindPhysicalOutput) && 295 AuComponentKindPhysicalOutput) &&
182 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) { 296 AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
199 if (this->hidden == NULL) { 313 if (this->hidden == NULL) {
200 SDL_OutOfMemory(); 314 SDL_OutOfMemory();
201 return 0; 315 return 0;
202 } 316 }
203 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 317 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
318
319 if (LoadNASLibrary() < 0) {
320 NAS_CloseDevice(this);
321 return 0;
322 }
204 323
205 /* Try for a closest match on audio format */ 324 /* Try for a closest match on audio format */
206 format = 0; 325 format = 0;
207 for (test_format = SDL_FirstAudioFormat(this->spec.format); 326 for (test_format = SDL_FirstAudioFormat(this->spec.format);
208 !format && test_format;) { 327 !format && test_format;) {
216 SDL_SetError("NAS: Couldn't find any hardware audio formats"); 335 SDL_SetError("NAS: Couldn't find any hardware audio formats");
217 return 0; 336 return 0;
218 } 337 }
219 this->spec.format = test_format; 338 this->spec.format = test_format;
220 339
221 this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL); 340 this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
222 if (this->hidden->aud == 0) { 341 if (this->hidden->aud == 0) {
223 NAS_CloseDevice(this); 342 NAS_CloseDevice(this);
224 SDL_SetError("NAS: Couldn't open connection to NAS server"); 343 SDL_SetError("NAS: Couldn't open connection to NAS server");
225 return 0; 344 return 0;
226 } 345 }
227 346
228 this->hidden->dev = find_device(this, this->spec.channels); 347 this->hidden->dev = find_device(this, this->spec.channels);
229 if ((this->hidden->dev == AuNone) 348 if ((this->hidden->dev == AuNone)
230 || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) { 349 || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
231 NAS_CloseDevice(this); 350 NAS_CloseDevice(this);
232 SDL_SetError("NAS: Couldn't find a fitting device on NAS server"); 351 SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
233 return 0; 352 return 0;
234 } 353 }
235 354
247 366
248 AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels, 367 AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels,
249 AuTrue, buffer_size, buffer_size / 4, 0, NULL); 368 AuTrue, buffer_size, buffer_size / 4, 0, NULL);
250 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq, 369 AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
251 AuUnlimitedSamples, 0, NULL); 370 AuUnlimitedSamples, 0, NULL);
252 AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL); 371 NAS_AuSetElements(this->hidden->aud, this->hidden->flow,
253 AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, 372 AuTrue, 2, elms, NULL);
254 this->hidden->flow, event_handler, 373 NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
255 (AuPointer) NULL); 374 this->hidden->flow, event_handler,
256 375 (AuPointer) NULL);
257 AuStartFlow(this->hidden->aud, this->hidden->flow, NULL); 376
377 NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
258 378
259 /* Allocate mixing buffer */ 379 /* Allocate mixing buffer */
260 this->hidden->mixlen = this->spec.size; 380 this->hidden->mixlen = this->spec.size;
261 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 381 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
262 if (this->hidden->mixbuf == NULL) { 382 if (this->hidden->mixbuf == NULL) {
263 NAS_CloseDevice(this); 383 NAS_CloseDevice(this);
264 SDL_OutOfMemory(); 384 SDL_OutOfMemory();
265 return (-1); 385 return 0;
266 } 386 }
267 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 387 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
268 388
269 /* We're ready to rock and roll. :-) */ 389 /* We're ready to rock and roll. :-) */
270 return 1; 390 return 1;