Cabbage Logo
Back to Cabbage Site

Delay Midi Notes

Hi,
I am creating a midi device and I have a problem that I can´t solve.
I would like to be able to control the delay of each note. For example, when I press a MIDI key that triggers a MIDI chord I want to control the delay of each note through a “Slider”. I have tried several things with “event / event_i” but it does not work correctly. I would also like that when the MIDI key is released, all the chord notes receive the “note off” and turn off or, in case the delayed note has not started, it will not be activated.
I do not know if I have explained myself correctly. I’m quite blocked And I would really appreciate some help :slight_smile:
Thanks!

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("midO")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1

instr 1

midion 1,p4, p5
midion 1,p4+4, p5
midion 1,p4+7, p5

endin

</CsInstruments>
<CsScore>
f0 z
</CsScore>
</CsoundSynthesizer>

Are you talking about a pause before the initial notes are heard? Also , are you simply trying to build a MIDI processor that you use between a controller and a synth?

This one is a little better, but there are still issues with notes not being turned off. Note that I insert this instrument between a controller and a synth. I’ll have a think about how best to disable the hung notes…

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("midO")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1


instr 1 ;Triggered by MIDI notes on channel 1

    ifund notnum
    ivel  veloc

    knote1 init ifund

   
    if metro(1) == 1 then
        midion 1, knote1,ivel
        knote1+=3
        
    endif    
endin

</CsInstruments>
<CsScore>
f0 z
</CsScore>
</CsoundSynthesizer>

Delaying a note by even the smallest of time seems to prevent it from being turned off when instrument 1 stops. Hmmm. I wonder does @iainmccurdy have any input on this?

"Are you talking about a pause before the initial notes are heard? "

I want to reproduce the notes of the chord in different times. Like a really short Arpeggio.

Also , are you simply trying to build a MIDI processor that you use between a controller and a synth?

yes

This one is a little better, but there are still issues with notes not being turned off. Note that I insert this instrument between a controller and a synth. I’ll have a think about how best to disable the hung notes.

Thanks! that´s a good point to keep trying it :slight_smile:

What do you think about something like this? is it possible? but working well…

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(0, 0, 0), pluginid("midO")
keyboard bounds(8, 158, 381, 95)

rslider bounds(30, 12, 87, 91) range(0, 1, 0, 1, 0.001) trackerinsideradius(0.66) text("Delay ONE"), channel("delayOne") 
rslider bounds(130, 12, 87, 91) range(0, 1, 0, 1, 0.001) trackerinsideradius(0.66) text("Delay TWO"), channel("delayTwo")  
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1




instr 1 ;Triggered by MIDI notes on channel 1
    
    
    iDelayTimeOne chnget "delayOne" // GET THE SALIDER VALUE AND CHANGE THE DELAY NOTE TIME (IT DOSENT WORK BUT SHOW THE IDEA)
    iDelayTimeTwo chnget "delayTwo"

    ifund notnum
    ivel  veloc
    
    knote1 init ifund
    
    /*kRel release
    if kRel==1 then
        // ALL NOTES OFF IS IT POSSIBLE?
    endif*/
    
    if metro(iDelayTimeOne) == 1 then
        midion 1, knote1,ivel
      
    endif 
    
    if metro(iDelayTimeTwo) == 1 then
        midion 1, knote1+7,ivel
      
    endif 
    
          
endin

</CsInstruments>
<CsScore>
f0 z

</CsScore>
</CsoundSynthesizer

THANKS A LOT @rorywalsh

We can of course disable all notes, but my concern is we will end up disabling some notes that might have been started with a different note. So if a user plays two C notes in succession, will the first one then disable the new notes in the second, if this makes sense?

It’s true … I don’t know how I did not see it…

This one will delay each note pressed, and kill the note once the main instrument have been stopped. It might be adapted to work with more complex scenarios?

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("midO")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1


instr 1 ;Triggered by MIDI notes on channel 1
    
    kLine = int(line(0, 1000, 1000))
    ifund notnum
    ivel  veloc
    irand = rnd(100)/100


    if changed(kLine) == 1 && kLine == 1 then
        event "i", 200+irand, 0, -1, ifund, ivel
    endif  

    kRel release
    if kRel == 1 && changed(kRel) == 1 then
        event "i", -1*(200+irand), 0, -1, ifund, ivel
    endif
    
