comparison src/audio/SDL_audio.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 510892a215a2
children 716cf90f24a0
comparison
equal deleted inserted replaced
2048:6067c7f9a672 2049:5f6550e5184f
26 #include "SDL.h" 26 #include "SDL.h"
27 #include "SDL_audio_c.h" 27 #include "SDL_audio_c.h"
28 #include "SDL_audiomem.h" 28 #include "SDL_audiomem.h"
29 #include "SDL_sysaudio.h" 29 #include "SDL_sysaudio.h"
30 30
31 #ifdef __OS2__ 31 #define _THIS SDL_AudioDevice *this
32 /* We'll need the DosSetPriority() API! */ 32
33 #define INCL_DOSPROCESS 33 static SDL_AudioDriver current_audio;
34 #include <os2.h> 34 static SDL_AudioDevice *open_devices[16];
35 #endif 35
36 /* !!! FIXME: These are wordy and unlocalized... */
37 #define DEFAULT_OUTPUT_DEVNAME "System audio output device"
38 #define DEFAULT_INPUT_DEVNAME "System audio capture device"
39
40
41 /*
42 * Not all of these will be compiled and linked in, but it's convenient
43 * to have a complete list here and saves yet-another block of #ifdefs...
44 * Please see bootstrap[], below, for the actual #ifdef mess.
45 */
46 extern AudioBootStrap BSD_AUDIO_bootstrap;
47 extern AudioBootStrap DSP_bootstrap;
48 extern AudioBootStrap DMA_bootstrap;
49 extern AudioBootStrap ALSA_bootstrap;
50 extern AudioBootStrap QNXNTOAUDIO_bootstrap;
51 extern AudioBootStrap SUNAUDIO_bootstrap;
52 extern AudioBootStrap DMEDIA_bootstrap;
53 extern AudioBootStrap ARTS_bootstrap;
54 extern AudioBootStrap ESD_bootstrap;
55 extern AudioBootStrap NAS_bootstrap;
56 extern AudioBootStrap DSOUND_bootstrap;
57 extern AudioBootStrap WAVEOUT_bootstrap;
58 extern AudioBootStrap PAUDIO_bootstrap;
59 extern AudioBootStrap BEOSAUDIO_bootstrap;
60 extern AudioBootStrap COREAUDIO_bootstrap;
61 extern AudioBootStrap SNDMGR_bootstrap;
62 extern AudioBootStrap MINTAUDIO_GSXB_bootstrap;
63 extern AudioBootStrap MINTAUDIO_MCSN_bootstrap;
64 extern AudioBootStrap MINTAUDIO_STFA_bootstrap;
65 extern AudioBootStrap MINTAUDIO_XBIOS_bootstrap;
66 extern AudioBootStrap MINTAUDIO_DMA8_bootstrap;
67 extern AudioBootStrap DISKAUD_bootstrap;
68 extern AudioBootStrap DUMMYAUD_bootstrap;
69 extern AudioBootStrap DCAUD_bootstrap;
70 extern AudioBootStrap MMEAUDIO_bootstrap;
71 extern AudioBootStrap DART_bootstrap;
72
36 73
37 /* Available audio drivers */ 74 /* Available audio drivers */
38 static AudioBootStrap *bootstrap[] = { 75 static AudioBootStrap *bootstrap[] = {
39 #if SDL_AUDIO_DRIVER_BSD 76 #if SDL_AUDIO_DRIVER_BSD
40 &BSD_AUDIO_bootstrap, 77 &BSD_AUDIO_bootstrap,
68 &DSOUND_bootstrap, 105 &DSOUND_bootstrap,
69 #endif 106 #endif
70 #if SDL_AUDIO_DRIVER_WAVEOUT 107 #if SDL_AUDIO_DRIVER_WAVEOUT
71 &WAVEOUT_bootstrap, 108 &WAVEOUT_bootstrap,
72 #endif 109 #endif
73 #if SDL_AUDIO_DRIVER_PAUD 110 #if SDL_AUDIO_DRIVER_PAUDIO
74 &Paud_bootstrap, 111 &PAUDIO_bootstrap,
75 #endif 112 #endif
76 #if SDL_AUDIO_DRIVER_BAUDIO 113 #if SDL_AUDIO_DRIVER_BEOSAUDIO
77 &BAUDIO_bootstrap, 114 &BEOSAUDIO_bootstrap,
78 #endif 115 #endif
79 #if SDL_AUDIO_DRIVER_COREAUDIO 116 #if SDL_AUDIO_DRIVER_COREAUDIO
80 &COREAUDIO_bootstrap, 117 &COREAUDIO_bootstrap,
81 #endif 118 #endif
82 #if SDL_AUDIO_DRIVER_SNDMGR 119 #if SDL_AUDIO_DRIVER_SNDMGR
83 &SNDMGR_bootstrap, 120 &SNDMGR_bootstrap,
84 #endif
85 #if SDL_AUDIO_DRIVER_AHI
86 &AHI_bootstrap,
87 #endif 121 #endif
88 #if SDL_AUDIO_DRIVER_MINT 122 #if SDL_AUDIO_DRIVER_MINT
89 &MINTAUDIO_GSXB_bootstrap, 123 &MINTAUDIO_GSXB_bootstrap,
90 &MINTAUDIO_MCSN_bootstrap, 124 &MINTAUDIO_MCSN_bootstrap,
91 &MINTAUDIO_STFA_bootstrap, 125 &MINTAUDIO_STFA_bootstrap,
107 #if SDL_AUDIO_DRIVER_DART 141 #if SDL_AUDIO_DRIVER_DART
108 &DART_bootstrap, 142 &DART_bootstrap,
109 #endif 143 #endif
110 NULL 144 NULL
111 }; 145 };
112 SDL_AudioDevice *current_audio = NULL; 146
113 147 static SDL_AudioDevice *get_audio_device(SDL_AudioDeviceID id)
114 /* Various local functions */ 148 {
115 int SDL_AudioInit(const char *driver_name); 149 id--;
116 void SDL_AudioQuit(void); 150 if ( (id >= SDL_arraysize(open_devices)) || (open_devices[id] == NULL) ) {
117 151 SDL_SetError("Invalid audio device ID");
118 #if SDL_AUDIO_DRIVER_AHI 152 return NULL;
119 static int audio_configured = 0; 153 }
120 #endif 154
155 return open_devices[id];
156 }
157
158
159 /* stubs for audio drivers that don't need a specific entry point... */
160 static int SDL_AudioDetectDevices_Default(int iscapture) { return -1; }
161 static void SDL_AudioThreadInit_Default(_THIS) { /* no-op. */ }
162 static void SDL_AudioWaitDevice_Default(_THIS) { /* no-op. */ }
163 static void SDL_AudioPlayDevice_Default(_THIS) { /* no-op. */ }
164 static Uint8 *SDL_AudioGetDeviceBuf_Default(_THIS) { return NULL; }
165 static void SDL_AudioWaitDone_Default(_THIS) { /* no-op. */ }
166 static void SDL_AudioCloseDevice_Default(_THIS) { /* no-op. */ }
167 static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ }
168
169 static int
170 SDL_AudioOpenDevice_Default(_THIS, const char *devname, int iscapture)
171 {
172 return 0;
173 }
174
175 static const char *SDL_AudioGetDeviceName_Default(int index, int iscapture)
176 {
177 SDL_SetError("No such device");
178 return NULL;
179 }
180
181 static void
182 SDL_AudioLockDevice_Default(SDL_AudioDevice * device)
183 {
184 if (device->thread && (SDL_ThreadID() == device->threadid)) {
185 return;
186 }
187 SDL_mutexP(device->mixer_lock);
188 }
189
190 static void
191 SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device)
192 {
193 if (device->thread && (SDL_ThreadID() == device->threadid)) {
194 return;
195 }
196 SDL_mutexV(device->mixer_lock);
197 }
198
199
200 static void finalize_audio_entry_points(void)
201 {
202 /*
203 * Fill in stub functions for unused driver entry points. This lets us
204 * blindly call them without having to check for validity first.
205 */
206
207 #define FILL_STUB(x) \
208 if (current_audio.impl.x == NULL) { \
209 current_audio.impl.x = SDL_Audio##x##_Default; \
210 }
211 FILL_STUB(DetectDevices);
212 FILL_STUB(GetDeviceName);
213 FILL_STUB(OpenDevice);
214 FILL_STUB(ThreadInit);
215 FILL_STUB(WaitDevice);
216 FILL_STUB(PlayDevice);
217 FILL_STUB(GetDeviceBuf);
218 FILL_STUB(WaitDone);
219 FILL_STUB(CloseDevice);
220 FILL_STUB(LockDevice);
221 FILL_STUB(UnlockDevice);
222 FILL_STUB(Deinitialize);
223 #undef FILL_STUB
224 }
225
121 226
122 /* The general mixing thread function */ 227 /* The general mixing thread function */
123 int SDLCALL 228 int SDLCALL
124 SDL_RunAudio(void *audiop) 229 SDL_RunAudio(void *devicep)
125 { 230 {
126 SDL_AudioDevice *audio = (SDL_AudioDevice *) audiop; 231 SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
232 const int legacy_device = (device == open_devices[0]);
127 Uint8 *stream; 233 Uint8 *stream;
128 int stream_len; 234 int stream_len;
129 void *udata; 235 void *udata;
130 void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len); 236 void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len);
131 int silence; 237 int silence;
132 #if SDL_AUDIO_DRIVER_AHI
133 int started = 0;
134
135 /* AmigaOS NEEDS that the audio driver is opened in the thread that uses it! */
136
137 D(bug("Task audio started audio struct:<%lx>...\n", audiop));
138
139 D(bug("Before Openaudio..."));
140 if (audio->OpenAudio(audio, &audio->spec) == -1) {
141 D(bug("Open audio failed...\n"));
142 return (-1);
143 }
144 D(bug("OpenAudio...OK\n"));
145 #endif
146 238
147 /* Perform any thread setup */ 239 /* Perform any thread setup */
148 if (audio->ThreadInit) { 240 device->threadid = SDL_ThreadID();
149 audio->ThreadInit(audio); 241 current_audio.impl.ThreadInit(device);
150 }
151 audio->threadid = SDL_ThreadID();
152 242
153 /* Set up the mixing function */ 243 /* Set up the mixing function */
154 fill = audio->spec.callback; 244 fill = device->spec.callback;
155 udata = audio->spec.userdata; 245 udata = device->spec.userdata;
156 246
157 #if SDL_AUDIO_DRIVER_AHI 247 if (device->convert.needed) {
158 audio_configured = 1; 248 if (device->convert.src_format == AUDIO_U8) {
159
160 D(bug("Audio configured... Checking for conversion\n"));
161 SDL_mutexP(audio->mixer_lock);
162 D(bug("Semaphore obtained...\n"));
163 #endif
164
165 if (audio->convert.needed) {
166 if (audio->convert.src_format == AUDIO_U8) {
167 silence = 0x80; 249 silence = 0x80;
168 } else { 250 } else {
169 silence = 0; 251 silence = 0;
170 } 252 }
171 stream_len = audio->convert.len; 253 stream_len = device->convert.len;
172 } else { 254 } else {
173 silence = audio->spec.silence; 255 silence = device->spec.silence;
174 stream_len = audio->spec.size; 256 stream_len = device->spec.size;
175 } 257 }
176
177 #if SDL_AUDIO_DRIVER_AHI
178 SDL_mutexV(audio->mixer_lock);
179 D(bug("Entering audio loop...\n"));
180 #endif
181
182 #ifdef __OS2__
183 /* Increase the priority of this thread to make sure that
184 the audio will be continuous all the time! */
185 #ifdef USE_DOSSETPRIORITY
186 if (SDL_getenv("SDL_USE_TIMECRITICAL_AUDIO")) {
187 #ifdef DEBUG_BUILD
188 printf
189 ("[SDL_RunAudio] : Setting priority to TimeCritical+0! (TID%d)\n",
190 SDL_ThreadID());
191 #endif
192 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
193 } else {
194 #ifdef DEBUG_BUILD
195 printf
196 ("[SDL_RunAudio] : Setting priority to ForegroundServer+0! (TID%d)\n",
197 SDL_ThreadID());
198 #endif
199 DosSetPriority(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0);
200 }
201 #endif
202 #endif
203 258
204 /* Loop, filling the audio buffers */ 259 /* Loop, filling the audio buffers */
205 while (audio->enabled) { 260 while (device->enabled) {
206 261
207 /* Fill the current buffer with sound */ 262 /* Fill the current buffer with sound */
208 if (audio->convert.needed) { 263 if (device->convert.needed) {
209 if (audio->convert.buf) { 264 if (device->convert.buf) {
210 stream = audio->convert.buf; 265 stream = device->convert.buf;
211 } else { 266 } else {
212 continue; 267 continue;
213 } 268 }
214 } else { 269 } else {
215 stream = audio->GetAudioBuf(audio); 270 stream = current_audio.impl.GetDeviceBuf(device);
216 if (stream == NULL) { 271 if (stream == NULL) {
217 stream = audio->fake_stream; 272 stream = device->fake_stream;
218 } 273 }
219 } 274 }
220 SDL_memset(stream, silence, stream_len); 275
221 276 /* New code should fill buffer or set it to silence themselves. */
222 if (!audio->paused) { 277 if (legacy_device) {
223 SDL_mutexP(audio->mixer_lock); 278 SDL_memset(stream, silence, stream_len);
279 }
280
281 if (!device->paused) {
282 SDL_mutexP(device->mixer_lock);
224 (*fill) (udata, stream, stream_len); 283 (*fill) (udata, stream, stream_len);
225 SDL_mutexV(audio->mixer_lock); 284 SDL_mutexV(device->mixer_lock);
226 } 285 }
227 286
228 /* Convert the audio if necessary */ 287 /* Convert the audio if necessary */
229 if (audio->convert.needed) { 288 if (device->convert.needed) {
230 SDL_ConvertAudio(&audio->convert); 289 SDL_ConvertAudio(&device->convert);
231 stream = audio->GetAudioBuf(audio); 290 stream = current_audio.impl.GetDeviceBuf(device);
232 if (stream == NULL) { 291 if (stream == NULL) {
233 stream = audio->fake_stream; 292 stream = device->fake_stream;
234 } 293 }
235 SDL_memcpy(stream, audio->convert.buf, audio->convert.len_cvt); 294 SDL_memcpy(stream, device->convert.buf, device->convert.len_cvt);
236 } 295 }
237 296
238 /* Ready current buffer for play and change current buffer */ 297 /* Ready current buffer for play and change current buffer */
239 if (stream != audio->fake_stream) { 298 if (stream != device->fake_stream) {
240 audio->PlayAudio(audio); 299 current_audio.impl.PlayDevice(device);
241 } 300 }
242 301
243 /* Wait for an audio buffer to become available */ 302 /* Wait for an audio buffer to become available */
244 if (stream == audio->fake_stream) { 303 if (stream == device->fake_stream) {
245 SDL_Delay((audio->spec.samples * 1000) / audio->spec.freq); 304 SDL_Delay((device->spec.samples * 1000) / device->spec.freq);
246 } else { 305 } else {
247 audio->WaitAudio(audio); 306 current_audio.impl.WaitDevice(device);
248 } 307 }
249 } 308 }
250 309
251 /* Wait for the audio to drain.. */ 310 /* Wait for the audio to drain.. */
252 if (audio->WaitDone) { 311 current_audio.impl.WaitDone(device);
253 audio->WaitDone(audio); 312
254 }
255 #if SDL_AUDIO_DRIVER_AHI
256 D(bug("WaitAudio...Done\n"));
257
258 audio->CloseAudio(audio);
259
260 D(bug("CloseAudio..Done, subtask exiting...\n"));
261 audio_configured = 0;
262 #endif
263 #ifdef __OS2__
264 #ifdef DEBUG_BUILD
265 printf("[SDL_RunAudio] : Task exiting. (TID%d)\n", SDL_ThreadID());
266 #endif
267 #endif
268 return (0); 313 return (0);
269 } 314 }
270 315
271 static void
272 SDL_LockAudio_Default(SDL_AudioDevice * audio)
273 {
274 if (audio->thread && (SDL_ThreadID() == audio->threadid)) {
275 return;
276 }
277 SDL_mutexP(audio->mixer_lock);
278 }
279
280 static void
281 SDL_UnlockAudio_Default(SDL_AudioDevice * audio)
282 {
283 if (audio->thread && (SDL_ThreadID() == audio->threadid)) {
284 return;
285 }
286 SDL_mutexV(audio->mixer_lock);
287 }
288 316
289 static SDL_AudioFormat 317 static SDL_AudioFormat
290 SDL_ParseAudioFormat(const char *string) 318 SDL_ParseAudioFormat(const char *string)
291 { 319 {
292 SDL_AudioFormat format = 0; 320 #define CHECK_FMT_STRING(x) if (strcmp(string, #x) == 0) return AUDIO_##x
293 321 CHECK_FMT_STRING(U8);
294 switch (*string) { 322 CHECK_FMT_STRING(S8);
295 case 'U': 323 CHECK_FMT_STRING(U16LSB);
296 ++string; 324 CHECK_FMT_STRING(S16LSB);
297 format |= 0x0000; 325 CHECK_FMT_STRING(U16MSB);
298 break; 326 CHECK_FMT_STRING(S16MSB);
299 case 'S': 327 CHECK_FMT_STRING(U16SYS);
300 ++string; 328 CHECK_FMT_STRING(S16SYS);
301 format |= 0x8000; 329 CHECK_FMT_STRING(U16);
302 break; 330 CHECK_FMT_STRING(S16);
303 default: 331 CHECK_FMT_STRING(S32LSB);
304 return 0; 332 CHECK_FMT_STRING(S32MSB);
305 } 333 CHECK_FMT_STRING(S32SYS);
306 switch (SDL_atoi(string)) { 334 CHECK_FMT_STRING(S32);
307 case 8: 335 CHECK_FMT_STRING(F32LSB);
308 string += 1; 336 CHECK_FMT_STRING(F32MSB);
309 format |= 8; 337 CHECK_FMT_STRING(F32SYS);
310 break; 338 CHECK_FMT_STRING(F32);
311 case 16: 339 #undef CHECK_FMT_STRING
312 string += 2; 340 return 0;
313 format |= 16;
314 if (SDL_strcmp(string, "LSB") == 0
315 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
316 || SDL_strcmp(string, "SYS") == 0
317 #endif
318 ) {
319 format |= 0x0000;
320 }
321 if (SDL_strcmp(string, "MSB") == 0
322 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
323 || SDL_strcmp(string, "SYS") == 0
324 #endif
325 ) {
326 format |= 0x1000;
327 }
328 break;
329 default:
330 return 0;
331 }
332 return format;
333 } 341 }
334 342
335 int 343 int
336 SDL_GetNumAudioDrivers(void) 344 SDL_GetNumAudioDrivers(void)
337 { 345 {
348 } 356 }
349 357
350 int 358 int
351 SDL_AudioInit(const char *driver_name) 359 SDL_AudioInit(const char *driver_name)
352 { 360 {
353 SDL_AudioDevice *audio; 361 int i = 0;
354 int i = 0, idx; 362 int initialized = 0;
355 363 int tried_to_init = 0;
356 /* Check to make sure we don't overwrite 'current_audio' */ 364
357 if (current_audio != NULL) { 365 if (SDL_WasInit(SDL_INIT_AUDIO)) {
358 SDL_AudioQuit(); 366 SDL_AudioQuit(); /* shutdown driver if already running. */
359 } 367 }
368
369 SDL_memset(&current_audio, '\0', sizeof (current_audio));
370 SDL_memset(open_devices, '\0', sizeof (open_devices));
360 371
361 /* Select the proper audio driver */ 372 /* Select the proper audio driver */
362 audio = NULL;
363 idx = 0;
364 if (driver_name == NULL) { 373 if (driver_name == NULL) {
365 driver_name = SDL_getenv("SDL_AUDIODRIVER"); 374 driver_name = SDL_getenv("SDL_AUDIODRIVER");
366 } 375 }
367 #if SDL_AUDIO_DRIVER_ESD 376
368 if ((driver_name == NULL) && (SDL_getenv("ESPEAKER") != NULL)) { 377 for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
369 /* Ahem, we know that if ESPEAKER is set, user probably wants 378 /* make sure we should even try this driver before doing so... */
370 to use ESD, but don't start it if it's not already running. 379 const AudioBootStrap *backend = bootstrap[i];
371 This probably isn't the place to do this, but... Shh! :) 380 if ( ((driver_name) && (SDL_strcasecmp(backend->name, driver_name))) ||
372 */ 381 ((!driver_name) && (backend->demand_only)) ) {
373 for (i = 0; bootstrap[i]; ++i) { 382 continue;
374 if (SDL_strcasecmp(bootstrap[i]->name, "esd") == 0) { 383 }
375 #ifdef HAVE_PUTENV 384
376 const char *esd_no_spawn; 385 tried_to_init = 1;
377 386 SDL_memset(&current_audio, 0, sizeof (current_audio));
378 /* Don't start ESD if it's not running */ 387 current_audio.name = backend->name;
379 esd_no_spawn = getenv("ESD_NO_SPAWN"); 388 current_audio.desc = backend->desc;
380 if (esd_no_spawn == NULL) { 389 initialized = backend->init(&current_audio.impl);
381 putenv("ESD_NO_SPAWN=1"); 390 }
382 } 391
383 #endif 392 if (!initialized) {
384 if (bootstrap[i]->available()) { 393 /* specific drivers will set the error message if they fail... */
385 audio = bootstrap[i]->create(0); 394 if (!tried_to_init) {
386 break;
387 }
388 #ifdef HAVE_UNSETENV
389 if (esd_no_spawn == NULL) {
390 unsetenv("ESD_NO_SPAWN");
391 }
392 #endif
393 }
394 }
395 }
396 #endif /* SDL_AUDIO_DRIVER_ESD */
397 if (audio == NULL) {
398 if (driver_name != NULL) {
399 for (i = 0; bootstrap[i]; ++i) {
400 if (SDL_strcasecmp(bootstrap[i]->name, driver_name) == 0) {
401 if (bootstrap[i]->available()) {
402 audio = bootstrap[i]->create(idx);
403 }
404 break;
405 }
406 }
407 } else {
408 for (i = 0; bootstrap[i]; ++i) {
409 if (bootstrap[i]->available()) {
410 audio = bootstrap[i]->create(idx);
411 if (audio != NULL) {
412 break;
413 }
414 }
415 }
416 }
417 if (audio == NULL) {
418 if (driver_name) { 395 if (driver_name) {
419 SDL_SetError("%s not available", driver_name); 396 SDL_SetError("%s not available", driver_name);
420 } else { 397 } else {
421 SDL_SetError("No available audio device"); 398 SDL_SetError("No available audio device");
422 } 399 }
423 #if 0 400 }
424 /* Don't fail SDL_Init() if audio isn't available. 401
425 SDL_OpenAudio() will handle it at that point. *sigh* 402 SDL_memset(&current_audio, 0, sizeof (current_audio));
426 */ 403 return (-1); /* No driver was available, so fail. */
427 return (-1); 404 }
428 #endif 405
429 } 406 finalize_audio_entry_points();
430 } 407
431 current_audio = audio;
432 if (current_audio) {
433 current_audio->name = bootstrap[i]->name;
434 if (!current_audio->LockAudio && !current_audio->UnlockAudio) {
435 current_audio->LockAudio = SDL_LockAudio_Default;
436 current_audio->UnlockAudio = SDL_UnlockAudio_Default;
437 }
438 }
439 return (0); 408 return (0);
440 } 409 }
441 410
442 /* 411 /*
443 * Get the current audio driver name 412 * Get the current audio driver name
444 */ 413 */
445 const char * 414 const char *
446 SDL_GetCurrentAudioDriver() 415 SDL_GetCurrentAudioDriver()
447 { 416 {
448 if (current_audio) { 417 return current_audio.name;
449 return current_audio->name; 418 }
450 } 419
451 return (NULL);
452 }
453 420
454 int 421 int
455 SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained) 422 SDL_GetNumAudioDevices(int iscapture)
456 { 423 {
457 SDL_AudioDevice *audio; 424 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
458 const char *env; 425 return -1;
459 426 }
460 /* Start up the audio driver, if necessary */ 427 if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
461 if (!current_audio) { 428 return 0;
462 if ((SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) || 429 }
463 (current_audio == NULL)) { 430
464 return (-1); 431 if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
465 } 432 return 1;
466 } 433 }
467 audio = current_audio; 434
468 435 if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
469 if (audio->opened) { 436 return 1;
470 SDL_SetError("Audio device is already opened"); 437 }
471 return (-1); 438
472 } 439 return current_audio.impl.DetectDevices(iscapture);
473 440 }
474 /* Verify some parameters */ 441
475 if (desired->freq == 0) { 442
476 env = SDL_getenv("SDL_AUDIO_FREQUENCY"); 443 const char *
477 if (env) { 444 SDL_GetAudioDeviceName(int index, int iscapture)
478 desired->freq = SDL_atoi(env); 445 {
479 } 446 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
480 } 447 SDL_SetError("Audio subsystem is not initialized");
481 if (desired->freq == 0) { 448 return NULL;
482 /* Pick some default audio frequency */ 449 }
483 desired->freq = 22050; 450
484 } 451 if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
485 if (desired->format == 0) { 452 SDL_SetError("No capture support");
486 env = SDL_getenv("SDL_AUDIO_FORMAT"); 453 return NULL;
487 if (env) { 454 }
488 desired->format = SDL_ParseAudioFormat(env); 455
489 } 456 if (index < 0) {
490 } 457 SDL_SetError("No such device");
491 if (desired->format == 0) { 458 return NULL;
492 /* Pick some default audio format */ 459 }
493 desired->format = AUDIO_S16; 460
494 } 461 if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
495 if (desired->channels == 0) { 462 return DEFAULT_INPUT_DEVNAME;
496 env = SDL_getenv("SDL_AUDIO_CHANNELS"); 463 }
497 if (env) { 464
498 desired->channels = (Uint8) SDL_atoi(env); 465 if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
499 } 466 return DEFAULT_OUTPUT_DEVNAME;
500 } 467 }
501 if (desired->channels == 0) { 468
502 /* Pick a default number of channels */ 469 return current_audio.impl.GetDeviceName(index, iscapture);
503 desired->channels = 2; 470 }
504 } 471
505 switch (desired->channels) { 472
473 static void
474 close_audio_device(SDL_AudioDevice *device)
475 {
476 device->enabled = 0;
477 if (device->thread != NULL) {
478 SDL_WaitThread(device->thread, NULL);
479 }
480 if (device->mixer_lock != NULL) {
481 SDL_DestroyMutex(device->mixer_lock);
482 }
483 if (device->fake_stream != NULL) {
484 SDL_FreeAudioMem(device->fake_stream);
485 }
486 if (device->convert.needed) {
487 SDL_FreeAudioMem(device->convert.buf);
488 }
489 if (device->opened) {
490 current_audio.impl.CloseDevice(device);
491 device->opened = 0;
492 }
493 SDL_FreeAudioMem(device);
494 }
495
496
497 /*
498 * Sanity check desired AudioSpec for SDL_OpenAudio() in (orig).
499 * Fills in a sanitized copy in (prepared).
500 * Returns non-zero if okay, zero on fatal parameters in (orig).
501 */
502 static int
503 prepare_audiospec(const SDL_AudioSpec *orig, SDL_AudioSpec *prepared)
504 {
505 SDL_memcpy(prepared, orig, sizeof (SDL_AudioSpec));
506
507 if (orig->callback == NULL) {
508 SDL_SetError("SDL_OpenAudio() passed a NULL callback");
509 return 0;
510 }
511
512 if (orig->freq == 0) {
513 const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY");
514 if ( (!env) || ((prepared->freq = SDL_atoi(env)) == 0) ) {
515 prepared->freq = 22050; /* a reasonable default */
516 }
517 }
518
519 if (orig->format == 0) {
520 const char *env = SDL_getenv("SDL_AUDIO_FORMAT");
521 if ((!env) || ((prepared->format = SDL_ParseAudioFormat(env)) == 0)) {
522 prepared->format = AUDIO_S16; /* a reasonable default */
523 }
524 }
525
526 switch (orig->channels) {
527 case 0: {
528 const char *env = SDL_getenv("SDL_AUDIO_CHANNELS");
529 if ( (!env) || ((prepared->channels = SDL_atoi(env)) == 0) ) {
530 prepared->channels = 2; /* a reasonable default */
531 }
532 break;
533 }
506 case 1: /* Mono */ 534 case 1: /* Mono */
507 case 2: /* Stereo */ 535 case 2: /* Stereo */
508 case 4: /* surround */ 536 case 4: /* surround */
509 case 6: /* surround with center and lfe */ 537 case 6: /* surround with center and lfe */
510 break; 538 break;
511 default: 539 default:
512 SDL_SetError("1 (mono) and 2 (stereo) channels supported"); 540 SDL_SetError("Unsupported number of audio channels.");
513 return (-1); 541 return 0;
514 } 542 }
515 if (desired->samples == 0) { 543
516 env = SDL_getenv("SDL_AUDIO_SAMPLES"); 544 if (orig->samples == 0) {
517 if (env) { 545 const char *env = SDL_getenv("SDL_AUDIO_SAMPLES");
518 desired->samples = (Uint16) SDL_atoi(env); 546 if ( (!env) || ((prepared->samples = (Uint16) SDL_atoi(env)) == 0) ) {
519 } 547 /* Pick a default of ~46 ms at desired frequency */
520 } 548 /* !!! FIXME: remove this when the non-Po2 resampling is in. */
521 if (desired->samples == 0) { 549 const int samples = (prepared->freq / 1000) * 46;
522 /* Pick a default of ~46 ms at desired frequency */ 550 int power2 = 1;
523 int samples = (desired->freq / 1000) * 46; 551 while (power2 < samples) {
524 int power2 = 1; 552 power2 *= 2;
525 while (power2 < samples) { 553 }
526 power2 *= 2; 554 prepared->samples = power2;
527 } 555 }
528 desired->samples = power2; 556 }
529 } 557
530 if (desired->callback == NULL) { 558 /* Calculate the silence and size of the audio specification */
531 SDL_SetError("SDL_OpenAudio() passed a NULL callback"); 559 SDL_CalculateAudioSpec(prepared);
532 return (-1); 560
533 } 561 return 1;
534 #if defined(__MINT__) && SDL_THREADS_DISABLED 562 }
535 /* Uses interrupt driven audio, without thread */ 563
536 #else 564
565 static SDL_AudioDeviceID
566 open_audio_device(const char *devname, int iscapture,
567 const SDL_AudioSpec *_desired, SDL_AudioSpec *obtained,
568 int min_id)
569 {
570 SDL_AudioDeviceID id = 0;
571 SDL_AudioSpec desired;
572 SDL_AudioDevice *device;
573 int i = 0;
574
575 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
576 SDL_SetError("Audio subsystem is not initialized");
577 return 0;
578 }
579
580 if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
581 SDL_SetError("No capture support");
582 return 0;
583 }
584
585 if (!prepare_audiospec(_desired, &desired)) {
586 return 0;
587 }
588
589 /* If app doesn't care about a specific device, let the user override. */
590 if (devname == NULL) {
591 devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME");
592 }
593
594 /*
595 * Catch device names at the high level for the simple case...
596 * This lets us have a basic "device enumeration" for systems that
597 * don't have multiple devices, but makes sure the device name is
598 * always NULL when it hits the low level.
599 *
600 * Also make sure that the simple case prevents multiple simultaneous
601 * opens of the default system device.
602 */
603
604 if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
605 if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
606 SDL_SetError("No such device");
607 return 0;
608 }
609 devname = NULL;
610
611 for (i = 0; i < SDL_arraysize(open_devices); i++) {
612 if ((open_devices[i]) && (open_devices[i]->iscapture)) {
613 SDL_SetError("Audio device already open");
614 return 0;
615 }
616 }
617 }
618
619 if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
620 if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
621 SDL_SetError("No such device");
622 return 0;
623 }
624 devname = NULL;
625
626 for (i = 0; i < SDL_arraysize(open_devices); i++) {
627 if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
628 SDL_SetError("Audio device already open");
629 return 0;
630 }
631 }
632 }
633
634 device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof (SDL_AudioDevice));
635 if (device == NULL) {
636 SDL_OutOfMemory();
637 return 0;
638 }
639 SDL_memset(device, '\0', sizeof (SDL_AudioDevice));
640 SDL_memcpy(&device->spec, &desired, sizeof (SDL_AudioSpec));
641 device->enabled = 1;
642 device->paused = 1;
643 device->iscapture = iscapture;
644
537 /* Create a semaphore for locking the sound buffers */ 645 /* Create a semaphore for locking the sound buffers */
538 audio->mixer_lock = SDL_CreateMutex(); 646 if (!current_audio.impl.SkipMixerLock) {
539 if (audio->mixer_lock == NULL) { 647 device->mixer_lock = SDL_CreateMutex();
540 SDL_SetError("Couldn't create mixer lock"); 648 if (device->mixer_lock == NULL) {
541 SDL_CloseAudio(); 649 close_audio_device(device);
542 return (-1); 650 SDL_SetError("Couldn't create mixer lock");
543 } 651 return 0;
544 #endif /* __MINT__ */ 652 }
545 653 }
546 /* Calculate the silence and size of the audio specification */ 654
547 SDL_CalculateAudioSpec(desired); 655 if (!current_audio.impl.OpenDevice(device, devname, iscapture)) {
548 656 close_audio_device(device);
549 /* Open the audio subsystem */ 657 return 0;
550 SDL_memcpy(&audio->spec, desired, sizeof(audio->spec)); 658 }
551 audio->convert.needed = 0; 659 device->opened = 1;
552 audio->enabled = 1;
553 audio->paused = 1;
554
555 #if !SDL_AUDIO_DRIVER_AHI
556
557 /* AmigaOS opens audio inside the main loop */
558 audio->opened = audio->OpenAudio(audio, &audio->spec) + 1;
559
560 if (!audio->opened) {
561 SDL_CloseAudio();
562 return (-1);
563 }
564 #else
565 D(bug("Locking semaphore..."));
566 SDL_mutexP(audio->mixer_lock);
567
568
569 audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
570 D(bug("Created thread...\n"));
571
572 if (audio->thread == NULL) {
573 SDL_mutexV(audio->mixer_lock);
574 SDL_CloseAudio();
575 SDL_SetError("Couldn't create audio thread");
576 return (-1);
577 }
578
579 while (!audio_configured)
580 SDL_Delay(100);
581 #endif
582 660
583 /* If the audio driver changes the buffer size, accept it */ 661 /* If the audio driver changes the buffer size, accept it */
584 if (audio->spec.samples != desired->samples) { 662 if (device->spec.samples != desired.samples) {
585 desired->samples = audio->spec.samples; 663 desired.samples = device->spec.samples;
586 SDL_CalculateAudioSpec(desired); 664 SDL_CalculateAudioSpec(&device->spec);
587 } 665 }
588 666
589 /* Allocate a fake audio memory buffer */ 667 /* Allocate a fake audio memory buffer */
590 audio->fake_stream = SDL_AllocAudioMem(audio->spec.size); 668 device->fake_stream = SDL_AllocAudioMem(device->spec.size);
591 if (audio->fake_stream == NULL) { 669 if (device->fake_stream == NULL) {
592 SDL_CloseAudio(); 670 close_audio_device(device);
593 SDL_OutOfMemory(); 671 SDL_OutOfMemory();
594 return (-1); 672 return 0;
595 } 673 }
596 674
597 /* See if we need to do any conversion */ 675 /* See if we need to do any conversion */
598 if (obtained != NULL) { 676 if (obtained != NULL) {
599 SDL_memcpy(obtained, &audio->spec, sizeof(audio->spec)); 677 SDL_memcpy(obtained, &device->spec, sizeof(SDL_AudioSpec));
600 } else if (desired->freq != audio->spec.freq || 678 } else if (desired.freq != device->spec.freq ||
601 desired->format != audio->spec.format || 679 desired.format != device->spec.format ||
602 desired->channels != audio->spec.channels) { 680 desired.channels != device->spec.channels) {
603 /* Build an audio conversion block */ 681 /* Build an audio conversion block */
604 if (SDL_BuildAudioCVT(&audio->convert, 682 if (SDL_BuildAudioCVT(&device->convert,
605 desired->format, desired->channels, 683 desired.format, desired.channels,
606 desired->freq, 684 desired.freq,
607 audio->spec.format, audio->spec.channels, 685 device->spec.format, device->spec.channels,
608 audio->spec.freq) < 0) { 686 device->spec.freq) < 0) {
609 SDL_CloseAudio(); 687 close_audio_device(device);
610 return (-1); 688 return 0;
611 } 689 }
612 if (audio->convert.needed) { 690 if (device->convert.needed) {
613 audio->convert.len = desired->size; 691 device->convert.len = desired.size;
614 audio->convert.buf = 692 device->convert.buf =
615 (Uint8 *) SDL_AllocAudioMem(audio->convert.len * 693 (Uint8 *) SDL_AllocAudioMem(device->convert.len *
616 audio->convert.len_mult); 694 device->convert.len_mult);
617 if (audio->convert.buf == NULL) { 695 if (device->convert.buf == NULL) {
618 SDL_CloseAudio(); 696 close_audio_device(device);
619 SDL_OutOfMemory(); 697 SDL_OutOfMemory();
620 return (-1); 698 return 0;
621 } 699 }
622 } 700 }
623 } 701 }
624 #if !SDL_AUDIO_DRIVER_AHI 702
703 /* Find an available device ID and store the structure... */
704 for (id = min_id-1; id < SDL_arraysize(open_devices); id++) {
705 if (open_devices[id] == NULL) {
706 open_devices[id] = device;
707 break;
708 }
709 }
710
711 if (id == SDL_arraysize(open_devices)) {
712 SDL_SetError("Too many open audio devices");
713 close_audio_device(device);
714 return 0;
715 }
716
625 /* Start the audio thread if necessary */ 717 /* Start the audio thread if necessary */
626 switch (audio->opened) { 718 if (!current_audio.impl.ProvidesOwnCallbackThread) {
627 case 1:
628 /* Start the audio thread */ 719 /* Start the audio thread */
720 /* !!! FIXME: this is nasty. */
629 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC) 721 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
630 #undef SDL_CreateThread 722 #undef SDL_CreateThread
631 audio->thread = SDL_CreateThread(SDL_RunAudio, audio, NULL, NULL); 723 device->thread = SDL_CreateThread(SDL_RunAudio, device, NULL, NULL);
632 #else 724 #else
633 audio->thread = SDL_CreateThread(SDL_RunAudio, audio); 725 device->thread = SDL_CreateThread(SDL_RunAudio, device);
634 #endif 726 #endif
635 if (audio->thread == NULL) { 727 if (device->thread == NULL) {
636 SDL_CloseAudio(); 728 SDL_CloseAudioDevice(id+1);
637 SDL_SetError("Couldn't create audio thread"); 729 SDL_SetError("Couldn't create audio thread");
730 return 0;
731 }
732 }
733
734 return id+1;
735 }
736
737
738 int
739 SDL_OpenAudio(const SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
740 {
741 SDL_AudioDeviceID id = 0;
742
743 /* Start up the audio driver, if necessary. This is legacy behaviour! */
744 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
745 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
638 return (-1); 746 return (-1);
639 } 747 }
640 break; 748 }
641 749
642 default: 750 /* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
643 /* The audio is now playing */ 751 if (open_devices[0] != NULL) {
644 break; 752 SDL_SetError("Audio device is already opened");
645 } 753 return (-1);
646 #else 754 }
647 SDL_mutexV(audio->mixer_lock); 755
648 D(bug("SDL_OpenAudio USCITA...\n")); 756 id = open_audio_device(NULL, 0, desired, obtained, 1);
649 757 if (id > 1) { /* this should never happen in theory... */
650 #endif 758 SDL_CloseAudioDevice(id);
651 759 SDL_SetError("Internal error"); /* MUST be Device ID #1! */
652 return (0); 760 return (-1);
761 }
762
763 return ((id == 0) ? -1 : 0);
764 }
765
766 SDL_AudioDeviceID
767 SDL_OpenAudioDevice(const char *device, int iscapture,
768 const SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
769 {
770 return open_audio_device(device, iscapture, desired, obtained, 2);
653 } 771 }
654 772
655 SDL_audiostatus 773 SDL_audiostatus
656 SDL_GetAudioStatus(void) 774 SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
657 { 775 {
658 SDL_AudioDevice *audio = current_audio; 776 SDL_AudioDevice *device = get_audio_device(devid);
659 SDL_audiostatus status; 777 SDL_audiostatus status = SDL_AUDIO_STOPPED;
660 778 if (device && device->enabled) {
661 status = SDL_AUDIO_STOPPED; 779 if (device->paused) {
662 if (audio && audio->enabled) {
663 if (audio->paused) {
664 status = SDL_AUDIO_PAUSED; 780 status = SDL_AUDIO_PAUSED;
665 } else { 781 } else {
666 status = SDL_AUDIO_PLAYING; 782 status = SDL_AUDIO_PLAYING;
667 } 783 }
668 } 784 }
669 return (status); 785 return (status);
670 } 786 }
671 787
788
789 SDL_audiostatus
790 SDL_GetAudioStatus(void)
791 {
792 return SDL_GetAudioDeviceStatus(1);
793 }
794
795 void
796 SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
797 {
798 SDL_AudioDevice *device = get_audio_device(devid);
799 if (device) {
800 device->paused = pause_on;
801 }
802 }
803
672 void 804 void
673 SDL_PauseAudio(int pause_on) 805 SDL_PauseAudio(int pause_on)
674 { 806 {
675 SDL_AudioDevice *audio = current_audio; 807 SDL_PauseAudioDevice(1, pause_on);
676 808 }
677 if (audio) { 809
678 audio->paused = pause_on; 810
811 void
812 SDL_LockAudioDevice(SDL_AudioDeviceID devid)
813 {
814 /* Obtain a lock on the mixing buffers */
815 SDL_AudioDevice *device = get_audio_device(devid);
816 if (device) {
817 current_audio.impl.LockDevice(device);
679 } 818 }
680 } 819 }
681 820
682 void 821 void
683 SDL_LockAudio(void) 822 SDL_LockAudio(void)
684 { 823 {
685 SDL_AudioDevice *audio = current_audio; 824 SDL_LockAudioDevice(1);
686 825 }
826
827 void
828 SDL_UnlockAudioDevice(SDL_AudioDeviceID devid)
829 {
687 /* Obtain a lock on the mixing buffers */ 830 /* Obtain a lock on the mixing buffers */
688 if (audio && audio->LockAudio) { 831 SDL_AudioDevice *device = get_audio_device(devid);
689 audio->LockAudio(audio); 832 if (device) {
833 current_audio.impl.UnlockDevice(device);
690 } 834 }
691 } 835 }
692 836
693 void 837 void
694 SDL_UnlockAudio(void) 838 SDL_UnlockAudio(void)
695 { 839 {
696 SDL_AudioDevice *audio = current_audio; 840 SDL_UnlockAudioDevice(1);
697 841 }
698 /* Release lock on the mixing buffers */ 842
699 if (audio && audio->UnlockAudio) { 843 void
700 audio->UnlockAudio(audio); 844 SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
845 {
846 SDL_AudioDevice *device = get_audio_device(devid);
847 if (device) {
848 close_audio_device(device);
849 open_devices[devid-1] = NULL;
701 } 850 }
702 } 851 }
703 852
704 void 853 void
705 SDL_CloseAudio(void) 854 SDL_CloseAudio(void)
706 { 855 {
707 SDL_QuitSubSystem(SDL_INIT_AUDIO); 856 SDL_CloseAudioDevice(1);
708 } 857 }
709 858
710 void 859 void
711 SDL_AudioQuit(void) 860 SDL_AudioQuit(void)
712 { 861 {
713 SDL_AudioDevice *audio = current_audio; 862 SDL_AudioDeviceID i;
714 863 for (i = 0; i < SDL_arraysize(open_devices); i++) {
715 if (audio) { 864 SDL_CloseAudioDevice(i);
716 audio->enabled = 0; 865 }
717 if (audio->thread != NULL) { 866
718 SDL_WaitThread(audio->thread, NULL); 867 /* Free the driver data */
719 } 868 current_audio.impl.Deinitialize();
720 if (audio->mixer_lock != NULL) { 869 SDL_memset(&current_audio, '\0', sizeof (current_audio));
721 SDL_DestroyMutex(audio->mixer_lock); 870 SDL_memset(open_devices, '\0', sizeof (open_devices));
722 }
723 if (audio->fake_stream != NULL) {
724 SDL_FreeAudioMem(audio->fake_stream);
725 }
726 if (audio->convert.needed) {
727 SDL_FreeAudioMem(audio->convert.buf);
728
729 }
730 #if !SDL_AUDIO_DRIVER_AHI
731 if (audio->opened) {
732 audio->CloseAudio(audio);
733 audio->opened = 0;
734 }
735 #endif
736 /* Free the driver data */
737 audio->free(audio);
738 current_audio = NULL;
739 }
740 } 871 }
741 872
742 #define NUM_FORMATS 10 873 #define NUM_FORMATS 10
743 static int format_idx; 874 static int format_idx;
744 static int format_idx_sub; 875 static int format_idx_sub;
795 break; 926 break;
796 default: 927 default:
797 spec->silence = 0x00; 928 spec->silence = 0x00;
798 break; 929 break;
799 } 930 }
800 spec->size = (spec->format & 0xFF) / 8; 931 spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
801 spec->size *= spec->channels; 932 spec->size *= spec->channels;
802 spec->size *= spec->samples; 933 spec->size *= spec->samples;
803 } 934 }
804 935
936
937 /*
938 * Moved here from SDL_mixer.c, since it relies on internals of an opened
939 * audio device (and is deprecated, by the way!).
940 */
941 void
942 SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
943 {
944 /* Mix the user-level audio format */
945 SDL_AudioDevice *device = get_audio_device(1);
946 if (device != NULL) {
947 SDL_AudioFormat format;
948 if (device->convert.needed) {
949 format = device->convert.src_format;
950 } else {
951 format = device->spec.format;
952 }
953 SDL_MixAudioFormat(dst, src, format, len, volume);
954 }
955 }
956
805 /* vi: set ts=4 sw=4 expandtab: */ 957 /* vi: set ts=4 sw=4 expandtab: */