LFOs stand for Low Frequency Oscillators. There are many different types of LFOs, the most common of which are triangle, square, sawtooth and sinusoidal.
Different types of LFOs. Source: [Square wave, Wikipedia](https://en.wikipedia.org/wiki/Square_wave)
In this recipe we'll focus on the sinusoidal LFO.
As the name implies, LFOs are essentially algorithms that transition around a key displacement incrementally over time. The ‘low’ in LFO refers to the relatively slow and predictable rate at which the transitions occur, generally at a speed slow enough for human perception.
An example of an LFO, using the sine wave. Source: [Sine Wave, Wikipedia](https://simple.wikipedia.org/wiki/Sine_wave)
The term ‘LFO’ is rooted in audio engineering and electronic music – those of you who work in audio would have come across this when working with audio signal effect chains, and/or amplifiers. However LFOs are not just specific to the audio world. They are known in other circles such as animation and motion graphics, too.
LFOs are not to be confused with easing curves. LFOs _oscillate_ around a set point, while easing curves _interpolate_ from one point to another, over a predefined curve.
Making sense of the LFO
Referring to the graph above, visualise the dotted line as the ‘key’ value – for example, this could be the incoming data stream that you are receiving from a sensor feed, or some other data source. The red sine curve is the undulating ‘texture’ that oscillates around this ‘key’ value. This means that the final reading will be offset by this oscillation.
Both the amplitude and frequency of the sine wave can be further manipulated, which gives you an opportunity to create an even more organic ‘texture’. And like all data points, amplitude and frequency can be dynamically modified by other data streams to generate highly organic movement that can be potentially non-repeating and ‘natural’ looking. On top of this, when the key value changes, the LFO ‘travels’ along with that base key, giving you a nice, self-contained algorithm that is highly tune-able to your needs.
In our case, we can implement LFOs to generate organic swells and textures to an otherwise slow-changing data stream. This makes your expressions far more natural, as they will continue to have basic movement even when no new data is coming in.
Sample output of this recipe, visualised using the Arduino IDE's Serial Plotter. The keyValue is set at 90, with the amplitude also set to 90, hence achieving a full 0-180 sweep that will make the servo connected at pin `D0` do a full sinusoidal sweep. These values, along with the frequency (i.e. wavelength), can be easily set in IoTa through the exposed Particle Functions (see code below).
This recipe presents a DIY solution that does not rely on 3rd-party libraries, and instead utilises a simple sine wave algorithm to demonstrate how the concept works. You might find it really useful to combine this recipe with Sensor Ramp or Data Smoothies. Once you figure out how to integrate the two, you can then graduate towards applying custom easing curves using formulae beyond the simple sine wave function.
A neat trick here is to use the [Arduino IDE's](https://www.arduino.cc) Serial Plotter feature as a very fast way to visualise data coming directly out from the Photon. You need to connect your Photon via the USB cable to view this data stream. Check also the Serial.printlnf() command on how the values are printed and formatted. A third-party utility called [RealtimePlotter](https://github.com/sebnil/RealtimePlotter) – gives you even better visualisation options from your Serial port.
Sample flows showing the 3 Particle Functions used to change the LFO parameters dynamically. **`newKey`** sets the key displacement value from which the LFO oscillates around; **`newAmp`** sets the amplitude; **`newRes`** sets the resolution of the curve (higher = slower)
Going Further
This recipe shows probably the ‘slowest’ method in getting a sine-based LFO to work (i.e. engineers and coders will shake their heads in horror at the sheer wastage and excess). Performing trigonometry functions on a microcontroller can be costly, both in terms of computing power and memory.
If you refer to the code below, the double datatype is a double-precision float which requires 64 bits per variable declared. That's a whopping 8 bytes for just one variable. This adds up pretty quickly on a microcontroller, particularly if you intend to run multiple LFOs at the same time.
Unless you are working on an extreme case scenario, optimisation can sometimes be more of an academic pursuit. The math.h library does a decent job of the trig functions, and as you can see in this recipe, it is more than capable of driving the LFOs smoothly.
Sine Look-up Tables (LUT) - a computationally efficient alternative method
Look-up tables are an alternative to generating sine values on the fly. The idea is to have a pre-generated list of sine values that you expect to use at a particular amplitude and frequency. Using this ‘look-up table’, all you need to do is to iterate over the values in this list to get the smooth readouts expected from a sine curve.
Because this array is pre-generated, the only calculations needed are simpler arithmetic to scale the amplitude to a desired range. The benefit here is if you generate large integer values for the amplitude, floating point (decimal) calculations are completely avoided which gives much faster performance. The drawback to this method, however, is the resolution – you can't generate another look-up table with finer or coarser resolution easily, and will have to manage be limited to coarser multiples of the original resolution which might not provide a clean transition between the start and end of the sine curves when you use non-even multipliers.
Long story short:
Stick with dynamically-calculated sine tables if you're just beginning to learn this concept
Use dynamically-calculated sine tables to start out, work out the parameters that require dynamic updates
Convert to using LUTs if your LFOs are not going to change frequency (i.e. constant speed/wavelength)
When using LUTs, set a high enough amplitude so you can perform integer math instead of floating point Math
Optimisation trick: An even more efficient way of generating a pure sine LUT is to define just a 1/4 wave – the other three quarters are simply reflected sequences of the first quarter that you can easily iterate backwards/invert in code!
math.h (For code variant 1: this is a built-in library, so you don't need to explicitly do the import sequence (but you'll still need the #include statment)