HELP - MIDI Controller - Storing Values until trigger note is received, other stuff

Status
Not open for further replies.

stublito

Member
I'm on OSX, working on a sort of One Man Band / Arranger project. Please bear with me, here is description.

I have my DAW running 4 instances of a Korg M1. I have a set of bass pedals sending notes to MIDIpipe, I use Applescript to convert those notes to pitch bend values. All the M1 voices have their non drum pitch bends set at +12. So I can transpose the MIDI tracks by playing the pedals.

There are 4 sets of looping MIDI tracks (each driving one M1) so I can have 4 variations of the same basic pattern (playing at the same time). I select just one of the M1s' audio outputs, by sending an array of CCs to the DAW faders on the M1 plug channels. Mix one would be Controllers 10,127 & 11,0 & 12,0 & 13,0. Mix 2 is 10,0 & 11,127 & 12,0 & 13,0 etc. (In case you were wondering, directly muting individual MIDI tracks results in hung notes ect.)

I have a homemade switch box to switch mixes and also control a looper (which can get pretty nutty). Right now the footswitch uses an old Dell keyboard PCB. That outputs ASCII characters to MIDI Keys (which can run in the background.) The resulting note output from MIDI Keys go to MIDI Pipe (again in the background) and I have written Applescript to convert the notes to the CC arrays (and other CCs for the looper).

However pressing the Mix footswitches on the downbeat is difficult, as I am doing other things with my feet as well (playing bass pedals, looper switches). So I want to be able to press the mix buttons in the previous bar, store the resulting CCs, and then have them auto send to the DAW, based on a MIDI trigger note sent from the DAW, programmed on every downbeat.

I had a go at the Applescript but the peek and poke to another background Applescript app seemed a stretch. The it occurred to me that a Teensy could do ALL of the above, and eliminate all that other background app action, and be more robust.

HOWEVER, I can do cheezy scripting but do not know how to C code. I got some "Simple MIDI Controller Code" of the web, but nothing that will remap notes to pitch bends, and write and recall the data as I want.

If someone would help me with this I would be extremely grateful, and would gladly send beer money (or Red Bull money). I will send you the Applescript, it should take someone who knows what they are doing prolly 30 minutes.

Thanks for reading.

Stublito
 
Additional Explanation

If it was not clear, each of the M1 plugs is multitimbral, one channel drums, one bass, one rhythm keys and one pad track. There are 4 of them, addressed by 4 sets of 4 MIDI tracks per "song". As I said, only one M1 sounds at any given time.

My DAW is Digital Performer, which has a sort of relational database thing going on, it separates the master mixer/plugin holder mixer "Vrack" and the MIDI/song parts, allowing you to select from unlimited MIDI sequence "chunks" that send MIDI to the master mixer, without reloading a new project each time. It's like having a separate sequencer program and plugin host program.

Which is tough to get your head around if you use a "flat" DAW like Ableton or Logic.

Stublito
 
Turns out this is not as easy as you might think...

The hard part is how to tell when the new bar has arrived...

I think this code does the rest but the call getBar() is just a placeholder... what's needed is a function that returns the bar number so the main loop can tell when it's in a new bar for the first pass.

You could count MIDI ticks but you'd still need to know where at least one downbeat is...

Code:
#include <Bounce.h>
Bounce btn1 = Bounce(1, 10);
Bounce btn2 = Bounce(2, 10);
Bounce btn3 = Bounce(3, 10);
Bounce btn4 = Bounce(4, 10);
unsigned int btn;
boolean flag;
boolean newBar;
int thisBar;
int lastBar;

void setup() {
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
}

void loop() {
//update buttons
  btn1.update(); 
  btn2.update(); 
  btn3.update(); 
  btn4.update(); 
  
//look for pressed button
  if (btn1.fallingEdge()){
    btn = 1;
    flag = true;
    newBar = true;
  }
  if (btn2.fallingEdge()){
    btn = 2;
    flag = true;
  }
  if (btn3.fallingEdge()){
    btn = 3;
    flag = true;
  }
  if (btn4.fallingEdge()){
    btn = 4;
    flag = true;
  }

// set newBar to true when new bar is detected (the function below does not do this it's just a placeholder)
  thisBar = getBar();
  if (thisBar != lastBar){
    newBar = true;
    lastBar = thisBar;
  }
  
  if (flag && newBar) {  
    usbMIDI.sendControlChange(10, btn==1?127:0, 1);
    usbMIDI.sendControlChange(11, btn==2?127:0, 1);
    usbMIDI.sendControlChange(12, btn==3?127:0, 1);
    usbMIDI.sendControlChange(13, btn==4?127:0, 1);
    flag = false;
    newBar = false;
  }
}

