Storing midi notes

Hello everyone,

I am just revisiting an old patch and wanted to simplify a problem that has eluded me for some time now.

What I want to do is record playing midi notes while they are coming in live and have several spreads with information like note, velocity, length, etc. I have a working version, which is unfortunately terribly ineffective, so I am trying to build a more efficient version of that part.

The new version is working pretty well, but I just cant figure out one of the remaining bugs, which is that sometimes when several notes are playing close to each other the length of the spread decreases again by 1 or 2, which shouldnā€™t happen, as notes are either in the buffer or stored.

Does someone know of a better way in vvvv or can spot the error in my patch? I have done something similar in Max, where you just have a super simple node that does the hard part of keeping track which notes are still playing so you dont need some sort of buffer, but not sure if something like that exists for vvvv (maybe a plugin).

Here is the new version:

saving_midi_notes.v4p (26.4 KB)

maybe the midi nodes in vvvv audio can help youā€¦ there is also a module that only gives you the currently played notes that might be more reliable than your framedelay approach.

ah, the module is here: https://vvvv.org/documentation/tonfilm-modules

Hi tonfilm,

Thanks so much for that, I will check it out now. The ā€œproblemā€ is that I need to store the notes as soon as they are not playing any more. For that to work I need a trigger to insert into the store node, which fires when a note ends. But you can only really detect it when it switches from on to off, at which point the data is already gone and has nothing to store. Therefore I dont see a way around delaying the data by 1 frame so it can be stored during the same frame that the switch from on to off is detected.

I will still try it a slightly different way using your method for the first part.

I think whats actually causing the notes to disappear is when notes disappear in consecutive frames. When I check which of the notes in the buffer have just stopped playing I get their index and use that to store. But because actual data and indexes are effectively offset by 1 frame, it can sometimes happen that the index in question doesnā€™t exist any more. It must be something like that, but I always find it so hard to follow whats happening as soon as frame delay is coming into play. One pretty cool was is to use mainloop and temporarily set vvvv to run at 0.5 fps. Then you can literally follow the data around. But even then sometimes things are just not really clear.

Anyways, thanks so much and I will report back with any progress.

ah, if your only interested in midi note off, you could use MidiShort and react only to note off messages. see this table for the bytes: https://www.midi.org/specifications/item/table-2-expanded-messages-list-status-bytes

note that some devices send note on with velocity 0 as a note off message.

of course, that is a good idea. The fact still remains though, that as soon as you get the note off, then you somehow need to link it to the data of current note and store it in the same frame, otherwise the data is gone.

The way around this currently is by not having a buffer and use the set input in the store node to set the data as long as the note is being played. That way you dont need to worry about the note off. But then you have the problem, that one store node cannot listen to multiple notes at the same time, as keeping track of which index to set and where to insert becomes (at least for my understanding) completely impossible. Thats why for example in Max/Msp there is a special component just tasked with keeping track of which notes are still playing.

Right now I basically have 1 store node for each note in the midi spectrum, meaning 128 store nodes basically. That of course is quite a bit of overhead to deal with having 128 instances of 1 small patch, especially since I would like to be able to record not just on one, but all 16 channels.

So I think what I will do is to switch from having one store node for one midi note to having a bunch of store nodes which can listen to any simultaneous midi notes. Say if you have 16 of those that should cover 99% of cases, as very rarely 16 notes are played simultaneously even over several channels.

Thanks for your valuable input.

sounds like a job for VL. a dictionary will come in handy so you donā€™t have to worry about indices at all.

but i have not understood in what form you want to store the notes and what their porpose will be. do you need to play them back later? of for what purpose do you need to store them?

gave it a try in VL and it worked out quite nicely. but you were totally right, you need some sort of frame delay to remember the last played notes (here the ā€˜Last Notesā€™ property) and add them if the current velocity is lower than the one from the last frame. in VL it is also easy to store the velocity along with the note number. time stamp and midi channel would also be an easy extension.

Midi Recorder.zip (9.0 KB)

Hi tonfilm.

That looks pretty cool and I will try and understand the VL part a bit better.

So I am not just recording note and velocity but a few other things as well - I just didnā€™t show them in the original post for clarity. Like you say, some other things would be nice to be added, most importantly the timestamp and a running counter for note length - ie. the length value for note keeps running till the note stops playing.

Right now I am doing it the following way. On the right is the module for 1 particular note, there is 128 of them. Data gets zipped to contain 7 values: note, count (not so important), velocity, time, length, pitchbend and modulation. The last 3 are sampled continously till the note finishes playing. After putting all the data together its sorted by timestamp and then goes into the rest of the patch.

So what is still missing in my current setup is that I dont want to just listen to 1 channel, but to all 16 channels of one port and then have the channel as another ā€œcolumnā€ in the list.

Could you show me how to record more data like time or sample the pitchbend for each note while it is playing using VL?

I need the midi notes in this format, as they get mapped to 3D transformations. The whole thing is a 3D midi visualiser, which you can play and controll in real-time. Itā€™s pretty cool and has been a loong time in the making. Iā€™ll post a video of it soon, when we show it for the first time at the end of January.

