yes! it works... great work! i tested on the patch i sent you and also on a more complex patch where i have more routing going on. next test will be to bypass and change out plugins while running (not in setup)

It should be fairly cheap to do the bypassing and rerouting in render(). The only possible negative consequence is that you'll find more of my bugs 🙂.

    giuliomoro what is the default behaviour of mono plugins regarding stereo input in LV2Host? does it connect both outputs (from another plugin or adc) to the mono input? my tests seem to indicate that.

    no, inputs are never summed at the moment, but a given input can be duplicated to several outputs.

    If I understand correctly the code at these lines does, the current behaviour is as follows, I think:

    • if you have more inputs than outputs, the last few inputs will be filled with copies of the last output.
      e.g.:
      MONOout -> LRin
      
      the LRin plug in will have duplicated copies of the MONOout signal into its L and R inputs.
      - If you have more outputs than inputs, the last input will be filled with the last output .
      e.g.:
      LRout -> MONOin
      
      the MONOin plugin will have its input connected to the R output of the LRout plugin
      The latter behaviour doesn't seem particularly clever to me. It should take the L value instead.

      Can you verify practically if the above is true?

      giuliomoro no, inputs are never summed at the moment, but a given input can be duplicated to several outputs.

      ah my bad, you wrote that before...

      my findings with a mono plugin as the only plugin in a chain are:

      • only the left channel of the stereo input is going through the plugin
        -i only get output from bela only on the left channel

      i am fairly certain my test procedure is right, i am sending two hard panned and totally different audio signals from my DAW.

      this seems fairly logical to me like that. have to run, will test further later on

        lokki this seems fairly logical to me like that. have to run, will test further later on

        to me too, and it is my recollection of how I programmed it. However, reading my code suggested me otherwise. One of the two versions of me (the one who wrote it and the one who read it) must be smarter than the other, or maybe just luckier 🙂

        ok, some more testing done...it all works when switching out plugins in the chain from render.cpp which is great!

        to bypass a plugin at slot 1 and enable slot 2 (and vice versa) and connect to the rest of the chain i have to do something like this:

        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);
        	}

        note that in the vocoder case i also have to send the direct adc signal to the second input, to get carrier and modulator :-)

        ideally, since you said an input can only have one port connected the disconnects should not be needed if i run another connect, right? so whenever the connect function is run it should run the respective disconnect function first.
        in the state it is now if i don't do the disconnects like so:

        if (!vocoder) {
        	gLv2Host.bypass(1,0);
        	
        	gLv2Host.bypass(2,1);
        	gLv2Host.connect(1,0,3,0);
        	} else {
        	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);
        	}

        the vocoder does not work (i just get a frozen sound, like a repeated buffer or similar) once i switch back to non vocoder (autotune plugin) it works again.

        full code, or at least the name and order of the plugins? disconnect() assigns an empty buffer to the disconnected inputs, but that should be overridden by the next call to connect(). This may not be true for the input (-1) and output (numSlots).

        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.

        9 days later

        giuliomoro coming back to the heavy lv2 custom render file i have a strange behaviour that you maybe can explain?

        if i run this simple test patch:

        /*
         ____  _____ _        _    
        | __ )| ____| |      / \   
        |  _ \|  _| | |     / _ \  
        | |_) | |___| |___ / ___ \ 
        |____/|_____|_____/_/   \_\
        
        The platform for ultra-low latency audio and sensor processing
        
        http://bela.io
        
        A project of the Augmented Instruments Laboratory within the
        Centre for Digital Music at Queen Mary University of London.
        http://www.eecs.qmul.ac.uk/~andrewm
        
        (c) 2016 Augmented Instruments Laboratory: Andrew McPherson,
          Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack,
          Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved.
        
        The Bela software is distributed under the GNU Lesser General Public License
        (LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt
        */
        
        
        #include <Bela.h>
        
        
        int gCount = 0; //counts elapsed samples
        int tap = 0; //count taps 
        
        #define DEBOUNCE_TIME 40 //how long the button has to be "off" to be considered off
        bool cState_b0 = 0;
        bool cState_b1 = 0;
        bool pState_b0 = 1;
        bool pState_b1 = 1;
        int debounce_b0 = 0;
        int debounce_b1 = 0;
        int switch_fx = 0;
        int old_fx = 1;
        
        
        bool setup(BelaContext *context, void *userData)
        {
             pinMode(context,0,1, OUTPUT); // LED1
             pinMode(context,0,2, OUTPUT); // LED2
             pinMode(context,0,3, INPUT); //Button 1
             pinMode(context,0,4, INPUT); //Button 2
        	return true;
        }
        
        void render(BelaContext *context, void *userData)
        {
        
        	for(unsigned int n = 0; n < context->digitalFrames; ++n){
        		cState_b0 = digitalRead(context, n, 4);
                cState_b1 = digitalRead(context, n, 3);
                gCount++;
        	}
        	if (gCount > 100000*(tap+1)) tap = 0;
        	if (!cState_b0 && pState_b0) {
          switch_fx = (switch_fx + 1)%3;
         }
        if (cState_b0 && !pState_b0) {
          if (debounce_b0 < DEBOUNCE_TIME) {
            debounce_b0++;
            cState_b0 = 0;
          } else debounce_b0 = 0;
         }
        pState_b0 = cState_b0;
        
        if (!cState_b1 && pState_b1) {
        	//taptempo
        	if (!tap) gCount = 0;
        	tap = tap + 1;
        	if (tap == 5) {
        		tap = 0;
        	float tempo = 240.0/float(gCount/context->digitalSampleRate);
        	rt_printf("tempo: %f\n", tempo);
        		
        	}
         }
        if (cState_b1 && !pState_b1) {
          if (debounce_b1 < DEBOUNCE_TIME) {
            debounce_b1++;
            cState_b1 = 0;
          } else debounce_b1 = 0;
         }
        pState_b1 = cState_b1;
        if (switch_fx != old_fx) {
        switch (switch_fx) {
         case 0: {
           digitalWrite(context, 0, 1, 1);
           digitalWrite(context, 0, 2, 1);
           break;
         }
         case 1: {
           digitalWrite(context, 0, 1, 0);
           digitalWrite(context, 0, 2, 1);
           break;
         }
          case 2: {
           digitalWrite(context, 0, 1, 1);
           digitalWrite(context, 0, 2, 0);
           break;
         } 
         }
        }
        old_fx = switch_fx;
        
        }
        
        void cleanup(BelaContext *context, void *userData)
        {
        	// Nothing to do here
        }

        everything works as expected, as i press the button either both, the left or the right LED are lit up to indicate at which switch_fx i am.

        however if i run the same things in my big render.cpp file (the one with heavy and lv2) i put the above code into the heavy (default render() function). of course before my if statements that switch out the heavy and lv2 part.

        now for the strange part:

        the switching works just fine, but the LED's don't go dark when they should, they stay lit all the time albeit at a little different brightnesses depending on the value of switch_fx. as soon as i remove the if (switch_fx != old_fx) { bracket around the digitalWrite part it works as expected, but it seems like a waste of CPU to run the digitalWrite even if nothing has changed. or is this just how it works? but why would it then work in the example patch by just writing once per change?

          Full code? It could be that something else is resetting the value of those channels and/or changing their direction

          sure (still in development, so a bit messy)

          /*
           ____  _____ _        _    
          | __ )| ____| |      / \   
          |  _ \|  _| | |     / _ \  
          | |_) | |___| |___ / ___ \ 
          |____/|_____|_____/_/   \_\
          
          The platform for ultra-low latency audio and sensor processing
          
          http://bela.io
          
          A project of the Augmented Instruments Laboratory within the
          Centre for Digital Music at Queen Mary University of London.
          http://www.eecs.qmul.ac.uk/~andrewm
          
          (c) 2016 Augmented Instruments Laboratory: Andrew McPherson,
          	Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack,
          	Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved.
          
          The Bela software is distributed under the GNU Lesser General Public License
          (LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt
          */
          
          #include <Bela.h>
          #include <libraries/Midi/Midi.h>
          #include <libraries/Scope/Scope.h>
          #include <cmath>
          #include <Heavy_bela.h>
          #include <string.h>
          #include <stdlib.h>
          #include <string>
          #include <sstream>
          #include <DigitalChannelManager.h>
          #include <algorithm>
          #include <array>
          #include <SampleLoader.h>
          #include <SampleData.h>
          #include <BelaContextFifo.h>
          #include <vector>
          #include "Lv2Host.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;
          }
          
          bool heavy_lv2 = 0;
          //patch switching switch 
          #define DEBOUNCE_TIME 200 //how long the button has to be "off" to be considered off
          bool cState_b0 = 0;
          bool pState_b0 = 1;
          int debounce_b0 = 0;
          bool cState_b1 = 0;
          bool pState_b1 = 1;
          int debounce_b1 = 0;
          int fx_nr = 0;
          int old_fx = 1;
          
          
          //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 gVelocity = 0;
          int gNote = 0;
          int gControl = 0;
          int oldControl = 0;
          int gCCVal = 0;
          int choose_fx = 0;
          int tap_count = 0;
          int frame_count = 0;
          bool vocoder = 1;
          
          BelaContextFifo gBcf;
          double gBlockDurationMs;
          enum { minFirstDigitalChannel = 10 };
          
          static unsigned int gAudioChannelsInUse;
          static unsigned int gAnalogChannelsInUse;
          static unsigned int gDigitalChannelsInUse;
          unsigned int gScopeChannelsInUse;
          static unsigned int gChannelsInUse;
          static unsigned int gFirstAnalogChannel;
          static unsigned int gFirstDigitalChannel;
          static unsigned int gDigitalChannelOffset;
          static unsigned int gFirstScopeChannel;
          
          static unsigned int gDigitalSigInChannelsInUse;
          static unsigned int gDigitalSigOutChannelsInUse;
          
          float* gScopeOut;
          // Bela Scope
          static Scope* scope = NULL;
          static char multiplexerArray[] = {"bela_multiplexer"};
          static int multiplexerArraySize = 0;
          static bool pdMultiplexerActive = false;
          bool gDigitalEnabled = 0;
          
          // Bela Midi
          unsigned int hvMidiHashes[7]; // heavy-specific
          static std::vector<Midi*> midi;
          std::vector<std::string> gMidiPortNames;
          
          void longRender(BelaContext* context, void* arg)
          {
          	/*	if (fx_nr == 1) {
          	gLv2Host.disconnect(3,0);
          	gLv2Host.bypass(1,0);
          	
          	gLv2Host.bypass(2,1);
          	gLv2Host.connect(1,0,3,0);
          	} else if (fx_nr == 2) {
          	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);
          	} */
          		if (fx_nr == 1) {
          	gLv2Host.disconnect(3,0);
          	gLv2Host.disconnect(5,0);
          	gLv2Host.bypass(1,0);
          	gLv2Host.bypass(3,0);
          	gLv2Host.bypass(4,0);
          	gLv2Host.bypass(2,1);
          	gLv2Host.bypass(5,1);
          	gLv2Host.connect(1,0,3,0);
          	gLv2Host.connect(3,0,4,0);
          	gLv2Host.connect(4,0,6,0);
          	
          	} else if (fx_nr==2) {
          		
          	gLv2Host.disconnect(3,0);
          	gLv2Host.disconnect(5,0);
          	gLv2Host.disconnect(6,0);
          	gLv2Host.bypass(1,1);
          	gLv2Host.bypass(3,1);
          	gLv2Host.bypass(4,1);
          	gLv2Host.bypass(5,0);
          	gLv2Host.bypass(2,0);
          	gLv2Host.connect(-1,1,2,1);
          	gLv2Host.connect(0,0,2,0);
          	gLv2Host.connect(2,0,5,0);
          	gLv2Host.connect(5,0,6,0);
          	}
          frame_count++;
          if (frame_count > (1000*tap_count)) {
          	tap_count = 0;
          }
           // code here runs at "long" blocksize
           
          	
          //	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.0));
          					break;
          				}	
          				case 8: {
          						gLv2Host.setPort(4, 0, float(gCCVal>>5)/4.0);
          					break;
          				}	
          				case 9: {
          						gLv2Host.setPort(8, 6, (gCCVal/8) + 1);
          					//	gLv2Host.setPort(7, 1, float(gCCVal/ 127.0));
          					break;
          				}	
          				case 10: {
          					gLv2Host.setPort(9, 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(8, 10, 0.3);
          			
          				echo = 1;
          			} else {
          			gLv2Host.setPort(8, 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, 1);
          				gLv2Host.setPort(3, 4, 1);
          				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(1, 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 longThread(void*)
          {
             while(!gShouldStop)
            {
              //   BelaContext* context = gBcf.pop(BelaContextFifo::kToLong, gBlockDurationMs * 2);
              BelaContext* context = gBcf.pop(BelaContextFifo::kToLong, 100); // wait up to 100ms
                  if(context)
                  {
                 //     ((InternalBelaContext*)context)->audioFramesElapsed = audioFramesElapsed; // keep track of elapsed samples if your longRender needs it
                      longRender(context, NULL);
          //            audioFramesElapsed += context->audioFrames;
                      gBcf.push(BelaContextFifo::kToShort, context);
                  } else {
                   //   if(gRTAudioVerbose)
                     //   rt_fprintf(stderr, "fifoTask did not receive a valid context\n");
                      usleep(10000); // TODO: this  should not be needed, given how the timeout in pop() is for a reasonable amount of time
                  }
            }
          }
          AuxiliaryTask longThreadTask;
          
          
          
          void dumpMidi()
          {
          	if(midi.size() == 0)
          	{
          		printf("No MIDI device enabled\n");
          		return;
          	}
          	printf("The following MIDI devices are enabled:\n");
          	printf("%4s%20s %3s %3s %s\n",
          			"Num",
          			"Name",
          			"In",
          			"Out",
          			"Pd channels"
          	      );
          	for(unsigned int n = 0; n < midi.size(); ++n)
          	{
          		printf("[%2d]%20s %3s %3s (%d-%d)\n",
          			n,
          			gMidiPortNames[n].c_str(),
          			midi[n]->isInputEnabled() ? "x" : "_",
          			midi[n]->isOutputEnabled() ? "x" : "_",
          			n * 16 + 1,
          			n * 16 + 16
          		);
          	}
          }
          
          Midi* openMidiDevice(std::string name, bool verboseSuccess = false, bool verboseError = false)
          {
          	Midi* newMidi;
          	newMidi = new Midi();
          	newMidi->readFrom(name.c_str());
          	newMidi->writeTo(name.c_str());
          	newMidi->enableParser(true);
          	if(newMidi->isOutputEnabled())
          	{
          		if(verboseSuccess)
          			printf("Opened MIDI device %s as output\n", name.c_str());
          	}
          	if(newMidi->isInputEnabled())
          	{
          		if(verboseSuccess)
          			printf("Opened MIDI device %s as input\n", name.c_str());
          	}
          	if(!newMidi->isInputEnabled() && !newMidi->isOutputEnabled())
          	{
          		if(verboseError)
          			fprintf(stderr, "Failed to open  MIDI device %s\n", name.c_str());
          		return nullptr;
          	} else {
          		return newMidi;
          	}
          }
          
          static unsigned int getPortChannel(int* channel){
          	unsigned int port = 0;
          	while(*channel > 16){
          		*channel -= 16;
          		port += 1;
          	}
          	if(port >= midi.size()){
          		// if the port number exceeds the number of ports available, send out
          		// of the first port
          		rt_fprintf(stderr, "Port out of range, using port 0 instead\n");
          		port = 0;
          	}
          	return port;
          }
          
          /*
           *	HEAVY CONTEXT & BUFFERS
           */
          
          HeavyContextInterface *gHeavyContext;
          float *gHvInputBuffers = NULL, *gHvOutputBuffers = NULL;
          unsigned int gHvInputChannels = 0, gHvOutputChannels = 0;
          uint32_t multiplexerTableHash;
          
          float gInverseSampleRate;
          
          /*
           *	HEAVY FUNCTIONS
           */
          
          
          void printHook(HeavyContextInterface *context, const char *printLabel, const char *msgString, const HvMessage *msg) {
          	const double timestampSecs = ((double) hv_msg_getTimestamp(msg)) / hv_getSampleRate(context);
          	rt_printf("Message from Heavy patch: [@ %.3f] %s: %s\n", timestampSecs, printLabel, msgString);
          }
          
          
          // digitals
          static DigitalChannelManager dcm;
          
          void sendDigitalMessage(bool state, unsigned int delay, void* receiverName){
          	hv_sendFloatToReceiver(gHeavyContext, hv_stringToHash((char*)receiverName), (float)state);
          //	rt_printf("%s: %d\n", (char*)receiverName, state);
          }
          
          
          std::vector<std::string> gHvDigitalInHashes;
          void generateDigitalNames(unsigned int numDigitals, unsigned int digitalOffset, std::vector<std::string>& receiverInputNames)
          {
          	std::string inBaseString = "bela_digitalIn";
          	for(unsigned int i = 0; i<numDigitals; i++)
          	{
          		receiverInputNames.push_back(inBaseString + std::to_string(i+digitalOffset));
          	}
          }
          
          // For a message to be received here, you need to use the following syntax in Pd:
          // [send receiverName @hv_param]
          static void sendHook(
          		HeavyContextInterface *context,
          		const char *receiverName,
          		hv_uint32_t sendHash,
          		const HvMessage *m) {
          
          	// Bela digital run-time messages
          
          	// TODO: this first block is almost an exact copy of libpd's code, should we add this to the class?
          	// let's make this as optimized as possible for built-in digital Out parsing
          	// the built-in digital receivers are of the form "bela_digitalOutXX" where XX is between 11 and 26
          	static const int prefixLength = 15; // strlen("bela_digitalOut")
          	if(strncmp(receiverName, "bela_digitalOut", prefixLength)==0){
          		if(receiverName[prefixLength] != 0){ //the two ifs are used instead of if(strlen(source) >= prefixLength+2)
          			if(receiverName[prefixLength + 1] != 0){
          				// quickly convert the suffix to integer, assuming they are numbers, avoiding to call atoi
          				int receiver = ((receiverName[prefixLength] - '0') * 10);
          				receiver += (receiverName[prefixLength+1] - '0');
          				unsigned int channel = receiver - gDigitalChannelOffset; // go back to the actual Bela digital channel number
          				bool value = (hv_msg_getFloat(m, 0) != 0.0f);
          				if(channel < gDigitalChannelsInUse){ //gDigitalChannelsInUse is the number of digital channels
          					dcm.setValue(channel, value);
          				}
          			}
          		}
          		return;
          	}
          
          	// More MIDI and digital messages. To obtain the hashes below, use hv_stringToHash("yourString")
          	switch (sendHash) {
          		case 0xfb212be8: { // bela_setMidi
          			if (!hv_msg_hasFormat(m, "sfff")) {
          				fprintf(stderr, "Wrong format for Bela_setMidi, expected:[hw 1 0 0(");
          				return;
          			}
          			const char* symbol = hv_msg_getSymbol(m, 0);
          			int num[3] = {0, 0, 0};
          			for(int n = 0; n < 3; ++n)
          			{
          				num[n] = hv_msg_getFloat(m, n + 1);
          			}
          			std::ostringstream deviceName;
          			deviceName << symbol << ":" << num[0] << "," << num[1] << "," << num[2];
          			printf("Adding Midi device: %s\n", deviceName.str().c_str());
          			Midi* newMidi = openMidiDevice(deviceName.str(), true, true);
          			if(newMidi)
          			{
          				midi.push_back(newMidi);
          				gMidiPortNames.push_back(deviceName.str());
          			}
          			dumpMidi();
          			break;
          		}
          		case 0x70418732: { // bela_setDigital
          			if(gDigitalEnabled)
          			{
          				// Third argument (optional) can be ~ or sig for signal-rate, message-rate otherwise.
          				// [in 14 ~(
          				// |
          				// [s bela_setDigital]
          				// is signal("sig" or "~") or message("message", default) rate
          				bool isMessageRate = true; // defaults to message rate
          				bool direction = 0; // initialize it just to avoid the compiler's warning
          				bool disable = false;
          				if (!(hv_msg_isSymbol(m, 0) && hv_msg_isFloat(m, 1))) return;
          				const char *symbol = hv_msg_getSymbol(m, 0);
          				if(strcmp(symbol, "in") == 0){
          					direction = INPUT;
          				} else if(strcmp(symbol, "out") == 0){
          					direction = OUTPUT;
          				} else if(strcmp(symbol, "disable") == 0){
          					disable = true;
          				} else {
          					return;
          				}
          				int channel = hv_msg_getFloat(m, 1) - gDigitalChannelOffset;
          				if(disable == true){
          					dcm.unmanage(channel);
          					return;
          				}
          				if(hv_msg_isSymbol(m, 2)){
          					const char *s = hv_msg_getSymbol(m, 2);
          					if(strcmp(s, "~") == 0  || strncmp(s, "sig", 3) == 0){
          						isMessageRate = false;
          					}
          				}
          				dcm.manage(channel, direction, isMessageRate);
          			}
          			break;
          		}
          		case 0xd1d4ac2: { // __hv_noteout
          			if (!hv_msg_hasFormat(m, "fff")) return;
          			midi_byte_t pitch = (midi_byte_t) hv_msg_getFloat(m, 0);
          			midi_byte_t velocity = (midi_byte_t) hv_msg_getFloat(m, 1);
          			int channel = (midi_byte_t) hv_msg_getFloat(m, 2);
          			int port = getPortChannel(&channel);
          		//	rt_printf("noteon[%d]: %d %d %d\n", port, channel, pitch, velocity);
          			midi[port]->writeNoteOn(channel, pitch, velocity);
          			break;
          		}
          		case 0xe5e2a040: { // __hv_ctlout
          			if (!hv_msg_hasFormat(m, "fff")) return;
          			midi_byte_t value = (midi_byte_t) hv_msg_getFloat(m, 0);
          			midi_byte_t controller = (midi_byte_t) hv_msg_getFloat(m, 1);
          			int channel = (midi_byte_t) hv_msg_getFloat(m, 2);
          			int port = getPortChannel(&channel);
          		//	rt_printf("controlchange[%d]: %d %d %d\n", port, channel, controller, value);
          			midi[port]->writeControlChange(channel, controller, value);
          			break;
          		}
          		case 0x8753e39e: { // __hv_pgmout
          			midi_byte_t program = (midi_byte_t) hv_msg_getFloat(m, 0);
          			int channel = (midi_byte_t) hv_msg_getFloat(m, 1);
          			int port = getPortChannel(&channel);
          		//	rt_printf("programchange[%d]: %d %d\n", port, channel, program);
          			midi[port]->writeProgramChange(channel, program);
          			break;
          		}
          		case 0xe8458013: { // __hv_bendout
          			if (!hv_msg_hasFormat(m, "ff")) return;
          			unsigned int value = ((midi_byte_t) hv_msg_getFloat(m, 0)) + 8192;
          			int channel = (midi_byte_t) hv_msg_getFloat(m, 1);
          			int port = getPortChannel(&channel);
          		//	rt_printf("pitchbend[%d]: %d %d\n", port, channel, value);
          			midi[port]->writePitchBend(channel, value);
          			break;
          		}
          		case 0x476d4387: { // __hv_touchout
          			if (!hv_msg_hasFormat(m, "ff")) return;
          			midi_byte_t pressure = (midi_byte_t) hv_msg_getFloat(m, 0);
          			int channel = (midi_byte_t) hv_msg_getFloat(m, 1);
          			int port = getPortChannel(&channel);
          		//	rt_printf("channelPressure[%d]: %d %d\n", port, channel, pressure);
          			midi[port]->writeChannelPressure(channel, pressure);
          			break;
          		}
          		case 0xd5aca9d1: { // __hv_polytouchout, not currently supported by Heavy. You have to [send __hv_polytouchout]
          			if (!hv_msg_hasFormat(m, "fff")) return;
          			midi_byte_t pitch = (midi_byte_t) hv_msg_getFloat(m, 0);
          			midi_byte_t pressure = (midi_byte_t) hv_msg_getFloat(m, 1);
          			int channel = (midi_byte_t) hv_msg_getFloat(m, 2);
          			int port = getPortChannel(&channel);
          		//	rt_printf("polytouch[%d]: %d %d %d\n", port, channel, pitch, pressure);
          			midi[port]->writePolyphonicKeyPressure(channel, pitch, pressure);
          			break;
          		}
          		case 0x6511de55: { // __hv_midiout, not currently supported by Heavy. You have to [send __hv_midiout]
          			if (!hv_msg_hasFormat(m, "ff")) return;
          			midi_byte_t byte = (midi_byte_t) hv_msg_getFloat(m, 0);
          			int port = (int) hv_msg_getFloat(m, 1);
          		//	rt_printf("port: %d, byte: %d\n", port, byte);
          			midi[port]->writeOutput(byte);
          			break;
          		}
          		case 0x6E64CDC1: { //save_table custom
          		   if (!hv_msg_hasFormat(m, "f")) return;
          		   int bank_nr = (int) hv_msg_getFloat(m, 0);
          		   char fileName4[10];
          		    sprintf(fileName4, "presets%d.wav", bank_nr);
          		    //const char fileName[] = "presets.wav";
          		const char tableName[] = "presets";
          
          		hv_uint32_t tableHash = hv_stringToHash(tableName);
          
          			float * table = hv_table_getBuffer(gHeavyContext, tableHash);
          
          			writeSamples(fileName4, table, 656);
          		break;
          		}
          		  	  case 0x116A3F3C: { //switch_preset_file
          		    if (!hv_msg_hasFormat(m, "f")) return;
          		    int bank_nr = (int) hv_msg_getFloat(m, 0);
          		    char fileName3[10];
          		    sprintf(fileName3, "presets%d.wav", bank_nr);
          		const char tableName[] = "presets";
          int sampleLen = getNumFrames(fileName3);
          hv_uint32_t tableHash = hv_stringToHash(tableName);
          hv_table_setLength(gHeavyContext, tableHash, sampleLen); // resize the table
          float * table = hv_table_getBuffer(gHeavyContext, tableHash); // once resized, get a pointer to the array
          int channel = 0; // take the first channel of the file
          int startFrame = 0; // start from the beginning
          int lastFrame = sampleLen; // until the end of the file
          getSamples(fileName3, table, channel, startFrame, lastFrame);
          	
          		break;
          		}
          		
          	/*	case 0x5466427a: { //save current preset for restart
          		  
          		    const char fileName[] = "current.wav";
          		const char tableName[] = "current";
          		hv_uint32_t tableHash = hv_stringToHash(tableName);
          
          			float * table = hv_table_getBuffer(gHeavyContext, tableHash);
          
          			writeSamples(fileName, table, 2);
          		break;
          		} */
          		  
          		default: {
          			break;
          		}
          	}
          }
          
          
          /*
           * SETUP, RENDER LOOP & CLEANUP
           */
          
          
          bool setup(BelaContext *context, void *userData)	{
          		pinMode(context,0,1, OUTPUT); // LED1
               pinMode(context,0,2, OUTPUT); // LED2
               pinMode(context,0,3, INPUT); //Button 1
               pinMode(context,0,4, INPUT); //Button 2
          	
          	        int fifoFactor = 8; // 16 to 128
                  BelaContext* longContext = gBcf.setup(context, fifoFactor);
                  if(!longContext)
                  {
                  	fprintf(stderr, "Error: unable to initialise BelaContextFifo\n");
                       return false;}
                 longThreadTask = Bela_createAuxiliaryTask(longThread, 94, "long-thread", NULL);
                 Bela_scheduleAuxiliaryTask(longThreadTask);
               gBlockDurationMs = context->audioFrames * fifoFactor / context->audioSampleRate * 1000;
                
                //long thread setup (lv2host)
          
          	// 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(longContext->audioSampleRate, longContext->audioFrames,
          				longContext->audioInChannels, longContext->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://moddevices.com/plugins/caps/Compress");
          	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, -15);
          	gLv2Host.setPort(0, 2, -20);
          	//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, 1);
          				
          					//mda subsynth
          				//	gLv2Host.setPort(4,1, 0);
          	gLv2Host.setPort(4, 3, 1);
          	gLv2Host.setPort(4, 4, 0);
          	//compressor
          	gLv2Host.setPort(5, 1, 2);
          	gLv2Host.setPort(5, 3, 0.6);
          	gLv2Host.setPort(5, 4, 0.3);
          //	gLv2Host.setPort(5, 6, 20);
          	//mda detune
          	gLv2Host.setPort(6, 0,0.1 );
          	gLv2Host.setPort(6, 1,0.5 );
          gLv2Host.setPort(6, 3,0.3 );
          	gLv2Host.setPort(6, 2, 0.3);
          //mda stereo
          	gLv2Host.setPort(7, 0, 0.5);
          	gLv2Host.setPort(7, 1, 0.3);
          	
          
          				
          	//echo
          	
          	
          	gLv2Host.setPort(8, 11, 0);
          	gLv2Host.setPort(8, 10, 0);
          	gLv2Host.setPort(8, 4, 100);
          //	gLv2Host.setPort(7, 15, 1);
          	//_reverb
          	
          	gLv2Host.setPort(9, 0, 40);
          	gLv2Host.setPort(9, 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;
          
          	// Check if digitals are enabled
          	if(context->digitalFrames > 0 && context->digitalChannels > 0)
          		gDigitalEnabled = 1;
          
          	gAudioChannelsInUse = std::max(context->audioInChannels, context->audioOutChannels);
          	gAnalogChannelsInUse = std::max(context->analogInChannels, context->analogOutChannels);
          	gDigitalChannelsInUse = context->digitalChannels;
          
          	// Channel distribution
          	gFirstAnalogChannel = std::max(context->audioInChannels, context->audioOutChannels);
          	gFirstDigitalChannel = gFirstAnalogChannel + std::max(context->analogInChannels, context->analogOutChannels);
          	if(gFirstDigitalChannel < minFirstDigitalChannel)
          		gFirstDigitalChannel = minFirstDigitalChannel; //for backwards compatibility
          	gDigitalChannelOffset = gFirstDigitalChannel + 1;
          	gFirstScopeChannel = gFirstDigitalChannel + gDigitalChannelsInUse;
          
          	gChannelsInUse = gFirstScopeChannel + gScopeChannelsInUse;
          	
          	// Create hashes for digital channels
          	generateDigitalNames(gDigitalChannelsInUse, gDigitalChannelOffset, gHvDigitalInHashes);
          
          	/* HEAVY */
          	std::array<std::string,  11> outs = {{
          		"__hv_noteout",
          		"__hv_ctlout",
          		"__hv_pgmout",
          		"__hv_touchout",
          		"__hv_polytouchout",
          		"__hv_bendout",
          		"__hv_midiout",
          		"switch_bank",
          		"save_table",
          		"last_state",
          	}};
          /*	for(auto &st : outs)
          	{
          		// uncomment this if you want to display the hashes for midi
          		// outs. Then hardcode them in the switch() in sendHook()
          	  //	printf("%s: %#x\n", st.c_str(), hv_stringToHash(st.c_str()));
          	} */
          	hvMidiHashes[kmmNoteOn] = hv_stringToHash("__hv_notein");
          //	hvMidiHashes[kmmNoteOff] = hv_stringToHash("noteoff"); // this is handled differently, see the render function
          	hvMidiHashes[kmmControlChange] = hv_stringToHash("__hv_ctlin");
          	// Note that the ones below are not defined by Heavy, but they are here for (wishing) forward-compatibility
          	// You need to receive from the corresponding symbol in Pd and unpack the message, e.g.:
          	//[r __hv_pgmin]
          	//|
          	//[unpack f f]
          	//|   |
          	//|   [print pgmin_channel]
          	//[print pgmin_number]
          	hvMidiHashes[kmmProgramChange] = hv_stringToHash("__hv_pgmin");
          	hvMidiHashes[kmmPolyphonicKeyPressure] = hv_stringToHash("__hv_polytouchin");
          	hvMidiHashes[kmmChannelPressure] = hv_stringToHash("__hv_touchin");
          	hvMidiHashes[kmmPitchBend] = hv_stringToHash("__hv_bendin");
          
          	gHeavyContext = hv_bela_new_with_options(context->audioSampleRate, 10, 2, 0);
          
          	gHvInputChannels = hv_getNumInputChannels(gHeavyContext);
          	gHvOutputChannels = hv_getNumOutputChannels(gHeavyContext);
          	
          	// add the lines below
          	//	int k = 0;
          	/*	const char fileName[] = "presets0.wav";
          const char tableName[] = "presets";
             //sprintf(lengthName, "samplelength%d", k);
          int sampleLen = getNumFrames(fileName);
          hv_uint32_t tableHash = hv_stringToHash(tableName);
          hv_table_setLength(gHeavyContext, tableHash, sampleLen); // resize the table
          float * table = hv_table_getBuffer(gHeavyContext, tableHash); // once resized, get a pointer to the array
          int channel = 0; // take the first channel of the file
          int startFrame = 0; // start from the beginning
          int lastFrame = sampleLen; // until the end of the file
          getSamples(fileName, table, channel, startFrame, lastFrame);
          */
          const char fileName5[] = "current.wav";
          const char tableName5[] = "current";
          int sampleLen5 = getNumFrames(fileName5);
          hv_uint32_t tableHash5 = hv_stringToHash(tableName5);
          hv_table_setLength(gHeavyContext, tableHash5, sampleLen5); // resize the table
          float * table5 = hv_table_getBuffer(gHeavyContext, tableHash5); // once resized, get a pointer to the array
          int channel = 0; // take the first channel of the file
          int startFrame = 0; // start from the beginning
          int lastFrame5 = sampleLen5; // until the end of the file
          getSamples(fileName5, table5, channel, startFrame, lastFrame5);
          
          char fileName2[10];
           char tableName2[8];
           //char lengthName2[14];
           for (int k = 0; k < 48; k++) {
             sprintf(fileName2, "wt%d.wav", k);
             sprintf(tableName2, "wt%d", k);
             //  sprintf(lengthName2, "samplelength%d", k);
          int sampleLen = getNumFrames(fileName2);
          hv_uint32_t tableHash = hv_stringToHash(tableName2);
          hv_table_setLength(gHeavyContext, tableHash, sampleLen); // resize the table
          float * table = hv_table_getBuffer(gHeavyContext, tableHash); // once resized, get a pointer to the array
          int channel = 0; // take the first channel of the file
          int startFrame = 0; // start from the beginning
          int lastFrame = sampleLen; // until the end of the file
          getSamples(fileName2, table, channel, startFrame, lastFrame);
          
           }
          
          
          	gScopeChannelsInUse = gHvOutputChannels > gFirstScopeChannel ?
          			gHvOutputChannels - gFirstScopeChannel : 0;
          	if(gDigitalEnabled)
          	{
          		gDigitalSigInChannelsInUse = gHvInputChannels > gFirstDigitalChannel ?
          			gHvInputChannels - gFirstDigitalChannel : 0;
          		gDigitalSigOutChannelsInUse = gHvOutputChannels > gFirstDigitalChannel ?
          			gHvOutputChannels - gFirstDigitalChannel - gScopeChannelsInUse: 0;
          	}
          	else
          	{
          		gDigitalSigInChannelsInUse = 0;
          		gDigitalSigOutChannelsInUse = 0;
          	}
          
          	printf("Starting Heavy context with %d input channels and %d output channels\n",
          			gHvInputChannels, gHvOutputChannels);
          	printf("Channels in use:\n");
          	printf("Digital in : %u, Digital out: %u\n", gDigitalSigInChannelsInUse, gDigitalSigOutChannelsInUse);
          	printf("Scope out: %u\n", gScopeChannelsInUse);
          
          	if(gHvInputChannels != 0) {
          		gHvInputBuffers = (float *)calloc(gHvInputChannels * context->audioFrames,sizeof(float));
          	}
          	if(gHvOutputChannels != 0) {
          		gHvOutputBuffers = (float *)calloc(gHvOutputChannels * context->audioFrames,sizeof(float));
          	}
          
          	gInverseSampleRate = 1.0 / context->audioSampleRate;
          
          	// Set heavy print hook
          	hv_setPrintHook(gHeavyContext, printHook);
          	// Set heavy send hook
          	hv_setSendHook(gHeavyContext, sendHook);
          
          	// add here other devices you need
          	gMidiPortNames.push_back("hw:1,0,0");
          	//gMidiPortNames.push_back("hw:0,0,0");
          	//gMidiPortNames.push_back("hw:1,0,1");
          	unsigned int n = 0;
          	while(n < gMidiPortNames.size())
          	{
          		Midi* newMidi = openMidiDevice(gMidiPortNames[n], true, true);
          		if(newMidi)
          		{
          			midi.push_back(newMidi);
          			++n;
          		} else {
          			gMidiPortNames.erase(gMidiPortNames.begin() + n);
          		}
          	}
          	dumpMidi();
          
          	if(gScopeChannelsInUse > 0){
          #if __clang_major__ == 3 && __clang_minor__ == 8
          		fprintf(stderr, "Scope currently not supported when compiling heavy with clang3.8, see #265 https://github.com/BelaPlatform/Bela/issues/265. You should specify `COMPILER gcc;` in your Makefile options\n");
          		exit(1);
          #endif
          		scope = new Scope();
          		scope->setup(gScopeChannelsInUse, context->audioSampleRate);
          		gScopeOut = new float[gScopeChannelsInUse];
          	}
          	// Bela digital
          	if(gDigitalEnabled)
          	{
          		dcm.setCallback(sendDigitalMessage);
          		if(gDigitalChannelsInUse> 0){
          			for(unsigned int ch = 0; ch < gDigitalChannelsInUse; ++ch){
          				dcm.setCallbackArgument(ch, (void *) gHvDigitalInHashes[ch].c_str());
          			}
          		}
          	}
          	// unlike libpd, no need here to bind the bela_digitalOut.. receivers
          	// but make sure you do something like [send receiverName @hv_param]
          	// when you want to send a message from Heavy to the wrapper.
          	multiplexerTableHash = hv_stringToHash(multiplexerArray);
          	if(context->multiplexerChannels > 0){
          		pdMultiplexerActive = true;
          		multiplexerArraySize = context->multiplexerChannels * context->analogInChannels;
          		hv_table_setLength(gHeavyContext, multiplexerTableHash, multiplexerArraySize);
          		hv_sendFloatToReceiver(gHeavyContext, hv_stringToHash("bela_multiplexerChannels"), context->multiplexerChannels);
          	}
          
          	return true;
          }
          
          
          void render(BelaContext *context, void *userData)
          {
          	for(unsigned int n = 0; n < context->digitalFrames; ++n){
          		cState_b0 = digitalRead(context, n, 4);
          		cState_b1 = digitalRead(context, n, 3);
          	}
          
          	if (!cState_b0 && pState_b0) {
          		fx_nr = (fx_nr + 1)%3;
          		rt_printf("fx_nr: %d\n", fx_nr);
          //button is pressed down
           }
          if (cState_b0 && !pState_b0) {
            if (debounce_b0 < DEBOUNCE_TIME) {
              debounce_b0++;
              cState_b0 = 0;
            } else debounce_b0 = 0;
           }
          pState_b0 = cState_b0;
          	if (fx_nr != old_fx) {
          switch (fx_nr) {
           case 0: {
             digitalWrite(context, 0, 1, 1);
             digitalWrite(context, 0, 2, 1);
             break;
           }
           case 1: {
             digitalWrite(context, 0, 1, 0);
             digitalWrite(context, 0, 2, 1);
             break;
           }
            case 2: {
             digitalWrite(context, 0, 1, 1);
             digitalWrite(context, 0, 2, 0);
             break;
           } 
           }
           old_fx = fx_nr;
          	}
          	if (fx_nr == 0) {
          	int num;
                 
          	for(unsigned int port = 0; port < midi.size(); ++port){
          		while((num = midi[port]->getParser()->numAvailableMessages()) > 0){
          		 
          			static MidiChannelMessage message;
          			message = midi[port]->getParser()->getNextChannelMessage();
          			switch(message.getType()){
          			case kmmNoteOn: {
          				//message.prettyPrint();
          				int noteNumber = message.getDataByte(0);
          				int velocity = message.getDataByte(1);
          				int channel = message.getChannel();
          				if (velocity > 0) gIsNoteOn = 1;
          				gNote = noteNumber;
          				// rt_printf("message: noteNumber: %f, velocity: %f, channel: %f\n", noteNumber, velocity, channel);
          				hv_sendMessageToReceiverV(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff",
          						(float)noteNumber, (float)velocity, (float)channel+1);
          				break;
          			}
          			case kmmNoteOff: {
          				/* PureData does not seem to handle noteoff messages as per the MIDI specs,
          				 * so that the noteoff velocity is ignored. Here we convert them to noteon
          				 * with a velocity of 0.
          				 */
          				int noteNumber = message.getDataByte(0);
          				// int velocity = message.getDataByte(1); // would be ignored by Pd
          				int channel = message.getChannel();
          				// note we are sending the below to hvHashes[kmmNoteOn] !!
          				hv_sendMessageToReceiverV(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff",
          						(float)noteNumber, (float)0, (float)channel+1);
          				break;
          			}
          			case kmmControlChange: {
          				int channel = message.getChannel();
          				int controller = message.getDataByte(0);
          				int value = message.getDataByte(1);
          				gControl = controller;
          				gCCVal = value;
          				hv_sendMessageToReceiverV(gHeavyContext, hvMidiHashes[kmmControlChange], 0, "fff",
          						(float)value, (float)controller, (float)channel+1);
          				break;
          			}
          			case kmmProgramChange: {
          				int channel = message.getChannel();
          				int program = message.getDataByte(0);
          				hv_sendMessageToReceiverV(gHeavyContext, hvMidiHashes[kmmProgramChange], 0, "ff",
          						(float)program, (float)channel+1);
          				break;
          			}
          			case kmmPolyphonicKeyPressure: {
          				//TODO: untested, I do not have anything with polyTouch... who does, anyhow?
          				int channel = message.getChannel();
          				int pitch = message.getDataByte(0);
          				int value = message.getDataByte(1);
          				hv_sendMessageToReceiverV(gHeavyContext, hvMidiHashes[kmmPolyphonicKeyPressure], 0, "fff",
          						(float)channel+1, (float)pitch, (float)value);
          				break;
          			}
          			case kmmChannelPressure:
          			{
          				int channel = message.getChannel();
          				int value = message.getDataByte(0);
          				hv_sendMessageToReceiverV(gHeavyContext, hvMidiHashes[kmmChannelPressure], 0, "ff",
          						(float)value, (float)channel+1);
          				break;
          			}
          			case kmmPitchBend:
          			{
          				int channel = message.getChannel();
          				int value = ((message.getDataByte(1) << 7) | message.getDataByte(0));
          				hv_sendMessageToReceiverV(gHeavyContext, hvMidiHashes[kmmPitchBend], 0, "ff",
          						(float)value, (float)channel+1);
          				break;
          			}
          			case kmmSystem:
          			case kmmNone:
          			case kmmAny:
          				break;
          			}
          		}
          	}
          
          
          	// De-interleave the data
          	if(gHvInputBuffers != NULL) {
          		for(unsigned int n = 0; n < context->audioFrames; n++) {
          			for(unsigned int ch = 0; ch < gHvInputChannels; ch++) {
          				if(ch >= gAudioChannelsInUse + gAnalogChannelsInUse) {
          					// THESE ARE PARAMETER INPUT 'CHANNELS' USED FOR ROUTING
          					// 'sensor' outputs from routing channels of dac~ are passed through here
          					// these could be also digital channels (handled by the dcm)
          					// or parameter channels used for routing (currently unhandled)
          					break;
          				} else {
          					// If more than 2 ADC inputs are used in the pd patch, route the analog inputs
          					// i.e. ADC3->analogIn0 etc. (first two are always audio inputs)
          					if(ch >= gAudioChannelsInUse)
          					{
          						unsigned int analogCh = ch - gAudioChannelsInUse;
          						if(analogCh < context->analogInChannels)
          						{
          							int m = n;
          							float mIn = analogReadNI(context, m, analogCh);
          							gHvInputBuffers[ch * context->audioFrames + n] = mIn;
          						}
          					} else {
          						if(ch < context->audioInChannels)
          							gHvInputBuffers[ch * context->audioFrames + n] = audioReadNI(context, n, ch);
          					}
          				}
          			}
          		}
          	}
          
          	if(pdMultiplexerActive){
          		static int lastMuxerUpdate = 0;
          		if(++lastMuxerUpdate == multiplexerArraySize){
          			lastMuxerUpdate = 0;
          			memcpy(hv_table_getBuffer(gHeavyContext, multiplexerTableHash), (float *const)context->multiplexerAnalogIn, multiplexerArraySize * sizeof(float));
          		}
          	}
          
          
          	// Bela digital in
          	if(gDigitalEnabled)
          	{
          		// note: in multiple places below we assume that the number of digital frames is same as number of audio
          		// Bela digital in at message-rate
          		dcm.processInput(context->digital, context->digitalFrames);
          	
          		// Bela digital in at signal-rate
          		if(gDigitalSigInChannelsInUse > 0)
          		{
          			unsigned int j, k;
          			float *p0, *p1;
          			const unsigned int gLibpdBlockSize = context->audioFrames;
          			const unsigned int  audioFrameBase = 0;
          			float* gInBuf = gHvInputBuffers;
          			// block below copy/pasted from libpd, except
          			// 16 has been replaced with gDigitalSigInChannelsInUse
          			for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) {
          				unsigned int digitalFrame = audioFrameBase + j;
          				for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstDigitalChannel;
          						k < gDigitalSigInChannelsInUse; ++k, p1 += gLibpdBlockSize) {
          					if(dcm.isSignalRate(k) && dcm.isInput(k)){ // only process input channels that are handled at signal rate
          						*p1 = digitalRead(context, digitalFrame, k);
          					}
          				}
          			}
          		} 
          	} 
          	
          
          	// replacement for bang~ object
          	//hv_sendMessageToReceiverV(gHeavyContext, "bela_bang", 0.0f, "b");
          	
          	// heavy audio callback
          	hv_processInline(gHeavyContext, gHvInputBuffers, gHvOutputBuffers, context->audioFrames);
          	/*
          	for(int n = 0; n < context->audioFrames*gHvOutputChannels; ++n)
          	{
          		printf("%.3f, ", gHvOutputBuffers[n]);
          		if(n % context->audioFrames == context->audioFrames - 1)
          			printf("\n");
          	}
          	*/
          
          	// Bela digital out
          	if(gDigitalEnabled)
          	{
          		// Bela digital out at signal-rate
          		if(gDigitalSigOutChannelsInUse > 0)
          		{
          				unsigned int j, k;
          				float *p0, *p1;
          				const unsigned int gLibpdBlockSize = context->audioFrames;
          				const unsigned int  audioFrameBase = 0;
          				float* gOutBuf = gHvOutputBuffers;
          				// block below copy/pasted from libpd, except
          				// context->digitalChannels has been replaced with gDigitalSigOutChannelsInUse
          				for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) {
          					unsigned int digitalFrame = (audioFrameBase + j);
          					for (k = 0, p1 = p0  + gLibpdBlockSize * gFirstDigitalChannel;
          							k < gDigitalSigOutChannelsInUse; k++, p1 += gLibpdBlockSize) {
          						if(dcm.isSignalRate(k) && dcm.isOutput(k)){ // only process output channels that are handled at signal rate
          							digitalWriteOnce(context, digitalFrame, k, *p1 > 0.5);
          						}
          					}
          				}
          		}
          		// Bela digital out at message-rate
          		dcm.processOutput(context->digital, context->digitalFrames);
          	}
          	
          	// Bela scope
          	if(gScopeChannelsInUse > 0)
          	{
          		unsigned int j, k;
          		float *p0, *p1;
          		const unsigned int gLibpdBlockSize = context->audioFrames;
          		float* gOutBuf = gHvOutputBuffers;
          
          		// block below copy/pasted from libpd
          		for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) {
          			for (k = 0, p1 = p0  + gLibpdBlockSize * gFirstScopeChannel; k < gScopeChannelsInUse; k++, p1 += gLibpdBlockSize) {
          				gScopeOut[k] = *p1;
          			}
          			scope->log(gScopeOut);
          		}
          	}
          
          	// Interleave the output data
          	if(gHvOutputBuffers != NULL) {
          		for(unsigned int n = 0; n < context->audioFrames; n++) {
          			for(unsigned int ch = 0; ch < gHvOutputChannels; ch++) {
          				if(ch >= gAudioChannelsInUse + gAnalogChannelsInUse) {
          					// THESE ARE SENSOR OUTPUT 'CHANNELS' USED FOR ROUTING
          					// they are the content of the 'sensor output' dac~ channels
          				} else {
          					if(ch >= gAudioChannelsInUse)	{
          						int m = n;
          						unsigned int analogCh = ch - gAudioChannelsInUse;
          						if(analogCh < context->analogOutChannels)
          							analogWriteOnceNI(context, m, analogCh, gHvOutputBuffers[ch*context->audioFrames + n]);
          					} else {
          						if(ch < context->audioOutChannels)
          							audioWriteNI(context, n, ch, gHvOutputBuffers[ch * context->audioFrames + n]);
          					}
          				}
          			}
          		}
          	}
          } else {
          		int num;
                 
          	for(unsigned int port = 0; port < midi.size(); ++port){
          		while((num = midi[port]->getParser()->numAvailableMessages()) > 0){
          		 
          			static MidiChannelMessage message;
          			message = midi[port]->getParser()->getNextChannelMessage();
          			switch(message.getType()){
          			case kmmNoteOn: {
          				//message.prettyPrint();
          				gNote = message.getDataByte(0);
          				int velocity = message.getDataByte(1);
          			//	int channel = message.getChannel();
          				if (velocity > 0) gIsNoteOn = 1;
          				break;
          			}
          			case kmmNoteOff: {
          				break;
          			}
          			case kmmControlChange: {
          			//	int channel = message.getChannel();
          				gControl = message.getDataByte(0);
          				gCCVal = message.getDataByte(1);
          				break;
          			}
          			case kmmProgramChange:
          			case kmmPolyphonicKeyPressure:
          			case kmmChannelPressure:
          			case kmmPitchBend:
          			case kmmSystem:
          			case kmmNone:
          			case kmmAny:
          			break;
          			}
          			}
          	}
          	
          		gBcf.push(BelaContextFifo::kToLong, context);
          /// receive from the "long" render
              const InternalBelaContext* rctx = (InternalBelaContext*)gBcf.pop(BelaContextFifo::kToShort);
          
              if(rctx) {
                  BelaContextSplitter::contextCopyData(rctx, (InternalBelaContext*)context);
              }
          }
          }
          
          
          void cleanup(BelaContext *context, void *userData)
          {
          	const char fileName[] = "current.wav";
          		const char tableName[] = "current";
          		hv_uint32_t tableHash = hv_stringToHash(tableName);
          
          			float * table = hv_table_getBuffer(gHeavyContext, tableHash);
          
          			writeSamples(fileName, table, 2);
          	
          	hv_delete(gHeavyContext);
          	free(gHvInputBuffers);
          	free(gHvOutputBuffers);
          	delete[] gScopeOut;
          	delete scope;
          }

          i already tried commenting the digital write and read parts in the heavy render, but that did not help...

          so, even with the whole heavy part in render commented out the problem still persists, my guess is that the switching of blocksize via

          gBcf.push(BelaContextFifo::kToLong, context);
           /// receive from the "long" render
               const InternalBelaContext* rctx = (InternalBelaContext*)gBcf.pop(BelaContextFifo::kToShort);
               if(rctx) {
                 BelaContextSplitter::contextCopyData(rctx, (InternalBelaContext*)context);
               }

          is somehow causing this.

          Probably a bug in the BelaContextSplitter, or in its integration. Will have a look tomorrow

          so it seems that the persistency of analog and digital outputs is broken when using the BelaContextSplitter. There is no easy solution for it, as it requires quite some refactoring of the code, moving/copying the handling of persistency of digitals and analogs from PRU.cpp to BelaContextSplitter::pop().

          lokki bracket around the digitalWrite part it works as expected, but it seems like a waste of CPU to run the digitalWrite even if nothing has changed. or is this just how it works?

          It is not actually a huge waste of CPU, so you should be able to safely remove the conditional if (switch_fx != old_fx) { and everything will be fine.

            giuliomoro so it seems that the persistency of analog and digital outputs is broken when using the BelaContextSplitter

            good to know i am not crazy and thanks for checking.