OOLOI.ORG
Menu

OOLOI

An Organism Evolved.

OVERVIEW

DOCUMENTATION

NEWSLETTER

Taking Measure

3/10/2025

5 Comments

 
Picture
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 Structure

The hierarchy looked like this
Picture
​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 Structure

Picture
​Measures 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 Implementation

The 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 Planning

Here'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 Method

A 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 Matters

When 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
Peter Bengtson link
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
Peter Bengtson link
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!

I don’t understand why those ponytail guys won’t release your original source code to you. It’s probably just locked away and not being used, sad really.

Any way this was a good read and progress is happening.
Thanks Peter.

Peter Bengtson link
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.

But I do agree: it's a little jarring that the code is in the custody of people who really don't care. They didn't back then, and they sure don't now. ;)

Reply



Leave a Reply.

    Author

    Peter Bengtson –
    Cloud architect, Clojure advocate, concert organist, opera composer. Craft over commodity. Still windsurfing through parentheses.

    Search

    Archives

    February 2026
    January 2026
    December 2025
    November 2025
    October 2025
    September 2025
    August 2025
    July 2025
    June 2025
    April 2025
    March 2025
    September 2024
    August 2024
    July 2024

    Categories

    All
    Accidentals
    Alfred Korzybski
    Architecture
    Benchmarks
    Clojure
    CLOS
    Common Lisp
    DDD
    Death Of Igor Engraver
    Documentation
    Donald E Knuth
    Dorico
    Dynamic Programming
    Finale
    FrankenScore
    Franz Kafka
    Frontend
    Functional Programming
    Generative AI
    GRPC
    Igor Engraver
    Jacques Derrida
    JVM
    License
    LilyPond
    Lisp
    Localisation
    MIDI
    MPL 2.0
    MuseScore
    MusicXML
    Ooloi
    Ortography
    Pitches
    Playback
    Plugins
    Python
    QuickDraw GX
    Rendering
    Rhythm
    Rich Hickey
    Road Map
    Scheme
    Semiotics
    Sibelius
    Site
    Skia
    Sponsorship
    UI
    Umberto Eco
    Vertigo
    VST/AU
    Wednesday Addams

    RSS Feed

Home
​Overview
Documentation
About
Contact
Newsletter
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.


  • Home
  • Overview
    • Background and History
    • Project Goals
    • Introduction for Musicians
    • Introduction for Programmers
    • Technical Comparison
  • Documentation
  • About
  • Contact
  • Home
  • Overview
    • Background and History
    • Project Goals
    • Introduction for Musicians
    • Introduction for Programmers
    • Technical Comparison
  • Documentation
  • About
  • Contact