Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIDI] Sustain effect when same notes are played. #10199

Closed
3araht opened this issue Aug 29, 2020 · 17 comments
Closed

[MIDI] Sustain effect when same notes are played. #10199

3araht opened this issue Aug 29, 2020 · 17 comments

Comments

@3araht
Copy link
Contributor

3araht commented Aug 29, 2020

I referred here to assign a chord to each key. When multiple chords are played and when the chords share the same note(s), it activates a sustain effect that stays until you unplug the keyboard.
When you are using GarageBand, the symptom can be fixed by clicking "Revert" button on the bottom left corner on the GarageBand window. But want to avoid it from the first place...
Could someone help me out?

Thank you in advance!

@jakobkg
Copy link

jakobkg commented Sep 11, 2020

That's interesting, it's probably some kind of oddity that happens when the note on message is sent for the same note multiple times without the note off message being sent in between. I'll try to replicate it and see what I can figure out!

@3araht
Copy link
Contributor Author

3araht commented Sep 11, 2020

Thanks! I appreciate it.

@jakobkg
Copy link

jakobkg commented Sep 12, 2020

Hmm, I've been trying for a while now but I'm unable to replicate the issue. I've set two keys as MI_CH_C and MI_CH_CDom7 and playing them both at the same time just plays the two chords. When I release one chord, the notes that are shared are all turned off.

I'm flashing QMK on a pro micro and connecting it to my PC which is running Ableton Live 10.

Example code:

#include QMK_KEYBOARD_H
#include "qmk_midi.h"

enum custom_keys {
  // MIDI Chord Keycodes - Major

  MI_CH_C,
  MI_CH_Cs,
  MI_CH_Db = MI_CH_Cs,
  MI_CH_D,
  MI_CH_Ds,
  MI_CH_Eb = MI_CH_Ds,
  MI_CH_E,
  MI_CH_F,
  MI_CH_Fs,
  MI_CH_Gb = MI_CH_Fs,
  MI_CH_G ,
  MI_CH_Gs,
  MI_CH_Ab = MI_CH_Gs,
  MI_CH_A,
  MI_CH_As,
  MI_CH_Bb = MI_CH_As,
  MI_CH_B,

  //MIDI Chord Keycodes Dominant Seventh

  MI_CH_CDom7,
  MI_CH_CsDom7,
  MI_CH_DbDom7 = MI_CH_CsDom7,
  MI_CH_DDom7,
  MI_CH_DsDom7,
  MI_CH_EbDom7 = MI_CH_DsDom7,
  MI_CH_EDom7,
  MI_CH_FDom7,
  MI_CH_FsDom7,
  MI_CH_GbDom7 = MI_CH_FsDom7,
  MI_CH_GDom7,
  MI_CH_GsDom7,
  MI_CH_AbDom7 = MI_CH_GsDom7,
  MI_CH_ADom7,
  MI_CH_AsDom7,
  MI_CH_BbDom7 = MI_CH_AsDom7,
  MI_CH_BDom7
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /* Base */
    [0] = LAYOUT(
    MI_CH_C, MI_CH_CDom7
    )
};

bool process_record_user (uint16_t keycode, keyrecord_t *record) {

  uint16_t root_note = MIDI_INVALID_NOTE; // Starting value for the root note of each chord

  switch(keycode) {

	// MIDI Chord Keycodes

  case MI_CH_C ... MI_CH_B: // Major Chords
	root_note = keycode - MI_CH_C + MI_C;
	process_midi(root_note, record);
	process_midi(root_note + 4, record); // Major Third Note
	process_midi(root_note + 7, record); // Fifth Note
	break;

  case MI_CH_CDom7 ... MI_CH_BDom7: // Dominant 7th Chord
	root_note = keycode - MI_CH_CDom7 + MI_C;
	process_midi(root_note, record);
	process_midi(root_note + 4, record); // Major Third Note
	process_midi(root_note + 10, record); // Minor Seventh Note
	break;
  }
  return true;
}

For example:
I press MI_CH_C so the notes C, E and G play. I keep that held and press MI_CH_CDom7. MI_CH_CDom7 plays the notes C, E and Bb so the C and E are re-triggered and a Bb is played, while the G keeps ringing.
I then release MI_CH_C, and the notes C, E and G are all stopped. This means that the only note still playing is Bb even though I'm still holding MI_CH_CDom7.
The opposite is also true, so if I release MI_CH_CDom7 the C, E and Bb stop and the only note still playing is a G.