int getBar(){
  // need something to read MIDI messages and determine what bar we're in
  return 12345;
}
The only 'trick' is using the Ternary operator to send 127 to unmute the channel associated with the button that was pressed and zero for the others.
 
You could program your daw to send a specific event at downbeat and then it's trivial to set the newBar boolean when you receive that message.
 
Untested code (and therefore bound to have mistakes) for a scheme where you send a midi event to mark downbeat:

Code:
#include <Bounce.h>    // bounce library removes 'chatter' from switch
Bounce btn1 = Bounce(1, 10); // pin 1 with 10 ms debounce time
Bounce btn2 = Bounce(2, 10);
Bounce btn3 = Bounce(3, 10);
Bounce btn4 = Bounce(4, 10);


unsigned int btn;   // variable tracks which button was last pressed

boolean flag;   // flag is set when button press is detected and reset when midi is sent

boolean downbeat;  // set to true on (or just before) downbeat when marker recieved, reset with midi send


void setup() {
  pinMode(1, INPUT_PULLUP);   // set up internal pullups for buttons used
  pinMode(2, INPUT_PULLUP);   // pins need to match bounce statements above
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
}


void loop() {

//update buttons
  btn1.update(); 
  btn2.update(); 
  btn3.update(); 
  btn4.update(); 
  
//scan for pressed button, fallingEdge is the voltage being pulled low with contact... we can ingore the rise
  if (btn1.fallingEdge()){
    btn = 1;
    flag = true;
   //
  }
  if (btn2.fallingEdge()){
    btn = 2;
    flag = true;
  }
  if (btn3.fallingEdge()){
    btn = 3;
    flag = true;
  }
  if (btn4.fallingEdge()){
    btn = 4;
    flag = true;
  }


// listen for downbeat marker (here a note off for MIDI note 0 with vel=127 -- shouldn't appear normally)
  if (usbMIDI.read() && usbMIDI.getType() == 0 && usbMIDI.getData1() == 0 && usbMIDI.getData2() == 127){
    downbeat = true;
  }



 // send midi if flag was set and downbeat was detected above 
  if (flag && downbeat) {  
    usbMIDI.sendControlChange(10, btn==1?127:0, 1);
    usbMIDI.sendControlChange(11, btn==2?127:0, 1);
    usbMIDI.sendControlChange(12, btn==3?127:0, 1);
    usbMIDI.sendControlChange(13, btn==4?127:0, 1);
    flag = false;
    downbeat = false;
  }
}

A note off for note 0 with off-velocity = 127 is used here as I can't image one ever getting generated except on purpose.

I'm not on a machine I can test this on but I believe it's close to a working sketch.
 
Last edited:
Mahalo again!

This is great.

Of course the hardest part of the coding is to understand the syntax and your examples really help me see the structure of Teensy C programming, having already done a similar thing in Applescript. I also program Filemaker Pro, so I have good experience with scripts.

BTW I see you are from Vancouver. I was a "career west coast rock musician" and electronics guru, before moving to Honolulu in the 90s. I once tried to learn C from a book, but couldn't get past the first chapter, the bubble sort, ha ha.

Stublito
 
Last edited:
So do I have what you are trying to achieve correct?

You want to send four CC messages on the downbeat with the D2 value set to zero for all but the one that corresponds with the button pushed?

And is sending a usbMIDI message from the DAW to mark the downbeat acceptable? There may be some way to use MIDI timing codes but I think this is likely adequate for your needs and you should be able to adjust the timing to have the MIDI send prior to the downbeat so the mute/unmute has time to take effect.

BTW - I've been trying to learn C off and on for twenty-five years... I still have to look up syntax stuff all the time.

The C used here is pretty basic and I purposely avoided loops and arrays.

The ternary operator is an 'if-else' selector... I hadn't used it in years and had to look up how.
 
Yes, you have decoded my explanation perfectly. And sending the trigger note is the best solution, as you said, it allows for timing variation.

I will do a test run next week.

Cheers

Stublito
 
ok... looks like only two minor errors... should be test-equals operators not assignment operators in the getMIDI line:

'==' not '='

Also, note-off is getType()==0 and I was testing for 1.

From what I can tell it's working in the currently posted code above.
 
Just Fabulous

This has been such a valuable experience, my first toe dip in the Teensy pool.

Thank you so much,

Stublito
 
Here's a version with all the specifics moved into constant value assignments at the top of the code so you can set the CC values, pins, de-bounce time and MIDI specifics.

I've also got rid of the downbeat variable and just test for it along with 'flag' before sending MIDI.

Note that you have to test usbMIDI.read() before 'flag' or the MIDI event queue is left untouched until the next time flag is true... that had me confused for a bit as it seemed to remember how many downbeat messages it had received... and it would send new groups for each one each time a button was pressed...

