Teensy 3.6 MIDI keyboard with knobs and buttons latency issue

Status
Not open for further replies.

fanatic606

New member
Hi,
few weeks ago my old Farfisa organs broke down as it used to do every couple months. Before it happened I have sampled to WAV file all notes and sounds from it.
As I've already had two-manuals great keyboard I decided to make my own MIDI keyboard out of it.

I've made some research and found ot that Teensy would be great. As I wanted to have lowest possible latency I bought newest teensy 3.6. I also got some MCP23017 digital IO extender and started to develop this new instrument. Farfisa keyboard doesn't have matrix connected keys. Every key has it's own wire sticking out from it. As you push the key, it connects to common wire which i decided to make grounded. Using Fritzing I've made circuit diagram to know how to deal with tons of cables. I have changed it many times but what you can see below reflect what has been done till now.
farfisa v15_schem.jpg
DSC_0966.jpg
DSC_0970.jpg
I still did not connect analog knobs, buttons and diodes. I'm trying to solve latency issue with all the keys connected. There are 88 of them!
I'm not so good at programming so maybe there is only need in optimising the code. At the beginning when I had only two MCP 23017 connected latency was unnoticeable. Right know there are about 1300 lines of code and all six boards are connected(two more will be connected soon). Latency is big and it really influences playing on it. I have recorded with microphone key click and sound coming out of speakers. I have done it with both codes - short and long one. Below you can see two audio waveforms compared to each other. Long code has about 140ms from hitting the button to playing sound. Short is about 70ms(with all boards physically connected but just not included in the code).
latency comparison.png

Both codes and other files you can find on https://github.com/fanatic606/Farfisa-Teensy-MIDI
(sorry I'm new to Github. I hope I uploaded everything right).

Please help me decrease the latency :)

p.s. How to make sustain pedal work? Is there any midi message for it or should I just make "if" function to don't change the value of notes to 0 when the key is pushed back?
p.s2. if I'm using midi channel 1 to notes from 5 to 100 and CC 0-4 and 101-115 do I have to use channel 2 for next 15 buttons? Or maybe I can fit it somehow on channel 1?
 
Have you determined what parts of your code are using large amounts of time? You can use millis() or the elapsedMillis objects to get a timestamp and the start and end of sections of your code. How long is one pass through your loop? Is there a delay from sending a midi command to when you hear the sound?

I2C is not very fast and you have considerable I2C activity. If you are at 100 khz, you can make that 4 times faster by using a 400 khz clock.
 
Have you determined what parts of your code are using large amounts of time? You can use millis() or the elapsedMillis objects to get a timestamp and the start and end of sections of your code. How long is one pass through your loop? Is there a delay from sending a midi command to when you hear the sound?

I2C is not very fast and you have considerable I2C activity. If you are at 100 khz, you can make that 4 times faster by using a 400 khz clock.

I don't know what parts are using large amount of time. Code that was pretty fast is almost the same as the final code that is using a large amount of time. The only difference is how many of lines of code I'm using. There are the same sections but bigger. You can compare it if you go to Github link.

I don't know how to check delay from sending midi command to when i hear the sound. All I can measure is time between physically hitting the key and sound being played from speakers. I explained it and provided screenshot from waveform that shows difference. You can hear the difference if you go to Github and download Wav file.

Can you give an example how to use millis() or elapsedMillis?
How to switch to 400khz clock from 100khz, can you give me some tutorial or sample code?
 
Sustain is MIDI CC 64, value=0 is off, value=127 is on

sort of like this:
usbMIDI.sendControlChange(64, 0, 1); // sust off
usbMIDI.sendControlChange(64, 127, 1); // sust on

MIDI note ranges from 0 to 127 so you can keep both keyboards within a same channel.
How about you split the keyboard into two channels (upper keys = CH1, lower keys = CH2)?
That way you can easily layer two different instruments, or just select piano to both of them with some transpose going on on the upper keys.

There's lots of inputs involved, and that may slow down your loop routine.
Can you cut the wire that connects to all notes that you connected to GND?
If yes, multiplexing is possible. Almost all modern keyboards use multiplexing nowadays.
However, you need a diode for every note (1N4148 is good).

Connection will be like this: (groups of 8, you can group as many as you want)
piano-847409_960_720_2.jpg
So with your setup, (44x2 keys), you can read the whole keyboard by using just 19 wires! (8 + (88 / 8))
 
Hello everyone,

nice project! It reminds me of the old and defect home organ which sadly went to the trash a few years ago. Sadly, I did not know anything about Teensy and Arduino at this point of time, so I would have saved it :(

But back to topic:

Can you give an example how to use millis() or elapsedMillis?

Yes, the example uses "elapsedMicros" (using microseconds instead of milliseconds), but you could easily use "elapsedMillis" instead:

Code:
// if you need longer times, you can also use elapsedMillis instead of elapsedMicros  
elapsedMicros myTimer = 0; // elapsedMillis myTimer = 0; // 
uint16_t myLoopCounter = 0;
uint16_t myloopCount = 5000;



void setup()
{
	// setup stuff here
}

void loop()
{
	// reset timer at the beginning of you loop
	myTimer = 0;

	// do all you loop stuff like scanning keys, debouncing etc. here ...
	// ...
	// ...




	// at the end of the loop: 
	// increase loop counter, print the loop data every 'myloopCount' times (increase 'myloopCount' if the serial monitor gets to crowded)
	// you could also calculate the average loop time
	myLoopCounter++;
	if (myLoopCounter >= myloopCount) {
		myLoopCounter = 0;
		Serial.println("Loop Time in us:");
		Serial.println(myTimer);
	}
}


How to switch to 400khz clock from 100khz, can you give me some tutorial or sample code?

I2C setup:

Code:
Wire.begin(); //Join the bus as master. 
//By default .begin() will set I2C SCL to Standard Speed mode of 100kHz
Wire.setClock(400000); //Optional - set I2C SCL to High Speed Mode of 400kHz

Source see here.


Also have a look at the optimized Teensy I2C library.

I do not know if it is at this time of the project an option for you to change the hardware, fanatic606, but I would say the same as Revalogics:
maybe you should have a look into mulitplexing (matrix scanning). I am not sure, if you can achieve the goal of low latency with the multiple port expanders. I guess with mulitplexing you can get down your latency.

The diode for each key would be required for polyphony.

You can find the concepts of input matrix scanning (especially for musical instruments) here. Scroll down to 'Matrix Scanning Options' and see the futher links.

I am currently working on the option using 'Parallel-out shift register mux’d with uC' in my project.

You can also check the Teensy forum thread I openeded on debouncing a button matrix, there are the schematics and some code. Maybe there are better ways (as I am also learning), but the results look promising so far.

Hope that helps :)

Best wishes,

afterwards
 
Last edited:
Status
Not open for further replies.
Back
Top