Hi Everyone,
I am trying to write a MIDI note repeat sketch and I am a bit stuck and trying to figure out why it isn't working and how to move forward. Here is a short demo of the idea using Logic Pro X MIDI note repeat effect for reference: https://youtu.be/kAuJ-MEJQCQ?t=135
I am using a Teensy LC and I’ve built a basic DIN MIDI input output circuit. There are no issues with the circuit, it is the code that is the issue at this time as I am still very much learning.
The idea of the sketch is that at X amount of clock tick intervals, X amount of notes are repeated.
So for example: I want a dotted 16th note repeat to play 3 times each time a note on message is sent.
In my sketch here, I've hard coded the note repeat tick and the note repeat amount for now, but eventually, I'd like to use a rotary encoder w/ push button to select different values...
I am trying to use an array to keep track of notes so I can “note off” the correct notes.
When I serial print out the repeated note ons, I see them in the serial monitor but I do not hear their output, nor are they registered in a MIDI monitor.
In short: The initial note ons are working, but I cannot seem to connect the repeated notes to those original note on messages.
Any guidance and support is greatly appreciated! Thank you in advance
I am trying to write a MIDI note repeat sketch and I am a bit stuck and trying to figure out why it isn't working and how to move forward. Here is a short demo of the idea using Logic Pro X MIDI note repeat effect for reference: https://youtu.be/kAuJ-MEJQCQ?t=135
I am using a Teensy LC and I’ve built a basic DIN MIDI input output circuit. There are no issues with the circuit, it is the code that is the issue at this time as I am still very much learning.
The idea of the sketch is that at X amount of clock tick intervals, X amount of notes are repeated.
So for example: I want a dotted 16th note repeat to play 3 times each time a note on message is sent.
In my sketch here, I've hard coded the note repeat tick and the note repeat amount for now, but eventually, I'd like to use a rotary encoder w/ push button to select different values...
I am trying to use an array to keep track of notes so I can “note off” the correct notes.
When I serial print out the repeated note ons, I see them in the serial monitor but I do not hear their output, nor are they registered in a MIDI monitor.
In short: The initial note ons are working, but I cannot seem to connect the repeated notes to those original note on messages.
Any guidance and support is greatly appreciated! Thank you in advance
Code:
#include <Arduino.h>
#include <MIDI.h>
/*
(d): dotted time, one and half note length
(tr): triplet time, two thirds of duration
0: 32th notes - 3 ticks
1: 16th (tr) - 4 ticks
2: 16th - 6 ticks
3: 8th (tr) - 8 ticks
4: 16th (d) - 9 ticks
5: 8th - 12 ticks
6: quarter (tr) - 16 ticks
7: 8th (d) - 18 ticks
8: quarter - 24 ticks (default PPQN is 24 PPQN or ticks)
9: half (tr) - 32 ticks
10: quarter (d) - 36 ticks
11: half - 48 ticks
*/
byte dm_type, dm_note, dm_velocity, dm_channel_received, dm_data1, dm_data2, dm_cc_num, dm_cc_val; // DIN MIDI data
byte dm_channel_select = 0; //0 to receive any DIN MIDI channel
unsigned long clock_ticks;
unsigned long old_clock_ticks;
unsigned long note_repeat_tick;
byte note_repeat_amount;
long current_time;
long prev_time;
/*
Use arrays to keep track of what notes we've received and what note we've output
so we can noteoff the correct note(s)
a define is not a variable and can be used in the declaration section to set array sizes
for example, 6 means it can track 6 notes at once. We can easily increase it but this makes it easier to see whats happening in the printout
*/
#define note_array_len 256 // plenty of room here for notes :)
int notes_received[note_array_len];
int notes_sent[note_array_len];
int note_index;
int while_count;
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
void setup() {
MIDI.begin();
MIDI.turnThruOff(); // software Thru is enabled by default on Serial so we need to disable.
}
void loop() {
current_time = millis();
//DIN MIDI input
if (MIDI.read()) { // Is there a MIDI message incoming ?
dm_type = MIDI.getType();
dm_channel_received = MIDI.getChannel();
if (0) { //change to 1 to print the channel
Serial.print("dm channel received: ");
Serial.print(dm_channel_received);
}
// only do all of this if it's the channel we want
// "||" is "logical or", allowing us to go in when dm_channel_select is 0 aka Omni
if (dm_channel_received == dm_channel_select || dm_channel_select == 0) {
if (dm_type == midi::NoteOn) {
dm_note = MIDI.getData1();
dm_velocity = MIDI.getData2();
// set these when you get the new note
note_repeat_tick = 4; // when do we want to send the repeated note on?
note_repeat_amount = 3; // how many times do we want to repeat the note
if (dm_velocity > 0) { // is it a note on message?
// while keeps doing the code inside the {} untill it's true
// we want to skip the slots that are filled but we also need a way of exiting if all slots are filled
while_count = 0;
while (notes_received[note_index] != 0) {
Serial.print("note_index ");
Serial.println(note_index);
note_index++;
if (note_index > note_array_len - 1) {
note_index = 0;
while_count++;
if (while_count > 1) {
//we've gone through the whole array at least once and theres no place for the note
// if this happens we just need to increases note_array_len
Serial.print(" ! TOO MANY NOTES !");
break; //exit the while
}
}
}
// access the array so we can track both the incoming notes and what notes are being sent on as note on data
notes_received[note_index] = dm_note;
notes_sent[note_index] = dm_note;
//send the first note on
MIDI.sendNoteOn(dm_note, dm_velocity, dm_channel_received);
Serial.print("Note On:");
Serial.print(dm_note);
Serial.print(" velocity: ");
Serial.println(dm_velocity);
Serial.print("tick set: ");
Serial.println(note_repeat_tick);
Serial.print(" repeat set: ");
Serial.print(note_repeat_amount);
Serial.println();
}
if (dm_velocity == 0) { //some systems send velocity 0 as note off
Serial.println("Note Off: ");
Serial.println(dm_note);
}
}
else if (dm_type == midi::NoteOff) { //else if can be used so we only have one outcome
dm_note = MIDI.getData1();
for (int i = 0; i < note_array_len; i++) { // this loop allows us to access the note array to note off the correct note(s)
if (dm_note == notes_received[i]) {
MIDI.sendNoteOff(notes_sent[i], dm_velocity, dm_channel_received);
notes_received[i] = 0;
notes_sent[i] = 0;
break; //exit this for loop as we found what we want
}
}
Serial.print("Note Off: ");
Serial.println(dm_note);
// Serial.print(" note repeat: ");
// Serial.println(note_repeat_amount);
}
else if (dm_type == midi::ControlChange) {
dm_cc_num = MIDI.getData1();
dm_cc_val = MIDI.getData2();
Serial.print("CC#: ");
Serial.print(dm_cc_num);
Serial.print(" value: ");
Serial.println(dm_cc_val);
}
else if (dm_type == midi::Clock) {
clock_ticks++; // count incoming clock ticks
if (clock_ticks >= note_repeat_tick && note_repeat_amount > 0) { // condition for note repeat instance and note repeat amount
MIDI.sendNoteOn(dm_note, dm_velocity, dm_channel_received); // send the note repeat data
note_repeat_amount--; //reduce the repeat amount each time we play a note
clock_ticks = 0; //reset this so it will take another X amount of ticks to play the next note
}
Serial.println();
Serial.print(" note repeat: ");
Serial.println(note_repeat_amount);
Serial.print("note 1: ");
Serial.println(dm_note);
Serial.print(" note 2: ");
Serial.println(dm_note);
Serial.print(" note 3: ");
Serial.println(dm_note);
Serial.println();
}
else if (dm_type == midi::Start) {
// clock_ticks = 0;
Serial.println(" Starting");
}
else if (dm_type == midi::Continue) {
// clock_ticks = old_clock_ticks;
Serial.println("Continuing");
}
else if (dm_type == midi::Stop) {
// old_clock_ticks = clock_ticks;
Serial.println(" Stopping");
}
else if (dm_type == midi::ActiveSensing) {
// Serial.println("ActiveSense");
}
//If it's not a note on or off or cc do this.
// If there were just "ifs" being used above there could be a final else that would catch all the other message types
// the other types are :
/*
midi::AfterTouchPoly
midi::ProgramChange
midi::AfterTouchChannel
midi::PitchBend
midi::SystemExclusive
*/
else {
dm_data1 = MIDI.getData1();
dm_data2 = MIDI.getData2();
}
}
}
//DIN Send
//There are lots of types of midi messages. Here are the most used ones. All of them are here https://www.pjrc.com/teensy/td_midi.html (just change them to MIDI instead of usb MIDI)
/*
MIDI.sendNoteOn(note, velocity, channel);
MIDI.sendNoteOff(note, velocity, channel);
MIDI.sendControlChange(control, value, channel);
MIDI.sendAfterTouch(pressure, channel);
MIDI.sendPitchBend(value, channel);
*/
// if (current_time - prev_time > 100) { // checking the note array buffer
//
// prev_time = current_time;
//
// Serial.print("received ");
//
// for (int i = 0; i < note_array_len; i++) {
//
// Serial.print(notes_received[i]);
// Serial.print(" ");
//
// }
//
// Serial.println();
// Serial.print("sent ");
//
// for (int i = 0; i < note_array_len; i++) {
//
// Serial.print(notes_sent[i]);
// Serial.print(" ");
//
// }
//
// }
}