Monday, February 9, 2009

The Rendering Graph: What Renders When?

Using the current pipeline for mixing tracks, I tried to write a metronome synth for QGrids, but that was actually way too mindbending to be fun.

To explain: traditional DSP works with sequential rendering, where each samples value kind of depends on the previous one, and intermediate states need to be saved. Since my concept renders audio offline, and only subportions of a clip may change, the architecture requires that any clip can render one buffer of its range (8192 samples) independent of location.

Given this limitation, it is a bit tricky to render e.g. a metronome sound from somewhere in the middle of the clip. I need to calculate what kind of click I currently am at (to determine pitch) and how far I am away from the clicks location (to determine phase and volume). Right now, this is possible, but kind of impractical, given that most of the time, I still know pitch, phase and volume from the previous location.

The concept breaks apart as soon as I employ delay lines and reverbs, which strongly rely on previous states and additional delay buffers. It is still possible to calculate values independently, but it requires to be able to look up input sample values from other locations. Chances are usually big that these locations have been rendered before, and so I would often recalculate stuff that I already know. Another issue is that I would not be able to use traditional DSP code, because it is usually written for linear signal processing.

Three questions emerge, a.) when I have already rendered a part of the pipeline, how can I prevent to render it again? b.) how can I simulate linear signal processing to simplify algorithms? - and since we are at it, c.) how can I neatly parallelize all this?

a.) Transparent bouncing. Whenever a clip renders, its rendered output is usually written to a cache on disk. When the output is needed again, I just read it from the cache. Since disk space is far from being sparse these days, that should usually not be a problem. DONE

b.) Bounce linearly. Render a clip from start to finish in usual block sizes, and store intermediate states like phase, volume, voices, if applicable. DONE

c.) Invalidate dependencies. Fragments of a clip are dependent on other fragments. Root fragments need to be rendered first, then dependent fragments can be rendered. Most of the time, unrelated clips can render in parallel.

0 comments:

Post a Comment