This project uses Bela GUI library to build a drum sample sequencer, and it is a good example of sending data back and forth between Bela (render.cpp) and the GUI (sketch.js). The GUI displays an 8x8 matrix of rectangles, in which each column is a beat and each row corresponds to a speceific WAV sound file that is loaded to the project and stored in a buffer. The user can activate and deactivate the rectangles by clicking on them. For each beat, Bela will receive from the GUI the sound files for that column which are active and it will play them. At the same time, Bela will send a message back to the GUI on each beat to update what is shown.
Note that for each beat we can have up to eight different sounds playing simultaneously (more if a sound is still playing from the previous beat!) so we need multiple read pointers reading from multiple buffers at the same time. An easy way to achieve this is to generate a series of read pointers (i.e., an array of ints where each int will indicate the position of the file sound where we should read).
When a sound starts playing we will assign a new read pointer to the specific buffer. This will tell in the position where we are reading. For example let's say sample number 5 (i.e. gDrumSampleBuffers[4]) starts playing when no other sound is playing. Then, we will assign the first available read pointer to that buffer. As no other sound is playing, then gReadPointers[0] will keep track of where in the buffer we are reading. Additionally we need an array to store the fact that gReadPointers[0] corresponds to gDrumSampleBuffers[4]. Thus, the array gDrumBufferForReadPointer[] will indicate this. This means that gDrumBufferForReadPointer[0]=4;
#include <iostream>
#include <libraries/sndfile/sndfile.h>
#include <libraries/Gui/Gui.h>
#include <cmath>
#include "drums.h"
using namespace std;
float *gDrumSampleBuffers[NUMBER_OF_DRUMS];
int gDrumSampleBufferLengths[NUMBER_OF_DRUMS];
int gDrumBufferForReadPointer[NUMBER_OF_READPOINTERS];
int gReadPointers[NUMBER_OF_READPOINTERS];
int gPattern[NUMBER_OF_DRUMS][PATTERN_LENGTH];
int gPatternLength = PATTERN_LENGTH;
int gEventIntervalMilliseconds = 150;
int gCounter = 0;
int gCurrentBeat = 0;
int gIsPlaying = 0;
{
if(initDrums()) {
printf("Unable to load drum sounds. Check that you have all the WAV files!\n");
return -1;
}
for (int i = 0; i<NUMBER_OF_DRUMS; i++){
for (int j = 0; j<PATTERN_LENGTH; j++){
gPattern[i][j]= 0;
}
}
myGui.setBuffer('f', 11);
return true;
}
{
for (
unsigned int n = 0; n < context->
audioFrames; n++) {
gCounter++;
if (gCounter * 1000 / context->
audioSampleRate >= gEventIntervalMilliseconds ){
gCounter=0;
updatePattern();
if ((int) gIsPlaying == 1) {
startNextBeat();
}
}
for (int i=0;i<16;i++) {
if (gDrumBufferForReadPointer[i]>=0){
if (gReadPointers[i] < gDrumSampleBufferLengths[gDrumBufferForReadPointer[i]]) {
gReadPointers[i]++;
}
else {
gReadPointers[i]=0;
gDrumBufferForReadPointer[i]=-1;
}
}
}
float out = 0;
for (int i=0;i<16;i++){
if (gDrumBufferForReadPointer[i]>=0)
out += gDrumSampleBuffers[gDrumBufferForReadPointer[i]][gReadPointers[i]];
}
out /= 2;
}
}
void updatePattern() {
if ((int)data[10]==gCurrentBeat){
for (unsigned int i = 0; i < 8; i++){
gPattern[i][gCurrentBeat] = (int)data[i];
}
gEventIntervalMilliseconds=400-(int)data[8];
gIsPlaying = (int)data[9];
}
}
void startNextBeat() {
for (int i = 0; i < NUMBER_OF_DRUMS; i++){
if (gPattern[i][gCurrentBeat]){
startPlayingDrum(i);
}
}
gCurrentBeat++;
if(gCurrentBeat > 7)
gCurrentBeat = 0;
myGui.sendBuffer(1, gCurrentBeat);
}
void startPlayingDrum(int drumIndex) {
for (int i=0; i < 16;i++){
if (gDrumBufferForReadPointer[i] == -1) {
gDrumBufferForReadPointer[i]=drumIndex;
gReadPointers[i]=0;
break;
}
}
}
int initDrums() {
SNDFILE *sndfile ;
SF_INFO sfinfo ;
char filename[64];
for(int i = 0; i < NUMBER_OF_DRUMS; i++) {
snprintf(filename, 64, "./drum%d.wav", i);
if (!(sndfile = sf_open (filename, SFM_READ, &sfinfo))) {
printf("Couldn't open file %s\n", filename);
for(int j = 0; j < i; j++)
free(gDrumSampleBuffers[j]);
return 1;
}
if (sfinfo.channels != 1) {
printf("Error: %s is not a mono file\n", filename);
for(int j = 0; j < i; j++)
free(gDrumSampleBuffers[j]);
return 1;
}
gDrumSampleBufferLengths[i] = sfinfo.frames;
gDrumSampleBuffers[i] = (float *)malloc(gDrumSampleBufferLengths[i] * sizeof(float));
if(gDrumSampleBuffers[i] == NULL) {
printf("Error: couldn't allocate buffer for %s\n", filename);
for(int j = 0; j < i; j++)
free(gDrumSampleBuffers[j]);
return 1;
}
int subformat = sfinfo.format & SF_FORMAT_SUBMASK;
int readcount = sf_read_float(sndfile, gDrumSampleBuffers[i], gDrumSampleBufferLengths[i]);
for(int k = readcount; k < gDrumSampleBufferLengths[i]; k++)
gDrumSampleBuffers[i][k] = 0;
if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) {
double scale ;
int m ;
sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
if (scale < 1e-10)
scale = 1.0 ;
else
scale = 32700.0 / scale ;
printf("Scale = %f\n", scale);
for (m = 0; m < gDrumSampleBufferLengths[i]; m++)
gDrumSampleBuffers[i][m] *= scale;
}
sf_close(sndfile);
}
return 0;
}
{
cleanupDrums();
}
void cleanupDrums() {
for(int i = 0; i < NUMBER_OF_DRUMS; i++)
free(gDrumSampleBuffers[i]);
}
Definition DataBuffer.h:12
float * getAsFloat()
Definition DataBuffer.h:103
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
Structure holding audio and sensor settings and pointers to I/O data buffers.
Definition Bela.h:231
const uint32_t audioFrames
The number of audio frames per block.
Definition Bela.h:322
char projectName[MAX_PROJECTNAME_LENGTH]
Name of running project.
Definition Bela.h:417
const float audioSampleRate
The audio sample rate in Hz (currently always 44100.0).
Definition Bela.h:328