comparison src/audio/bsd/SDL_bsdaudio.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
59 59
60 /* #define DEBUG_AUDIO */ 60 /* #define DEBUG_AUDIO */
61 /* #define DEBUG_AUDIO_STREAM */ 61 /* #define DEBUG_AUDIO_STREAM */
62 62
63 #ifdef USE_BLOCKING_WRITES 63 #ifdef USE_BLOCKING_WRITES
64 #define OPEN_FLAGS O_WRONLY 64 #define OPEN_FLAGS_OUTPUT O_WRONLY
65 #define OPEN_FLAGS_INPUT O_RDONLY
65 #else 66 #else
66 #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) 67 #define OPEN_FLAGS_OUTPUT (O_WRONLY|O_NONBLOCK)
67 #endif 68 #define OPEN_FLAGS_INPUT (O_RDONLY|O_NONBLOCK)
68 69 #endif
69 /* Audio driver functions */ 70
70 static void OBSD_WaitAudio(_THIS); 71 /* !!! FIXME: so much cut and paste with dsp/dma drivers... */
71 static int OBSD_OpenAudio(_THIS, SDL_AudioSpec * spec); 72 static char **outputDevices = NULL;
72 static void OBSD_PlayAudio(_THIS); 73 static int outputDeviceCount = 0;
73 static Uint8 *OBSD_GetAudioBuf(_THIS); 74 static char **inputDevices = NULL;
74 static void OBSD_CloseAudio(_THIS); 75 static int inputDeviceCount = 0;
75 76
77 static inline void
78 free_device_list(char ***devs, int *count)
79 {
80 SDL_FreeUnixAudioDevices(devs, count);
81 }
82
83 static inline void
84 build_device_list(int iscapture, char ***devs, int *count)
85 {
86 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
87 free_device_list(devs, count);
88 SDL_EnumUnixAudioDevices(flags, 0, NULL, devs, count);
89 }
90
91 static inline void
92 build_device_lists(void)
93 {
94 build_device_list(0, &outputDevices, &outputDeviceCount);
95 build_device_list(1, &inputDevices, &inputDeviceCount);
96 }
97
98
99 static inline void
100 free_device_lists(void)
101 {
102 free_device_list(&outputDevices, &outputDeviceCount);
103 free_device_list(&inputDevices, &inputDeviceCount);
104 }
105
106
107 static void
108 BSDAUDIO_Deinitialize(void)
109 {
110 free_device_lists();
111 }
112
113
114 static int
115 BSDAUDIO_DetectDevices(int iscapture)
116 {
117 if (iscapture) {
118 build_device_list(1, &inputDevices, &inputDeviceCount);
119 return inputDeviceCount;
120 } else {
121 build_device_list(0, &outputDevices, &outputDeviceCount);
122 return outputDeviceCount;
123 }
124
125 return 0; /* shouldn't ever hit this. */
126 }
127
128 static const char *
129 BSDAUDIO_GetDeviceName(int index, int iscapture)
130 {
131 if ((iscapture) && (index < inputDeviceCount)) {
132 return inputDevices[index];
133 } else if ((!iscapture) && (index < outputDeviceCount)) {
134 return outputDevices[index];
135 }
136
137 SDL_SetError("No such device");
138 return NULL;
139 }
140
141
142 static void
143 BSDAUDIO_Status(_THIS)
144 {
76 #ifdef DEBUG_AUDIO 145 #ifdef DEBUG_AUDIO
77 static void OBSD_Status(_THIS);
78 #endif
79
80 /* Audio driver bootstrap functions */
81
82 static int
83 Audio_Available(void)
84 {
85 int fd;
86 int available;
87
88 available = 0;
89 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
90 if (fd >= 0) {
91 available = 1;
92 close(fd);
93 }
94 return (available);
95 }
96
97 static void
98 Audio_DeleteDevice(SDL_AudioDevice * device)
99 {
100 SDL_free(device->hidden);
101 SDL_free(device);
102 }
103
104 static SDL_AudioDevice *
105 Audio_CreateDevice(int devindex)
106 {
107 SDL_AudioDevice *this;
108
109 /* Initialize all variables that we clean on shutdown */
110 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
111 if (this) {
112 SDL_memset(this, 0, (sizeof *this));
113 this->hidden = (struct SDL_PrivateAudioData *)
114 SDL_malloc((sizeof *this->hidden));
115 }
116 if ((this == NULL) || (this->hidden == NULL)) {
117 SDL_OutOfMemory();
118 if (this)
119 SDL_free(this);
120 return (0);
121 }
122 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
123 audio_fd = -1;
124
125 /* Set the function pointers */
126 this->OpenAudio = OBSD_OpenAudio;
127 this->WaitAudio = OBSD_WaitAudio;
128 this->PlayAudio = OBSD_PlayAudio;
129 this->GetAudioBuf = OBSD_GetAudioBuf;
130 this->CloseAudio = OBSD_CloseAudio;
131
132 this->free = Audio_DeleteDevice;
133
134 return this;
135 }
136
137 AudioBootStrap BSD_AUDIO_bootstrap = {
138 BSD_AUDIO_DRIVER_NAME, BSD_AUDIO_DRIVER_DESC,
139 Audio_Available, Audio_CreateDevice
140 };
141
142 /* This function waits until it is possible to write a full sound buffer */
143 static void
144 OBSD_WaitAudio(_THIS)
145 {
146 #ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
147 /* See if we need to use timed audio synchronization */
148 if (frame_ticks) {
149 /* Use timer for general audio synchronization */
150 Sint32 ticks;
151
152 ticks = ((Sint32) (next_frame - SDL_GetTicks())) - FUDGE_TICKS;
153 if (ticks > 0) {
154 SDL_Delay(ticks);
155 }
156 } else {
157 /* Use select() for audio synchronization */
158 fd_set fdset;
159 struct timeval timeout;
160
161 FD_ZERO(&fdset);
162 FD_SET(audio_fd, &fdset);
163 timeout.tv_sec = 10;
164 timeout.tv_usec = 0;
165 #ifdef DEBUG_AUDIO
166 fprintf(stderr, "Waiting for audio to get ready\n");
167 #endif
168 if (select(audio_fd + 1, NULL, &fdset, NULL, &timeout) <= 0) {
169 const char *message =
170 "Audio timeout - buggy audio driver? (disabled)";
171 /* In general we should never print to the screen,
172 but in this case we have no other way of letting
173 the user know what happened.
174 */
175 fprintf(stderr, "SDL: %s\n", message);
176 this->enabled = 0;
177 /* Don't try to close - may hang */
178 audio_fd = -1;
179 #ifdef DEBUG_AUDIO
180 fprintf(stderr, "Done disabling audio\n");
181 #endif
182 }
183 #ifdef DEBUG_AUDIO
184 fprintf(stderr, "Ready!\n");
185 #endif
186 }
187 #endif /* !USE_BLOCKING_WRITES */
188 }
189
190 static void
191 OBSD_PlayAudio(_THIS)
192 {
193 int written, p = 0;
194
195 /* Write the audio data, checking for EAGAIN on broken audio drivers */
196 do {
197 written = write(audio_fd, &mixbuf[p], mixlen - p);
198 if (written > 0)
199 p += written;
200 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
201 /* Non recoverable error has occurred. It should be reported!!! */
202 perror("audio");
203 break;
204 }
205
206 if (p < written
207 || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
208 SDL_Delay(1); /* Let a little CPU time go by */
209 }
210 }
211 while (p < written);
212
213 /* If timer synchronization is enabled, set the next write frame */
214 if (frame_ticks) {
215 next_frame += frame_ticks;
216 }
217
218 /* If we couldn't write, assume fatal error for now */
219 if (written < 0) {
220 this->enabled = 0;
221 }
222 #ifdef DEBUG_AUDIO
223 fprintf(stderr, "Wrote %d bytes of audio data\n", written);
224 #endif
225 }
226
227 static Uint8 *
228 OBSD_GetAudioBuf(_THIS)
229 {
230 return (mixbuf);
231 }
232
233 static void
234 OBSD_CloseAudio(_THIS)
235 {
236 if (mixbuf != NULL) {
237 SDL_FreeAudioMem(mixbuf);
238 mixbuf = NULL;
239 }
240 if (audio_fd >= 0) {
241 close(audio_fd);
242 audio_fd = -1;
243 }
244 }
245
246 #ifdef DEBUG_AUDIO
247 void
248 OBSD_Status(_THIS)
249 {
250 audio_info_t info; 146 audio_info_t info;
251 147
252 if (ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { 148 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
253 fprintf(stderr, "AUDIO_GETINFO failed.\n"); 149 fprintf(stderr, "AUDIO_GETINFO failed.\n");
254 return; 150 return;
255 } 151 }
256 152
257 fprintf(stderr, "\n" 153 fprintf(stderr, "\n"
267 "paused : %s\n" 163 "paused : %s\n"
268 "error occured : %s\n" 164 "error occured : %s\n"
269 "waiting : %s\n" 165 "waiting : %s\n"
270 "active : %s\n" 166 "active : %s\n"
271 "", 167 "",
272 info. 168 info.play.buffer_size,
273 play. 169 info.play.sample_rate,
274 buffer_size, 170 info.play.channels,
275 info. 171 info.play.precision,
276 play. 172 info.play.encoding,
277 sample_rate, 173 info.play.seek,
278 info. 174 info.play.samples,
279 play. 175 info.play.eof,
280 channels, 176 info.play.pause ? "yes" : "no",
281 info. 177 info.play.error ? "yes" : "no",
282 play. 178 info.play.waiting ? "yes" : "no",
283 precision, 179 info.play.active ? "yes" : "no");
284 info.
285 play.
286 encoding,
287 info.
288 play.
289 seek,
290 info.
291 play.
292 samples,
293 info.
294 play.
295 eof,
296 info.
297 play.
298 pause
299 ?
300 "yes"
301 :
302 "no",
303 info.
304 play.
305 error
306 ?
307 "yes"
308 :
309 "no",
310 info.
311 play.waiting ? "yes" : "no", info.play.active ? "yes" : "no");
312 180
313 fprintf(stderr, "\n" 181 fprintf(stderr, "\n"
314 "[audio info]\n" 182 "[audio info]\n"
315 "monitor_gain : %i\n" 183 "monitor_gain : %i\n"
316 "hw block size : %d bytes\n" 184 "hw block size : %d bytes\n"
322 info.blocksize, 190 info.blocksize,
323 info.hiwat, info.lowat, 191 info.hiwat, info.lowat,
324 (info.mode == AUMODE_PLAY) ? "PLAY" 192 (info.mode == AUMODE_PLAY) ? "PLAY"
325 : (info.mode = AUMODE_RECORD) ? "RECORD" 193 : (info.mode = AUMODE_RECORD) ? "RECORD"
326 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); 194 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
327 }
328 #endif /* DEBUG_AUDIO */ 195 #endif /* DEBUG_AUDIO */
196 }
197
198
199 /* This function waits until it is possible to write a full sound buffer */
200 static void
201 BSDAUDIO_WaitDevice(_THIS)
202 {
203 #ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
204 /* See if we need to use timed audio synchronization */
205 if (this->hidden->frame_ticks) {
206 /* Use timer for general audio synchronization */
207 Sint32 ticks;
208
209 ticks = ((Sint32)(this->hidden->next_frame-SDL_GetTicks()))-FUDGE_TICKS;
210 if (ticks > 0) {
211 SDL_Delay(ticks);
212 }
213 } else {
214 /* Use select() for audio synchronization */
215 fd_set fdset;
216 struct timeval timeout;
217
218 FD_ZERO(&fdset);
219 FD_SET(this->hidden->audio_fd, &fdset);
220 timeout.tv_sec = 10;
221 timeout.tv_usec = 0;
222 #ifdef DEBUG_AUDIO
223 fprintf(stderr, "Waiting for audio to get ready\n");
224 #endif
225 if (select(this->hidden->audio_fd+1,NULL,&fdset,NULL,&timeout) <= 0) {
226 const char *message =
227 "Audio timeout - buggy audio driver? (disabled)";
228 /* In general we should never print to the screen,
229 but in this case we have no other way of letting
230 the user know what happened.
231 */
232 fprintf(stderr, "SDL: %s\n", message);
233 this->enabled = 0;
234 /* Don't try to close - may hang */
235 this->hidden->audio_fd = -1;
236 #ifdef DEBUG_AUDIO
237 fprintf(stderr, "Done disabling audio\n");
238 #endif
239 }
240 #ifdef DEBUG_AUDIO
241 fprintf(stderr, "Ready!\n");
242 #endif
243 }
244 #endif /* !USE_BLOCKING_WRITES */
245 }
246
247 static void
248 BSDAUDIO_PlayDevice(_THIS)
249 {
250 int written, p = 0;
251
252 /* Write the audio data, checking for EAGAIN on broken audio drivers */
253 do {
254 written = write(this->hidden->audio_fd,
255 &this->hidden->mixbuf[p],
256 this->hidden->mixlen - p);
257
258 if (written > 0)
259 p += written;
260 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
261 /* Non recoverable error has occurred. It should be reported!!! */
262 perror("audio");
263 break;
264 }
265
266 if (p < written
267 || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
268 SDL_Delay(1); /* Let a little CPU time go by */
269 }
270 }
271 while (p < written);
272
273 /* If timer synchronization is enabled, set the next write frame */
274 if (this->hidden->frame_ticks) {
275 this->hidden->next_frame += this->hidden->frame_ticks;
276 }
277
278 /* If we couldn't write, assume fatal error for now */
279 if (written < 0) {
280 this->enabled = 0;
281 }
282 #ifdef DEBUG_AUDIO
283 fprintf(stderr, "Wrote %d bytes of audio data\n", written);
284 #endif
285 }
286
287 static Uint8 *
288 BSDAUDIO_GetDeviceBuf(_THIS)
289 {
290 return (this->hidden->mixbuf);
291 }
292
293 static void
294 BSDAUDIO_CloseDevice(_THIS)
295 {
296 if (this->hidden != NULL) {
297 if (this->hidden->mixbuf != NULL) {
298 SDL_FreeAudioMem(this->hidden->mixbuf);
299 this->hidden->mixbuf = NULL;
300 }
301 if (this->hidden->audio_fd >= 0) {
302 close(this->hidden->audio_fd);
303 this->hidden->audio_fd = -1;
304 }
305 SDL_free(this->hidden);
306 this->hidden = NULL;
307 }
308 }
329 309
330 static int 310 static int
331 OBSD_OpenAudio(_THIS, SDL_AudioSpec * spec) 311 BSDAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
332 { 312 {
333 char audiodev[64]; 313 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
334 SDL_AudioFormat format; 314 SDL_AudioFormat format = 0;
335 audio_info_t info; 315 audio_info_t info;
336 316
317 /* We don't care what the devname is...we'll try to open anything. */
318 /* ...but default to first name in the list... */
319 if (devname == NULL) {
320 if ( ((iscapture) && (inputDeviceCount == 0)) ||
321 ((!iscapture) && (outputDeviceCount == 0)) ) {
322 SDL_SetError("No such audio device");
323 return 0;
324 }
325 devname = ((iscapture) ? inputDevices[0] : outputDevices[0]);
326 }
327
328 /* Initialize all variables that we clean on shutdown */
329 this->hidden = (struct SDL_PrivateAudioData *)
330 SDL_malloc((sizeof *this->hidden));
331 if (this->hidden == NULL) {
332 SDL_OutOfMemory();
333 return 0;
334 }
335 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
336
337 /* Open the audio device */
338 this->hidden->audio_fd = open(devname, flags, 0);
339 if (this->hidden->audio_fd < 0) {
340 SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
341 return 0;
342 }
343
337 AUDIO_INITINFO(&info); 344 AUDIO_INITINFO(&info);
338 345
339 /* Calculate the final parameters for this audio specification */ 346 /* Calculate the final parameters for this audio specification */
340 SDL_CalculateAudioSpec(spec); 347 SDL_CalculateAudioSpec(&this->spec);
341
342 #ifdef USE_TIMER_SYNC
343 frame_ticks = 0.0;
344 #endif
345
346 /* Open the audio device */
347 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
348 if (audio_fd < 0) {
349 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
350 return (-1);
351 }
352 348
353 /* Set to play mode */ 349 /* Set to play mode */
354 info.mode = AUMODE_PLAY; 350 info.mode = AUMODE_PLAY;
355 if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { 351 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
352 BSDAUDIO_CloseDevice(this);
356 SDL_SetError("Couldn't put device into play mode"); 353 SDL_SetError("Couldn't put device into play mode");
357 return (-1); 354 return 0;
358 } 355 }
359 356
360 mixbuf = NULL;
361 AUDIO_INITINFO(&info); 357 AUDIO_INITINFO(&info);
362 for (format = SDL_FirstAudioFormat(spec->format); 358 for (format = SDL_FirstAudioFormat(this->spec.format);
363 format; format = SDL_NextAudioFormat()) { 359 format; format = SDL_NextAudioFormat()) {
364 switch (format) { 360 switch (format) {
365 case AUDIO_U8: 361 case AUDIO_U8:
366 info.play.encoding = AUDIO_ENCODING_ULINEAR; 362 info.play.encoding = AUDIO_ENCODING_ULINEAR;
367 info.play.precision = 8; 363 info.play.precision = 8;
387 info.play.precision = 16; 383 info.play.precision = 16;
388 break; 384 break;
389 default: 385 default:
390 continue; 386 continue;
391 } 387 }
392 if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0) 388
393 break; 389 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
390 break;
391 }
394 } 392 }
395 393
396 if (!format) { 394 if (!format) {
397 SDL_SetError("No supported encoding for 0x%x", spec->format); 395 BSDAUDIO_CloseDevice(this);
398 return (-1); 396 SDL_SetError("No supported encoding for 0x%x", this->spec.format);
399 } 397 return 0;
400 398 }
401 spec->format = format; 399
400 this->spec.format = format;
402 401
403 AUDIO_INITINFO(&info); 402 AUDIO_INITINFO(&info);
404 info.play.channels = spec->channels; 403 info.play.channels = this->spec.channels;
405 if (ioctl(audio_fd, AUDIO_SETINFO, &info) == -1) 404 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
406 spec->channels = 1; 405 this->spec.channels = 1;
406 }
407 AUDIO_INITINFO(&info); 407 AUDIO_INITINFO(&info);
408 info.play.sample_rate = spec->freq; 408 info.play.sample_rate = this->spec.freq;
409 info.blocksize = spec->size; 409 info.blocksize = this->spec.size;
410 info.hiwat = 5; 410 info.hiwat = 5;
411 info.lowat = 3; 411 info.lowat = 3;
412 (void) ioctl(audio_fd, AUDIO_SETINFO, &info); 412 (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
413 (void) ioctl(audio_fd, AUDIO_GETINFO, &info); 413 (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
414 spec->freq = info.play.sample_rate; 414 this->spec.freq = info.play.sample_rate;
415 /* Allocate mixing buffer */ 415 /* Allocate mixing buffer */
416 mixlen = spec->size; 416 this->hidden->mixlen = this->spec.size;
417 mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen); 417 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
418 if (mixbuf == NULL) { 418 if (this->hidden->mixbuf == NULL) {
419 return (-1); 419 BSDAUDIO_CloseDevice(this);
420 } 420 SDL_OutOfMemory();
421 SDL_memset(mixbuf, spec->silence, spec->size); 421 return 0;
422 422 }
423 /* Get the parent process id (we're the parent of the audio thread) */ 423 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
424 parent = getpid(); 424
425 425 BSDAUDIO_Status(this);
426 #ifdef DEBUG_AUDIO
427 OBSD_Status(this);
428 #endif
429 426
430 /* We're ready to rock and roll. :-) */ 427 /* We're ready to rock and roll. :-) */
431 return (0); 428 return (0);
432 } 429 }
433 430
431 static int
432 BSDAUDIO_Init(SDL_AudioDriverImpl *impl)
433 {
434 /* Set the function pointers */
435 impl->DetectDevices = BSDAUDIO_DetectDevices;
436 impl->GetDeviceName = BSDAUDIO_GetDeviceName;
437 impl->OpenDevice = BSDAUDIO_OpenDevice;
438 impl->PlayDevice = BSDAUDIO_PlayDevice;
439 impl->WaitDevice = BSDAUDIO_WaitDevice;
440 impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
441 impl->CloseDevice = BSDAUDIO_CloseDevice;
442 impl->Deinitialize = BSDAUDIO_Deinitialize;
443
444 build_device_lists();
445 return 1;
446 }
447
448
449 AudioBootStrap BSD_AUDIO_bootstrap = {
450 BSD_AUDIO_DRIVER_NAME, BSD_AUDIO_DRIVER_DESC, BSDAUDIO_Init, 0
451 };
452
434 /* vi: set ts=4 sw=4 expandtab: */ 453 /* vi: set ts=4 sw=4 expandtab: */