acorde-core 1.2.11

Platform-agnostic music score model and command engine
Documentation

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 acorde_core::{Command, ScoreEngine, SetTempoCmd};

let mut engine = ScoreEngine::new();
engine.apply(Command::SetTempo(SetTempoCmd { bpm: 120 }))?;
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.

API documentation · Repository