comparison src/audio/dma/SDL_dmaaudio.c @ 3795:589bc3d060cd SDL-ryan-multiple-audio-device

More 1.3 audio work...moved dsp and dma drivers over to new model. Untested!
author Ryan C. Gordon <icculus@icculus.org>
date Wed, 04 Oct 2006 06:00:10 +0000
parents 3b4ce57c6215
children c8b3d3d13ed1
comparison
equal deleted inserted replaced
3794:db24e43972ac 3795:589bc3d060cd
18 18
19 Sam Lantinga 19 Sam Lantinga
20 slouken@libsdl.org 20 slouken@libsdl.org
21 */ 21 */
22 #include "SDL_config.h" 22 #include "SDL_config.h"
23
24 /* !!! FIXME: merge this driver with "dsp". */
23 25
24 /* Allow access to a raw mixing buffer */ 26 /* Allow access to a raw mixing buffer */
25 27
26 #include <stdio.h> 28 #include <stdio.h>
27 #include <string.h> /* For strerror() */ 29 #include <string.h> /* For strerror() */
58 60
59 /* Open the audio device for playback, and don't block if busy */ 61 /* Open the audio device for playback, and don't block if busy */
60 #define OPEN_FLAGS (O_RDWR|O_NONBLOCK) 62 #define OPEN_FLAGS (O_RDWR|O_NONBLOCK)
61 63
62 /* Audio driver functions */ 64 /* Audio driver functions */
63 static int DMA_OpenAudio(_THIS, SDL_AudioSpec * spec); 65 static int DMA_DetectDevices(int iscapture);
64 static void DMA_WaitAudio(_THIS); 66 static const char *DMA_GetDeviceName(int index, int iscapture);
65 static void DMA_PlayAudio(_THIS); 67 static int DMA_OpenDevice(_THIS, const char *devname, int iscapture);
66 static Uint8 *DMA_GetAudioBuf(_THIS); 68 static void DMA_WaitDevice(_THIS);
67 static void DMA_CloseAudio(_THIS); 69 static void DMA_PlayDevice(_THIS);
70 static Uint8 *DMA_GetDeviceBuf(_THIS);
71 static void DMA_CloseDevice(_THIS);
68 72
69 /* Audio driver bootstrap functions */ 73 /* Audio driver bootstrap functions */
70 74
71 static int 75 static int
72 Audio_Available(void) 76 DMA_Available(void)
73 { 77 {
78 /*
79 * !!! FIXME: maybe change this to always available, and move this to
80 * !!! FIXME: to device enumeration and opening?
81 */
74 int available; 82 int available;
75 int fd; 83 int fd;
76 84
77 available = 0; 85 available = 0;
78 86
89 close(fd); 97 close(fd);
90 } 98 }
91 return (available); 99 return (available);
92 } 100 }
93 101
94 static void 102
95 Audio_DeleteDevice(SDL_AudioDevice * device) 103 static int
96 { 104 DMA_Init(SDL_AudioDriverImpl *impl)
97 SDL_free(device->hidden); 105 {
98 SDL_free(device);
99 }
100
101 static SDL_AudioDevice *
102 Audio_CreateDevice(int devindex)
103 {
104 SDL_AudioDevice *this;
105
106 /* Initialize all variables that we clean on shutdown */
107 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
108 if (this) {
109 SDL_memset(this, 0, (sizeof *this));
110 this->hidden = (struct SDL_PrivateAudioData *)
111 SDL_malloc((sizeof *this->hidden));
112 }
113 if ((this == NULL) || (this->hidden == NULL)) {
114 SDL_OutOfMemory();
115 if (this) {
116 SDL_free(this);
117 }
118 return (0);
119 }
120 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
121 audio_fd = -1;
122
123 /* Set the function pointers */ 106 /* Set the function pointers */
124 this->OpenAudio = DMA_OpenAudio; 107 impl->DetectDevices = DMA_DetectDevices;
125 this->WaitAudio = DMA_WaitAudio; 108 impl->GetDeviceName = DMA_GetDeviceName;
126 this->PlayAudio = DMA_PlayAudio; 109 impl->OpenDevice = DMA_OpenDevice;
127 this->GetAudioBuf = DMA_GetAudioBuf; 110 impl->WaitDevice = DMA_WaitDevice;
128 this->CloseAudio = DMA_CloseAudio; 111 impl->PlayDevice = DMA_PlayDevice;
129 112 impl->GetDeviceBuf = DMA_GetDeviceBuf;
130 this->free = Audio_DeleteDevice; 113 impl->CloseDevice = DMA_CloseDevice;
131 114
132 return this; 115 return 1;
133 } 116 }
134 117
135 AudioBootStrap DMA_bootstrap = { 118 AudioBootStrap DMA_bootstrap = {
136 DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio", 119 DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio",
137 Audio_Available, Audio_CreateDevice 120 DMA_Available, DMA_Init
138 }; 121 };
122
123
124 static int
125 DMA_DetectDevices(int iscapture)
126 {
127 return -1; /* !!! FIXME */
128 }
129
130
131 static const char *
132 DMA_GetDeviceName(int index, int iscapture)
133 {
134 SDL_SetError("No such device"); /* !!! FIXME */
135 return NULL;
136 }
137
138
139 static int
140 DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo)
141 {
142 int frag_spec;
143 int value;
144
145 /* Close and then reopen the audio device */
146 close(audio_fd);
147 audio_fd = open(audiodev, O_RDWR, 0);
148 if (audio_fd < 0) {
149 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
150 return (-1);
151 }
152
153 /* Calculate the final parameters for this audio specification */
154 SDL_CalculateAudioSpec(&this->spec);
155
156 /* Determine the power of two of the fragment size */
157 for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
158 if ((0x01 << frag_spec) != this->spec.size) {
159 SDL_SetError("Fragment size must be a power of two");
160 return (-1);
161 }
162
163 /* Set the audio buffering parameters */
164 if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
165 SDL_SetError("Couldn't set audio fragment spec");
166 return (-1);
167 }
168
169 /* Set the audio format */
170 value = format;
171 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) {
172 SDL_SetError("Couldn't set audio format");
173 return (-1);
174 }
175
176 /* Set mono or stereo audio */
177 value = (this->spec.channels > 1);
178 if ((ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) ||
179 (value != stereo)) {
180 SDL_SetError("Couldn't set audio channels");
181 return (-1);
182 }
183
184 /* Set the DSP frequency */
185 value = this->spec.freq;
186 if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
187 SDL_SetError("Couldn't set audio frequency");
188 return (-1);
189 }
190 this->spec.freq = value;
191
192 /* We successfully re-opened the audio */
193 return (0);
194 }
195
196
197
198 static int
199 open_device_internal(_THIS, const char *devname, int iscapture)
200 {
201 char audiodev[1024];
202 int format;
203 int stereo;
204 int value;
205 SDL_AudioFormat test_format;
206 struct audio_buf_info info;
207
208 /* Initialize all variables that we clean on shutdown */
209 this->hidden = (struct SDL_PrivateAudioData *)
210 SDL_malloc((sizeof *this->hidden));
211 if (this->hidden == NULL) {
212 SDL_OutOfMemory();
213 return 0;
214 }
215 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
216
217 /* !!! FIXME: handle devname */
218 /* !!! FIXME: handle iscapture */
219
220 /* Open the audio device */
221 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
222 if (audio_fd < 0) {
223 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
224 return 0;
225 }
226 dma_buf = NULL;
227 ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
228
229 /* Get a list of supported hardware formats */
230 if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
231 SDL_SetError("Couldn't get audio format list");
232 return 0;
233 }
234
235 /* Try for a closest match on audio format */
236 format = 0;
237 for (test_format = SDL_FirstAudioFormat(this->spec.format);
238 !format && test_format;) {
239 #ifdef DEBUG_AUDIO
240 fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
241 #endif
242 switch (test_format) {
243 case AUDIO_U8:
244 if (value & AFMT_U8) {
245 format = AFMT_U8;
246 }
247 break;
248 case AUDIO_S8:
249 if (value & AFMT_S8) {
250 format = AFMT_S8;
251 }
252 break;
253 case AUDIO_S16LSB:
254 if (value & AFMT_S16_LE) {
255 format = AFMT_S16_LE;
256 }
257 break;
258 case AUDIO_S16MSB:
259 if (value & AFMT_S16_BE) {
260 format = AFMT_S16_BE;
261 }
262 break;
263 case AUDIO_U16LSB:
264 if (value & AFMT_U16_LE) {
265 format = AFMT_U16_LE;
266 }
267 break;
268 case AUDIO_U16MSB:
269 if (value & AFMT_U16_BE) {
270 format = AFMT_U16_BE;
271 }
272 break;
273 default:
274 format = 0;
275 break;
276 }
277 if (!format) {
278 test_format = SDL_NextAudioFormat();
279 }
280 }
281 if (format == 0) {
282 SDL_SetError("Couldn't find any hardware audio formats");
283 return 0;
284 }
285 this->spec.format = test_format;
286
287 /* Set the audio format */
288 value = format;
289 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) {
290 SDL_SetError("Couldn't set audio format");
291 return 0;
292 }
293
294 /* Set mono or stereo audio (currently only two channels supported) */
295 stereo = (this->spec.channels > 1);
296 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
297 if (stereo) {
298 this->spec.channels = 2;
299 } else {
300 this->spec.channels = 1;
301 }
302
303 /* Because some drivers don't allow setting the buffer size
304 after setting the format, we must re-open the audio device
305 once we know what format and channels are supported
306 */
307 if (DMA_ReopenAudio(this, audiodev, format, stereo) < 0) {
308 /* Error is set by DMA_ReopenAudio() */
309 return 0;
310 }
311
312 /* Memory map the audio buffer */
313 if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
314 SDL_SetError("Couldn't get OSPACE parameters");
315 return 0;
316 }
317 this->spec.size = info.fragsize;
318 this->spec.samples = this->spec.size / ((this->spec.format & 0xFF) / 8);
319 this->spec.samples /= this->spec.channels;
320 num_buffers = info.fragstotal;
321 dma_len = num_buffers * this->spec.size;
322 dma_buf = (Uint8 *) mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED,
323 audio_fd, 0);
324 if (dma_buf == MAP_FAILED) {
325 SDL_SetError("DMA memory map failed");
326 dma_buf = NULL;
327 return 0;
328 }
329 SDL_memset(dma_buf, this->spec.silence, dma_len);
330
331 /* Check to see if we need to use select() workaround */
332 {
333 char *workaround;
334 workaround = SDL_getenv("SDL_DSP_NOSELECT");
335 if (workaround) {
336 frame_ticks = (float) (this->spec.samples*1000) / this->spec.freq;
337 next_frame = SDL_GetTicks() + frame_ticks;
338 }
339 }
340
341 /* Trigger audio playback */
342 value = 0;
343 ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value);
344 value = PCM_ENABLE_OUTPUT;
345 if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0) {
346 SDL_SetError("Couldn't trigger audio output");
347 return 0;
348 }
349
350 /* Get the parent process id (we're the parent of the audio thread) */
351 parent = getpid();
352
353 /* We're ready to rock and roll. :-) */
354 return 1;
355 }
356
357 static int
358 DMA_OpenDevice(_THIS, const char *devname, int iscapture)
359 {
360 int retval = open_device_internal(this, devname, iscapture);
361 if (!retval)
362 DMA_CloseDevice(this); /* !!! FIXME: do this at higher level. */
363 return retval;
364 }
365
366
367
139 368
140 /* This function waits until it is possible to write a full sound buffer */ 369 /* This function waits until it is possible to write a full sound buffer */
141 static void 370 static void
142 DMA_WaitAudio(_THIS) 371 DMA_WaitDevice(_THIS)
143 { 372 {
144 fd_set fdset; 373 fd_set fdset;
145 374
146 /* Check to see if the thread-parent process is still alive */ 375 /* Check to see if the thread-parent process is still alive */
147 { 376 {
187 the user know what happened. 416 the user know what happened.
188 */ 417 */
189 fprintf(stderr, "SDL: %s\n", message); 418 fprintf(stderr, "SDL: %s\n", message);
190 #ifdef AUDIO_OSPACE_HACK 419 #ifdef AUDIO_OSPACE_HACK
191 /* We may be able to use GET_OSPACE trick */ 420 /* We may be able to use GET_OSPACE trick */
192 frame_ticks = (float) (this->spec->samples * 1000) / 421 frame_ticks = (float) (this->spec.samples * 1000) /
193 this->spec->freq; 422 this->spec.freq;
194 next_frame = SDL_GetTicks() + frame_ticks; 423 next_frame = SDL_GetTicks() + frame_ticks;
195 #else 424 #else
196 this->enabled = 0; 425 this->enabled = 0;
197 /* Don't try to close - may hang */ 426 /* Don't try to close - may hang */
198 audio_fd = -1; 427 audio_fd = -1;
206 #endif 435 #endif
207 } 436 }
208 } 437 }
209 438
210 static void 439 static void
211 DMA_PlayAudio(_THIS) 440 DMA_PlayDevice(_THIS)
212 { 441 {
213 /* If timer synchronization is enabled, set the next write frame */ 442 /* If timer synchronization is enabled, set the next write frame */
214 if (frame_ticks) { 443 if (frame_ticks) {
215 next_frame += frame_ticks; 444 next_frame += frame_ticks;
216 } 445 }
217 return; 446 return;
218 } 447 }
219 448
220 static Uint8 * 449 static Uint8 *
221 DMA_GetAudioBuf(_THIS) 450 DMA_GetDeviceBuf(_THIS)
222 { 451 {
223 count_info info; 452 count_info info;
224 int playing; 453 int playing;
225 int filling; 454 int filling;
226 455
242 filling = (playing + 1) % num_buffers; 471 filling = (playing + 1) % num_buffers;
243 return (dma_buf + (filling * this->spec.size)); 472 return (dma_buf + (filling * this->spec.size));
244 } 473 }
245 474
246 static void 475 static void
247 DMA_CloseAudio(_THIS) 476 DMA_CloseDevice(_THIS)
248 { 477 {
249 if (dma_buf != NULL) { 478 if (this->hidden != NULL) {
250 munmap(dma_buf, dma_len); 479 if (dma_buf != NULL) {
251 dma_buf = NULL; 480 munmap(dma_buf, dma_len);
252 } 481 dma_buf = NULL;
253 if (audio_fd >= 0) { 482 }
254 close(audio_fd); 483 if (audio_fd >= 0) {
255 audio_fd = -1; 484 close(audio_fd);
256 } 485 audio_fd = -1;
257 } 486 }
258 487 SDL_free(this->hidden);
259 static int 488 this->hidden = NULL;
260 DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo, 489 }
261 SDL_AudioSpec * spec)
262 {
263 int frag_spec;
264 int value;
265
266 /* Close and then reopen the audio device */
267 close(audio_fd);
268 audio_fd = open(audiodev, O_RDWR, 0);
269 if (audio_fd < 0) {
270 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
271 return (-1);
272 }
273
274 /* Calculate the final parameters for this audio specification */
275 SDL_CalculateAudioSpec(spec);
276
277 /* Determine the power of two of the fragment size */
278 for (frag_spec = 0; (0x01 << frag_spec) < spec->size; ++frag_spec);
279 if ((0x01 << frag_spec) != spec->size) {
280 SDL_SetError("Fragment size must be a power of two");
281 return (-1);
282 }
283
284 /* Set the audio buffering parameters */
285 if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
286 SDL_SetError("Couldn't set audio fragment spec");
287 return (-1);
288 }
289
290 /* Set the audio format */
291 value = format;
292 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) {
293 SDL_SetError("Couldn't set audio format");
294 return (-1);
295 }
296
297 /* Set mono or stereo audio */
298 value = (spec->channels > 1);
299 if ((ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) ||
300 (value != stereo)) {
301 SDL_SetError("Couldn't set audio channels");
302 return (-1);
303 }
304
305 /* Set the DSP frequency */
306 value = spec->freq;
307 if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
308 SDL_SetError("Couldn't set audio frequency");
309 return (-1);
310 }
311 spec->freq = value;
312
313 /* We successfully re-opened the audio */
314 return (0);
315 }
316
317 static int
318 DMA_OpenAudio(_THIS, SDL_AudioSpec * spec)
319 {
320 char audiodev[1024];
321 int format;
322 int stereo;
323 int value;
324 SDL_AudioFormat test_format;
325 struct audio_buf_info info;
326
327 /* Reset the timer synchronization flag */
328 frame_ticks = 0.0;
329
330 /* Open the audio device */
331 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
332 if (audio_fd < 0) {
333 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
334 return (-1);
335 }
336 dma_buf = NULL;
337 ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
338
339 /* Get a list of supported hardware formats */
340 if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
341 SDL_SetError("Couldn't get audio format list");
342 return (-1);
343 }
344
345 /* Try for a closest match on audio format */
346 format = 0;
347 for (test_format = SDL_FirstAudioFormat(spec->format);
348 !format && test_format;) {
349 #ifdef DEBUG_AUDIO
350 fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
351 #endif
352 switch (test_format) {
353 case AUDIO_U8:
354 if (value & AFMT_U8) {
355 format = AFMT_U8;
356 }
357 break;
358 case AUDIO_S8:
359 if (value & AFMT_S8) {
360 format = AFMT_S8;
361 }
362 break;
363 case AUDIO_S16LSB:
364 if (value & AFMT_S16_LE) {
365 format = AFMT_S16_LE;
366 }
367 break;
368 case AUDIO_S16MSB:
369 if (value & AFMT_S16_BE) {
370 format = AFMT_S16_BE;
371 }
372 break;
373 case AUDIO_U16LSB:
374 if (value & AFMT_U16_LE) {
375 format = AFMT_U16_LE;
376 }
377 break;
378 case AUDIO_U16MSB:
379 if (value & AFMT_U16_BE) {
380 format = AFMT_U16_BE;
381 }
382 break;
383 default:
384 format = 0;
385 break;
386 }
387 if (!format) {
388 test_format = SDL_NextAudioFormat();
389 }
390 }
391 if (format == 0) {
392 SDL_SetError("Couldn't find any hardware audio formats");
393 return (-1);
394 }
395 spec->format = test_format;
396
397 /* Set the audio format */
398 value = format;
399 if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) {
400 SDL_SetError("Couldn't set audio format");
401 return (-1);
402 }
403
404 /* Set mono or stereo audio (currently only two channels supported) */
405 stereo = (spec->channels > 1);
406 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
407 if (stereo) {
408 spec->channels = 2;
409 } else {
410 spec->channels = 1;
411 }
412
413 /* Because some drivers don't allow setting the buffer size
414 after setting the format, we must re-open the audio device
415 once we know what format and channels are supported
416 */
417 if (DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0) {
418 /* Error is set by DMA_ReopenAudio() */
419 return (-1);
420 }
421
422 /* Memory map the audio buffer */
423 if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
424 SDL_SetError("Couldn't get OSPACE parameters");
425 return (-1);
426 }
427 spec->size = info.fragsize;
428 spec->samples = spec->size / ((spec->format & 0xFF) / 8);
429 spec->samples /= spec->channels;
430 num_buffers = info.fragstotal;
431 dma_len = num_buffers * spec->size;
432 dma_buf = (Uint8 *) mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED,
433 audio_fd, 0);
434 if (dma_buf == MAP_FAILED) {
435 SDL_SetError("DMA memory map failed");
436 dma_buf = NULL;
437 return (-1);
438 }
439 SDL_memset(dma_buf, spec->silence, dma_len);
440
441 /* Check to see if we need to use select() workaround */
442 {
443 char *workaround;
444 workaround = SDL_getenv("SDL_DSP_NOSELECT");
445 if (workaround) {
446 frame_ticks = (float) (spec->samples * 1000) / spec->freq;
447 next_frame = SDL_GetTicks() + frame_ticks;
448 }
449 }
450
451 /* Trigger audio playback */
452 value = 0;
453 ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value);
454 value = PCM_ENABLE_OUTPUT;
455 if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0) {
456 SDL_SetError("Couldn't trigger audio output");
457 return (-1);
458 }
459
460 /* Get the parent process id (we're the parent of the audio thread) */
461 parent = getpid();
462
463 /* We're ready to rock and roll. :-) */
464 return (0);
465 } 490 }
466 491
467 /* vi: set ts=4 sw=4 expandtab: */ 492 /* vi: set ts=4 sw=4 expandtab: */