Skip to content

halp: one quantification grid, walked once, matching libossia exactly - #185

Open
jcelerier wants to merge 1 commit into
mainfrom
fix/quantification-backwards
Open

halp: one quantification grid, walked once, matching libossia exactly#185
jcelerier wants to merge 1 commit into
mainfrom
fix/quantification-backwards

Conversation

@jcelerier

@jcelerier jcelerier commented Jul 31, 2026

Copy link
Copy Markdown
Member

tick_musical reported no quantification date at all when the timeline ran backwards, and its sub-bar grid ran from the start of the tick rather than from the bar line — so a bar whose length is not a whole number of divisions (7/8 against a half-note grid) carried a stale phase into the next one.

What changed

One grid, walked once. Bar lines come from the signature and from the bar positions the host reports; the grid restarts at each bar line; the walk runs in tick order in both directions — [start; end[ forwards, ]end; start] rewinding — so a point on the tick's far end belongs to the next tick and fires exactly once.

metronome() and get_quantification_date() now share that walk and one map from a musical position to a frame, so a click and a quantized event on the same bar line land on the same sample.

Parity with libossia is now asserted, not assumed

ossia::token_request and halp::tick_musical were documented as behaviourally identical and nothing compared them. They were not identical: ossia truncated the musical position to a whole-flick date and its consumers floored that into a frame — two roundings — while halp floors the position directly. On ~2e-5 of points the two landed one frame apart.

ossia/score#2163 adds QuantificationParityTest, which drives both implementations from the token streams time_interval actually emits. Over five signatures, eight rates, four speeds and two buffer lengths they now agree exactly on the count, the index and the frame of every point — 1,529,184 assertions. The fix for the double rounding is in libossia (ossia/libossia#917); this branch is what it had to match.

Companion to ossia/libossia#917 and ossia/score#2163.

@jcelerier

Copy link
Copy Markdown
Member Author

Completed the consumer check for the last two objects, using libremidi and libossia from score's source tree.

consumer how result
Patternal.hpp avendish flags, -fsyntax-only clean
Melodial.hpp avendish flags clean
TimingSplitter.hpp avendish flags clean
AudioParticles score's own avnd_audio_particles_avnd.cpp TU, score's real flags 0 errors, identical to baseline
Arpeggiator.hpp avendish flags + libremidi see below

For AudioParticles I compiled score's actual generated TU with score's own defines/includes/flags, substituting this branch's include/ for the submodule's and dropping the PCH. Zero errors, same as baseline — so the real integration path is unaffected.

examples/Midi/Arpeggiator.hpp is already broken on main

It gets past halp cleanly, then fails on its own code:

Arpeggiator.hpp:213:22: error: no member named 'note_off' in 'libremidi::message';
                        did you mean 'libremidi::channel_events::note_off'?
Arpeggiator.hpp:237:22: error: no member named 'note_off' in 'libremidi::message'
Arpeggiator.hpp:251:20: error: no member named 'note_on' in 'libremidi::message'

The note_on / note_off factories moved to libremidi::channel_events. Running the identical check against unpatched main produces the exact same three errors, so this branch is neutral for it.

It also relies on the including TU to provide halp/meta.hpp, halp/controls.hpp and ossia/detail/{small_vector,flat_map}.hpp — it includes none of them itself. That is consistent with it not being registered in CMakeLists.txt: nothing builds it, so the bit-rot went unnoticed.

Happy to send a separate PR that updates the three call sites, adds the missing includes and registers it somewhere so it stays compiling — it felt out of scope to fold an unrelated libremidi API update into this one.

tick_musical reported no quantification date at all when the timeline ran
backwards, and its sub-bar grid ran from the start of the tick rather than
from the bar line, so a bar whose length is not a whole number of divisions
carried a stale phase into the next one.

Walk one grid instead: bar lines come from the signature and from the bar
positions the host reports, the grid restarts at each of them, and the walk
runs in tick order in both directions - [start; end[ forwards, ]end; start]
rewinding - so a point on the tick's far end belongs to the next tick and
fires exactly once.

metronome() and get_quantification_date() now share that walk and one map
from a musical position to a frame, so a click and a quantized event on the
same bar line land on the same sample. The result matches
ossia::token_request point for point: over five signatures, eight rates, four
speeds and two buffer lengths the two agree exactly on the count, the index
and the frame of every point, which ossia/score's QuantificationParityTest
now asserts strictly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGta2LPAedDP3Txvyq8kTW
@jcelerier
jcelerier force-pushed the fix/quantification-backwards branch from e3651ae to 948d21d Compare August 4, 2026 03:16
@jcelerier jcelerier changed the title halp: report quantification dates when the timeline runs backwards halp: one quantification grid, walked once, matching libossia exactly Aug 4, 2026
jcelerier added a commit to ossia/score that referenced this pull request Aug 4, 2026
The score changes here call token_request::physical_position,
get_quantification_point and the musical position a quantification point now
carries, and QuantificationParityTest compiles halp's grid alongside ossia's.
None of that exists in the submodule commits master records, so CI built the
new code against the old libraries and every job failed.

To be rebased onto the merge commits of ossia/libossia#917 and
celtera/avendish#185 before this is merged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant