Stoping a Midi Trigger with Interrupt

Status
Not open for further replies.

lennyooo

New member
Hi Guys,

so I am currently creating this project and got a little stuck with something I guess is super easy for a lot of you guys.

I'm a super newbie to all kind of programming, and although I was super enthusiastic about getting it done myself (which means copying together the working bits), my head just seems to pause instantly, when I try to read about Interrupts...

I am building a Midi-triggered device, that uses solenoids to play inside a Fender Rhodes. The tines basically just create a sound if they are hit really really shortly.

Ok, so I am having a couple of PWM Outputs, which are triggered by a Midi Note and all I want is to use Note On to put out the Velocity and then after a short break cut the output back to 0. Obviously that is easly accomplished by using delay(), but this tends to overflow the processor if I understand correctly???!!

So far I managed to get the wanted results by just chopping the Midi Notes in my DAW as short as I wanted and using the Note Off to cut everything down.

I am really sorry for this mess of a code, but then again I am already quite glad I even got this far:)

Hope someone can help me.


Code:
// USB MIDI receive example, Note on/off -> LED on/off
// contributed by Alessandro Fasan

const int PMW1 = 3;
const int PMW2 = 4;
const int PMW3 = 5;
const int PMW4 = 6;
const int PMW5 = 9;
const int PMW6 = 10;
const int PMW7 = 23;
const int PMW8 = 22;
const int LED = 13;
int Velo;
//unsigned long currentMillis;
const long DEL = 100;
const int LowV = 240;
unsigned long t = 0; 

void setup()

{
  Serial.begin(115200);
  pinMode(LED, OUTPUT);
  pinMode(PMW1, OUTPUT);
  pinMode(PMW2, OUTPUT);
  pinMode(PMW3, OUTPUT);
  pinMode(PMW4, OUTPUT);
  pinMode(PMW5, OUTPUT);
  pinMode(PMW6, OUTPUT);
  pinMode(PMW7, OUTPUT);
  pinMode(PMW8, OUTPUT);
  usbMIDI.setHandleNoteOn(OnNoteOn) ;
  usbMIDI.setHandleNoteOff(OnNoteOff);
}

void loop()         //im loop wird druchgehen der Midi Eingang ausgelesen
{
  usbMIDI.read();
}

void OnNoteOn ( byte channel , byte note , byte velocity)   //hier werden die NoteOn Befehle ausgelesen

{
  if ( note == 60)          //note 60 entspricht einem C3 und ist nur der Einfachheit halber ausgewählt. Ab dem C3 gehts chromatisch hoch
    {
      Velo = map(velocity, 0, 127, LowV, 255);          //um die Velocity auf den Hubmagneten zu übertragen muss sie auf die PMW umgerechnet werden.
      analogWrite(PMW1, Velo);
    }
  if ( note == 61)
    {
      Velo = map(velocity, 0, 127, LowV, 255);
      analogWrite(PMW2, Velo);
      //delay(DEL);           //diese Delay-Schleife muss anders gelöst werden -  Timer Interrupt
      //analogWrite(PMW2, 0);
    }
  if ( note == 62)
    {
      Velo = map(velocity, 0, 127, LowV, 255);
      analogWrite(PMW3, Velo);
    }
  if ( note == 63)
    {
      Velo = map(velocity, 0, 127, LowV, 255);
      analogWrite(PMW4, Velo);
      //delay(DEL);
      //analogWrite(PMW4, 0);
    }
  if ( note == 64)
    {      
      Velo = map(velocity, 0, 127, LowV, 255); 
      analogWrite(PMW5, Velo);     
    }
  if ( note == 65)
    {      
      Velo = map(velocity, 0, 127, LowV, 255); 
      analogWrite(PMW6, Velo);     
    }
  if ( note == 66)
    {      
      Velo = map(velocity, 0, 127, LowV, 255); 
      analogWrite(PMW7, Velo);
    }
  if ( note == 67)
    {    
      Velo = map(velocity, 0, 127, LowV, 255); 
      analogWrite(PMW8, Velo);
    }
}

void OnNoteOff (  byte channel , byte note, byte velocity)  //durch den NoteOFF befehlt wird der Ausgang direkt auf LOW gesetzt. 

{
  if ( note == 60)
    {
      analogWrite(PMW1, 0);
    }
  if ( note == 61)
    {
      analogWrite(PMW2, 0);
    }
  if ( note == 62)
    {
      analogWrite(PMW3, 0);
    }
  if ( note == 63)
    {
      analogWrite(PMW4, 0);
    }
  if ( note == 64)
    {
      analogWrite(PMW5, 0);
    }
  if ( note == 65)
    {
      analogWrite(PMW6, 0);
    }
  if ( note == 66)
    {
      analogWrite(PMW7, 0);
    }
  if ( note == 67)
    {
      analogWrite(PMW8, 0);
    }
}

Thank you!!!
 
how about something like this ..... it is fragile, since it will only work properly if you have restricted midi input to notes 67 to 60. Note that elapsedMillis overflows after some days or something, from memory

Code:
// USB MIDI receive example, Note on/off -> LED on/off
// contributed by Alessandro Fasan

long time [8] = {};
int pwmPin [8] = {3, 4, 5, 6, 9, 10, 23, 22};
int  status [8] = {};
elapsedMillis mytimer;

const int LowV = 240;

void setup()
{
  Serial.begin(115200);
for (int i = 0; i<8, i++)
{ 
  pinMode(pwmPin [i], OUTPUT);
  delay(1);
}  
  usbMIDI.setHandleNoteOn(OnNoteOn) ;
usbMIDI.setHandleNoteOff(OnNoteOff) ;
}