endin

instr 200
    midion 1, p4, p5 
endin

</CsInstruments>
<CsScore>
f0 z
</CsScore>
</CsoundSynthesizer>

Or how about something like this:

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("midO")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1



instr 1 ;Triggered by MIDI notes on channel 1
    
    kNotes[] init 10
    kNoteCount init 0

    ifund notnum
    kTrans init 0
    ivel  veloc
    krand init 0


    if metro(1) = 1 then
        krand random 0, 1
        event "i", 200+krand, 0, -1, ifund+kTrans, ivel
        kTrans+=3
        kNotes[kNoteCount] = krand   
        kNoteCount += 1
        
             
    endif  

    kRel release
    kCnt = 0
    if kRel == 1 && changed(kRel) == 1 then
        while kCnt < kNoteCount do 
            event "i", -1*(200+kNotes[kCnt]), 0, -1, ifund, ivel
            kCnt+=1
        od
    endif
    
    
    
    
endin

instr 200
    midion 1, p4, p5 
endin

</CsInstruments>
<CsScore>
f0 z
</CsScore>
</CsoundSynthesizer>

Hey Rory, both works fine! but as noob…I don’t understand some thingss…

Forme it´s more clear the version with events (I suppose it is less elegant than the last one), I can construct my chords with multiple events. My question is, How can I control the delay by note? lets say the first note starts at 0 seconds, the seconth at 1 second…

Here´s the code with my dirty touch:

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("midO")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1


instr 1 ;Triggered by MIDI notes on channel 1
    
    kLine = int(line(0, 1000, 1000))
    ifund notnum
    ivel  veloc
    irand = rnd(100)/100
    
    if changed(kLine) == 1 && kLine == 1 then 
        event "i", 200+irand, 0, -1, ifund, ivel
        event "i", 210+irand, 0, -1, ifund, ivel
     
    endif  

    kRel release
    if kRel == 1 && changed(kRel) == 1 then
        event "i", -1*(200+irand), 0, -1, ifund, ivel
        event "i", -1*(210+irand), 0, -1, ifund, ivel
    endif
    
endin

instr 200
    midion 1, p4, p5 
endin

instr 210
    midion 1, p4+10, p5 
endin



</CsInstruments>
<CsScore>
f0 z
</CsScore>
</CsoundSynthesizer>

If you’re using events, the easiest thing to do is just to offset the start time of the events:

instr 1 ;Triggered by MIDI notes on channel 1
    
    ifund notnum
    ivel  veloc
    irand = rnd(100)/100
    
    iDelayTimeOne chnget "delayOne"
    iDelayTimeTwo chnget "delayTwo"


    event_i "i", 200+irand, iDelayTimeOne, -1, ifund, ivel
    event_i "i", 210+irand, iDelayTimeTwo, -1, ifund, ivel
    
    kRel release
    if kRel == 1 && changed(kRel) == 1 then
        event "i", -1*(200+irand), 0, -1, ifund, ivel
        event "i", -1*(210+irand), 0, -1, ifund, ivel
    endif
endin

p.s. there’s nothing wrong with your approach. Mine is a little more dynamic, but might not be necessary for what you want. The simpler you can keep things the easier it is to debug later!

We are very close to what I need, the problem with the last code (Offseting events) is, when I press a key by a super short time it reproduce all the events, and I need to cancel all the events if I send a note off. I was testing some things without success…any idea?
Thank you for your help Rory!

Try sending the same time offsets to this code:

Instead of 0 use the same delay times as in the earlier events. I’m thinking that during short notes, these are called before the instruments actually start.

I’m parachuting into this discussion somewhat, but do you think it might be a better strategy to trigger the MIDI notes at k-rate and use trigger delays? This way you don’t have to worry about MIDI notes being sent after that key has been lifted. Here is the modification of the note triggering code:

kTrig init 1
schedkwhen delayk(kTrig,iDelayTimeOne),0,0,200+irand, 0, -1, ifund, ivel
schedkwhen delayk(kTrig,iDelayTimeTwo),0,0,210+irand, 0, -1, ifund, ivel
kTrig = 0
1 Like

Pardon me, you were already doing this during k-time with the changed(kLine) construction.

Awesome! I implemented the triggering code of @iainmccurdy and finally it works perfect!

Thousands of thanks @rorywalsh and @iainmccurdy !

