|
I've been thinking about how to actually render music notation in Ooloi – not the data structures or the algorithms, but the visual side. Putting notes on a page. It turns out that trying to draw something has a way of exposing assumptions you didn't know you'd made. When you're formatting a measure, you need to see everything that's happening simultaneously in that slice of time. All the voices at once, so you can work out spacing, avoid collisions, align things vertically. It's how a conductor thinks about a score: bar 5 across all the instruments, not following each instrument's line from beginning to end. Here's the difficulty: in our current model, voices own measures. To get 'everything in measure 5', I have to find all the voices in a staff, look inside each one, find measure 5 in each voice, and collect them all together. I'm reconstructing what the printed page shows me directly: measure 5 contains these voices. The Original StructureThe hierarchy looked like this This seemed sensible at first. We were thinking about voices as independent melodic lines that happen to align at measure boundaries. We even structured it this way to handle ossia staves (those little alternative passages that appear for a few measures then disappear). But that's not how notation actually works. Measures are the organising unit. Voices are just 'simultaneous material in this measure'. The Revised StructureMeasures contain voices. Problem solved: 'give me everything in measure 5' is just a direct lookup. All the voices are right there. And ossia staves? Actually clearer in this model. A staff just enters at measure 17 and exits at measure 20. Clean and explicit. The ImplementationThe conceptual change is straightforward: flip the hierarchy so measures contain voices instead of voices containing measures. The implementation touches about 40 files because this hierarchy is everywhere – it's the spine of the system. Record definitions, accessor methods, specs, generators, thousands of tests, documentation. Everything builds on this assumption. But here's the thing: it's still intellectually manageable despite being architecturally pervasive. We're not adding complexity or clever tricks. We're just making the model honest. Every place that changes is a place where the code now says something true instead of something awkward. The timewalker (the thing that walks through a piece in temporal order) becomes significantly more efficient too. Instead of recursing through voices to find measures, it just walks the measures directly. We're expecting 15–35% fewer allocations – fewer temporary objects being created and discarded, which means it runs faster with less memory overhead. Discovery, Not PlanningHere's what's interesting: I didn't know this change was necessary until I started thinking about rendering. The original model wasn't obviously wrong. It worked fine for everything we'd built so far. Only when I sat down to work out how to actually draw the notation did the mismatch become clear. This is the value of working without artificial deadlines. If I'd been rushing to ship something, pressured to show 'progress' to investors or whoever, I'd have built the formatting layer on top of the awkward structure. It would have worked, sort of. With enough wrapper functions and helper methods, you can make anything work. But that's exactly how technical debt accumulates. You build on a foundation that's slightly crooked because you don't have time to straighten it. Then you build on top of that. Eventually the whole thing is held together with duct tape and hope rather than sound engineering. Instead, because I'm approaching this from a computer science angle rather than chasing some market opportunity, I can pause when something feels wrong and ask: is the model lying to me? Should I fix this now, while it's still straightforward, before I've built a cathedral on quicksand? The answer was yes. So we fix it. The architecture stays clean. No debt accumulates. When we get to rendering, the code will be straightforward because the structure matches what we're trying to do. Transparency as MethodA commercial project wouldn't announce an architectural change like this. It would be seen as weakness – proof that the team didn't plan properly, didn't understand the domain, had to change course because they got it wrong. But this isn't a commercial project. It's research. It's domain adaptation. In research, discovering that your model needs adjustment isn't failure – it's progress. You learn something about the domain you didn't know before. You refine your understanding. You make the system better. So I'm documenting it openly. Not because I have to, but because it's interesting. Because someone else working on music software might hit the same realisation. Because transparency about process (including the adjustments and course corrections) is more valuable than maintaining some illusion of perfect foresight. In commercial software, there's pressure to appear confident and consistent: it's part of the corporate theatre. In research software, there's freedom to be honest about what you're learning as you go. Why This MattersWhen your data structure fights the domain, everything becomes harder. Performance tricks, validation logic, traversal algorithms – they all work around the mismatch instead of with the model.
When the structure matches reality, everything becomes easier. The formatting code that sparked this whole thing? It'll be trivial now. Just iterate measures, render their voices. Done. This is one of those changes that seems obvious in hindsight but not when you're in the middle of building. You pick a structure that appears reasonable, you build on it, and only later when you try to use it do you realise the foundation is slightly crooked. The question is whether you have the time and space to fix it, and whether you have the honesty to admit it needs fixing. I'm fortunate. I have the time. This project doesn't require me to pretend otherwise. And if the page keeps telling me the model is lying, I'll keep listening.
5 Comments
Magnus Johansson
3/10/2025 11:18:56
Thank you, Peter, for this very interesting article. When you write "In research, discovering that your model needs adjustment isn't failure – it's progress. You learn something about the domain you didn't know before. You refine your understanding. You make the system better." it reminds me of what legendary table tennis player Jan-Ove Waldner once said when he was asked about how he could become so good at his sport: he said that he, to a lesser degree than other players, was afraid of making mistakes, and that this actually helped him in his research to improve his game. Jan-Ove turns 60 today, 3 of October 2025, by the way.
Reply
3/10/2025 12:03:49
Spot on! And in Ooloi's case, there are many thousands of tests that serve to reduce exactly that fear: with the tests guaranteeing correctness in the end, a major restructuring such as this is straightforward.
Reply
3/10/2025 12:06:45
And of course - Igor Engraver had the right structure. But as "guys in ponytails" have the Igor Engraver source code, I had to rediscover the correct nesting order again, on my own... ;)
Reply
Scott Lannie
4/10/2025 13:13:13
The analogy that springs to mind is that of making a cake. The sponge is heavy and soggy but it has a lovely layer of icing! 4/10/2025 14:35:32
Scott, to be quite honest I don't think the source of Igor Engraver would be of any use to me now. Clojure is very different from Common Lisp. The only possible use it could have to me is to verify some of the engraving decisions we took, to see if they're of value in constructing the rule engine of Ooloi.
Reply
Leave a Reply. |
AuthorPeter Bengtson – SearchArchives
February 2026
Categories
All
|
|
|
Ooloi is an open-source desktop music notation system for musicians who need stable, precise engraving and the freedom to notate complex music without workarounds. Scores and parts are handled consistently, remain responsive at scale, and support collaborative work without semantic compromise. They are not tied to proprietary formats or licensing.
Ooloi is currently under development. No release date has been announced.
|
RSS Feed