npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@audio/musicxml

v0.1.0

Published

MusicXML 4.0 writer/parser and ABC writer — note events ↔ notation

Readme

@audio/musicxml npm MIT

MusicXML 4.0 writer/parser and ABC writer — note events → notation

npm install @audio/musicxml
import write, { parse, toAbc, quantize } from '@audio/musicxml'

Takes the note-event shape shared by @audio/mir-transcribe, @audio/mir-drums and @audio/midi-write ([{time, duration, midi, velocity}], seconds) and writes standard notation: MusicXML 4.0 score-partwise (W3C Music Notation CG spec) or ABC 2.1 (abcnotation.com standard). Onsets/durations are snapped to a rhythmic grid, split into a tied run where a duration isn't directly expressible (whole/half/quarter/eighth/16th/32nd + dots + triplets), and spelled key-aware (sharp/flat choice + courtesy accidentals from the key signature). parse() reads MusicXML back into the same note-event shape. For turning MIDI note numbers into synth voices or scale math, see @audio/note; for the reverse direction (SMF bytes), see @audio/midi-write/@audio/midi-parse.

import write, { parse, toAbc } from '@audio/musicxml'

let notes = [
  { time: 0, duration: 0.5, midi: 60, velocity: 0.8 },
  { time: 0.5, duration: 0.5, midi: 64, velocity: 0.8 },
]

let xml = write(notes, { bpm: 120, key: 0, title: 'Untitled' })   // MusicXML 4.0 string
let abc = toAbc(notes, { bpm: 120 })                              // ABC 2.1 string

let { parts } = parse(xml)                                        // back to note events
parts[0].notes   // [{time, duration, midi, velocity, voice}, …]

Multiple parts, percussion, and an explicit clef:

write({
  parts: [
    { name: 'Melody', notes: melodyNotes, clef: 'treble' },
    { name: 'Drums', notes: drumEvents, percussion: true },   // mir-drums {time, type, strength}
  ],
}, { bpm: 100, timeSignature: [3, 4], key: 'G' })

| Param | Default | | |---|---|---| | bpm | 120 | Tempo, quarter-notes/minute — ignored when tempos is given | | tempos | — | Piecewise tempo map [{time, bpm}], seconds | | timeSignature | [4, 4] | [beats, beat-type] | | key | 0 | Circle-of-fifths position (−7..7) or a key name ('C', 'G', 'Fm', 'Am', …) | | mode | 'major' | Ignored when key is a name that already encodes mode ('Am') | | divisions | 480 | Ticks per quarter note; bumped up automatically so every duration is exact | | quantize | 16 | Rhythmic grid denominator (4, 8, 16, 32; 0 = no quantization) | | triplets | true | Also try the 1/8 and 1/16 triplet grids per note, keep whichever fits | | minDuration | one grid step | Shortest kept note, in beats | | title, composer | — | <work-title> / <creator type="composer"> | | software | '@audio/musicxml' | <identification><encoding><software> | | pickup | false | Anacrusis: shorten measure 1 to the piece's remainder-length pickup |

Each part is { name, notes, instrument?, percussion?, clef? }. clef is 'treble' | 'bass' | 'grand' | 'percussion', default auto by median pitch (≥60 → treble). 'grand' writes two staves split at middle C. instrument is { name?, channel?, program? } (0-based, like the rest of this ecosystem — converted to MusicXML's 1-based midi-channel/midi-program). percussion: true reads GM channel-10 events: either mir-drums-style {type: 'kick'|'snare'|'hihat', …} or notes that already carry a raw midi number.

Rhythm: onsets/durations are quantized to the grid (quantize(), exported separately — seconds → {beat, beats} in quarter-note beats via a piecewise tempo map), then a duration too long for one notated value is split into a tied run, greedy from the largest expressible value (Ross, The Art of Music Engraving, ch. 4). A note or rest crossing a barline is likewise split and tied. Chords (<chord/>) form from equal-duration simultaneous onsets; unequal durations at the same onset become a second voice via <backup> — up to 2 voices per part. Pitch spelling follows the key signature: diatonic notes take the key's accidental with no <accidental> element; a chromatic note is spelled sharp in sharp/neutral keys and flat in flat keys; a minor key's raised 7th (leading tone) is spelled as a raised natural-7th letter, not a flat of the letter above. Accidentals are measure-scoped, with courtesy naturals when a note cancels the key signature.

Percussion: the GM Level 1 percussion key map (MIDI Manufacturers Association, General MIDI System Level 1, §Percussion Key Map) for type↔MIDI, plus the display-step/octave convention most notation software uses for a 5-line percussion staff (kick→F4, snare→C5, closed hi-hat→G5, …). Several GM instruments share a staff position (open/closed hi-hat both sit on G5); parse()'s reverse lookup resolves ties by table order, so only the kick/snare/hihat trio mir-drums emits is guaranteed to round-trip exactly.

Not done: velocity has no standard per-note home in MusicXML, so parse() always returns 0.8 — it is not round-tripped. toAbc() renders the primary voice only (a second concurrent voice from unequal-duration overlaps is dropped — use write() for that). Transposing-instrument <attributes><transpose> is read but not applied — pitches come back exactly as written in the file, not concert pitch. pickup computes the anacrusis length from the total note span (totalBeats mod beatsPerMeasure), not from an explicit pickup length.

Use when: exporting recognizer/analysis output (mir-transcribe, mir-drums) or a parsed MIDI file to a score a musician can read in MuseScore/Finale/Sibelius, or to a lightweight ABC string for the web. Not a full engraving engine — no beaming, no lyrics, no multi-staff systems beyond one piano grand staff.


Part of @audio/midi — the midi family umbrella.

MIT © audiojs