[Sharing] 3D piano roll visualizer

TFL's icon


Hi,
Answering this post, I found fun to try to create a 3D piano roll visualization of incoming MIDI notes.

How it works basically:
- The geomtry is based on a simple [jit.gl.gridshape @shape cube] attached to a [jit.gl.multiple]
- A [jit.matrix 3 float32 1000 1 @name notes] is made to store the note history. A history of 1000 works very well, it can go higher without issue.
- Using a [counter 0 999] to address each new note to the next matrix cell
- Using a [poly~] to keep track of held notes and editing the note and velocity value.
- Using jit.gen and a couple buffer matrices, each cube is scaled through the y axis as long as the corresponding note is held, and everything is translated through time.
- I struggled with the reset system due to the feedbacks. Had to create some kind of global gate that would send 0s everywere at once.

Have fun!

midi_roll_3D.zip
application/x-zip-compressed 5.81 KB

Falk Grieffenhagen's icon

Hey TFL,

This is amazing and just what I needed many thanks for this!!
I basically understand what you did, this approach is a good way to go with this, but very specific to Max.
@cycling:
To be honest sometimes I wish Max could me more like Processing in this respect. It would be cool to have like a straight forward poly possibility for jitter e.g.: You put a jit.gl object including features inside a sort of jit.gl.poly and instanciate it for a particle system for example. Here you did instances with a matrix inside poly, this is surely possible but you really need a deep understanding to go this way.

cheers Falk

TFL's icon

To be honest sometimes I wish Max could me more like Processing in this respect. It would be cool to have like a straight forward poly possibility for jitter e.g.: You put a jit.gl object including features inside a sort of jit.gl.poly and instanciate it for a particle system for example

I think you are mixing up various concepts.
- For a particle system, just use a jit.matrix to store your particles (one cell = one particle) and use it to feed a [jit.g.mesh @draw_mode points] to draw each vertice as a point. You can also use [jit.gl.buffer] and [jit.gl.tf] to efficiently perform transform feedbacks on your particle system.

I feel like the jit.gl.poly you are describing is actually what [jit.gl.multiple] does: dynamically creating multiple instances of a [jit.gl.mesh] or [jit.gl.gridshape]. I am using it in the patch as each note is basically drawn from the same cube geometry, but scaled and positionned differently. The thing is, you also need a way to "record" every incoming note and modify the notes matrix accordingly. Given that you could receive a held note while simultaneously receiving shorter notes (because polyphony), it's not that trivial to keep track of which data to change and where. And for that purpose [poly~] is very helpful. I could have done it differently but that would have consisted of basically recreating my own poly~.

You could also not use [jit.gl.multiple] at all but only [poly~], and put the [jit.gl.gridshape @shape cube] inside of the [poly~] patcher. Each voice would be attributed to a unique note, and once you reach the poly~ limitation of 1023 voices, you start over from first voice. This solution might be less convoluted than mine, but you're limited to an history of a total of 1023 notes, and I have some reserve about its efficiency, as I assume having 1023 instances of a [jit.gl.gridshape] is more costly than using only one with a [jit.g.multiple].

Max Doe's icon

Very nice. I‘d love to use this for setup with my dual manual Hammond which outputs 61 notes from the upper manual on midi channel 1 and 61 notes from the lower manual on midi ch 2.

any suggestions ? 🤔