Tutorial 01. Program structure and audio output

There are several functions that form the skeleton of any MEAP program. The first set of tutorials are designed to get you familiar with this structure that you will be building your program around.

Like many computer music programs, MEAP separates events into two categories:

When working with a microcontroller, it is important to understand that it won't be able to handle everything you throw at it the way a modern laptop or desktop computer might and we need to do our best to not ask too much of it. A big part of this is not asking it to calculate things more often than we need them. Running as many events as possible at control rate rather than audio rate will help ease the load!

. . . . . . .

The following functions occur in every MEAP program and are the main places you will be adding code.


AudioOutput_t updateAudio()

updateAudio() runs at the sampling rate. It is in charge of generating new samples of audio and sending them to the output DAC. This is where most of your audio generation code will live; such as grabbing samples from oscillators or processing them with filters or effects.


void updateControl()

updateControl() runs at the control rate. This is where the brains of your program go. Changing the frequency of oscillators, running compositional structures, etc.


void setup()

setup() is executed once each time your board is powered up or reset. Code placed in here is typically used for initialization; for example setting the initial frequency of an oscillator, the shape of an envelope or the starting state of a generative algorithm.


void loop()

loop() runs repeatedly at a high speed between calls to updateControl() and updateAudio(). It handles some behind the scenes parts of the audio engine, and generally you should avoid adding extra code to this section as too many calculations may disrupt your sampling rate.


void updateTouch()

updateTouch() is automatically called whenever one of the touch pads is pressed or released. This is where you handle anything you may want to be controlled by the touch pads.


void updateDip()

updateDip() is automatically called whenever one of the DIP switches is toggled up or down. This is where you handle anything you may want to be controlled by the DIP switches.


. . . . . . .

So let's write our first MEAP program!

  1. Start by opening MEAP_BASIC_TEMPLATE within the 0_Testing_And_Templates folder of the MEAP library. This template is a good starting point for any MEAP program as it includes all of the code blocks you will typically use.

    You can find this template:

    • through the Arduino IDE's examples menu: File > examples > Meap-main > 0_Testing_And_Templates > MEAP_BASIC_TEMPLATE > MEAP_BASIC_TEMPLATE.ino
    • or on the MEAP GitHub

    Create a new Arduino sketch by pressing CMD+N and copy all of the template code into it.

  2. Most of the audio functions we will be using to begin with come from classes in the Mozzi library which are all extensively documented. For this tutorial, we will be using the mOscil class (which is based on Mozzi's Oscil class, with a few minor improvements) which implements a basic wavetable-based oscillator. Before we can use the class, however, we will need to choose a wavetable, which will determine the waveform of our oscillator. The MEAP library includes a number of wavetables which can be found in the tables directory of the MEAP library folder. In this example we will be using the sin8192_int16 wavetable.

    To include this wavetable, add the following line to your global variable section (beneath the line that says // ---------- YOUR GLOBAL VARIABLES BELOW ----------)

    #include<tables/sin8192_int16.h>

  3. Now that we have included the wavetable in our sketch, we can create an oscillator that uses it. Add the following line to your global variable section as well:

    mOscil<sin8192_int16_NUM_CELLS, AUDIO_RATE, int16_t> my_sine(sin8192_int16_DATA);

    This line:

    1. creates an oscillator object: mOscil
    2. tells the object how many samples are in the wavetable: sin8192_int16_NUM_CELLS
    3. specifies that the oscillator will be an audio rate oscillator (rather than a control rate LFO): AUDIO_RATE
    4. specifies that the wavetable contains 16-bit samples: int16_t
    5. names the oscillator object: my_sine
    6. and finally points it to the wavetable we included: sin8192_int16_DATA

  4. sin8192_int16_NUM_CELLS and sin8192_int16_DATA are macros which are defined in sin8192_int16.h. Every table will have macros named in this way.

  5. In the setup() function, we want to specify the frequency of this oscillator. Beneath the // ---------- YOUR SETUP CODE BELOW ---------- line, add the following:

    my_sine.setFreq(220);

    This will tell the oscillator to continuously cycle at 220Hz (an A3 in 12-tone equal temperament) until it is told to change to a different frequency.

  6. Now we just need to connect this oscillator to the audio output.

    In the updateAudio() function, rather than setting the variable out_sample equal to zero we want to set it to the output of my_sine as follows.

    int out_sample = my_sine.next();

    Finally we need to tell the output what bit-rate of audio we are dealing with. We defined a 16-bit oscillator (which has numbers oscillating between -32768 and 32767) and by default, the output expects 8-bit audio (numbers between -128 and 127), so if we leave this as is, we'll hear a lot of distortion as the output is given numbers outside the range it is expecting.

    Change the last line of the updateAudio() function to the following:

    return StereoOutput::fromNBit(16, (out_sample * meap.volume_val) >> 12, (out_sample * meap.volume_val) >> 12);

    This will allow the output to accept numbers up to a 16-bit range. We'll look more at this concept of bit-rate being tied to volume in future tutorials, but for now just know that the bitrate specified in our output function needs to be at least as high as the loudest audio we send to it to avoid distortion.

    Upload the code to your MEAP

    Enable the speaker (turn the speaker switch, located near the potentiometers, to the right) or plug in headphones.

    Turn up the volume knob (the center of the three knobs)

    You should hear a constant sine tone.

  7. Save this code as it will be the starting point of the next couple tutorials.

FULL CODE HERE