def _createRecipTokenFromDuration(duration: HumNumIn) -> HumdrumToken: dur: HumNum = opFrac(duration) dur = opFrac(dur / opFrac(4)) # convert to quarter note units durFraction: Fraction = Fraction(dur) if durFraction.numerator == 0: # if the GridSlice is at the end of a measure, the # time between the starttime/endtime of the GridSlice should # be subtracted from the endtime of the current GridMeasure. return HumdrumToken('g') if durFraction.numerator == 1: return HumdrumToken(str(durFraction.denominator)) if durFraction.numerator % 3 == 0: dotdur: HumNum = dur * opFrac(Fraction(2, 3)) dotdurFraction: Fraction = Fraction(dotdur) if dotdurFraction.numerator == 1: return HumdrumToken(str(dotdurFraction.denominator) + '.') # try to fit to two dots here # try to fit to three dots here return HumdrumToken( str(durFraction.denominator) + '%' + str(durFraction.numerator))
def transferSidesFromStaff(line: HumdrumLine, staff: GridStaff, emptyStr: str, maxxcount: int, maxdcount: int, maxvcount: int): sides: GridSide = staff.sides vcount: int = sides.verseCount # XMLID if maxxcount > 0: xmlId: HumdrumToken = sides.xmlId if xmlId is not None: line.appendToken(xmlId) else: line.appendToken(HumdrumToken(emptyStr)) # DYNAMICS if maxdcount > 0: dynamics: HumdrumToken = sides.dynamics if dynamics is not None: line.appendToken(dynamics) else: line.appendToken(HumdrumToken(emptyStr)) # VERSES for i in range(0, vcount): verse: HumdrumToken = sides.getVerse(i) if verse is not None: line.appendToken(verse) else: line.appendToken(HumdrumToken(emptyStr)) for i in range(vcount, maxvcount): line.appendToken(HumdrumToken(emptyStr))
def addLayoutParameter(self, associatedSlice: GridSlice, partIndex: int, staffIndex: int, voiceIndex: int, locomment: str) -> None: # add this '!LO:' string just before this associatedSlice if len(self.slices) == 0: # something strange happened: expecting at least one item in measure. # associatedSlice is supposed to already be in the measure. return associatedSliceIdx: t.Optional[int] = None if associatedSlice is None: # place at end of measure (associate with imaginary slice just off the end) associatedSliceIdx = len(self.slices) else: # find owning line (associatedSlice) foundIt: bool = False for associatedSliceIdx in range(len(self.slices) - 1, -1, -1): gridSlice: GridSlice = self.slices[associatedSliceIdx] if gridSlice is associatedSlice: foundIt = True break if not foundIt: # cannot find owning line (a.k.a. associatedSlice is not in this GridMeasure) return # see if the previous slice is a layout slice we can use prevIdx: int = associatedSliceIdx - 1 prevSlice: GridSlice = self.slices[prevIdx] if prevSlice.isLocalLayoutSlice: prevStaff: GridStaff = prevSlice.parts[partIndex].staves[staffIndex] prevVoice: GridVoice = self._getIndexedVoice_AppendingIfNecessary(prevStaff.voices, voiceIndex) if prevVoice.token is None or prevVoice.token.text == '!': prevVoice.token = HumdrumToken(locomment) return # if we get here, we couldn't use the previous slice, so we need to insert # a new Layout slice to use, just before the associated slice. insertPoint: int = associatedSliceIdx newSlice: GridSlice if associatedSlice is not None: newSlice = GridSlice(self, associatedSlice.timestamp, SliceType.Layouts) newSlice.initializeBySlice(associatedSlice) self.slices.insert(insertPoint, newSlice) else: newSlice = GridSlice(self, self.timestamp + self.duration, SliceType.Layouts) newSlice.initializeBySlice(self.slices[-1]) self.slices.append(newSlice) newStaff: GridStaff = newSlice.parts[partIndex].staves[staffIndex] newVoice: GridVoice = self._getIndexedVoice_AppendingIfNecessary(newStaff.voices, voiceIndex) newVoice.token = HumdrumToken(locomment)
def createTokensFromLine(self) -> int: # returns number of tokens created ''' // delete previous tokens (will need to re-analyze structure // of file after this). ''' self._tokens = [] self._numTabsAfterToken = [] if self.text == '': # one empty token token = HumdrumToken() token.ownerLine = self self._tokens = [token] self._numTabsAfterToken = [0] return 1 if self.text.startswith('!!'): # global, so just one token for the whole line token = HumdrumToken(self.text) token.ownerLine = self self._tokens = [token] self._numTabsAfterToken = [0] return 1 # tokenStrList: [str] = self.text.split('\t') # for tokenStr in tokenStrList: # token = HumdrumToken(tokenStr) # token.ownerLine = self # self._tokens.append(token) # self._numTabsAfterToken.append(1) for m in re.finditer(r'([^\t]+)(\t*)', self.text): # m is a match object containing two groups: first the token, then any trailing tabs tokenStr = m.group(1) if tokenStr is None: break tabsStr = m.group(2) if tabsStr is None: numTabsAfterThisToken = 0 else: numTabsAfterThisToken = len(tabsStr) token = HumdrumToken(tokenStr) token.ownerLine = self self._tokens.append(token) self._numTabsAfterToken.append(numTabsAfterThisToken) return len(self._tokens)
def addToken(self, tok: Union[HumdrumToken, str], parti: int, staffi: int, voicei: int): if isinstance(tok, str): tok = HumdrumToken(tok) if not 0 <= parti < len(self.parts): raise HumdrumInternalError( f'Error: part index {parti} is out of range(0, {len(self.parts)})' ) if staffi < 0: raise HumdrumInternalError(f'Error: staff index {staffi} < 0') part: GridPart = self.parts[parti] # fill in enough staves to get you to staffi if staffi >= len(part.staves): for _ in range(len(part.staves), staffi + 1): part.staves.append(GridStaff()) staff: GridStaff = part.staves[staffi] # fill in enough voices to get you to voicei if voicei >= len(staff.voices): for _ in range(len(staff.voices), voicei + 1): staff.voices.append(GridVoice()) voice: GridVoice = staff.voices[voicei] # Ok, finally do what you came to do... voice.token = tok
def appendToken(self, token, tabCount: int = 0): # token can be HumdrumToken or str if isinstance(token, str): token = HumdrumToken(token) self._tokens.append(token) self._numTabsAfterToken.append(tabCount)
def dataType(self): # -> HumdrumToken if self._dataTypeTokenCached is not None: return self._dataTypeTokenCached from converter21.humdrum import HumdrumToken if self.ownerLine is None: return HumdrumToken('') tok = self.ownerLine.trackStart(self.track) if tok is None: return HumdrumToken('') # cache it self._dataTypeTokenCached = tok return tok
def addToken(self, tok: t.Union[HumdrumToken, str], parti: int, staffi: int, voicei: int) -> None: if isinstance(tok, str): tok = HumdrumToken(tok) if not 0 <= parti < len(self.parts): raise HumdrumInternalError( f'Error: part index {parti} is out of range(0, {len(self.parts)})' ) if staffi < 0: raise HumdrumInternalError(f'Error: staff index {staffi} < 0') part: GridPart = self.parts[parti] # fill in enough staves to get you to staffi if staffi >= len(part.staves): for _ in range(len(part.staves), staffi + 1): part.staves.append(GridStaff()) staff: GridStaff = part.staves[staffi] # fill in enough voices to get you to voicei if voicei >= len(staff.voices): for _ in range(len(staff.voices), voicei + 1): staff.voices.append(GridVoice()) voice: t.Optional[GridVoice] = staff.voices[voicei] if t.TYPE_CHECKING: # because we just filled staff.voices[voicei] in with a GridVoice if necessary assert isinstance(voice, GridVoice) # Ok, finally do what you came to do... voice.token = tok
def token(self, newToken: Union[HumdrumToken, str]): if isinstance(newToken, HumdrumToken) or newToken is None: self._token = newToken elif isinstance(newToken, str): self._token = HumdrumToken(newToken) else: raise HumdrumInternalError(f'invalid type of token: {newToken}') self._isTransfered = False
def insertToken(self, index: int, token, tabCount: int): # token can be HumdrumToken or str if isinstance(token, HumdrumToken): self._tokens.insert(index, token) self._numTabsAfterToken.insert(index, tabCount) return if isinstance(token, str): self._tokens.insert(index, HumdrumToken(token)) self._numTabsAfterToken.insert(index, tabCount)
def __init__( self, token: t.Optional[t.Union[HumdrumToken, str]] = None, duration: HumNumIn = opFrac(0) ) -> None: if isinstance(token, str): token = HumdrumToken(token) self._token: t.Optional[HumdrumToken] = token self._nextDur = opFrac(duration) # self._prevDur = opFrac(0) # appears to be unused (never set to anything but zero) self._isTransfered: bool = False
def addDynamicsLayoutParameters(self, associatedSlice: GridSlice, partIndex: int, staffIndex: int, locomment: str): if len(self.slices) == 0: # something strange happened: expecting at least one item in measure. return # associatedSlice is supposed to already be in the measure # find owning line (associatedSlice) foundIt: bool = False associatedSliceIdx: int = None for associatedSliceIdx in range(len(self.slices) - 1, -1, -1): # loop in reverse index order gridSlice: GridSlice = self.slices[associatedSliceIdx] if gridSlice is associatedSlice: foundIt = True break if not foundIt: # cannot find owning line (a.k.a. associatedSlice is not in this GridMeasure) return # see if the previous slice is a layout slice we can use prevIdx: int = associatedSliceIdx - 1 prevSlice: GridSlice = self.slices[prevIdx] if prevSlice.isLocalLayoutSlice: prevStaff: GridStaff = prevSlice.parts[partIndex].staves[ staffIndex] if prevStaff.dynamics is None: prevStaff.dynamics = HumdrumToken(locomment) return # if we get here, we couldn't use the previous slice, so we need to insert # a new Layout slice to use, just before the associated slice. insertPoint: int = associatedSliceIdx newSlice: GridSlice = GridSlice(self, associatedSlice.timestamp, SliceType.Layouts) newSlice.initializeBySlice(associatedSlice) self.slices.insert(insertPoint, newSlice) newStaff: GridStaff = newSlice.parts[partIndex].staves[staffIndex] newStaff.dynamics = HumdrumToken(locomment)
def copyStructure(self, fromLine: 'HumdrumLine', nullStr: str) -> None: for fromToken in fromLine.tokens(): newToken: HumdrumToken = HumdrumToken(nullStr) newToken.ownerLine = self newToken.copyStructure(fromToken) self._tokens.append(newToken) self.createLineFromTokens() # pylint: disable=protected-access self._numTabsAfterToken = fromLine._numTabsAfterToken self._rhythmAnalyzed = fromLine._rhythmAnalyzed # pylint: enable=protected-access self.ownerFile = fromLine.ownerFile
def _createRecipTokenFromDuration(duration: HumNum) -> HumdrumToken: duration /= 4 # convert to quarter note units if duration.numerator == 0: # if the GridSlice is at the end of a measure, the # time between the starttime/endtime of the GridSlice should # be subtracted from the endtime of the current GridMeasure. return HumdrumToken('g') if duration.numerator == 1: return HumdrumToken(str(duration.denominator)) if duration.numerator % 3 == 0: dotdur: HumNum = (duration * 2) / 3 if dotdur.numerator == 1: return HumdrumToken(str(dotdur.denominator) + '.') # try to fit to two dots here # try to fit to three dots here return HumdrumToken( str(duration.denominator) + '%' + str(duration.numerator))
def transferSidesFromPart(line: HumdrumLine, part: GridPart, emptyStr: str, maxhcount: int, maxfcount: int): sides: GridSide = part.sides hcount: int = sides.harmonyCount # FIGURED BASS if maxfcount > 0: figuredBass: HumdrumToken = sides.figuredBass if figuredBass is not None: line.appendToken(figuredBass) else: line.appendToken(HumdrumToken(emptyStr)) # HARMONY for _ in range(0, hcount): harmony: HumdrumToken = sides.harmony if harmony is not None: line.appendToken(harmony) else: line.appendToken(HumdrumToken(emptyStr)) for _ in range(hcount, maxhcount): line.appendToken(HumdrumToken(emptyStr))
def __init__(self, token: Union[HumdrumToken, str] = None, duration: HumNum = HumNum(0)): self._token: HumdrumToken = None if isinstance(token, HumdrumToken) or token is None: self._token = token elif isinstance(token, str): self._token = HumdrumToken(token) else: raise HumdrumInternalError(f'invalid type of token: {token}') self._nextDur = duration # self._prevDur = HumNum(0) # appears to be unused (never set to anything but zero) self._isTransfered: bool = False
def setVerse(self, index: int, token: Union[HumdrumToken, str]): if isinstance(token, str): # make it into a proper token token = HumdrumToken(token) if index == self.verseCount: self._verses.append(token) elif index < self.verseCount: # Insert in a slot which might already have a verse token self._verses[index] = token else: # add more than one verse spot, and insert verse: for _ in range(self.verseCount, index + 1): # verseCount through index, inclusive self._verses.append(None) self._verses[index] = token
def setNullTokenLayer(self, layerIndex: int, sliceType: SliceType, nextDur: HumNum): if sliceType == SliceType.Invalid: return if sliceType == SliceType.GlobalLayouts: return if sliceType == SliceType.GlobalComments: return if sliceType == SliceType.ReferenceRecords: return nullStr: str = '' if sliceType < SliceType.Data_: nullStr = '.' elif sliceType <= SliceType.Measure_: nullStr = '=' elif sliceType <= SliceType.Interpretation_: nullStr = '*' elif sliceType <= SliceType.Spined_: nullStr = '!' else: raise HumdrumInternalError( f'!!STRANGE ERROR: {self}, SLICE TYPE: {sliceType}') if layerIndex < len(self.voices): if (self.voices[layerIndex] is not None and self.voices[layerIndex].token is not None): if self.voices[layerIndex].token.text == nullStr: # there is already a null data token here, so don't # replace it. return raise HumdrumExportError( 'Warning, existing token: \'{self.voices[layerIndex].token.text}\' where a null token should be.' ) token: HumdrumToken = HumdrumToken(nullStr) self.setTokenLayer(layerIndex, token, nextDur)
def __init__( self, line: str = '', asGlobalToken: bool = False, ownerFile=None # HumdrumFile ) -> None: from converter21.humdrum import HumdrumFile super().__init__() # initialize the HumHash fields ''' _text: the line's text string ''' if len(line) > 0 and line[-1] == '\n': # strip off any trailing LF line = line[:-1] self._text: str = line ''' // owner: This is the HumdrumFile which manages the given line. ''' self._ownerFile: HumdrumFile = ownerFile ''' // m_lineindex: Used to store the index number of the HumdrumLine in // the owning HumdrumFile object. // This variable is filled by HumdrumFileStructure::analyzeLines(). ''' self._lineIndex: int = -1 ''' // m_tokens: Used to store the individual tab-separated token fields // on a line. These are prepared automatically after reading in // a full line of text (which is accessed throught the string parent // class). If the full line is changed, the tokens are not updated // automatically -- use createTokensFromLine(). Likewise the full // text line is not updated if any tokens are changed -- use // createLineFromTokens() in that case. The second case is more // useful: you can read in a HumdrumFile, tweak the tokens, then // reconstruct the full line and print out again. // This variable is filled by HumdrumFile::read(). // The contents of this vector should be deleted when deconstructing // a HumdrumLine object. ''' self._tokens: t.List[HumdrumToken] = [] if asGlobalToken: self._tokens = [HumdrumToken(line)] ''' // m_tabs: Used to store a count of the number of tabs between // each token on a line. This is the number of tabs after the // token at the given index (so no tabs before the first token). ''' self._numTabsAfterToken: t.List[int] = [] if asGlobalToken: self._numTabsAfterToken = [0] ''' // m_duration: This is the "duration" of a line. The duration is // equal to the minimum time unit of all durational tokens on the // line. This also includes null tokens when the duration of a // previous note in a previous spine is ending on the line, so it is // not just the minimum duration on the line. // This variable is filled by HumdrumFileStructure::analyzeRhythm(). ''' self._duration: HumNum = -1.0 ''' // m_durationFromStart: This is the cumulative duration of all lines // prior to this one in the owning HumdrumFile object. For example, // the first notes in a score start at time 0, If the duration of the // first data line is 1 quarter note, then the durationFromStart for // the second line will be 1 quarter note. // This variable is filled by HumdrumFileStructure::analyzeRhythm(). ''' self._durationFromStart: HumNum = -1.0 ''' // m_durationFromBarline: This is the cumulative duration from the // last barline to the current data line. // This variable is filled by HumdrumFileStructure::analyzeMeter(). ''' self._durationFromBarline: HumNum = -1.0 ''' // m_durationToBarline: This is the duration from the start of the // current line to the next barline in the owning HumdrumFile object. // This variable is filled by HumdrumFileStructure::analyzeMeter(). ''' self._durationToBarline: HumNum = -1.0 ''' // m_linkedParameters: List of Humdrum tokens which are parameters // (mostly only layout parameters at the moment) ''' self._linkedParameters: t.List[HumdrumToken] = [] ''' // m_rhythm_analyzed: True if duration information from HumdrumFile // has been added to line. ''' self._rhythmAnalyzed: bool = False ''' We are also a HumHash, and that was initialized via super() above. But we want our default HumHash prefix to be '!!', not '' ''' self.prefix: str = '!!'
def token(self, newToken: t.Optional[t.Union[HumdrumToken, str]]) -> None: if isinstance(newToken, str): newToken = HumdrumToken(newToken) self._token = newToken self._isTransfered = False
def insertToken(self, index: int, token: t.Union[HumdrumToken, str], tabCount: int = 0) -> None: if isinstance(token, str): token = HumdrumToken(token) self._tokens.insert(index, token) self._numTabsAfterToken.insert(index, tabCount)
def appendToken(self, token: t.Union[HumdrumToken, str], tabCount: int = 0) -> None: if isinstance(token, str): token = HumdrumToken(token) self._tokens.append(token) self._numTabsAfterToken.append(tabCount)
def transferTokens(self, outFile: HumdrumFile, recip: bool): line: HumdrumLine = HumdrumLine() voice: GridVoice = None emptyStr: str = '.' if self.isMeasureSlice: if len(self.parts) > 0: if len(self.parts[0].staves[0].voices) > 0: voice = self.parts[0].staves[0].voices[0] if voice.token is not None: emptyStr = voice.token.text else: emptyStr = '=YYYYYY' else: emptyStr = '=YYYYYY' elif self.isInterpretationSlice: emptyStr = '*' elif self.isLocalLayoutSlice: emptyStr = '!' elif not self.hasSpines: emptyStr = '???' if recip: token: HumdrumToken = None if self.isNoteSlice: token = self._createRecipTokenFromDuration(self.duration) elif self.isClefSlice: token = HumdrumToken('*') emptyStr = '*' elif self.isMeasureSlice: if len(self.parts[0].staves[0]) > 0: voice = self.parts[0].staves[0].voices[0] token = HumdrumToken(voice.token.text) else: token = HumdrumToken('=XXXXX') emptyStr = token.text elif self.isInterpretationSlice: token = HumdrumToken('*') emptyStr = '*' elif self.isGraceSlice: token = HumdrumToken('q') emptyStr = '.' elif self.hasSpines: token = HumdrumToken('55') emptyStr = '!' if token is not None: if self.hasSpines: line.appendToken(token) else: token = None # extract the Tokens from each part/staff for p in range(len(self.parts) - 1, -1, -1): part = self.parts[p] if not self.hasSpines and p != 0: continue for s in range(len(part.staves) - 1, -1, -1): staff = part.staves[s] if not self.hasSpines and s != 0: continue if len(staff.voices) == 0: # 888: fix this later. For now if there are no notes # 888: ... on the staff, add a null token. Fix so that # 888: ... all open voices are given null tokens. line.appendToken(HumdrumToken(emptyStr)) else: for voice in staff.voices: # NOT reversed (voices different from parts/staves) if voice is not None and voice.token is not None: line.appendToken(voice.token) else: line.appendToken(HumdrumToken(emptyStr)) if not self.hasSpines: # Don't add sides to non-spined lines continue maxxcount: int = self.getXmlIdCount(p, s) maxdcount: int = self.getDynamicsCount(p, s) maxvcount: int = self.getVerseCount(p, s) self.transferSidesFromStaff(line, staff, emptyStr, maxxcount, maxdcount, maxvcount) if not self.hasSpines: # Don't add sides to non-spined lines continue maxhcount: int = self.getHarmonyCount(p) maxfcount: int = self.getFiguredBassCount(p) self.transferSidesFromPart(line, part, emptyStr, maxhcount, maxfcount) outFile.appendLine(line)