I’ve been working on implementing Envelope Generator into microprocessors. Output of an Envelope Generator consists of rising and falling exponential curve. I’m currently doing table lookup to generate those curves.
https://github.com/naokiiwakami/vceg
This is a quick and straightforward approach, and you don’t have to be worried much about data resolution. But the program is a little complicated, and memory consuming. I don’t really like this approach. I don’t really like the approach, so I started to think about alternative approaches.
Mathematically, a exponentially decaying curve can be calculated recursively by following formula:
V(i+1) = V(i) * k
where 0 <= k < 1
This is very simple. You can program this such as:
double value = INITIAL_VALUE;
double decay_factor = 0.99309249543703590153321021688807;
while (true) {
value *= decay_factor;
}
Cbut implementing the algorithm into a microprocessor is not straightforward like this.
I’m planning to use an AVR (e.g. atmega168), it has limited number of registers, and floating point multiplication is generally slow in AVR. So I would like to avoid floating point data types.
So shall I go for simple integer calculation? It’s actually not feasible. For example, if I update the value in every 10ms (100 updates per second) and would like to make a decaying curve that reduces its value to 50% after 1 second, the decay factor k would be
k = 0.5 ^ (1 / 100) = 0.99309249543703590153321021688807
It’s less than 1 so cannot be expressed by an integer.
There’s a technique called fixed point multiplication. This fundamentally uses integers but can handle fractions. This is my first time to try this technique, so learned how you do it. I found following link a good instruction:
Tried to write it in C.
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[]) {
// fixed point integer 10bit.6bit
uint16_t value = (1023) << 6;
// fixed point integer .16bit
uint16_t decay_factor = 0.99309249543703590153321021688807 * 65536;
for (uint16_t i = 0; i < 1000; ++i) {
// multiplication
uint32_t temp = value * decay_factor;
value = temp >> 16;
// retrieve the integer part
uint16_t output = value >> 6;
printf("%d %d\n", i, output, 10);
}
return 0;
}
CRan it on my PC (not on the microprocessor yet). Plotted the result to a graph:
OK it looks good so far.