acorde-core
Score data model and command engine for Rust and WebAssembly.
This crate has no I/O, filesystem, renderer, or async-runtime dependency. It provides Score, Part,
Staff, Measure, Note, notation types, ScoreEngine, serializable Command values, undo/redo,
validation, playback-event generation, score diff/patch, transposition, and music-theory helpers.
Playback-event generation coalesces contiguous authored ties into one continuous event while
keeping malformed or non-contiguous tie endings observable as separate events.
Pitches preserve fractional cents, and playback events expose exact MIDI-cent values in addition to
the rounded MIDI note, while notes and staves can carry tablature string/fret and
tuning metadata with serde defaults for older score JSON.
Pitch::to_scientific_name() preserves authored accidental runs beyond double-sharp/double-flat;
scientific-name parsing rejects an accidental run that would overflow the signed alteration range.
Validation rejects deserialized microtone_cents values outside the canonical -99..99 range.
assign_tablature_positions provides deterministic capo-aware placement, while
optimize_tablature_positions considers movement between notes and chord strings. SetTabPositionCmd
makes explicit positions editable through the command engine.
SetTablatureConfigCmd makes staff tuning, string count, and capo editable with undo/redo.
Ordered alternate fingering candidates are stored in Note.fingerings; SetFingeringsCmd keeps
the first candidate synchronized with the legacy Note.fingering field.
Note::select_fingering provides a non-mutating deterministic source-order, lowest-number, or
highest-number projection.
MusicXML guitar bend amounts are retained as optional cent values on Note and can be edited with
SetGuitarBendAlterCmd.
Unpitched notes can be resolved with Part::percussion_instrument_for_note, which prefers an
explicit note instrument ID, then a declared MIDI display-key mapping, and never guesses an
unmatched sound identity.
MEI staff-group boundaries are represented separately as Part.staff_groups; they are not
confused with Score.part_groups, which connect distinct parts.
Chord symbols preserve optional vertical placement and structured MusicXML degree
value/alter/type data through ChordSymbol.degrees. MEI/MusicXML/MSCX harmonic-function metadata
is preserved through the serde-defaulted ChordSymbol.harmony_function field.
Pitch::try_with_microtone() rejects cents outside the canonical -99..=99 range without
clamping; the legacy with_microtone() constructor retains its clamping behavior for
compatibility. Measure-level styled text can be edited transactionally with
Command::SetMeasureText: provide an existing text_index to replace/remove an entry, or the
current length to append one. Score-level styled text uses the same contract through
Command::SetScoreText, keeping title-page annotations editable after import. Invalid indexes
return an error before mutation, and both operations participate in the normal undo/redo and
JSON command-history contracts.
diff and ScorePatch also preserve the complete score-level text collection, so collaboration
and snapshot synchronization do not drop title-page annotations.
Structured figured bass, measure presentation, measure text, tablature configuration, chord definitions, part names, and
common part MIDI routing use local score-patch operations. When a score contains fields without a dedicated positional
patch variant, patch generation uses an atomic full-score replacement instead of silently dropping
those fields. ScoreChange reports these editing changes explicitly as well.
Score-level annotation changes are reported with their complete old/new collections.
Fields without dedicated diff variants are reported with stable semantic paths rather than being
silently treated as equivalent.
EngineHistory::base_matches and ScoreEngine::from_history_on_base provide a deterministic
preflight and typed rejection for replaying a stale command log on an unrelated collaboration
snapshot.
EngineHistory::compare classifies equivalent, extending, divergent, and base-mismatched logs
without replaying commands; the WASM ScoreEngine.compare_histories method exposes the same
diagnostic to browser hosts.
ScoreEngine::append_history_extension replays and installs only a safe remote suffix
transactionally, preserving the incoming command-log shape and rejecting divergent logs without
mutating the local score.
EngineHistory::conflict reports the first divergent command keys and remaining lengths for
explainable host-side conflict UI; WASM exposes it as history_conflict.
use ;
let mut engine = new;
engine.apply?;
engine.undo?;
The current Score JSON schema version is 1. MIDI pitch-bend, Controller Change, Program Change, and key/channel Aftertouch events are preserved per part with canonical 480-PPQ tick and channel metadata. Tablature performance projections also preserve authored guitar techniques for host-owned articulation, including authored bend alteration cents. Add acorde-io for format conversion and acorde-layout for renderer-independent layout.
Security invariants and resource-limit ownership are documented in the security contract.