def _parseEventsIn(self, m21Stream: Union[m21.stream.Voice, m21.stream.Measure], voiceIndex: int, emptyStartDuration: HumNum = HumNum(0), emptyEndDuration: HumNum = HumNum(0)): if emptyStartDuration > 0: # make m21 hidden rests totalling this duration, and pretend they # were at the beginning of m21Stream durations: List[HumNum] = Convert.getPowerOfTwoDurationsWithDotsAddingTo(emptyStartDuration) startTime: HumNum = self.startTime for duration in durations: m21StartRest: m21.note.Rest = m21.note.Rest(duration=m21.duration.Duration(duration)) m21StartRest.style.hideObjectOnPrint = True event: EventData = EventData(m21StartRest, -1, voiceIndex, self, offsetInScore=startTime) if event is not None: self.events.append(event) startTime += duration m21FlatStream: Union[m21.stream.Voice, m21.stream.Measure] = m21Stream.flat for elementIndex, element in enumerate(m21FlatStream): event: EventData = EventData(element, elementIndex, voiceIndex, self) if event is not None: self.events.append(event) if emptyEndDuration > 0: # make m21 hidden rests totalling this duration, and pretend they # were at the end of m21Stream durations: List[HumNum] = Convert.getPowerOfTwoDurationsWithDotsAddingTo(emptyEndDuration) startTime: HumNum = self.startTime + self.duration - emptyEndDuration for duration in durations: m21EndRest: m21.note.Rest = m21.note.Rest(duration=m21.duration.Duration(duration)) m21EndRest.style.hideObjectOnPrint = True event: EventData = EventData(m21EndRest, -1, voiceIndex, self, offsetInScore=startTime) if event is not None: self.events.append(event) startTime += duration
def _parseEventsIn( self, m21Stream: t.Union[m21.stream.Voice, m21.stream.Measure], voiceIndex: int, emptyStartDuration: HumNumIn = 0, emptyEndDuration: HumNumIn = 0 ) -> None: event: EventData durations: t.List[HumNum] startTime: HumNum if emptyStartDuration > 0: # make m21 hidden rests totalling this duration, and pretend they # were at the beginning of m21Stream durations = Convert.getPowerOfTwoDurationsWithDotsAddingTo(emptyStartDuration) startTime = self.startTime for duration in durations: m21StartRest: m21.note.Rest = m21.note.Rest( duration=m21.duration.Duration(duration) ) m21StartRest.style.hideObjectOnPrint = True event = EventData(m21StartRest, -1, voiceIndex, self, offsetInScore=startTime) if event is not None: self.events.append(event) startTime = opFrac(startTime + duration) for elementIndex, element in enumerate(m21Stream.recurse() .getElementsNotOfClass(m21.stream.Stream)): event = EventData(element, elementIndex, voiceIndex, self) if event is not None: self.events.append(event) # Make a separate event for any DynamicWedge (in score's # spannerBundle) that starts with this element. # Why? # 1. So we don't put the end of the wedge in the same slice as the # endNote (wedges end at the end time of the endNote, not at # the start time of the endNote). # 2. So wedge starts/ends will go in their own slice if necessary (e.g. # if we choose not to export the voice-with-only-invisible-rests we # may have made to position them correctly. extraEvents: t.List[EventData] = self._parseDynamicWedgesStartedOrStoppedAt(event) if extraEvents: self.events += extraEvents if emptyEndDuration > 0: # make m21 hidden rests totalling this duration, and pretend they # were at the end of m21Stream durations = Convert.getPowerOfTwoDurationsWithDotsAddingTo(emptyEndDuration) startTime = opFrac(self.startTime + self.duration - opFrac(emptyEndDuration)) for duration in durations: m21EndRest: m21.note.Rest = m21.note.Rest(duration=m21.duration.Duration(duration)) m21EndRest.style.hideObjectOnPrint = True event = EventData(m21EndRest, -1, voiceIndex, self, offsetInScore=startTime) if event is not None: self.events.append(event) startTime = opFrac(startTime + duration)
def _parseEventsAtTopLevelOf(self, m21Stream: m21.stream.Measure): for elementIndex, element in enumerate(m21Stream): if 'Stream' in element.classes: # skip substreams, just parse the top-level objects continue event: EventData = EventData(element, elementIndex, -1, self) if event is not None: self.events.append(event)
def _parseMeasure(self): self._setStartTimeOfMeasure() self._duration = HumNum(self.m21Measure.duration.quarterLength) # m21 tracks timesigdur for us in barDuration, which is always the latest timeSig duration # But it can be ridiculously slow if it has to search back in the score for a timesignature, # so we only call it if the measure has a timesignature. If it doesn't have one, we # just use the _timeSigDur of the previous measure. if self.m21Measure.timeSignature is None and self._prevMeasData is not None: self._timeSigDur = self._prevMeasData.timeSigDur else: self._timeSigDur = HumNum(self.m21Measure.barDuration.quarterLength) self._measureNumberString = self.m21Measure.measureNumberWithSuffix() if self._measureNumberString == '0': self._measureNumberString = '' # In humdrum, measure style is a combination of this measure's left barline and the # previous measure's right barline. (For example, ':|!|:' in a humdrum barline comes # from the previous right barline being an end-repeat (light-heavy) and the current # left barline being a start repeat (heavy-light)). # The very last humdrum barline (last measure's right barline) is handled as a special # case at a higher level, not here. # Compute our left and right barline style self.leftBarlineStyle = M21Convert.measureStyleFromM21Barline(self.m21Measure.leftBarline) self.rightBarlineStyle = M21Convert.measureStyleFromM21Barline(self.m21Measure.rightBarline) # Grab the previous measure's right barline style (if there is one) and # combine it with our left barline style, giving our measureStyle. prevRightMeasureStyle: MeasureStyle = MeasureStyle.Regular if self._prevMeasData is not None: prevRightMeasureStyle = self._prevMeasData.rightBarlineStyle self.measureStyle = M21Convert.combineTwoMeasureStyles(self.leftBarlineStyle, prevRightMeasureStyle) # measure index 0 only: add events for any Instruments found in ownerStaff.m21PartStaff if self.measureIndex == 0: for elementIndex, inst in enumerate( self.ownerStaff.m21PartStaff.getElementsByClass(m21.instrument.Instrument)): event: EventData = EventData(inst, elementIndex, -1, self) if event is not None: self.events.append(event) if len(list(self.m21Measure.voices)) == 0: # treat the measure itself as voice 0 self._parseEventsIn(self.m21Measure, 0) else: # parse the 0-offset non-streams first... self._parseEventsAtTopLevelOf(self.m21Measure) # ... then parse the voices for voiceIndex, voice in enumerate(self.m21Measure.voices): emptyStartDuration: HumNum = HumNum(voice.offset) emptyEndDuration: HumNum = HumNum(self.duration - (HumNum(voice.offset) + HumNum(voice.duration.quarterLength))) self._parseEventsIn(voice, voiceIndex, emptyStartDuration, emptyEndDuration) self._sortEvents()
def _parseEventsAtTopLevelOf(self, m21Stream: m21.stream.Measure) -> None: for elementIndex, element in enumerate(m21Stream): if 'Stream' in element.classes: # skip substreams, just parse the top-level objects continue event: EventData = EventData(element, elementIndex, -1, self) if event is not None: self.events.append(event) extraEvents: t.List[EventData] = self._parseDynamicWedgesStartedOrStoppedAt(event) if extraEvents: self.events += extraEvents
def _parseDynamicWedgesStartedOrStoppedAt(self, event: EventData) -> t.List[EventData]: if t.TYPE_CHECKING: assert isinstance(event.m21Object, m21.note.GeneralNote) output: t.List[EventData] = [] wedges: t.List[m21.dynamics.DynamicWedge] = ( M21Utilities.getDynamicWedgesStartedOrStoppedWithGeneralNote( event.m21Object, self.spannerBundle) ) wedge: m21.dynamics.DynamicWedge for wedge in wedges: ownerScore = self.ownerStaff.ownerPart.ownerScore score: m21.stream.Score = ownerScore.m21Score startNote: m21.note.GeneralNote = wedge.getFirst() endNote: m21.note.GeneralNote = wedge.getLast() thisEventIsStart: bool = startNote is event.m21Object thisEventIsEnd: bool = endNote is event.m21Object wedgeStartTime: t.Optional[HumNum] = None wedgeDuration: t.Optional[HumNum] = None wedgeEndTime: HumNum = opFrac( endNote.getOffsetInHierarchy(score) + endNote.duration.quarterLength ) if thisEventIsStart: wedgeStartTime = startNote.getOffsetInHierarchy(score) wedgeDuration = opFrac(wedgeEndTime - wedgeStartTime) if thisEventIsStart and thisEventIsEnd: # print(f'wedgeStartStopEvent: {event}', file=sys.stderr) # one note for the wedge? let's make one event, so it can become '>]' or whatever wedgeStartStopEvent: EventData = EventData(wedge, -1, event.voiceIndex, self, offsetInScore=wedgeStartTime, duration=wedgeDuration) output.append(wedgeStartStopEvent) elif thisEventIsStart: # add the start event, with duration # print(f'wedgeStartEvent: {event}', file=sys.stderr) wedgeStartEvent: EventData = EventData(wedge, -1, event.voiceIndex, self, offsetInScore=wedgeStartTime, duration=wedgeDuration) output.append(wedgeStartEvent) elif thisEventIsEnd: # add the end event (duration == 0) # but add it to the same measure/voice as the wedge start event # print(f'wedgeStopEvent: {event}', file=sys.stderr) matchingStartEvent: t.Optional[EventData] = ( ownerScore.eventFromM21Object.get(id(wedge), None) ) endVoiceIndex: int if matchingStartEvent is None: # print('wedgeStop with no wedgeStart, putting it in its own measure/voice', # file=sys.stderr) endVoiceIndex = event.voiceIndex endSelf = self else: endVoiceIndex = matchingStartEvent.voiceIndex endSelf = matchingStartEvent.ownerMeasure wedgeEndEvent: EventData = EventData(wedge, -1, endVoiceIndex, endSelf, offsetInScore=wedgeEndTime, duration=0) output.append(wedgeEndEvent) return output
def _parseMeasure(self) -> None: self._setStartTimeOfMeasure() self._duration = self.m21Measure.duration.quarterLength # m21 tracks timesigdur for us in barDuration, which is always the latest timeSig duration # But it can be ridiculously slow if it has to search back in the score for a timesignature, # so we only call it if the measure has a timesignature. If it doesn't have one, we # just use the _timeSigDur of the previous measure. if self.m21Measure.timeSignature is None and self._prevMeasData is not None: self._timeSigDur = self._prevMeasData.timeSigDur else: self._timeSigDur = self.m21Measure.barDuration.quarterLength self._measureNumberString = self.m21Measure.measureNumberWithSuffix() if self._measureNumberString == '0': self._measureNumberString = '' # In humdrum, measure style is a combination of this measure's left barline and the # previous measure's right barline. (For example, ':|!|:' in a humdrum barline comes # from the previous right barline being an end-repeat (light-heavy) and the current # left barline being a start repeat (heavy-light)). # The very last humdrum barline (last measure's right barline) is handled as a special # case at a higher level, not here. # Compute our left and right barline style self.leftBarlineStyle = M21Convert.measureStyleFromM21Barline(self.m21Measure.leftBarline) self.rightBarlineStyle = M21Convert.measureStyleFromM21Barline(self.m21Measure.rightBarline) # Grab the previous measure's right barline style (if there is one) and # combine it with our left barline style, giving our measureStyle. prevRightMeasureStyle: MeasureStyle = MeasureStyle.Regular if self._prevMeasData is not None: prevRightMeasureStyle = self._prevMeasData.rightBarlineStyle self.measureStyle = M21Convert.combineTwoMeasureStyles(self.leftBarlineStyle, prevRightMeasureStyle) # Extract left and right barline Fermata self.leftBarlineFermataStyle = M21Convert.fermataStyleFromM21Barline( self.m21Measure.leftBarline ) self.rightBarlineFermataStyle = M21Convert.fermataStyleFromM21Barline( self.m21Measure.rightBarline ) # Grab the previous measure's right barline fermata style (if there is one) and # combine it with our left barline fermata style, giving our fermataStyle. prevRightBarlineFermataStyle: FermataStyle = FermataStyle.NoFermata if self._prevMeasData is not None: prevRightBarlineFermataStyle = self._prevMeasData.rightBarlineFermataStyle self.fermataStyle = M21Convert.combineTwoFermataStyles( self.leftBarlineFermataStyle, prevRightBarlineFermataStyle ) # measure index 0 only: add events for any Instruments found in ownerStaff.m21PartStaff if self.measureIndex == 0: for elementIndex, inst in enumerate( self.ownerStaff.m21PartStaff.getElementsByClass(m21.instrument.Instrument)): event: EventData = EventData(inst, elementIndex, -1, self) if event is not None: self.events.append(event) # parse any RepeatBracket this measure is in. for rb in self.m21Measure.getSpannerSites([m21.spanner.RepeatBracket]): # measure is in this RepeatBracket if t.TYPE_CHECKING: assert isinstance(rb, m21.spanner.RepeatBracket) self.inRepeatBracket = True if rb.overrideDisplay: self.repeatBracketName = rb.overrideDisplay else: self.repeatBracketName = rb.number if rb.getFirst() == self.m21Measure: # this measure starts the RepeatBracket (it may also stop it) self.startsRepeatBracket = True if rb.getLast() == self.m21Measure: # this measure stops the RepeatBracket (it may also start it) self.stopsRepeatBracket = True # a measure can only be in one RepeatBracket, so stop looking break if len(list(self.m21Measure.voices)) == 0: # treat the measure itself as voice 0 self._parseEventsIn(self.m21Measure, 0) else: # first parse the voices... for voiceIndex, voice in enumerate(self.m21Measure.voices): emptyStartDuration: HumNum = voice.offset emptyEndDuration: HumNum = opFrac( self.duration - (voice.offset + voice.duration.quarterLength) ) self._parseEventsIn(voice, voiceIndex, emptyStartDuration, emptyEndDuration) # ... then parse the non-streams last, so that the ending barline lands after # any objects in the voices that are also at the last offset in the measure # (e.g. TextExpressions after the last note in the measure). self._parseEventsAtTopLevelOf(self.m21Measure) self._sortEvents()