def __init__(self, offsetStart=0.0, offsetEnd=None, *, includeEndBoundary=True, mustFinishInSpan=False, mustBeginInSpan=True, includeElementsThatEndAtStart=True): super().__init__() self.offsetStart = opFrac(offsetStart) if offsetEnd is None: self.offsetEnd = offsetStart self.zeroLengthSearch = True else: self.offsetEnd = opFrac(offsetEnd) if offsetEnd > offsetStart: self.zeroLengthSearch = False else: self.zeroLengthSearch = True self.mustFinishInSpan = mustFinishInSpan self.mustBeginInSpan = mustBeginInSpan self.includeEndBoundary = includeEndBoundary self.includeElementsThatEndAtStart = includeElementsThatEndAtStart
def makeTupletBrackets(s: m21.stream.Stream, inPlace=False) -> m21.stream.Stream: returnObj = s if inPlace else copy.deepcopy(s) durationList = [ n.duration for n in returnObj.notesAndRests if n.duration.quarterLength > 0 ] if not durationList: logger.info( f"No notes or rests to make tuplet brackets on stream: {s}") return returnObj tupletMap = [] # a list of (tuplet obj / Duration) pairs for dur in durationList: # all Duration objects tupletList = dur.tuplets if tupletList in [(), None]: # no tuplets, length is zero tupletMap.append([None, dur]) elif len(tupletList) > 1: logger.warning( 'got multi-tuplet duration; cannot yet handle this. %s' % repr(tupletList)) elif len(tupletList) == 1: tupletMap.append([tupletList[0], dur]) if tupletList[0] != dur.tuplets[0]: raise Exception( 'cannot access Tuplets object from within DurationTuple.') else: raise Exception('cannot handle these tuplets: %s' % tupletList) # have a list of tuplet, Duration pairs completionCount = 0 # qLen currently filled completionTarget = None # qLen necessary to fill tuplet for i in range(len(tupletMap)): tupletObj, dur = tupletMap[i] tupletPrevious = tupletMap[i - 1][0] if i > 0 else None tupletNext = tupletMap[i + 1][0] if i < len(tupletMap) - 1 else None if tupletObj is not None: completionCount = opFrac(completionCount + dur.quarterLength) # if previous tuplet is None, always start. Always reset completion target if tupletPrevious is None or completionTarget is None: if tupletNext is None: # single tuplet w/o tuplets either side tupletObj.type = 'startStop' tupletObj.bracket = False completionCount = 0 # reset else: tupletObj.type = 'start' completionTarget = tupletObj.totalTupletLength() # if tuplet next is None, always stop # if both previous and next are None, just keep a start # this, below, is optional: # if next normal type is not the same as this one, also stop elif tupletNext is None or completionCount >= completionTarget: tupletObj.type = 'stop' # should be impossible once frozen... completionTarget = None # reset completionCount = 0 # reset elif tupletPrevious is not None and tupletNext is not None: # do not need to change tuplet type; should be None pass return returnObj
def correlateHarmonies(currentMapping, music21Part): # noinspection PyShadowingNames ''' Adds a new :class:`~music21.stream.Part` to an existing offset mapping. >>> from music21 import corpus >>> from music21.figuredBass import checker >>> score = corpus.parse('corelli/opus3no1/1grave').measures(1, 3) >>> v0 = score[0] >>> offsetMapping = checker.createOffsetMapping(v0) >>> v1 = score[1] >>> newMapping = checker.correlateHarmonies(offsetMapping, v1) >>> for (offsets, notes) in sorted(newMapping.items()): ... print("{0!s:15}[{1!s:23}{2!s:21}]".format(offsets, notes[0], notes[1])) (0.0, 1.5) [<music21.note.Note C> <music21.note.Note A>] (1.5, 2.0) [<music21.note.Note C> <music21.note.Note A>] (2.0, 3.0) [<music21.note.Note B-> <music21.note.Note G>] (3.0, 4.0) [<music21.note.Note A> <music21.note.Note F>] (4.0, 6.0) [<music21.note.Note G> <music21.note.Note E>] (6.0, 6.5) [<music21.note.Note A> <music21.note.Note F>] (6.5, 7.0) [<music21.note.Note B-> <music21.note.Note F>] (7.0, 7.5) [<music21.note.Note C> <music21.note.Note F>] (7.5, 8.0) [<music21.note.Note C> <music21.note.Note E>] (8.0, 8.5) [<music21.note.Note C> <music21.note.Note D>] (8.5, 9.0) [<music21.note.Note F> <music21.note.Note D>] (9.0, 9.5) [<music21.note.Note B-> <music21.note.Note D>] (9.5, 10.0) [<music21.note.Note B-> <music21.note.Note G>] (10.0, 10.5) [<music21.note.Note B-> <music21.note.Note E>] (10.5, 11.0) [<music21.note.Note B-> <music21.note.Note C>] (11.0, 12.0) [<music21.note.Note A> <music21.note.Note F>] ''' newMapping = {} for offsets in sorted(currentMapping.keys()): (initOffset, endTime) = offsets notesInRange = music21Part.flatten().notesAndRests.getElementsByOffset( initOffset, offsetEnd=endTime, includeEndBoundary=False, mustFinishInSpan=False, mustBeginInSpan=False, includeElementsThatEndAtStart=False) allNotesSoFar = currentMapping[offsets] for music21GeneralNote in notesInRange: newInitOffset = initOffset newEndTime = endTime if not music21GeneralNote.offset < initOffset: newInitOffset = music21GeneralNote.offset if not music21GeneralNote.offset + music21GeneralNote.quarterLength > endTime: newEndTime = opFrac(music21GeneralNote.offset + music21GeneralNote.quarterLength) allNotesCopy = copy.copy(allNotesSoFar) allNotesCopy.append(music21GeneralNote) newMapping[(newInitOffset, newEndTime)] = allNotesCopy return newMapping
def calculateOrnamentNoteQl(self, busyNotes, simpleNotes=None): ''' Finds the quarter length value for each ornament note assuming busy notes all are an expanded ornament. Expanded ornament total duration is time of all busy notes combined or duration of the first note in simpleNotes when provided. ''' numOrnamentNotes = len(busyNotes) totalDurationQuarterLength = self.calculateOrnamentTotalQl( busyNotes, simpleNotes) return opFrac(totalDurationQuarterLength / numOrnamentNotes)
def calculateOrnamentTotalQl( self, busyNotes: List[note.GeneralNote], simpleNotes: Optional[List[note.GeneralNote]] = None): ''' Returns total length of trill assuming busy notes are all an expanded trill. This is either the time of all busy notes combined or duration of the first note in simpleNotes when provided. ''' if simpleNotes: return simpleNotes[0].duration.quarterLength trillQl = 0 for n in busyNotes: trillQl += float(n.duration.quarterLength) return opFrac(trillQl)
def coreSetElementOffset(self, element: Music21Object, offset: t.Union[int, float, Fraction, OffsetSpecial], *, addElement=False, setActiveSite=True): ''' Sets the Offset for an element, very quickly. Caller is responsible for calling :meth:`~music21.stream.core.coreElementsChanged` afterward. >>> s = stream.Stream() >>> s.id = 'Stream1' >>> n = note.Note('B-4') >>> s.insert(10, n) >>> n.offset 10.0 >>> s.coreSetElementOffset(n, 20.0) >>> n.offset 20.0 >>> n.getOffsetBySite(s) 20.0 ''' # Note: not documenting 'highestTime' is on purpose, since can only be done for # elements already stored at end. Infinite loop. try: offset = opFrac(offset) except TypeError: if offset not in OffsetSpecial: # pragma: no cover raise StreamException( f'Cannot set offset to {offset!r} for {element}') idEl = id(element) if not addElement and idEl not in self._offsetDict: raise StreamException( f'Cannot set the offset for element {element}, not in Stream {self}.' ) self._offsetDict[idEl] = (offset, element) # fast if setActiveSite: self.coreSelfActiveSite(element)
def isElementOffsetInRange(self, e, offset, *, stopAfterEnd=False) -> bool: ''' Given an element, offset, and stream, return True, False, or raise StopIteration if the element is in the range, not in the range, or (if stopAfterEnd is True) is not and no future elements will be in the range. Factored out from __call__ to be used by OffsetHierarchyFilter and it's just a beast. :-) ''' if offset > self.offsetEnd: # anything that begins after the span is definitely out if stopAfterEnd: # if sorted, optimize by breaking after exceeding offsetEnd # eventually we could do a binary search to speed up... raise StopIteration return False dur = e.duration elementEnd = opFrac(offset + dur.quarterLength) if elementEnd < self.offsetStart: # anything that finishes before the span ends is definitely out return False # some part of the element is at least touching some part of span. # all the simple cases done! Now need to filter out those that # are border cases depending on settings if dur.quarterLength == 0: elementIsZeroLength = True else: elementIsZeroLength = False if self.zeroLengthSearch is True and elementIsZeroLength is True: # zero Length Searches -- include all zeroLengthElements return True if self.mustFinishInSpan is True: if elementEnd > self.offsetEnd: # environLocal.warn([elementEnd, offsetEnd, e]) return False if self.includeEndBoundary is False: # we include the end boundary if the search is zeroLength -- # otherwise nothing can be retrieved if elementEnd == self.offsetEnd: return False if self.mustBeginInSpan is True: if offset < self.offsetStart: return False if self.includeEndBoundary is False and offset == self.offsetEnd: return False elif (elementIsZeroLength is False and elementEnd == self.offsetEnd and self.zeroLengthSearch is True): return False if self.includeEndBoundary is False and offset == self.offsetEnd: return False if self.includeElementsThatEndAtStart is False and elementEnd == self.offsetStart: return False return True
def makeBeams(s: m21.stream.Stream, *, inPlace=False) -> m21.stream.Stream: returnObj = s if inPlace else copy.deepcopy(s) mColl: List[m21.stream.Measure] if 'Measure' in s.classes: mColl = [returnObj] # store a list of measures for processing else: mColl = list( returnObj.iter.getElementsByClass('Measure')) # a list of measures if not mColl: raise m21.stream.StreamException( 'Cannot process a stream that is neither a Measure nor has no Measures' ) lastTimeSignature = None for m in mColl: # this means that the first of a stream of time signatures will be used if m.timeSignature is not None: lastTimeSignature = m.timeSignature if lastTimeSignature is None: raise m21.stream.StreamException( 'cannot process beams in a Measure without a time signature') noteGroups = [] if m.hasVoices(): for v in m.voices: noteGroups.append(v.notesAndRests.stream()) else: noteGroups.append(m.notesAndRests.stream()) for noteStream in noteGroups: if len(noteStream) <= 1: continue # nothing to beam realNotes = [n for n in noteStream if n.duration.quarterLength > 0] durList = [n.duration for n in realNotes] # error check; call before sending to time signature, as, if this # fails, it represents a problem that happens before time signature # processing summed = sum([d.quarterLength for d in durList]) durSum = opFrac(opFrac( summed)) # the double call corrects for tiny errors in adding # floats and Fractions in the sum() call -- the first opFrac makes it # impossible to have 4.00000000001, but returns Fraction(4, 1). The # second call converts Fraction(4, 1) to 4.0 barQuarterLength = lastTimeSignature.barDuration.quarterLength if durSum > barQuarterLength: continue # getBeams offset = 0.0 if m.paddingLeft != 0.0: offset = opFrac(m.paddingLeft) elif noteStream.highestTime < barQuarterLength: offset = barQuarterLength - noteStream.highestTime beamsList = lastTimeSignature.getBeams(realNotes, measureStartOffset=offset) for n, beams in zip(realNotes, beamsList): n.beams = beams if beams is not None else m21.beam.Beams() del mColl # remove Stream no longer needed returnObj.streamStatus.beams = True return returnObj