I know the space for code is limited on the espruino, but would there be space to add a simple sine wave oscillator in the Espruino?
The motivation for this is that I use a couple of espruino boards to drive some WS2812 LED installations (mostly goodnight light installation for my kids). These use slow sines waving colors in and out (from different directions and over time), which has proven to be very good to fall into sleep.
However the pure Math.sin call is pretty expensive and often just avoidable given the fact that the distance from one sample to the next stays the same. Thus a simple (sine) oscillator would work wonders performance wise. Unfortunately, if I implement these in JS code it's both too heavy in terms of memory and processing speed.
However, since the underlying math is dead simple and the implementation very small, I'd like to ask whether it is possible to implement this in the Espruino firmware.
What I want is something like this:
osc = new Oscillator(frequency, phase, amplitude); // phase defaults to 0, amplitude to 1
After the installation, sequent calls to the following member should be enough:
osc.getSineValueAndAdvance; // or a shorter name like getValue
It should just spit out the current sine value and advance to the next sample
The implementation is easy and just rely on 4 values stored in an array. The values are actually just two complex values. Let's call them 'position' and 'angle'.
The 'position' will contain the current sine (real) and cosine (imaginary) value and 'angle' the angle advance on the unit circle.
Now for every call to the function the current (real) position value should get returned. After that the next 'position' value is obtained by code like this (a simple butterfly algorithm):
var temp = position.re * angle.im - position.re * angle.im;
position.im = position.re * angle.re + position.im * angle.im;
position.re = temp;
The above code works for the normed unit circle (with the radius 1). However, since floating point calculations are a bit expensive on some boards, it also works if the values are fixed point. Often, even 16 bit internal values work sufficiently. This means position and angle are int16, where the unit circle radius of 1 becomes 2^31-1. With this, the integer multiplication must still be scaled (simple bit shift) to fit.
If implemented in assembler, the code could be very tiny and thus worth the addition.
What remains missing is the implementation. This is just slightly more complicated like:
angle.re = Math.sin(phase);
angle.im = Math.cos(phase);
pos.re = Math.sin(frequency)*amplitude;
pos.im = Math.cos(frequency)*amplitude;
In the above code the amplitude is taken into account directly. However, in case of a fixed point implementation it makes sense to stay at the maxInt precision and just multiply the value per sample converting the integer into float again.
I don't know how much work this will be or how big the footprint will be in the end, but at least for my projects it will be a big improvement. The alternative is to supply the above functions by hand, which is luckily possible with the inline assembler or nativeCalls
PS: Don't take the pseudo code above for grant, it is written without testing just out of my head. For in-depth information you can look at this site: https://dspguru.com/dsp/howtos/how-to-create-oscillators-in-software/
© Espruino, powered by microcosm.
Report a problem