@@ -35,27 +35,27 @@ import { Voice } from '@coderline/alphatab/model/Voice';
3535import type { Settings } from '@coderline/alphatab/Settings' ;
3636import { XmlDocument } from '@coderline/alphatab/xml/XmlDocument' ;
3737
38- import { type XmlNode , XmlNodeType } from '@coderline/alphatab/xml/XmlNode' ;
39- import { MidiUtils } from '@coderline/alphatab/midi/MidiUtils' ;
40- import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection' ;
41- import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode' ;
42- import { PercussionMapper } from '@coderline/alphatab/model/PercussionMapper' ;
43- import { InstrumentArticulation , TechniqueSymbolPlacement } from '@coderline/alphatab/model/InstrumentArticulation' ;
44- import { MusicFontSymbol } from '@coderline/alphatab/model/MusicFontSymbol' ;
4538import { BeatCloner } from '@coderline/alphatab/generated/model/BeatCloner' ;
4639import { NoteCloner } from '@coderline/alphatab/generated/model/NoteCloner' ;
4740import { Logger } from '@coderline/alphatab/Logger' ;
48- import { GolpeType } from '@coderline/alphatab/model/GolpeType' ;
49- import { FadeType } from '@coderline/alphatab/model/FadeType' ;
50- import { WahPedal } from '@coderline/alphatab/model/WahPedal' ;
41+ import { MidiUtils } from '@coderline/alphatab/midi/MidiUtils' ;
42+ import { BackingTrack } from '@coderline/alphatab/model/BackingTrack' ;
5143import { BarreShape } from '@coderline/alphatab/model/BarreShape' ;
52- import { NoteOrnament } from '@coderline/alphatab/model/NoteOrnament' ;
53- import { Rasgueado } from '@coderline/alphatab/model/Rasgueado' ;
5444import { Direction } from '@coderline/alphatab/model/Direction' ;
45+ import { FadeType } from '@coderline/alphatab/model/FadeType' ;
46+ import { GolpeType } from '@coderline/alphatab/model/GolpeType' ;
47+ import { InstrumentArticulation , TechniqueSymbolPlacement } from '@coderline/alphatab/model/InstrumentArticulation' ;
5548import { ModelUtils } from '@coderline/alphatab/model/ModelUtils' ;
56- import { BackingTrack } from '@coderline/alphatab/model/BackingTrack' ;
49+ import { MusicFontSymbol } from '@coderline/alphatab/model/MusicFontSymbol' ;
50+ import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode' ;
51+ import { NoteOrnament } from '@coderline/alphatab/model/NoteOrnament' ;
52+ import { PercussionMapper } from '@coderline/alphatab/model/PercussionMapper' ;
53+ import { Rasgueado } from '@coderline/alphatab/model/Rasgueado' ;
5754import { Tuning } from '@coderline/alphatab/model/Tuning' ;
5855import { TremoloPickingEffect } from '@coderline/alphatab/model/TremoloPickingEffect' ;
56+ import { WahPedal } from '@coderline/alphatab/model/WahPedal' ;
57+ import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection' ;
58+ import { type XmlNode , XmlNodeType } from '@coderline/alphatab/xml/XmlNode' ;
5959
6060/**
6161 * This structure represents a duration within a gpif
@@ -705,7 +705,7 @@ export class GpifParser {
705705 }
706706 break ;
707707 case 'Elements' :
708- this . _parseElements ( track , c ) ;
708+ this . _parseElements ( track , c , false ) ;
709709 break ;
710710 }
711711 }
@@ -722,7 +722,7 @@ export class GpifParser {
722722 }
723723 break ;
724724 case 'Elements' :
725- this . _parseElements ( track , c ) ;
725+ this . _parseElements ( track , c , true ) ;
726726 break ;
727727 case 'LineCount' :
728728 const lineCount = GpifParser . _parseIntSafe ( c . innerText , 5 ) ;
@@ -733,80 +733,74 @@ export class GpifParser {
733733 }
734734 }
735735 }
736- private _parseElements ( track : Track , node : XmlNode ) {
736+ private _parseElements ( track : Track , node : XmlNode , isInstrumentSet : boolean ) {
737737 for ( const c of node . childElements ( ) ) {
738738 switch ( c . localName ) {
739739 case 'Element' :
740- this . _parseElement ( track , c ) ;
740+ this . _parseElement ( track , c , isInstrumentSet ) ;
741741 break ;
742742 }
743743 }
744744 }
745745
746- private _parseElement ( track : Track , node : XmlNode ) {
747- const type = node . findChildElement ( 'Type' ) ?. innerText ?? '' ;
746+ private _parseElement ( track : Track , node : XmlNode , isInstrumentSet : boolean ) {
747+ const name = node . findChildElement ( 'Name' ) ?. innerText ?? '' ;
748+
748749 for ( const c of node . childElements ( ) ) {
749750 switch ( c . localName ) {
750751 case 'Name' :
751752 case 'Articulations' :
752- this . _parseArticulations ( track , c , type ) ;
753+ this . _parseArticulations ( track , c , isInstrumentSet , name ) ;
753754 break ;
754755 }
755756 }
756757 }
757- private _parseArticulations ( track : Track , node : XmlNode , elementType : string ) {
758+ private _parseArticulations ( track : Track , node : XmlNode , isInstrumentSet : boolean , elementName : string ) {
758759 for ( const c of node . childElements ( ) ) {
759760 switch ( c . localName ) {
760761 case 'Articulation' :
761- this . _parseArticulation ( track , c , elementType ) ;
762+ this . _parseArticulation ( track , c , isInstrumentSet , elementName ) ;
762763 break ;
763764 }
764765 }
765766 }
766767
767- private _parseArticulation ( track : Track , node : XmlNode , elementType : string ) {
768+ private _parseArticulation ( track : Track , node : XmlNode , isInstrumentSet : boolean , elementName : string ) {
768769 const articulation = new InstrumentArticulation ( ) ;
769770 articulation . outputMidiNumber = - 1 ;
770- articulation . elementType = elementType ;
771+ // NOTE: in the past we used the type here, but it is not unique enough. e.g. there are multiple kinds of "ride" ('Ride' vs 'Ride Cymbal 2')
772+ // we have to use the name as element identifier
773+ // using a wrong type leads to wrong "NotationPatch" updates
774+ articulation . elementType = elementName ;
771775 let name = '' ;
772776 for ( const c of node . childElements ( ) ) {
773777 const txt = c . innerText ;
774778 switch ( c . localName ) {
775779 case 'Name' :
776780 name = c . innerText ;
777781 break ;
782+ case 'InputMidiNumbers' :
783+ articulation . id = GpifParser . _parseIntSafe ( txt . split ( ' ' ) [ 0 ] , 0 ) ;
784+ break ;
778785 case 'OutputMidiNumber' :
779786 articulation . outputMidiNumber = GpifParser . _parseIntSafe ( txt , 0 ) ;
780787 break ;
781788 case 'TechniqueSymbol' :
782- articulation . techniqueSymbol = this . _parseTechniqueSymbol ( txt ) ;
789+ articulation . techniqueSymbol = GpifParser . parseTechniqueSymbol ( txt ) ;
783790 break ;
784791 case 'TechniquePlacement' :
785- switch ( txt ) {
786- case 'outside' :
787- articulation . techniqueSymbolPlacement = TechniqueSymbolPlacement . Outside ;
788- break ;
789- case 'inside' :
790- articulation . techniqueSymbolPlacement = TechniqueSymbolPlacement . Inside ;
791- break ;
792- case 'above' :
793- articulation . techniqueSymbolPlacement = TechniqueSymbolPlacement . Above ;
794- break ;
795- case 'below' :
796- articulation . techniqueSymbolPlacement = TechniqueSymbolPlacement . Below ;
797- break ;
798- }
792+ articulation . techniqueSymbolPlacement = GpifParser . parseTechniqueSymbolPlacement ( txt ) ;
799793 break ;
800794 case 'Noteheads' :
801795 const noteHeadsTxt = GpifParser . _splitSafe ( txt ) ;
802796 if ( noteHeadsTxt . length >= 1 ) {
803- articulation . noteHeadDefault = this . _parseNoteHead ( noteHeadsTxt [ 0 ] ) ;
797+ articulation . noteHeadDefault = GpifParser . parseNoteHead ( noteHeadsTxt [ 0 ] ) ;
804798 }
805799 if ( noteHeadsTxt . length >= 2 ) {
806- articulation . noteHeadHalf = this . _parseNoteHead ( noteHeadsTxt [ 1 ] ) ;
800+ articulation . noteHeadHalf = GpifParser . parseNoteHead ( noteHeadsTxt [ 1 ] ) ;
807801 }
808802 if ( noteHeadsTxt . length >= 3 ) {
809- articulation . noteHeadWhole = this . _parseNoteHead ( noteHeadsTxt [ 2 ] ) ;
803+ articulation . noteHeadWhole = GpifParser . parseNoteHead ( noteHeadsTxt [ 2 ] ) ;
810804 }
811805
812806 if ( articulation . noteHeadHalf === MusicFontSymbol . None ) {
@@ -824,17 +818,20 @@ export class GpifParser {
824818 }
825819 }
826820
827- if ( articulation . outputMidiNumber !== - 1 ) {
821+ const fullName = `${ elementName } .${ name } ` ;
822+ if ( isInstrumentSet ) {
828823 track . percussionArticulations . push ( articulation ) ;
829- if ( name . length > 0 ) {
830- this . _articulationByName . set ( name , articulation ) ;
831- }
832- } else if ( name . length > 0 && this . _articulationByName . has ( name ) ) {
833- this . _articulationByName . get ( name ) ! . staffLine = articulation . staffLine ;
824+ this . _articulationByName . set ( fullName , articulation ) ;
825+ } else if ( this . _articulationByName . has ( fullName ) ) {
826+ // notation patch
827+ this . _articulationByName . get ( fullName ) ! . staffLine = articulation . staffLine ;
834828 }
835829 }
836830
837- private _parseTechniqueSymbol ( txt : string ) : MusicFontSymbol {
831+ /**
832+ * @internal
833+ */
834+ public static parseTechniqueSymbol ( txt : string ) : MusicFontSymbol {
838835 switch ( txt ) {
839836 case 'pictEdgeOfCymbal' :
840837 return MusicFontSymbol . PictEdgeOfCymbal ;
@@ -853,7 +850,28 @@ export class GpifParser {
853850 }
854851 }
855852
856- private _parseNoteHead ( txt : string ) : MusicFontSymbol {
853+ /**
854+ * @internal
855+ */
856+ public static parseTechniqueSymbolPlacement ( txt : string ) : TechniqueSymbolPlacement {
857+ switch ( txt ) {
858+ case 'outside' :
859+ return TechniqueSymbolPlacement . Outside ;
860+ case 'inside' :
861+ return TechniqueSymbolPlacement . Inside ;
862+ case 'above' :
863+ return TechniqueSymbolPlacement . Above ;
864+ case 'below' :
865+ return TechniqueSymbolPlacement . Below ;
866+ default :
867+ return TechniqueSymbolPlacement . Outside ;
868+ }
869+ }
870+
871+ /**
872+ * @internal
873+ */
874+ public static parseNoteHead ( txt : string ) : MusicFontSymbol {
857875 switch ( txt ) {
858876 case 'noteheadDoubleWholeSquare' :
859877 return MusicFontSymbol . NoteheadDoubleWholeSquare ;
0 commit comments