import { MusicSheet } from "../MusicSheet" ; import { SourceMeasure } from "../VoiceData/SourceMeasure" ; import { Fraction } from "../../Common/DataObjects/Fraction" ; import { InstrumentReader } from "./InstrumentReader" ; import { IXmlElement } from "../../Common/FileIO/Xml" ; import { Instrument } from "../Instrument" ; import { ITextTranslation } from "../Interfaces/ITextTranslation" ; import { MusicSheetReadingException } from "../Exceptions" ; import log from "loglevel" ; import { IXmlAttribute } from "../../Common/FileIO/Xml" ; import { RhythmInstruction } from "../VoiceData/Instructions/RhythmInstruction" ; import { RhythmSymbolEnum } from "../VoiceData/Instructions/RhythmInstruction" ; import { SourceStaffEntry } from "../VoiceData/SourceStaffEntry" ; import { VoiceEntry } from "../VoiceData/VoiceEntry" ; import { InstrumentalGroup } from "../InstrumentalGroup" ; import { SubInstrument } from "../SubInstrument" ; import { MidiInstrument } from "../VoiceData/Instructions/ClefInstruction" ; import { AbstractNotationInstruction } from "../VoiceData/Instructions/AbstractNotationInstruction" ; import { Label } from "../Label" ; import { MusicSymbolModuleFactory } from "./MusicSymbolModuleFactory" ; import { IAfterSheetReadingModule } from "../Interfaces/IAfterSheetReadingModule" ; import { RepetitionInstructionReader } from "./MusicSymbolModules/RepetitionInstructionReader" ; import { RepetitionCalculator } from "./MusicSymbolModules/RepetitionCalculator" ; import { EngravingRules } from "../Graphical/EngravingRules" ; import { ReaderPluginManager } from "./ReaderPluginManager" ; export class MusicSheetReader { constructor ( afterSheetReadingModules = undefined , rules = new EngravingRules ( ) ) { if ( ! afterSheetReadingModules ) { this . afterSheetReadingModules = [ ] ; } else { this . afterSheetReadingModules = afterSheetReadingModules ; } this . repetitionInstructionReader = MusicSymbolModuleFactory . createRepetitionInstructionReader ( ) ; this . repetitionCalculator = MusicSymbolModuleFactory . createRepetitionCalculator ( ) ; this . rules = rules ; } private repetitionInstructionReader : RepetitionInstructionReader ; private repetitionCalculator : RepetitionCalculator ; private afterSheetReadingModules : IAfterSheetReadingModule [ ] ; private musicSheet : MusicSheet ; private completeNumberOfStaves : number = 0 ; private currentMeasure : SourceMeasure ; private previousMeasure : SourceMeasure ; private currentFraction : Fraction ; private pluginManager : ReaderPluginManager = new ReaderPluginManager ( ) ; public rules : EngravingRules ; public get PluginManager ( ) : ReaderPluginManager { return this . pluginManager ; } public get CompleteNumberOfStaves ( ) : number { return this . completeNumberOfStaves ; } private static doCalculationsAfterDurationHasBeenSet ( instrumentReaders ) { for ( const instrumentReader of instrumentReaders ) { instrumentReader . doCalculationsAfterDurationHasBeenSet ( ) ; } } param root param path returns { MusicSheet } public createMusicSheet ( root , path ) { try { return this . _createMusicSheet ( root , path ) ; } catch ( e ) { log . error ( "MusicSheetReader.CreateMusicSheet" , e ) ; return undefined ; } } private _removeFromArray ( list , elem ) { const i = list . indexOf ( elem ) ; if ( i !== - 1 ) { list . splice ( i , 1 ) ; } } private trimString ( str ) { return str . replace ( /^\s+|\s+$/g , "" ) ; } private _lastElement < T > ( list ) { return list [ list . length - 1 ] ; } private _createMusicSheet ( root , path ) { const instrumentReaders = [ ] ; let sourceMeasureCounter = 0 ; this . musicSheet = new MusicSheet ( ) ; this . musicSheet . Path = path ; this . musicSheet . Rules = this . rules ; if ( ! root ) { throw new MusicSheetReadingException ( "Undefined root element" ) ; } this . pushSheetLabels ( root , path ) ; const partlistNode = root . element ( "part-list" ) ; if ( ! partlistNode ) { throw new MusicSheetReadingException ( "Undefined partListNode" ) ; } const partInst = root . elements ( "part" ) ; const partList = partlistNode . elements ( ) ; this . initializeReading ( partList , partInst , instrumentReaders ) ; let couldReadMeasure = true ; this . currentFraction = new Fraction ( 0 , 1 ) ; let octavePlusOneEncoding = false ; let encoding = root . element ( "identification" ) ; if ( encoding ) { encoding = encoding . element ( "encoding" ) ; } if ( encoding ) { encoding = encoding . element ( "software" ) ; } if ( encoding !== undefined && ( encoding . value === "Guitar Pro 5" ) ) { octavePlusOneEncoding = true ; } while ( couldReadMeasure ) { if ( this . currentMeasure !== undefined && this . currentMeasure . HasEndLine && this . rules . NewPartAndSystemAfterFinalBarline ) { sourceMeasureCounter = 0 ; } this . currentMeasure = new SourceMeasure ( this . completeNumberOfStaves , this . musicSheet . Rules ) ; for ( const instrumentReader of instrumentReaders ) { try { couldReadMeasure = couldReadMeasure && instrumentReader . readNextXmlMeasure ( this . currentMeasure , this . currentFraction , octavePlusOneEncoding ) ; } catch ( e ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/InstrumentError" , "Error while reading instruments." ) ; throw new MusicSheetReadingException ( errorMsg , e ) ; } } if ( couldReadMeasure ) { this . musicSheet . addMeasure ( this . currentMeasure ) ; this . checkIfRhythmInstructionsAreSetAndEqual ( instrumentReaders ) ; this . checkSourceMeasureForNullEntries ( ) ; sourceMeasureCounter = this . setSourceMeasureDuration ( instrumentReaders , sourceMeasureCounter ) ; MusicSheetReader . doCalculationsAfterDurationHasBeenSet ( instrumentReaders ) ; this . currentMeasure . AbsoluteTimestamp = this . currentFraction . clone ( ) ; this . musicSheet . SheetErrors . finalizeMeasure ( this . currentMeasure . MeasureNumber ) ; this . currentFraction . Add ( this . currentMeasure . Duration ) ; this . previousMeasure = this . currentMeasure ; } } if ( this . repetitionInstructionReader ) { this . repetitionInstructionReader . removeRedundantInstructions ( ) ; if ( this . repetitionCalculator ) { this . repetitionCalculator . calculateRepetitions ( this . musicSheet , this . repetitionInstructionReader . repetitionInstructions ) ; } } this . musicSheet . checkForInstrumentWithNoVoice ( ) ; this . musicSheet . fillStaffList ( ) ; for ( let idx = 0 , len = this . afterSheetReadingModules . length ; idx < len ; ++ idx ) { const afterSheetReadingModule = this . afterSheetReadingModules [ idx ] ; afterSheetReadingModule . calculate ( this . musicSheet ) ; } this . musicSheet . userStartTempoInBPM = this . musicSheet . userStartTempoInBPM || this . musicSheet . DefaultStartTempoInBpm ; return this . musicSheet ; } private initializeReading ( partList , partInst , instrumentReaders ) { const instrumentDict = this . createInstrumentGroups ( partList ) ; this . completeNumberOfStaves = this . getCompleteNumberOfStavesFromXml ( partInst ) ; if ( partInst . length !== 0 ) { this . repetitionInstructionReader . MusicSheet = this . musicSheet ; this . currentFraction = new Fraction ( 0 , 1 ) ; this . currentMeasure = undefined ; this . previousMeasure = undefined ; } let counter = 0 ; for ( const node of partInst ) { const idNode = node . attribute ( "id" ) ; if ( idNode ) { const currentInstrument = instrumentDict [ idNode . value ] ; const xmlMeasureList = node . elements ( "measure" ) ; let instrumentNumberOfStaves = 1 ; try { instrumentNumberOfStaves = this . getInstrumentNumberOfStavesFromXml ( node ) ; } catch ( err ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/InstrumentStavesNumberError" , "Invalid number of staves at instrument: " ) ; this . musicSheet . SheetErrors . push ( errorMsg + currentInstrument . Name ) ; continue ; } currentInstrument . createStaves ( instrumentNumberOfStaves ) ; instrumentReaders . push ( new InstrumentReader ( this . pluginManager , this . repetitionInstructionReader , xmlMeasureList , currentInstrument ) ) ; if ( this . repetitionInstructionReader ) { this . repetitionInstructionReader . xmlMeasureList [ counter ] = xmlMeasureList ; } counter ++ ; } } } param instrumentReaders private checkIfRhythmInstructionsAreSetAndEqual ( instrumentReaders ) { const rhythmInstructions = [ ] ; for ( let i = 0 ; i < this . completeNumberOfStaves ; i ++ ) { if ( this . currentMeasure . FirstInstructionsStaffEntries [ i ] ) { const last = this . currentMeasure . FirstInstructionsStaffEntries [ i ] . Instructions [ this . currentMeasure . FirstInstructionsStaffEntries [ i ] . Instructions . length - 1 ] ; if ( last instanceof RhythmInstruction ) { rhythmInstructions . push ( < RhythmInstruction > last ) ; } } } let maxRhythmValue = 0.0 ; let index = - 1 ; for ( let idx = 0 , len = rhythmInstructions . length ; idx < len ; ++ idx ) { const rhythmInstruction = rhythmInstructions [ idx ] ; if ( rhythmInstruction . Rhythm . RealValue > maxRhythmValue ) { if ( this . areRhythmInstructionsMixed ( rhythmInstructions ) && rhythmInstruction . SymbolEnum !== RhythmSymbolEnum . NONE ) { continue ; } maxRhythmValue = rhythmInstruction . Rhythm . RealValue ; index = rhythmInstructions . indexOf ( rhythmInstruction ) ; } } if ( rhythmInstructions . length > 0 && rhythmInstructions . length < this . completeNumberOfStaves ) { const rhythmInstruction = rhythmInstructions [ index ] . clone ( ) ; for ( let i = 0 ; i < this . completeNumberOfStaves ; i ++ ) { if ( this . currentMeasure . FirstInstructionsStaffEntries [ i ] !== undefined && ! ( this . _lastElement ( this . currentMeasure . FirstInstructionsStaffEntries [ i ] . Instructions ) instanceof RhythmInstruction ) ) { this . currentMeasure . FirstInstructionsStaffEntries [ i ] . removeAllInstructionsOfTypeRhythmInstruction ( ) ; this . currentMeasure . FirstInstructionsStaffEntries [ i ] . Instructions . push ( rhythmInstruction . clone ( ) ) ; } if ( ! this . currentMeasure . FirstInstructionsStaffEntries [ i ] ) { this . currentMeasure . FirstInstructionsStaffEntries [ i ] = new SourceStaffEntry ( undefined , undefined ) ; this . currentMeasure . FirstInstructionsStaffEntries [ i ] . Instructions . push ( rhythmInstruction . clone ( ) ) ; } } for ( let idx = 0 , len = instrumentReaders . length ; idx < len ; ++ idx ) { const instrumentReader = instrumentReaders [ idx ] ; instrumentReader . ActiveRhythm = rhythmInstruction ; } } if ( rhythmInstructions . length === 0 && this . currentMeasure === this . musicSheet . SourceMeasures [ 0 ] ) { const rhythmInstruction = new RhythmInstruction ( new Fraction ( 4 , 4 , 0 , false ) , RhythmSymbolEnum . NONE ) ; for ( let i = 0 ; i < this . completeNumberOfStaves ; i ++ ) { if ( ! this . currentMeasure . FirstInstructionsStaffEntries [ i ] ) { this . currentMeasure . FirstInstructionsStaffEntries [ i ] = new SourceStaffEntry ( undefined , undefined ) ; } else { this . currentMeasure . FirstInstructionsStaffEntries [ i ] . removeAllInstructionsOfTypeRhythmInstruction ( ) ; } this . currentMeasure . FirstInstructionsStaffEntries [ i ] . Instructions . push ( rhythmInstruction ) ; } for ( let idx = 0 , len = instrumentReaders . length ; idx < len ; ++ idx ) { const instrumentReader = instrumentReaders [ idx ] ; instrumentReader . ActiveRhythm = rhythmInstruction ; } } for ( let idx = 0 , len = rhythmInstructions . length ; idx < len ; ++ idx ) { const rhythmInstruction = rhythmInstructions [ idx ] ; if ( rhythmInstruction . Rhythm . RealValue < maxRhythmValue ) { if ( this . _lastElement ( this . currentMeasure . FirstInstructionsStaffEntries [ rhythmInstructions . indexOf ( rhythmInstruction ) ] . Instructions ) instanceof RhythmInstruction ) { const instrs = this . currentMeasure . FirstInstructionsStaffEntries [ rhythmInstructions . indexOf ( rhythmInstruction ) ] . Instructions ; instrs [ instrs . length - 1 ] = rhythmInstructions [ index ] . clone ( ) ; } } if ( Math . abs ( rhythmInstruction . Rhythm . RealValue - maxRhythmValue ) < 0.000001 && rhythmInstruction . SymbolEnum !== RhythmSymbolEnum . NONE && this . areRhythmInstructionsMixed ( rhythmInstructions ) ) { rhythmInstruction . SymbolEnum = RhythmSymbolEnum . NONE ; } } } param rhythmInstructions returns { boolean } private areRhythmInstructionsMixed ( rhythmInstructions ) { for ( let i = 1 ; i < rhythmInstructions . length ; i ++ ) { if ( Math . abs ( rhythmInstructions [ i ] . Rhythm . RealValue - rhythmInstructions [ 0 ] . Rhythm . RealValue ) < 0.000001 && rhythmInstructions [ i ] . SymbolEnum !== rhythmInstructions [ 0 ] . SymbolEnum ) { return true ; } } return false ; } param instrumentReaders param sourceMeasureCounter returns { number } private setSourceMeasureDuration ( instrumentReaders , sourceMeasureCounter ) { let activeRhythm = new Fraction ( 0 , 1 ) ; const instrumentsMaxTieNoteFractions = [ ] ; for ( const instrumentReader of instrumentReaders ) { instrumentsMaxTieNoteFractions . push ( instrumentReader . MaxTieNoteFraction ) ; const activeRythmMeasure = instrumentReader . ActiveRhythm . Rhythm ; if ( activeRhythm . lt ( activeRythmMeasure ) ) { activeRhythm = new Fraction ( activeRythmMeasure . Numerator , activeRythmMeasure . Denominator , 0 , false ) ; } } const instrumentsDurations = this . currentMeasure . calculateInstrumentsDuration ( this . musicSheet , instrumentsMaxTieNoteFractions ) ; let maxInstrumentDuration = new Fraction ( 0 , 1 ) ; for ( const instrumentsDuration of instrumentsDurations ) { if ( maxInstrumentDuration . lt ( instrumentsDuration ) ) { maxInstrumentDuration = instrumentsDuration ; } } if ( Fraction . Equal ( maxInstrumentDuration , activeRhythm ) ) { this . checkFractionsForEquivalence ( maxInstrumentDuration , activeRhythm ) ; } else { if ( maxInstrumentDuration . lt ( activeRhythm ) ) { maxInstrumentDuration = this . currentMeasure . reverseCheck ( this . musicSheet , maxInstrumentDuration ) ; this . checkFractionsForEquivalence ( maxInstrumentDuration , activeRhythm ) ; } } this . currentMeasure . ImplicitMeasure = this . checkIfMeasureIsImplicit ( maxInstrumentDuration , activeRhythm ) ; if ( ! this . currentMeasure . ImplicitMeasure ) { sourceMeasureCounter ++ ; } this . currentMeasure . Duration = maxInstrumentDuration ; this . currentMeasure . ActiveTimeSignature = activeRhythm ; this . currentMeasure . MeasureNumber = sourceMeasureCounter ; for ( let i = 0 ; i < instrumentsDurations . length ; i ++ ) { const instrumentsDuration = instrumentsDurations [ i ] ; if ( ( this . currentMeasure . ImplicitMeasure && instrumentsDuration !== maxInstrumentDuration ) || ! Fraction . Equal ( instrumentsDuration , activeRhythm ) && ! this . allInstrumentsHaveSameDuration ( instrumentsDurations , maxInstrumentDuration ) ) { const firstStaffIndexOfInstrument = this . musicSheet . getGlobalStaffIndexOfFirstStaff ( this . musicSheet . Instruments [ i ] ) ; for ( let staffIndex = 0 ; staffIndex < this . musicSheet . Instruments [ i ] . Staves . length ; staffIndex ++ ) { if ( ! this . graphicalMeasureIsEmpty ( firstStaffIndexOfInstrument + staffIndex ) ) { this . currentMeasure . setErrorInGraphicalMeasure ( firstStaffIndexOfInstrument + staffIndex , true ) ; const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/MissingNotesError" , "Given Notes don't correspond to measure duration." ) ; this . musicSheet . SheetErrors . pushMeasureError ( errorMsg ) ; } } } } return sourceMeasureCounter ; } param maxInstrumentDuration param activeRhythm private checkFractionsForEquivalence ( maxInstrumentDuration , activeRhythm ) { if ( activeRhythm . Denominator > maxInstrumentDuration . Denominator ) { const factor = activeRhythm . Denominator / maxInstrumentDuration . Denominator ; maxInstrumentDuration . expand ( factor ) ; } } param maxInstrumentDuration param activeRhythm returns { boolean } private checkIfMeasureIsImplicit ( maxInstrumentDuration , activeRhythm ) { if ( ! this . previousMeasure && maxInstrumentDuration . lt ( activeRhythm ) ) { return true ; } if ( this . previousMeasure ) { return Fraction . plus ( this . previousMeasure . Duration , maxInstrumentDuration ) . Equals ( activeRhythm ) ; } return false ; } param instrumentsDurations param maxInstrumentDuration returns { boolean } private allInstrumentsHaveSameDuration ( instrumentsDurations , maxInstrumentDuration ) { let counter = 0 ; for ( let idx = 0 , len = instrumentsDurations . length ; idx < len ; ++ idx ) { const instrumentsDuration = instrumentsDurations [ idx ] ; if ( instrumentsDuration . Equals ( maxInstrumentDuration ) ) { counter ++ ; } } return ( counter === instrumentsDurations . length && maxInstrumentDuration !== new Fraction ( 0 , 1 ) ) ; } private graphicalMeasureIsEmpty ( index ) { let counter = 0 ; for ( let i = 0 ; i < this . currentMeasure . VerticalSourceStaffEntryContainers . length ; i ++ ) { if ( ! this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] . StaffEntries [ index ] ) { counter ++ ; } } return ( counter === this . currentMeasure . VerticalSourceStaffEntryContainers . length ) ; } /**
     * Check a [[SourceMeasure]] for possible empty / undefined entries ([[VoiceEntry]], [[SourceStaffEntry]], VerticalContainer)
     * (caused from TieAlgorithm removing EndTieNote) and removes them if completely empty / null
     */ private checkSourceMeasureForNullEntries ( ) { for ( let i = this . currentMeasure . VerticalSourceStaffEntryContainers . length - 1 ; i >= 0 ; i -- ) { for ( let j = this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] . StaffEntries . length - 1 ; j >= 0 ; j -- ) { const sourceStaffEntry = this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] . StaffEntries [ j ] ; if ( sourceStaffEntry ) { for ( let k = sourceStaffEntry . VoiceEntries . length - 1 ; k >= 0 ; k -- ) { const voiceEntry = sourceStaffEntry . VoiceEntries [ k ] ; if ( voiceEntry . Notes . length === 0 ) { this . _removeFromArray ( voiceEntry . ParentVoice . VoiceEntries , voiceEntry ) ; this . _removeFromArray ( sourceStaffEntry . VoiceEntries , voiceEntry ) ; } } } if ( sourceStaffEntry !== undefined && sourceStaffEntry . VoiceEntries . length === 0 ) { this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] . StaffEntries [ j ] = undefined ; } } } for ( let i = this . currentMeasure . VerticalSourceStaffEntryContainers . length - 1 ; i >= 0 ; i -- ) { let counter = 0 ; for ( let idx = 0 , len = this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] . StaffEntries . length ; idx < len ; ++ idx ) { const sourceStaffEntry = this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] . StaffEntries [ idx ] ; if ( ! sourceStaffEntry ) { counter ++ ; } } if ( counter === this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] . StaffEntries . length ) { this . _removeFromArray ( this . currentMeasure . VerticalSourceStaffEntryContainers , this . currentMeasure . VerticalSourceStaffEntryContainers [ i ] ) ; } } } param root param filePath private pushSheetLabels ( root , filePath ) { this . readComposer ( root ) ; this . readTitle ( root ) ; try { if ( ! this . musicSheet . Title || ! this . musicSheet . Composer ) { this . readTitleAndComposerFromCredits ( root ) ; } } catch ( ex ) { log . info ( "MusicSheetReader.pushSheetLabels" , "readTitleAndComposerFromCredits" , ex ) ; } try { if ( ! this . musicSheet . Title ) { const barI = Math . max ( 0 , filePath . lastIndexOf ( "/" ) , filePath . lastIndexOf ( "\\" ) ) ; const filename = filePath . substr ( barI ) ; const filenameSplits = filename . split ( "." , 1 ) ; this . musicSheet . Title = new Label ( filenameSplits [ 0 ] ) ; } } catch ( ex ) { log . info ( "MusicSheetReader.pushSheetLabels" , "read title from file name" , ex ) ; } } private presentAttrsWithValue ( elem , val ) { for ( const attr of elem . attributes ( ) ) { if ( attr . value === val ) { return true ; } } return false ; } private readComposer ( root ) { const identificationNode = root . element ( "identification" ) ; if ( identificationNode ) { const creators = identificationNode . elements ( "creator" ) ; for ( let idx = 0 , len = creators . length ; idx < len ; ++ idx ) { const creator = creators [ idx ] ; if ( creator . hasAttributes ) { if ( this . presentAttrsWithValue ( creator , "composer" ) ) { this . musicSheet . Composer = new Label ( this . trimString ( creator . value ) ) ; continue ; } if ( this . presentAttrsWithValue ( creator , "lyricist" ) || this . presentAttrsWithValue ( creator , "poet" ) ) { this . musicSheet . Lyricist = new Label ( this . trimString ( creator . value ) ) ; } } } } } private readTitleAndComposerFromCredits ( root ) { const systemYCoordinates = this . computeSystemYCoordinates ( root ) ; if ( systemYCoordinates === 0 ) { return ; } let largestTitleCreditSize = 1 ; let finalTitle = undefined ; let largestCreditYInfo = 0 ; let finalSubtitle = undefined ; let possibleTitle = undefined ; const creditElements = root . elements ( "credit" ) ; for ( let idx = 0 , len = creditElements . length ; idx < len ; ++ idx ) { const credit = creditElements [ idx ] ; if ( ! credit . attribute ( "page" ) ) { return ; } if ( credit . attribute ( "page" ) . value === "1" ) { let creditChild = undefined ; if ( credit ) { creditChild = credit . element ( "credit-words" ) ; if ( ! creditChild . attribute ( "justify" ) ) { break ; } const creditJustify = creditChild . attribute ( "justify" ) ?. value ; const creditY = creditChild . attribute ( "default-y" ) ?. value ; const creditYGiven = creditY !== undefined && creditY !== null ; const creditYInfo = creditYGiven ? parseFloat ( creditY ) : Number . MIN_VALUE ; if ( creditYGiven && creditYInfo > systemYCoordinates ) { if ( ! this . musicSheet . Title ) { const creditSize = creditChild . attribute ( "font-size" ) . value ; const titleCreditSizeInt = parseFloat ( creditSize ) ; if ( largestTitleCreditSize < titleCreditSizeInt ) { largestTitleCreditSize = titleCreditSizeInt ; finalTitle = creditChild . value ; } } if ( ! this . musicSheet . Subtitle ) { if ( creditJustify !== "right" && creditJustify !== "left" ) { if ( largestCreditYInfo < creditYInfo ) { largestCreditYInfo = creditYInfo ; if ( possibleTitle ) { finalSubtitle = possibleTitle ; possibleTitle = creditChild . value ; } else { possibleTitle = creditChild . value ; } } } } if ( ! ( this . musicSheet . Composer !== undefined && this . musicSheet . Lyricist ) ) { switch ( creditJustify ) { case "right" : this . musicSheet . Composer = new Label ( this . trimString ( creditChild . value ) ) ; break ; case "left" : this . musicSheet . Lyricist = new Label ( this . trimString ( creditChild . value ) ) ; break ; default : break ; } } } } } } if ( ! this . musicSheet . Title && finalTitle ) { this . musicSheet . Title = new Label ( this . trimString ( finalTitle ) ) ; } if ( ! this . musicSheet . Subtitle && finalSubtitle ) { this . musicSheet . Subtitle = new Label ( this . trimString ( finalSubtitle ) ) ; } } private computeSystemYCoordinates ( root ) { if ( ! root . element ( "defaults" ) ) { return 0 ; } let paperHeight = 0 ; let topSystemDistance = 0 ; try { const defi = root . element ( "defaults" ) . element ( "page-layout" ) . element ( "page-height" ) . value ; paperHeight = parseFloat ( defi ) ; } catch ( e ) { log . info ( "MusicSheetReader.computeSystemYCoordinates(): couldn't find page height, not reading title/composer." ) ; return 0 ; } let found = false ; const parts = root . elements ( "part" ) ; for ( let idx = 0 , len = parts . length ; idx < len ; ++ idx ) { const measures = parts [ idx ] . elements ( "measure" ) ; for ( let idx2 = 0 , len2 = measures . length ; idx2 < len2 ; ++ idx2 ) { const measure = measures [ idx2 ] ; if ( measure . element ( "print" ) ) { const systemLayouts = measure . element ( "print" ) . elements ( "system-layout" ) ; for ( let idx3 = 0 , len3 = systemLayouts . length ; idx3 < len3 ; ++ idx3 ) { const syslab = systemLayouts [ idx3 ] ; if ( syslab . element ( "top-system-distance" ) ) { const topSystemDistanceString = syslab . element ( "top-system-distance" ) . value ; topSystemDistance = parseFloat ( topSystemDistanceString ) ; found = true ; break ; } } break ; } } if ( found ) { break ; } } if ( root . element ( "defaults" ) . element ( "system-layout" ) ) { const syslay = root . element ( "defaults" ) . element ( "system-layout" ) ; if ( syslay . element ( "top-system-distance" ) ) { const topSystemDistanceString = root . element ( "defaults" ) . element ( "system-layout" ) . element ( "top-system-distance" ) . value ; topSystemDistance = parseFloat ( topSystemDistanceString ) ; } } if ( topSystemDistance === 0 ) { return 0 ; } return paperHeight - topSystemDistance ; } private readTitle ( root ) { const titleNode = root . element ( "work" ) ; let titleNodeChild = undefined ; if ( titleNode ) { titleNodeChild = titleNode . element ( "work-title" ) ; if ( titleNodeChild && titleNodeChild . value ) { this . musicSheet . Title = new Label ( this . trimString ( titleNodeChild . value ) ) ; } } const movementNode = root . element ( "movement-title" ) ; let finalSubTitle = "" ; if ( movementNode ) { if ( ! this . musicSheet . Title ) { this . musicSheet . Title = new Label ( this . trimString ( movementNode . value ) ) ; } else { finalSubTitle = this . trimString ( movementNode . value ) ; } } if ( titleNode ) { const subtitleNodeChild = titleNode . element ( "work-number" ) ; if ( subtitleNodeChild ) { const workNumber = subtitleNodeChild . value ; if ( workNumber ) { if ( finalSubTitle === "" ) { finalSubTitle = workNumber ; } else { finalSubTitle = finalSubTitle + ", " + workNumber ; } } } } if ( finalSubTitle ) { this . musicSheet . Subtitle = new Label ( finalSubTitle ) ; } } param entryList returns { { } } private createInstrumentGroups ( entryList ) { let instrumentId = 0 ; const instrumentDict = { } ; let currentGroup ; try { const entryArray = entryList ; for ( let idx = 0 , len = entryArray . length ; idx < len ; ++ idx ) { const node = entryArray [ idx ] ; if ( node . name === "score-part" ) { const instrIdString = node . attribute ( "id" ) . value ; const instrument = new Instrument ( instrumentId , instrIdString , this . musicSheet , currentGroup ) ; instrumentId ++ ; const partElements = node . elements ( ) ; for ( let idx2 = 0 , len2 = partElements . length ; idx2 < len2 ; ++ idx2 ) { const partElement = partElements [ idx2 ] ; try { if ( partElement . name === "part-name" ) { instrument . Name = partElement . value ; if ( partElement . attribute ( "print-object" ) && partElement . attribute ( "print-object" ) . value === "no" ) { instrument . NameLabel . print = false ; } } else if ( partElement . name === "part-abbreviation" ) { instrument . PartAbbreviation = partElement . value ; } else if ( partElement . name === "score-instrument" ) { const subInstrument = new SubInstrument ( instrument ) ; subInstrument . idString = partElement . firstAttribute . value ; instrument . SubInstruments . push ( subInstrument ) ; const subElement = partElement . element ( "instrument-name" ) ; if ( subElement ) { subInstrument . name = subElement . value ; subInstrument . setMidiInstrument ( subElement . value ) ; } } else if ( partElement . name === "midi-instrument" ) { let subInstrument = instrument . getSubInstrument ( partElement . firstAttribute . value ) ; for ( let idx3 = 0 , len3 = instrument . SubInstruments . length ; idx3 < len3 ; ++ idx3 ) { const subInstr = instrument . SubInstruments [ idx3 ] ; if ( subInstr . idString === partElement . value ) { subInstrument = subInstr ; break ; } } const instrumentElements = partElement . elements ( ) ; for ( let idx3 = 0 , len3 = instrumentElements . length ; idx3 < len3 ; ++ idx3 ) { const instrumentElement = instrumentElements [ idx3 ] ; try { if ( instrumentElement . name === "midi-channel" ) { if ( parseInt ( instrumentElement . value , 10 ) === 10 ) { instrument . MidiInstrumentId = MidiInstrument . Percussion ; } } else if ( instrumentElement . name === "midi-program" ) { if ( instrument . SubInstruments . length > 0 && instrument . MidiInstrumentId !== MidiInstrument . Percussion ) { subInstrument . midiInstrumentID = < MidiInstrument > Math . max ( 0 , parseInt ( instrumentElement . value , 10 ) - 1 ) ; } } else if ( instrumentElement . name === "midi-unpitched" ) { subInstrument . fixedKey = Math . max ( 0 , parseInt ( instrumentElement . value , 10 ) ) ; } else if ( instrumentElement . name === "volume" ) { try { const result = parseFloat ( instrumentElement . value ) ; subInstrument . volume = result / 127.0 ; } catch ( ex ) { log . debug ( "ExpressionReader.readExpressionParameters" , "read volume" , ex ) ; } } else if ( instrumentElement . name === "pan" ) { try { const result = parseFloat ( instrumentElement . value ) ; subInstrument . pan = result / 64.0 ; } catch ( ex ) { log . debug ( "ExpressionReader.readExpressionParameters" , "read pan" , ex ) ; } } } catch ( ex ) { log . info ( "MusicSheetReader.createInstrumentGroups midi settings: " , ex ) ; } } } } catch ( ex ) { log . info ( "MusicSheetReader.createInstrumentGroups: " , ex ) ; } } if ( instrument . SubInstruments . length === 0 ) { const subInstrument = new SubInstrument ( instrument ) ; instrument . SubInstruments . push ( subInstrument ) ; } instrumentDict [ instrIdString ] = instrument ; if ( currentGroup ) { currentGroup . InstrumentalGroups . push ( instrument ) ; this . musicSheet . Instruments . push ( instrument ) ; } else { this . musicSheet . InstrumentalGroups . push ( instrument ) ; this . musicSheet . Instruments . push ( instrument ) ; } } else { if ( ( node . name === "part-group" ) && ( node . attribute ( "type" ) . value === "start" ) ) { const iG = new InstrumentalGroup ( "group" , this . musicSheet , currentGroup ) ; if ( currentGroup ) { currentGroup . InstrumentalGroups . push ( iG ) ; } else { this . musicSheet . InstrumentalGroups . push ( iG ) ; } currentGroup = iG ; } else { if ( ( node . name === "part-group" ) && ( node . attribute ( "type" ) . value === "stop" ) ) { if ( currentGroup ) { if ( currentGroup . InstrumentalGroups . length === 1 ) { const instr = currentGroup . InstrumentalGroups [ 0 ] ; if ( currentGroup . Parent ) { currentGroup . Parent . InstrumentalGroups . push ( instr ) ; this . _removeFromArray ( currentGroup . Parent . InstrumentalGroups , currentGroup ) ; } else { this . musicSheet . InstrumentalGroups . push ( instr ) ; this . _removeFromArray ( this . musicSheet . InstrumentalGroups , currentGroup ) ; } } currentGroup = currentGroup . Parent ; } } } } } } catch ( e ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/InstrumentError" , "Error while reading Instruments" ) ; throw new MusicSheetReadingException ( errorMsg , e ) ; } for ( let idx = 0 , len = this . musicSheet . Instruments . length ; idx < len ; ++ idx ) { const instrument = this . musicSheet . Instruments [ idx ] ; if ( ! instrument . Name ) { instrument . Name = "Instr. " + instrument . IdString ; } } return instrumentDict ; } param partInst returns { number } private getCompleteNumberOfStavesFromXml ( partInst ) { let num = 0 ; for ( const partNode of partInst ) { const xmlMeasureList = partNode . elements ( "measure" ) ; if ( xmlMeasureList . length > 0 ) { const xmlMeasure = xmlMeasureList [ 0 ] ; if ( xmlMeasure ) { let stavesNode = xmlMeasure . element ( "attributes" ) ; if ( stavesNode ) { stavesNode = stavesNode . element ( "staves" ) ; } if ( ! stavesNode ) { num ++ ; } else { num += parseInt ( stavesNode . value , 10 ) ; } } } } if ( isNaN ( num ) || num <= 0 ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/StaffError" , "Invalid number of staves." ) ; throw new MusicSheetReadingException ( errorMsg ) ; } return num ; } param partNode returns { number } private getInstrumentNumberOfStavesFromXml ( partNode ) { let num = 0 ; const xmlMeasure = partNode . element ( "measure" ) ; if ( xmlMeasure ) { const attributes = xmlMeasure . element ( "attributes" ) ; let staves = undefined ; if ( attributes ) { staves = attributes . element ( "staves" ) ; } if ( ! attributes || ! staves ) { num = 1 ; } else { num = parseInt ( staves . value , 10 ) ; } } if ( isNaN ( num ) || num <= 0 ) { const errorMsg = ITextTranslation . translateText ( "ReaderErrorMessages/StaffError" , "Invalid number of Staves." ) ; throw new MusicSheetReadingException ( errorMsg ) ; } return num ; } }