Monday, January 19, 2015

Ramp generation

If the running time of the matrix calculations is constant and triggered by a timer, generating a ramp is simply the task of adding the same number to an accumulator variable every cycle.

But since the number added is never exactly the number necessary for a correct ramp progression (we are dealing with integers after all), we will accumulate an error. This may be ok, but we should keep control of how big the error may become and take actions to prevent it from becoming too big.

If we work with 16 bit numbers, we will get an error of maximum 1/65536 per cycle. But assuming we are running at 44.1kHz, this will accumulate to an error of 44100/65535 = 67% in one second! Not very accurate I'd say.

Lets do the cslculation the opposite way. If we decide that yhe maximum acceptable error is 1% per second, what resolution do we need for our interval?

Well, in one second, we should be at most 0.01 * 65536 = 655 away from the correct answer.  At 44.1kHz, this means that the maximum error per cycle must be less than 655/44100 = 0.015, or 1/67th of the error we get from using an integer.

To remedy this, we may store the accumulator and the value to add as a number with more bits (in doing this, we will multiply the interval, or rather, calculate it differently) and divide the accumulator down to 16 bits when the output is needed.

To get the necessary precision we need to be able to store 1/67ths. By adding 6 bits to the number we can store 1/64 fractions, which is not quite 1/64 but may be good enough, or we can add an additional two bits and get a 1/256 precision, making the accumulator and interval variables 24bit (it may be practical to make them 32bit while at it, not sure).

Converting the accumulator back to 16bit is simply a matter of shifting it 8bits to the right.

Looping or multi stage ramps (envelopes)


When the ramp reaches the desired height, we want to stop adding to it, reset it or even start subtracting from it. This means that every time we have added something to the accumulator, we should check its value and possibly perform an action. We may even have to keep track of state, as what stage of an envelope we are in. The state, as well as the accumulator and value to add should be stored within the ramp node's struct if we want a truly node-type agnostic algorithm that lets us have any number of any type of node (unlike the Matrix 12 which has a fixed number of ramps, LFOs etc).

No comments:

Post a Comment