I never understood why “rewriting” samples was ever considered a good design choice. As I recall, DirectSound employs this technique.
The big problem here is that you’re assuming you have enough CPU to write the same sample slots more than once. Most modern users expect to be able to dedicate nearly 100% of the available CPU to meaningful audio calculations. If there were ever enough CPU cycles to afford going back to invalidate and rewrite samples on the fly, then those users would simply crank up the number of plugins and/or the complexity of the synthesis until no spare cycles are left. In other words, you can’t rely on being given an opportunity for a “do over” when it comes to audio. Basically, the best design choice that meets customer expectations is to assume in your code that you only have one chance to write a given sample slot. Reducing latency between parameter updates and audible results simply requires smaller buffers at the hardware level. In response to your earlier question, I was going to say that shifting audio processing to another thread and then feeding the results to the audio thread via a ringbuffer doesn’t actually give you any more time. There is still a hard limit for real time audio. Whatever time duration is represented by the audio buffer (given the sample rate and buffer size), that’s all the time you have for audio processing. Non-audio threads can’t run any faster than the audio thread. The only exception is, as Paul points out, those cases where audio is coming from non-real-time sources like disk I/O or perhaps network streaming. In the latter cases, having a non-audio-thread buffering scheme is a good idea (and necessary, since disk I/O cannot occur on the audio thread). There are probably some special cases like convolution reverb where you can benefit from processing some of the algorithm on a separate thread that feeds the audio thread. But there will still be a requirement that low-latency processing be handling in the audio thread itself, without forgetting that low-latency processing must be bounded by the amount of CPU processing available in each audio time slot. I suppose we should also consider the cases where multi-threaded processing could potentially run faster than the audio thread, in which case feeding the audio thread via a ringbuffer could allow a lot more processing. Note that latency is nearly always increased in these situations. In contrast, if you have an audio application with basically no real-time requirements, e.g., iTunes playback, then you could easily process all audio in a separate thread and feed the finished results to the audio thread with a ring buffer. I believe that iTunes computes the crossfades between tracks in advance - it’s rather entertaining to listen to the audio glitches that occur when seemingly innocuous edits to the playlist trigger iTunes’ heuristics to recalculate that crossfade. iTunes used to be a glitch-free playback system, but there have been so many features tacked on that it has become unreliable. Brian On Feb 26, 2018, at 7:46 PM, Brian Armstrong <[email protected]> wrote: > Thanks, that makes sense. > > Even in the example you suggested, couldn't the producer invalidate/rewrite > the samples in the ring on parameter change? If the consumer reads front to > back, and producer writes back to front, then the change will occur at some > indeterminate point in the stream but will be applied consistently after > that. Assuming correct memory fences anyway, I think. > > Brian > > On Mon, Feb 26, 2018 at 7:36 PM Paul Davis <[email protected]> wrote: >> On Mon, Feb 26, 2018 at 9:16 PM, Brian Armstrong >> <[email protected]> wrote: >>> As a related question, is there any reason not to just feed the audio >>> thread with a ringbuffer filled on a different thread? You could manage >>> pointers either with try-lock or atomics. >> >> that's exactly what you're supposed to do. and what just about every >> correctly implemented audio application does. >> >> except that processing needs to happen in the audio thread to minimize >> latency. imagine the user tweaks the parameter of a plugin. they expect to >> hear the result ASAP. if you pre-process the audio before it hits the audio >> thread, you can't ensure that. so, you disk i/o and other >> non-latency-dependent processing outside the audio thread, write to the >> buffer; audio thread reads from it, runs plugins and the rest, delivers to >> the "hardware" (you don't have access to the hardware with coreaudio, but >> the concept is the same). >> >> you don't need locks on a single-reader/single-writer circular buffer. you >> do need to ensure correct memory ordering. _______________________________________________ Do not post admin requests to the list. They will be ignored. Coreaudio-api mailing list ([email protected]) Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/coreaudio-api/archive%40mail-archive.com This email sent to [email protected]