Great. And no more hung notes? I’m curious why it works OK with the schedule opcodes but not the event ones :thinking:

This seems to work similarly…

kTrig init 1    
if (delayk(kTrig,iDelayTimeOne)==1) then
 event "i", 200+irand, 0, -1, ifund, ivel
endif
if (delayk(kTrig,iDelayTimeTwo)==1) then
 event "i", 210+irand, 0, -1, ifund, ivel
endif
kTrig = 0

After testing it I found a new bug and I don´t know if is it possible to fix it. The problem is when I trigger notes that are too close in between them some of them stops. Can someone give me some light?
Heres a demo and the code:

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("midO")
keyboard bounds(8, 158, 381, 95)
rslider bounds(74, 28, 60, 60) range(0, 1, 0, 1, 0.001),channel("DelNoteOne")
rslider bounds(148, 28, 60, 60) range(0, 1, 0, 1, 0.001),channel("DelNoteTwo")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1

instr 1 ;Triggered by MIDI notes on channel 1
    
    ifund notnum
    ivel  veloc
    irand = rnd(100)/100
    
    iDelayTimeOne chnget "DelNoteOne"
    iDelayTimeTwo chnget "DelNoteTwo"
          
                
    kTrig init 1
    schedkwhen delayk(kTrig,iDelayTimeOne),0,0,200+irand, 0, -1, ifund, ivel
    schedkwhen delayk(kTrig,iDelayTimeTwo),0,0,210+irand, 0, -1, ifund, ivel
    kTrig = 0    
     
                   
    kRel release
    if kRel == 1 && changed(kRel) == 1 then
        event "i", -1*(200+irand), 0, -1, ifund, ivel
        event "i", -1*(210+irand), 0, -1, ifund, ivel
    endif
    
endin

instr 200
    midion 1, p4, p5 
endin

instr 210
    midion 1, p4+10, p5 
endin


</CsInstruments>
<CsScore>
f0 z
</CsScore>
</CsoundSynthesizer>

You may be encountering problems with interleaving note ons and offs. The guys who implemented serial MIDI in 1983 never envisioned the crazy stuff you are doing here! Maybe by adding a kind of MPE functionality you can avoid these problems. This means that you cycle through the 16 MIDI channels as you play sequences of notes. Here’s the code:

<Cabbage> bounds(0, 0, 0, 0)
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("midO")
keyboard bounds(8, 158, 381, 95)
rslider bounds(74, 28, 60, 60) range(0, 1, 0, 1, 0.001),channel("DelNoteOne")
rslider bounds(148, 28, 60, 60) range(0, 1, 0, 1, 0.001),channel("DelNoteTwo")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=NULL -M0 -Q0 --midi-key=4 --midi-velocity=5
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1

giChan = 1 ; initial MIDI channel

instr 1 ;Triggered by MIDI notes on channel 1
    
    ifund notnum
    ivel  veloc
    irand = rnd(100)/100
    
    iDelayTimeOne chnget "DelNoteOne"
    iDelayTimeTwo chnget "DelNoteTwo"
                
    kTrig init 1
    schedkwhen delayk(kTrig,iDelayTimeOne),0,0,200+irand, 0, -1, ifund, ivel, giChan
    schedkwhen delayk(kTrig,iDelayTimeTwo),0,0,210+irand, 0, -1, ifund, ivel, giChan
    kTrig = 0    
     
                   
    kRel release
    if kRel == 1 && changed(kRel) == 1 then
        event "i", -1*(200+irand), 0, -1, ifund, ivel, giChan
        event "i", -1*(210+irand), 0, -1, ifund, ivel, giChan
    endif
    
    giChan  wrap  giChan + 1, 1, 17 ; increment MIDI channel and wrap after channel 16
endin

instr 200                         
    midion p6, p4, p5 
endin

instr 210
    midion p6, p4+10, p5 
endin


</CsInstruments>
<CsScore>
f0 z
</CsScore>
</CsoundSynthesizer>

With the last code I´m having the same problem. But, After some tests i found that, if I delay the notes 0.008 all notes are triggered but the result in time is not perfect and for some reason all notes start before their time and no after…and I don´t know why…
I´m wondering if I can turn the device to monofonic (the instrument should recieve one midi note at time and trigger a chord and when I send the new midi note cancel the last) will help? but I don´t how I can do that…
Here I´m showing the problem: