import { IStafflineNoteCalculator } from "../../Interfaces/IStafflineNoteCalculator" ; import { GraphicalNote } from "../GraphicalNote" ; import { Pitch , NoteEnum } from "../../../Common/DataObjects/Pitch" ; import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote" ; import { Dictionary } from "typescript-collections" ; import { EngravingRules } from "../EngravingRules" ; import { ClefEnum } from "../../VoiceData/Instructions/ClefInstruction" ; import { StemDirectionType , VoiceEntry } from "../../VoiceData/VoiceEntry" ; export class VexflowStafflineNoteCalculator implements IStafflineNoteCalculator { private rules : EngravingRules ; private staffPitchListMapping : Dictionary < number , Array < Pitch > > = new Dictionary < number , Array < Pitch > > ( ) ; private baseLineNote : NoteEnum = NoteEnum . B ; private baseLineOctave : number = 1 ; constructor ( rules ) { this . rules = rules ; } param graphicalNote param staffIndex public trackNote ( graphicalNote ) { if ( ! ( graphicalNote instanceof VexFlowGraphicalNote ) || graphicalNote . Clef ( ) . ClefType !== ClefEnum . percussion || graphicalNote . sourceNote . isRest ( ) || this . rules . PercussionOneLineCutoff === 0 || this . rules . PercussionForceVoicesOneLineCutoff === - 1 ) { return ; } const staffIndex = graphicalNote . parentVoiceEntry . parentStaffEntry . sourceStaffEntry . ParentStaff . idInMusicSheet ; let currentPitchList = undefined ; if ( ! this . staffPitchListMapping . containsKey ( staffIndex ) ) { this . staffPitchListMapping . setValue ( staffIndex , new Array < Pitch > ( ) ) ; } currentPitchList = this . staffPitchListMapping . getValue ( staffIndex ) ; const pitch = graphicalNote . sourceNote . Pitch ; VexflowStafflineNoteCalculator . findOrInsert ( currentPitchList , pitch ) ; } private static PitchIndexOf ( array , pitch , start = 0 ) { if ( start > array . length - 1 ) { return - 1 ; } for ( let i = start ; i < array . length ; i ++ ) { const p2 = array [ i ] ; if ( pitch . OperatorEquals ( p2 ) ) { return i ; } } return - 1 ; } private static findOrInsert ( array , pitch ) { for ( let i = 0 ; i < array . length ; i ++ ) { const p2 = array [ i ] ; if ( pitch . OperatorEquals ( p2 ) ) { return i ; } else { if ( pitch . OperatorFundamentalLessThan ( p2 ) ) { array . splice ( i , 0 , pitch ) ; return i ; } } } array . push ( pitch ) ; return array . length - 1 ; } param graphicalNote param staffIndex returns public positionNote ( graphicalNote ) { const staffIndex = graphicalNote . parentVoiceEntry . parentStaffEntry . sourceStaffEntry . ParentStaff . idInMusicSheet ; if ( ! ( graphicalNote instanceof VexFlowGraphicalNote ) || graphicalNote . sourceNote . isRest ( ) || ! this . staffPitchListMapping . containsKey ( staffIndex ) ) { return graphicalNote ; } const currentPitchList = this . staffPitchListMapping . getValue ( staffIndex ) ; const positionByXml = this . rules . PercussionUseXMLDisplayStep && graphicalNote . sourceNote . displayStepUnpitched !== undefined ; if ( currentPitchList . length > this . rules . PercussionOneLineCutoff && ! positionByXml ) { return graphicalNote ; } const vfGraphicalNote = graphicalNote as VexFlowGraphicalNote ; const notePitch = graphicalNote . sourceNote . Pitch ; let displayNote = this . baseLineNote ; let displayOctave = this . baseLineOctave ; if ( this . rules . PercussionUseXMLDisplayStep && graphicalNote . sourceNote . displayStepUnpitched !== undefined ) { displayNote = graphicalNote . sourceNote . displayStepUnpitched ; displayOctave = graphicalNote . sourceNote . displayOctaveUnpitched + this . rules . PercussionOneLineXMLDisplayStepOctaveOffset ; } if ( currentPitchList . length <= this . rules . PercussionForceVoicesOneLineCutoff ) { vfGraphicalNote . setAccidental ( new Pitch ( displayNote , displayOctave , notePitch . Accidental ) ) ; } else { const pitchIndex = VexflowStafflineNoteCalculator . PitchIndexOf ( currentPitchList , notePitch ) ; if ( pitchIndex > - 1 ) { const half = Math . ceil ( currentPitchList . length / 2 ) ; if ( ! this . rules . PercussionUseXMLDisplayStep ) { if ( pitchIndex >= half ) { displayOctave = 2 ; switch ( ( pitchIndex - half ) % 5 ) { case 1 : displayNote = NoteEnum . E ; break ; case 2 : displayNote = NoteEnum . G ; break ; case 3 : displayNote = NoteEnum . B ; break ; case 4 : displayNote = NoteEnum . D ; displayOctave = 3 ; break ; default : displayNote = NoteEnum . C ; break ; } } else { switch ( pitchIndex % 5 ) { case 1 : displayNote = NoteEnum . F ; break ; case 2 : displayNote = NoteEnum . D ; break ; case 3 : displayNote = NoteEnum . B ; displayOctave = 0 ; break ; case 4 : displayNote = NoteEnum . G ; displayOctave = 0 ; break ; default : displayNote = NoteEnum . A ; break ; } } } const mappedPitch = new Pitch ( displayNote , displayOctave , notePitch . Accidental ) ; vfGraphicalNote . setAccidental ( mappedPitch ) ; const parentVoiceEntry = vfGraphicalNote . parentVoiceEntry . parentVoiceEntry ; if ( ! this . rules . SetWantedStemDirectionByXml && parentVoiceEntry . Notes . length < 2 ) { if ( mappedPitch . Octave > this . baseLineOctave || ( mappedPitch . FundamentalNote === this . baseLineNote && mappedPitch . Octave === this . baseLineOctave ) ) { vfGraphicalNote . parentVoiceEntry . parentVoiceEntry . WantedStemDirection = StemDirectionType . Up ; } else { vfGraphicalNote . parentVoiceEntry . parentVoiceEntry . WantedStemDirection = StemDirectionType . Down ; } } } } return vfGraphicalNote ; } param staffIndex public getStafflineUniquePositionCount ( staffIndex ) { if ( this . staffPitchListMapping . containsKey ( staffIndex ) ) { return this . staffPitchListMapping . getValue ( staffIndex ) . length ; } return 0 ; } }