comparison src/audio/dsp/SDL_dspaudio.c @ 968:4675910b0b7b

Date: Mon, 11 Oct 2004 15:17:27 +0300 (EEST) From: Hannu Savolainen Subject: Re: SDL uses obsolete OSS features I did some work on getting OSS to work better with SDL. There have been some problems with select which should be fixed now. I'm having some problems in understanding what is the purpose of the DSP_WaitAudio() routine. I added a return to the very beginning of this routine and commendted out the define for USE_BLOCKING_WRITES. At least lbreakout2 seems to work as well as earlier. The latencies are the same. An ordinary blocking write does exactly the same thing than DSP_WaitAudio does. So I would recommend using the USE_BLOCKING_WRITES approach and removing everything from the DSP_WaitAudio routine. Also enabling USE_BLOCKING_WRITES makes it possible to simplify DSP_PlayAudio() because you don't need to handle the partial writes (the do-while loop). Attached is a patch against SDL-1.2.7. After these changes SDL will use OSS as it's designed to be used (make it as simple as possible). This code should work with all OSS implementations because it uses only the very fundamental features that have been there since the jurassic times.
author Sam Lantinga <slouken@libsdl.org>
date Fri, 12 Nov 2004 21:39:04 +0000
parents eec28a5278be
children c5dedfdb4e42
comparison
equal deleted inserted replaced
967:cda407d627a3 968:4675910b0b7b
16 License along with this library; if not, write to the Free 16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 18
19 Sam Lantinga 19 Sam Lantinga
20 slouken@libsdl.org 20 slouken@libsdl.org
21
22 Modified in Oct 2004 by Hannu Savolainen
23 hannu@opensound.com
21 */ 24 */
22 25
23 #ifdef SAVE_RCSID 26 #ifdef SAVE_RCSID
24 static char rcsid = 27 static char rcsid =
25 "@(#) $Id$"; 28 "@(#) $Id$";
55 58
56 /* The tag name used by DSP audio */ 59 /* The tag name used by DSP audio */
57 #define DSP_DRIVER_NAME "dsp" 60 #define DSP_DRIVER_NAME "dsp"
58 61
59 /* Open the audio device for playback, and don't block if busy */ 62 /* Open the audio device for playback, and don't block if busy */
60 /*#define USE_BLOCKING_WRITES*/
61 #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) 63 #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK)
62 64
63 /* Audio driver functions */ 65 /* Audio driver functions */
64 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec); 66 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
65 static void DSP_WaitAudio(_THIS); 67 static void DSP_WaitAudio(_THIS);
128 }; 130 };
129 131
130 /* This function waits until it is possible to write a full sound buffer */ 132 /* This function waits until it is possible to write a full sound buffer */
131 static void DSP_WaitAudio(_THIS) 133 static void DSP_WaitAudio(_THIS)
132 { 134 {
133 /* Check to see if the thread-parent process is still alive */ 135 /* Not needed at all since OSS handles waiting automagically */
134 { static int cnt = 0; 136 }
135 /* Note that this only works with thread implementations 137
136 that use a different process id for each thread. 138 static void DSP_PlayAudio(_THIS)
137 */ 139 {
138 if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ 140 if (write(audio_fd, mixbuf, mixlen)==-1)
139 if ( kill(parent, 0) < 0 ) { 141 {
140 this->enabled = 0; 142 perror("Audio write");
141 } 143 this->enabled = 0;
142 } 144 }
143 } 145
144
145 #ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
146 /* See if we need to use timed audio synchronization */
147 if ( frame_ticks ) {
148 /* Use timer for general audio synchronization */
149 Sint32 ticks;
150
151 ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
152 if ( ticks > 0 ) {
153 SDL_Delay(ticks);
154 }
155 } else {
156 /* Use select() for audio synchronization */
157 fd_set fdset;
158 struct timeval timeout;
159
160 FD_ZERO(&fdset);
161 FD_SET(audio_fd, &fdset);
162 timeout.tv_sec = 10;
163 timeout.tv_usec = 0;
164 #ifdef DEBUG_AUDIO 146 #ifdef DEBUG_AUDIO
165 fprintf(stderr, "Waiting for audio to get ready\n"); 147 fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
166 #endif
167 if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
168 const char *message =
169 "Audio timeout - buggy audio driver? (disabled)";
170 /* In general we should never print to the screen,
171 but in this case we have no other way of letting
172 the user know what happened.
173 */
174 fprintf(stderr, "SDL: %s\n", message);
175 this->enabled = 0;
176 /* Don't try to close - may hang */
177 audio_fd = -1;
178 #ifdef DEBUG_AUDIO
179 fprintf(stderr, "Done disabling audio\n");
180 #endif
181 }
182 #ifdef DEBUG_AUDIO
183 fprintf(stderr, "Ready!\n");
184 #endif
185 }
186 #endif /* !USE_BLOCKING_WRITES */
187 }
188
189 static void DSP_PlayAudio(_THIS)
190 {
191 int written, p=0;
192
193 /* Write the audio data, checking for EAGAIN on broken audio drivers */
194 do {
195 written = write(audio_fd, &mixbuf[p], mixlen-p);
196 if (written>0)
197 p += written;
198 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR)
199 {
200 /* Non recoverable error has occurred. It should be reported!!! */
201 perror("audio");
202 break;
203 }
204
205 if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) {
206 SDL_Delay(1); /* Let a little CPU time go by */
207 }
208 } while ( p < written );
209
210 /* If timer synchronization is enabled, set the next write frame */
211 if ( frame_ticks ) {
212 next_frame += frame_ticks;
213 }
214
215 /* If we couldn't write, assume fatal error for now */
216 if ( written < 0 ) {
217 this->enabled = 0;
218 }
219 #ifdef DEBUG_AUDIO
220 fprintf(stderr, "Wrote %d bytes of audio data\n", written);
221 #endif 148 #endif
222 } 149 }
223 150
224 static Uint8 *DSP_GetAudioBuf(_THIS) 151 static Uint8 *DSP_GetAudioBuf(_THIS)
225 { 152 {
231 if ( mixbuf != NULL ) { 158 if ( mixbuf != NULL ) {
232 SDL_FreeAudioMem(mixbuf); 159 SDL_FreeAudioMem(mixbuf);
233 mixbuf = NULL; 160 mixbuf = NULL;
234 } 161 }
235 if ( audio_fd >= 0 ) { 162 if ( audio_fd >= 0 ) {
236 int value;
237 ioctl(audio_fd, SNDCTL_DSP_RESET, &value);
238 close(audio_fd); 163 close(audio_fd);
239 audio_fd = -1; 164 audio_fd = -1;
240 } 165 }
241 }
242
243 static int DSP_ReopenAudio(_THIS, const char *audiodev, int format,
244 SDL_AudioSpec *spec)
245 {
246 int frag_spec;
247 int value;
248
249 /* Close and then reopen the audio device */
250 close(audio_fd);
251 audio_fd = open(audiodev, O_WRONLY, 0);
252 if ( audio_fd < 0 ) {
253 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
254 return(-1);
255 }
256
257 /* Calculate the final parameters for this audio specification */
258 SDL_CalculateAudioSpec(spec);
259
260 /* Determine the power of two of the fragment size */
261 for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
262 if ( (0x01<<frag_spec) != spec->size ) {
263 SDL_SetError("Fragment size must be a power of two");
264 return(-1);
265 }
266 frag_spec |= 0x00020000; /* two fragments, for low latency */
267
268 /* Set the audio buffering parameters */
269 #ifdef DEBUG_AUDIO
270 fprintf(stderr, "Requesting %d fragments of size %d\n",
271 (frag_spec >> 16), 1<<(frag_spec&0xFFFF));
272 #endif
273 if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
274 fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
275 }
276 #ifdef DEBUG_AUDIO
277 { audio_buf_info info;
278 ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
279 fprintf(stderr, "fragments = %d\n", info.fragments);
280 fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
281 fprintf(stderr, "fragsize = %d\n", info.fragsize);
282 fprintf(stderr, "bytes = %d\n", info.bytes);
283 }
284 #endif
285
286 /* Set the audio format */
287 value = format;
288 if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
289 (value != format) ) {
290 SDL_SetError("Couldn't set audio format");
291 return(-1);
292 }
293
294 /* Set the number of channels of output */
295 value = spec->channels;
296 #ifdef SNDCTL_DSP_CHANNELS
297 if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
298 #endif
299 value = (spec->channels > 1);
300 ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
301 value = (value ? 2 : 1);
302 #ifdef SNDCTL_DSP_CHANNELS
303 }
304 #endif
305 if ( value != spec->channels ) {
306 SDL_SetError("Couldn't set audio channels");
307 return(-1);
308 }
309
310 /* Set the DSP frequency */
311 value = spec->freq;
312 if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
313 SDL_SetError("Couldn't set audio frequency");
314 return(-1);
315 }
316 spec->freq = value;
317
318 /* We successfully re-opened the audio */
319 return(0);
320 } 166 }
321 167
322 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec) 168 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
323 { 169 {
324 char audiodev[1024]; 170 char audiodev[1024];
325 int format; 171 int format;
326 int value; 172 int value;
173 int frag_spec;
327 Uint16 test_format; 174 Uint16 test_format;
328
329 /* Reset the timer synchronization flag */
330 frame_ticks = 0.0;
331 175
332 /* Open the audio device */ 176 /* Open the audio device */
333 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); 177 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
334 if ( audio_fd < 0 ) { 178 if ( audio_fd < 0 ) {
335 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); 179 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
336 return(-1); 180 return(-1);
337 } 181 }
338 mixbuf = NULL; 182 mixbuf = NULL;
339 183
340 #ifdef USE_BLOCKING_WRITES
341 /* Make the file descriptor use blocking writes with fcntl() */ 184 /* Make the file descriptor use blocking writes with fcntl() */
342 { long flags; 185 { long flags;
343 flags = fcntl(audio_fd, F_GETFL); 186 flags = fcntl(audio_fd, F_GETFL);
344 flags &= ~O_NONBLOCK; 187 flags &= ~O_NONBLOCK;
345 if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) { 188 if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
346 SDL_SetError("Couldn't set audio blocking mode"); 189 SDL_SetError("Couldn't set audio blocking mode");
347 return(-1); 190 return(-1);
348 } 191 }
349 } 192 }
350 #endif
351 193
352 /* Get a list of supported hardware formats */ 194 /* Get a list of supported hardware formats */
353 if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) { 195 if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
196 perror("SNDCTL_DSP_GETFMTS");
354 SDL_SetError("Couldn't get audio format list"); 197 SDL_SetError("Couldn't get audio format list");
355 return(-1); 198 return(-1);
356 } 199 }
357 200
358 /* Try for a closest match on audio format */ 201 /* Try for a closest match on audio format */
366 case AUDIO_U8: 209 case AUDIO_U8:
367 if ( value & AFMT_U8 ) { 210 if ( value & AFMT_U8 ) {
368 format = AFMT_U8; 211 format = AFMT_U8;
369 } 212 }
370 break; 213 break;
214 case AUDIO_S16LSB:
215 if ( value & AFMT_S16_LE ) {
216 format = AFMT_S16_LE;
217 }
218 break;
219 case AUDIO_S16MSB:
220 if ( value & AFMT_S16_BE ) {
221 format = AFMT_S16_BE;
222 }
223 break;
224 #if 0
225 /*
226 * These formats are not used by any real life systems so they are not
227 * needed here.
228 */
371 case AUDIO_S8: 229 case AUDIO_S8:
372 if ( value & AFMT_S8 ) { 230 if ( value & AFMT_S8 ) {
373 format = AFMT_S8; 231 format = AFMT_S8;
374 } 232 }
375 break; 233 break;
376 case AUDIO_S16LSB:
377 if ( value & AFMT_S16_LE ) {
378 format = AFMT_S16_LE;
379 }
380 break;
381 case AUDIO_S16MSB:
382 if ( value & AFMT_S16_BE ) {
383 format = AFMT_S16_BE;
384 }
385 break;
386 case AUDIO_U16LSB: 234 case AUDIO_U16LSB:
387 if ( value & AFMT_U16_LE ) { 235 if ( value & AFMT_U16_LE ) {
388 format = AFMT_U16_LE; 236 format = AFMT_U16_LE;
389 } 237 }
390 break; 238 break;
391 case AUDIO_U16MSB: 239 case AUDIO_U16MSB:
392 if ( value & AFMT_U16_BE ) { 240 if ( value & AFMT_U16_BE ) {
393 format = AFMT_U16_BE; 241 format = AFMT_U16_BE;
394 } 242 }
395 break; 243 break;
244 #endif
396 default: 245 default:
397 format = 0; 246 format = 0;
398 break; 247 break;
399 } 248 }
400 if ( ! format ) { 249 if ( ! format ) {
409 258
410 /* Set the audio format */ 259 /* Set the audio format */
411 value = format; 260 value = format;
412 if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || 261 if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
413 (value != format) ) { 262 (value != format) ) {
263 perror("SNDCTL_DSP_SETFMT");
414 SDL_SetError("Couldn't set audio format"); 264 SDL_SetError("Couldn't set audio format");
415 return(-1); 265 return(-1);
416 } 266 }
417 267
418 /* Set the number of channels of output */ 268 /* Set the number of channels of output */
419 value = spec->channels; 269 value = spec->channels;
420 #ifdef SNDCTL_DSP_CHANNELS
421 if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) { 270 if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
422 #endif 271 perror("SNDCTL_DSP_CHANNELS");
423 value = (spec->channels > 1); 272 SDL_SetError("Cannot set the number of channels");
424 ioctl(audio_fd, SNDCTL_DSP_STEREO, &value); 273 return(-1);
425 value = (value ? 2 : 1); 274 }
426 #ifdef SNDCTL_DSP_CHANNELS
427 }
428 #endif
429 spec->channels = value; 275 spec->channels = value;
430 276
431 /* Because some drivers don't allow setting the buffer size 277 /* Set the DSP frequency */
432 after setting the format, we must re-open the audio device 278 value = spec->freq;
433 once we know what format and channels are supported 279 if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
434 */ 280 perror("SNDCTL_DSP_SPEED");
435 if ( DSP_ReopenAudio(this, audiodev, format, spec) < 0 ) { 281 SDL_SetError("Couldn't set audio frequency");
436 /* Error is set by DSP_ReopenAudio() */ 282 return(-1);
437 return(-1); 283 }
438 } 284 spec->freq = value;
285
286 /* Calculate the final parameters for this audio specification */
287 SDL_CalculateAudioSpec(spec);
288
289 /* Determine the power of two of the fragment size */
290 for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
291 if ( (0x01<<frag_spec) != spec->size ) {
292 SDL_SetError("Fragment size must be a power of two");
293 return(-1);
294 }
295 frag_spec |= 0x00020000; /* two fragments, for low latency */
296
297 /* Set the audio buffering parameters */
298 #ifdef DEBUG_AUDIO
299 fprintf(stderr, "Requesting %d fragments of size %d\n",
300 (frag_spec >> 16), 1<<(frag_spec&0xFFFF));
301 #endif
302 if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
303 perror("SNDCTL_DSP_SETFRAGMENT");
304 fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
305 }
306 #ifdef DEBUG_AUDIO
307 { audio_buf_info info;
308 ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
309 fprintf(stderr, "fragments = %d\n", info.fragments);
310 fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
311 fprintf(stderr, "fragsize = %d\n", info.fragsize);
312 fprintf(stderr, "bytes = %d\n", info.bytes);
313 }
314 #endif
439 315
440 /* Allocate mixing buffer */ 316 /* Allocate mixing buffer */
441 mixlen = spec->size; 317 mixlen = spec->size;
442 mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); 318 mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
443 if ( mixbuf == NULL ) { 319 if ( mixbuf == NULL ) {
444 return(-1); 320 return(-1);
445 } 321 }
446 memset(mixbuf, spec->silence, spec->size); 322 memset(mixbuf, spec->silence, spec->size);
447 323
448 #ifndef USE_BLOCKING_WRITES
449 /* Check to see if we need to use select() workaround */
450 { char *workaround;
451 workaround = getenv("SDL_DSP_NOSELECT");
452 if ( workaround ) {
453 frame_ticks = (float)(spec->samples*1000)/spec->freq;
454 next_frame = SDL_GetTicks()+frame_ticks;
455 }
456 }
457 #endif /* !USE_BLOCKING_WRITES */
458
459 /* Get the parent process id (we're the parent of the audio thread) */ 324 /* Get the parent process id (we're the parent of the audio thread) */
460 parent = getpid(); 325 parent = getpid();
461 326
462 /* We're ready to rock and roll. :-) */ 327 /* We're ready to rock and roll. :-) */
463 return(0); 328 return(0);