void loop() 
{
  usbMIDI.read();
 for (int i = 0; i<8, i++)
{
if (status[i]) 
{
if ((mytimer - time [i]) > 100) 
{
analogWrite(pwmPin, 0);
status[i] = 0;
}
}
}
}

void OnNoteOn ( byte channel , byte note , byte velocity) 
{
status [67-note] = 0;
analogWrite (pwmPin[67-note], map(velocity, 0, 127, LowV, 255));
}

void OnNoteOff ( byte channel , byte note , byte velocity)   
{
status [67-note] = 1;
time [67-note] = mytimer;
}

edit: you could move the usbMIDI.read(); inside the for loop in main. AND sorry for the redefinition error for the callback ... I wrote this with no compiler handy to test ...
 
Last edited:
wow, thank you so much!!! obviously working with arrays is so much smarte and more practical, than what I did:)

took a bit of adjustment to get it to work, but this here seems to be fine now:

Code:
// USB MIDI receive example, Note on/off -> LED on/off
// contributed by Alessandro Fasan

unsigned long time [8] = {};
int pwmPin[8]= { 3, 4, 5, 6, 9, 10, 23, 22};
int status[8]= {};
elapsedMillis mytimer;

const int LowV = 240;
const int hammerTime = 30;
void setup()
{
  Serial.begin(115200);
for (int i = 0; i<8; i++)
{ 
  pinMode(pwmPin[i], OUTPUT);

}  
  usbMIDI.setHandleNoteOn(OnNoteOn) ;
  //usbMIDI.setHandleNoteOn(OnNoteOff) ;
}

void loop() 
{
  usbMIDI.read();
  
 for (int i = 0; i<8; i++)
{
if (status[i] == 1) 
{
if ((mytimer - time[i]) >= hammerTime) 
{
analogWrite(pwmPin[i], 0);
status[i] = 0;
}
}
}
}

void OnNoteOn ( byte channel , byte note , byte velocity) 
{
status [67-note] = 1;
analogWrite (pwmPin[ 67-note ], map(velocity, 0, 127, LowV, 255));
time [67-note] = mytimer;
}

edit: oh, didn't see your edit yet!
somehow once I moved (time [67-note] = mytimer;) into the same OnNoteOn - function, it worked. This should be fine right?

next edit: yes, now I understand. I actually had to repel the NoteOff command and just work with NoteOn as I did now. So whenever I hit a key, no matter how long I press it down, I only have a short output-spike. Thanks so much, this is it!!
 
Last edited:
edit: oh, didn't see your edit yet!
somehow once I moved (time [67-note] = mytimer;) into the same OnNoteOn - function, it worked. This should be fine right?

next edit: yes, now I understand. I actually had to repel the NoteOff command and just work with NoteOn as I did now. So whenever I hit a key, no matter how long I press it down, I only have a short output-spike. Thanks so much, this is it!!

Glad you have worked it out .... be aware that anything but notes 67 - 60 will give an array out of bounds error and everything falls over ... the code is fragile ... funnily enough, I originally only did a note on short spike, but I thought you might want to time it off the release event!! Anyway.... all goood
 
.... be aware that anything but notes 67 - 60 will give an array out of bounds error and everything falls over

would this do the trick?
stocked it up to 10 channel output.

Code:
void OnNoteOn ( byte channel , byte note , byte velocity) 
{
  if (note == 60 || 61 || 62 || 63 || 64 || 65 || 66 || 67 || 68 || 69 )
  {
status [69-note] = 1;
analogWrite (pwmPin[ 69-note ], map(velocity, 0, 127, LowV, 255));
time [69-note] = mytimer;
  }
}
 
The status and time global variables should be declared with the volatile keyword. The volatile keyword tells the compiler not to cache the contents of memory in registers, because that memory can be changed at any time (i.e. in an interrupt handler).
 
thanks guys, applied both suggestions.
will upload a video of the machine once its properly finished and ready to be documented:)
 
@MichaelMeissner ... the volatile keyword ... is it necessary if global variables are not used in an interrupt? I ask for my own information.

OP
How about this ...

Code:
if (note < 60) {note = 60};
if (note > 69) {note = 69};

That way you always get some sound. Please give us some soundclips!!
 
You only need volatile if the variable could change in ways that the compiler does not know about. So any variable that is set or accessed in the interrupt handler needs to have voltatile. If you have devices that look like memory addresses, those also need volatile. Normal global variables that might be modified in another function do not need the declaration, because the compiler assumes that any function outside of the current source file can change any global.

Note, the C++ (and C) syntax can be surprising:

Code:
volatile int foo;

Declares foo to be volatile. However, for pointers:

Code:
volatile int *ptr;

Declares the pointer itself to be volatile, but the memory that the pointer points to is not considered volatile. Instead you would use the syntax:

Code:
int *volatile ptr;

This says ptr is a normal global pointer, but it points to volatile memory, and every use must be direct, and the compiler cannot cache the results. You can use both together:

Code:
volatile int *volatile ptr;

Says that ptr itself is volatile, and it points to volatile memory.
 
The status and time global variables should be declared with the volatile keyword. The volatile keyword tells the compiler not to cache the contents of memory in registers, because that memory can be changed at any time (i.e. in an interrupt handler).
Which interrupt handler? OnNoteOn / OnNoteOff are synchronously invoked via usbMIDI.read().
 
Status
Not open for further replies.
Back
Top