here is the code:
#include <Bela.h>
#include <vector>
#include "Lv2Host.h"
#include <libraries/Midi/Midi.h>
#include <libraries/Gui/Gui.h>
Lv2Host gLv2Host;
int gAudioFramesPerAnalogFrame;
int gLedPin = 0;
float gUpdateInterval = 0.05;
void Bela_userSettings(BelaInitSettings *settings)
{
settings->uniformSampleRate = 1;
settings->interleave = 0;
settings->analogOutputsPersist = 0;
}
//scales to choose
bool chromatic[12] {1,1,1,1,1,1,1,1,1,1,1,1,};
bool major[12] {1,0,1,0,1,1,0,1,0,1,0,1,};
bool minor[12] {1,0,1,1,0,1,0,1,1,0,0,1,};
bool penta[12] {1,0,1,0,1,0,0,1,0,1,0,0,};
bool whole[12] {1,0,1,0,1,0,1,0,1,0,1,0,};
bool dim[12] {1,0,0,1,0,0,1,0,0,1,0,0,};
bool octave = 0;
bool powercut = 0;
bool echo = 0;
//pointer to choosen scale
bool *scale = chromatic;
bool gIsNoteOn = 0;
int tap_count = 0;
int frame_count = 0;
int gVelocity = 0;
int gNote = 0;
int gControl = 0;
int gCCVal = 0;
bool vocoder = 1;
void midiMessageCallback(MidiChannelMessage message, void* arg){
/* if(arg != NULL){
rt_printf("Message from midi port %s ", (const char*) arg);
} */
// message.prettyPrint();
if(message.getType() == kmmNoteOn){
gNote = message.getDataByte(0);
gVelocity = message.getDataByte(1);
gIsNoteOn = gVelocity > 0;
}
if(message.getType() == kmmControlChange){
gControl = message.getDataByte(0);
gCCVal = message.getDataByte(1);
}
}
Midi midi;
const char* gMidiPort0 = "hw:1,0,0";
bool setup(BelaContext* context, void* userData)
{
midi.readFrom(gMidiPort0);
midi.writeTo(gMidiPort0);
midi.enableParser(true);
midi.setParserCallback(midiMessageCallback, (void*) gMidiPort0);
// these should be initialized by Bela_userSettings above
if((context->flags & BELA_FLAG_INTERLEAVED) || context->audioSampleRate != context->analogSampleRate)
{
fprintf(stderr, "Using Lv2Host requires non-interleaved buffers and uniform sample rate\n");
return false;
}
if(!gLv2Host.setup(context->audioSampleRate, context->audioFrames,
context->audioInChannels, context->audioOutChannels))
{
fprintf(stderr, "Unable to create Lv2 host\n");
return false;
}
std::vector<std::string> lv2Chain;
lv2Chain.emplace_back("http://moddevices.com/plugins/caps/Noisegate");
// lv2Chain.emplace_back("http://moddevices.com/plugins/caps/Compress");
lv2Chain.emplace_back("http://gareus.org/oss/lv2/fat1");
lv2Chain.emplace_back("http://drobilla.net/plugins/mda/TalkBox");
lv2Chain.emplace_back("http://guitarix.sourceforge.net/plugins/gx_oc_2_#_oc_2_");
lv2Chain.emplace_back("http://drobilla.net/plugins/mda/SubSynth");
lv2Chain.emplace_back("http://drobilla.net/plugins/mda/Detune");
lv2Chain.emplace_back("http://drobilla.net/plugins/mda/Stereo");
lv2Chain.emplace_back("http://calf.sourceforge.net/plugins/VintageDelay");
lv2Chain.emplace_back("http://guitarix.sourceforge.net/plugins/gx_reverb_stereo#_reverb_stereo");
for(auto &name : lv2Chain)
{
gLv2Host.add(name);
}
if(0 == gLv2Host.count())
{
fprintf(stderr, "No plugins were successfully instantiated\n");
return false;
}
if(context->analogFrames)
gAudioFramesPerAnalogFrame = context->audioFrames / context->analogFrames;
//noisegate
gLv2Host.setPort(0, 0, -20);
gLv2Host.setPort(0, 2, -30);
//autotune
gLv2Host.setPort(1, 6, 0.5);
gLv2Host.setPort(1, 7, 0.02);
// gLv2Host.setPort(2, 9, 24);
gLv2Host.setPort(1, 11, 1); //fastmode
gLv2Host.setPort(1, 12, 1); //c
gLv2Host.setPort(1, 13, 0);
gLv2Host.setPort(1, 14, 0);
gLv2Host.setPort(1, 15, 1);
gLv2Host.setPort(1, 16, 0);
gLv2Host.setPort(1, 17, 1);
gLv2Host.setPort(1, 18, 0);
gLv2Host.setPort(1, 19, 1);
gLv2Host.setPort(1, 20, 0);
gLv2Host.setPort(1, 21, 0);
gLv2Host.setPort(1, 22, 1);
gLv2Host.setPort(1, 23, 0);
//vocoder (talkbox)
gLv2Host.setPort(2, 0, 1); //wet signal
gLv2Host.setPort(2, 2, 0); //choose carrier channel
gLv2Host.setPort(2, 3, 1); //quality
// octaver off by default
gLv2Host.setPort(3, 3, 0);
gLv2Host.setPort(3, 4, 0);
gLv2Host.setPort(3, 2, 0.3);
//mda subsynth
// gLv2Host.setPort(4,1, 0);
gLv2Host.setPort(4, 3, 1);
gLv2Host.setPort(4, 4, 0);
//mda detune
gLv2Host.setPort(5, 0,0.1 );
gLv2Host.setPort(5, 1,0.5 );
gLv2Host.setPort(5, 3,0.3 );
gLv2Host.setPort(5, 2, 0.3);
//mda stereo
gLv2Host.setPort(6, 0, 0.5);
gLv2Host.setPort(6, 1, 0.3);
//echo
gLv2Host.setPort(7, 11, 0);
gLv2Host.setPort(7, 10, 0);
gLv2Host.setPort(7, 4, 100);
// gLv2Host.setPort(7, 15, 1);
//_reverb
gLv2Host.setPort(8, 0, 40);
gLv2Host.setPort(8, 3, 0.1);
//connect carrier to vocoder directly (without mono fx, gate and compressor)
// gLv2Host.connect(-1,1,2,1);
// scope.setup(4, context->audioSampleRate);
// Turn LED on
pinMode(context, 0, gLedPin, OUTPUT); // Set pin as output
digitalWrite(context, 0, gLedPin, 1); //Turn LED on
return true;
}
void render(BelaContext* context, void* userData)
{
if (!vocoder) {
gLv2Host.disconnect(3,0);
gLv2Host.bypass(1,0);
gLv2Host.bypass(2,1);
gLv2Host.connect(1,0,3,0);
} else {
gLv2Host.disconnect(3,0);
gLv2Host.bypass(1,1);
gLv2Host.bypass(2,0);
gLv2Host.connect(-1,1,2,1);
gLv2Host.connect(0,0,2,0);
gLv2Host.connect(2,0,3,0);
}
frame_count++;
if (frame_count > (1000*tap_count)) {
tap_count = 0;
}
// static bool pluginsOn[3] = {true, true, true};
// set inputs and outputs
const float* inputs[context->audioInChannels];
float* outputs[context->audioOutChannels];
for(unsigned int ch = 0; ch < context->audioInChannels; ++ch)
inputs[ch] = (float*)&context->audioIn[context->audioFrames * ch];
for(unsigned int ch = 0; ch < context->audioOutChannels; ++ch)
outputs[ch] = &context->audioOut[context->audioFrames * ch];
// do the actual processing on the buffers specified above
gLv2Host.render(context->audioFrames, inputs, outputs);
switch (gControl) {
case 7: {
gLv2Host.setPort(2, 3, float(gCCVal/127));
break;
}
case 8: {
gLv2Host.setPort(4, 0, float(gCCVal>>5)/4.0);
break;
}
case 9: {
gLv2Host.setPort(7, 6, (gCCVal/8) + 1);
// gLv2Host.setPort(7, 1, float(gCCVal/ 127.0));
break;
}
case 10: {
gLv2Host.setPort(8, 2, float(gCCVal/ 127.0));
break;
}
}
if(gIsNoteOn == 1){
//logic to switch between non "tonal" and "semitonal" scales
if ((scale == whole) | (scale == dim)) scale = chromatic;
switch (gNote) {
case 20: {
if (!echo) {
gLv2Host.setPort(7, 10, 0.3);
echo = 1;
} else {
gLv2Host.setPort(7, 10, 0);
echo = 0;
}
break;
}
case 21: {
/* if (tap_count == 4) {
float bpm = float(60.0/float(frame_count * 8 / 11025.0));
// gLv2Host.setPort(7, 4, bpm);
gLv2Host.setPort(7, 4, bpm);
rt_printf("bpm: %f\n",bpm);
tap_count = 0;
}
if (!tap_count) frame_count = 0;
if (tap_count < 4) tap_count++;
*/
vocoder = !vocoder;
break;
}
case 22: {
if (!octave) {
gLv2Host.setPort(3, 3, 0.5);
gLv2Host.setPort(3, 4, 0.5);
octave = 1;
} else {
gLv2Host.setPort(3, 3, 0);
gLv2Host.setPort(3, 4, 0);
octave = 0;
}
break;
}
case 23: {
// subsynth
if (!powercut) {
gLv2Host.setPort(4, 2, 1);
powercut = 1;
} else {
gLv2Host.setPort(4, 2, 0);
powercut = 0;
}
break;
}
case 36: {
//set scale, in key of c, no offset
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(2, n + 12, scale[n]);
// rt_printf("value%d: %d\n", n, scale[n]);
}
break;
}
case 37: {
//set scale, in key of c# offset of 1 halftone
if (scale == chromatic) scale = whole;
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+1)%12 + 12), scale[(n)]);
// rt_printf("value%d: %d\n", (n+1)%12, scale[n]);
}
break;
}
case 38: {
//set scale, in key of d offset of 2 halftones
if (scale == chromatic) scale = whole;
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+2)%12 + 12), scale[(n)]);
}
break;
}
case 39: {
scale = chromatic;
break;
}
case 40: {
//set scale, in key of d# offset of 3 halftones
if (scale == chromatic) scale = dim;
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+3)%12 + 12), scale[(n)]);
}
break;
}
case 41: {
//set scale, in key of e offset of 4 halftones
if (scale == chromatic) scale = dim;
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+4)%12 + 12), scale[(n)]);
}
break;
}
case 42: {
//set scale, in key of f offset of 5 halftones
if (scale == chromatic) scale = dim;
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+5)%12 + 12), scale[(n)]);
}
break;
}
case 43: {
scale = major;
break;
}
case 44: {
//set scale, in key of f# offset of 6 halftones
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+6)%12 + 12), scale[(n)]);
}
break;
}
case 45: {
//set scale, in key of g offset of 7 halftones
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+7)%12 + 12), scale[(n)]);
}
break;
}
case 46: {
//set scale, in key of g# offset of 8 halftones
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+8)%12 + 12), scale[(n)]);
}
break;
}
case 47: {
scale = minor;
break;
}
case 48: {
//set scale, in key of a offset of 9 halftones
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+9)%12 + 12), scale[(n)]);
}
break;
}
case 49: {
//set scale, in key of a# offset of 10 halftones
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+10)%12 + 12), scale[(n)]);
}
break;
}
case 50: {
//set scale, in key of b offset of 11 halftones
for(unsigned int n = 0; n < 12; n++){
gLv2Host.setPort(1, ((n+11)%12 + 12), scale[(n)]);
}
break;
}
case 51: {
scale = penta;
break;
}
}
gIsNoteOn = 0;
}
}
void cleanup(BelaContext* context, void* userData) {
}
this code works as intended. to reproduce the "error"/bug you have to remove the two disconnects at start of render() i.e.
if (!vocoder) {
// gLv2Host.disconnect(3,0);
gLv2Host.bypass(1,0);
gLv2Host.bypass(2,1);
gLv2Host.connect(1,0,3,0);
} else {
// gLv2Host.disconnect(3,0);
gLv2Host.bypass(1,1);
gLv2Host.bypass(2,0);
gLv2Host.connect(-1,1,2,1);
gLv2Host.connect(0,0,2,0);
gLv2Host.connect(2,0,3,0);
}
to reproduce you will have to switch the plugins live (currently by sending a note on 21), simply setting bool vocoder
does not show the bug. so to make it clear again, leaving the disconnects out produces a frozen sound when i switch to the vocoder part, having the disconnects in there solves it.