Mercurial > sdl-ios-xcode
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: */ |