Mercurial > sdl-ios-xcode
comparison src/audio/SDL_audio.c @ 2716:f8f68f47285a
Final merge of Google Summer of Code 2008 work...
Audio Ideas - Resampling and Pitch Shifting
by Aaron Wishnick, mentored by Ryan C. Gordon
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Mon, 25 Aug 2008 15:08:59 +0000 |
parents | aedfcdeb69b6 |
children | 2768bd7281e0 |
comparison
equal
deleted
inserted
replaced
2715:336b604ec15b | 2716:f8f68f47285a |
---|---|
254 FILL_STUB(UnlockDevice); | 254 FILL_STUB(UnlockDevice); |
255 FILL_STUB(Deinitialize); | 255 FILL_STUB(Deinitialize); |
256 #undef FILL_STUB | 256 #undef FILL_STUB |
257 } | 257 } |
258 | 258 |
259 /* Streaming functions (for when the input and output buffer sizes are different) */ | |
260 /* Write [length] bytes from buf into the streamer */ | |
261 void | |
262 SDL_StreamWrite(SDL_AudioStreamer * stream, Uint8 * buf, int length) | |
263 { | |
264 int i; | |
265 | |
266 for (i = 0; i < length; ++i) { | |
267 stream->buffer[stream->write_pos] = buf[i]; | |
268 ++stream->write_pos; | |
269 } | |
270 } | |
271 | |
272 /* Read [length] bytes out of the streamer into buf */ | |
273 void | |
274 SDL_StreamRead(SDL_AudioStreamer * stream, Uint8 * buf, int length) | |
275 { | |
276 int i; | |
277 | |
278 for (i = 0; i < length; ++i) { | |
279 buf[i] = stream->buffer[stream->read_pos]; | |
280 ++stream->read_pos; | |
281 } | |
282 } | |
283 | |
284 int | |
285 SDL_StreamLength(SDL_AudioStreamer * stream) | |
286 { | |
287 return (stream->write_pos - stream->read_pos) % stream->max_len; | |
288 } | |
289 | |
290 /* Initialize the stream by allocating the buffer and setting the read/write heads to the beginning */ | |
291 int | |
292 SDL_StreamInit(SDL_AudioStreamer * stream, int max_len, Uint8 silence) | |
293 { | |
294 int i; | |
295 | |
296 /* First try to allocate the buffer */ | |
297 stream->buffer = (Uint8 *) malloc(max_len); | |
298 if (stream->buffer == NULL) { | |
299 return -1; | |
300 } | |
301 | |
302 stream->max_len = max_len; | |
303 stream->read_pos = 0; | |
304 stream->write_pos = 0; | |
305 | |
306 /* Zero out the buffer */ | |
307 for (i = 0; i < max_len; ++i) { | |
308 stream->buffer[i] = silence; | |
309 } | |
310 } | |
311 | |
312 /* Deinitialize the stream simply by freeing the buffer */ | |
313 void | |
314 SDL_StreamDeinit(SDL_AudioStreamer * stream) | |
315 { | |
316 if (stream->buffer != NULL) { | |
317 free(stream->buffer); | |
318 } | |
319 } | |
320 | |
259 | 321 |
260 /* The general mixing thread function */ | 322 /* The general mixing thread function */ |
261 int SDLCALL | 323 int SDLCALL |
262 SDL_RunAudio(void *devicep) | 324 SDL_RunAudio(void *devicep) |
263 { | 325 { |
265 Uint8 *stream; | 327 Uint8 *stream; |
266 int stream_len; | 328 int stream_len; |
267 void *udata; | 329 void *udata; |
268 void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len); | 330 void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len); |
269 int silence; | 331 int silence; |
332 int stream_max_len; | |
333 | |
334 /* For streaming when the buffer sizes don't match up */ | |
335 Uint8 *istream; | |
336 int istream_len; | |
270 | 337 |
271 /* Perform any thread setup */ | 338 /* Perform any thread setup */ |
272 device->threadid = SDL_ThreadID(); | 339 device->threadid = SDL_ThreadID(); |
273 current_audio.impl.ThreadInit(device); | 340 current_audio.impl.ThreadInit(device); |
274 | 341 |
275 /* Set up the mixing function */ | 342 /* Set up the mixing function */ |
276 fill = device->spec.callback; | 343 fill = device->spec.callback; |
277 udata = device->spec.userdata; | 344 udata = device->spec.userdata; |
345 | |
346 /* By default do not stream */ | |
347 device->use_streamer = 0; | |
278 | 348 |
279 if (device->convert.needed) { | 349 if (device->convert.needed) { |
280 if (device->convert.src_format == AUDIO_U8) { | 350 if (device->convert.src_format == AUDIO_U8) { |
281 silence = 0x80; | 351 silence = 0x80; |
282 } else { | 352 } else { |
283 silence = 0; | 353 silence = 0; |
284 } | 354 } |
285 stream_len = device->convert.len; | 355 |
356 /* If the result of the conversion alters the length, i.e. resampling is being used, use the streamer */ | |
357 if (device->convert.len_mult != 1 || device->convert.len_div != 1) { | |
358 /* The streamer's maximum length should be twice whichever is larger: spec.size or len_cvt */ | |
359 stream_max_len = 2 * device->spec.size; | |
360 if (device->convert.len_mult > device->convert.len_div) { | |
361 stream_max_len *= device->convert.len_mult; | |
362 stream_max_len /= device->convert.len_div; | |
363 } | |
364 if (SDL_StreamInit(&device->streamer, stream_max_len, silence) < | |
365 0) | |
366 return -1; | |
367 device->use_streamer = 1; | |
368 | |
369 /* istream_len should be the length of what we grab from the callback and feed to conversion, | |
370 so that we get close to spec_size. I.e. we want device.spec_size = istream_len * u / d | |
371 */ | |
372 istream_len = | |
373 device->spec.size * device->convert.len_div / | |
374 device->convert.len_mult; | |
375 } | |
376 | |
377 /* stream_len = device->convert.len; */ | |
378 stream_len = device->spec.size; | |
286 } else { | 379 } else { |
287 silence = device->spec.silence; | 380 silence = device->spec.silence; |
288 stream_len = device->spec.size; | 381 stream_len = device->spec.size; |
289 } | 382 } |
290 | 383 |
291 /* Loop, filling the audio buffers */ | 384 /* Determine if the streamer is necessary here */ |
292 while (device->enabled) { | 385 if (device->use_streamer == 1) { |
293 | 386 /* This code is almost the same as the old code. The difference is, instead of reding |
294 /* Fill the current buffer with sound */ | 387 directly from the callback into "stream", then converting and sending the audio off, |
295 if (device->convert.needed) { | 388 we go: callback -> "istream" -> (conversion) -> streamer -> stream -> device. |
296 if (device->convert.buf) { | 389 However, reading and writing with streamer are done separately: |
297 stream = device->convert.buf; | 390 - We only call the callback and write to the streamer when the streamer does not |
391 contain enough samples to output to the device. | |
392 - We only read from the streamer and tell the device to play when the streamer | |
393 does have enough samples to output. | |
394 This allows us to perform resampling in the conversion step, where the output of the | |
395 resampling process can be any number. We will have to see what a good size for the | |
396 stream's maximum length is, but I suspect 2*max(len_cvt, stream_len) is a good figure. | |
397 */ | |
398 while (device->enabled) { | |
399 /* Only read in audio if the streamer doesn't have enough already (if it does not have enough samples to output) */ | |
400 if (SDL_StreamLength(&device->streamer) < stream_len) { | |
401 /* Set up istream */ | |
402 if (device->convert.needed) { | |
403 if (device->convert.buf) { | |
404 istream = device->convert.buf; | |
405 } else { | |
406 continue; | |
407 } | |
408 } else { | |
409 istream = current_audio.impl.GetDeviceBuf(device); | |
410 if (istream == NULL) { | |
411 istream = device->fake_stream; | |
412 } | |
413 } | |
414 | |
415 /* Read from the callback into the _input_ stream */ | |
416 if (!device->paused) { | |
417 SDL_mutexP(device->mixer_lock); | |
418 (*fill) (udata, istream, istream_len); | |
419 SDL_mutexV(device->mixer_lock); | |
420 } | |
421 | |
422 /* Convert the audio if necessary and write to the streamer */ | |
423 if (device->convert.needed) { | |
424 SDL_ConvertAudio(&device->convert); | |
425 if (istream == NULL) { | |
426 istream = device->fake_stream; | |
427 } | |
428 /*SDL_memcpy(istream, device->convert.buf, device->convert.len_cvt); */ | |
429 SDL_StreamWrite(&device->streamer, device->convert.buf, | |
430 device->convert.len_cvt); | |
431 } else { | |
432 SDL_StreamWrite(&device->streamer, istream, istream_len); | |
433 } | |
434 } | |
435 | |
436 /* Only output audio if the streamer has enough to output */ | |
437 if (SDL_StreamLength(&device->streamer) >= stream_len) { | |
438 /* Set up the output stream */ | |
439 if (device->convert.needed) { | |
440 if (device->convert.buf) { | |
441 stream = device->convert.buf; | |
442 } else { | |
443 continue; | |
444 } | |
445 } else { | |
446 stream = current_audio.impl.GetDeviceBuf(device); | |
447 if (stream == NULL) { | |
448 stream = device->fake_stream; | |
449 } | |
450 } | |
451 | |
452 /* Now read from the streamer */ | |
453 SDL_StreamRead(&device->streamer, stream, stream_len); | |
454 | |
455 /* Ready current buffer for play and change current buffer */ | |
456 if (stream != device->fake_stream) { | |
457 current_audio.impl.PlayDevice(device); | |
458 } | |
459 | |
460 /* Wait for an audio buffer to become available */ | |
461 if (stream == device->fake_stream) { | |
462 SDL_Delay((device->spec.samples * 1000) / | |
463 device->spec.freq); | |
464 } else { | |
465 current_audio.impl.WaitDevice(device); | |
466 } | |
467 } | |
468 | |
469 } | |
470 } else { | |
471 /* Otherwise, do not use the streamer. This is the old code. */ | |
472 | |
473 /* Loop, filling the audio buffers */ | |
474 while (device->enabled) { | |
475 | |
476 /* Fill the current buffer with sound */ | |
477 if (device->convert.needed) { | |
478 if (device->convert.buf) { | |
479 stream = device->convert.buf; | |
480 } else { | |
481 continue; | |
482 } | |
298 } else { | 483 } else { |
299 continue; | 484 stream = current_audio.impl.GetDeviceBuf(device); |
300 } | 485 if (stream == NULL) { |
301 } else { | 486 stream = device->fake_stream; |
302 stream = current_audio.impl.GetDeviceBuf(device); | 487 } |
303 if (stream == NULL) { | 488 } |
304 stream = device->fake_stream; | 489 |
305 } | 490 if (!device->paused) { |
306 } | 491 SDL_mutexP(device->mixer_lock); |
307 | 492 (*fill) (udata, stream, stream_len); |
308 if (!device->paused) { | 493 SDL_mutexV(device->mixer_lock); |
309 SDL_mutexP(device->mixer_lock); | 494 } |
310 (*fill) (udata, stream, stream_len); | 495 |
311 SDL_mutexV(device->mixer_lock); | 496 /* Convert the audio if necessary */ |
312 } | 497 if (device->convert.needed) { |
313 | 498 SDL_ConvertAudio(&device->convert); |
314 /* Convert the audio if necessary */ | 499 stream = current_audio.impl.GetDeviceBuf(device); |
315 if (device->convert.needed) { | 500 if (stream == NULL) { |
316 SDL_ConvertAudio(&device->convert); | 501 stream = device->fake_stream; |
317 stream = current_audio.impl.GetDeviceBuf(device); | 502 } |
318 if (stream == NULL) { | 503 SDL_memcpy(stream, device->convert.buf, |
319 stream = device->fake_stream; | 504 device->convert.len_cvt); |
320 } | 505 } |
321 SDL_memcpy(stream, device->convert.buf, device->convert.len_cvt); | 506 |
322 } | 507 /* Ready current buffer for play and change current buffer */ |
323 | 508 if (stream != device->fake_stream) { |
324 /* Ready current buffer for play and change current buffer */ | 509 current_audio.impl.PlayDevice(device); |
325 if (stream != device->fake_stream) { | 510 } |
326 current_audio.impl.PlayDevice(device); | 511 |
327 } | 512 /* Wait for an audio buffer to become available */ |
328 | 513 if (stream == device->fake_stream) { |
329 /* Wait for an audio buffer to become available */ | 514 SDL_Delay((device->spec.samples * 1000) / device->spec.freq); |
330 if (stream == device->fake_stream) { | 515 } else { |
331 SDL_Delay((device->spec.samples * 1000) / device->spec.freq); | 516 current_audio.impl.WaitDevice(device); |
332 } else { | 517 } |
333 current_audio.impl.WaitDevice(device); | |
334 } | 518 } |
335 } | 519 } |
336 | 520 |
337 /* Wait for the audio to drain.. */ | 521 /* Wait for the audio to drain.. */ |
338 current_audio.impl.WaitDone(device); | 522 current_audio.impl.WaitDone(device); |
523 | |
524 /* If necessary, deinit the streamer */ | |
525 if (device->use_streamer == 1) | |
526 SDL_StreamDeinit(&device->streamer); | |
339 | 527 |
340 return (0); | 528 return (0); |
341 } | 529 } |
342 | 530 |
343 | 531 |