usbMidi recieve problems

artUdino

Member
Hello,
I'm working on a project, that should use incoming different specific "note on" - messages to shift out to 74HC registers. "Note off" - messages are irrelevant.
That works fine with the recommended "usbMIDI.setHandleNoteOn(OnNoteOn);" in the setup and the "void OnNoteOn(byte channel, byte note, byte velocity)"
But I need a time distance to do two more things (two shiftregister changes) after the incoming specific "note on". I don't want to use the "delay();" - function, because I have to read other incoming messages. Now I'm thinking the "void OnNoteOn(.......)" runs only once after each "note on" - message. Thats why I can't use a variable that stores the "millis()"-function to do a second and a third task after a while in this void like this:

Code:
void OnNoteOn(byte channel, byte note, byte velocity) {
  
 unsigned long T = millis(); // T = time = now

// **************** B0 midi 35 ************************
// magnet forward
  if (note == 35) { // = B0 - 61,735 Hz
    digitalWrite(ledPin, HIGH); // LED ON
    magnets[0] = 1;   // "magnets[]" is an array with 16 elements
    magnets[1] = 0;
    Tnote35on = T;  // time when note on was played ("Tnote35on" is declared as unsigned long)
    shiftPlay();   // shift out to the two 74HC959 registers
  }
// I think the next task and the following can't be done 
// magnet back
  if (T >= Tnote35on + Tforw) {   // "Tforw" is declared as const int to make a time distance 
    digitalWrite(ledPin, LOW); // Led Off 
    magnets[0] = 0;
    magnets[1] = 1;
    shiftPlay();
  }

// magnet off
  if (T >= Tnote35on + Tforw + Tback) {  // "Tback" is declared as const int
    magnets[0] = 0;
    magnets[1] = 0;
    shiftPlay();
  }

My first question is: can I use a "return" - command from inside the "void OnNoteOn(byte channel, byte ...)" to the void loop to do the time distance and the next related tasks? I don't think so, but I have not much informatic knowledge - just learning by doing.
If the answer is "no", my second question: Is it possible to use the "void OnNoteOn(byte ...)" as "unsigned long OnNoteOn(...)" with a "return Tnote35on;" - command, to realize my intention???
I've tried this. Not yet successful. That's why I read the "Using USB MIDI" - page again and now I'm thinking about the "usbMidi.get..." - commands. I tried "usbMIDI.getNoteOn" after "usbMIDI.read();" and get this:
'class usb_midi_class' has no member named 'getNoteOn'

I clearly need a better understanding, how to use the "usbMIDI.get..." - command. What is needed for its use (which other commands around, implementations in the scope or at other places in the programm)? The 4th question - probably the most significant one.

Thanks for your help and kind regards,
artU
 
Hello, are you using a MIDI note 35 to trigger a routine that shifts twice at time intervals? If so, how about using TeensyThreads to handle the shifting routine when triggered by the noteOn callback?

TeesnyThreads is part of Teensyduino and should be installed. Have a look at the Blink.ino example, the blinkcode variable is your trigger - is this similar to what you want to do?
 
Thanks for prompt reply.

Ther are some instruments driven by magnets to punch different drums or bells for instance.

Yes there are coming in specific midi note on messages (for instance: midi note 35 on) via usbMIDI.

midi noteON --> [usbIn Teensy4.0] --> 7 times [in - 74HC959 shiftregister - out] --> [H-bridges] --> 26 magnets

I only need the start of the tone, thats why I use only note on.
Each magnet is related to its specific midi on note.

The magnet tasks are:
1. task1: punch out a drumstick (magnet lines 24V, left = HIGH, right = LOW)
2. time distance 1
3. task2: retract stick (left = LOW, right = HIGH)
4. time distance 2
5. task3: magnet off (left = LOW, right = LOW)

The time distances are different (60 up to 150 ms) depending on the different length and distances of the specific arms/instruments and the punching power I need for musical dynamic.

TeesnyThreads is part of Teensyduino and should be installed.
I can open the example - I think TeensyThreads is installed. If not, what should I do? What should I look for?

Have a look at the Blink.ino example, the blinkcode variable is your trigger - is this similar to what you want to do?
There are some "threads.delay()" in the example. Are they similar with the normal "delay()"? If they stop the whole programm, I can't use them, because I have to read other incoming midi noteOn's in the meantime. If the notes are played at the"same" time, the distance between them is shorter than the distance I need between the magnet tasks.
If the "threads.delay()" - function only stops the current void or function and not the loop of the main programm, than they could be the solution.
Pause the "threads.delay()" only the current void or function?
 
Last edited:
@artUdino:

[ Please note that I am typing this without actually testing/running it on a Teensy, as I am not where I can actually do any testing (in fact, being lazy, I have not even tried to compile anything, so take all of this with due care !!) ]

I might have a suggestion, but it depends critically on whether your magnet transitions are guaranteed to be complete before the next noteOn is received:

1) at the top level of your sketch, define (& initialize) a "note_35_state" variable as such:

