Lab 2: Embedded Audio DSP with Faust and the Teensy

By the end of this lab, you should know how to synthesize and process sound on the Teensy using Faust!

Assembling the Teensy and the Audio Shield

To assemble your Teensy and your audio shield, take the following elements from your lab kit (the Teensy 4.0, the Teensy Audio Shield, and the stackable header sets):

Take a small wire cutter (e.g., the one provided with your 3D printer) and cut all the stackable pin headers on their 15th pin (the Teensy and the audio shield both have 14 pins on both sides). Warning: the cut should be carried out on the 15th pin input itself and not on the junction of the pin! You do not want to damage the neighboring pin as this could prevent your audio shield from working properly!

Place 2 of the stackable header sets on the Teensy as shown on this picture:

Put your assembly on a table and make sure that it is reasonably stable. Note the orientation of the Teensy! The male pins of the headers should come out on the opposite side of the USB port on the Teensy. Make sure that the headers sets are fully perpendicular to the Teensy (90 degrees)! We’re about to solder them and they will need to perfectly align with the female headers on the audio shield!

Start by soldering one pin (any of them) on each header. That should be enough to hold the header on the Teensy. Your soldering point should have a nice “dome/conic” shape covering the hole on the Teensy. After making these 2 solder points, verify that the headers are still perpendicular to the Teensy. A good way to do this is to place the Teensy on your breadboard without pushing it into it. If everything looks straight and square, then you should be good to go ahead with the next step. On the other hand, if one of the headers is not perpendicular, try to adjust it by gently bending it.

Solder all the remaining pins of the headers to the Teensy. After this, your system should look like this:

Once again, note the nice conic shape of the solder points. Be careful not to put too much solder to prevent neighboring pins to connect to each others as this could potentially be harmful for your Teensy. Inversely, not having enough solder on the pin might prevent it from working properly.

Take the two remaining headers sets and insert them on the male headers of the Teensy:

Place the audio shield on top of the Teensy as shown here:

The headphone jack on the audio shield should be on the same side as the USB port on the Teensy! You can now solder all the pins of the audio shield. Recommendations are the same as for the Teensy. After this step, your system should look like this:

If you didn’t make any mistake, you should be ready to rock’n’roll with your Teensy!

Making Sound

The Teensy is programmed using the Arduino software. Install Teensyduino on your system using the following instructions: https://www.pjrc.com/teensy/td_download.html.

Follow the DSP on the Teensy with Faust tutorial on the Faust website. Skip the section on “Audio Effect” (it is slightly outdated and we’ll do that later in this lab). Since Faust is probably not installed on your system, you should use the export (truck) function of the Faust Web IDE to generate Faust Teensy DSP objects (select Teensy as platform and Teensy as architecture). For now, use the headphone jack output on the Teensy as the main output.

Don’t forget to read the section on audio latency of the tutorial! This is important stuff!

Additional Information on Using setParamValue

The parameter name used as the first argument of the setParamValue method is directly connected to the path pointing to a Faust parameter. In the case of a simple Faust program such as:

import("stdfaust.lib");
freq = hslider("freq",440,50,1000,0.01);
gain = hslider("gain",1,0,1,0.01);
process = os.sawtooth(freq)*gain;

setParamValue("freq",val) and setParamValue("gain",val) can be used. This is also true if freq and gain were to be placed in a group:

import("stdfaust.lib");
freq = hslider("freq",440,50,1000,0.01);
gain = hslider("gain",1,0,1,0.01);
process = vgroup("mySaw",os.sawtooth(freq)*gain);

In that case, both

setParamValue("freq",val)
setParamValue("gain",val)

and

setParamValue("/mySaw/freq",val)
setParamValue("/mySaw/gain",val)

can be used (without distinction).

Please, note that an alternative syntax for creating a group in Faust is:

import("stdfaust.lib");
freq = hslider("v:mySaw/freq",440,50,1000,0.01);
gain = hslider("v:mySaw/gain",1,0,1,0.01);
process = os.sawtooth(freq)*gain;

which is exactly the same as using vgroup in the code as in the previous example. The v: should not be used in the parameter path when calling setParamValue, this is very important!

It is fine to declare the same parameter name twice as long as it’s placed in a different group. E.g.,:

import("stdfaust.lib");
freq0 = hslider("v:mySaw/freq",440,50,1000,0.01);
gain0 = hslider("v:mySaw/gain",1,0,1,0.01);
freq1 = hslider("v:myOsc/freq",440,50,1000,0.01);
gain1 = hslider("v:myOsc/gain",1,0,1,0.01);
process = os.sawtooth(freq0)*gain0 + os.osc(freq1)*gain1;

but in this case, you should use the full path to access different parameters:

setParamValue("/mySaw/freq",val)
setParamValue("/mySaw/gain",val)
setParamValue("/myOsc/freq",val)
setParamValue("/myOsc/gain",val)

I.e., setParamValue("freq",val) will only set the frequency for the sawtooth or the sine oscillator, but it’s hard to know which one it will actually be.

Using a parameter name that doesn’t exist with setParamValue will literally make your Teensy crash, yes!! In that case, no sound will be produced. To know if your Teensy is crashed or not, you can place a Serial.println in the loop. If println actually prints something, then your Teensy is alive, if not, then it means that there is something wrong.

To prevent problems, we strongly encourage everyone in Music 250a to not use groups in Faust. Taking the previous example, it’s safer to create unique parameter names instead of using groups. I.e.,

import("stdfaust.lib");
freq0 = hslider("mySawFreq",440,50,1000,0.01);
gain0 = hslider("mySawGain",1,0,1,0.01);
freq1 = hslider("myOscFreq",440,50,1000,0.01);
gain1 = hslider("myOscGain",1,0,1,0.01);
process = os.sawtooth(freq0)*gain0 + os.osc(freq1)*gain1;

Assignment: Music Box/Sound Generator (Due on January 26, 2022)

loop{
  faustInstrument.setParamValue("gate",1);
  faustInstrument.setParamValue("freq",440);
  delay(1000);
  faustInstrument.setParamValue("gate",0);
  delay(100);
  faustInstrument.setParamValue("gate",1);
  faustInstrument.setParamValue("freq",880);
  delay(1000);
  faustInstrument.setParamValue("gate",0);
  delay(100);
}

Note how delays here (in ms) are used to control the “rhythm,” etc. One more thing: controlling pitch with a frequency is not necessarily convenient so you might want to use the ba.midikey2hz Faust function.

Submissions

  • Carlino Cuono
  • Champ Darabundit
  • Dominic DeMarco
  • Reid Devereaux
  • Estelle He
  • Aaron Hodges
  • Kimia Koochakzadeh-Yazdi
  • Giancarlo Ricci
  • Olga Saadi
  • Vardaan Shah
  • Donald Swen
  • Kathleen Yuan
  • Andrew Lee Zhu