Skip to content

fix(musicxml): import harmonics and fold touching and sounding pitch notes - #2947

Merged
Danielku15 merged 2 commits into
developfrom
fix/2942-musicxml-harmonics
Oct 9, 2026
Merged

Danielku15 merged 2 commits into
developfrom
fix/2942-musicxml-harmonics

Conversation

@Danielku15

Copy link
Copy Markdown
Member

Note

AI-authored disclosure (alphatab-ai-authored-v1)

Portions of this content were authored by an AI agent. The agent has read
AGENTS.md and the human submitter accepts responsibility for
compliance with the rules in that document.

Issues

Fixes #2942

Proposed changes

MusicXML writes a harmonic as separate chord notes: the base pitch (the played note), the touching pitch (where the string is touched) and the sounding pitch (what is heard). alphaTab ignored <harmonic>, so the touching and sounding notes were imported as extra played notes:

  • on tablature staves they have no <string>/<fret>, which gave NaN positions: a "NaN" label on develop, an exception in 1.8.4;
  • on the notation staff they added stray note heads;
  • all three pitches were played.

Importer

  • <technical><harmonic> is read before the note is attached. Touching and sounding chord notes are folded into their base pitch note: they only set its harmonic value and are never added to the beat.
  • A base pitch note starts a new harmonic, so several harmonics in one chord stay separate. The MusicXML 4.1 number attribute also groups them.
  • The interval is compared in concert pitch, using the staff each note specifies. Guitar Pro 8 omits <staff> on the touching and sounding notes of a tablature staff, so they belong to staff 1 and are written in its (octave transposed) pitch.

Mapping, checked against Guitar Pro 8

GP8 exports and imports were inspected through its automation API (export to MusicXML, import back, read the resulting gpif).

MusicXML Result Guitar Pro 8 import
base + touching + sounding, artificial (GP8 export) Artificial, value from the touching interval (5/7/12 round-trip) same
base + sounding, artificial Artificial, value = lowest fret sounding the interval same
base + touching, artificial Artificial, value from the touching interval harmonic dropped (GP needs the sounding pitch)
<natural/> (+ base or touching pitch) on a node fret Natural, value = fret same
natural base on a fret without node (e.g. fret 1) no harmonic same
natural base + touching + sounding one Natural note, value = touched fret 3 separate harmonic notes
empty <harmonic/> (GP8's export of pinch, tap, semi) ignored same
<artificial/> without touching or sounding pitch ignored same

Where GP loses information (the third and sixth rows), the spec semantics are kept. Files written by GP8 import the same either way.

Model

Artificial, pinch, tap, semi and feedback harmonics sound an interval above the played pitch, which needs no string. On pitched notes without a string (MusicXML notation staves, alphaTex C4{ah 12}), harmonicPitch was 0: the harmonic diamond was drawn on top of the note head and the base pitch was played. The interval now applies to these notes too. Natural harmonics without a string are unchanged, because there is no open string to derive them from. The value → interval table moved to ModelUtils.harmonicValueToPitch and is shared with the importer.

Reference change

32a-Notations.png (bars 11–13): the base/touching/sounding chords are now single harmonic notes.

Not part of this PR

A pitch-only note on a MusicXML tablature staff (no <string>/<fret>) still gets NaN tab positions. This PR removes the trigger from #2942, but the general case needs its own issue and design.

Checklist

  • I consent that this change becomes part of alphaTab under its current or any future open source license
  • This PR is linked to an accepted issue (see above)
  • Changes are implemented
  • New tests were added
  • I have read AGENTS.md if an AI helped draft any part of this PR

AI authorship disclosure

  • No AI agent authored any part of this PR (description, code, tests, or commit messages)
  • An AI agent contributed to this PR. The AI-authored disclosure block
    (alphatab-ai-authored-v1) is present at the top of this body, and I have
    personally reviewed every change and can explain each one

Further details

  • This is a breaking change
  • This change will require update of the documentation/website

…out string

Artificial, pinch, tap, semi and feedback harmonics sound an interval above the
played pitch, which does not need a string. On pitched notes without string the
harmonic diamond was drawn on top of the note head and the played pitch was heard.
The harmonic value to interval table moves to ModelUtils to be shared with importers.
…notes (#2942)

MusicXML writes a harmonic as chord notes for the base, touching and sounding pitch.
These were imported as additional played notes, on tablature staves without string
and fret (NaN positions). The touching and sounding notes now define the harmonic
value of their base pitch note, matching how Guitar Pro 8 writes and reads them.
@Danielku15
Danielku15 merged commit a89101c into develop Oct 9, 2026
6 checks passed
@Danielku15
Danielku15 deleted the fix/2942-musicxml-harmonics branch October 9, 2026 18:20
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.

MusicXML: Guitar Pro 8 artificial harmonic (chord notes without staff/string) breaks the tab staff: exception in 1.8.4, "NaN" in 1.9.0-alpha.1891

1 participant