This is an audio recording of both these tests:
https://vocaroo.com/23kuNYatIeK

I also tested something equivalent by setting two keys to MI_C, and the same thing happens. If I press them both and release only one of the keys, the note stops since the note off message gets sent when a key is released even though I'm still holding the note on another key.

This is kind of bad in my opinion, but it's a different problem to the one you're describing. Maybe this is a problem specific to GarageBand? I'd love it if you could provide more details about your setup and if the problem persists with a more barebones firmware or in other apps than GarageBand.

@3araht
Copy link
Contributor Author

3araht commented Sep 12, 2020

I see. That is also kind of strange.
At least, we share an issue that the note which is pressed stops ringing when a duplicate note is released at your end.
On my side, maybe related to GarageBand, the note which has to stop keeps ringing.

I'll try "Ableton Live 10 Lite" and see what happens on my side.

Here's an example on my side. It's very noisy since I couldn't find a way to directly record the output of GarageBand and end up recording it once on my iPad, and record via vocaroo... I feel I'm 80 years old...
https://voca.ro/1nBms4FSkU4N

I played just like you did first, and then in order to emphasize the sustain effect, I played F major, C major (sustain), G major,
Fdom7, Cdom7 (sustain again), Gdom7.

I'm using Pro Micro, too. The keyboard I use is shown here:
https://github.com/3araht/giabalanai

Thanks!

@jakobkg
Copy link

jakobkg commented Sep 12, 2020

I recorded a file on my PC and uploaded to vocaroo with the little "upload" button on the page instead of recording directly on vocaroo, that should work for you too.

That is a very interesting board you're making, I like it a lot!

@3araht
Copy link
Contributor Author

3araht commented Sep 12, 2020

LOL. I should have done that.
Here you are. Now I'm back to 40s.
https://voca.ro/14nwiestv17w

I'm glad you like the board.

@3araht
Copy link
Contributor Author

3araht commented Sep 12, 2020

Yep, the symptom seems MIDI software dependent.
I was able to replicate yours. Sorry for using a microphone again. didn't have the authority to export data directly from Ableton Live 10 Lite.
https://voca.ro/18F45DA201XJ

