comparison src/audio/bsd/SDL_bsdaudio.c @ 3820:1f156fd874fa SDL-ryan-multiple-audio-device

Moved more audio drivers to 1.3 API (all 5 MiNT backends, BSD, IRIX...), and some other tweaks in already-converted drivers.
author Ryan C. Gordon <icculus@icculus.org>
date Sat, 07 Oct 2006 05:36:36 +0000
parents 9d070c1a45fa
children 66fb40445587
comparison
equal deleted inserted replaced
3819:b225d9820ee3 3820:1f156fd874fa
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 int
108 BSDAUDIO_Available(void)
109 {
110 int available = 0;
111 build_device_lists();
112 available = ((outputDeviceCount > 0) || (inputDeviceCount > 0));
113 free_device_lists();
114 return available;
115 }
116
117
118 static void
119 BSDAUDIO_Deinitialize(void)
120 {
121 free_device_lists();
122 }
123
124
125 static int
126 BSDAUDIO_DetectDevices(int iscapture)
127 {
128 if (iscapture) {
129 build_device_list(1, &inputDevices, &inputDeviceCount);
130 return inputDeviceCount;
131 } else {
132 build_device_list(0, &outputDevices, &outputDeviceCount);
133 return outputDeviceCount;
134 }
135
136 return 0; /* shouldn't ever hit this. */
137 }
138
139 static const char *
140 BSDAUDIO_GetDeviceName(int index, int iscapture)
141 {
142 if ((iscapture) && (index < inputDeviceCount)) {
143 return inputDevices[index];
144 } else if ((!iscapture) && (index < outputDeviceCount)) {
145 return outputDevices[index];
146 }
147
148 SDL_SetError("No such device");
149 return NULL;
150 }
151
152
153 static void
154 BSDAUDIO_Status(_THIS)
155 {
76 #ifdef DEBUG_AUDIO 156 #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, 0
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; 157 audio_info_t info;
251 158
252 if (ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { 159 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
253 fprintf(stderr, "AUDIO_GETINFO failed.\n"); 160 fprintf(stderr, "AUDIO_GETINFO failed.\n");
254 return; 161 return;
255 } 162 }
256 163
257 fprintf(stderr, "\n" 164 fprintf(stderr, "\n"
294 info.blocksize, 201 info.blocksize,
295 info.hiwat, info.lowat, 202 info.hiwat, info.lowat,
296 (info.mode == AUMODE_PLAY) ? "PLAY" 203 (info.mode == AUMODE_PLAY) ? "PLAY"
297 : (info.mode = AUMODE_RECORD) ? "RECORD" 204 : (info.mode = AUMODE_RECORD) ? "RECORD"
298 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); 205 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
299 }
300 #endif /* DEBUG_AUDIO */ 206 #endif /* DEBUG_AUDIO */
207 }
208
209
210 /* This function waits until it is possible to write a full sound buffer */
211 static void
212 BSDAUDIO_WaitDevice(_THIS)
213 {
214 #ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
215 /* See if we need to use timed audio synchronization */
216 if (this->hidden->frame_ticks) {
217 /* Use timer for general audio synchronization */
218 Sint32 ticks;
219
220 ticks = ((Sint32)(this->hidden->next_frame-SDL_GetTicks()))-FUDGE_TICKS;
221 if (ticks > 0) {
222 SDL_Delay(ticks);
223 }
224 } else {
225 /* Use select() for audio synchronization */
226 fd_set fdset;
227 struct timeval timeout;
228
229 FD_ZERO(&fdset);
230 FD_SET(this->hidden->audio_fd, &fdset);
231 timeout.tv_sec = 10;
232 timeout.tv_usec = 0;
233 #ifdef DEBUG_AUDIO
234 fprintf(stderr, "Waiting for audio to get ready\n");
235 #endif
236 if (select(this->hidden->audio_fd+1,NULL,&fdset,NULL,&timeout) <= 0) {
237 const char *message =
238 "Audio timeout - buggy audio driver? (disabled)";
239 /* In general we should never print to the screen,
240 but in this case we have no other way of letting
241 the user know what happened.
242 */
243 fprintf(stderr, "SDL: %s\n", message);
244 this->enabled = 0;
245 /* Don't try to close - may hang */
246 this->hidden->audio_fd = -1;
247 #ifdef DEBUG_AUDIO
248 fprintf(stderr, "Done disabling audio\n");
249 #endif
250 }
251 #ifdef DEBUG_AUDIO
252 fprintf(stderr, "Ready!\n");
253 #endif
254 }
255 #endif /* !USE_BLOCKING_WRITES */
256 }
257
258 static void
259 BSDAUDIO_PlayDevice(_THIS)
260 {
261 int written, p = 0;
262
263 /* Write the audio data, checking for EAGAIN on broken audio drivers */
264 do {
265 written = write(this->hidden->audio_fd,
266 &this->hidden->mixbuf[p],
267 this->hidden->mixlen - p);
268
269 if (written > 0)
270 p += written;
271 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
272 /* Non recoverable error has occurred. It should be reported!!! */
273 perror("audio");
274 break;
275 }
276
277 if (p < written
278 || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
279 SDL_Delay(1); /* Let a little CPU time go by */
280 }
281 }
282 while (p < written);
283
284 /* If timer synchronization is enabled, set the next write frame */
285 if (this->hidden->frame_ticks) {
286 this->hidden->next_frame += this->hidden->frame_ticks;
287 }
288
289 /* If we couldn't write, assume fatal error for now */
290 if (written < 0) {
291 this->enabled = 0;
292 }
293 #ifdef DEBUG_AUDIO
294 fprintf(stderr, "Wrote %d bytes of audio data\n", written);
295 #endif
296 }
297
298 static Uint8 *
299 BSDAUDIO_GetDeviceBuf(_THIS)
300 {
301 return (this->hidden->mixbuf);
302 }
303
304 static void
305 BSDAUDIO_CloseDevice(_THIS)
306 {
307 if (this->hidden != NULL) {
308 if (this->hidden->mixbuf != NULL) {
309 SDL_FreeAudioMem(this->hidden->mixbuf);
310 this->hidden->mixbuf = NULL;
311 }
312 if (this->hidden->audio_fd >= 0) {
313 close(this->hidden->audio_fd);
314 this->hidden->audio_fd = -1;
315 }
316 SDL_free(this->hidden);
317 this->hidden = NULL;
318 }
319 }
301 320
302 static int 321 static int
303 OBSD_OpenAudio(_THIS, SDL_AudioSpec * spec) 322 BSDAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
304 { 323 {
305 char audiodev[64]; 324 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
306 SDL_AudioFormat format; 325 SDL_AudioFormat format = 0;
307 audio_info_t info; 326 audio_info_t info;
308 327
328 /* We don't care what the devname is...we'll try to open anything. */
329 /* ...but default to first name in the list... */
330 if (devname == NULL) {
331 if ( ((iscapture) && (inputDeviceCount == 0)) ||
332 ((!iscapture) && (outputDeviceCount == 0)) ) {
333 SDL_SetError("No such audio device");
334 return 0;
335 }
336 devname = ((iscapture) ? inputDevices[0] : outputDevices[0]);
337 }
338
339 /* Initialize all variables that we clean on shutdown */
340 this->hidden = (struct SDL_PrivateAudioData *)
341 SDL_malloc((sizeof *this->hidden));
342 if (this->hidden == NULL) {
343 SDL_OutOfMemory();
344 return 0;
345 }
346 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
347
348 /* Open the audio device */
349 this->hidden->audio_fd = open(devname, flags, 0);
350 if (this->hidden->audio_fd < 0) {
351 SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
352 return 0;
353 }
354
309 AUDIO_INITINFO(&info); 355 AUDIO_INITINFO(&info);
310 356
311 /* Calculate the final parameters for this audio specification */ 357 /* Calculate the final parameters for this audio specification */
312 SDL_CalculateAudioSpec(spec); 358 SDL_CalculateAudioSpec(&this->spec);
313
314 #ifdef USE_TIMER_SYNC
315 frame_ticks = 0.0;
316 #endif
317
318 /* Open the audio device */
319 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
320 if (audio_fd < 0) {
321 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
322 return (-1);
323 }
324 359
325 /* Set to play mode */ 360 /* Set to play mode */
326 info.mode = AUMODE_PLAY; 361 info.mode = AUMODE_PLAY;
327 if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { 362 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
363 BSDAUDIO_CloseDevice(this);
328 SDL_SetError("Couldn't put device into play mode"); 364 SDL_SetError("Couldn't put device into play mode");
329 return (-1); 365 return 0;
330 } 366 }
331 367
332 mixbuf = NULL;
333 AUDIO_INITINFO(&info); 368 AUDIO_INITINFO(&info);
334 for (format = SDL_FirstAudioFormat(spec->format); 369 for (format = SDL_FirstAudioFormat(this->spec.format);
335 format; format = SDL_NextAudioFormat()) { 370 format; format = SDL_NextAudioFormat()) {
336 switch (format) { 371 switch (format) {
337 case AUDIO_U8: 372 case AUDIO_U8:
338 info.play.encoding = AUDIO_ENCODING_ULINEAR; 373 info.play.encoding = AUDIO_ENCODING_ULINEAR;
339 info.play.precision = 8; 374 info.play.precision = 8;
359 info.play.precision = 16; 394 info.play.precision = 16;
360 break; 395 break;
361 default: 396 default:
362 continue; 397 continue;
363 } 398 }
364 if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0) 399
365 break; 400 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
401 break;
402 }
366 } 403 }
367 404
368 if (!format) { 405 if (!format) {
369 SDL_SetError("No supported encoding for 0x%x", spec->format); 406 BSDAUDIO_CloseDevice(this);
370 return (-1); 407 SDL_SetError("No supported encoding for 0x%x", this->spec.format);
371 } 408 return 0;
372 409 }
373 spec->format = format; 410
411 this->spec.format = format;
374 412
375 AUDIO_INITINFO(&info); 413 AUDIO_INITINFO(&info);
376 info.play.channels = spec->channels; 414 info.play.channels = this->spec.channels;
377 if (ioctl(audio_fd, AUDIO_SETINFO, &info) == -1) 415 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
378 spec->channels = 1; 416 this->spec.channels = 1;
417 }
379 AUDIO_INITINFO(&info); 418 AUDIO_INITINFO(&info);
380 info.play.sample_rate = spec->freq; 419 info.play.sample_rate = this->spec.freq;
381 info.blocksize = spec->size; 420 info.blocksize = this->spec.size;
382 info.hiwat = 5; 421 info.hiwat = 5;
383 info.lowat = 3; 422 info.lowat = 3;
384 (void) ioctl(audio_fd, AUDIO_SETINFO, &info); 423 (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
385 (void) ioctl(audio_fd, AUDIO_GETINFO, &info); 424 (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
386 spec->freq = info.play.sample_rate; 425 this->spec.freq = info.play.sample_rate;
387 /* Allocate mixing buffer */ 426 /* Allocate mixing buffer */
388 mixlen = spec->size; 427 this->hidden->mixlen = this->spec.size;
389 mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen); 428 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
390 if (mixbuf == NULL) { 429 if (this->hidden->mixbuf == NULL) {
391 return (-1); 430 BSDAUDIO_CloseDevice(this);
392 } 431 SDL_OutOfMemory();
393 SDL_memset(mixbuf, spec->silence, spec->size); 432 return 0;
394 433 }
395 /* Get the parent process id (we're the parent of the audio thread) */ 434 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
396 parent = getpid(); 435
397 436 BSDAUDIO_Status(this);
398 #ifdef DEBUG_AUDIO
399 OBSD_Status(this);
400 #endif
401 437
402 /* We're ready to rock and roll. :-) */ 438 /* We're ready to rock and roll. :-) */
403 return (0); 439 return (0);
404 } 440 }
405 441
442 static int
443 BSDAUDIO_Init(SDL_AudioDriverImpl *impl)
444 {
445 /* Set the function pointers */
446 impl->DetectDevices = DSP_DetectDevices;
447 impl->GetDeviceName = DSP_GetDeviceName;
448 impl->OpenDevice = DSP_OpenDevice;
449 impl->PlayDevice = DSP_PlayDevice;
450 impl->WaitDevice = DSP_WaitDevice;
451 impl->GetDeviceBuf = DSP_GetDeviceBuf;
452 impl->CloseDevice = DSP_CloseDevice;
453 impl->Deinitialize = DSP_Deinitialize;
454
455 build_device_lists();
456 return 1;
457 }
458
459
460 AudioBootStrap BSD_AUDIO_bootstrap = {
461 BSD_AUDIO_DRIVER_NAME, BSD_AUDIO_DRIVER_DESC,
462 BSDAUDIO_Available, BSDAUDIO_Init, 0
463 };
464
406 /* vi: set ts=4 sw=4 expandtab: */ 465 /* vi: set ts=4 sw=4 expandtab: */