Note overlap

Thank you for this great library. I am developing a rhythm sequencer. If for instance, I have a kick sound lasting for 600ms, and I have two consecutive pulses of this kick separated by 500ms, I can’t achieve to play the two pulses during their original length, or without cutting their duration manually or inherited from duration.

  • Can those two pulses overlay on the same channel?
  • If not, what would be a solution to get something like this:

Kick [ms

Kick         Kick
|---------------
             |---------------

In conclusion: can midi notes overlay?

Comments

  • Unfortunately for you, as far as I know, this is the normal MIDI behaviour. You cannot have the same note on the same channel playing twice at the same time.

    In situations like this, people have been using another channel for the second note. This is not the most convenient thing but it does the trick.

  • Thank you very much for your answer Jean-Philippe. Currently, I suspect my notes to be ignored because the previous one’s duration isn’t over. Considering that, what would you recommand to allow all my notes to play? Should I, for instance add a stopNote() before all playnote()?

  • edited February 4

    Usually, the new note shuts down the old one. That is, there is an implicit noteoff before a noteon. This is the device's doing. You can try calling stopNote() but I do not think it will change anything.

  • Fine. One more last question, I don’t find how to control volume with the library. The W3C spec is also very quiet about volume control with webmidi. Did I miss something?

  • Device-wide master volume control can be achieved by sending a "Universal Real Time System Exclusive message". The code to do this in v2.5.x would look something like this:

    WebMidi.outputs[1].sendSysex(0x7F, [0x7F, 0x7F, 0x04, 0x01, 0x12, 0x34]);
    

    WebMidi.js does not really offer any help when it comes to system exclusive message. This is something that's on my todo list. This is the actual syntax of that message:

    0xF0  SysEx (this is auytomatically included by WebMidi.js)
    
    0x7F  Realtime
    0x7F  Channel. Could be from 0x00 to 0x7F. Here we set it to "disregard channel".
    0x04  Sub-ID -- Device Control
    0x01  Sub-ID2 -- Master Volume
    0xLL  Bits 0 to 6 of a 14-bit volume
    0xMM  Bits 7 to 13 of a 14-bit volume
    
    0xF7  End of SysEx (this is auytomatically included by WebMidi.js)
    

    You can also send a control change message to whichever channel you want to assign the volume for. For example, here is how you can set the volume of channel 1 to 54:

    WebMidi.outputs[1].sendControlChange("volumecoarse", 54, 1);
    

    Hope this helps!

  • I come back to this discussion because I have related questions you may be able to help me with.

    1. If I understand correctly, playNote() without duration will only send a note on, and notes can’t overlap, so a note off will be automatically sent at the next note on. Is that right?
    2. If the assumption 1. is right, is it possible to know if a note is still playing (no note off triggered yet) at any moment ?
    3. Let’s say I want to stop a note at 50% of this original duration. Can I do that? Or am I forced to use an arbitrary duration because original note durations is something the library can’t retrieve?

    I hope my questions are intelligible, thank you in advance for your answers.

  • If I understand correctly, playNote() without duration will only send a note on, and notes can’t overlap, so a note off will be automatically sent at the next note on. Is that right?

    The playNote() method only sends a note on message if no duration is specified. If you send another note on for the same note, the device will stop the previously playing sound and start to play the new one. In this case, no note off message is ever sent.

    If the assumption 1. is right, is it possible to know if a note is still playing (no note off triggered yet) at any moment ?

    Currently, this is not possible. However, it is on my todo lit for v3 which I will start working on heavily in the next few weeks.

    Let’s say I want to stop a note at 50% of this original duration. Can I do that? Or am I forced to use an arbitrary duration because original note durations is something the library can’t retrieve?

    Hmm... that's a very interesting question. If I understand you correctly, the scenario is that you use playNote() with a duration but want to stop note playback before the duration has expired. This is a tricky scenario. You could call stopNote() early. This would stop the note but would not prevent the note off to be sent at the end of the duration. You would be better off simply playing the note without duration and stopping it when you need.

    I hope this is clear...

  • edited August 20

    If I understand you correctly, the scenario is that you use playNote() with a duration but want to stop note playback before the duration has expired. This is a tricky scenario. You could call stopNote() early. This would stop the note but would not prevent the note off to be sent at the end of the duration. You would be better off simply playing the note without duration and stopping it when you need.

    No for now I don’t have any duration attached to playnote() because I want it to last the longer it can. Ideally I would need a variable duration depending on my original instrument’s duration. Some extra details for you to understand:

    • I have ogg samples in my midi software
    • I trigger them with kind of a webmidi sequencer
    • In this sequencer, I would like to implement a control over the note duration. For instance, if a bell sound is played, this control would allow to let it sound or cut its tail of x%.

    Here is my code right now

              if (totalBorderRadius > 0) {
                let duration = interval - (interval * totalBorderRadius / 100)
                midiOutput.playNote(note, channel, {velocity, duration });
              }else {
                midiOutput.playNote(note, channel, {velocity})
              }
    

    The issue with the code above is that interval is currently the duration of my beat (depending of course of my bpm), so if the bell note’s duration is longer than one beat duration, it will be cut anyway after a beat time.

    The following video demo shows how a positive border radius will trigger the duration cut at minimum the length of a beat.


  • Can't you simply use setTimeout() to trigger stopNote() in the future depending on whatever criteria you need?