import Vex from "vexflow" ; import { GraphicalMeasure } from "../GraphicalMeasure" ; import { SourceMeasure } from "../../VoiceData/SourceMeasure" ; import { Staff } from "../../VoiceData/Staff" ; import { StaffLine } from "../StaffLine" ; import { SystemLinesEnum } from "../SystemLinesEnum" ; import { ClefInstruction , ClefEnum } from "../../VoiceData/Instructions/ClefInstruction" ; import { KeyInstruction } from "../../VoiceData/Instructions/KeyInstruction" ; import { RhythmInstruction } from "../../VoiceData/Instructions/RhythmInstruction" ; import { VexFlowConverter } from "./VexFlowConverter" ; import { VexFlowStaffEntry } from "./VexFlowStaffEntry" ; import { Beam } from "../../VoiceData/Beam" ; import { GraphicalNote } from "../GraphicalNote" ; import { GraphicalStaffEntry } from "../GraphicalStaffEntry" ; import StaveConnector = Vex . Flow . StaveConnector ; import StaveNote = Vex . Flow . StaveNote ; import StemmableNote = Vex . Flow . StemmableNote ; import NoteSubGroup = Vex . Flow . NoteSubGroup ; import log from "loglevel" ; import { unitInPixels } from "./VexFlowMusicSheetDrawer" ; import { Tuplet } from "../../VoiceData/Tuplet" ; import { RepetitionInstructionEnum , RepetitionInstruction , AlignmentType } from "../../VoiceData/Instructions/RepetitionInstruction" ; import { SystemLinePosition } from "../SystemLinePosition" ; import { StemDirectionType } from "../../VoiceData/VoiceEntry" ; import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry" ; import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry" ; import { Fraction } from "../../../Common/DataObjects/Fraction" ; import { Voice } from "../../VoiceData/Voice" ; import { EngravingRules } from "../EngravingRules" ; import { OrnamentContainer } from "../../VoiceData/OrnamentContainer" ; import { TechnicalInstruction } from "../../VoiceData/Instructions/TechnicalInstruction" ; import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression" ; import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote" ; import { AutoBeamOptions } from "../../../OpenSheetMusicDisplay/OSMDOptions" ; import { SkyBottomLineCalculator } from "../SkyBottomLineCalculator" ; import { NoteType } from "../../VoiceData/NoteType" ; import { Arpeggio } from "../../VoiceData/Arpeggio" ; export class VexFlowMeasure extends GraphicalMeasure { constructor ( staff , sourceMeasure = undefined , staffLine = undefined ) { super ( staff , sourceMeasure , staffLine ) ; this . minimumStaffEntriesWidth = - 1 ; if ( staffLine ) { this . rules = staffLine . ParentMusicSystem . rules ; } else if ( sourceMeasure ) { this . rules = sourceMeasure . Rules ; } this . resetLayout ( ) ; } public isTabMeasure : boolean = false ; /** octaveOffset according to active clef */ public octaveOffset : number = 3 ; /** The VexFlow Voices in the measure */ public vfVoices : { [ voiceID ] : Vex . Flow . Voice ; } = { } ; /** Call this function (if present) to x-format all the voices in the measure */ public formatVoices : ( width : number , parent : VexFlowMeasure ) => void ; /** The VexFlow Ties in the measure */ public vfTies : Vex . Flow . StaveTie [ ] = [ ] ; /** The repetition instructions given as words or symbols (coda, dal segno..) */ public vfRepetitionWords : Vex . Flow . Repetition [ ] = [ ] ; /** The VexFlow Stave (= one measure in a staffline) */ protected stave : Vex . Flow . Stave ; /** VexFlow StaveConnectors (vertical lines) */ protected connectors : Vex . Flow . StaveConnector [ ] = [ ] ; /** Intermediate object to construct beams */ private beams : { [ voiceID ] : [ Beam , VexFlowVoiceEntry [ ] ] [ ] ; } = { } ; /** Beams created by (optional) autoBeam function. */ private autoVfBeams : Vex . Flow . Beam [ ] ; /** Beams of tuplet notes created by (optional) autoBeam function. */ private autoTupletVfBeams : Vex . Flow . Beam [ ] ; /** VexFlow Beams */ private vfbeams : { [ voiceID ] : Vex . Flow . Beam [ ] ; } ; /** Intermediate object to construct tuplets */ protected tuplets : { [ voiceID ] : [ Tuplet , VexFlowVoiceEntry [ ] ] [ ] ; } = { } ; /** VexFlow Tuplets */ private vftuplets : { [ voiceID ] : Vex . Flow . Tuplet [ ] ; } = { } ; public rules : EngravingRules ; public setAbsoluteCoordinates ( x , y ) { this . stave . setX ( x ) . setY ( y ) ; } /**
     * Reset all the geometric values and parameters of this measure and put it in an initialized state.
     * This is needed to evaluate a measure a second time by system builder.
     */ public resetLayout ( ) { this . stave = new Vex . Flow . Stave ( 0 , 0 , 0 , { fill_style : this . rules . StaffLineColor , space_above_staff_ln : 0 , space_below_staff_ln : 0 } ) ; ( this . stave as any ) . MeasureNumber = this . MeasureNumber ; if ( this . ParentStaff ) { this . setLineNumber ( this . ParentStaff . StafflineCount ) ; } this . stave . setBegBarType ( Vex . Flow . Barline . type . NONE ) ; if ( this . parentSourceMeasure && this . parentSourceMeasure . endingBarStyleEnum === SystemLinesEnum . None ) { this . stave . setEndBarType ( Vex . Flow . Barline . type . NONE ) ; } this . updateInstructionWidth ( ) ; } public clean ( ) { this . vfTies . length = 0 ; this . connectors = [ ] ; this . resetLayout ( ) ; } param line returns public getLineWidth ( line ) { switch ( line ) { case SystemLinesEnum . BoldThinDots : case SystemLinesEnum . DotsThinBold : return 10.0 / unitInPixels ; case SystemLinesEnum . DotsBoldBoldDots : return 10.0 / unitInPixels + this . rules . RepeatEndStartPadding ; default : return 0 ; } } param clef public addClefAtBegin ( clef ) { this . octaveOffset = clef . OctaveOffset ; if ( clef . ClefType === ClefEnum . TAB ) { this . stave . addClef ( "tab" , undefined , undefined , undefined ) ; } else { const vfclef = VexFlowConverter . Clef ( clef , "default" ) ; this . stave . addClef ( vfclef . type , vfclef . size , vfclef . annotation , Vex . Flow . StaveModifier . Position . BEGIN ) ; } this . updateInstructionWidth ( ) ; } param lineNumber public setLineNumber ( lineNumber ) { if ( lineNumber !== 5 ) { if ( lineNumber === 0 ) { ( this . stave as any ) . setNumLines ( 0 ) ; this . stave . getBottomLineY = function ( ) : number { return this . getYForLine ( this . options . num_lines ) ; } ; } else if ( lineNumber === 1 ) { ( this . stave . options as any ) . line_config = [ { visible : false } , { visible : false } , { visible : true } , { visible : false } , { visible : false } , ] ; this . stave . getBottomLineY = function ( ) : number { return this . getYForLine ( 2 ) ; } ; } else if ( lineNumber === 2 ) { ( this . stave . options as any ) . line_config = [ { visible : false } , { visible : false } , { visible : true } , { visible : true } , { visible : false } , ] ; this . stave . getBottomLineY = function ( ) : number { return this . getYForLine ( 3 ) ; } ; } else if ( lineNumber === 3 ) { ( this . stave . options as any ) . line_config = [ { visible : false } , { visible : true } , { visible : true } , { visible : true } , { visible : false } , ] ; this . stave . getBottomLineY = function ( ) : number { return this . getYForLine ( 2 ) ; } ; } else { ( this . stave as any ) . setNumLines ( lineNumber ) ; this . stave . getBottomLineY = function ( ) : number { return this . getYForLine ( this . options . num_lines ) ; } ; } } } param currentKey param previousKey param currentClef public addKeyAtBegin ( currentKey , previousKey , currentClef ) { if ( ! this . rules . RenderKeySignatures ) { return ; } this . stave . setKeySignature ( VexFlowConverter . keySignature ( currentKey ) , VexFlowConverter . keySignature ( previousKey ) , undefined ) ; this . updateInstructionWidth ( ) ; } param rhythm public addRhythmAtBegin ( rhythm ) { const timeSig = VexFlowConverter . TimeSignature ( rhythm ) ; this . stave . addModifier ( timeSig , Vex . Flow . StaveModifier . Position . BEGIN ) ; this . updateInstructionWidth ( ) ; } param clef public addClefAtEnd ( clef , visible = true ) { const vfclef = VexFlowConverter . Clef ( clef , "small" ) ; if ( ! visible && ( this . stave as any ) . endClef ) { return ; } this . stave . setEndClef ( vfclef . type , vfclef . size , vfclef . annotation ) ; for ( const modifier of this . stave . getModifiers ( ) ) { if ( ! visible ) { if ( modifier . getCategory ( ) === "clefs" && modifier . getPosition ( ) === Vex . Flow . StaveModifier . Position . END ) { if ( ( modifier as any ) . type === vfclef . type ) { const transparentStyle = "#12345600" ; const originalStyle = ( modifier as any ) . getStyle ( ) ; if ( originalStyle ) { ( modifier as any ) . originalStrokeStyle = originalStyle . strokeStyle ; ( modifier as any ) . originalFillStyle = originalStyle . fillStyle ; } ( modifier as any ) . setStyle ( { strokeStyle : transparentStyle , fillStyle : transparentStyle } ) ; } } } else { const originalStrokeStyle = ( modifier as any ) . originalStrokeStyle ; const originalFillStyle = ( modifier as any ) . originalFillStyle ; if ( ( modifier as any ) . getStyle ( ) ) { if ( originalStrokeStyle && originalFillStyle ) { ( ( modifier as any ) . getStyle ( ) as any ) . strokeStyle = originalStrokeStyle ; ( ( modifier as any ) . getStyle ( ) as any ) . fillStyle = originalFillStyle ; } else { ( ( modifier as any ) . getStyle ( ) as any ) . strokeStyle = null ; ( ( modifier as any ) . getStyle ( ) as any ) . fillStyle = null ; } } } } this . parentSourceMeasure . hasEndClef = true ; return this . updateInstructionWidth ( ) ; } public addMeasureLine ( lineType , linePosition , renderInitialLine = true ) { switch ( linePosition ) { case SystemLinePosition . MeasureBegin : switch ( lineType ) { case SystemLinesEnum . BoldThinDots : if ( ! renderInitialLine ) { ( this . stave as any ) . modifiers [ 0 ] . draw = function ( stave ) : void { ( stave as any ) . checkContext ( ) ; this . setRendered ( ) ; switch ( this . type ) { case Vex . Flow . Barline . type . SINGLE : this . drawVerticalBar ( stave , this . x , false ) ; break ; case Vex . Flow . Barline . type . DOUBLE : this . drawVerticalBar ( stave , this . x , true ) ; break ; case Vex . Flow . Barline . type . END : this . drawVerticalEndBar ( stave , this . x ) ; break ; case Vex . Flow . Barline . type . REPEAT_BEGIN : this . drawRepeatBar ( stave , this . x , true ) ; break ; case Vex . Flow . Barline . type . REPEAT_END : this . drawRepeatBar ( stave , this . x , false ) ; break ; case Vex . Flow . Barline . type . REPEAT_BOTH : this . drawRepeatBar ( stave , this . x , false ) ; this . drawRepeatBar ( stave , this . x , true ) ; break ; default : break ; } } ; } this . stave . setBegBarType ( Vex . Flow . Barline . type . REPEAT_BEGIN ) ; break ; default : break ; } break ; case SystemLinePosition . MeasureEnd : switch ( lineType ) { case SystemLinesEnum . DotsBoldBoldDots : this . stave . setEndBarType ( Vex . Flow . Barline . type . REPEAT_BOTH ) ; break ; case SystemLinesEnum . DotsThinBold : this . stave . setEndBarType ( Vex . Flow . Barline . type . REPEAT_END ) ; break ; case SystemLinesEnum . DoubleThin : this . stave . setEndBarType ( Vex . Flow . Barline . type . DOUBLE ) ; break ; case SystemLinesEnum . ThinBold : this . stave . setEndBarType ( Vex . Flow . Barline . type . END ) ; break ; case SystemLinesEnum . None : this . stave . setEndBarType ( Vex . Flow . Barline . type . NONE ) ; break ; default : break ; } break ; default : break ; } } /**
     * Adds a measure number to the top left corner of the measure
     * This method is not used currently in favor of the calculateMeasureNumberPlacement
     * method in the MusicSheetCalculator.ts
     */ public addMeasureNumber ( ) { const text = this . MeasureNumber . toString ( ) ; const position = StavePositionEnum . ABOVE ; const options = { justification : 1 , shift_x : 0 , shift_y : 0 , } ; this . stave . setText ( text , position , options ) ; } public addWordRepetition ( repetitionInstruction ) { let instruction = undefined ; let position = Vex . Flow . StaveModifier . Position . END ; const xShift = this . beginInstructionsWidth ; switch ( repetitionInstruction . type ) { case RepetitionInstructionEnum . Segno : instruction = Vex . Flow . Repetition . type . SEGNO_LEFT ; position = Vex . Flow . StaveModifier . Position . LEFT ; break ; case RepetitionInstructionEnum . Coda : instruction = Vex . Flow . Repetition . type . CODA_LEFT ; position = Vex . Flow . StaveModifier . Position . LEFT ; break ; case RepetitionInstructionEnum . DaCapo : instruction = Vex . Flow . Repetition . type . DC ; break ; case RepetitionInstructionEnum . DalSegno : instruction = Vex . Flow . Repetition . type . DS ; break ; case RepetitionInstructionEnum . Fine : instruction = Vex . Flow . Repetition . type . FINE ; break ; case RepetitionInstructionEnum . ToCoda : instruction = ( Vex . Flow . Repetition as any ) . type . TO_CODA ; break ; case RepetitionInstructionEnum . DaCapoAlFine : instruction = Vex . Flow . Repetition . type . DC_AL_FINE ; break ; case RepetitionInstructionEnum . DaCapoAlCoda : instruction = Vex . Flow . Repetition . type . DC_AL_CODA ; break ; case RepetitionInstructionEnum . DalSegnoAlFine : instruction = Vex . Flow . Repetition . type . DS_AL_FINE ; break ; case RepetitionInstructionEnum . DalSegnoAlCoda : instruction = Vex . Flow . Repetition . type . DS_AL_CODA ; break ; default : break ; } if ( instruction ) { const repetition = new Vex . Flow . Repetition ( instruction , xShift , - this . rules . RepetitionSymbolsYOffset ) ; this . stave . addModifier ( repetition , position ) ; return ; } this . addVolta ( repetitionInstruction ) ; } protected addVolta ( repetitionInstruction ) { let voltaType = Vex . Flow . Volta . type . BEGIN ; if ( repetitionInstruction . type === RepetitionInstructionEnum . Ending ) { switch ( repetitionInstruction . alignment ) { case AlignmentType . Begin : if ( this . parentSourceMeasure . endsRepetitionEnding ( ) ) { voltaType = Vex . Flow . Volta . type . BEGIN_END ; } else { voltaType = Vex . Flow . Volta . type . BEGIN ; } break ; case AlignmentType . End : if ( this . parentSourceMeasure . beginsRepetitionEnding ( ) ) { return ; } else { voltaType = Vex . Flow . Volta . type . END ; } break ; default : break ; } const skyBottomLineCalculator = this . ParentStaffLine . SkyBottomLineCalculator ; const start = this . PositionAndShape . AbsolutePosition . x + this . PositionAndShape . BorderMarginLeft + 0.4 ; const end = Math . max ( this . PositionAndShape . AbsolutePosition . x + this . PositionAndShape . BorderMarginRight , start + 0.4 ) ; const skylineMinForMeasure = skyBottomLineCalculator . getSkyLineMinInRange ( start , end ) - 2 ; let newSkylineValueForMeasure = - 6.1 + this . rules . VoltaOffset ; let vexFlowVoltaHeight = this . rules . VoltaOffset ; if ( skylineMinForMeasure < newSkylineValueForMeasure ) { const skylineDifference = skylineMinForMeasure - newSkylineValueForMeasure ; vexFlowVoltaHeight += skylineDifference ; newSkylineValueForMeasure = skylineMinForMeasure ; } let prevMeasure = undefined ; const currentMeasureNumber = this . parentSourceMeasure . MeasureNumber ; for ( let i = 0 ; i < this . ParentStaffLine . Measures . length ; i ++ ) { const tempMeasure = this . ParentStaffLine . Measures [ i ] ; if ( ! ( tempMeasure instanceof VexFlowMeasure ) ) { continue ; } if ( tempMeasure . MeasureNumber === currentMeasureNumber - 1 || tempMeasure . MeasureNumber + tempMeasure . parentSourceMeasure ?. multipleRestMeasures === currentMeasureNumber ) { prevMeasure = tempMeasure as VexFlowMeasure ; } } if ( prevMeasure ) { const prevStaveModifiers = prevMeasure . stave . getModifiers ( ) ; for ( let i = 0 ; i < prevStaveModifiers . length ; i ++ ) { const nextStaveModifier = prevStaveModifiers [ i ] ; if ( nextStaveModifier . hasOwnProperty ( "volta" ) ) { const prevskyBottomLineCalculator = prevMeasure . ParentStaffLine . SkyBottomLineCalculator ; const prevStart = prevMeasure . PositionAndShape . AbsolutePosition . x + prevMeasure . PositionAndShape . BorderMarginLeft + 0.4 ; const prevEnd = Math . max ( prevMeasure . PositionAndShape . AbsolutePosition . x + prevMeasure . PositionAndShape . BorderMarginRight , prevStart + 0.4 ) ; const prevMeasureSkyline = prevskyBottomLineCalculator . getSkyLineMinInRange ( prevStart , prevEnd ) ; if ( prevMeasureSkyline <= newSkylineValueForMeasure ) { const skylineDifference = prevMeasureSkyline - newSkylineValueForMeasure ; vexFlowVoltaHeight += skylineDifference ; newSkylineValueForMeasure = prevMeasureSkyline ; } else { ( nextStaveModifier as any ) . y_shift = vexFlowVoltaHeight * unitInPixels ; prevMeasure . ParentStaffLine . SkyBottomLineCalculator . updateSkyLineInRange ( prevStart , prevEnd , newSkylineValueForMeasure ) ; } } } } vexFlowVoltaHeight *= 10 ; this . stave . setVoltaType ( voltaType , repetitionInstruction . endingIndices [ 0 ] , vexFlowVoltaHeight ) ; skyBottomLineCalculator . updateSkyLineInRange ( start , end , newSkylineValueForMeasure ) ; } } param width public setWidth ( width ) { super . setWidth ( width ) ; this . stave . setWidth ( width * unitInPixels ) ; } /**
     * This method is called after the StaffEntriesScaleFactor has been set.
     * Here the final x-positions of the staff entries have to be set.
     * (multiply the minimal positions with the scaling factor, considering the BeginInstructionsWidth)
     */ public layoutSymbols ( ) { } param ctx public draw ( ctx ) { this . stave . setContext ( ctx ) . draw ( ) ; for ( const voiceID in this . vfVoices ) { if ( this . vfVoices . hasOwnProperty ( voiceID ) ) { ctx . save ( ) ; this . vfVoices [ voiceID ] . draw ( ctx , this . stave ) ; ctx . restore ( ) ; } } for ( const voiceID in this . vfbeams ) { if ( this . vfbeams . hasOwnProperty ( voiceID ) ) { for ( const beam of this . vfbeams [ voiceID ] ) { beam . setContext ( ctx ) . draw ( ) ; } } } if ( this . autoVfBeams ) { for ( const beam of this . autoVfBeams ) { beam . setContext ( ctx ) . draw ( ) ; } } if ( ! this . isTabMeasure || this . rules . TupletNumbersInTabs ) { if ( this . autoTupletVfBeams ) { for ( const beam of this . autoTupletVfBeams ) { beam . setContext ( ctx ) . draw ( ) ; } } for ( const voiceID in this . vftuplets ) { if ( this . vftuplets . hasOwnProperty ( voiceID ) ) { for ( const tuplet of this . vftuplets [ voiceID ] ) { tuplet . setContext ( ctx ) . draw ( ) ; } } } } for ( const tie of this . vfTies ) { tie . setContext ( ctx ) . draw ( ) ; } for ( const connector of this . connectors ) { connector . setContext ( ctx ) . draw ( ) ; } this . correctNotePositions ( ) ; } public format ( ) { if ( this . formatVoices ) { this . formatVoices ( ( this . PositionAndShape . Size . width - this . beginInstructionsWidth - this . endInstructionsWidth ) * unitInPixels , this ) ; } } public correctNotePositions ( ) { if ( this . isTabMeasure ) { return ; } for ( const voice of this . getVoicesWithinMeasure ( ) ) { for ( const ve of voice . VoiceEntries ) { for ( const note of ve . Notes ) { const gNote = this . rules . GNote ( note ) as VexFlowGraphicalNote ; if ( ! gNote ?. vfnote ) { return ; } const vfnote = gNote . vfnote [ 0 ] ; let relPosY = 0 ; if ( gNote . parentVoiceEntry . parentVoiceEntry . StemDirection === StemDirectionType . Up ) { relPosY += 3.5 ; } else { relPosY += 0.5 ; } const line = - gNote . notehead ( vfnote ) . line ; relPosY += line + ( gNote . parentVoiceEntry . notes . last ( ) as VexFlowGraphicalNote ) . notehead ( ) . line ; gNote . PositionAndShape . RelativePosition . y = relPosY ; } } } } /**
     * Returns all the voices that are present in this measure
     */ public getVoicesWithinMeasure ( ) { const voices = [ ] ; for ( const gse of this . staffEntries ) { for ( const gve of gse . graphicalVoiceEntries ) { if ( voices . indexOf ( gve . parentVoiceEntry . ParentVoice ) === - 1 ) { voices . push ( gve . parentVoiceEntry . ParentVoice ) ; } } } return voices ; } param voice public getGraphicalVoiceEntriesPerVoice ( voice ) { const voiceEntries = [ ] ; for ( const gse of this . staffEntries ) { for ( const gve of gse . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry . ParentVoice === voice ) { voiceEntries . push ( gve ) ; } } } return voiceEntries ; } param voice protected getRestFilledVexFlowStaveNotesPerVoice ( voice ) { let latestVoiceTimestamp = undefined ; let gvEntries = this . getGraphicalVoiceEntriesPerVoice ( voice ) ; for ( let idx = 0 ; idx < gvEntries . length ; idx ++ ) { const gve = gvEntries [ idx ] ; const gNotesStartTimestamp = gve . notes [ 0 ] . sourceNote . getAbsoluteTimestamp ( ) ; let gNotesEndTimestamp = new Fraction ( ) ; for ( const graphicalNote of gve . notes ) { const noteEnd = Fraction . plus ( graphicalNote . sourceNote . getAbsoluteTimestamp ( ) , graphicalNote . sourceNote . Length ) ; if ( gNotesEndTimestamp . lt ( noteEnd ) ) { gNotesEndTimestamp = noteEnd ; } } if ( ! latestVoiceTimestamp ) { const gapFromMeasureStart = Fraction . minus ( gNotesStartTimestamp , this . parentSourceMeasure . AbsoluteTimestamp ) ; if ( gapFromMeasureStart . RealValue > 0 ) { log . trace ( `Ghost Found at start (measure ${ this . MeasureNumber })` ) ; const ghostGves = this . createGhostGves ( gapFromMeasureStart ) ; gvEntries . splice ( 0 , 0 , ... ghostGves ) ; idx += ghostGves . length ; } } else { const inBetweenLength = Fraction . minus ( gNotesStartTimestamp , latestVoiceTimestamp ) ; if ( inBetweenLength . RealValue > 0 ) { log . trace ( `Ghost Found in between (measure ${ this . MeasureNumber })` ) ; const ghostGves = this . createGhostGves ( inBetweenLength ) ; gvEntries . splice ( idx , 0 , ... ghostGves ) ; idx += ghostGves . length ; } } latestVoiceTimestamp = gNotesEndTimestamp ; } const measureEndTimestamp = Fraction . plus ( this . parentSourceMeasure . AbsoluteTimestamp , this . parentSourceMeasure . Duration ) ; const restLength = Fraction . minus ( measureEndTimestamp , latestVoiceTimestamp ) ; if ( restLength . RealValue > 0 ) { log . trace ( `Ghost Found at end (measure ${ this . MeasureNumber })` ) ; const ghostGves = this . createGhostGves ( restLength ) ; gvEntries = gvEntries . concat ( ghostGves ) ; } return gvEntries ; } private createGhostGves ( duration ) { const vfghosts = VexFlowConverter . GhostNotes ( duration ) ; const ghostGves = [ ] ; for ( const vfghost of vfghosts ) { const ghostGve = new VexFlowVoiceEntry ( undefined , undefined ) ; ghostGve . vfStaveNote = vfghost ; ghostGves . push ( ghostGve ) ; } return ghostGves ; } param graphicalNote param beam public handleBeam ( graphicalNote , beam ) { const voiceID = graphicalNote . sourceNote . ParentVoiceEntry . ParentVoice . VoiceId ; let beams = this . beams [ voiceID ] ; if ( ! beams ) { beams = this . beams [ voiceID ] = [ ] ; } let data ; for ( const mybeam of beams ) { if ( mybeam [ 0 ] === beam ) { data = mybeam ; } } if ( ! data ) { data = [ beam , [ ] ] ; beams . push ( data ) ; } const parent = graphicalNote . parentVoiceEntry as VexFlowVoiceEntry ; if ( data [ 1 ] . indexOf ( parent ) < 0 ) { data [ 1 ] . push ( parent ) ; } } public handleTuplet ( graphicalNote , tuplet ) { const voiceID = graphicalNote . sourceNote . ParentVoiceEntry . ParentVoice . VoiceId ; tuplet = graphicalNote . sourceNote . NoteTuplet ; let tuplets = this . tuplets [ voiceID ] ; if ( ! tuplets ) { tuplets = this . tuplets [ voiceID ] = [ ] ; } let currentTupletBuilder ; for ( const t of tuplets ) { if ( t [ 0 ] === tuplet ) { currentTupletBuilder = t ; } } if ( ! currentTupletBuilder ) { currentTupletBuilder = [ tuplet , [ ] ] ; tuplets . push ( currentTupletBuilder ) ; } const parent = graphicalNote . parentVoiceEntry as VexFlowVoiceEntry ; if ( currentTupletBuilder [ 1 ] . indexOf ( parent ) < 0 ) { currentTupletBuilder [ 1 ] . push ( parent ) ; } } /**
     * Complete the creation of VexFlow Beams in this measure
     */ public finalizeBeams ( ) { this . vfbeams = { } ; const beamedNotes = [ ] ; for ( const voiceID in this . beams ) { if ( this . beams . hasOwnProperty ( voiceID ) ) { let vfbeams = this . vfbeams [ voiceID ] ; if ( ! vfbeams ) { vfbeams = this . vfbeams [ voiceID ] = [ ] ; } for ( const beam of this . beams [ voiceID ] ) { let beamHasQuarterNoteOrLonger = false ; for ( const note of beam [ 0 ] . Notes ) { if ( note . Length . RealValue >= new Fraction ( 1 , 4 ) . RealValue && ( ! note . TypeLength || note . TypeLength . RealValue > 0.125 ) ) { beamHasQuarterNoteOrLonger = true ; break ; } } if ( beamHasQuarterNoteOrLonger ) { log . debug ( "Beam between note >= quarter, likely tremolo, currently unsupported. continuing." ) ; continue ; } const notes = [ ] ; const psBeam = beam [ 0 ] ; const voiceEntries = beam [ 1 ] ; let autoStemBeam = true ; for ( const gve of voiceEntries ) { if ( gve . parentVoiceEntry . ParentVoice === psBeam . Notes [ 0 ] . ParentVoiceEntry . ParentVoice ) { autoStemBeam = gve . parentVoiceEntry . WantedStemDirection === StemDirectionType . Undefined ; } } let isGraceBeam = false ; let beamColor ; const stemColors = [ ] ; for ( const entry of voiceEntries ) { const note = ( ( < VexFlowVoiceEntry > entry ) . vfStaveNote as StaveNote ) ; if ( note ) { notes . push ( note ) ; beamedNotes . push ( note ) ; } if ( entry . parentVoiceEntry . IsGrace ) { isGraceBeam = true ; } if ( entry . parentVoiceEntry . StemColor && this . rules . ColoringEnabled ) { stemColors . push ( entry . parentVoiceEntry . StemColor ) ; } } if ( notes . length > 1 ) { const vfBeam = new Vex . Flow . Beam ( notes , autoStemBeam ) ; if ( isGraceBeam ) { ( < any > vfBeam ) . render_options . beam_width = 3 ; ( < any > vfBeam ) . render_options . partial_beam_length = 4 ; } if ( stemColors . length >= 2 && this . rules . ColorBeams ) { beamColor = stemColors [ 0 ] ; for ( const stemColor of stemColors ) { if ( stemColor !== beamColor ) { beamColor = undefined ; break ; } } vfBeam . setStyle ( { fillStyle : beamColor , strokeStyle : beamColor } ) ; } if ( this . rules . FlatBeams ) { ( < any > vfBeam ) . render_options . flat_beams = true ; ( < any > vfBeam ) . render_options . flat_beam_offset = this . rules . FlatBeamOffset ; ( < any > vfBeam ) . render_options . flat_beam_offset_per_beam = this . rules . FlatBeamOffsetPerBeam ; } vfbeams . push ( vfBeam ) ; } else { log . debug ( "Warning! Beam with no notes!" ) ; } } } } if ( this . rules . AutoBeamNotes ) { this . autoBeamNotes ( beamedNotes ) ; } } param beamedNotes private autoBeamNotes ( beamedNotes ) { let notesToAutoBeam = [ ] ; let consecutiveBeamableNotes = [ ] ; let currentTuplet ; let tupletNotesToAutoBeam = [ ] ; this . autoTupletVfBeams = [ ] ; const separateAutoBeams = [ ] ; this . autoVfBeams = [ ] ; let timeSignature = this . parentSourceMeasure . ActiveTimeSignature ; if ( ! timeSignature ) { timeSignature = this . parentSourceMeasure . Duration ; } for ( const staffEntry of this . staffEntries ) { for ( const gve of staffEntry . graphicalVoiceEntries ) { const vfStaveNote = < StaveNote > ( gve as VexFlowVoiceEntry ) . vfStaveNote ; const gNote = gve . notes [ 0 ] ; const isOnBeat = staffEntry . relInMeasureTimestamp . isOnBeat ( timeSignature ) ; const haveTwoOrMoreNotesToBeamAlready = consecutiveBeamableNotes . length >= 2 ; const noteIsQuarterOrLonger = gNote . sourceNote . Length . RealValue - new Fraction ( 1 , 4 ) . RealValue > ( - Fraction . FloatInaccuracyTolerance ) ; const unbeamableNote = gve . parentVoiceEntry . IsGrace || noteIsQuarterOrLonger || beamedNotes . contains ( vfStaveNote ) ; if ( unbeamableNote || isOnBeat ) { if ( haveTwoOrMoreNotesToBeamAlready ) { for ( const note of consecutiveBeamableNotes ) { notesToAutoBeam . push ( note ) ; } separateAutoBeams . push ( notesToAutoBeam . slice ( ) ) ; notesToAutoBeam = [ ] ; } consecutiveBeamableNotes = [ ] ; if ( unbeamableNote ) { continue ; } } const noteTuplet = gve . notes [ 0 ] . sourceNote . NoteTuplet ; if ( noteTuplet ) { let tupletContainsUnbeamableNote = false ; for ( const notes of noteTuplet . Notes ) { for ( const note of notes ) { if ( note . NoteTypeXml >= NoteType . QUARTER || note . ParentVoiceEntry . IsGrace || note . isRest ( ) && ! this . rules . AutoBeamOptions . beam_rests ) { tupletContainsUnbeamableNote = true ; break ; } } if ( tupletContainsUnbeamableNote ) { break ; } } if ( ! currentTuplet ) { currentTuplet = noteTuplet ; } else { if ( currentTuplet !== noteTuplet ) { if ( tupletNotesToAutoBeam . length > 1 ) { const vfBeam = new Vex . Flow . Beam ( tupletNotesToAutoBeam , true ) ; if ( this . rules . FlatBeams ) { ( < any > vfBeam ) . render_options . flat_beams = true ; ( < any > vfBeam ) . render_options . flat_beam_offset = this . rules . FlatBeamOffset ; ( < any > vfBeam ) . render_options . flat_beam_offset_per_beam = this . rules . FlatBeamOffsetPerBeam ; } this . autoTupletVfBeams . push ( vfBeam ) ; } tupletNotesToAutoBeam = [ ] ; currentTuplet = noteTuplet ; } } if ( ! tupletContainsUnbeamableNote ) { tupletNotesToAutoBeam . push ( vfStaveNote ) ; } continue ; } else { currentTuplet = undefined ; } consecutiveBeamableNotes . push ( vfStaveNote ) ; } } if ( tupletNotesToAutoBeam . length >= 2 ) { const vfBeam = new Vex . Flow . Beam ( tupletNotesToAutoBeam , true ) ; if ( this . rules . FlatBeams ) { ( < any > vfBeam ) . render_options . flat_beams = true ; ( < any > vfBeam ) . render_options . flat_beam_offset = this . rules . FlatBeamOffset ; ( < any > vfBeam ) . render_options . flat_beam_offset_per_beam = this . rules . FlatBeamOffsetPerBeam ; } this . autoTupletVfBeams . push ( vfBeam ) ; } if ( consecutiveBeamableNotes . length >= 2 ) { for ( const note of consecutiveBeamableNotes ) { notesToAutoBeam . push ( note ) ; } separateAutoBeams . push ( notesToAutoBeam ) ; } const autoBeamOptions = this . rules . AutoBeamOptions ; const generateBeamOptions = { beam_middle_only : autoBeamOptions . beam_middle_rests_only , beam_rests : autoBeamOptions . beam_rests , maintain_stem_directions : autoBeamOptions . maintain_stem_directions , } ; if ( autoBeamOptions . groups && autoBeamOptions . groups . length ) { const groups = [ ] ; for ( const fraction of autoBeamOptions . groups ) { groups . push ( new Vex . Flow . Fraction ( fraction [ 0 ] , fraction [ 1 ] ) ) ; } generateBeamOptions . groups = groups ; } for ( const notesForSeparateAutoBeam of separateAutoBeams ) { const newBeams = Vex . Flow . Beam . generateBeams ( notesForSeparateAutoBeam , generateBeamOptions ) ; for ( const vfBeam of newBeams ) { if ( this . rules . FlatBeams ) { ( < any > vfBeam ) . render_options . flat_beams = true ; ( < any > vfBeam ) . render_options . flat_beam_offset = this . rules . FlatBeamOffset ; ( < any > vfBeam ) . render_options . flat_beam_offset_per_beam = this . rules . FlatBeamOffsetPerBeam ; } this . autoVfBeams . push ( vfBeam ) ; } } } /**
     * Complete the creation of VexFlow Tuplets in this measure
     */ public finalizeTuplets ( ) { this . vftuplets = { } ; for ( const voiceID in this . tuplets ) { if ( this . tuplets . hasOwnProperty ( voiceID ) ) { let vftuplets = this . vftuplets [ voiceID ] ; if ( ! vftuplets ) { vftuplets = this . vftuplets [ voiceID ] = [ ] ; } for ( const tupletBuilder of this . tuplets [ voiceID ] ) { const tupletStaveNotes = [ ] ; const tupletVoiceEntries = tupletBuilder [ 1 ] ; for ( const tupletVoiceEntry of tupletVoiceEntries ) { tupletStaveNotes . push ( ( ( tupletVoiceEntry ) . vfStaveNote as StaveNote ) ) ; } if ( tupletStaveNotes . length > 1 ) { const tuplet = tupletBuilder [ 0 ] ; const notesOccupied = tuplet . Notes [ 0 ] [ 0 ] . NormalNotes ; const bracketed = tuplet . Bracket || ( tuplet . TupletLabelNumber === 3 && this . rules . TripletsBracketed ) || ( tuplet . TupletLabelNumber !== 3 && this . rules . TupletsBracketed ) ; let location = Vex . Flow . Tuplet . LOCATION_TOP ; if ( tuplet . tupletLabelNumberPlacement === PlacementEnum . Below ) { location = Vex . Flow . Tuplet . LOCATION_BOTTOM ; } vftuplets . push ( new Vex . Flow . Tuplet ( tupletStaveNotes , { bracketed : bracketed , location : location , notes_occupied : notesOccupied , num_notes : tuplet . TupletLabelNumber , ratioed : this . rules . TupletsRatioed , } ) ) ; } else { log . debug ( "Warning! Tuplet with no notes! Trying to ignore, but this is a serious problem." ) ; } } } } } public layoutStaffEntry ( graphicalStaffEntry ) { return ; } public graphicalMeasureCreatedCalculations ( ) { let graceSlur ; let graceGVoiceEntriesBefore = [ ] ; const graveGVoiceEntriesAdded = [ ] ; for ( const graphicalStaffEntry of this . staffEntries as VexFlowStaffEntry [ ] ) { graceSlur = false ; graceGVoiceEntriesBefore = [ ] ; for ( const gve of graphicalStaffEntry . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry . IsGrace ) { graceGVoiceEntriesBefore . push ( gve ) ; graveGVoiceEntriesAdded . push ( gve ) ; if ( ! graceSlur ) { graceSlur = gve . parentVoiceEntry . GraceSlur ; } continue ; } ( gve as VexFlowVoiceEntry ) . vfStaveNote = VexFlowConverter . StaveNote ( gve ) ; if ( graceGVoiceEntriesBefore . length > 0 ) { const graceNotes = [ ] ; for ( let i = 0 ; i < graceGVoiceEntriesBefore . length ; i ++ ) { const gveGrace = < VexFlowVoiceEntry > graceGVoiceEntriesBefore [ i ] ; gveGrace . GraceSlash = gveGrace . parentVoiceEntry . GraceNoteSlash ; if ( i > 0 ) { gveGrace . GraceSlash = false ; } const vfStaveNote = VexFlowConverter . StaveNote ( gveGrace ) ; gveGrace . vfStaveNote = vfStaveNote ; graceNotes . push ( vfStaveNote ) ; } const graceNoteGroup = new Vex . Flow . GraceNoteGroup ( graceNotes , graceSlur ) ; ( ( gve as VexFlowVoiceEntry ) . vfStaveNote as StaveNote ) . addModifier ( 0 , graceNoteGroup ) ; graceGVoiceEntriesBefore = [ ] ; } } } if ( graceGVoiceEntriesBefore . length > 0 ) { for ( const graceGve of graceGVoiceEntriesBefore ) { ( graceGve as VexFlowVoiceEntry ) . vfStaveNote = VexFlowConverter . StaveNote ( graceGve ) ; graceGve . parentVoiceEntry . GraceAfterMainNote = true ; } } this . finalizeBeams ( ) ; this . finalizeTuplets ( ) ; const voices = this . getVoicesWithinMeasure ( ) ; if ( this . rules . RenderFingerings ) { for ( const graphicalStaffEntry of this . staffEntries as VexFlowStaffEntry [ ] ) { graphicalStaffEntry . setModifierXOffsets ( ) ; } } for ( const voice of voices ) { if ( ! voice ) { continue ; } this . vfVoices [ voice . VoiceId ] = new Vex . Flow . Voice ( { beat_value : this . parentSourceMeasure . Duration . Denominator , num_beats : this . parentSourceMeasure . Duration . Numerator , resolution : Vex . Flow . RESOLUTION , } ) . setMode ( Vex . Flow . Voice . Mode . SOFT ) ; const restFilledEntries = this . getRestFilledVexFlowStaveNotesPerVoice ( voice ) ; for ( const voiceEntry of restFilledEntries ) { if ( voiceEntry . parentVoiceEntry ) { if ( voiceEntry . parentVoiceEntry . IsGrace && ! voiceEntry . parentVoiceEntry . GraceAfterMainNote ) { continue ; } } const vexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry ; if ( ( vexFlowVoiceEntry . vfStaveNote as any ) . ticks . denominator === 0 ) { continue ; } if ( voiceEntry . notes . length === 0 || ! voiceEntry . notes [ 0 ] || ! voiceEntry . notes [ 0 ] . sourceNote . PrintObject ) { this . vfVoices [ voice . VoiceId ] . addTickable ( vexFlowVoiceEntry . vfStaveNote ) ; continue ; } const vfse = vexFlowVoiceEntry . parentStaffEntry as VexFlowStaffEntry ; if ( vfse && vfse . vfClefBefore ) { const clefModifier = new NoteSubGroup ( [ vfse . vfClefBefore ] ) ; if ( vexFlowVoiceEntry . vfStaveNote . getCategory && vexFlowVoiceEntry . vfStaveNote . getCategory ( ) === "stavenotes" ) { ( vexFlowVoiceEntry . vfStaveNote as Vex . Flow . StaveNote ) . addModifier ( 0 , clefModifier ) ; } } if ( voiceEntry . parentVoiceEntry && this . rules . RenderFingerings ) { if ( this . rules . FingeringPosition === PlacementEnum . Left || this . rules . FingeringPosition === PlacementEnum . Right ) { this . createFingerings ( voiceEntry ) ; } this . createStringNumber ( voiceEntry ) ; } this . createArpeggio ( voiceEntry ) ; this . vfVoices [ voice . VoiceId ] . addTickable ( vexFlowVoiceEntry . vfStaveNote ) ; } } this . setStemDirectionFromVexFlow ( ) ; for ( const graceGVoiceEntry of graveGVoiceEntriesAdded ) { this . createFingerings ( graceGVoiceEntry ) ; this . createStringNumber ( graceGVoiceEntry ) ; this . createArpeggio ( graceGVoiceEntry ) ; } this . createArticulations ( ) ; this . createOrnaments ( ) ; } private createArpeggio ( voiceEntry ) { if ( voiceEntry . parentVoiceEntry && voiceEntry . parentVoiceEntry . Arpeggio ) { const arpeggio = voiceEntry . parentVoiceEntry . Arpeggio ; if ( voiceEntry . notes && voiceEntry . notes . length > 1 ) { const type = VexFlowConverter . StrokeTypeFromArpeggioType ( arpeggio . type ) ; const stroke = new Vex . Flow . Stroke ( type , { all_voices : this . rules . ArpeggiosGoAcrossVoices } ) ; if ( this . rules . RenderArpeggios ) { ( voiceEntry as VexFlowVoiceEntry ) . vfStaveNote . addStroke ( 0 , stroke ) ; } } else { log . debug ( `[OSMD] arpeggio in measure ${ this . MeasureNumber } could not be drawn.
                voice entry had less than two notes, arpeggio is likely between voice entries, not currently supported in Vexflow.` ) ; } } } /**
     * Copy the stem directions chosen by VexFlow to the StemDirection variable of the graphical notes
     */ private setStemDirectionFromVexFlow ( ) { for ( const vfStaffEntry of this . staffEntries ) { for ( const gVoiceEntry of vfStaffEntry . graphicalVoiceEntries ) { for ( const gnote of gVoiceEntry . notes ) { const vfnote = ( gnote as VexFlowGraphicalNote ) . vfnote ; if ( ! vfnote || ! vfnote [ 0 ] ) { continue ; } const vfStemDir = vfnote [ 0 ] . getStemDirection ( ) ; switch ( vfStemDir ) { case ( Vex . Flow . Stem . UP ) : gVoiceEntry . parentVoiceEntry . StemDirection = StemDirectionType . Up ; break ; case ( Vex . Flow . Stem . DOWN ) : gVoiceEntry . parentVoiceEntry . StemDirection = StemDirectionType . Down ; break ; default : } } } } } /**
     * Create the articulations for all notes of the current staff entry
     */ protected createArticulations ( ) { for ( let idx = 0 , len = this . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = ( this . staffEntries [ idx ] as VexFlowStaffEntry ) ; const graphicalVoiceEntries = graphicalStaffEntry . graphicalVoiceEntries ; for ( const gve of graphicalVoiceEntries ) { const vfStaveNote = ( gve as VexFlowVoiceEntry ) . vfStaveNote ; VexFlowConverter . generateArticulations ( vfStaveNote , gve . notes [ 0 ] . sourceNote . ParentVoiceEntry . Articulations , this . rules ) ; } } } /**
     * Create the ornaments for all notes of the current staff entry
     */ protected createOrnaments ( ) { for ( let idx = 0 , len = this . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = ( this . staffEntries [ idx ] as VexFlowStaffEntry ) ; const gvoices = graphicalStaffEntry . graphicalVoiceEntries ; for ( const voiceID in gvoices ) { if ( gvoices . hasOwnProperty ( voiceID ) ) { const vfStaveNote = ( gvoices [ voiceID ] as VexFlowVoiceEntry ) . vfStaveNote ; const ornamentContainer = gvoices [ voiceID ] . notes [ 0 ] . sourceNote . ParentVoiceEntry . OrnamentContainer ; if ( ornamentContainer ) { VexFlowConverter . generateOrnaments ( vfStaveNote , ornamentContainer ) ; } } } } } protected createFingerings ( voiceEntry ) { const vexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry ; let numberOfFingerings = 0 ; for ( const note of voiceEntry . notes ) { const fingering = note . sourceNote . Fingering ; if ( fingering ) { numberOfFingerings ++ ; } } let fingeringIndex = - 1 ; for ( const note of voiceEntry . notes ) { const fingering = note . sourceNote . Fingering ; if ( ! fingering ) { fingeringIndex ++ ; continue ; } fingeringIndex ++ ; let fingeringPosition = this . rules . FingeringPosition ; if ( this . rules . FingeringPositionGrace === PlacementEnum . AboveOrBelow ) { if ( this . isUpperStaffOfInstrument ( ) ) { fingeringPosition = PlacementEnum . Above ; } else if ( this . isLowerStaffOfInstrument ( ) ) { fingeringPosition = PlacementEnum . Below ; } } if ( fingering . placement !== PlacementEnum . NotYetDefined ) { fingeringPosition = fingering . placement ; } let offsetX = this . rules . FingeringOffsetX ; let modifierPosition ; switch ( fingeringPosition ) { default : case PlacementEnum . Left : modifierPosition = Vex . Flow . StaveModifier . Position . LEFT ; offsetX -= note . baseFingeringXOffset * unitInPixels ; break ; case PlacementEnum . Right : modifierPosition = Vex . Flow . StaveModifier . Position . RIGHT ; offsetX += note . baseFingeringXOffset * unitInPixels ; break ; case PlacementEnum . Above : modifierPosition = Vex . Flow . StaveModifier . Position . ABOVE ; break ; case PlacementEnum . Below : modifierPosition = Vex . Flow . StaveModifier . Position . BELOW ; break ; case PlacementEnum . NotYetDefined : const sourceStaff = voiceEntry . parentStaffEntry . sourceStaffEntry . ParentStaff ; if ( voiceEntry . notes . length > 1 || voiceEntry . parentStaffEntry . graphicalVoiceEntries . length > 1 ) { modifierPosition = Vex . Flow . StaveModifier . Position . LEFT ; } else if ( sourceStaff . idInMusicSheet === 0 ) { modifierPosition = Vex . Flow . StaveModifier . Position . ABOVE ; fingeringPosition = PlacementEnum . Above ; } else { modifierPosition = Vex . Flow . StaveModifier . Position . BELOW ; fingeringPosition = PlacementEnum . Below ; } } const fretFinger = new Vex . Flow . FretHandFinger ( fingering . value ) ; fretFinger . setPosition ( modifierPosition ) ; fretFinger . setOffsetX ( offsetX ) ; if ( fingeringPosition === PlacementEnum . Above || fingeringPosition === PlacementEnum . Below ) { const offsetYSign = fingeringPosition === PlacementEnum . Above ? - 1 : 1 ; const ordering = fingeringPosition === PlacementEnum . Above ? fingeringIndex : numberOfFingerings - 1 - fingeringIndex ; if ( this . rules . FingeringInsideStafflines && numberOfFingerings > 1 ) { const perFingeringShift = fretFinger . getWidth ( ) / 2 ; const shiftCount = numberOfFingerings * 2.5 ; fretFinger . setOffsetY ( offsetYSign * ( ordering + shiftCount ) * perFingeringShift ) ; } else if ( ! this . rules . FingeringInsideStafflines ) { const stringNumber = new Vex . Flow . StringNumber ( fingering . value ) ; ( < any > stringNumber ) . radius = 0 ; stringNumber . setPosition ( modifierPosition ) ; stringNumber . setOffsetY ( offsetYSign * ordering * stringNumber . getWidth ( ) * 2 / 3 ) ; vexFlowVoiceEntry . vfStaveNote . addModifier ( ( fingeringIndex as any ) , ( stringNumber as any ) ) ; continue ; } } vexFlowVoiceEntry . vfStaveNote . addModifier ( ( fingeringIndex as any ) , ( fretFinger as any ) ) ; } } protected createStringNumber ( voiceEntry ) { if ( ! this . rules . RenderStringNumbersClassical ) { return ; } const vexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry ; voiceEntry . notes . forEach ( ( note , stringIndex ) => { const stringInstruction = note . sourceNote . StringInstruction ; if ( stringInstruction ) { let stringNumber = stringInstruction . value ; switch ( stringNumber ) { case "1" : stringNumber = "I" ; break ; case "2" : stringNumber = "II" ; break ; case "3" : stringNumber = "III" ; break ; case "4" : stringNumber = "IV" ; break ; case "5" : stringNumber = "V" ; break ; case "6" : stringNumber = "VI" ; break ; default : } const vfStringNumber = new Vex . Flow . StringNumber ( stringNumber ) ; ( < any > vfStringNumber ) . radius = 0 ; const offsetY = - this . rules . StringNumberOffsetY ; if ( voiceEntry . notes . length > 1 || voiceEntry . parentStaffEntry . graphicalVoiceEntries . length > 1 ) { vfStringNumber . setOffsetX ( note . baseStringNumberXOffset * 13 ) ; vfStringNumber . setPosition ( Vex . Flow . Modifier . Position . RIGHT ) ; } else { vfStringNumber . setPosition ( Vex . Flow . Modifier . Position . ABOVE ) ; } vfStringNumber . setOffsetY ( offsetY ) ; vexFlowVoiceEntry . vfStaveNote . addModifier ( ( stringIndex as any ) , ( vfStringNumber as any ) ) ; } } ) ; } param top param lineType public lineTo ( top , lineType ) { const connector = new Vex . Flow . StaveConnector ( top . getVFStave ( ) , this . stave ) ; connector . setType ( lineType ) ; this . connectors . push ( connector ) ; } returns { Vex . Flow . Stave } public getVFStave ( ) { return this . stave ; } /**
     * After re-running the formatting on the VexFlow Stave, update the
     * space needed by Instructions (in VexFlow: StaveModifiers)
     */ protected updateInstructionWidth ( ) { let vfBeginInstructionsWidth = 0 ; let vfEndInstructionsWidth = 0 ; const modifiers = this . stave . getModifiers ( ) ; for ( const mod of modifiers ) { if ( mod . getPosition ( ) === StavePositionEnum . BEGIN ) { vfBeginInstructionsWidth += mod . getWidth ( ) + mod . getPadding ( undefined ) ; } else if ( mod . getPosition ( ) === StavePositionEnum . END ) { vfEndInstructionsWidth += mod . getWidth ( ) + mod . getPadding ( undefined ) ; } } this . beginInstructionsWidth = ( vfBeginInstructionsWidth ?? 0 ) / unitInPixels ; this . endInstructionsWidth = ( vfEndInstructionsWidth ?? 0 ) / unitInPixels ; } } export enum StavePositionEnum { LEFT = 1 , RIGHT = 2 , ABOVE = 3 , BELOW = 4 , BEGIN = 5 , END = 6 }