import { MusicSheetCalculator } from "../MusicSheetCalculator" ; import { VexFlowGraphicalSymbolFactory } from "./VexFlowGraphicalSymbolFactory" ; import { GraphicalMeasure } from "../GraphicalMeasure" ; import { StaffLine } from "../StaffLine" ; import { VoiceEntry } from "../../VoiceData/VoiceEntry" ; import { GraphicalNote } from "../GraphicalNote" ; import { GraphicalStaffEntry } from "../GraphicalStaffEntry" ; import { GraphicalTie } from "../GraphicalTie" ; import { Tie } from "../../VoiceData/Tie" ; import { SourceMeasure } from "../../VoiceData/SourceMeasure" ; import { MultiExpression } from "../../VoiceData/Expressions/MultiExpression" ; import { RepetitionInstruction } from "../../VoiceData/Instructions/RepetitionInstruction" ; import { Beam } from "../../VoiceData/Beam" ; import { ClefInstruction } from "../../VoiceData/Instructions/ClefInstruction" ; import { OctaveEnum , OctaveShift } from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift" ; import { Fraction } from "../../../Common/DataObjects/Fraction" ; import { LyricWord } from "../../VoiceData/Lyrics/LyricsWord" ; import { OrnamentContainer } from "../../VoiceData/OrnamentContainer" ; import { Articulation } from "../../VoiceData/Articulation" ; import { Tuplet } from "../../VoiceData/Tuplet" ; import { VexFlowMeasure } from "./VexFlowMeasure" ; import { VexFlowTextMeasurer } from "./VexFlowTextMeasurer" ; import Vex from "vexflow" ; import log from "loglevel" ; import { unitInPixels } from "./VexFlowMusicSheetDrawer" ; import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote" ; import { TechnicalInstruction } from "../../VoiceData/Instructions/TechnicalInstruction" ; import { GraphicalLyricEntry } from "../GraphicalLyricEntry" ; import { GraphicalLabel } from "../GraphicalLabel" ; import { LyricsEntry } from "../../VoiceData/Lyrics/LyricsEntry" ; import { GraphicalLyricWord } from "../GraphicalLyricWord" ; import { VexFlowStaffEntry } from "./VexFlowStaffEntry" ; import { VexFlowOctaveShift } from "./VexFlowOctaveShift" ; import { VexFlowInstantaneousDynamicExpression } from "./VexFlowInstantaneousDynamicExpression" ; import { Slur } from "../../VoiceData/Expressions/ContinuousExpressions/Slur" ; import { PointF2D } from "../../../Common/DataObjects/PointF2D" ; import { TextAlignmentEnum , TextAlignment } from "../../../Common/Enums/TextAlignment" ; import { GraphicalSlur } from "../GraphicalSlur" ; import { BoundingBox } from "../BoundingBox" ; import { ContinuousDynamicExpression } from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression" ; import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression" ; import { InstantaneousTempoExpression , TempoEnum } from "../../VoiceData/Expressions/InstantaneousTempoExpression" ; import { AlignRestOption } from "../../../OpenSheetMusicDisplay/OSMDOptions" ; import { VexFlowStaffLine } from "./VexFlowStaffLine" ; import { EngravingRules } from "../EngravingRules" ; import { VexflowStafflineNoteCalculator } from "./VexflowStafflineNoteCalculator" ; import { MusicSystem } from "../MusicSystem" ; import { NoteTypeHandler } from "../../VoiceData/NoteType" ; import { VexFlowConverter } from "./VexFlowConverter" ; import { TabNote } from "../../VoiceData/TabNote" ; import { PlacementEnum } from "../../VoiceData/Expressions" ; import { GraphicalChordSymbolContainer } from "../GraphicalChordSymbolContainer" ; import { RehearsalExpression } from "../../VoiceData/Expressions/RehearsalExpression" ; export class VexFlowMusicSheetCalculator extends MusicSheetCalculator { /** space needed for a dash for lyrics spacing, calculated once */ private dashSpace : number ; public beamsNeedUpdate : boolean = false ; constructor ( rules ) { super ( ) ; this . rules = rules ; MusicSheetCalculator . symbolFactory = new VexFlowGraphicalSymbolFactory ( ) ; MusicSheetCalculator . TextMeasurer = new VexFlowTextMeasurer ( this . rules ) ; MusicSheetCalculator . stafflineNoteCalculator = new VexflowStafflineNoteCalculator ( this . rules ) ; if ( this . rules . DefaultVexFlowNoteFont ?. toLowerCase ( ) === "gonville" ) { ( Vex . Flow as any ) . DEFAULT_FONT_STACK = [ ( Vex . Flow as any ) . Fonts ?. Gonville , ( Vex . Flow as any ) . Fonts ?. Bravura , ( Vex . Flow as any ) . Fonts ?. Custom ] ; } else if ( this . rules . DefaultVexFlowNoteFont ?. toLowerCase ( ) === "petaluma" ) { ( Vex . Flow as any ) . DEFAULT_FONT_STACK = [ ( Vex . Flow as any ) . Fonts ?. Petaluma , ( Vex . Flow as any ) . Fonts ?. Gonville , ( Vex . Flow as any ) . Fonts ?. Bravura ] ; } } protected clearRecreatedObjects ( ) { super . clearRecreatedObjects ( ) ; MusicSheetCalculator . stafflineNoteCalculator = new VexflowStafflineNoteCalculator ( this . rules ) ; for ( const graphicalMeasures of this . graphicalMusicSheet . MeasureList ) { for ( const graphicalMeasure of graphicalMeasures ) { ( < VexFlowMeasure > graphicalMeasure ) ?. clean ( ) ; } } } protected formatMeasures ( ) { for ( const verticalMeasureList of this . graphicalMusicSheet . MeasureList ) { if ( ! verticalMeasureList || ! verticalMeasureList [ 0 ] ) { continue ; } const firstMeasure = verticalMeasureList [ 0 ] as VexFlowMeasure ; firstMeasure . format ( ) ; for ( const measure of verticalMeasureList ) { for ( const staffEntry of measure . staffEntries ) { ( < VexFlowStaffEntry > staffEntry ) . calculateXPosition ( ) ; } if ( true || this . beamsNeedUpdate ) { ( measure as VexFlowMeasure ) . finalizeBeams ( ) ; } } } this . beamsNeedUpdate = false ; } param measures returns protected calculateMeasureXLayout ( measures ) { const visibleMeasures = [ ] ; for ( const measure of measures ) { if ( measure ) { visibleMeasures . push ( measure ) ; } } if ( visibleMeasures . length === 0 ) { return 0 ; } measures = visibleMeasures ; const allVoices = [ ] ; const formatter = new Vex . Flow . Formatter ( { softmaxFactor : this . rules . SoftmaxFactorVexFlow } ) ; let maxStaffEntries = measures [ 0 ] . staffEntries . length ; let maxStaffEntriesPlusAccidentals = 1 ; for ( const measure of measures ) { if ( ! measure ) { continue ; } let measureAccidentals = 0 ; for ( const staffEntry of measure . staffEntries ) { measureAccidentals += ( staffEntry as VexFlowStaffEntry ) . setMaxAccidentals ( ) ; } if ( measure . parentSourceMeasure . ImplicitMeasure ) { maxStaffEntries = Math . max ( measure . staffEntries . length , maxStaffEntries ) ; maxStaffEntriesPlusAccidentals = Math . max ( measure . staffEntries . length + measureAccidentals , maxStaffEntriesPlusAccidentals ) ; } const mvoices = ( measure as VexFlowMeasure ) . vfVoices ; const voices = [ ] ; for ( const voiceID in mvoices ) { if ( mvoices . hasOwnProperty ( voiceID ) ) { voices . push ( mvoices [ voiceID ] ) ; allVoices . push ( mvoices [ voiceID ] ) ; } } if ( voices . length === 0 ) { log . debug ( "Found a measure with no voices. Continuing anyway." , mvoices ) ; continue ; } formatter . joinVoices ( voices ) ; } let minStaffEntriesWidth = 12 ; const parentSourceMeasure = measures [ 0 ] . parentSourceMeasure ; const staffEntryFactor = 0.3 ; if ( allVoices . length > 0 ) { minStaffEntriesWidth = formatter . preCalculateMinTotalWidth ( allVoices ) / unitInPixels * this . rules . VoiceSpacingMultiplierVexflow + this . rules . VoiceSpacingAddendVexflow + maxStaffEntries * staffEntryFactor ; if ( parentSourceMeasure ?. ImplicitMeasure ) { minStaffEntriesWidth = parentSourceMeasure . Duration . RealValue / parentSourceMeasure . ActiveTimeSignature . RealValue * minStaffEntriesWidth ; if ( maxStaffEntries > 1 ) { minStaffEntriesWidth += maxStaffEntriesPlusAccidentals * staffEntryFactor * 1.5 ; } minStaffEntriesWidth *= this . rules . PickupMeasureWidthMultiplier ; } MusicSheetCalculator . setMeasuresMinStaffEntriesWidth ( measures , minStaffEntriesWidth ) ; const formatVoicesDefault = ( w , p ) => { formatter . formatToStave ( allVoices , p . getVFStave ( ) ) ; } ; const formatVoicesAlignRests = ( w , p ) => { formatter . formatToStave ( allVoices , p . getVFStave ( ) , { align_rests : true , context : undefined } ) ; } ; for ( const measure of measures ) { if ( this . rules . AlignRests === AlignRestOption . Never ) { ( measure as VexFlowMeasure ) . formatVoices = formatVoicesDefault ; } else if ( this . rules . AlignRests === AlignRestOption . Always ) { ( measure as VexFlowMeasure ) . formatVoices = formatVoicesAlignRests ; } else if ( this . rules . AlignRests === AlignRestOption . Auto ) { let alignRests = false ; for ( const staffEntry of measure . staffEntries ) { let collidableVoiceEntries = 0 ; let numberOfRests = 0 ; for ( const voiceEntry of staffEntry . graphicalVoiceEntries ) { if ( ! voiceEntry . parentVoiceEntry . IsGrace ) { if ( voiceEntry && voiceEntry . notes && voiceEntry . notes [ 0 ] && voiceEntry . notes [ 0 ] . sourceNote ) { if ( voiceEntry . notes [ 0 ] . sourceNote . PrintObject ) { collidableVoiceEntries ++ ; } } } if ( voiceEntry && voiceEntry . notes && voiceEntry . notes [ 0 ] && voiceEntry . notes [ 0 ] . sourceNote ) { if ( voiceEntry . notes [ 0 ] . sourceNote . isRest ( ) && voiceEntry . notes [ 0 ] . sourceNote . PrintObject ) { numberOfRests ++ ; } } if ( collidableVoiceEntries > 1 && numberOfRests >= 1 ) { alignRests = true ; break ; } } if ( alignRests ) { break ; } } if ( alignRests ) { ( measure as VexFlowMeasure ) . formatVoices = formatVoicesAlignRests ; } else { ( measure as VexFlowMeasure ) . formatVoices = formatVoicesDefault ; } } if ( measure === measures [ 0 ] ) { const vexflowMeasure = ( measure as VexFlowMeasure ) ; vexflowMeasure . formatVoices ( minStaffEntriesWidth * unitInPixels , vexflowMeasure ) ; } else { } } } for ( const graphicalMeasure of measures ) { if ( ! graphicalMeasure ) { continue ; } for ( const staffEntry of graphicalMeasure . staffEntries ) { ( < VexFlowStaffEntry > staffEntry ) . calculateXPosition ( ) ; } } for ( const measure of measures ) { if ( ! measure ) { continue ; } const mvoices = ( measure as VexFlowMeasure ) . vfVoices ; const voices = [ ] ; for ( const voiceID in mvoices ) { if ( mvoices . hasOwnProperty ( voiceID ) ) { voices . push ( mvoices [ voiceID ] ) ; } } if ( voices . length === 0 ) { log . debug ( "Found a measure with no voices. Continuing anyway." , mvoices ) ; continue ; } formatter . joinVoices ( voices ) ; } return minStaffEntriesWidth ; } private calculateElongationFactor ( containers , staffEntry , lastEntryDict , oldMinimumStaffEntriesWidth , elongationFactorForMeasureWidth , measureNumber , oldMinSpacing , nextMeasureOverlap ) { let newElongationFactorForMeasureWidth = elongationFactorForMeasureWidth ; let currentContainerIndex = 0 ; for ( const container of containers ) { const alignment = container . GraphicalLabel . Label . textAlignment ; let minSpacing = oldMinSpacing ; let overlapAllowedIntoNextMeasure = nextMeasureOverlap ; if ( container instanceof GraphicalLyricEntry && container . ParentLyricWord ) { if ( container . LyricsEntry . SyllableIndex > 0 ) { minSpacing = this . rules . BetweenSyllableMinimumDistance ; if ( TextAlignment . IsCenterAligned ( alignment ) ) { minSpacing += 1.0 ; } } const syllables = container . ParentLyricWord . GetLyricWord . Syllables ; if ( syllables . length > 1 ) { if ( container . LyricsEntry . SyllableIndex < syllables . length - 1 ) { if ( this . dashSpace === undefined ) { this . dashSpace = 1.5 ; } overlapAllowedIntoNextMeasure -= this . dashSpace ; } } } const bBox = container instanceof GraphicalLyricEntry ? container . GraphicalLabel . PositionAndShape : container . PositionAndShape ; const labelWidth = bBox . Size . width ; const staffEntryXPosition = ( staffEntry as VexFlowStaffEntry ) . PositionAndShape . RelativePosition . x ; const xPosition = staffEntryXPosition + bBox . BorderMarginLeft ; if ( lastEntryDict [ currentContainerIndex ] !== undefined ) { if ( lastEntryDict [ currentContainerIndex ] . extend ) { } } let spacingNeededToLastContainer ; let currentSpacingToLastContainer ; if ( lastEntryDict [ currentContainerIndex ] ) { currentSpacingToLastContainer = xPosition - lastEntryDict [ currentContainerIndex ] . xPosition ; } let currentSpacingToMeasureEnd ; let spacingNeededToMeasureEnd ; const maxXInMeasure = oldMinimumStaffEntriesWidth * elongationFactorForMeasureWidth ; if ( TextAlignment . IsCenterAligned ( alignment ) ) { overlapAllowedIntoNextMeasure /= 4 ; currentSpacingToMeasureEnd = maxXInMeasure - xPosition ; spacingNeededToMeasureEnd = ( labelWidth / 2 ) - overlapAllowedIntoNextMeasure ; if ( lastEntryDict [ currentContainerIndex ] ) { spacingNeededToLastContainer = lastEntryDict [ currentContainerIndex ] . labelWidth / 2 + labelWidth / 2 + minSpacing ; } } else if ( TextAlignment . IsLeft ( alignment ) ) { currentSpacingToMeasureEnd = maxXInMeasure - xPosition ; spacingNeededToMeasureEnd = labelWidth - overlapAllowedIntoNextMeasure ; if ( lastEntryDict [ currentContainerIndex ] ) { spacingNeededToLastContainer = lastEntryDict [ currentContainerIndex ] . labelWidth + minSpacing ; } } let elongationFactorForMeasureWidthForCurrentContainer = 1 ; const elongationFactorNeededForMeasureEnd = spacingNeededToMeasureEnd / currentSpacingToMeasureEnd ; let elongationFactorNeededForLastContainer = 1 ; if ( container instanceof GraphicalLyricEntry && container . LyricsEntry ) { if ( lastEntryDict [ currentContainerIndex ] ) { const lastNoteDuration = lastEntryDict [ currentContainerIndex ] . sourceNoteDuration ; elongationFactorNeededForLastContainer = spacingNeededToLastContainer / currentSpacingToLastContainer ; if ( ( lastNoteDuration . Denominator ) > 4 ) { elongationFactorNeededForLastContainer *= 1.1 ; } } } else if ( lastEntryDict [ currentContainerIndex ] ) { elongationFactorNeededForLastContainer = spacingNeededToLastContainer / currentSpacingToLastContainer ; } elongationFactorForMeasureWidthForCurrentContainer = Math . max ( elongationFactorNeededForMeasureEnd , elongationFactorNeededForLastContainer ) ; newElongationFactorForMeasureWidth = Math . max ( newElongationFactorForMeasureWidth , elongationFactorForMeasureWidthForCurrentContainer ) ; let overlap = Math . max ( ( spacingNeededToLastContainer - currentSpacingToLastContainer ) || 0 , 0 ) ; if ( lastEntryDict [ currentContainerIndex ] ) { overlap += lastEntryDict [ currentContainerIndex ] . cumulativeOverlap ; } lastEntryDict [ currentContainerIndex ] = { cumulativeOverlap : overlap , extend : container instanceof GraphicalLyricEntry ? container . LyricsEntry . extend : false , labelWidth : labelWidth , measureNumber : measureNumber , sourceNoteDuration : container instanceof GraphicalLyricEntry ? ( container . LyricsEntry && container . LyricsEntry . Parent . Notes [ 0 ] . Length ) : false , text : container instanceof GraphicalLyricEntry ? container . LyricsEntry . Text : container . GraphicalLabel . Label . text , xPosition : xPosition , } ; currentContainerIndex ++ ; } return newElongationFactorForMeasureWidth ; } public calculateElongationFactorFromStaffEntries ( staffEntries , oldMinimumStaffEntriesWidth , elongationFactorForMeasureWidth , measureNumber ) { interface EntryInfo { cumulativeOverlap : number ; extend : boolean ; labelWidth : number ; xPosition : number ; sourceNoteDuration : Fraction ; text : string ; measureNumber : number ; } interface EntryDict { [ i ] : EntryInfo ; } let newElongationFactorForMeasureWidth = elongationFactorForMeasureWidth ; const lastLyricEntryDict = { } ; const lastChordEntryDict = { } ; for ( const staffEntry of staffEntries ) { if ( staffEntry . LyricsEntries . length > 0 ) { newElongationFactorForMeasureWidth = this . calculateElongationFactor ( staffEntry . LyricsEntries , staffEntry , lastLyricEntryDict , oldMinimumStaffEntriesWidth , newElongationFactorForMeasureWidth , measureNumber , this . rules . HorizontalBetweenLyricsDistance , this . rules . LyricOverlapAllowedIntoNextMeasure ) ; } if ( staffEntry . graphicalChordContainers . length > 0 ) { newElongationFactorForMeasureWidth = this . calculateElongationFactor ( staffEntry . graphicalChordContainers , staffEntry , lastChordEntryDict , oldMinimumStaffEntriesWidth , newElongationFactorForMeasureWidth , measureNumber , this . rules . ChordSymbolXSpacing , this . rules . ChordOverlapAllowedIntoNextMeasure ) ; } } return newElongationFactorForMeasureWidth ; } public calculateMeasureWidthFromStaffEntries ( measuresVertical , oldMinimumStaffEntriesWidth ) { let elongationFactorForMeasureWidth = 1 ; for ( const measure of measuresVertical ) { if ( ! measure || measure . staffEntries . length === 0 ) { continue ; } elongationFactorForMeasureWidth = this . calculateElongationFactorFromStaffEntries ( measure . staffEntries , oldMinimumStaffEntriesWidth , elongationFactorForMeasureWidth , measure . MeasureNumber ) ; } elongationFactorForMeasureWidth = Math . min ( elongationFactorForMeasureWidth , this . rules . MaximumLyricsElongationFactor ) ; const newMinimumStaffEntriesWidth = oldMinimumStaffEntriesWidth * elongationFactorForMeasureWidth ; return newMinimumStaffEntriesWidth ; } protected createGraphicalTie ( tie , startGse , endGse , startNote , endNote ) { return new GraphicalTie ( tie , startNote , endNote ) ; } protected updateStaffLineBorders ( staffLine ) { staffLine . SkyBottomLineCalculator . updateStaffLineBorders ( ) ; } protected graphicalMeasureCreatedCalculations ( measure ) { ( measure as VexFlowMeasure ) . rules = this . rules ; ( measure as VexFlowMeasure ) . graphicalMeasureCreatedCalculations ( ) ; } param voiceEntry param graphicalNotes param graphicalStaffEntry param hasPitchedNote protected layoutVoiceEntry ( voiceEntry , graphicalNotes , graphicalStaffEntry , hasPitchedNote ) { for ( let i = 0 ; i < graphicalNotes . length ; i ++ ) { graphicalNotes [ i ] = MusicSheetCalculator . stafflineNoteCalculator . positionNote ( graphicalNotes [ i ] ) ; } } param graphicalStaffEntry protected layoutStaffEntry ( graphicalStaffEntry ) { ( graphicalStaffEntry . parentMeasure as VexFlowMeasure ) . layoutStaffEntry ( graphicalStaffEntry ) ; } /**
     * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
     */ protected initGraphicalMeasuresCreation ( ) { return ; } param articulations param voiceEntry param graphicalStaffEntry protected layoutArticulationMarks ( articulations , voiceEntry , graphicalStaffEntry ) { return ; } param tie param tieIsAtSystemBreak param isTab protected layoutGraphicalTie ( tie , tieIsAtSystemBreak , isTab ) { const startNote = ( tie . StartNote as VexFlowGraphicalNote ) ; const endNote = ( tie . EndNote as VexFlowGraphicalNote ) ; let vfStartNote = undefined ; let startNoteIndexInTie = 0 ; if ( startNote && startNote . vfnote && startNote . vfnote . length >= 2 ) { vfStartNote = startNote . vfnote [ 0 ] ; startNoteIndexInTie = startNote . vfnote [ 1 ] ; } let vfEndNote = undefined ; let endNoteIndexInTie = 0 ; if ( endNote && endNote . vfnote && endNote . vfnote . length >= 2 ) { vfEndNote = endNote . vfnote [ 0 ] ; endNoteIndexInTie = endNote . vfnote [ 1 ] ; } if ( tieIsAtSystemBreak ) { if ( vfStartNote ) { const vfTie1 = new Vex . Flow . StaveTie ( { first_indices : [ startNoteIndexInTie ] , first_note : vfStartNote } ) ; const measure1 = ( startNote . parentVoiceEntry . parentStaffEntry . parentMeasure as VexFlowMeasure ) ; measure1 . vfTies . push ( vfTie1 ) ; } if ( vfEndNote ) { const vfTie2 = new Vex . Flow . StaveTie ( { last_indices : [ endNoteIndexInTie ] , last_note : vfEndNote } ) ; const measure2 = ( endNote . parentVoiceEntry . parentStaffEntry . parentMeasure as VexFlowMeasure ) ; measure2 . vfTies . push ( vfTie2 ) ; } } else { if ( vfStartNote || vfEndNote ) { let vfTie ; if ( isTab ) { if ( tie . Tie . Type === "S" ) { const startTieNote = < TabNote > tie . StartNote . sourceNote ; const endTieNote = < TabNote > tie . EndNote . sourceNote ; let slideDirection = 1 ; if ( startTieNote . FretNumber > endTieNote . FretNumber ) { slideDirection = - 1 ; } vfTie = new Vex . Flow . TabSlide ( { first_indices : [ startNoteIndexInTie ] , first_note : vfStartNote , last_indices : [ endNoteIndexInTie ] , last_note : vfEndNote , } , slideDirection ) ; } else { vfTie = new Vex . Flow . TabTie ( { first_indices : [ startNoteIndexInTie ] , first_note : vfStartNote , last_indices : [ endNoteIndexInTie ] , last_note : vfEndNote , } , tie . Tie . Type ) ; } } else { vfTie = new Vex . Flow . StaveTie ( { first_indices : [ startNoteIndexInTie ] , first_note : vfStartNote , last_indices : [ endNoteIndexInTie ] , last_note : vfEndNote } ) ; if ( tie . Tie . TieDirection === PlacementEnum . Below ) { vfTie . setDirection ( 1 ) ; } else if ( tie . Tie . TieDirection === PlacementEnum . Above ) { vfTie . setDirection ( - 1 ) ; } } const measure = ( endNote . parentVoiceEntry . parentStaffEntry . parentMeasure as VexFlowMeasure ) ; measure . vfTies . push ( vfTie ) ; } } } protected calculateDynamicExpressionsForMultiExpression ( multiExpression , measureIndex , staffIndex ) { if ( measureIndex < this . rules . MinMeasureToDrawIndex || measureIndex > this . rules . MaxMeasureToDrawIndex ) { return ; } const absoluteTimestamp = multiExpression . AbsoluteTimestamp ; const measures = this . graphicalMusicSheet . MeasureList [ measureIndex ] ; const staffLine = measures [ staffIndex ] . ParentStaffLine ; const startMeasure = measures [ staffIndex ] ; const startPosInStaffline = this . getRelativePositionInStaffLineFromTimestamp ( absoluteTimestamp , staffIndex , staffLine , staffLine ?. isPartOfMultiStaffInstrument ( ) ) ; const dynamicStartPosition = startPosInStaffline ; if ( startPosInStaffline . x <= 0 ) { dynamicStartPosition . x = startMeasure . beginInstructionsWidth + this . rules . RhythmRightMargin ; } if ( multiExpression . InstantaneousDynamic ) { const graphicalInstantaneousDynamic = new VexFlowInstantaneousDynamicExpression ( multiExpression . InstantaneousDynamic , staffLine , startMeasure ) ; this . calculateGraphicalInstantaneousDynamicExpression ( graphicalInstantaneousDynamic , dynamicStartPosition , absoluteTimestamp ) ; this . dynamicExpressionMap . set ( absoluteTimestamp . RealValue , graphicalInstantaneousDynamic . PositionAndShape ) ; } if ( multiExpression . StartingContinuousDynamic ) { const continuousDynamic = multiExpression . StartingContinuousDynamic ; const graphicalContinuousDynamic = new VexFlowContinuousDynamicExpression ( multiExpression . StartingContinuousDynamic , staffLine , startMeasure . parentSourceMeasure ) ; graphicalContinuousDynamic . StartMeasure = startMeasure ; if ( ! graphicalContinuousDynamic . IsVerbal && continuousDynamic . EndMultiExpression ) { try { this . calculateGraphicalContinuousDynamic ( graphicalContinuousDynamic , dynamicStartPosition ) ; graphicalContinuousDynamic . updateSkyBottomLine ( ) ; } catch ( e ) { } } else if ( graphicalContinuousDynamic . IsVerbal ) { this . calculateGraphicalVerbalContinuousDynamic ( graphicalContinuousDynamic , dynamicStartPosition ) ; } else { log . warn ( "This continuous dynamic is not covered" ) ; } } } protected createMetronomeMark ( metronomeExpression ) { const measureNumber = Math . max ( metronomeExpression . ParentMultiTempoExpression . SourceMeasureParent . MeasureNumber - 1 , 0 ) ; const staffNumber = Math . max ( metronomeExpression . StaffNumber - 1 , 0 ) ; const firstMetronomeMark = measureNumber === 0 && staffNumber === 0 ; const vfStave = ( this . graphicalMusicSheet . MeasureList [ measureNumber ] [ staffNumber ] as VexFlowMeasure ) . getVFStave ( ) ; let vexflowDuration = "q" ; if ( metronomeExpression . beatUnit ) { const duration = NoteTypeHandler . getNoteDurationFromType ( metronomeExpression . beatUnit ) ; vexflowDuration = VexFlowConverter . durations ( duration , false ) [ 0 ] ; } let yShift = this . rules . MetronomeMarkYShift ; let hasExpressionsAboveStaffline = false ; for ( const expression of metronomeExpression . parentMeasure . TempoExpressions ) { const isMetronomeExpression = expression . InstantaneousTempo ?. Enum === TempoEnum . metronomeMark ; if ( expression . getPlacementOfFirstEntry ( ) === PlacementEnum . Above && ! isMetronomeExpression ) { hasExpressionsAboveStaffline = true ; break ; } } if ( hasExpressionsAboveStaffline ) { yShift -= 1.4 ; } const skyline = this . graphicalMusicSheet . MeasureList [ 0 ] [ 0 ] . ParentStaffLine ?. SkyLine ; vfStave . setTempo ( { bpm : metronomeExpression . TempoInBpm , dots : metronomeExpression . dotted , duration : vexflowDuration } , yShift * unitInPixels ) ; const xShift = firstMetronomeMark ? this . rules . MetronomeMarkXShift * unitInPixels : 0 ; ( < any > vfStave . getModifiers ( ) [ vfStave . getModifiers ( ) . length - 1 ] ) . setShiftX ( xShift ) ; if ( skyline ) { skyline [ 0 ] = Math . min ( skyline [ 0 ] , - 4.5 + yShift ) ; } } protected calculateRehearsalMark ( measure ) { const rehearsalExpression = measure . rehearsalExpression ; if ( ! rehearsalExpression ) { return ; } const firstMeasureNumber = this . graphicalMusicSheet . MeasureList [ 0 ] [ 0 ] . MeasureNumber ; const measureNumber = Math . max ( measure . MeasureNumber - firstMeasureNumber , 0 ) ; const staffNumber = 0 ; const vfStave = ( this . graphicalMusicSheet . MeasureList [ measureNumber ] [ staffNumber ] as VexFlowMeasure ) ?. getVFStave ( ) ; if ( ! vfStave ) { return ; } const yOffset = - this . rules . RehearsalMarkYOffsetDefault - this . rules . RehearsalMarkYOffset ; let xOffset = this . rules . RehearsalMarkXOffsetDefault + this . rules . RehearsalMarkXOffset ; if ( measure . IsSystemStartMeasure ) { xOffset += this . rules . RehearsalMarkXOffsetSystemStartMeasure ; } const fontSize = this . rules . RehearsalMarkFontSize ; ( vfStave as any ) . setSection ( rehearsalExpression . label , yOffset , xOffset , fontSize ) ; } param sourceMeasure param multiExpression param measureIndex param staffIndex protected calculateSingleOctaveShift ( sourceMeasure , multiExpression , measureIndex , staffIndex ) { const octaveShift = multiExpression . OctaveShiftStart ; const startTimeStamp = octaveShift . ParentStartMultiExpression . Timestamp ; const endTimeStamp = octaveShift . ParentEndMultiExpression ?. Timestamp ; const minMeasureToDrawIndex = this . rules . MinMeasureToDrawIndex ; const maxMeasureToDrawIndex = this . rules . MaxMeasureToDrawIndex ; let startStaffLine = this . graphicalMusicSheet . MeasureList [ measureIndex ] [ staffIndex ] . ParentStaffLine ; if ( ! startStaffLine ) { startStaffLine = this . graphicalMusicSheet . MeasureList [ minMeasureToDrawIndex ] [ staffIndex ] . ParentStaffLine ; } let endMeasure = undefined ; if ( octaveShift . ParentEndMultiExpression ) { endMeasure = this . graphicalMusicSheet . getGraphicalMeasureFromSourceMeasureAndIndex ( octaveShift . ParentEndMultiExpression . SourceMeasureParent , staffIndex ) ; } else { endMeasure = this . graphicalMusicSheet . getLastGraphicalMeasureFromIndex ( staffIndex , true ) ; } if ( endMeasure . MeasureNumber > maxMeasureToDrawIndex + 1 ) { endMeasure = this . graphicalMusicSheet . getLastGraphicalMeasureFromIndex ( staffIndex , true ) ; } let startMeasure = undefined ; if ( octaveShift . ParentEndMultiExpression ) { startMeasure = this . graphicalMusicSheet . getGraphicalMeasureFromSourceMeasureAndIndex ( octaveShift . ParentStartMultiExpression . SourceMeasureParent , staffIndex ) ; } else { startMeasure = this . graphicalMusicSheet . MeasureList [ minMeasureToDrawIndex ] [ staffIndex ] ; } if ( startMeasure . MeasureNumber < minMeasureToDrawIndex + 1 ) { startMeasure = this . graphicalMusicSheet . MeasureList [ minMeasureToDrawIndex ] [ staffIndex ] ; } if ( startMeasure . MeasureNumber < minMeasureToDrawIndex + 1 || startMeasure . MeasureNumber > maxMeasureToDrawIndex + 1 || endMeasure . MeasureNumber < minMeasureToDrawIndex + 1 || endMeasure . MeasureNumber > maxMeasureToDrawIndex + 1 ) { return ; } let endStaffLine = endMeasure . ParentStaffLine ; if ( ! endStaffLine ) { endStaffLine = startStaffLine ; } if ( endMeasure && startStaffLine && endStaffLine ) { const graphicalOctaveShift = new VexFlowOctaveShift ( octaveShift , startStaffLine . PositionAndShape ) ; if ( ! graphicalOctaveShift . startNote ) { let startGse ; for ( const gse of startMeasure . staffEntries ) { if ( gse ) { startGse = gse ; break ; } } if ( ! startGse ) { return ; } graphicalOctaveShift . setStartNote ( startGse ) ; if ( ! graphicalOctaveShift . startNote ) { return ; } } if ( ! graphicalOctaveShift . endNote ) { let endGse ; for ( let i = endMeasure . staffEntries . length - 1 ; i >= 0 ; i ++ ) { if ( endMeasure . staffEntries [ i ] ) { endGse = endMeasure . staffEntries [ i ] ; break ; } } graphicalOctaveShift . setEndNote ( endGse ) ; if ( ! graphicalOctaveShift . endNote ) { return ; } } let startStaffEntry = startMeasure . findGraphicalStaffEntryFromTimestamp ( startTimeStamp ) ; if ( ! startStaffEntry ) { startStaffEntry = startMeasure . staffEntries [ 0 ] ; } let endStaffEntry = endMeasure . findGraphicalStaffEntryFromTimestamp ( endTimeStamp ) ; if ( ! endStaffEntry ) { endStaffEntry = endMeasure . staffEntries [ endMeasure . staffEntries . length - 1 ] ; } graphicalOctaveShift . setStartNote ( startStaffEntry ) ; if ( endStaffLine !== startStaffLine ) { graphicalOctaveShift . endsOnDifferentStaffLine = true ; let lastMeasureOfFirstShift = startStaffLine . Measures [ startStaffLine . Measures . length - 1 ] ; if ( lastMeasureOfFirstShift === undefined ) { lastMeasureOfFirstShift = endMeasure ; } const lastNoteOfFirstShift = lastMeasureOfFirstShift . staffEntries [ lastMeasureOfFirstShift . staffEntries . length - 1 ] ; graphicalOctaveShift . setEndNote ( lastNoteOfFirstShift ) ; const systemsInBetweenCount = endStaffLine . ParentMusicSystem . Id - startStaffLine . ParentMusicSystem . Id ; if ( systemsInBetweenCount > 0 ) { for ( let i = startStaffLine . ParentMusicSystem . Id ; i < endStaffLine . ParentMusicSystem . Id ; i ++ ) { const idx = i + 1 ; const nextShiftMusicSystem = this . musicSystems [ idx ] ; const nextShiftStaffline = nextShiftMusicSystem . StaffLines [ staffIndex ] ; const nextShiftFirstMeasure = nextShiftStaffline . Measures [ 0 ] ; const nextOctaveShift = new VexFlowOctaveShift ( octaveShift , nextShiftFirstMeasure . PositionAndShape ) ; if ( i < systemsInBetweenCount ) { nextOctaveShift . endsOnDifferentStaffLine = true ; } let nextShiftLastMeasure = nextShiftStaffline . Measures [ nextShiftStaffline . Measures . length - 1 ] ; if ( nextShiftLastMeasure . IsExtraGraphicalMeasure ) { nextShiftLastMeasure = nextShiftStaffline . Measures [ nextShiftStaffline . Measures . length - 2 ] ; } const firstNote = nextShiftFirstMeasure . staffEntries [ 0 ] ; let lastNote = nextShiftLastMeasure . staffEntries [ nextShiftLastMeasure . staffEntries . length - 1 ] ; if ( endMeasure . ParentStaffLine === nextShiftStaffline ) { nextShiftLastMeasure = endMeasure ; lastNote = endStaffEntry ; } const logPrefix = "VexFlowMusicSheetCalculator.calculateSingleOctaveShift: " ; if ( ! firstNote ) { log . warn ( logPrefix + "no firstNote found" ) ; } if ( ! lastNote ) { log . warn ( logPrefix + "no lastNote found" ) ; } nextOctaveShift . setStartNote ( firstNote ) ; nextOctaveShift . setEndNote ( lastNote ) ; nextShiftStaffline . OctaveShifts . push ( nextOctaveShift ) ; this . calculateOctaveShiftSkyBottomLine ( firstNote , lastNote , nextOctaveShift , nextShiftStaffline ) ; } } this . calculateOctaveShiftSkyBottomLine ( startStaffEntry , lastNoteOfFirstShift , graphicalOctaveShift , startStaffLine ) ; } else { graphicalOctaveShift . setEndNote ( endStaffEntry ) ; this . calculateOctaveShiftSkyBottomLine ( startStaffEntry , endStaffEntry , graphicalOctaveShift , startStaffLine ) ; } startStaffLine . OctaveShifts . push ( graphicalOctaveShift ) ; } else { log . warn ( "End measure or staffLines for octave shift are undefined! This should not happen!" ) ; } } private calculateOctaveShiftSkyBottomLine ( startStaffEntry , endStaffEntry , vfOctaveShift , parentStaffline ) { if ( ! endStaffEntry ) { log . warn ( "octaveshift: no endStaffEntry" ) ; return ; } let startXOffset = startStaffEntry . PositionAndShape . Size . width ; let endXOffset = endStaffEntry . PositionAndShape . Size . width ; if ( startStaffEntry . hasOnlyRests ( ) ) { startXOffset = - startXOffset ; } else { startXOffset /= 2 ; } if ( ! endStaffEntry . hasOnlyRests ( ) ) { endXOffset /= 2 ; } else { endXOffset *= 2 ; } if ( startStaffEntry === endStaffEntry ) { endXOffset *= 2 ; } const startX = startStaffEntry . PositionAndShape . AbsolutePosition . x - startXOffset ; const stopX = endStaffEntry . PositionAndShape . AbsolutePosition . x + endXOffset ; vfOctaveShift . PositionAndShape . Size . width = startX - stopX ; const textBracket = vfOctaveShift . getTextBracket ( ) ; const fontSize = ( textBracket as any ) . font . size / 10 ; if ( ( < any > textBracket ) . position === Vex . Flow . TextBracket . Positions . TOP ) { const headroom = Math . ceil ( parentStaffline . SkyBottomLineCalculator . getSkyLineMinInRange ( startX , stopX ) ) ; if ( headroom === Infinity ) { return ; } ( textBracket . start . getStave ( ) . options as any ) . top_text_position = Math . abs ( headroom ) ; parentStaffline . SkyBottomLineCalculator . updateSkyLineInRange ( startX , stopX , headroom - fontSize * 2 ) ; } else { const footroom = parentStaffline . SkyBottomLineCalculator . getBottomLineMaxInRange ( startX , stopX ) ; if ( footroom === Infinity ) { return ; } ( textBracket . start . getStave ( ) . options as any ) . bottom_text_position = footroom ; parentStaffline . SkyBottomLineCalculator . updateBottomLineInRange ( startX , stopX , footroom + fontSize * 1.5 ) ; } } param repetitionInstruction param measureIndex protected calculateWordRepetitionInstruction ( repetitionInstruction , measureIndex ) { let uppermostMeasure = undefined ; const measures = < VexFlowMeasure [ ] > this . graphicalMusicSheet . MeasureList [ measureIndex ] ; for ( let idx = 0 , len = measures . length ; idx < len ; ++ idx ) { const graphicalMeasure = measures [ idx ] ; if ( graphicalMeasure && graphicalMeasure . ParentStaffLine && graphicalMeasure . ParentStaff . ParentInstrument . Visible ) { uppermostMeasure = < VexFlowMeasure > graphicalMeasure ; break ; } } if ( uppermostMeasure ) { uppermostMeasure . addWordRepetition ( repetitionInstruction ) ; } } /**
     * Re-adjust the x positioning of expressions. Update the skyline afterwards
     */ protected calculateExpressionAlignements ( ) { for ( const musicSystem of this . musicSystems ) { for ( const staffLine of musicSystem . StaffLines ) { try { ( < VexFlowStaffLine > staffLine ) . AlignmentManager . alignDynamicExpressions ( ) ; staffLine . AbstractExpressions . forEach ( ae => { ae . updateSkyBottomLine ( ) ; } ) ; } catch ( e ) { } } } } param tiedGraphicalNote param beams param activeClef param octaveShiftValue param graphicalStaffEntry param duration param openTie param isLastTieNote protected handleTiedGraphicalNote ( tiedGraphicalNote , beams , activeClef , octaveShiftValue , graphicalStaffEntry , duration , openTie , isLastTieNote ) { return ; } param graphicalNote param beam param openBeams protected handleBeam ( graphicalNote , beam , openBeams ) { ( graphicalNote . parentVoiceEntry . parentStaffEntry . parentMeasure as VexFlowMeasure ) . handleBeam ( graphicalNote , beam ) ; } protected handleVoiceEntryLyrics ( voiceEntry , graphicalStaffEntry , lyricWords ) { voiceEntry . LyricsEntries . forEach ( ( key , lyricsEntry ) => { const graphicalLyricEntry = new GraphicalLyricEntry ( lyricsEntry , graphicalStaffEntry , this . rules . LyricsHeight , this . rules . StaffHeight ) ; graphicalStaffEntry . LyricsEntries . push ( graphicalLyricEntry ) ; const graphicalLabel = graphicalLyricEntry . GraphicalLabel ; graphicalLabel . setLabelPositionAndShapeBorders ( ) ; if ( lyricsEntry . Word ) { const lyricsEntryIndex = lyricsEntry . Word . Syllables . indexOf ( lyricsEntry ) ; let index = lyricWords . indexOf ( lyricsEntry . Word ) ; if ( index === - 1 ) { lyricWords . push ( lyricsEntry . Word ) ; index = lyricWords . indexOf ( lyricsEntry . Word ) ; } if ( this . graphicalLyricWords . length === 0 || index > this . graphicalLyricWords . length - 1 ) { const graphicalLyricWord = new GraphicalLyricWord ( lyricsEntry . Word ) ; graphicalLyricEntry . ParentLyricWord = graphicalLyricWord ; graphicalLyricWord . GraphicalLyricsEntries [ lyricsEntryIndex ] = graphicalLyricEntry ; this . graphicalLyricWords . push ( graphicalLyricWord ) ; } else { const graphicalLyricWord = this . graphicalLyricWords [ index ] ; graphicalLyricEntry . ParentLyricWord = graphicalLyricWord ; graphicalLyricWord . GraphicalLyricsEntries [ lyricsEntryIndex ] = graphicalLyricEntry ; if ( graphicalLyricWord . isFilled ( ) ) { lyricWords . splice ( index , 1 ) ; this . graphicalLyricWords . splice ( this . graphicalLyricWords . indexOf ( graphicalLyricWord ) , 1 ) ; } } } } ) ; } protected handleVoiceEntryOrnaments ( ornamentContainer , voiceEntry , graphicalStaffEntry ) { return ; } param articulations param voiceEntry param graphicalStaffEntry protected handleVoiceEntryArticulations ( articulations , voiceEntry , staffEntry ) { return ; } param technicalInstructions param voiceEntry param staffEntry protected handleVoiceEntryTechnicalInstructions ( technicalInstructions , voiceEntry , staffEntry ) { return ; } param graphicalNote param tuplet param openTuplets protected handleTuplet ( graphicalNote , tuplet , openTuplets ) { ( graphicalNote . parentVoiceEntry . parentStaffEntry . parentMeasure as VexFlowMeasure ) . handleTuplet ( graphicalNote , tuplet ) ; } param gSlurs param slur public findIndexGraphicalSlurFromSlur ( gSlurs , slur ) { for ( let slurIndex = 0 ; slurIndex < gSlurs . length ; slurIndex ++ ) { if ( gSlurs [ slurIndex ] . slur === slur ) { return slurIndex ; } } return - 1 ; } protected calculateSlurs ( ) { const openSlursDict = { } ; for ( const graphicalMeasure of this . graphicalMusicSheet . MeasureList [ 0 ] ) { openSlursDict [ graphicalMeasure . ParentStaff . idInMusicSheet ] = [ ] ; } for ( const musicSystem of this . musicSystems ) { for ( const staffLine of musicSystem . StaffLines ) { const openGraphicalSlurs = openSlursDict [ staffLine . ParentStaff . idInMusicSheet ] ; for ( let slurIndex = 0 ; slurIndex < openGraphicalSlurs . length ; slurIndex ++ ) { const oldGSlur = openGraphicalSlurs [ slurIndex ] ; const newGSlur = new GraphicalSlur ( oldGSlur . slur , this . rules ) ; staffLine . addSlurToStaffline ( newGSlur ) ; openGraphicalSlurs [ slurIndex ] = newGSlur ; } for ( const graphicalMeasure of staffLine . Measures ) { for ( const graphicalStaffEntry of graphicalMeasure . staffEntries ) { for ( const graphicalVoiceEntry of graphicalStaffEntry . graphicalVoiceEntries ) { for ( const graphicalNote of graphicalVoiceEntry . notes ) { for ( const slur of graphicalNote . sourceNote . NoteSlurs ) { if ( ! slur . EndNote || ! slur . StartNote ) { continue ; } if ( slur . StartNote === graphicalNote . sourceNote ) { const gSlur = new GraphicalSlur ( slur , this . rules ) ; openGraphicalSlurs . push ( gSlur ) ; staffLine . addSlurToStaffline ( gSlur ) ; } if ( slur . EndNote === graphicalNote . sourceNote ) { const index = this . findIndexGraphicalSlurFromSlur ( openGraphicalSlurs , slur ) ; if ( index >= 0 ) { const gSlur = openGraphicalSlurs [ index ] ; if ( gSlur . staffEntries . indexOf ( graphicalStaffEntry ) === - 1 ) { gSlur . staffEntries . push ( graphicalStaffEntry ) ; } openGraphicalSlurs . splice ( index , 1 ) ; } } } } } for ( const gSlur of openGraphicalSlurs ) { if ( gSlur . staffEntries . indexOf ( graphicalStaffEntry ) === - 1 ) { gSlur . staffEntries . push ( graphicalStaffEntry ) ; } } } } } } for ( const musicSystem of this . musicSystems ) { for ( const staffLine of musicSystem . StaffLines ) { const sortedGSlurs = staffLine . GraphicalSlurs . sort ( GraphicalSlur . Compare ) ; for ( const gSlur of sortedGSlurs ) { if ( gSlur . slur . isCrossed ( ) ) { continue ; } gSlur . calculateCurve ( this . rules ) ; } } } } }