Bela
Real-time, ultra-low-latency audio and sensor processing system for BeagleBone Black
Loading...
Searching...
No Matches
Trill/bar-sound/render.cpp

Trill Bar Multitouch Theremin

This example shows how to communicate with the Trill Bar sensor using the Trill library.

Each touch on the sensor controls the pitch and volume of an oscillator.

The Trill sensor is scanned on an auxiliary task running parallel to the audio thread and the number of active touches, their position and size stored as global variables.

Position and size for each touch are then mapped to frequency and amplitude of their corresponding oscillator. Changes in frequency and amplitude are smoothed using low pass filters to avoid artifacts.

/*
____ _____ _ _
| __ )| ____| | / \
| _ \| _| | | / _ \
| |_) | |___| |___ / ___ \
|____/|_____|_____/_/ \_\
http://bela.io
*/
#include <Bela.h>
#include <cmath>
#include <libraries/Trill/Trill.h>
#include <libraries/OnePole/OnePole.h>
#include <libraries/Oscillator/Oscillator.h>
#define NUM_TOUCH 5 // Number of touches on Trill sensor
// Trill object declaration
Trill touchSensor;
// Location of touches on Trill Bar
float gTouchLocation[NUM_TOUCH] = { 0.0, 0.0, 0.0, 0.0, 0.0 };
// Size of touches on Trill Bar
float gTouchSize[NUM_TOUCH] = { 0.0, 0.0, 0.0, 0.0, 0.0 };
// Number of active touches
int gNumActiveTouches = 0;
// Sleep time for auxiliary task in microseconds
unsigned int gTaskSleepTime = 12000; // microseconds
// One Pole filters objects declaration
OnePole freqFilt[NUM_TOUCH], ampFilt[NUM_TOUCH];
// Frequency of one pole filters
float gCutOffFreq = 5, gCutOffAmp = 15;
// Oscillators objects declaration
Oscillator osc[NUM_TOUCH];
// Range for oscillator frequency mapping
float gFreqRange[2] = { 200.0, 1500.0 };
// Range for oscillator amplitude mapping
float gAmplitudeRange[2] = { 0.0, 1.0 } ;
/*
* Function to be run on an auxiliary task that reads data from the Trill sensor.
* Here, a loop is defined so that the task runs recurrently for as long as the
* audio thread is running.
*/
void loop(void*)
{
{
// Read locations from Trill sensor
touchSensor.readI2C();
gNumActiveTouches = touchSensor.getNumTouches();
for(unsigned int i = 0; i < gNumActiveTouches; i++) {
gTouchLocation[i] = touchSensor.touchLocation(i);
gTouchSize[i] = touchSensor.touchSize(i);
}
// For all inactive touches, set location and size to 0
for(unsigned int i = gNumActiveTouches; i < NUM_TOUCH; i++) {
gTouchLocation[i] = 0.0;
gTouchSize[i] = 0.0;
}
usleep(gTaskSleepTime);
}
}
bool setup(BelaContext *context, void *userData)
{
// Setup a Trill Bar sensor on i2c bus 1, using the default mode and address
if(touchSensor.setup(1, Trill::BAR) != 0) {
fprintf(stderr, "Unable to initialise Trill Bar\n");
return false;
}
touchSensor.printDetails();
// Set and schedule auxiliary task for reading sensor data from the I2C bus
// For each possible touch...
for(unsigned int i = 0; i < NUM_TOUCH; i++) {
// Setup corresponding oscillator
osc[i].setup(context->audioSampleRate, Oscillator::sine);
// Setup low pass filters for smoothing frequency and amplitude
freqFilt[i].setup(gCutOffFreq, context->audioSampleRate);
ampFilt[i].setup(gCutOffAmp, context->audioSampleRate);
}
return true;
}
void render(BelaContext *context, void *userData)
{
for(unsigned int n = 0; n < context->audioFrames; n++) {
float out = 0.0;
/* For each touch:
*
* - Map touch location to frequency of the oscillator
* and smooth value changes using a single pole LP filter
* - Map touch size toa amplitude of the oscillator and
* smooth value changes using a single pole LP filter
* - Compute oscillator value and add to output.
* - The overall output will be scaled by the number of touches.
*/
for(unsigned int i = 0; i < NUM_TOUCH; i++) {
float frequency, amplitude;
frequency = map(gTouchLocation[i], 0, 1, gFreqRange[0], gFreqRange[1]);
// Uncomment the line below to apply a filter to the frequency of the oscillators
// frequency = freqFilt[i].process(frequency);
amplitude = map(gTouchSize[i], 0, 1, gAmplitudeRange[0], gAmplitudeRange[1]);
amplitude = ampFilt[i].process(amplitude);
out += (1.f/NUM_TOUCH) * amplitude * osc[i].process(frequency);
}
// Write computed output to audio channels
for(unsigned int channel = 0; channel < context->audioOutChannels; channel++) {
audioWrite(context, n, channel, out);
}
}
}
void cleanup(BelaContext *context, void *userData)
{}
Main Bela public API.
Definition OnePole.h:12
Definition Oscillator.h:3
A class to use the Trill family of capacitive sensors. http://bela.io/trill.
Definition Trill.h:14
@ BAR
Trill Bar
Definition Trill.h:34
AuxiliaryTask Bela_runAuxiliaryTask(void(*callback)(void *), int priority=0, void *arg=nullptr)
Create and start an AuxiliaryTask.
int Bela_stopRequested()
Check whether the program should stop.
static void audioWrite(BelaContext *context, int frame, int channel, float value)
Write an audio output, specifying the frame number (when to write) and the channel.
Definition Bela.h:1469
void render(BelaContext *context, void *userData)
User-defined callback function to process audio and sensor data.
Definition render.cpp:68
bool setup(BelaContext *context, void *userData)
User-defined initialisation function which runs before audio rendering begins.
Definition render.cpp:51
void cleanup(BelaContext *context, void *userData)
User-defined cleanup function which runs when the program finishes.
Definition render.cpp:96
static float map(float x, float in_min, float in_max, float out_min, float out_max)
Linearly rescale a number from one range of values to another.
Definition Utilities.h:71
Structure holding audio and sensor settings and pointers to I/O data buffers.
Definition Bela.h:231
const uint32_t audioOutChannels
The number of audio output channels.
Definition Bela.h:326
const uint32_t audioFrames
The number of audio frames per block.
Definition Bela.h:322
const float audioSampleRate
The audio sample rate in Hz (currently always 44100.0).
Definition Bela.h:328