Mercurial > sdl-ios-xcode
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); |