The first thing we'll do this round is add setters for the various parameters in our FM synth so we can make some real noise: level, feedback, and ratio for both the modulator and the carrier. (To do: find/write a good explanation of what these parameters do. The hits I get on google for "fm synthesis" are okay, but usually written for a specific program.) On the Lua side we have that subclass problem mentioned previously, so here's how we'll deal with it this time: In addition to returning a playdate.sound.synth object
that uses our custom generator we'll also return a separate control object for doing things specific to our FM synth--that is, what we'd normally put in a subclass.
synth, fm = snd.synth.newFM() -- both control the same synth :(
Here we have synth
to do the normal synth stuff like playing notes and setting envelope parameters, and fm
to tweak those FM parameters we added. We'll use the x/y axes of the accelerometer to change the modulator level and feedback, and the crank to tweak the carrier feedback.
One more thing I want to add in this part: While hacking on the FM algorithm I ran into a lot of bugs that I could hear but I wanted to be able to see them as well. Usually I'd use an audio routing app so that I could record the output of the Simulator to disk and look at it in an audio editor, but I thought it'd be fun to have the audio waveforms on the display as well (and I was curious how well it would perform). Now instead of using the C FMSynth
object as our userdata
in setGenerator
we have a new wrapper struct that contains the FMSynth
, a buffer to store 400 samples, and an offset that tracks where we are in the buffer. The fm
control object has a drawPlot
function that calls playdate->graphics->fillRect
400 times to draw a 1x1 square at each sample position--not the most efficient way to do it, but it seems to perform okay.
fmsynth tutorial pt 3.zip (12.6 KB)