The last sound (A#) was tapped while I keep pressing Cdom7 code button.
All sounds stop ringing.

Compared to the sustain effect seen with GarageBand, the symptom seen with Ableton Live 10 would be easier to live with, in my opinion.

Well, a lot of novice players, including me, use GarageBand, so I wish if there is any solution to deal with it.

@3araht
Copy link
Contributor Author

3araht commented Sep 13, 2020

Logic Pro also shows the same symptom.
Do you think it is possible to fix both symptoms?

@jakobkg
Copy link

jakobkg commented Sep 17, 2020

How strange! I've been busy with work, but I'll have some time today to install Logic and do some testing. I think I have an idea for a fix, but it'll take me a bit to test and implement.

@3araht
Copy link
Contributor Author

3araht commented Sep 18, 2020

It seems I accidentally found a fix!!!
See below for the modification I made.
This solved the sustain problem, but not the new issue you found with Ableton Live 10...
However, it is much much better for GarageBand users and Logic Pro users.
Is this modification acceptable?


diff --git a/quantum/process_keycode/process_midi.c b/quantum/process_keycode/process_midi.c
index b2fb902eb..e52577014 100644
--- a/quantum/process_keycode/process_midi.c
+++ b/quantum/process_keycode/process_midi.c
@@ -68,10 +68,12 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) {
             uint8_t tone     = keycode - MIDI_TONE_MIN;
             uint8_t velocity = compute_velocity(midi_config.velocity);
             if (record->event.pressed) {
-                uint8_t note = midi_compute_note(keycode);
-                midi_send_noteon(&midi_device, channel, note, velocity);
-                dprintf("midi noteon channel:%d note:%d velocity:%d\n", channel, note, velocity);
-                tone_status[tone] = note;
+                if (tone_status[tone] == MIDI_INVALID_NOTE) {
+                    uint8_t note = midi_compute_note(keycode);
+                    midi_send_noteon(&midi_device, channel, note, velocity);
+                    dprintf("midi noteon channel:%d note:%d velocity:%d\n", channel, note, velocity);
+                    tone_status[tone] = note;
+                }
             } else {
                 uint8_t note = tone_status[tone];
                 if (note != MIDI_INVALID_NOTE) {

@3araht
Copy link
Contributor Author

3araht commented Sep 18, 2020

I got a question from ralfkaa, what I intended to do. I apologize for not describing it beforehand.
See below.
#9456 (comment)

@jakobkg
Copy link

jakobkg commented Sep 19, 2020

Very clever, nice find! I feel like the three of us have slightly different ideas about how pressing the same note multiple times should be handled, it's probably best if we keep it to discussing in the PR you've opened so we don't end up having the same conversation in multiple different places!

@jakobkg
Copy link

jakobkg commented Sep 19, 2020

Actually, here are my thoughts before I start coding something that might be unneccesary or not work

I would personally like it if it was possible to send note on messages for the same note multiple times, and as long as the note off message is only sent once per note there shouldn't be any issues, at least from my very quick read-through of the MIDI note message specification. I'm fairly certain the issues you've been experiencing have been due to software receiving note off messages for notes that are already off, but I'll have to test a little to be sure of that.

My thinking is that it should be possible to use a QMK MIDI device to produce the effect of playing the same not on different strings on a guitar: every time you pluck a string (press a key) a new note is played (a new MIDI note on message is sent) but if you mute one of the strings (release one of the keys) the note doesn't stop as long as it's still playing on a different string (a different key with the same note is still held).

A very rough sketch of this idea would be

when note key pressed:
  add note to array of currently playing notes
  send midi note on message

when note key released:
  remove note from array of currently playing notes
  if note is not in array of currently playing notes:
    send midi note off message

So if I for example have two keys mapped to MI_C_1 and I press the first one, the contents of the array would be [ MI_C_1 ] and the note would trigger. If I then press the other MI_C_1 key, the note would be triggered again and the contents of the array would be [MI_C_1, MI_C_1]. If I then release one of the keys, the array would have one element removed and only be [ MI_C_1 ]. Even though I released one key, another key with the same note is still pressed and so the note off message isn't sent. If I then release the other MI_C_1 key, the array would be empty and the MIDI note off message would be sent.

I hope QMK already keeps a list of currently pressed keys somewhere to make this easier, but I haven't been able to find anything yet. If done from scratch this might end up a little memory hungry, but unless someone made a keyboard with lots of the same note it likely won't be an issue.

@3araht
Copy link
Contributor Author

3araht commented Sep 19, 2020

How nice! I like your idea. Your solution is "how it should be". It will solve the issue you find with Ableton Live 10, and what we share now with GarageBand, Logic Pro X, and so forth, after the fix shown in the PR I sent.
So I look forward to it.
The PR I sent is somewhat a temporal update. But it makes GarageBand and Logic Pro X users' life easier at the moment.

Regarding how many same notes on a keyboard, I'm not sure how many out there.
For example, there are 60 keys for chords as you can see here on the left hand side keyboard.
I have no idea which has duplicate notes... At least 5 maybe? No one should play all of them at the same time though. But it has to be prepared for such situation.

@3araht
Copy link
Contributor Author

3araht commented Oct 4, 2020

The fix has been made and merged. See here for details.
#10361

@3araht 3araht closed this as completed Oct 4, 2020
@jakobkg
Copy link

jakobkg commented Oct 6, 2020

Great work! I have been very busy lately and have not been taking the time or energy to work on QMK things so it's great that you've picked this up and solved it!

@3araht
Copy link
Contributor Author

3araht commented Oct 6, 2020

Oh, but the issue you found remains there.
It's just the quick fix for the sustain effect issue.

tzarc pushed a commit that referenced this issue Mar 25, 2021
… issue #10199) (#11639)

* Fix handling multiples of the same MIDI note

* Extend MIDI note status to fix note releases
mrlinuxfish pushed a commit to mrlinuxfish/qmk_firmware that referenced this issue Mar 28, 2021
… issue qmk#10199) (qmk#11639)

* Fix handling multiples of the same MIDI note

* Extend MIDI note status to fix note releases
mrtnee pushed a commit to mrtnee/qmk_firmware that referenced this issue Nov 20, 2021
… issue qmk#10199) (qmk#11639)

* Fix handling multiples of the same MIDI note

* Extend MIDI note status to fix note releases
BorisTestov pushed a commit to BorisTestov/qmk_firmware that referenced this issue May 23, 2024
… issue qmk#10199) (qmk#11639)

* Fix handling multiples of the same MIDI note

* Extend MIDI note status to fix note releases
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants