import { Instrument } from "../Instrument" ; import { MusicSheet } from "../MusicSheet" ; import { VoiceGenerator } from "./VoiceGenerator" ; import { Staff } from "../VoiceData/Staff" ; import { SourceMeasure } from "../VoiceData/SourceMeasure" ; import { SourceStaffEntry } from "../VoiceData/SourceStaffEntry" ; import { ClefInstruction } from "../VoiceData/Instructions/ClefInstruction" ; import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction" ; import { RhythmInstruction } from "../VoiceData/Instructions/RhythmInstruction" ; import { AbstractNotationInstruction } from "../VoiceData/Instructions/AbstractNotationInstruction" ; import { Fraction } from "../../Common/DataObjects/Fraction" ; import { IXmlElement } from "../../Common/FileIO/Xml" ; import { ITextTranslation } from "../Interfaces/ITextTranslation" ; import { MusicSheetReadingException } from "../Exceptions" ; import { ClefEnum } from "../VoiceData/Instructions/ClefInstruction" ; import { RhythmSymbolEnum } from "../VoiceData/Instructions/RhythmInstruction" ; import { KeyEnum } from "../VoiceData/Instructions/KeyInstruction" ; import { IXmlAttribute } from "../../Common/FileIO/Xml" ; import { ChordSymbolContainer } from "../VoiceData/ChordSymbolContainer" ; import log from "loglevel" ; import { MidiInstrument } from "../VoiceData/Instructions/ClefInstruction" ; import { ChordSymbolReader } from "./MusicSymbolModules/ChordSymbolReader" ; import { ExpressionReader } from "./MusicSymbolModules/ExpressionReader" ; import { RepetitionInstructionReader } from "./MusicSymbolModules/RepetitionInstructionReader" ; import { SlurReader } from "./MusicSymbolModules/SlurReader" ; import { StemDirectionType } from "../VoiceData/VoiceEntry" ; import { NoteType , NoteTypeHandler } from "../VoiceData/NoteType" ; import { SystemLinesEnumHelper } from "../Graphical/SystemLinesEnum" ; import { ReaderPluginManager } from "./ReaderPluginManager" ; /**
 * An InstrumentReader is used during the reading phase to keep parsing new measures from the MusicXML file
 * with the readNextXmlMeasure method.
 */ export class InstrumentReader { constructor ( pluginManager , repetitionInstructionReader , xmlMeasureList , instrument ) { this . repetitionInstructionReader = repetitionInstructionReader ; this . xmlMeasureList = xmlMeasureList ; this . musicSheet = instrument . GetMusicSheet ; this . instrument = instrument ; this . activeClefs = new Array ( instrument . Staves . length ) ; this . activeClefsHaveBeenInitialized = new Array ( instrument . Staves . length ) ; for ( let i = 0 ; i < instrument . Staves . length ; i ++ ) { this . activeClefsHaveBeenInitialized [ i ] = false ; } this . createExpressionGenerators ( instrument . Staves . length ) ; this . slurReader = new SlurReader ( this . musicSheet ) ; this . pluginManager = pluginManager ; } private repetitionInstructionReader : RepetitionInstructionReader ; private xmlMeasureList : IXmlElement [ ] ; private musicSheet : MusicSheet ; private slurReader : SlurReader ; public pluginManager : ReaderPluginManager ; private instrument : Instrument ; private voiceGeneratorsDict : { [ n ] : VoiceGenerator ; } = { } ; private staffMainVoiceGeneratorDict : { [ staffId ] : VoiceGenerator ; } = { } ; private inSourceMeasureInstrumentIndex : number ; private divisions : number = 0 ; private currentMeasure : SourceMeasure ; private previousMeasure : SourceMeasure ; private currentClefNumber : number = 1 ; private currentXmlMeasureIndex : number = 0 ; private currentStaff : Staff ; private currentStaffEntry : SourceStaffEntry ; private activeClefs : ClefInstruction [ ] ; private activeKey : KeyInstruction ; private activeRhythm : RhythmInstruction ; private activeClefsHaveBeenInitialized : boolean [ ] ; private activeKeyHasBeenInitialized : boolean = false ; private abstractInstructions : [ number , AbstractNotationInstruction , Fraction ] [ ] = [ ] ; private openChordSymbolContainers : ChordSymbolContainer [ ] = [ ] ; private expressionReaders : ExpressionReader [ ] ; private currentVoiceGenerator : VoiceGenerator ; private maxTieNoteFraction : Fraction ; private currentMultirestStartMeasure : SourceMeasure ; private followingMultirestMeasures : number ; public get ActiveKey ( ) : KeyInstruction { return this . activeKey ; } public get MaxTieNoteFraction ( ) : Fraction { return this . maxTieNoteFraction ; } public get ActiveRhythm ( ) : RhythmInstruction { return this . activeRhythm ; } public set ActiveRhythm ( value ) { this . activeRhythm = value ; } param currentMeasure param measureStartAbsoluteTimestamp param octavePlusOne returns { boolean } public readNextXmlMeasure ( currentMeasure , measureStartAbsoluteTimestamp , octavePlusOne ) { if ( this . currentXmlMeasureIndex >= this . xmlMeasureList . length ) { return false ; } this . currentMeasure = currentMeasure ; this . followingMultirestMeasures = Math . max ( this . followingMultirestMeasures - 1 , 0 ) ; this . inSourceMeasureInstrumentIndex = this . musicSheet . getGlobalStaffIndexOfFirstStaff ( this . instrument ) ; if ( this . repetitionInstructionReader ) { this . repetitionInstructionReader . prepareReadingMeasure ( currentMeasure , this . currentXmlMeasureIndex ) ; } let currentFraction = new Fraction ( 0 , 1 ) ; let previousFraction = new Fraction ( 0 , 1 ) ; let divisionsException = false ; this . maxTieNoteFraction = new Fraction ( 0 , 1 ) ; let lastNoteWasGrace = false ; try { const measureNode = this . xmlMeasureList [ this . currentXmlMeasureIndex ] ; const xmlMeasureListArr = measureNode . elements ( ) ; if ( currentMeasure . Rules . UseXMLMeasureNumbers && ! Number . isInteger ( currentMeasure . MeasureNumberXML ) ) { const measureNumberXml = parseInt ( measureNode . attribute ( "number" ) ?. value , 10 ) ; if ( Number . isInteger ( measureNumberXml ) ) { currentMeasure . MeasureNumberXML = measureNumberXml ; } } let previousNode ; for ( let xmlNodeIndex = 0 ; xmlNodeIndex < xmlMeasureListArr . length ; xmlNodeIndex ++ ) { const xmlNode = xmlMeasureListArr [ xmlNodeIndex ] ; if ( xmlNodeIndex > 0 ) { previousNode = xmlMeasureListArr [ xmlNodeIndex - 1 ] ; } if ( xmlNode . name === "print" ) { const newSystemAttr = xmlNode . attribute ( "new-system" ) ; if ( newSystemAttr ?. value === "yes" ) { currentMeasure . printNewSystemXml = true ; } const newPageAttr = xmlNode . attribute ( "new-page" ) ; if ( newPageAttr ?. value === "yes" ) { currentMeasure . printNewPageXml = true ; } } else if ( xmlNode . name === "note" ) { let printObject = true ; if ( xmlNode . attribute ( "print-object" ) ?. value === "no" ) { printObject = false ; } let noteStaff = 1 ; if ( this . instrument . Staves . length > 1 ) { if ( xmlNode . element ( "staff" ) ) { noteStaff = parseInt ( xmlNode . element ( "staff" ) . value , 10 ) ; if ( isNaN ( noteStaff ) ) { log . debug ( "InstrumentReader.readNextXmlMeasure.get staff number" ) ; noteStaff = 1 ; } } } this . currentStaff = this . instrument . Staves [ noteStaff - 1 ] ; const isChord = xmlNode . element ( "chord" ) !== undefined ; if ( xmlNode . element ( "voice" ) ) { const noteVoice = parseInt ( xmlNode . element ( "voice" ) . value , 10 ) ; this . currentVoiceGenerator = this . getOrCreateVoiceGenerator ( noteVoice , noteStaff - 1 ) ; } else { if ( ! isChord || ! this . currentVoiceGenerator ) { this . currentVoiceGenerator = this . getOrCreateVoiceGenerator ( 1 , noteStaff - 1 ) ; } } let noteDivisions = 0 ; let noteDuration = new Fraction ( 0 , 1 ) ; let normalNotes = 2 ; let typeDuration = undefined ; let isTuplet = false ; if ( xmlNode . element ( "duration" ) ) { noteDivisions = parseInt ( xmlNode . element ( "duration" ) . value , 10 ) ; if ( ! isNaN ( noteDivisions ) ) { noteDuration = new Fraction ( noteDivisions , 4 * this . divisions ) ; if ( noteDivisions === 0 ) { noteDuration = this . getNoteDurationFromTypeNode ( xmlNode ) ; } else { typeDuration = this . getNoteDurationFromTypeNode ( xmlNode ) ; } if ( xmlNode . element ( "time-modification" ) ) { noteDuration = this . getNoteDurationForTuplet ( xmlNode ) ; const time = xmlNode . element ( "time-modification" ) ; if ( time ?. element ( "normal-notes" ) ) { normalNotes = parseInt ( time . element ( "normal-notes" ) . value , 10 ) ; } isTuplet = true ; } } else { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/NoteDurationError" , "Invalid Note Duration." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; log . debug ( "InstrumentReader.readNextXmlMeasure" , errorMsg ) ; continue ; } } const restNote = xmlNode . element ( "rest" ) !== undefined ; const notationsNode = xmlNode . element ( "notations" ) ; const isGraceNote = xmlNode . element ( "grace" ) !== undefined || noteDivisions === 0 || isChord && lastNoteWasGrace ; let graceNoteSlash = false ; let graceSlur = false ; if ( isGraceNote ) { const graceNode = xmlNode . element ( "grace" ) ; if ( graceNode && graceNode . attributes ( ) ) { if ( graceNode . attribute ( "slash" ) ) { const slash = graceNode . attribute ( "slash" ) . value ; if ( slash === "yes" ) { graceNoteSlash = true ; } } } noteDuration = this . getNoteDurationFromTypeNode ( xmlNode ) ; const notationNode = xmlNode . element ( "notations" ) ; if ( notationNode ) { if ( notationNode . element ( "slur" ) ) { graceSlur = true ; } } } let isCueNote = false ; const cueNode = xmlNode . element ( "cue" ) ; if ( cueNode ) { isCueNote = true ; } const typeNode = xmlNode . element ( "type" ) ; let noteTypeXml = NoteType . UNDEFINED ; if ( typeNode ) { const sizeAttr = typeNode . attribute ( "size" ) ; if ( sizeAttr ?. value === "cue" ) { isCueNote = true ; } noteTypeXml = NoteTypeHandler . StringToNoteType ( typeNode . value ) ; } let stemDirectionXml = StemDirectionType . Undefined ; let stemColorXml ; const stemNode = xmlNode . element ( "stem" ) ; if ( stemNode ) { switch ( stemNode . value ) { case "down" : stemDirectionXml = StemDirectionType . Down ; break ; case "up" : stemDirectionXml = StemDirectionType . Up ; break ; case "double" : stemDirectionXml = StemDirectionType . Double ; break ; case "none" : stemDirectionXml = StemDirectionType . None ; break ; default : stemDirectionXml = StemDirectionType . Undefined ; } const stemColorAttr = stemNode . attribute ( "color" ) ; if ( stemColorAttr ) { stemColorXml = this . parseXmlColor ( stemColorAttr . value ) ; } } let tremoloStrokes = 0 ; let vibratoStrokes = false ; if ( notationsNode ) { const ornamentsNode = notationsNode . element ( "ornaments" ) ; if ( ornamentsNode ) { const tremoloNode = ornamentsNode . element ( "tremolo" ) ; if ( tremoloNode ) { const tremoloType = tremoloNode . attribute ( "type" ) ; if ( tremoloType && tremoloType . value === "single" ) { const tremoloStrokesGiven = parseInt ( tremoloNode . value , 10 ) ; if ( tremoloStrokesGiven > 0 ) { tremoloStrokes = tremoloStrokesGiven ; } } } const vibratoNode = ornamentsNode . element ( "wavy-line" ) ; if ( vibratoNode !== undefined ) { const vibratoType = vibratoNode . attribute ( "type" ) ; if ( vibratoType && vibratoType . value === "start" ) { vibratoStrokes = true ; } } } } let noteheadColorXml ; const noteheadNode = xmlNode . element ( "notehead" ) ; if ( noteheadNode ) { const colorAttr = noteheadNode . attribute ( "color" ) ; if ( colorAttr ) { noteheadColorXml = this . parseXmlColor ( colorAttr . value ) ; } } let noteColorXml ; const noteColorAttr = xmlNode . attribute ( "color" ) ; if ( noteColorAttr ) { noteColorXml = this . parseXmlColor ( noteColorAttr . value ) ; if ( ! noteheadColorXml ) { noteheadColorXml = noteColorXml ; } if ( ! stemColorXml ) { stemColorXml = noteColorXml ; } } let musicTimestamp = currentFraction . clone ( ) ; if ( isChord ) { musicTimestamp = previousFraction . clone ( ) ; } this . currentStaffEntry = this . currentMeasure . findOrCreateStaffEntry ( musicTimestamp , this . inSourceMeasureInstrumentIndex + noteStaff - 1 , this . currentStaff ) . staffEntry ; if ( ! this . currentVoiceGenerator . hasVoiceEntry ( ) || ( ! isChord && ! isGraceNote && ! lastNoteWasGrace ) || ( isGraceNote && ! lastNoteWasGrace ) || ( isGraceNote && ! isChord ) || ( ! isGraceNote && lastNoteWasGrace ) ) { this . currentVoiceGenerator . createVoiceEntry ( musicTimestamp , this . currentStaffEntry , ! restNote && ! isGraceNote , isGraceNote , graceNoteSlash , graceSlur ) ; } if ( ! isGraceNote && ! isChord ) { previousFraction = currentFraction . clone ( ) ; currentFraction . Add ( noteDuration ) ; } if ( isChord && this . currentStaffEntry !== undefined && this . currentStaffEntry . ParentStaff !== this . currentStaff ) { this . currentStaffEntry = this . currentVoiceGenerator . checkForStaffEntryLink ( this . inSourceMeasureInstrumentIndex + noteStaff - 1 , this . currentStaff , this . currentStaffEntry , this . currentMeasure ) ; } const beginOfMeasure = ( this . currentStaffEntry !== undefined && this . currentStaffEntry . Timestamp !== undefined && this . currentStaffEntry . Timestamp . Equals ( new Fraction ( 0 , 1 ) ) && ! this . currentStaffEntry . hasNotes ( ) ) ; this . saveAbstractInstructionList ( this . instrument . Staves . length , beginOfMeasure ) ; if ( this . openChordSymbolContainers . length !== 0 ) { this . currentStaffEntry . ChordContainers = this . openChordSymbolContainers ; this . openChordSymbolContainers = [ ] ; } if ( this . activeRhythm ) { } if ( ! isTuplet && ! isGraceNote ) { noteDuration = new Fraction ( noteDivisions , 4 * this . divisions ) ; } const dots = xmlNode . elements ( "dot" ) . length ; this . currentVoiceGenerator . read ( xmlNode , noteDuration , typeDuration , noteTypeXml , normalNotes , restNote , this . currentStaffEntry , this . currentMeasure , measureStartAbsoluteTimestamp , this . maxTieNoteFraction , isChord , octavePlusOne , printObject , isCueNote , isGraceNote , stemDirectionXml , tremoloStrokes , stemColorXml , noteheadColorXml , vibratoStrokes , dots ) ; if ( notationsNode !== undefined && notationsNode . element ( "dynamics" ) ) { const expressionReader = this . expressionReaders [ this . readExpressionStaffNumber ( xmlNode ) - 1 ] ; if ( expressionReader ) { expressionReader . readExpressionParameters ( xmlNode , this . instrument , this . divisions , currentFraction , previousFraction , this . currentMeasure . MeasureNumber , false ) ; expressionReader . read ( xmlNode , this . currentMeasure , previousFraction ) ; } } lastNoteWasGrace = isGraceNote ; } else if ( xmlNode . name === "attributes" ) { const divisionsNode = xmlNode . element ( "divisions" ) ; if ( divisionsNode ) { this . divisions = parseInt ( divisionsNode . value , 10 ) ; if ( isNaN ( this . divisions ) ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/DivisionError" , "Invalid divisions value at Instrument: " ) ; log . debug ( "InstrumentReader.readNextXmlMeasure" , errorMsg ) ; this . divisions = this . readDivisionsFromNotes ( ) ; if ( this . divisions > 0 ) { this . musicSheet . SheetErrors . push ( errorMsg + this . instrument . Name ) ; } else { divisionsException = true ; throw new MusicSheetReadingException ( errorMsg + this . instrument . Name ) ; } } } if ( ! xmlNode . element ( "divisions" ) && this . divisions === 0 && this . currentXmlMeasureIndex === 0 ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/DivisionError" , "Invalid divisions value at Instrument: " ) ; this . divisions = this . readDivisionsFromNotes ( ) ; if ( this . divisions > 0 ) { this . musicSheet . SheetErrors . push ( errorMsg + this . instrument . Name ) ; } else { divisionsException = true ; throw new MusicSheetReadingException ( errorMsg + this . instrument . Name ) ; } } this . addAbstractInstruction ( xmlNode , octavePlusOne , previousNode , currentFraction . clone ( ) ) ; if ( currentFraction . Equals ( new Fraction ( 0 , 1 ) ) && this . isAttributesNodeAtBeginOfMeasure ( this . xmlMeasureList [ this . currentXmlMeasureIndex ] , xmlNode ) ) { this . saveAbstractInstructionList ( this . instrument . Staves . length , true ) ; } if ( this . isAttributesNodeAtEndOfMeasure ( this . xmlMeasureList [ this . currentXmlMeasureIndex ] , xmlNode ) ) { this . saveClefInstructionAtEndOfMeasure ( ) ; } const staffDetailsNodes = xmlNode . elements ( "staff-details" ) ; for ( const staffDetailsNode of staffDetailsNodes ) { const staffLinesNode = staffDetailsNode . element ( "staff-lines" ) ; if ( staffLinesNode ) { let staffNumber = 1 ; const staffNumberAttr = staffDetailsNode . attribute ( "number" ) ; if ( staffNumberAttr ) { staffNumber = parseInt ( staffNumberAttr . value , 10 ) ; } this . instrument . Staves [ staffNumber - 1 ] . StafflineCount = parseInt ( staffLinesNode . value , 10 ) ; } } const measureStyle = xmlNode . element ( "measure-style" ) ; if ( measureStyle ) { const multipleRest = measureStyle . element ( "multiple-rest" ) ; if ( multipleRest ) { const multipleRestValueXml = multipleRest . value ; let multipleRestNumber = 0 ; try { multipleRestNumber = Number . parseInt ( multipleRestValueXml , 10 ) ; if ( currentMeasure . multipleRestMeasures !== undefined && multipleRestNumber !== currentMeasure . multipleRestMeasures ) { currentMeasure . multipleRestMeasures = 0 ; } else { currentMeasure . multipleRestMeasures = multipleRestNumber ; this . currentMultirestStartMeasure = currentMeasure ; this . followingMultirestMeasures = multipleRestNumber + 1 ; } } catch ( e ) { console . log ( "multirest parse error: " + e ) ; } } } } else if ( xmlNode . name === "forward" ) { const forFraction = parseInt ( xmlNode . element ( "duration" ) . value , 10 ) ; currentFraction . Add ( new Fraction ( forFraction , 4 * this . divisions ) ) ; } else if ( xmlNode . name === "backup" ) { const backFraction = parseInt ( xmlNode . element ( "duration" ) . value , 10 ) ; currentFraction . Sub ( new Fraction ( backFraction , 4 * this . divisions ) ) ; if ( currentFraction . IsNegative ( ) ) { currentFraction = new Fraction ( 0 , 1 ) ; } previousFraction . Sub ( new Fraction ( backFraction , 4 * this . divisions ) ) ; if ( previousFraction . IsNegative ( ) ) { previousFraction = new Fraction ( 0 , 1 ) ; } } else if ( xmlNode . name === "direction" ) { const directionTypeNode = xmlNode . element ( "direction-type" ) ; let relativePositionInMeasure = Math . min ( 1 , currentFraction . RealValue ) ; if ( this . activeRhythm !== undefined && this . activeRhythm . Rhythm ) { relativePositionInMeasure /= this . activeRhythm . Rhythm . RealValue ; } let handeled = false ; if ( this . repetitionInstructionReader ) { handeled = this . repetitionInstructionReader . handleRepetitionInstructionsFromWordsOrSymbols ( directionTypeNode , relativePositionInMeasure ) ; } if ( ! handeled ) { let expressionReader = this . expressionReaders [ 0 ] ; const staffIndex = this . readExpressionStaffNumber ( xmlNode ) - 1 ; if ( staffIndex < this . expressionReaders . length ) { expressionReader = this . expressionReaders [ staffIndex ] ; } if ( expressionReader ) { if ( directionTypeNode . element ( "octave-shift" ) ) { expressionReader . readExpressionParameters ( xmlNode , this . instrument , this . divisions , currentFraction , previousFraction , this . currentMeasure . MeasureNumber , true ) ; expressionReader . addOctaveShift ( xmlNode , this . currentMeasure , previousFraction . clone ( ) ) ; } expressionReader . readExpressionParameters ( xmlNode , this . instrument , this . divisions , currentFraction , previousFraction , this . currentMeasure . MeasureNumber , false ) ; expressionReader . read ( xmlNode , this . currentMeasure , currentFraction , previousFraction . clone ( ) ) ; } } } else if ( xmlNode . name === "barline" ) { if ( this . repetitionInstructionReader ) { const measureEndsSystem = this . repetitionInstructionReader . handleLineRepetitionInstructions ( xmlNode ) ; if ( measureEndsSystem ) { this . currentMeasure . HasEndLine = true ; } } const location = xmlNode . attribute ( "location" ) ; const isEndingBarline = ( xmlNodeIndex === xmlMeasureListArr . length - 1 ) ; if ( isEndingBarline || ( location && location . value === "right" ) ) { const stringValue = xmlNode . element ( "bar-style" ) ?. value ; if ( stringValue ) { this . currentMeasure . endingBarStyleXml = stringValue ; this . currentMeasure . endingBarStyleEnum = SystemLinesEnumHelper . xmlBarlineStyleToSystemLinesEnum ( stringValue ) ; } } } else if ( xmlNode . name === "sound" ) { try { if ( xmlNode . attribute ( "tempo" ) ) { const tempo = parseFloat ( xmlNode . attribute ( "tempo" ) . value ) ; if ( this . currentXmlMeasureIndex === 0 ) { this . musicSheet . DefaultStartTempoInBpm = tempo ; this . musicSheet . HasBPMInfo = true ; } } } catch ( e ) { log . debug ( "InstrumentReader.readTempoInstruction" , e ) ; } } else if ( xmlNode . name === "harmony" ) { this . openChordSymbolContainers . push ( ChordSymbolReader . readChordSymbol ( xmlNode , this . musicSheet , this . activeKey ) ) ; } } for ( const j in this . voiceGeneratorsDict ) { if ( this . voiceGeneratorsDict . hasOwnProperty ( j ) ) { const voiceGenerator = this . voiceGeneratorsDict [ j ] ; voiceGenerator . checkForOpenBeam ( ) ; } } if ( this . currentXmlMeasureIndex === this . xmlMeasureList . length - 1 ) { for ( let i = 0 ; i < this . instrument . Staves . length ; i ++ ) { if ( ! this . activeClefsHaveBeenInitialized [ i ] ) { this . createDefaultClefInstruction ( this . musicSheet . getGlobalStaffIndexOfFirstStaff ( this . instrument ) + i ) ; } } if ( ! this . activeKeyHasBeenInitialized ) { this . createDefaultKeyInstruction ( ) ; } for ( let i = 0 ; i < this . expressionReaders . length ; i ++ ) { const reader = this . expressionReaders [ i ] ; if ( reader ) { reader . checkForOpenExpressions ( this . currentMeasure , currentFraction ) ; } } } if ( ! this . musicSheet . HasBPMInfo ) { this . currentMeasure . TempoInBPM = 120 ; } else if ( currentMeasure . TempoInBPM === 0 && this . previousMeasure ) { this . currentMeasure . TempoInBPM = this . previousMeasure . TempoInBPM ; } } catch ( e ) { if ( divisionsException ) { throw new MusicSheetReadingException ( e . Message ) ; } const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/MeasureError" , "Error while reading Measure." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; log . debug ( "InstrumentReader.readNextXmlMeasure" , errorMsg , e ) ; } this . previousMeasure = this . currentMeasure ; this . currentXmlMeasureIndex += 1 ; return true ; } return public parseXmlColor ( xmlColorString ) { if ( ! xmlColorString ) { return undefined ; } if ( xmlColorString . length === 7 ) { return xmlColorString ; } else if ( xmlColorString . length === 9 ) { return "#" + xmlColorString . substr ( 3 ) ; } else { return undefined ; } } public doCalculationsAfterDurationHasBeenSet ( ) { for ( const j in this . voiceGeneratorsDict ) { if ( this . voiceGeneratorsDict . hasOwnProperty ( j ) ) { this . voiceGeneratorsDict [ j ] . checkOpenTies ( ) ; } } } param voiceId param staffId returns { VoiceGenerator } private getOrCreateVoiceGenerator ( voiceId , staffId ) { const staff = this . instrument . Staves [ staffId ] ; let voiceGenerator = this . voiceGeneratorsDict [ voiceId ] ; if ( voiceGenerator ) { if ( staff . Voices . indexOf ( voiceGenerator . GetVoice ) === - 1 ) { staff . Voices . push ( voiceGenerator . GetVoice ) ; } } else { const mainVoiceGenerator = this . staffMainVoiceGeneratorDict [ staffId ] ; if ( mainVoiceGenerator ) { voiceGenerator = new VoiceGenerator ( this . pluginManager , staff , voiceId , this . slurReader , mainVoiceGenerator . GetVoice ) ; staff . Voices . push ( voiceGenerator . GetVoice ) ; this . voiceGeneratorsDict [ voiceId ] = voiceGenerator ; } else { voiceGenerator = new VoiceGenerator ( this . pluginManager , staff , voiceId , this . slurReader ) ; staff . Voices . push ( voiceGenerator . GetVoice ) ; this . voiceGeneratorsDict [ voiceId ] = voiceGenerator ; this . staffMainVoiceGeneratorDict [ staffId ] = voiceGenerator ; } } return voiceGenerator ; } private createExpressionGenerators ( numberOfStaves ) { this . expressionReaders = new Array ( numberOfStaves ) ; for ( let i = 0 ; i < numberOfStaves ; i ++ ) { this . expressionReaders [ i ] = new ExpressionReader ( this . musicSheet , this . instrument , i + 1 ) ; } } param staffIndex private createDefaultClefInstruction ( staffIndex ) { let first ; if ( this . musicSheet . SourceMeasures . length > 0 ) { first = this . musicSheet . SourceMeasures [ 0 ] ; } else { first = this . currentMeasure ; } const clefInstruction = new ClefInstruction ( ClefEnum . G , 0 , 2 ) ; let firstStaffEntry ; if ( ! first . FirstInstructionsStaffEntries [ staffIndex ] ) { firstStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; first . FirstInstructionsStaffEntries [ staffIndex ] = firstStaffEntry ; } else { firstStaffEntry = first . FirstInstructionsStaffEntries [ staffIndex ] ; firstStaffEntry . removeFirstInstructionOfTypeClefInstruction ( ) ; } clefInstruction . Parent = firstStaffEntry ; firstStaffEntry . Instructions . splice ( 0 , 0 , clefInstruction ) ; } /**
     * Create the default [[KeyInstruction]] in case no [[KeyInstruction]] is given in the whole [[Instrument]].
     */ private createDefaultKeyInstruction ( ) { let first ; if ( this . musicSheet . SourceMeasures . length > 0 ) { first = this . musicSheet . SourceMeasures [ 0 ] ; } else { first = this . currentMeasure ; } const keyInstruction = new KeyInstruction ( undefined , 0 , KeyEnum . major ) ; for ( let j = this . inSourceMeasureInstrumentIndex ; j < this . inSourceMeasureInstrumentIndex + this . instrument . Staves . length ; j ++ ) { if ( ! first . FirstInstructionsStaffEntries [ j ] ) { const firstStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; first . FirstInstructionsStaffEntries [ j ] = firstStaffEntry ; keyInstruction . Parent = firstStaffEntry ; firstStaffEntry . Instructions . push ( keyInstruction ) ; } else { const firstStaffEntry = first . FirstInstructionsStaffEntries [ j ] ; keyInstruction . Parent = firstStaffEntry ; firstStaffEntry . removeFirstInstructionOfTypeKeyInstruction ( ) ; if ( firstStaffEntry . Instructions [ 0 ] instanceof ClefInstruction ) { firstStaffEntry . Instructions . splice ( 1 , 0 , keyInstruction ) ; } else { firstStaffEntry . Instructions . splice ( 0 , 0 , keyInstruction ) ; } } } } param parentNode param attributesNode returns { boolean } private isAttributesNodeAtBeginOfMeasure ( parentNode , attributesNode ) { const children = parentNode . elements ( ) ; const attributesNodeIndex = children . indexOf ( attributesNode ) ; if ( attributesNodeIndex > 0 && children [ attributesNodeIndex - 1 ] . name === "backup" ) { return true ; } let firstNoteNodeIndex = - 1 ; for ( let i = 0 ; i < children . length ; i ++ ) { if ( children [ i ] . name === "note" ) { firstNoteNodeIndex = i ; break ; } } return ( attributesNodeIndex < firstNoteNodeIndex && firstNoteNodeIndex > 0 ) || ( firstNoteNodeIndex < 0 ) ; } param parentNode param attributesNode returns { boolean } private isAttributesNodeAtEndOfMeasure ( parentNode , attributesNode ) { const childs = parentNode . elements ( ) . slice ( ) ; let attributesNodeIndex = 0 ; for ( let i = 0 ; i < childs . length ; i ++ ) { if ( childs [ i ] === attributesNode ) { attributesNodeIndex = i ; break ; } } let nextNoteNodeIndex = 0 ; for ( let i = attributesNodeIndex ; i < childs . length ; i ++ ) { if ( childs [ i ] . name === "note" ) { nextNoteNodeIndex = i ; break ; } } return attributesNodeIndex > nextNoteNodeIndex ; } param xmlNode returns { Fraction } private getNoteDurationFromTypeNode ( xmlNode ) { const typeNode = xmlNode . element ( "type" ) ; if ( typeNode ) { const type = typeNode . value ; return NoteTypeHandler . getNoteDurationFromType ( type ) ; } return new Fraction ( 0 , 4 * this . divisions ) ; } param attrNode param guitarPro private addAbstractInstruction ( attrNode , guitarPro , previousNode , currentFraction ) { if ( attrNode . element ( "divisions" ) ) { if ( attrNode . elements ( ) . length === 1 ) { return ; } } const transposeNode = attrNode . element ( "transpose" ) ; if ( transposeNode ) { const chromaticNode = transposeNode . element ( "chromatic" ) ; if ( chromaticNode ) { this . instrument . PlaybackTranspose = parseInt ( chromaticNode . value , 10 ) ; } } const clefList = attrNode . elements ( "clef" ) ; let errorMsg ; if ( clefList . length > 0 ) { for ( let idx = 0 , len = clefList . length ; idx < len ; ++ idx ) { const nodeList = clefList [ idx ] ; let clefEnum = ClefEnum . G ; let line = 2 ; let staffNumber = 1 ; let clefOctaveOffset = 0 ; const lineNode = nodeList . element ( "line" ) ; if ( lineNode ) { try { line = parseInt ( lineNode . value , 10 ) ; } catch ( ex ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/ClefLineError" , "Invalid clef line given -> using default clef line." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; line = 2 ; log . debug ( "InstrumentReader.addAbstractInstruction" , errorMsg , ex ) ; } } const signNode = nodeList . element ( "sign" ) ; if ( signNode ) { try { clefEnum = ClefEnum [ signNode . value ] ; if ( ! ClefInstruction . isSupportedClef ( clefEnum ) ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/ClefError" , "Unsupported clef found -> using default clef." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; clefEnum = ClefEnum . G ; line = 2 ; } if ( clefEnum === ClefEnum . TAB ) { clefOctaveOffset = - 1 ; } } catch ( e ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/ClefError" , "Invalid clef found -> using default clef." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; clefEnum = ClefEnum . G ; line = 2 ; log . debug ( "InstrumentReader.addAbstractInstruction" , errorMsg , e ) ; } } const clefOctaveNode = nodeList . element ( "clef-octave-change" ) ; if ( clefOctaveNode ) { try { clefOctaveOffset = parseInt ( clefOctaveNode . value , 10 ) ; } catch ( e ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/ClefOctaveError" , "Invalid clef octave found -> using default clef octave." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; clefOctaveOffset = 0 ; } } if ( nodeList . hasAttributes && nodeList . attribute ( "number" ) ) { try { staffNumber = parseInt ( nodeList . attribute ( "number" ) . value , 10 ) ; if ( staffNumber > this . currentClefNumber ) { staffNumber = this . currentClefNumber ; } this . currentClefNumber = staffNumber + 1 ; } catch ( err ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/ClefError" , "Invalid clef found -> using default clef." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; staffNumber = 1 ; this . currentClefNumber = staffNumber + 1 ; } } const clefInstruction = new ClefInstruction ( clefEnum , clefOctaveOffset , line ) ; this . abstractInstructions . push ( [ staffNumber , clefInstruction , currentFraction ] ) ; } } if ( attrNode . element ( "key" ) !== undefined && this . instrument . MidiInstrumentId !== MidiInstrument . Percussion ) { let key = 0 ; const keyNode = attrNode . element ( "key" ) . element ( "fifths" ) ; if ( keyNode ) { try { key = parseInt ( keyNode . value , 10 ) ; } catch ( ex ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/KeyError" , "Invalid key found -> set to default." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; key = 0 ; log . debug ( "InstrumentReader.addAbstractInstruction" , errorMsg , ex ) ; } } let keyEnum = KeyEnum . none ; let modeNode = attrNode . element ( "key" ) ; if ( modeNode ) { modeNode = modeNode . element ( "mode" ) ; } if ( modeNode ) { try { keyEnum = KeyEnum [ modeNode . value ] ; } catch ( ex ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/KeyError" , "Invalid key found -> set to default." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; keyEnum = KeyEnum . major ; log . debug ( "InstrumentReader.addAbstractInstruction" , errorMsg , ex ) ; } } const keyInstruction = new KeyInstruction ( undefined , key , keyEnum ) ; this . abstractInstructions . push ( [ 1 , keyInstruction , currentFraction ] ) ; } if ( attrNode . element ( "time" ) ) { const timeNode = attrNode . element ( "time" ) ; let symbolEnum = RhythmSymbolEnum . NONE ; let timePrintObject = true ; if ( timeNode !== undefined && timeNode . hasAttributes ) { const symbolAttribute = timeNode . attribute ( "symbol" ) ; if ( symbolAttribute ) { if ( symbolAttribute . value === "common" ) { symbolEnum = RhythmSymbolEnum . COMMON ; } else if ( symbolAttribute . value === "cut" ) { symbolEnum = RhythmSymbolEnum . CUT ; } } const printObjectAttribute = timeNode . attribute ( "print-object" ) ; if ( printObjectAttribute ) { if ( printObjectAttribute . value === "no" ) { timePrintObject = false ; } } } let num = 0 ; let denom = 0 ; const senzaMisura = ( timeNode && timeNode . element ( "senza-misura" ) !== undefined ) ; const timeList = attrNode . elements ( "time" ) ; const beatsList = [ ] ; const typeList = [ ] ; for ( let idx = 0 , len = timeList . length ; idx < len ; ++ idx ) { const xmlNode = timeList [ idx ] ; beatsList . push . apply ( beatsList , xmlNode . elements ( "beats" ) ) ; typeList . push . apply ( typeList , xmlNode . elements ( "beat-type" ) ) ; } if ( ! senzaMisura ) { try { if ( beatsList !== undefined && beatsList . length > 0 && typeList !== undefined && beatsList . length === typeList . length ) { const length = beatsList . length ; const fractions = new Array ( length ) ; let maxDenom = 0 ; for ( let i = 0 ; i < length ; i ++ ) { const s = beatsList [ i ] . value ; let n = 0 ; let d = 0 ; if ( s . indexOf ( "+" ) !== - 1 ) { const numbers = s . split ( "+" ) ; for ( let idx = 0 , len = numbers . length ; idx < len ; ++ idx ) { n += parseInt ( numbers [ idx ] , 10 ) ; } } else { n = parseInt ( s , 10 ) ; } d = parseInt ( typeList [ i ] . value , 10 ) ; maxDenom = Math . max ( maxDenom , d ) ; fractions [ i ] = new Fraction ( n , d , 0 , false ) ; } for ( let i = 0 ; i < length ; i ++ ) { if ( fractions [ i ] . Denominator === maxDenom ) { num += fractions [ i ] . Numerator ; } else { num += ( maxDenom / fractions [ i ] . Denominator ) * fractions [ i ] . Numerator ; } } denom = maxDenom ; } else { num = parseInt ( attrNode . element ( "time" ) . element ( "beats" ) . value , 10 ) ; denom = parseInt ( attrNode . element ( "time" ) . element ( "beat-type" ) . value , 10 ) ; } } catch ( ex ) { errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/RhythmError" , "Invalid rhythm found -> set to default." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; num = 4 ; denom = 4 ; log . debug ( "InstrumentReader.addAbstractInstruction" , errorMsg , ex ) ; } const newRhythmInstruction = new RhythmInstruction ( new Fraction ( num , denom , 0 , false ) , symbolEnum ) ; newRhythmInstruction . PrintObject = timePrintObject ; this . abstractInstructions . push ( [ 1 , newRhythmInstruction , currentFraction ] ) ; } else { this . abstractInstructions . push ( [ 1 , new RhythmInstruction ( new Fraction ( 4 , 4 , 0 , false ) , RhythmSymbolEnum . NONE ) , currentFraction ] ) ; } } } param numberOfStaves param beginOfMeasure private saveAbstractInstructionList ( numberOfStaves , beginOfMeasure ) { for ( let i = this . abstractInstructions . length - 1 ; i >= 0 ; i -- ) { const instruction = this . abstractInstructions [ i ] ; const key = instruction [ 0 ] ; const value = instruction [ 1 ] ; const instructionTimestamp = instruction [ 2 ] ; if ( value instanceof ClefInstruction ) { const clefInstruction = < ClefInstruction > value ; if ( this . currentXmlMeasureIndex === 0 || ( key <= this . activeClefs . length && clefInstruction !== this . activeClefs [ key - 1 ] ) ) { if ( ! beginOfMeasure && this . currentStaffEntry !== undefined && ! this . currentStaffEntry . hasNotes ( ) && key - 1 === this . instrument . Staves . indexOf ( this . currentStaffEntry . ParentStaff ) ) { const newClefInstruction = clefInstruction ; const staffEntry = this . currentStaffEntry ; if ( instructionTimestamp && Math . abs ( instructionTimestamp . RealValue - staffEntry . Timestamp . RealValue ) > 0.01 ) { continue ; } newClefInstruction . Parent = staffEntry ; staffEntry . removeFirstInstructionOfTypeClefInstruction ( ) ; staffEntry . Instructions . push ( newClefInstruction ) ; this . activeClefs [ key - 1 ] = clefInstruction ; this . abstractInstructions . splice ( i , 1 ) ; } else if ( beginOfMeasure ) { if ( instructionTimestamp . RealValue !== 0 ) { continue ; } let firstStaffEntry ; if ( this . currentMeasure ) { const newClefInstruction = clefInstruction ; const sseIndex = this . inSourceMeasureInstrumentIndex + key - 1 ; const firstSse = this . currentMeasure . FirstInstructionsStaffEntries [ sseIndex ] ; if ( this . currentXmlMeasureIndex === 0 ) { if ( ! firstSse ) { firstStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; this . currentMeasure . FirstInstructionsStaffEntries [ sseIndex ] = firstStaffEntry ; newClefInstruction . Parent = firstStaffEntry ; firstStaffEntry . Instructions . push ( newClefInstruction ) ; this . activeClefsHaveBeenInitialized [ key - 1 ] = true ; } else if ( this . currentMeasure . FirstInstructionsStaffEntries [ sseIndex ] !== undefined && ! ( firstSse . Instructions [ 0 ] instanceof ClefInstruction ) ) { firstStaffEntry = firstSse ; newClefInstruction . Parent = firstStaffEntry ; firstStaffEntry . removeFirstInstructionOfTypeClefInstruction ( ) ; firstStaffEntry . Instructions . splice ( 0 , 0 , newClefInstruction ) ; this . activeClefsHaveBeenInitialized [ key - 1 ] = true ; } else { const lastStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; this . currentMeasure . LastInstructionsStaffEntries [ sseIndex ] = lastStaffEntry ; newClefInstruction . Parent = lastStaffEntry ; lastStaffEntry . Instructions . push ( newClefInstruction ) ; } } else if ( ! this . activeClefsHaveBeenInitialized [ key - 1 ] ) { const first = this . musicSheet . SourceMeasures [ 0 ] ; if ( ! first . FirstInstructionsStaffEntries [ sseIndex ] ) { firstStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; } else { firstStaffEntry = first . FirstInstructionsStaffEntries [ sseIndex ] ; firstStaffEntry . removeFirstInstructionOfTypeClefInstruction ( ) ; } newClefInstruction . Parent = firstStaffEntry ; firstStaffEntry . Instructions . splice ( 0 , 0 , newClefInstruction ) ; this . activeClefsHaveBeenInitialized [ key - 1 ] = true ; } else { let previousPrintedMeasure = this . previousMeasure ; if ( this . followingMultirestMeasures > 0 && this . currentMeasure . Rules . RenderMultipleRestMeasures ) { previousPrintedMeasure = this . currentMultirestStartMeasure ; } const lastStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; previousPrintedMeasure . LastInstructionsStaffEntries [ sseIndex ] = lastStaffEntry ; newClefInstruction . Parent = lastStaffEntry ; lastStaffEntry . Instructions . push ( newClefInstruction ) ; } this . activeClefs [ key - 1 ] = clefInstruction ; this . abstractInstructions . splice ( i , 1 ) ; } } else { let lastStaffEntryBefore ; const duration = this . activeRhythm . Rhythm ; if ( duration . RealValue > 0 && instructionTimestamp . RealValue / duration . RealValue > 0.90 ) { if ( ! this . currentMeasure . LastInstructionsStaffEntries [ key - 1 ] ) { this . currentMeasure . LastInstructionsStaffEntries [ key - 1 ] = new SourceStaffEntry ( undefined , this . instrument . Staves [ key - 1 ] ) ; } lastStaffEntryBefore = this . currentMeasure . LastInstructionsStaffEntries [ key - 1 ] ; } if ( lastStaffEntryBefore ) { clefInstruction . Parent = lastStaffEntryBefore ; lastStaffEntryBefore . Instructions . push ( clefInstruction ) ; this . activeClefs [ key - 1 ] = clefInstruction ; this . abstractInstructions . splice ( i , 1 ) ; } } } else if ( key <= this . activeClefs . length && clefInstruction === this . activeClefs [ key - 1 ] ) { this . abstractInstructions . splice ( i , 1 ) ; } } if ( value instanceof KeyInstruction ) { const keyInstruction = < KeyInstruction > value ; if ( ! this . activeKey || this . activeKey . Key !== keyInstruction . Key ) { this . activeKey = keyInstruction ; this . abstractInstructions . splice ( i , 1 ) ; let sourceMeasure ; if ( ! this . activeKeyHasBeenInitialized ) { this . activeKeyHasBeenInitialized = true ; if ( this . currentXmlMeasureIndex > 0 ) { sourceMeasure = this . musicSheet . SourceMeasures [ 0 ] ; } else { sourceMeasure = this . currentMeasure ; } } else { sourceMeasure = this . currentMeasure ; } if ( sourceMeasure ) { for ( let j = this . inSourceMeasureInstrumentIndex ; j < this . inSourceMeasureInstrumentIndex + numberOfStaves ; j ++ ) { const newKeyInstruction = keyInstruction ; if ( ! sourceMeasure . FirstInstructionsStaffEntries [ j ] ) { const firstStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; sourceMeasure . FirstInstructionsStaffEntries [ j ] = firstStaffEntry ; newKeyInstruction . Parent = firstStaffEntry ; firstStaffEntry . Instructions . push ( newKeyInstruction ) ; } else { const firstStaffEntry = sourceMeasure . FirstInstructionsStaffEntries [ j ] ; newKeyInstruction . Parent = firstStaffEntry ; firstStaffEntry . removeFirstInstructionOfTypeKeyInstruction ( ) ; if ( firstStaffEntry . Instructions . length === 0 ) { firstStaffEntry . Instructions . push ( newKeyInstruction ) ; } else { if ( firstStaffEntry . Instructions [ 0 ] instanceof ClefInstruction ) { firstStaffEntry . Instructions . splice ( 1 , 0 , newKeyInstruction ) ; } else { firstStaffEntry . Instructions . splice ( 0 , 0 , newKeyInstruction ) ; } } } } } } else { this . abstractInstructions . splice ( i , 1 ) ; } } if ( value instanceof RhythmInstruction ) { const rhythmInstruction = < RhythmInstruction > value ; if ( ! this . activeRhythm || this . activeRhythm !== rhythmInstruction ) { this . activeRhythm = rhythmInstruction ; this . abstractInstructions . splice ( i , 1 ) ; if ( this . currentMeasure ) { for ( let j = this . inSourceMeasureInstrumentIndex ; j < this . inSourceMeasureInstrumentIndex + numberOfStaves ; j ++ ) { const newRhythmInstruction = rhythmInstruction ; let firstStaffEntry ; if ( ! this . currentMeasure . FirstInstructionsStaffEntries [ j ] ) { firstStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; this . currentMeasure . FirstInstructionsStaffEntries [ j ] = firstStaffEntry ; } else { firstStaffEntry = this . currentMeasure . FirstInstructionsStaffEntries [ j ] ; firstStaffEntry . removeFirstInstructionOfTypeRhythmInstruction ( ) ; } newRhythmInstruction . Parent = firstStaffEntry ; firstStaffEntry . Instructions . push ( newRhythmInstruction ) ; } } } else { this . abstractInstructions . splice ( i , 1 ) ; } } } } /**
     * Save any ClefInstruction given - exceptionally - at the end of the currentMeasure.
     */ private saveClefInstructionAtEndOfMeasure ( ) { for ( let i = this . abstractInstructions . length - 1 ; i >= 0 ; i -- ) { const key = this . abstractInstructions [ i ] [ 0 ] ; const value = this . abstractInstructions [ i ] [ 1 ] ; if ( value instanceof ClefInstruction ) { const clefInstruction = < ClefInstruction > value ; if ( ( ! this . activeClefs [ key - 1 ] ) || ( clefInstruction . ClefType !== this . activeClefs [ key - 1 ] . ClefType || ( clefInstruction . ClefType === this . activeClefs [ key - 1 ] . ClefType && clefInstruction . Line !== this . activeClefs [ key - 1 ] . Line ) ) ) { const lastStaffEntry = new SourceStaffEntry ( undefined , undefined ) ; this . currentMeasure . LastInstructionsStaffEntries [ this . inSourceMeasureInstrumentIndex + key - 1 ] = lastStaffEntry ; const newClefInstruction = clefInstruction ; newClefInstruction . Parent = lastStaffEntry ; lastStaffEntry . Instructions . push ( newClefInstruction ) ; this . activeClefs [ key - 1 ] = clefInstruction ; this . abstractInstructions . splice ( i , 1 ) ; } } } } param xmlNode returns { Fraction } private getNoteDurationForTuplet ( xmlNode ) { let duration = new Fraction ( 0 , 1 ) ; const typeDuration = this . getNoteDurationFromTypeNode ( xmlNode ) ; if ( xmlNode . element ( "time-modification" ) ) { const time = xmlNode . element ( "time-modification" ) ; if ( time ) { if ( time . element ( "actual-notes" ) !== undefined && time . element ( "normal-notes" ) ) { const actualNotes = time . element ( "actual-notes" ) ; const normalNotes = time . element ( "normal-notes" ) ; if ( actualNotes !== undefined && normalNotes ) { const actual = parseInt ( actualNotes . value , 10 ) ; const normal = parseInt ( normalNotes . value , 10 ) ; duration = new Fraction ( normal * typeDuration . Numerator , actual * typeDuration . Denominator ) ; } } } } return duration ; } private readExpressionStaffNumber ( xmlNode ) { let directionStaffNumber = 1 ; if ( xmlNode . element ( "staff" ) ) { const staffNode = xmlNode . element ( "staff" ) ; if ( staffNode ) { try { directionStaffNumber = parseInt ( staffNode . value , 10 ) ; } catch ( ex ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/ExpressionStaffError" , "Invalid Expression staff number -> set to default." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; directionStaffNumber = 1 ; log . debug ( "InstrumentReader.readExpressionStaffNumber" , errorMsg , ex ) ; } } } return directionStaffNumber ; } returns { number } private readDivisionsFromNotes ( ) { let divisionsFromNote = 0 ; let xmlMeasureIndex = this . currentXmlMeasureIndex ; let read = false ; while ( ! read ) { const xmlMeasureListArr = this . xmlMeasureList [ xmlMeasureIndex ] . elements ( ) ; for ( let idx = 0 , len = xmlMeasureListArr . length ; idx < len ; ++ idx ) { const xmlNode = xmlMeasureListArr [ idx ] ; if ( xmlNode . name === "note" && ! xmlNode . element ( "time-modification" ) ) { const durationNode = xmlNode . element ( "duration" ) ; const typeNode = xmlNode . element ( "type" ) ; if ( durationNode !== undefined && typeNode ) { const type = typeNode . value ; let noteDuration = 0 ; try { noteDuration = parseInt ( durationNode . value , 10 ) ; } catch ( ex ) { log . debug ( "InstrumentReader.readDivisionsFromNotes" , ex ) ; continue ; } switch ( type ) { case "1024th" : divisionsFromNote = ( noteDuration / 4 ) * 1024 ; break ; case "512th" : divisionsFromNote = ( noteDuration / 4 ) * 512 ; break ; case "256th" : divisionsFromNote = ( noteDuration / 4 ) * 256 ; break ; case "128th" : divisionsFromNote = ( noteDuration / 4 ) * 128 ; break ; case "64th" : divisionsFromNote = ( noteDuration / 4 ) * 64 ; break ; case "32nd" : divisionsFromNote = ( noteDuration / 4 ) * 32 ; break ; case "16th" : divisionsFromNote = ( noteDuration / 4 ) * 16 ; break ; case "eighth" : divisionsFromNote = ( noteDuration / 4 ) * 8 ; break ; case "quarter" : divisionsFromNote = ( noteDuration / 4 ) * 4 ; break ; case "half" : divisionsFromNote = ( noteDuration / 4 ) * 2 ; break ; case "whole" : divisionsFromNote = ( noteDuration / 4 ) ; break ; case "breve" : divisionsFromNote = ( noteDuration / 4 ) / 2 ; break ; case "long" : divisionsFromNote = ( noteDuration / 4 ) / 4 ; break ; case "maxima" : divisionsFromNote = ( noteDuration / 4 ) / 8 ; break ; default : break ; } } } if ( divisionsFromNote > 0 ) { read = true ; break ; } } if ( divisionsFromNote === 0 ) { xmlMeasureIndex ++ ; if ( xmlMeasureIndex === this . xmlMeasureList . length ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMEssages/DivisionsError" , "Invalid divisions value at Instrument: " ) ; throw new MusicSheetReadingException ( errorMsg + this . instrument . Name ) ; } } } return divisionsFromNote ; } }