Delay a midi note by its length

unuftu's icon

The idea is; if I can delay a note by its length, then I can know its length by the time it arrives. I realize that borax outputs note length, but in this case I will require it at the beginning of the note for articulation switching.

Objects I've been attempting to combine include pipe, delay, bucket, onebang, zl.change, zl.queue, if, timer, clocker, qlim.

Wil's icon

Like this?

Max Patch
Copy patch and select New From Clipboard in Max.

Roman Thilenius's icon

depends where it is coming from.

if it is coming from within max, in an ideal case from detonate, you could read out the note on / note off data in NRT/uzi style and "convert" the loaded midi file into a detonate format file, which now includes the duration values as controller X value right before the note ons.

in realtime - aka the opposite of makenote - it is impossible. :)

Source Audio's icon

if I can delay a note by its length, then I can know its length by the time it arrives.

no, you can't, unless you can see in the future.
note arrival - if you mean the moment note ON arrives.
At that moment you can use pitch and velocity for your articulation, but length will be known only after note OFF arrives.
Only if you read midi file or seq , you can read and collect all infos about notes and their length in advance, before starting playback.
In case of real time midi input, and in case that you want to output note AFTER note OFF arrived using measured note length, you need a coll or something similar in addition to borax to combine pitch, velocity and length.

example input :
note 48 120 ..... 48 0 1000ms later.
At the point when 48 0 arrives you send list 48 120 1000 to makenote.
makenote object would be handy because you can pass pitch, velocity and length to it.

If what I describe is what you need, it should be no big deal to combine couple of objects to do so.

unuftu's icon

Thank you everyone for the replies.

RE: SOURCE "output note AFTER note OFF arrived using measured note length"

This has been the general direction of my efforts so far, but all of my attempts have been missing "something" and generally end up convoluted.

My suspicion is that at least one of my current points of failure has something to do with with storing the midi note(s) until the off message (0) comes around.

Come to think of it, part of the problem likely has to do with the scenario that if the next note in the sequence is SHORTER than the one just fired (by its own off message), then the next off message (of the shorter note) will cut off the currently currently playing note.

With that in mind, I could see how midi format might bypass this problem by using the duration inlet (from borax duration out).

. . . which leads me back to the issue of storing notes. I am familiar with coll, but I don't have much experience with updating it dynamically.

I will do some experimenting and report back.

Roman Thilenius's icon

>> if the next note in the sequence is SHORTER

yes, exactly, otherwise you might just use a bigger window such as 5 seconds and do the rest by waiting and recombining things per key.

recording midi events is quite possible with coll, but detonate would do the delta time stuff for you automatically, plus has tracks and channels.

unuftu's icon

>> bigger window such as 5 seconds and do the rest by waiting and recombining things per key.

I have also come to this conclusion. For now I'm trying to avoid the recombination as it would still likely involve some sort of note queue (the thing that I'm currently not doing right). I've had my eye on detonate as a performance capture feature anyways so maybe. . . two birds with one stone.

But in the spirit of contingency, I welcome any thoughts on recombination. Specifically in the context regarding a queue of some type.

Roman Thilenius's icon

this process would be very similar to keeping track about running notes for othgehr purposes, and there should be some example patchers in the forums or even in the examples.

i would probably use a coll with fixed index names from 0 to 127 and write 0 for free, 1 for note on, and X for note off behind them.

or why not use [pak] with 128 inlets?

then you have split the incoming stuff per key adn wrote a busymap into a list like this:

0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 1 0

the conversion itself simply means to measure the time between velocity nonzero and velocity zero. [timer] is the object you are looking for.

the most difficult part is to add a proper start/stop mechanism around it.

Source Audio's icon

You don't need to worry about retriggering of allready held notes when using makenote.
It has repeatmodes which can be adjusted to your likes.
check the help file for that.

here is one option with coll.

Max Patch
Copy patch and select New From Clipboard in Max.

cellblock is added just to monitor what happens, you can remove it.
if you want to keep notes in coll (not remove them after note off)
remove underlined part of this message.
merge $1 $2, $1, remove $1
next note on will replace it anyway.

unuftu's icon

SOURCE, I was experimenting with your makenote solution, but something is still missing.

I took a screenshot so I may offer you my interpretation before I end up deleting/starting from scratch. It's thrown together but allow me to offer an explanation.

note* I am using midiformat just before the unpack, but in the context of this screenshot/experiment I am staring with unpack. Also, I'm handling velocity performance elsewhere in the program so in this case it is simply a 1 or 0 (note on/off).

From the unpack, I'm sending velocity to the if statement. If it is equal to 0 (note off) then it sends a 1 (note on). This will simultaneously fire a note on message and last stored note in the message box below (40*). The message box to the right of the if statement is the duration from borax, sent at the note off . At this point, all three integers should be hitting the makenote at the same time. Makenote does its magic, while also sending the channel number (2*) to midiformat.