Code:
// constant values 
const unsigned int CC1 = 10; // CC of first button
const unsigned int CC2 = 11;
const unsigned int CC3 = 12;
const unsigned int CC4 = 13;
const unsigned int PIN1 = 0; // PIN number of first button
const unsigned int PIN2 = 1;
const unsigned int PIN3 = 2;
const unsigned int PIN4 = 3;
const unsigned int BOUNCE_TIME = 10; // Milliseconds debounce time
const unsigned int MIDI_CHANNEL = 1; // MIDI channel
const unsigned int MTYPE = 0; // downbeat trigger type 
const unsigned int MD1 = 0; // downbeat trigger D1 - note
const unsigned int MD2 = 127; // downbeat trigger D2 - vel

#include <Bounce.h>    // bounce library removes 'chatter' from switch
unsigned int btn;   // variable tracks which button was last pressed
boolean flag;   // flag is set when button press is detected and reset when midi is sent

Bounce btn1 = Bounce(PIN1, BOUNCE_TIME); // pin 1 with 10 ms debounce time
Bounce btn2 = Bounce(PIN2, BOUNCE_TIME);
Bounce btn3 = Bounce(PIN3, BOUNCE_TIME);
Bounce btn4 = Bounce(PIN4, BOUNCE_TIME);


void setup() {  
  pinMode(PIN1, INPUT_PULLUP);   // set up internal pullups for buttons used
  pinMode(PIN2, INPUT_PULLUP);   
  pinMode(PIN3, INPUT_PULLUP);
  pinMode(PIN4, INPUT_PULLUP);
}


void loop() {

  //update buttons
  btn1.update(); 
  btn2.update(); 
  btn3.update(); 
  btn4.update(); 
  
  // scan for pressed button, fallingEdge is the voltage being pulled low with contact... we can ingore the rise
  if (btn1.fallingEdge()){
    btn = 1;
    flag = true;
  }
  if (btn2.fallingEdge()){
    btn = 2;
    flag = true;
  }
  if (btn3.fallingEdge()){
    btn = 3;
    flag = true;
  }
  if (btn4.fallingEdge()){
    btn = 4;
    flag = true;
  }
  
  // send midi if flag was set and downbeat message is detected  
  if (usbMIDI.read() && usbMIDI.getType() == MTYPE && usbMIDI.getData1() == MD1 && usbMIDI.getData2() == MD2 && flag) {  
    usbMIDI.sendControlChange(CC1, btn==1?127:0, MIDI_CHANNEL); // ?: ternary operator sends 127 if btn equals button pushed last
    usbMIDI.sendControlChange(CC2, btn==2?127:0, MIDI_CHANNEL);
    usbMIDI.sendControlChange(CC3, btn==3?127:0, MIDI_CHANNEL);
    usbMIDI.sendControlChange(CC4, btn==4?127:0, MIDI_CHANNEL);
    flag = false;
  }
}
 
Development Environment

You must be working in a virtual environment to debug the code. Where/what is it?

Also, can I buy you a case of beer or something (or is it bad form to offer? If so, erase.)

I'd really like to thank you somehow.

Stublito
 
I use Flowstone as my MIDI utility as I used to bulid vst synths with its predecessor 'SynthMaker'.

The switches I mimic with a wire from ground to the pins on a T3.0.
 
Flowstone

That's pretty amazing software.

Apart from being a performing musician, I'm a Filemaker Pro developer (and Network Admin). Filemaker is a so called "rapid development" database app. I can see the similarities right away.

The MIDI to Robotics possibilities would be very interesting in the context of theater or show staging.

I also sent the link to my teenage son, he's a Minecraft guy.

Again, Mahalo for everything,

Stublito
 
Warning - development has not kept up with this software that never found its niche ...or at least not one that paid the bills.

The free version is useful and at $100USD the full version isn't too expensive but it's not clear if it will survive much longer.

Synthmaker was awesome in its day... and the legacy is still there but no 64-bit version seems forthcoming. The robotics focus doesn't seem to have paid off.
 
It sort of reminds me of the old analog synth days before MIDI. There were a lot of small companies making custom boxes to connect synths and drum machines together etc. At lease then there was a market - wage earning live musicians trying to keep up with the latest synth pop hits. I am an analog electronics guy, I did quite a bit of custom work while it lasted, but MIDI came along and that was it.

And playing cover music now for a living (what I dod for a long time) is essentially over, except for the high end entertainment market. So there goes 2/3 of the musical instrument business, including much software. Pro apps will always do well, but the lesser known stuff...

The small robotics thing went well for quite a few years but I think the novelty has worn off, like model airplanes or remote control to cars.

I don't think the kids want to deal with the hardware ha ha.
 
Status
Not open for further replies.
Back
Top