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 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 m21VersionIsAtLeast( neededVersion: t.Tuple[int, int, int, str]) -> bool: # m21.VERSION[0] * 10000 + m21.VERSION[1] * 100 + m21.VERSION[2] if len(m21.VERSION) == 0: raise HumdrumInternalError('music21 version must be set!') # compare element 0 if m21.VERSION[0] < neededVersion[0]: return False if m21.VERSION[0] > neededVersion[0]: return True # element 0 is equal... go on to next element if len(m21.VERSION) == 1 or len(neededVersion) == 1: # there is no next element to compare, so we are done. # result is True only if m21 version has >= elements of needed version. # if neededVersion has more elements, then result is False return len(m21.VERSION) >= len(neededVersion) # compare element 1 if m21.VERSION[1] < neededVersion[1]: return False if m21.VERSION[1] > neededVersion[1]: return True # element 1 is equal... go on to next element if len(m21.VERSION) == 2 or len(neededVersion) == 2: # there is no next element to compare, so we are done. # result is True only if m21 version has >= elements of needed version. # if neededVersion has more elements, then result is False return len(m21.VERSION) >= len(neededVersion) # compare element 2 if m21.VERSION[2] < neededVersion[2]: return False if m21.VERSION[2] > neededVersion[2]: return True # element 2 is equal... go on to next element if len(m21.VERSION) == 3 or len(neededVersion) == 3: # there is no next element to compare, so we are done. # result is True only if m21 version has >= elements of needed version. # if neededVersion has more elements, then result is False return len(m21.VERSION) >= len(neededVersion) # compare element 3 (probably a string) if m21.VERSION[3] < neededVersion[3]: return False if m21.VERSION[3] > neededVersion[3]: return True return True # four elements equal, that's all we care about
def setTokenLayer(self, layerIndex: int, token: HumdrumToken, duration: HumNum) -> GridVoice: if layerIndex < 0: raise HumdrumInternalError( f'Error: layer index is {layerIndex} for {token}') if layerIndex > len(self.voices) - 1: for _ in range(len(self.voices), layerIndex + 1): # range includes layerIndex self.voices.append(None) gv: GridVoice = GridVoice(token, duration) self.voices[layerIndex] = gv return gv
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 analyzeTextRepetition(self) -> None: spineStarts: t.List[t.Optional[HumdrumToken]] = self.spineStartList for start in spineStarts: ijstate: bool = False startij: bool = False lastIJToken: t.Optional[ HumdrumToken] = None # last token seen in ij range if start is None: raise HumdrumInternalError( 'start that is None found in spineStarts') # BUGFIX: **sylb -> **silbe if not start.isDataType('**text') and not start.isDataType( '**silbe'): continue current: t.Optional[HumdrumToken] = start while current is not None: if current.isNull: current = current.nextToken0 continue if current.isInterpretation: if current.text == '*ij': startij = True ijstate = True elif current.text == '*Xij': startij = False ijstate = False if lastIJToken is not None: lastIJToken.setValue('auto', 'ij-end', 'true') lastIJToken = None current = current.nextToken0 continue if current.isData: if ijstate: current.setValue('auto', 'ij', 'true') if startij: current.setValue('auto', 'ij-begin', 'true') startij = False lastIJToken = current current = current.nextToken0
def resolveNullTokens(self) -> None: if self._analyses.nullsAnalyzed: return self._analyses.nullsAnalyzed = True if not self.areStrandsAnalyzed: self.analyzeStrands() data: t.Optional[HumdrumToken] = None strandPair: TokenPair for strandPair in self._strand1d: token: t.Optional[HumdrumToken] = strandPair.first strandEnd: t.Optional[HumdrumToken] = strandPair.last if t.TYPE_CHECKING: # after analyzeStrands, no pair in self._strand1d will have Nones in it. assert token is not None assert strandEnd is not None while token != strandEnd: if token is None: # we never reached strandEnd, but we're done raise HumdrumInternalError( f'never found strandEnd ({strandEnd})') if not token.isData: token = token.nextToken0 continue if data is None: data = token token.nullResolution = data token = token.nextToken0 continue if token.isNull: token.nullResolution = data else: data = token token = token.nextToken0
def base40ToKern(b40: int) -> str: octave: int = b40 // 40 accidental: int = Convert.base40ToAccidental(b40) diatonic: int = Convert.base40ToDiatonic(b40) % 7 base: str = 'a' if diatonic == 0: base = 'c' elif diatonic == 1: base = 'd' elif diatonic == 2: base = 'e' elif diatonic == 3: base = 'f' elif diatonic == 4: base = 'g' elif diatonic == 5: base = 'a' elif diatonic == 6: base = 'b' if octave < 4: base = base.upper() repeat: int = 0 if octave > 4: repeat = octave - 4 elif octave < 3: repeat = 3 - octave if repeat > 12: raise HumdrumInternalError( f'Error: unreasonable octave value: {octave} for {b40}') output: str = base * (1 + repeat) if accidental > 0: output += '#' * accidental elif accidental < 0: output += '-' * -accidental return output
def __init__(self, ownerGrid) -> None: from converter21.humdrum import HumGrid if not isinstance(ownerGrid, HumGrid): raise HumdrumInternalError('invalid ownerGrid') self._ownerGrid: HumGrid = ownerGrid self.slices: t.List[GridSlice] = [] self._timestamp: HumNum = opFrac(-1) self._duration: HumNum = opFrac(-1) self._timeSigDur: HumNum = opFrac(-1) self.leftBarlineStyle: MeasureStyle = MeasureStyle.Regular self.rightBarlineStyle: MeasureStyle = MeasureStyle.Regular self.fermataStylePerStaff: t.List[FermataStyle] = [] self.measureStyle: MeasureStyle = MeasureStyle.Regular self.measureNumberString: str = '' self.inRepeatBracket: bool = False self.startsRepeatBracket: bool = False self.stopsRepeatBracket: bool = False self.repeatBracketName: str = '' # only used on last measure in score self.rightBarlineFermataStylePerStaff: t.List[FermataStyle] = []
def setOrigin(self, *ns1ns2keyvalue): # origin: HumdrumToken from converter21.humdrum import HumdrumToken origin = ns1ns2keyvalue[-1] ns1ns2key = ns1ns2keyvalue[:-1] ns1, ns2, key = fixupNamespace1Namespace2Key(*ns1ns2key) if not isinstance(origin, HumdrumToken): raise HumdrumInternalError( 'invalid origin token in HumHash.setOrigin') if self._parameters is None: return if ns1 not in self._parameters: return if ns2 not in self._parameters[ns1]: return if key not in self._parameters[ns1][ns2]: return self._parameters[ns1][ns2][key].origin = origin
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, ownerMeasure, timestamp: HumNumIn, sliceType: SliceType, partCount: int = 0, fromSlice: t.Optional['GridSlice'] = None) -> None: from converter21.humdrum import GridMeasure from converter21.humdrum import HumGrid self._timestamp: HumNum = opFrac(timestamp) self._type: SliceType = sliceType if not isinstance(ownerMeasure, GridMeasure): raise HumdrumInternalError('GridSlice init: invalid ownerMeasure') self._measure: GridMeasure = ownerMeasure # enclosing measure self._ownerGrid: HumGrid = self._measure.ownerGrid # measure's enclosing grid self.parts: t.List[GridPart] = [] if fromSlice is None: # GridSlice::GridSlice -- Constructor. If partcount is positive, then # allocate the desired number of parts (still have to allocate staves # in part before using). for _ in range(0, partCount): part: GridPart = GridPart() staff: GridStaff = GridStaff() voice: GridVoice = GridVoice() self.parts.append(part) part.staves.append(staff) staff.voices.append(voice) else: # This constructor allocates the matching part and staff count of the # input fromSlice parameter. There will be no GridVoices allocated inside # the GridStaffs (they will be required to have at least one). for fromPart in fromSlice.parts: part = GridPart() self.parts.append(part) for _ in fromPart.staves: staff = GridStaff() part.staves.append(staff)
def addGraceToken(self, tok: str, timestamp: HumNum, part: int, staff: int, voice: int, maxStaff: int, graceNumber: int) -> GridSlice: if graceNumber < 1: raise HumdrumInternalError( 'ERROR: graceNumber {} has to be larger than 0') gs: GridSlice = None if not self.slices: # add a new GridSlice to an empty list. gs = GridSlice(self, timestamp, SliceType.GraceNotes, maxStaff) gs.addToken(tok, part, staff, voice) self.slices.append(gs) return gs if timestamp > self.slices[-1].timestamp: # Grace note needs to be added at the end of a measure. idx: int = len(self.slices) - 1 # pointing at last slice counter: int = 0 while idx >= 0: if self.slices[idx].isGraceSlice: counter += 1 if counter == graceNumber: # insert grace note into this slice self.slices[idx].addToken(tok, part, staff, voice) return self.slices[idx] elif self.slices[idx].isLocalLayoutSlice: # skip over any layout parameter lines idx -= 1 continue if self.slices[idx].isDataSlice: # insert grace note after this note gs = GridSlice(self, timestamp, SliceType.GraceNotes, maxStaff) gs.addToken(tok, part, staff, voice) self.slices.insert(idx + 1, gs) return gs idx -= 1 return None # couldn't find anywhere to insert # search for existing line with same timestamp on a data slice: foundIndex: int = -1 for idx, gridSlice in enumerate(self.slices): if timestamp < gridSlice.timestamp: raise HumdrumInternalError( f'''STRANGE CASE 2 IN GRIDMEASURE::ADDGRACETOKEN \tGRACE TIMESTAMP: {timestamp} \tTEST TIMESTAMP: {gridSlice.timestamp}''') if gridSlice.isDataSlice: if gridSlice.timestamp == timestamp: foundIndex = idx break idx: int = foundIndex - 1 counter: int = 0 while idx >= 0: if self.slices[idx].isGraceSlice: counter += 1 if counter == graceNumber: # insert grace note into this slice self.slices[idx].addToken(tok, part, staff, voice) return self.slices[idx] elif self.slices[idx].isLocalLayoutSlice: # skip over any layout parameter lines idx -= 1 continue if self.slices[idx].isDataSlice: # insert grace note after this note gs = GridSlice(self, timestamp, SliceType.GraceNotes, maxStaff) gs.addToken(tok, part, staff, voice) self.slices.insert(idx + 1, gs) return gs idx -= 1 # grace note should be added at start of measure gs = GridSlice(self, timestamp, SliceType.GraceNotes, maxStaff) gs.addToken(tok, part, staff, voice) self.slices.insert(0, gs) return gs
def ownerGrid(self, newOwnerGrid) -> None: # newOwnerGrid: HumGrid from converter21.humdrum import HumGrid if not isinstance(newOwnerGrid, HumGrid): raise HumdrumInternalError('invalid newOwnerGrid') self._ownerGrid = newOwnerGrid
def __init__(self, score: m21.stream.Score, ownerWriter) -> None: from converter21.humdrum import HumdrumWriter self.ownerWriter: HumdrumWriter = ownerWriter if not isinstance(score, m21.stream.Score): raise HumdrumInternalError('ScoreData must be initialized with a music21 Score object') self.m21Score: m21.stream.Score = score self.spannerBundle: m21.spanner.SpannerBundle = ownerWriter.spannerBundle self.parts: t.List[PartData] = [] # key is id(m21Object): a large integer that is actually the mem address of m21Object self.eventFromM21Object: t.Dict[int, EventData] = {} # Following staff group code stolen from musicxml exporter in music21. # Keep it up-to-date! staffGroups = self.m21Score.getElementsByClass('StaffGroup') joinableGroups: t.List[m21.layout.StaffGroup] = [] # Joinable groups must consist of only PartStaffs with Measures # that exist in self.m21Score for sg in staffGroups: if len(sg) <= 1: continue if not all('PartStaff' in p.classes for p in sg): continue if not all(p.getElementsByClass('Measure') for p in sg): continue if not all(p in self.m21Score.parts for p in sg): continue joinableGroups.append(sg) # Deduplicate joinable groups (ex: bracket and brace enclose same PartStaffs) permutations = set() deduplicatedGroups: t.List[m21.layout.StaffGroup] = [] for jg in joinableGroups: containedParts = tuple(jg) if containedParts not in permutations: deduplicatedGroups.append(jg) permutations.add(containedParts) joinableGroups = deduplicatedGroups partsWithMoreThanOneStaff: t.List[t.List[m21.stream.Part]] = [] groupedParts: t.List[m21.stream.PartStaff] = [] for jg in joinableGroups: partsWithMoreThanOneStaff.append([]) for partStaff in jg: partsWithMoreThanOneStaff[-1].append(partStaff) groupedParts.append(partStaff) scorePartsStillToProcess = list(score.parts) for part in score.parts: # includes PartStaffs, too if part not in scorePartsStillToProcess: # we already processed this due to a staff group continue if 'PartStaff' in part.classes and part in groupedParts: # make a new partData entry for these PartStaffs and fill it for partStaffList in partsWithMoreThanOneStaff: if part in partStaffList: partOfStavesData: PartData = PartData(partStaffList, self, len(self.parts)) self.parts.append(partOfStavesData) for ps in partStaffList: scorePartsStillToProcess.remove(ps) # so we don't double process break else: # make a new partData entry for the Part (one staff which is the part) partData: PartData = PartData([part], self, len(self.parts)) self.parts.append(partData) scorePartsStillToProcess.remove(part)