import { MusicSheet } from "../../MusicSheet" ; import { IXmlElement } from "../../../Common/FileIO/Xml" ; import { SourceMeasure } from "../../VoiceData/SourceMeasure" ; import { RepetitionInstruction , RepetitionInstructionEnum , AlignmentType } from "../../VoiceData/Instructions/RepetitionInstruction" ; import { RepetitionInstructionComparer } from "../../VoiceData/Instructions/RepetitionInstruction" ; import { StringUtil } from "../../../Common/Strings/StringUtil" ; export class RepetitionInstructionReader { /**
     * A global list of all repetition instructions in the musicsheet.
     */ public repetitionInstructions : RepetitionInstruction [ ] ; public xmlMeasureList : IXmlElement [ ] [ ] ; private musicSheet : MusicSheet ; private currentMeasureIndex : number ; public set MusicSheet ( value ) { this . musicSheet = value ; this . xmlMeasureList = new Array ( this . musicSheet . Instruments . length ) ; this . repetitionInstructions = [ ] ; } param measure param currentMeasureIndex public prepareReadingMeasure ( measure , currentMeasureIndex ) { this . currentMeasureIndex = currentMeasureIndex ; } public handleLineRepetitionInstructions ( barlineNode ) { let pieceEndingDetected = false ; if ( barlineNode . elements ( ) . length > 0 ) { let location = "" ; let hasRepeat = false ; let direction = "" ; let type = "" ; let style = "" ; const endingIndices = [ ] ; const styleNode = barlineNode . element ( "bar-style" ) ; if ( styleNode ) { style = styleNode . value ; } if ( barlineNode . attributes ( ) . length > 0 && barlineNode . attribute ( "location" ) ) { location = barlineNode . attribute ( "location" ) . value ; } else { location = "right" ; } const barlineNodeElements = barlineNode . elements ( ) ; for ( let idx = 0 , len = barlineNodeElements . length ; idx < len ; ++ idx ) { const childNode = barlineNodeElements [ idx ] ; if ( "repeat" === childNode . name && childNode . hasAttributes ) { hasRepeat = true ; direction = childNode . attribute ( "direction" ) . value ; } else if ( "ending" === childNode . name && childNode . hasAttributes && childNode . attribute ( "type" ) !== undefined && childNode . attribute ( "number" ) ) { type = childNode . attribute ( "type" ) . value ; const num = childNode . attribute ( "number" ) . value ; const separatedEndingIndices = num . split ( "[,+]" ) ; for ( let idx2 = 0 , len2 = separatedEndingIndices . length ; idx2 < len2 ; ++ idx2 ) { const separatedEndingIndex = separatedEndingIndices [ idx2 ] ; const indices = separatedEndingIndex . match ( "[0-9]" ) ; if ( separatedEndingIndex . search ( "-" ) !== - 1 && indices . length === 2 ) { const startIndex = parseInt ( indices [ 0 ] , 10 ) ; const endIndex = parseInt ( indices [ 1 ] , 10 ) ; for ( let index = startIndex ; index <= endIndex ; index ++ ) { endingIndices . push ( index ) ; } } else { for ( let idx3 = 0 , len3 = indices . length ; idx3 < len3 ; ++ idx3 ) { const index = indices [ idx3 ] ; endingIndices . push ( parseInt ( index , 10 ) ) ; } } } } } if ( style === "light-heavy" && endingIndices . length === 0 && ! hasRepeat ) { pieceEndingDetected = true ; } if ( hasRepeat || endingIndices . length > 0 ) { if ( location === "left" ) { if ( type === "start" ) { const newInstruction = new RepetitionInstruction ( this . currentMeasureIndex , RepetitionInstructionEnum . Ending , AlignmentType . Begin , undefined , endingIndices ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; } if ( direction === "forward" ) { const newInstruction = new RepetitionInstruction ( this . currentMeasureIndex , RepetitionInstructionEnum . StartLine ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; } } else { if ( type === "stop" ) { const newInstruction = new RepetitionInstruction ( this . currentMeasureIndex , RepetitionInstructionEnum . Ending , AlignmentType . End , undefined , endingIndices ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; } if ( direction === "backward" ) { const newInstruction = new RepetitionInstruction ( this . currentMeasureIndex , RepetitionInstructionEnum . BackJumpLine ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; } } } } return pieceEndingDetected ; } public handleRepetitionInstructionsFromWordsOrSymbols ( directionTypeNode , relativeMeasurePosition ) { const wordsNode = directionTypeNode . element ( "words" ) ; const measureIndex = this . currentMeasureIndex ; if ( wordsNode ) { const dsRegEx = "d\\s?\\.s\\." ; const innerText = wordsNode . value . trim ( ) . toLowerCase ( ) ; if ( StringUtil . StringContainsSeparatedWord ( innerText , dsRegEx + " al fine" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . DalSegnoAlFine ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } const dcRegEx = "d\\.\\s?c\\." ; if ( StringUtil . StringContainsSeparatedWord ( innerText , dcRegEx + " al coda" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . DalSegnoAlCoda ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , dcRegEx + " al fine" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . DaCapoAlFine ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , dcRegEx + " al coda" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . DaCapoAlCoda ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , dcRegEx ) || StringUtil . StringContainsSeparatedWord ( innerText , "da\\s?capo" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . DaCapo ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , dsRegEx , true ) || StringUtil . StringContainsSeparatedWord ( innerText , "dal\\s?segno" , true ) ) { let newInstruction ; if ( StringUtil . StringContainsSeparatedWord ( innerText , "al\\s?coda" , true ) ) { newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . DalSegnoAlCoda ) ; } else { newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . DalSegno ) ; } this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , "to\\s?coda" , true ) || StringUtil . StringContainsSeparatedWord ( innerText , "a (la )?coda" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . ToCoda ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , "fine" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . Fine ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , "coda" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . Coda ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } if ( StringUtil . StringContainsSeparatedWord ( innerText , "segno" , true ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . Segno ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } } else if ( directionTypeNode . element ( "segno" ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . Segno ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } else if ( directionTypeNode . element ( "coda" ) ) { const newInstruction = new RepetitionInstruction ( measureIndex , RepetitionInstructionEnum . Coda ) ; this . addInstruction ( this . repetitionInstructions , newInstruction ) ; return true ; } return false ; } public removeRedundantInstructions ( ) { let segnoCount = 0 ; let codaCount = 0 ; let toCodaCount = 0 ; let dalSegnaCount = 0 ; for ( let index = 0 ; index < this . repetitionInstructions . length ; index ++ ) { const instruction = this . repetitionInstructions [ index ] ; switch ( instruction . type ) { case RepetitionInstructionEnum . Coda : if ( toCodaCount > 0 ) { if ( this . findInstructionInPreviousMeasure ( index , instruction . measureIndex , RepetitionInstructionEnum . ToCoda ) ) { instruction . type = RepetitionInstructionEnum . None ; } } if ( codaCount === 0 && toCodaCount === 0 ) { instruction . type = RepetitionInstructionEnum . ToCoda ; instruction . alignment = AlignmentType . End ; instruction . measureIndex -- ; } break ; case RepetitionInstructionEnum . Segno : if ( segnoCount - dalSegnaCount > 0 ) { let foundInstruction = false ; for ( let idx = 0 , len = this . repetitionInstructions . length ; idx < len ; ++ idx ) { const instr = this . repetitionInstructions [ idx ] ; if ( instruction . measureIndex - instr . measureIndex === 1 ) { switch ( instr . type ) { case RepetitionInstructionEnum . BackJumpLine : if ( toCodaCount - codaCount > 0 ) { instr . type = RepetitionInstructionEnum . DalSegnoAlCoda ; } else { instr . type = RepetitionInstructionEnum . DalSegno ; } instruction . type = RepetitionInstructionEnum . None ; foundInstruction = true ; break ; case RepetitionInstructionEnum . DalSegno : case RepetitionInstructionEnum . DalSegnoAlFine : case RepetitionInstructionEnum . DalSegnoAlCoda : instruction . type = RepetitionInstructionEnum . None ; foundInstruction = true ; break ; default : break ; } } if ( foundInstruction ) { break ; } } if ( foundInstruction ) { break ; } if ( toCodaCount - codaCount > 0 ) { instruction . type = RepetitionInstructionEnum . DalSegnoAlCoda ; } else { instruction . type = RepetitionInstructionEnum . DalSegno ; } instruction . alignment = AlignmentType . End ; instruction . measureIndex -- ; } break ; default : break ; } if ( this . backwardSearchForPreviousIdenticalInstruction ( index , instruction ) || instruction . type === RepetitionInstructionEnum . None ) { this . repetitionInstructions . splice ( index , 1 ) ; index -- ; } else { switch ( instruction . type ) { case RepetitionInstructionEnum . Fine : break ; case RepetitionInstructionEnum . ToCoda : toCodaCount ++ ; break ; case RepetitionInstructionEnum . Coda : codaCount ++ ; break ; case RepetitionInstructionEnum . Segno : segnoCount ++ ; break ; case RepetitionInstructionEnum . DalSegnoAlFine : case RepetitionInstructionEnum . DalSegnoAlCoda : dalSegnaCount ++ ; break ; default : break ; } } } this . repetitionInstructions . sort ( RepetitionInstructionComparer . Compare ) ; } private findInstructionInPreviousMeasure ( currentInstructionIndex , currentMeasureIndex , searchedType ) { for ( let index = currentInstructionIndex - 1 ; index >= 0 ; index -- ) { const instruction = this . repetitionInstructions [ index ] ; if ( currentMeasureIndex - instruction . measureIndex === 1 && instruction . type === searchedType ) { return true ; } } return false ; } private backwardSearchForPreviousIdenticalInstruction ( currentInstructionIndex , currentInstruction ) { for ( let index = currentInstructionIndex - 1 ; index >= 0 ; index -- ) { const instruction = this . repetitionInstructions [ index ] ; if ( instruction . equals ( currentInstruction ) ) { return true ; } } return false ; } private addInstruction ( currentRepetitionInstructions , newInstruction ) { let addInstruction = true ; for ( let idx = 0 , len = currentRepetitionInstructions . length ; idx < len ; ++ idx ) { const repetitionInstruction = currentRepetitionInstructions [ idx ] ; if ( newInstruction . equals ( repetitionInstruction ) ) { addInstruction = false ; break ; } } if ( addInstruction ) { currentRepetitionInstructions . push ( newInstruction ) ; } } }