Yes, this is true if you are looking at several notes at once or are using some sort of buffer. If you look at just a stream of single notes like I did with the 128 modules, then you dont need that, since 1 specific note can always just be either playing or not. That way you can just insert on note on and set while its playing, because it will definetly have a note off before the next note on. That way you dont need a frame delay. Maybe something like that can be done with VL as well.

Getting closer now with a version that uses a buffer and then distributes the notes for recording. The problem is that the buffer is changing in length while notes are still playing, but any single note needs to hold its position in the buffer for as long as its playing. So if I play 2 notes and hold a 3rd note, that note is now at index 2 in the buffer. But as soon as I let go of the first 2 notes and keep holding the 3rd, that third note is now at index 0 in the buffer, but should remain at index 2 till I let go of it.

Right now this part of the patch (recording the midi events) is what is using most performance - using the timing debug mode - even more then all the rest of the patch combined, which is gigantic. So optimising this part would be really great!

wow, okokā€¦ copying a subpatch 128 times is of course total overkill. it solves the problem of data relation in a brute force way that costs a lot of CPU power.

normally when handling midi notes, there is no such thing as length for midi notes. there is just on and off events. same for all other types of midi data.

to record multiple kinds of midi data, it is recommended to record the basic midi events, which is just two bytes. and then parse the recorded events later.

so a modified version of the VL patch after a MidiShort node would do the job not only efficient, but more important in a human understandable way.

but i see why you need the data in the kind of format you are suggesting. but that too is not so complicated in VL. iā€™ll give it a tryā€¦

Yeah, I was always aware that its not the most efficient way, but it was one that I was capable of doing (at least at the time - that part was written like 4 years ago). I tried again a few times, but without that crucial node that keeps track of which note is playing and where in the spread it is, its kind of difficult. It worked surprisingly well though (because Computers are crazy powerful, really) - even the craziest of midi files would crash the midi driver before it skipped recording a note :)

Of course interpreting midi straight away would be much more effective, but again its just something that isnt super easy to do (for me). I need the length that is running while the note is playing, by sampling some sort of master clock or simply calculating the offset between note on and current time.

If you could help me put something together that would be absolutely fantastic! I will buy you a beer (or other beverage) at NODE, if you are there. Iā€™ll be there for the first time this year and can show you what we are working on - I think you wont be disappointed. Iā€™ll post a little teaser video soon.

Yeah that whole VL thing sounds really interesting and Iā€™d love to learn more about it. It seems to me very similar to what they introduced recently in Fabric Engine, where you can make your own ā€œblocksā€ - basically the inside of a for loop that then works automagically with large datasets!? Exciting stuff!

itā€™s a very good example for VL. have a look insideā€¦ i made some extensions to record the midi notes with length and the modulation/pitch data with it per note. basically the patch inside the ForEach loop is that what you copied 128 times.
it is also spreaded 16 times for each midi channelā€¦

Midi Recorder.zip (24.8 KB)

2 Likes

Wow, tonfilm, youā€™re a legend!

Thanks so much, its almost perfect now. Just two very small details.

In the Store Midi Notes VL part you plugged in Modulation and Pitchbend the wrong way around so they were mixed up. I swapped them around and now its working as expected.

Would it be possible to use my own source clock. Right now you are just using errm, not sure exactly, time since opening the patch!? I have my own timer which can be reset and will eventually come from a midi clock, so it would be cool if it could use that as an input. I know I can also just calculate WHEN the reset was pressed and subtract it, but when I switch to midi clock it might be in a different time unit.

The timing is looking pretty good compared to the old one.

New using VL and recording all 16 midi channels:

Old one recording just 1 midi channel:

I was tempted to make another solution in c# to learn a little bit more about dynamic plugins. The Plugin uses 2 different ā€œBuffersā€ - the first for every Midi Note which is ā€œonā€, the second for every Midi Note which is in the first buffer and gets a ā€œoffā€ signal. I could only test it on a Maschine MK2 and an APCmini - so far it worked. Pitchbend etc. is missing, but maybe you want to take a look.

midiLogger.zip (6.3 KB)

no problem, was not too complicated. also hearing that it is almost 50 times faster in VL is nice.

to use your own time values, make an input for the time and route it into the NoteRecordingManager and connect it to the time input of the Create for the new MidiNoteData.

since the time input is not connected, the VL runtime will set the current frame time as default value for the Time type. thatā€™s indeed a bit magic, but also quite handy sometimes.

@tonfilm

Nice one! Kind of fun to play in the new VL environment. Quite different to vvvv, but nice. I had to change all the input types to float32, but figured it out. Now its pretty much perfect!

Next challenge will be to correctly implement the sustain paddle, so that all notes that are playing or start playing while the sustain paddle is pressed keep playing regardless of note off until the sustain paddle is released. But maybe save that one for later, there is still enough on the todo list as it is.

@jjh

Thanks so much, for your solution as well. Always better to have several approaches! :D

glad to hear that it works.

this can be done by preventing the note down edge while sustain is on an triggering it when sustain switches offā€¦ couple of AND and OR probablyā€¦

MidiBend channel spreading is fixed in latest alphas.