Code:
unsigned int note_35_state = 0;   // effect OFF

2) at the top level of your sketch, define a "note_35_state_delay" variable as such:

Code:
unsigned long note_35_state_delay;

3) at the top level of your sketch, define your constants as such:

Code:
const int NOTE_35_STATE_1_DELAY = 60; // or whatever constant is appropriate
const int NOTE_35_STATE_2_DELAY = 120; // or whatever constant is appropriate

4) Whenever you process a noteOn(), set the following:

Code:
// STRIKE the effect
magnets[0] = 1;
magnets[1] = 0;
shiftPlay();
note_35_state = 1;
note_35_state_delay = millis();

5) In your loop(), include the following:

Code:
switch(note_35_state)
{
   case 1:   // RETRACT the effect
   {
      if (millis() > (note_35_state_delay + NOTE_35_STATE_1_DELAY)
      {
         magnets[0] = 0;
         magnets[1] = 1;
         shiftPlay();
         note_35_state = 2;
         note_35_state_delay = millis();
      }
   }
   break;

   case 2:   // effect OFF
   {
      if (millis() > (note_35_state_delay + NOTE_35_STATE_2_DELAY)
      {
         magnets[0] = 0;
         magnets[1] = 0;
         note_35_state = 0;
      }
   }
   break;
}

This will allow the reception of a note 35 message to activate/start that specific effect in your noteOn() function, then the millis() counter will be used to trigger the subsequent transitions (RETRACT & OFF, as you described previously) in your loop() function. Of course, this depends critically on being sure that a complete transition thru all of the states will be completed before the next noteOn() for note 35 is received.

Hope this helps . . .

Mark J Culross
KD5RXT
 
@ kd5rxt-mark
Thanks a lot! I will try your suggestions.
I have a question:
to activate/start that specific effect in your noteOn() function,
What do you mean with "your noteOn() function"? So far I used only the "usbMIDI.read()" function.
In the setup() is
Code:
usbMIDI.setHandleNoteOn(OnNoteOn);
implemented as recommended from https://www.pjrc.com/teensy/td_midi.html.
And the related
Code:
void OnNoteOn(byte channel, byte note, byte velocity) {...}
is linked (connected? - I don't know, how to say correctly) as recommended as well. In this void I wrote the code I posted above.

Are your suggestions include this stuff or do you mean I have to create my own noteOn() function?
In this case I don't know how to get the noteOn. Then I have a lot of research to do.
The explenation on the PJRC-website is too little detailed, that I can understand enough:
Clearly I have to use usbMIDI.read(). But how to get the NoteOn information in the midi messages?
I tried many different "spellings" of the "usbMIDI.getType()" or "usbMIDI.NoteON". For instance I tried "usbMIDI.getType(144);" (144 is the number of the note on from the midi table).
I wish I could understand how to use the explained stuff...
 
Hello,
I have to report a little success, a solution of one of my "stupid" quenstions. :rolleyes:

How to get specific noteOn messages:

Code:
const int ledPin = 13;

void setup() {

  pinMode(ledPin, OUTPUT);

}

void loop() {
  usbMIDI.read(8);  // recieve messages exclusively fom channel 8

  if (usbMIDI.getType() == 144) { // 144 = NoteON
    if (usbMIDI.getData1() == 35) { // 35 = midi note "B0" 
      digitalWrite(ledPin, HIGH);   // every B0 noteOn message turn on LED
    }
    else digitalWrite(ledPin, LOW); // any other noteOn message turn LED off
  }

}

Now there is no need to implement:
Code:
usbMIDI.setHandleNoteOn(OnNoteOn);
and:
Code:
void OnNoteOn(byte channel, byte note, byte velocity) {...}

I know this isn't a big thing. It was a significant step for me.
There maybe someone else, who get a "knot in the head" because of the usbMIDI explanation on the https://www.pjrc.com/teensy/td_midi.html

I'm glad and impressed that it's so easy to recieve the desired midi messages. And a big thanks to those who programmed this!

With best wishes,
artU.
 
Last edited:
@artUdino:

When I was active as an Assistant Scoutmaster in our local Boy Scout Troop, I used to tell the Scouts: "There are no stupid questions. If you are thinking of asking the question, there are most certainly others who might very well have the same question. Ask any question that you want without fear !!" So, the same applies here.

Please feel free to ask any other questions, as you have fellow forum members on here who are happy to help & answer questions. For future posts, if you have specific Teensy code giving you problems, remember that it's much easier for us to provide help if you also post the specific code that you are working on. Continue posting code as you have done several times - you are to be commended for a very good start - it can be difficult to get some members (new & seasoned) to follow the "Forum Rule" posted at the top of every page, but you've got that down pat !!

So, I started to answer your "I wish I could understand how to use the explained stuff...", but luckily I continued reading your next follow-up. Good to see that you are making some progress. Now, try to take the concepts posted earlier, which were described as those to be included in the function that I (mistakenly) referenced as "noteOn()" (should have been "onNoteOn()"... I should have been more careful to match your posted function names), & include those concepts in your latest loop as follows:

At the top of loop, when you match channel+type+note (as your latest example code does, 8, 144, 35), then include the code to initiate the STRIKE:

Code:
// STRIKE the effect when a matching/desired note is initiated
magnets[0] = 1;
magnets[1] = 0;
shiftPlay();
note_35_state = 1;
note_35_state_delay = millis();

Then, farther down in your loop, include the code to process the RETRACT & OFF after the prescribed delay(s) in each case.

Code:
switch(note_35_state)
{
   case 1:   // RETRACT the effect
   {
      if (millis() > (note_35_state_delay + NOTE_35_STATE_1_DELAY)
      {
         magnets[0] = 0;
         magnets[1] = 1;
         shiftPlay();
         note_35_state = 2;
         note_35_state_delay = millis();
      }
   }
   break;

   case 2:   // effect OFF
   {
      if (millis() > (note_35_state_delay + NOTE_35_STATE_2_DELAY)
      {
         magnets[0] = 0;
         magnets[1] = 0;
         note_35_state = 0;
      }
   }
   break;
}

Good luck & have fun !! And again, feel free to ask more questions !!

Mark J Culross
KD5RXT
 
@kd5rxt-mark,
I'm finished my code for now. Its testet (with LEDs at the 74HC959-outputs) and works as desired.
I've only to implement 22 more magnets. But I have to build the instrument first.
Thank you for helping and for the great idea with the "switch/case" commands and the state - variables.
I made some changes, because of other needs:
1. the time distances between the different magnet actions (strike/retreat/off) has to be complete independent from the midi note length (= time length).
2. I thought it would be a good idea to call the "millis()" and the "usbMIDI.get..." - commands only once per loop pass. But is this true? Is this faster compared to making the query for each note?

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Now here two more (but final ;-) ) questions:

???????????????? FIRST QUESTION ??????????????????
1. Are there possibilities for me to make the code faster (other than overclocking the Teensy)?

?????????????? SECOND QUESTION ??????????????????
2. In the "shiftPlay()" - function I don't use "delayMicroseconds(1)". Thats working! But it shouldn't! In the forum here I read before that there's a problem with running the Teensy4.0 with 600Mhz and the usage of 74HC959 shift registers, because of their slowness. The recommended solution was to insert the delayMicroseconds(1);
What is wrong with my Teensy4.0??? (600MHz; Optimize Setting: faster; Vin 5V (also the 74HC959's driven by 5V)
Or why is this working? Any idea someone?

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Code:
// data-, store- & shiftPin can chosen freely! ???
int dataPin = 18; // normally 11
int storePin = 4; // normally 8
int shiftPin = 10;  // normally 13

int MidiType;
int Note;

// entsprechende Variablen für alle anderen 25 Töne "note36_onTime" usw.
unsigned long note35_onTime; // zur Speicherung der Anschaltzeit
int note35_state = 0;   // B0
unsigned long note38_onTime;
int note38_state = 0;   // D1
unsigned long note41_onTime;
int note41_state = 0;   // F1
unsigned long note42_onTime;
int note42_state = 0;   // F#1

boolean magnets[52];  // possible as int ???

unsigned long Time; // = now

const int Forw_DEL = 80;  // in ms
const int Back_DEL = 120; // in ms

// to monitor powerOn and the functioning
const int ledPin = 13;


void setup() {

// only debug ###################
//  Serial.begin(38400);
//  Serial.println("Start");
// #############################

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH); // always On (except NoteON) to monitor powerOn and the functioning 
  
  pinMode(dataPin, OUTPUT);
  pinMode(storePin, OUTPUT);
  pinMode(shiftPin, OUTPUT);

}


void shiftPlay() {
  digitalWrite(storePin, LOW);

  for (int i = 51; i >= 0; i--) {
    digitalWrite(shiftPin, LOW);
    digitalWrite(dataPin, magnets[i] );
    //delayMicroseconds(1);
    digitalWrite(shiftPin, HIGH);
    //delayMicroseconds(1);
  }
  //delayMicroseconds(1);
  digitalWrite(storePin, HIGH);
  //delayMicroseconds(1);
  
} // End shiftPlay 


void loop() {
  usbMIDI.read(8);   // Klammern freilassen, wenn alle Channel abgehört werden (in Klammern spezieller Midichannel 1-16)  
  Time = millis();
  MidiType = usbMIDI.getType();
  Note = usbMIDI.getData1();

//================= midiType = 144 NoteON =====================
  if (MidiType == 144) {
    digitalWrite(ledPin, LOW);  // every NoteON turn off LED

//---------------------- START (current magnet) ---------------  
//*********************** B0 midi35 ***************************
    if ((Note == 35) && (note35_state == 0)) { // 35 = midi note "B0" = 61,74 Hz
    // Magnet Forward
      magnets[0] = 1;
      magnets[1] = 0;
      shiftPlay();
      note35_onTime = Time;
      note35_state = 1;
    } // End B0 midi35 ***********************
    
//*********************** D1 midi38 ***************************
    if ((Note == 38) && (note38_state == 0)) { 
    // Magnet Forward
      magnets[6] = 1;
      magnets[7] = 0;
      shiftPlay();
      note38_onTime = Time;
      note38_state = 1;
    } // End D1 midi38 ***********************

//*********************** F1 midi41 ***************************
    if ((Note == 41) && (note41_state == 0)) { // 41 = midi note "F1" = 87,31 Hz
    // Magnet Forward
      magnets[12] = 1; 
      magnets[13] = 0; 
      shiftPlay();
      note41_onTime = Time;
      note41_state = 1;
    } // End B1 midi47 ***********************
//*********************** F#1 midi42 ***************************
    if ((Note == 42) && (note42_state == 0)) { 
    // Magnet Forward
      magnets[14] = 1;
      magnets[15] = 0;
      shiftPlay();
      note42_onTime = Time;
      note42_state = 1;
    } // End F#1 midi42 ***********************
//-------------------------- START End -------------------------
    
  } //================ End MidiType = NoteON =================== 



//------------------------ RETRACT & OFF ------------------------
//++++++++++++++++++++++++++ B0 midi35 ++++++++++++++++++++++++++  
    switch(note35_state) {
      case 1: // Magnet Back
        if (Time >= note35_onTime + Forw_DEL) { 
          magnets[0] = 0;
          magnets[1] = 1;
          shiftPlay();
          note35_state = 2;
        }
      case 2: // Magnet Off
        if (Time >= note35_onTime + Forw_DEL + Back_DEL) {
          magnets[0] = 0;
          magnets[1] = 0;
          shiftPlay();
          note35_state = 3;
        } 
    } // B0 midi38 End +++++++++++++++++++++++
    
//++++++++++++++++++++++++++ D1 midi38 ++++++++++++++++++++++++++  
    switch(note38_state) {
      case 1: // Magnet Back
        if (Time >= note38_onTime + Forw_DEL) { 
          magnets[6] = 0;
          magnets[7] = 1;
          shiftPlay();
          note38_state = 2;
        }
      case 2: // Magnet Off
        if (Time >= note38_onTime + Forw_DEL + Back_DEL) {
          magnets[6] = 0;
          magnets[7] = 0;
          shiftPlay();
          note38_state = 3;
        } 
    } // D1 midi38 End +++++++++++++++++++++++


//+++++++++++++++++++++++++++ F1 midi41 +++++++++++++++++++++++++  
    switch(note41_state) {
      case 1: // Magnet Back
        if (Time >= note41_onTime + Forw_DEL) { 
          magnets[12] = 0;
          magnets[13] = 1;
          shiftPlay();
          note41_state = 2;
        }
      case 2: // Magnet Off
        if (Time >= note41_onTime + Forw_DEL + Back_DEL) {
          magnets[12] = 0;
          magnets[13] = 0;
          shiftPlay();
          note41_state = 3;
        } 
    } // F1 midi41 End +++++++++++++++++++++
//+++++++++++++++++++++++++++ F#1 midi42 +++++++++++++++++++++++++  
    switch(note42_state) {
      case 1: // Magnet Back
        if (Time >= note42_onTime + Forw_DEL) { 
          magnets[14] = 0;
          magnets[15] = 1;
          shiftPlay();
          note42_state = 2;
        }
      case 2: // Magnet Off
        if (Time >= note42_onTime + Forw_DEL + Back_DEL) {
          magnets[14] = 0;
          magnets[15] = 0;
          shiftPlay();
          note42_state = 3;
        } 
    } // F#1 midi42 End +++++++++++++++++++++

    
//---------------------- RETRACT & OFF End -----------------------

//=================== midiType = 128 NoteOFF =====================
  if (MidiType == 128) { 
    digitalWrite(ledPin, HIGH);  // every NoteOFF turn on LED
    if ((Note == 35) && (note35_state == 3)) { // 128 = NoteOFF 
       note35_state = 0;  // B0
    } 
     if ((Note == 38) && (note38_state == 3)) {  
       note38_state = 0;  // D1
    }
    
    if ((Note == 41) && (note41_state == 3)) {  
       note41_state = 0;  // F1
    } 
    if ((Note == 42) && (note42_state == 3)) {  
       note42_state = 0;  // F#1
    }      

  }//================ End midiType = NoteOFF" ====================
  
} // End Loop

Thanks for helping @UHF too! The "TeensyThreads.h" - library seems very interesting.

Best regards,
Bertram Quosdorf
 
@artUdino (Bertram):

Good to hear that you are making great progress.

[ Again, these suggestions are extemporaneous, as I have not actually tested the code provided below, so take due care once again ]

As for improving speed, in your loop() function, you know that any message that you receive will only correspond to a single note, so once you've found & processed a matching note, you can avoid checking for any other notes from that specific message. If you'd like, you could make one of the two following changes (there's no guarantee that either of these options will make a significant difference in your loop speed, but they are possibilities to be considered) . . . what you have is certainly OK, but if you're trying to squeeze as much latency out of your processing as possible, then consider changing to one of these (if you choose to change it, you can also choose which style is more understandable for you):

BEGIN OPTION #1
= = = = = = = = =

Code:
//---------------------- START (current magnet) ---------------  
//*********************** B0 midi35 ***************************
    if (Note == 35) { // 35 = midi note "B0" = 61,74 Hz
        if (note35_state == 0) {
            // Magnet Forward
            magnets[0] = 1;
            magnets[1] = 0;
            shiftPlay();
            note35_onTime = Time;
            note35_state = 1;
        }
    } else { // End B0 midi35 ***********************
    
//*********************** D1 midi38 ***************************
        if (Note == 38) { // 38 = midi note "D1"
            if (note38_state == 0) { 
                // Magnet Forward
                magnets[6] = 1;
                magnets[7] = 0;
                shiftPlay();
                note38_onTime = Time;
                note38_state = 1;
            }
        } else { // End D1 midi38 ***********************

//*********************** F1 midi41 ***************************
            if (Note == 41) { // 41 = midi note "F1" = 87,31 Hz
                if (note41_state == 0) {
                    // Magnet Forward
                    magnets[12] = 1; 
                    magnets[13] = 0; 
                    shiftPlay();
                    note41_onTime = Time;
                    note41_state = 1;
                }
            } else { // End B1 midi47 ***********************
//*********************** F#1 midi42 ***************************
                if (Note == 42) { // 42 = midi note "F#1"
                    if (note42_state == 0) { 
                        // Magnet Forward
                        magnets[14] = 1;
                        magnets[15] = 0;
                        shiftPlay();
                        note42_onTime = Time;
                        note42_state = 1;
                    }
                } // End F#1 midi42 ***********************
            }
        }
    }
//-------------------------- START End -------------------------

END OPTION #1
= = = = = = = =


BEGIN OPTION #2
= = = = = = = = =

Code:
//---------------------- START (current magnet) ---------------  
    switch (Note) {
//*********************** B0 midi35 ***************************
        case 35: { // 35 = midi note "B0" = 61,74 Hz
            if (note35_state == 0) {
                // Magnet Forward
                magnets[0] = 1;
                magnets[1] = 0;
                shiftPlay();
                note35_onTime = Time;
                note35_state = 1;
            }
        } // End B0 midi35 ***********************
        break;
   
//*********************** D1 midi38 ***************************
        case 38: { // 38 = midi note "D1"
            if (note38_state == 0) { 
                // Magnet Forward
                magnets[6] = 1;
                magnets[7] = 0;
                shiftPlay();
                note38_onTime = Time;
                note38_state = 1;
            }
        } // End D1 midi38 ***********************
        break;

//*********************** F1 midi41 ***************************
        case 41: { // 41 = midi note "F1" = 87,31 Hz
            if (note41_state == 0) {
                // Magnet Forward
                magnets[12] = 1; 
                magnets[13] = 0; 
                shiftPlay();
                note41_onTime = Time;
                note41_state = 1;
            }
        } // End B1 midi47 ***********************
        break;

//*********************** F#1 midi42 ***************************
        case 42: { // 42 = midi note "F#1"
            if (note42_state == 0) { 
                // Magnet Forward
                magnets[14] = 1;
                magnets[15] = 0;
                shiftPlay();
                note42_onTime = Time;
                note42_state = 1;
            }
        } // End F#1 midi42 ***********************
        break;
    }
//-------------------------- START End -------------------------

END OPTION #2
= = = = = = = =


As for the need of using the delayMicroseconds(1) or not, the 74HC595 spec says that the minimum time required between asserting the control signals & clocking in data is in the 100ns or less range, so you may be "getting away with" not using the delay. For predictability and/or reliability, I would think that you would want to go ahead & leave the delay in there.

Well done on making progress as you have. Please feel free to ask any other questions that may come up.

Mark J Culross
KD5RXT
 
@ kd5rxt-mark
Thank you very much for this great tutorial! I understand the benefit described in your code examples. That is exactly what I was looking for.
I've opted for option 2, because there are less if-nesting and less brackets at the end. I see the powerful opportunities of the switch/case commands.
In the past I always use the if-query. Now it is much better.

In fact, without the improvement, the speed would be sufficient at the moment. I think in the field of music, the human perception can tolerate rhythmical delays up to ca. 20 ms without being able to notice it. And the clock of my teensy is 600MHz!
But for future improvements I'm planning to implement a sequenzer interface and a keyboard to play the instrument directly and not only via a PC. From this point possibly an efficient code is really needed and can make a significant difference. And then I'm glad about the opportunity here to ask questions again if I got problems or misunderstandings.

Thank you for the help, the ideas and the enlightning tutorial ;) about the switch/case - command.

Bertram Quosdorf
artUdino
 
Back
Top