Thanks to Jules and Dave, and other Tracktion Engine team members, I have a DAW that is approaching fully functional! There is no way I could have come this far without Tracktion Engine. Thank you!
My last big task to make my DAW usable is to create the piano roll midi editor. I am looking for pointers on what is the best approach to take? A rudimentary example would be great, of course, but any suggestions are most welcome.
My first thought is to implement the midi notes from the Component class. But i seem to remember Jules cautioning not to do that (several years ago), but I can’t find that post.
So, what would be the best approach for creating a piano roll midi editor?
I Waveform we don’t have Components for for each midi note. You can have 1000s of notes, I think it would be overkill.
In your midi editor paint function, we just loop over the notes and draw the ones on screen. I think it’s safer this way, you don’t have Components and events getting out of sync. You can make some functions that convert between x/y and time/note and operate on the midi events directly.
I’d also advise against a viewport, instead just keep an x offset and a y offset yourself and use that in your conversion functions.
I have a piano roll in another app that I want top bring to tracktion as well, the item renderers(notes) are virtual(JIT/Pool components ) just as the grid lines, measure bar etc all pooled.
I have not done anything like this in Juce but I use a Main component “Pane” that holds a Keyboard and Container, top and bottom margin for beat bar, scroll bar etc. This Container is a scroll container that holds the notes. I calculate the total dimensions of the “clip length” and put a fake content graphic into the back ground to control the scroll. this allows perfect scroll calculations while still maintaining the virtual note layout.
The dataProvider is a list arrays holding PianoRollListData items that wrap a NoteData event. So in render, I loop through each row of the DP, that row has the pitch list of notes found and I have some funky math for time signatures, pattern lengths, resolution etc to calculate true locations.
I also have a notion of “painter” that paints the chrome(gird lines, row lines, measure bg alternations etc.), watch out for zindex swapping performance as well.
I can almost keep my UI at 50-60 FPS on mobile Android doing this but I also use the GPU and texture batches instead of vectors.
Sorry that was a brain dump, maybe you will find something useful! out of it.
I am thinking I will end up with something somewhat in-between the suggestions. I envision a midi note class that will ride over my grid/bars/beats (already established). I will need to be able to place notes, change length, move, and delete, to obtain basic functionality. I can add copy/paste and paste repeat functions later.
Another idea that I thought of is to derive my own class from MidiKeyboardComponent with extensions to do the note grid and editing of the notes.
All my work is in a different language so some of my ideas may not translate over to C++/JUCE well.
The way I solved this problem in my piano roll was I have a Touch, Selection, Audition, Pan strategies. Basically states but not really because they are based on actions that mutate UI thing. (disable touch down note creation, enable scroll for pan etc.) Strategies worked well for this…
Then, the really important part was to create a proxy selection, that held the original notes in a selection. This abstraction allows me to tie more metadata/properties to the real selection.
Long story short, creating this next level allowed me to use a really efficient batch undo/redo CRUD impl of note management with selection etc.
Pretty much what I am describing, JUCE’s Value trees solve but I have not actually experimented in how they would solve what I do in a batch undo manager. I worked years on this design, mainly as thought exercises to solve problems when I actually used a professional audio platform (Android isn’t right now ).
I do like the fact I solved these problems without a large undo framework underneath me, I learned things I never would have because it forced me to understand the whole problem.
I’m going to make some type of OS thing for Android and Tracktion in the next while.
Have fun man, this type of stuff is what keeps getting me up in the morning, I truly love UI and complex performance problems. A piano roll will test you for sure and it can always be faster.
TLDR; It’s a lot harder to do CRUD stuff when you are not literally copying memory blocks like you do in C++ of data.
BTW, this whole app you see int he SS uses OSC messages for read/write. I had to create a bridge API across from C++. So all that data is not a local memory data model, it could be over a network or something. So much work.