the code is:
#include <Bela.h>
#include <cmath>
#include <libraries/Scope/Scope.h>
#include <libraries/OscSender/OscSender.h>
float out;
float gFrequency;
float gAmplitude;
int mode;
float masterVolume;
int bypass;
// Analog inputs
float gPot1;
float gPot2;
float gPot7;
// Digital inputs
int gButton = 1;
int gBypass = 3;
int gLastButtonStatus = HIGH;
int gLastBypassStatus = HIGH;
std::vector<int> gNum = {0, 1, 2, 3}; // vector to cylce through that chooses the filter type
std::vector<int> gNumBypass = {0, 1};
unsigned int gNumLocation = 0; // pointer for vector
unsigned int gNumBypassLocation = 0;
Scope scope;
float gPhase;
float gInverseSampleRate;
int gAudioFramesPerAnalogFrame = 0;
OscSender gOscSender;
size_t gRefreshSamples;
constexpr size_t kColumns = 128; // number of columns on your display
float gDisplayData[kColumns];
size_t gDisplayPtr = 0;
AuxiliaryTask gSendToDisplayTask;
void sendToDisplay(void*) {
gOscSender.newMessage("/targetMode");
gOscSender.add(1);
gOscSender.send();
gOscSender.newMessage("/waveform");
gOscSender.add(0);
for(size_t n = 0; n < kColumns; ++n) {
// the value in the buffer was between -1 and 1. Here we make it between 0 and 1
float value = gDisplayData[n] * 0.5f + 0.5f;
gOscSender.add(value);
}
gOscSender.send();
gOscSender.newMessage("/osc-test");
gOscSender.add(1);
gOscSender.send();
}
void sendToDisplayText(void*){
gOscSender.newMessage("/osc-test");
gOscSender.add(0);
gOscSender.send();
}
// define sign function for square wave from https://helloacm.com/how-to-implement-the-sgn-function-in-c/
int sgn(double v) {
return ( ( (v) < 0 ) ? -1 : ( (v) > 0 ) );
}
// signal generation, based on bela's basic.cpp and using definitions from wikipedia
void signal(float gFrequency, float gAmplitude, int mode)
{
switch(mode){
case 0:
out = gAmplitude*sinf(gPhase); // sine
break;
case 1:
out = gAmplitude*asinf(sinf(gPhase)); // triangle
break;
case 2:
out = gAmplitude*sgn(sinf(gPhase)); // square
break;
case 3:
out = gAmplitude*atanf(tanf(gPhase/2)); // sawtooth
break;
}
gPhase += 2.0 * M_PI * gFrequency * gInverseSampleRate;
if(gPhase > 2.0 * M_PI)
gPhase -= 2.0 * M_PI;
}
bool setup(BelaContext *context, void *userData)
{
// setup oscilloscope with one channel
scope.setup(1, context->audioSampleRate);
gInverseSampleRate = 1.0 / context->audioSampleRate;
gPhase = 0.0;
pinMode(context, 0, gButton, INPUT); //set input
pinMode(context, 0, gBypass, INPUT); //set input
// check audio and digital have same no. frames
if(context->audioFrames != context->digitalFrames) {
rt_fprintf(stderr, "This example needs audio and digital running at the same rate.\n");
return false;
}
// calculate no. audio frames per analog frame
if(context->analogFrames)
gAudioFramesPerAnalogFrame = context->audioFrames / context->analogFrames;
// inital signal
signal(440.0, 0.1, 0);
gRefreshSamples = context->audioSampleRate * 0.05; // 50ms refresh rate
gOscSender.setup(7562);
gSendToDisplayTask = Bela_createAuxiliaryTask(sendToDisplay, 5, "sendToDisplay");
return true;
}
float gPastValue = 0;
float gThreshold = 0.01;
void render(BelaContext *context, void *userData)
{
for(unsigned int n = 0; n < context->audioFrames; n++) {
// read analog inputs and update frequency and amplitude
gPot1 = analogRead(context, n/gAudioFramesPerAnalogFrame, 0);
gPot2 = analogRead(context, n/gAudioFramesPerAnalogFrame, 1);
gPot7 = analogRead(context, n/gAudioFramesPerAnalogFrame, 7);
float gFrequency = map(gPot1, 0, 0.8, 1000, 100);
float gAmplitude = map(gPot2, 0, 0.8, 0.5, 0.02);
masterVolume = map(gPot7, 0, 0.8, 0, 1);
// detect falling edge of button and cycle through filter types
int status = digitalRead(context, n, gButton);
if(status == LOW && gLastButtonStatus == HIGH){
gNumLocation++;
if(gNumLocation >= gNum.size()){
gNumLocation = 0;
}
}
gLastButtonStatus = status;
mode = gNum[gNumLocation];
int bypassStatus = digitalRead(context, n, gBypass);
if(bypassStatus == LOW && gLastBypassStatus == HIGH){
gNumBypassLocation++;
if(gNumBypassLocation >= gNumBypass.size()){
gNumBypassLocation = 0;
}
}
gLastBypassStatus = bypassStatus;
bypass = gNumBypass[gNumBypassLocation];
if(bypass==0){
signal(gFrequency, gAmplitude, mode);
}
if(bypass==1){
out = 0;
}
scope.log(out);
for(unsigned int channel = 0; channel < context->audioOutChannels; channel++) {
audioWrite(context, n, channel, masterVolume*out);
}
float value = out; /// fill this in
if(gDisplayPtr < kColumns)
gDisplayData[gDisplayPtr] = value;
bool trigger = false;
if(gPastValue < gThreshold && value >= gThreshold)
trigger = true; // trigger when crossing the threshold with a positive edge
if(0 == gDisplayPtr && !trigger)
; // do nothing
else
gDisplayPtr++;
if(gDisplayPtr == kColumns) {
// the buffer is full, schedule the other task, which will send the data to the display
Bela_scheduleAuxiliaryTask(gSendToDisplayTask);
}
if(gDisplayPtr == gRefreshSamples)
gDisplayPtr = 0;
}
}
void cleanup(BelaContext *context, void *userData)
{
}
And I believe sendToDisplay is being called every 50 ms