Generating Waveforms

This is just a small note about how to generate waveforms when the Teensy Audio Library isn’t an option—for example, if you are trying to embed the DRV2667 in a much smaller or lower-cost application.

The most common way to do waveform synthesis on a generic microcontroller is to write waveform-specific routines that interact with a phase accumulator. Over here in the old, outdated WEFT repo you can see the phase accumulator code:

void loop() {

  analogWrite(pwmOut, int(4096 * dutyCycle));

  float DACval = 0;
  phase = phase + (phaseOffset / 1000.0);
  if (phase >= twopi) {
    phase = 0;

  switch (waveType) {
    case SINE:
      DACval = sin(phase) * DACamplitude + 2050.0;
      // 2050.0 is DC offset?
    case SQUARE:
      // if phase > pi then 1 else 0
      (phase > twopi / 2) ? (DACval = (DACamplitude / maxAmpl) * 4095.0) : (DACval = 0.0);
    case SAW_DESC:
      // phase itself is linearly ramping
      DACval = floatmap(phase, 0, twopi, 0.0, 1.0) * (DACamplitude / maxAmpl) * 4095.0;
    case SAW_ASC:
      // phase itself is linearly ramping
      DACval = floatmap(phase, 0, twopi, 1.0, 0.0) * (DACamplitude / maxAmpl) * 4095.0;
    case NOISE:
      (random(0, 9) > 4.5) ? (DACval = (DACamplitude / maxAmpl) * 4095.0) : (DACval = 0.0);
      DACval = sin(phase) * DACamplitude + 2050.0;

  DACval = 4095.0 - DACval;
  analogWrite(A14, (int)DACval);


…as you can see, for each case of the wavetype we want to generate, we do a different operation on the phase accumulator variable called phase. With each iteration of the main loop, phase is incremented a tiny bit (the phaseOffset), looping back around to zero once it hits 2𝜋. This works well and is easy to experiment with (ie, what happens if you change phaseOffset?), but it is not very accurate in terms of reproducing exact frequencies. Also, adding waveforms or having them interact (summed, multiplied, etc) quickly gets complex.

But! Because Teensy comes with such an excellent Audio Library, we can make a more flexible signal-generation system like so:
Teensy Audio Waveforms

…in this example, we start up one each of all our waveforms right at the beginning of the sketch, then use the mixer objects to suppress, sum, or highlight one or many waveforms as we wish. Keeping a static number of waveforms going all the time helps with memory usage, keeping it predictable, and allows frequency adjustments to be handled in a nice modular method on its own:

void updateFreq(int newPos) {
  ff = constrain(floatmap(newPos, encLoLimit, encUpLimit, minFreq, maxFreq), minFreq, maxFreq);
  //  Serial.println("changing freq to " + newPos);

There is one more way, presently, to generate waveforms—with the DRV2667’s built-in waveform synthesis methods. Since it is pretty involved (but well-designed!), I’m going to focus on analog waveforms for now. Ask me about this when we’re wrapping up, though, as there are some cool features to expand upon!