def _parseAttributes(self, src): # assume that we have already checked that this is a measure g = reMeasureTag.match(src) if g is None: # not measure tag found raise RTHandlerException('found no measure tag: %s' % src) iEnd = g.end() # get end index rawTag = src[:iEnd].strip() self.tag = rawTag rawData = src[iEnd:].strip() # may have variant # get the number list from the tag self.number, self.repeatLetter = self._getMeasureNumberData(rawTag) # strip a variant indication off of rawData if found g = reVariant.match(rawData) if g is not None: # there is a variant tag varStr = g.group(0) self.variantNumber = int(common.getNumFromStr(varStr)[0]) self.data = rawData[g.end():].strip() else: self.data = rawData g = reVariantLetter.match(rawData) if g is not None: # there is a variant letter tag varStr = g.group(1) self.variantLetter = varStr self.data = rawData[g.end():].strip() if self.data.startswith('='): self.isCopyDefinition = True
def _getMeasureNumberData(self, src): '''Return the number or numbers as a list, as well as any repeat indications. >>> rtm = romanText.RTMeasure() >>> rtm._getMeasureNumberData('m77') ([77], ['']) >>> rtm._getMeasureNumberData('m123b-432b') ([123, 432], ['b', 'b']) ''' # note: this is separate procedure b/c it is used to get copy # boundaries if '-' in src: # its a range mnStart, mnEnd = src.split('-') proc = [mnStart, mnEnd] else: proc = [src] # treat as one number = [] repeatLetter = [] for mn in proc: # append in order, start, end numStr, alphaStr = common.getNumFromStr(mn) number.append(int(numStr)) # remove all 'm' in alpha alphaStr = alphaStr.replace('m', '') repeatLetter.append(alphaStr) return number, repeatLetter
def _getMeasureNumberData(self, src): '''Return the number or numbers as a list, as well as any repeat indications. >>> rtm = romanText.rtObjects.RTMeasure() >>> rtm._getMeasureNumberData('m77') ([77], ['']) >>> rtm._getMeasureNumberData('m123b-432b') ([123, 432], ['b', 'b']) ''' # note: this is separate procedure b/c it is used to get copy # boundaries if '-' in src: # its a range mnStart, mnEnd = src.split('-') proc = [mnStart, mnEnd] else: proc = [src] # treat as one number = [] repeatLetter = [] for mn in proc: # append in order, start, end numStr, alphaStr = common.getNumFromStr(mn) number.append(int(numStr)) # remove all 'm' in alpha alphaStr = alphaStr.replace('m', '') repeatLetter.append(alphaStr) return number, repeatLetter
def slashToTuple(value: str) -> Optional[MeterTerminalTuple]: ''' Returns a three-element MeterTerminalTuple of numerator, denominator, and optional division of the meter. >>> meter.tools.slashToTuple('3/8') MeterTerminalTuple(numerator=3, denominator=8, division=<MeterDivision.NONE>) >>> meter.tools.slashToTuple('7/32') MeterTerminalTuple(numerator=7, denominator=32, division=<MeterDivision.NONE>) >>> meter.tools.slashToTuple('slow 6/8') MeterTerminalTuple(numerator=6, denominator=8, division=<MeterDivision.SLOW>) ''' # split by numbers, include slash valueNumbers, valueChars = common.getNumFromStr(value, numbers='0123456789/.') valueNumbers = valueNumbers.strip() # remove whitespace valueChars = valueChars.strip() # remove whitespace if 'slow' in valueChars.lower(): division = MeterDivision.SLOW elif 'fast' in valueChars.lower(): division = MeterDivision.FAST else: division = MeterDivision.NONE matches = re.match(r'(\d+)/(\d+)', valueNumbers) if matches is not None: n = int(matches.group(1)) d = int(matches.group(2)) return MeterTerminalTuple(n, d, division) else: environLocal.printDebug(['slashToTuple() cannot find two part fraction', value]) return None
def process(self, minWindow=1, maxWindow=1, windowStepSize=1, windowType='overlap', includeTotalWindow=True): ''' Main method for windowed analysis across one or more window sizes. Calls :meth:`~music21.analysis.WindowedAnalysis.analyze` for the number of different window sizes to be analyzed. The `minWindow` and `maxWindow` set the range of window sizes in quarter lengths. The `windowStepSize` parameter determines the increment between these window sizes, in quarter lengths. If `minWindow` or `maxWindow` is None, the largest window size available will be set. If `includeTotalWindow` is True, the largest window size will always be added. >>> s = corpus.parse('bach/bwv324') >>> ksAnalyzer = analysis.discrete.KrumhanslSchmuckler() placing one part into analysis >>> sopr = s.parts[0] >>> wa = analysis.windowed.WindowedAnalysis(sopr, ksAnalyzer) >>> solutions, colors, meta = wa.process(1, 1, includeTotalWindow=False) >>> len(solutions) # we only have one series of windows 1 >>> solutions, colors, meta = wa.process(1, 2, includeTotalWindow=False) >>> len(solutions) # we have two series of windows 2 >>> solutions[1] [(<music21.pitch.Pitch B>, 'major', 0.6868...), (<music21.pitch.Pitch B>, 'minor', 0.8308...), (<music21.pitch.Pitch D>, 'major', 0.6868...), (<music21.pitch.Pitch B>, 'minor', 0.8308...),...] >>> colors[1] ['#ffb5ff', '#9b519b', '#ffd752', '#9b519b', ...] >>> meta [{'windowSize': 1}, {'windowSize': 2}] ''' if maxWindow is None: maxLength = len(self._windowedStream) else: maxLength = maxWindow if minWindow is None: minLength = len(self._windowedStream) else: minLength = minWindow if windowType is None: windowType = 'overlap' elif windowType.lower() in ['overlap']: windowType = 'overlap' elif windowType.lower() in ['nooverlap', 'nonoverlapping']: windowType = 'noOverlap' elif windowType.lower() in ['adjacentaverage']: windowType = 'adjacentAverage' # need to create storage for the output of each row, or the processing # of all windows of a single size across the entire Stream solutionMatrix = [] colorMatrix = [] # store meta data about each row as a dictionary metaMatrix = [] if common.isNum(windowStepSize): windowSizes = list(range(minLength, maxLength + 1, windowStepSize)) else: num, junk = common.getNumFromStr(windowStepSize) windowSizes = [] x = minLength while True: windowSizes.append(x) x = x * round(int(num)) if x > (maxLength * 0.75): break if includeTotalWindow: totalWindow = len(self._windowedStream) if totalWindow not in windowSizes: windowSizes.append(totalWindow) for i in windowSizes: #environLocal.printDebug(['processing window:', i]) # each of these results are lists, where len is based on soln, colorn = self.analyze(i, windowType=windowType) # store lists of results in a list of lists solutionMatrix.append(soln) colorMatrix.append(colorn) meta = {'windowSize': i} metaMatrix.append(meta) return solutionMatrix, colorMatrix, metaMatrix
def _setSrc(self, raw): raw = raw.strip() # get decimals and fractions raw, junk = common.getNumFromStr(raw, numbers='0123456789./') self.src = raw.strip()
def process(self, minWindow=1, maxWindow=1, windowStepSize=1, windowType='overlap', includeTotalWindow=True): ''' Main method for windowed analysis across one or more window size. Calls :meth:`~music21.analysis.WindowedAnalysis._analyze` for the number of different window sizes to be analyzed. The `minWindow` and `maxWindow` set the range of window sizes in quarter lengths. The `windowStepSize` parameter determines the increment between these window sizes, in quarter lengths. If `minWindow` or `maxWindow` is None, the largest window size available will be set. If `includeTotalWindow` is True, the largest window size will always be added. >>> s = corpus.parse('bach/bwv324') >>> p = analysis.discrete.KrumhanslSchmuckler() >>> # placing one part into analysis >>> wa = analysis.windowed.WindowedAnalysis(s.parts[0], p) >>> x, y, z = wa.process(1, 1, includeTotalWindow=False) >>> len(x) # we only have one series of windows 1 >>> y[0][0].startswith('#') # for each window, we get a solution and a color True >>> x[0][0][0] <music21.pitch.Pitch B> >>> x, y, z = wa.process(1, 2, includeTotalWindow=False) >>> len(x) # we have two series of windows 2 >>> x[0][0] # the data returned is processor dependent; here we get (<music21.pitch.Pitch B>, 'major', 0.6868258874056411) >>> y[0][0].startswith('#') # a color is returned for each matching data position True ''' if maxWindow == None: maxLength = len(self._windowedStream) else: maxLength = maxWindow if minWindow == None: minLength = len(self._windowedStream) else: minLength = minWindow if windowType == None: windowType = 'overlap' elif windowType.lower() in ['overlap']: windowType = 'overlap' elif windowType.lower() in ['nooverlap', 'nonoverlapping']: windowType = 'noOverlap' elif windowType.lower() in ['adjacentaverage']: windowType = 'adjacentAverage' # need to create storage for the output of each row, or the processing # of all windows of a single size across the entire Stream solutionMatrix = [] colorMatrix = [] # store meta data about each row as a dictionary metaMatrix = [] if common.isNum(windowStepSize): windowSizes = list(range(minLength, maxLength+1, windowStepSize)) else: num, junk = common.getNumFromStr(windowStepSize) windowSizes = [] x = minLength while True: windowSizes.append(x) x = x * int(round(float(num))) if x > (maxLength * .75): break if includeTotalWindow: totalWindow = len(self._windowedStream) if totalWindow not in windowSizes: windowSizes.append(totalWindow) for i in windowSizes: #environLocal.printDebug(['processing window:', i]) # each of these results are lists, where len is based on soln, colorn = self._analyze(i, windowType=windowType) # store lists of results in a list of lists solutionMatrix.append(soln) colorMatrix.append(colorn) meta = {'windowSize': i} metaMatrix.append(meta) return solutionMatrix, colorMatrix, metaMatrix
def mxToMeasure(mxMeasure, inputM21): '''Translate an mxMeasure (a MusicXML :class:`~music21.musicxml.Measure` object) into a music21 :class:`~music21.stream.Measure`. If an `inputM21` object reference is provided, this object will be configured and returned; otherwise, a new :class:`~music21.stream.Measure` object is created. ''' from music21 import stream from music21 import chord from music21 import dynamics from music21 import key from music21 import note from music21 import layout from music21 import bar from music21 import clef from music21 import meter if inputM21 == None: m = stream.Measure() else: m = inputM21 mNum, mSuffix = common.getNumFromStr(mxMeasure.get('number')) # assume that measure numbers are integers if mNum not in [None, '']: m.number = int(mNum) if mSuffix not in [None, '']: m.numberSuffix = mSuffix data = mxMeasure.get('width') if data != None: # may need to do a format/unit conversion? m.layoutWidth = data junk = mxMeasure.get('implicit') mxAttributes = mxMeasure.get('attributesObj') mxAttributesInternal = True # if we do not have defined mxAttributes, must get from stored attributes if mxAttributes is None: # need to keep track of where mxattributes src is coming from # if attributes are defined in this measure, mxAttributesInternal # is true mxAttributesInternal = False # not all measures have attributes definitions; this # gets the last-encountered measure attributes mxAttributes = mxMeasure.external['attributes'] if mxAttributes is None: raise TranslateException( 'no mxAttribues available for this measure') #environLocal.printDebug(['mxAttriutes clefList', mxAttributes.clefList, # mxAttributesInternal]) if mxAttributesInternal and len(mxAttributes.timeList) != 0: m.timeSignature = meter.TimeSignature() m.timeSignature.mx = mxAttributes.timeList if mxAttributesInternal is True and len(mxAttributes.clefList) != 0: m.clef = clef.Clef() m.clef.mx = mxAttributes.clefList if mxAttributesInternal is True and len(mxAttributes.keyList) != 0: m.keySignature = key.KeySignature() m.keySignature.mx = mxAttributes.keyList if mxAttributes.divisions is not None: divisions = mxAttributes.divisions else: divisions = mxMeasure.external['divisions'] #environLocal.printDebug(['mxToMeasure(): divisions', divisions, mxMeasure.getVoiceCount()]) if mxMeasure.getVoiceCount() > 1: useVoices = True # count from zero for id in mxMeasure.getVoiceIndices(): v = stream.Voice() v.id = id m.insert(0, v) else: useVoices = False # iterate through components found on components list # set to zero for each measure offsetMeasureNote = 0 # offset of note w/n measure mxNoteList = [] # for accumulating notes in chords for i in range(len(mxMeasure)): # try to get the next object for chord comparisons mxObj = mxMeasure[i] if i < len(mxMeasure)-1: mxObjNext = mxMeasure[i+1] else: mxObjNext = None # NOTE: tests have shown that using isinstance() here is much faster # than checking the .tag attribute. # check for backup and forward first if isinstance(mxObj, musicxmlMod.Backup): # resolve as quarterLength, subtract from measure offset offsetMeasureNote -= float(mxObj.duration) / float(divisions) continue elif isinstance(mxObj, musicxmlMod.Forward): # resolve as quarterLength, add to measure offset offsetMeasureNote += float(mxObj.duration) / float(divisions) continue elif isinstance(mxObj, musicxmlMod.Print): # mxPrint objects may be found in a Measure's componetns # contain system layout information mxPrint = mxObj sl = layout.SystemLayout() sl.mx = mxPrint # store at zero position m.insert(0, sl) elif isinstance(mxObj, musicxmlMod.Barline): # repeat is a tag found in the barline object mxBarline = mxObj mxRepeatObj = mxBarline.get('repeatObj') if mxRepeatObj != None: barline = bar.Repeat() else: barline = bar.Barline() barline.mx = mxBarline # configure if barline.location == 'left': #environLocal.printDebug(['setting left barline', barline]) m.leftBarline = barline elif barline.location == 'right': #environLocal.printDebug(['setting right barline', barline]) m.rightBarline = barline else: environLocal.printDebug(['not handling barline that is neither left nor right', barline, barline.location]) elif isinstance(mxObj, musicxmlMod.Note): mxNote = mxObj if isinstance(mxObjNext, musicxmlMod.Note): mxNoteNext = mxObjNext else: mxNoteNext = None if mxNote.get('print-object') == 'no': #environLocal.printDebug(['got mxNote with printObject == no', 'measure number', m.number]) continue mxGrace = mxNote.get('graceObj') if mxGrace is not None: # graces have a type but not a duration #TODO: add grace notes with duration equal to ZeroDuration #environLocal.printDebug(['got mxNote with an mxGrace', 'duration', mxNote.get('duration'), 'measure number', #m.number]) continue # the first note of a chord is not identified directly; only # by looking at the next note can we tell if we have a chord if mxNoteNext is not None and mxNoteNext.get('chord') is True: if mxNote.get('chord') is False: mxNote.set('chord', True) # set the first as a chord if mxNote.get('rest') in [None, False]: # it is a note # if a chord, do not increment until chord is complete if mxNote.get('chord') is True: mxNoteList.append(mxNote) offsetIncrement = 0 else: n = note.Note() n.mx = mxNote if useVoices: m.voices[mxNote.voice].insert(offsetMeasureNote, n) else: m.insert(offsetMeasureNote, n) offsetIncrement = n.quarterLength for mxLyric in mxNote.lyricList: lyricObj = note.Lyric() lyricObj.mx = mxLyric n.lyrics.append(lyricObj) if mxNote.get('notationsObj') is not None: for mxObjSub in mxNote.get('notationsObj'): # deal with ornaments, strill, etc pass else: # its a rest n = note.Rest() n.mx = mxNote # assign mxNote to rest obj #m.insert(offsetMeasureNote, n) if useVoices: m.voices[mxNote.voice].insert(offsetMeasureNote, n) else: m.insert(offsetMeasureNote, n) offsetIncrement = n.quarterLength # if we we have notes in the note list and the next # note either does not exist or is not a chord, we # have a complete chord if len(mxNoteList) > 0 and (mxNoteNext is None or mxNoteNext.get('chord') is False): c = chord.Chord() c.mx = mxNoteList mxNoteList = [] # clear for next chord #m.insert(offsetMeasureNote, c) if useVoices: m.voices[mxNote.voice].insert(offsetMeasureNote, c) else: m.insert(offsetMeasureNote, c) offsetIncrement = c.quarterLength # only increment Chords after completion offsetMeasureNote += offsetIncrement # load dynamics into Measure, not into Voice elif isinstance(mxObj, musicxmlMod.Direction): # mxDynamicsFound, mxWedgeFound = m._getMxDynamics(mxObj) # for mxDirection in mxDynamicsFound: if mxObj.getDynamicMark() is not None: d = dynamics.Dynamic() d.mx = mxObj m.insert(offsetMeasureNote, d) if mxObj.getWedge() is not None: w = dynamics.Wedge() w.mx = mxObj m.insert(offsetMeasureNote, w)