Repository navigation
Conversation
A note is beamable when it is shorter than a quarter note, whatever the meter (ABC v2.2 §4.7: "if L:1/8 then ABC2DE is equivalent to AB C2 DE"). BeamResolver used to compare against the meter's beat instead, so in compound meter (and 2/2, 3/2) quarter notes were beamed to their neighbours: `B2A G2d` in 6/8 came out as two beamed pairs. - BeamResolver takes only the unit note length; the meter no longer reaches it, so SemanticPass stops rebuilding it on [M:] and the now-unused BodyContext.openingMeter goes. - The #85 [M:] tests asserted the old rule; they now check the meter lands on the right measure and leaves the beams alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each beamed note used to be placed — stem x, and the stem's base on the notehead — for the direction it would have taken alone, and then drawn the way the group's first note went. In `Gd` the d was placed stem-down at its notehead's left edge and drawn up to the beam. - SVGEmitter decides each group's direction before drawing any of it, as abcm2ps does: down where the highest note is farther above the middle line than the lowest is below, up in the opposite case, and a group as far above as below follows the part's previous stem (up if none). - That previous stem is threaded across bars, systems and pages alongside the pending ties and slurs. - A voice with a direction of its own (stem=, pipeformat, a shared staff's opposition) is left as it was. - The jig and Canzonetta snapshots are re-recorded: both had a group with stems on the wrong side of their noteheads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix #217 — don't beam notes too long to carry a flag
Fix #218 — one stem direction per beam group, every stem placed for it
octave= was parsed on V: and ignored, and not parsed on K: at all. It now shifts Note.pitch in the semantic pass, as abcm2ps's get_note does, scoped as abcm2ps scopes it: a header K: covers every voice, a body K: or a V: its own voice, and each lasts until another octave= replaces it. Transposition gains an additive statesOctave flag so octave=0 can reset one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every notehead, stem, ledger line, accidental, tie and slur was placed at its treble-clef position whatever clef the staff carried. Staff position is now counted from the clef's bottom-line pitch (ClefSpec.staffPosition(of:)), the same table the floating-voice split already used, now shared. An octave clef (treble-8) moves nothing, as in abcm2ps. The vertical layout's ledger-line reservation uses the same mapping. The §7 and §14.4 page snapshots are re-recorded: their bass voices are on clef=bass octave=-2, which the treble placement had nearly cancelled out (#224). Both now match abcm2ps -g note for note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # Tests/CeolKitSVGRendererTests/__Snapshots__/CanzonettaConformanceTests/pageMatchesSnapshot.1.txt
An unbeamed note on the middle line in an `.auto` voice now follows the previous stem in its part (the `lastStemUp` state #218 threads through pages, systems and measures), and stems up where there is none — the same tie-break as an even beam group, and what abcm2ps 8.14 does. It carries across bar lines and rests. `stem=`, pipe format and a shared staff's opposition still decide outright. The Canzonetta snapshot changes in three stems, each a B following a down stem; abcm2ps stems all three down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix #221 — stem a middle-line note the way the stem before it went
…anges K:bass, K:clef=bass and [K:clef=bass] were rejected as malformed and the clef was dropped. A K: with no tonic is now read as options only (§3.1.14, §4.6), and KeySignature records whether a key and a clef were stated (statesKey, statesClef). The semantic pass resolves what was left unsaid against what is in force: in the header an empty key is K:none; part way through a voice the key carries on, and a K: with no clef keeps the clef (K:G after K:bass stays bass, as in abcm2ps). An unreadable K: still warns and now changes nothing. A clef change part way through a voice is a new Event.clefChange where the field was written; Measure.key is set only by a K: that names a key. The renderer tracks the clef in force per column and per event, as abcm2ps -g 8.14 draws it: a change against a bar line is a small clef before that bar (the courtesy clef at the end of a line, with the new clef full size at the head of the next), a change mid-bar is a small clef between the notes, and a clef change alone draws no key signature. Notes, ledger lines and ledger space follow the clef in force. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix #223 — accept a K: that names a clef but no key, and draw clef changes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Clefs, octaves and stems, as abcm2ps draws them.
A minor release. Notes are placed for the clef in force, clef changes part way through a
voice are drawn,
octave=takes effect, and stems and beams follow abcm2ps's rules. Themodel gains
Event.clefChange. See Upgrading.Clefs and octaves
slur used to be placed at its treble-clef position whatever clef the staff carried. Staff
position is now counted from the clef's bottom line (
ClefSpec.staffPosition(of:)). Anoctave clef (
treble-8) moves nothing, as in abcm2ps.octave=onK:andV:(Apply octave= on K: and V: — it is parsed and never used #224) shiftsNote.pitchin the semantic pass, scoped asabcm2ps scopes it: a header
K:covers every voice, a bodyK:or aV:its own voice,and each lasts until another
octave=replaces it.Transposition.statesOctaveletsoctave=0reset it.K:with a clef and no key (Accept a K: that names a clef but no key (K:bass, [K:clef=bass]) #223).K:bass,K:clef=bassand[K:clef=bass]areread as options only (§3.1.14, §4.6) instead of being rejected.
KeySignaturerecordswhat was stated (
statesKey,statesClef); what was left unsaid carries on from what isin force, so
K:GafterK:bassstays in the bass clef. An unreadableK:still warns,and now changes nothing.
Event.clefChangewhere the field was written. A change against a bar line is a smallclef before that bar (a courtesy clef at the end of a line, with the new clef full size
at the head of the next); a change mid-bar is a small clef between the notes; a clef
change alone draws no key signature.
Measure.keyis set only by aK:that names a key.Stems and beams
6/8, 2/2 and 3/2, quarter notes were beamed to their neighbours.
any of it is drawn: down where the highest note is farther above the middle line than the
lowest is below, up in the opposite case, and a group as far above as below follows the
part's previous stem (up if none). Every stem in the group is placed for that direction.
.autovoice, across barlines and rests, and stems up where there is none.
stem=, pipe format and a sharedstaff's opposition still decide outright.
Fixes #217, Fixes #218, Fixes #221, Fixes #222, Fixes #223, Fixes #224
🤖 Generated with Claude Code