import { ArticulationEnum , VoiceEntry } from "../../VoiceData/VoiceEntry" ; import { IXmlAttribute , IXmlElement } from "../../../Common/FileIO/Xml" ; import log from "loglevel" ; import { TechnicalInstruction , TechnicalInstructionType } from "../../VoiceData/Instructions/TechnicalInstruction" ; import { OrnamentContainer , OrnamentEnum } from "../../VoiceData/OrnamentContainer" ; import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression" ; import { AccidentalEnum } from "../../../Common/DataObjects/Pitch" ; import { Articulation } from "../../VoiceData/Articulation" ; import { Note } from "../../VoiceData/Note" ; import { EngravingRules } from "../../Graphical/EngravingRules" ; export class ArticulationReader { private rules : EngravingRules ; constructor ( rules ) { this . rules = rules ; } private getAccEnumFromString ( input ) { switch ( input ) { case "sharp" : return AccidentalEnum . SHARP ; case "flat" : return AccidentalEnum . FLAT ; case "natural" : return AccidentalEnum . NATURAL ; case "double-sharp" : case "sharp-sharp" : return AccidentalEnum . DOUBLESHARP ; case "double-flat" : case "flat-flat" : return AccidentalEnum . DOUBLEFLAT ; case "triple-sharp" : return AccidentalEnum . TRIPLESHARP ; case "triple-flat" : return AccidentalEnum . TRIPLEFLAT ; case "quarter-sharp" : return AccidentalEnum . QUARTERTONESHARP ; case "quarter-flat" : return AccidentalEnum . QUARTERTONEFLAT ; case "three-quarters-sharp" : return AccidentalEnum . THREEQUARTERSSHARP ; case "three-quarters-flat" : return AccidentalEnum . THREEQUARTERSFLAT ; case "slash-quarter-sharp" : return AccidentalEnum . SLASHQUARTERSHARP ; case "slash-sharp" : return AccidentalEnum . SLASHSHARP ; case "double-slash-flat" : return AccidentalEnum . DOUBLESLASHFLAT ; case "sori" : return AccidentalEnum . SORI ; case "koron" : return AccidentalEnum . KORON ; default : return AccidentalEnum . NONE ; } } param node param currentVoiceEntry public addArticulationExpression ( node , currentVoiceEntry ) { if ( node !== undefined && node . elements ( ) . length > 0 ) { const childNodes = node . elements ( ) ; for ( let idx = 0 , len = childNodes . length ; idx < len ; ++ idx ) { const childNode = childNodes [ idx ] ; let name = childNode . name ; try { name = name . replace ( "-" , "" ) ; const articulationEnum = ArticulationEnum [ name ] ; if ( VoiceEntry . isSupportedArticulation ( articulationEnum ) ) { let placement = PlacementEnum . NotYetDefined ; const placementValue = childNode . attribute ( "placement" ) ?. value ; if ( placementValue === "above" ) { placement = PlacementEnum . Above ; } else if ( placementValue === "below" ) { placement = PlacementEnum . Below ; } const newArticulation = new Articulation ( articulationEnum , placement ) ; if ( name === "staccato" ) { if ( currentVoiceEntry . Articulations . length > 0 && currentVoiceEntry . Articulations [ 0 ] . articulationEnum !== ArticulationEnum . staccato ) { currentVoiceEntry . Articulations . splice ( 0 , 0 , newArticulation ) ; } } if ( name === "strongaccent" ) { const marcatoType = childNode ?. attribute ( "type" ) ?. value ; if ( marcatoType === "up" ) { newArticulation . articulationEnum = ArticulationEnum . marcatoup ; } else if ( marcatoType === "down" ) { newArticulation . articulationEnum = ArticulationEnum . marcatodown ; } } if ( ! currentVoiceEntry . hasArticulation ( newArticulation ) ) { currentVoiceEntry . Articulations . push ( newArticulation ) ; } } } catch ( ex ) { const errorMsg = "Invalid note articulation." ; log . debug ( "addArticulationExpression" , errorMsg , ex ) ; return ; } } } } param xmlNode param currentVoiceEntry public addFermata ( xmlNode , currentVoiceEntry ) { let articulationEnum = ArticulationEnum . fermata ; if ( xmlNode . attributes ( ) . length > 0 && xmlNode . attribute ( "type" ) ) { if ( xmlNode . attribute ( "type" ) . value === "inverted" ) { articulationEnum = ArticulationEnum . invertedfermata ; } } let placement = PlacementEnum . Above ; if ( xmlNode . attribute ( "placement" ) ?. value === "below" ) { placement = PlacementEnum . Below ; } currentVoiceEntry . Articulations . push ( new Articulation ( articulationEnum , placement ) ) ; } param technicalNode param currentVoiceEntry public addTechnicalArticulations ( technicalNode , currentVoiceEntry , currentNote ) { interface XMLElementToArticulationEnum { [ xmlElement ] : ArticulationEnum ; } const xmlElementToArticulationEnum = { "bend" : ArticulationEnum . bend , "down-bow" : ArticulationEnum . downbow , "open-string" : ArticulationEnum . naturalharmonic , "snap-pizzicato" : ArticulationEnum . snappizzicato , "stopped" : ArticulationEnum . lefthandpizzicato , "up-bow" : ArticulationEnum . upbow , } ; for ( const xmlArticulation in xmlElementToArticulationEnum ) { if ( ! xmlElementToArticulationEnum . hasOwnProperty ( xmlArticulation ) ) { continue ; } const articulationEnum = xmlElementToArticulationEnum [ xmlArticulation ] ; const node = technicalNode . element ( xmlArticulation ) ; if ( node ) { let placement ; if ( node . attribute ( "placement" ) ?. value === "above" ) { placement = PlacementEnum . Above ; } if ( node . attribute ( "placement" ) ?. value === "below" ) { placement = PlacementEnum . Below ; } const newArticulation = new Articulation ( articulationEnum , placement ) ; if ( ! currentVoiceEntry . hasArticulation ( newArticulation ) ) { currentVoiceEntry . Articulations . push ( newArticulation ) ; } } } const nodeFingering = technicalNode . element ( "fingering" ) ; if ( nodeFingering ) { const currentTechnicalInstruction = this . createTechnicalInstruction ( nodeFingering , currentNote ) ; currentTechnicalInstruction . type = TechnicalInstructionType . Fingering ; currentNote . Fingering = currentTechnicalInstruction ; currentVoiceEntry . TechnicalInstructions . push ( currentTechnicalInstruction ) ; } const nodeString = technicalNode . element ( "string" ) ; if ( nodeString ) { const currentTechnicalInstruction = this . createTechnicalInstruction ( nodeString , currentNote ) ; currentTechnicalInstruction . type = TechnicalInstructionType . String ; currentNote . StringInstruction = currentTechnicalInstruction ; currentVoiceEntry . TechnicalInstructions . push ( currentTechnicalInstruction ) ; } } private createTechnicalInstruction ( stringOrFingeringNode , note ) { const technicalInstruction = new TechnicalInstruction ( ) ; technicalInstruction . sourceNote = note ; technicalInstruction . value = stringOrFingeringNode . value ; const placement = stringOrFingeringNode . attribute ( "placement" ) ; if ( this . rules . FingeringPositionFromXML ) { technicalInstruction . placement = this . getPlacement ( placement ) ; } return technicalInstruction ; } private getPlacement ( placementAttr , defaultPlacement = PlacementEnum . NotYetDefined ) { if ( defaultPlacement !== PlacementEnum . NotYetDefined ) { return defaultPlacement ; } if ( placementAttr ) { switch ( placementAttr . value ) { case "above" : return PlacementEnum . Above ; case "below" : return PlacementEnum . Below ; case "left" : return PlacementEnum . Left ; case "right" : return PlacementEnum . Right ; default : return PlacementEnum . NotYetDefined ; } } else { return PlacementEnum . NotYetDefined ; } } param ornamentsNode param currentVoiceEntry public addOrnament ( ornamentsNode , currentVoiceEntry ) { if ( ornamentsNode ) { let ornament = undefined ; interface XMLElementToOrnamentEnum { [ xmlElement ] : OrnamentEnum ; } const elementToOrnamentEnum = { "delayed-inverted-turn" : OrnamentEnum . DelayedInvertedTurn , "delayed-turn" : OrnamentEnum . DelayedTurn , "inverted-mordent" : OrnamentEnum . InvertedMordent , "inverted-turn" : OrnamentEnum . InvertedTurn , "mordent" : OrnamentEnum . Mordent , "trill-mark" : OrnamentEnum . Trill , "turn" : OrnamentEnum . Turn , } ; for ( const ornamentElement in elementToOrnamentEnum ) { if ( ! elementToOrnamentEnum . hasOwnProperty ( ornamentElement ) ) { continue ; } const node = ornamentsNode . element ( ornamentElement ) ; if ( node ) { ornament = new OrnamentContainer ( elementToOrnamentEnum [ ornamentElement ] ) ; const placementAttr = node . attribute ( "placement" ) ; if ( placementAttr ) { const placementString = placementAttr . value ; if ( placementString === "below" ) { ornament . placement = PlacementEnum . Below ; } } } } if ( ornament ) { const accidentalsList = ornamentsNode . elements ( "accidental-mark" ) ; if ( accidentalsList ) { let placement = PlacementEnum . Below ; let accidental = AccidentalEnum . NONE ; const accidentalsListArr = accidentalsList ; for ( let idx = 0 , len = accidentalsListArr . length ; idx < len ; ++ idx ) { const accidentalNode = accidentalsListArr [ idx ] ; let text = accidentalNode . value ; accidental = this . getAccEnumFromString ( text ) ; const placementAttr = accidentalNode . attribute ( "placement" ) ; if ( accidentalNode . hasAttributes && placementAttr ) { text = placementAttr . value ; if ( text === "above" ) { placement = PlacementEnum . Above ; } else if ( text === "below" ) { placement = PlacementEnum . Below ; } } if ( placement === PlacementEnum . Above ) { ornament . AccidentalAbove = accidental ; } else if ( placement === PlacementEnum . Below ) { ornament . AccidentalBelow = accidental ; } } } currentVoiceEntry . OrnamentContainer = ornament ; } } } }