To be clear, this does not work.

If you can offer any insights or interpretations, I would greatly appreciate it.

Source Audio's icon

ha,ha our messages were probably travelling synchronised,
mine was by chance triggered first, same as what happens in max with collection & execution order.
Take your time and have a look at the patch I posted.
I am now away for some time, but feel free to post any questions , I'll be back later.
Or Roman might jump in to help.

unuftu's icon

So after careful consideration of the suggestions in this thread, and subsequent testing; I found my solution in zl.queue. I had tried to make this object work several times before (by unpacking, packing fully formatted midi messages) but never came to a stable output.

Ultimately, I ended up going further back in my program and handling it in my main subpatcher (before the message is formatted (int)). I was trying to avoid this in the hopes of keeping the delay feature modular, but the result is negligible and can be reconciled on the back end (recording).

Thanks again everyone for the patchers/explanations.

unuftu's icon

For a moment my solution seemed to work, but that is not the case.

I'm thinking the only real solution is to create an arbitrary time buffer (5000 ms) and recombine the length with the associated note (as Roman mentioned).

While testing, I started to see that even if the queued note has an associated length (makenote), that still wont solve the issue that if the next note is longer, there won't be enough time to measure it before the first note ends (when triggering from note off (0)).

Just in case it makes any difference, I will mention a few particulars about the note behavior of my program:

1. Every note played is different than the one preceding it.

2. Note duration is dictated by the appearance of a new note (zl.change to Borax Reset (third inlet))

49 1 . . . . . . . . (247ms) . . . . . . . . 49 0 , 62 1 . . . . . .

Ideally, the length of arbitrary time buffer solution would be the shortest time needed (which is presumably the length of the two longest notes when consecutive (e.g. 780 + 1220 = 2000ms time buff))

Peak is just there to jankily automate the delay length. Also, I think I read somewhere on the forums to avoid the to/fromsymbol for real time use?

Pipe successfully delays the note. Next comes the issue of associating a note with its length (at output).

Roman, thank you for your thoughts on this matter in your previous reply.

And thank you Source for the patch. It is enlightening to see someone use coll in an optimized way.

Source Audio's icon

If you want to accumulate lengths of same pitched notes
and send zero only after last delayed-released note turns off,
then you could use poly~ for each note and place makenote in
each instance set to repeatmode 2.
I inserted > 0 after borax velocity output, you stated only 0 - 1 neeed.
You can remove it to route real velocity.

POLYNOTES.zip
application/zip 2.56 KB


Source Audio's icon

reading this again closely :

means you have no overlapping notes at all,
I did not get that, instead I expected poly notes each having own measured total length from last note on till last note off.

That monophonic note lasts as long as it lasts unless it gets turned off by ANY next input note, right ?
Makes everything much simpler.

unuftu's icon

I should have specified that the "particular note behavior of my program" is the current/intended note behavior (pre delay).

such that:


*there is only one note ever playing at a time
*the next note is always different than the one just played
*the length of any given note can and does change frequently
*a new note triggers the off message of the previous note

These criteria satisfy the output.

In the context of my patch, when monitoring the output of your solution via message box, I noticed the note off seems to fire somewhere in the middle of the note on's. A successful note off (given the criteria above) would NOT be perceivable in a message box because it happens too fast (directly before the next note on). You would only ever be able to see it "0" in the console .



Source Audio's icon

you need to measure note length only if notes are not overlapping.
For single end to end note, or that last uninterrupted note.
All overlapping notes anyway turn off (in your case play) previous ones.

can be done using just few objects.


unuftu's icon

I'm skeptic to say I've solved it, but I will say that my current solution is within tolerance and working 97% as expected. Occasionally, there seems to be outlier notes that miss the articulation, but they are rare and are usually associated with longer durations.

I am also noticing artifacts taking place in the 0 to 20 ms range. My thought is that this has to do with the order/scheduling of messages e.g. articulation message hitting the vst right after the the note, or vice versa.

I don't have much experience with the defer set of objects. Is this is the type of scenario they are meant to solve?

Currently testing/reading the reference.

Join, swap, and route are sending the note off from the first borax as a note on to the second borax. zl.change terminates the current note when a new note is seen.

Source Audio's icon

To be honest, I still don't understand you rules 100 %,
because from all infos you posted, one can take this or that
.
Also that screenshots are not enough to help about it.
Post a complete patch and also clear rules,

like what if note ends before you play next one.

Should repetiton of same note be prohibited,

what when chords come in at input,

what when a note is held, and another short one starts and ends
before first one gets released ?
And so on.

-----


I would avoid any defer objects in midi stream.
Usually best approach is taking care of timing of

collecting data and sending messages after processing.