def romanTextToStreamOpus(rtHandler, inputM21=None): '''The main processing routine for RomanText objects that may or may not be multi movement. Takes in a romanText.rtObjects.RTFile() object, or a string as rtHandler. Runs `romanTextToStreamScore()` as its main work. If inputM21 is None then it will create a Score or Opus object. Return either a Score object, or, if a multi-movement work is defined, an Opus object. ''' from music21 import stream if common.isStr(rtHandler): rtf = rtObjects.RTFile() rtHandler = rtf.readstr(rtHandler) # return handler, processes tokens if rtHandler.definesMovements(): # create an opus if inputM21 == None: s = stream.Opus() else: s = inputM21 # copy the common header to each of the sub-handlers handlerBundles = rtHandler.splitByMovement(duplicateHeader=True) # see if we have header information for h in handlerBundles: #print h, len(h) # append to opus s.append(romanTextToStreamScore(h)) return s # an opus else: # create a Score return romanTextToStreamScore(rtHandler, inputM21=inputM21)
def testExternalA(self): from music21.romanText import testFiles for tf in testFiles.ALL: rtf = rtObjects.RTFile() rth = rtf.readstr(tf) # return handler, processes tokens s = romanTextToStreamScore(rth) s.show()
def parseFile(self, fp, number=None): from music21.romanText import rtObjects from music21.romanText import translate as romanTextTranslate rtf = rtObjects.RTFile() # not sure why -- @UndefinedVariable rtf.open(fp) # returns a handler instance of parse tokens rtHandler = rtf.read() rtf.close() romanTextTranslate.romanTextToStreamScore(rtHandler, self.stream)
def parseData(self, strData, number=None): from music21.romanText import rtObjects from music21.romanText import translate as romanTextTranslate rtf = rtObjects.RTFile() # not sure why -- @UndefinedVariable rtHandler = rtf.readstr(strData) if rtHandler.definesMovements(): # this re-defines Score as an Opus self.stream = romanTextTranslate.romanTextToStreamOpus(rtHandler) else: romanTextTranslate.romanTextToStreamScore(rtHandler, self.stream)
def testBasicA(self): from music21.romanText import testFiles for tf in testFiles.ALL: rtf = rtObjects.RTFile() rth = rtf.readstr(tf) # return handler, processes tokens unused_s = romanTextToStreamOpus(rth) # will run romanTextToStreamScore on all but k273 #s.show() s = romanTextToStreamScore(testFiles.swv23) self.assertEqual(s.metadata.composer, 'Heinrich Schutz') # this is defined as a Piece tag, but shows up here as a title, after # being set as an alternate title self.assertEqual(s.metadata.title, 'Warum toben die Heiden, Psalmen Davids no. 2, SWV 23') s = romanTextToStreamScore(testFiles.riemenschneider001) self.assertEqual(s.metadata.composer, 'J. S. Bach') self.assertEqual(s.metadata.title, 'Aus meines Herzens Grunde') s = romanTextToStreamScore(testFiles.monteverdi_3_13) self.assertEqual(s.metadata.composer, 'Claudio Monteverdi')
def romanTextToStreamScore(rtHandler, inputM21=None): '''The main processing module for single-movement RomanText works. Given a romanText handler or string, return or fill a Score Stream. ''' # accept a string directly; mostly for testing if common.isStr(rtHandler): rtf = rtObjects.RTFile() rtHandler = rtf.readstr(rtHandler) # return handler, processes tokens # this could be just a Stream, but b/c we are creating metadata, perhaps better to match presentation of other scores. from music21 import metadata from music21 import stream from music21 import note from music21 import meter from music21 import key from music21 import roman from music21 import tie if inputM21 == None: s = stream.Score() else: s = inputM21 # metadata can be first md = metadata.Metadata() s.insert(0, md) p = stream.Part() # ts indication are found in header, and also found elsewhere tsCurrent = meter.TimeSignature('4/4') # create default 4/4 tsSet = False # store if set to a measure lastMeasureToken = None lastMeasureNumber = 0 previousRn = None keySigCurrent = None keySigSet = True # set a keySignature foundAKeySignatureSoFar = False kCurrent, unused_prefixLyric = _getKeyAndPrefix( 'C') # default if none defined prefixLyric = '' repeatEndings = {} rnKeyCache = {} for t in rtHandler.tokens: try: # environLocal.printDebug(['token', t]) if t.isTitle(): md.title = t.data elif t.isWork(): md.alternativeTitle = t.data elif t.isPiece(): md.alternativeTitle = t.data elif t.isComposer(): md.composer = t.data elif t.isMovement(): md.movementNumber = t.data elif t.isTimeSignature(): tsCurrent = meter.TimeSignature(t.data) tsSet = False # environLocal.printDebug(['tsCurrent:', tsCurrent]) elif t.isKeySignature(): if t.data == "": keySigCurrent = key.KeySignature(0) elif t.data == "Bb": keySigCurrent = key.KeySignature(-1) else: pass # better to print a message # environLocal.printDebug(['still need to write a generic RomanText KeySignature routine. this is just temporary']) # raise RomanTextTranslateException("still need to write a generic RomanText KeySignature routine. this is just temporary") keySigSet = False # environLocal.printDebug(['keySigCurrent:', keySigCurrent]) foundAKeySignatureSoFar = True elif t.isMeasure(): # environLocal.printDebug(['handling measure token:', t]) #if t.number[0] % 10 == 0: # print "at number " + str(t.number[0]) if t.variantNumber is not None: # environLocal.printDebug(['skipping variant: %s' % t]) continue if t.variantLetter is not None: # environLocal.printDebug(['skipping variant: %s' % t]) continue # if this measure number is more than 1 greater than the last # defined measure number, and the previous chord is not None, # then fill with copies of the last-defined measure if ((t.number[0] > lastMeasureNumber + 1) and (previousRn is not None)): for i in range(lastMeasureNumber + 1, t.number[0]): mFill = stream.Measure() mFill.number = i newRn = copy.deepcopy(previousRn) newRn.lyric = "" # set to entire bar duration and tie newRn.duration = copy.deepcopy(tsCurrent.barDuration) if previousRn.tie is None: previousRn.tie = tie.Tie('start') else: previousRn.tie.type = 'continue' # set to stop for now; may extend on next iteration newRn.tie = tie.Tie('stop') previousRn = newRn mFill.append(newRn) appendMeasureToRepeatEndingsDict( lastMeasureToken, mFill, repeatEndings, i) p._appendCore(mFill) lastMeasureNumber = t.number[0] - 1 lastMeasureToken = t # create a new measure or copy a past measure if len(t.number) == 1 and t.isCopyDefinition: # if not a range p.elementsChanged() m, kCurrent = _copySingleMeasure(t, p, kCurrent) p._appendCore(m) lastMeasureNumber = m.number lastMeasureToken = t romans = m.getElementsByClass(roman.RomanNumeral, returnStreamSubClass='list') if len(romans) > 0: previousRn = romans[-1] elif len(t.number) > 1: p.elementsChanged() measures, kCurrent = _copyMultipleMeasures(t, p, kCurrent) p.append(measures) # appendCore does not work with list lastMeasureNumber = measures[-1].number lastMeasureToken = t romans = measures[-1].getElementsByClass( roman.RomanNumeral, returnStreamSubClass='list') if len(romans) > 0: previousRn = romans[-1] else: m = stream.Measure() m.number = t.number[0] appendMeasureToRepeatEndingsDict(t, m, repeatEndings) lastMeasureNumber = t.number[0] lastMeasureToken = t if not tsSet: m.timeSignature = tsCurrent tsSet = True # only set when changed if not keySigSet and keySigCurrent is not None: m.insert(0, keySigCurrent) keySigSet = True # only set when changed o = 0.0 # start offsets at zero previousChordInMeasure = None pivotChordPossible = False numberOfAtoms = len(t.atoms) setKeyChangeToken = False # first RomanNumeral object after a key change should have this set to True for i, a in enumerate(t.atoms): if isinstance(a, rtObjects.RTKey) or \ ((foundAKeySignatureSoFar == False) and \ (isinstance(a, rtObjects.RTAnalyticKey))): # found a change of Key+KeySignature or # just found a change of analysis but no keysignature so far # environLocal.printDebug(['handling key token:', a]) try: # this sets the key and the keysignature kCurrent, pl = _getKeyAndPrefix(a) prefixLyric += pl except: raise RomanTextTranslateException( 'cannot get key from %s in line %s' % (a.src, t.src)) # insert at beginning of measure if at beginning -- for things like pickups. if m.number < 2: m._insertCore(0, kCurrent) else: m._insertCore(o, kCurrent) foundAKeySignatureSoFar = True setKeyChangeToken = True elif isinstance(a, rtObjects.RTKeySignature): try: # this sets the keysignature but not the prefix text thisSig = a.getKeySignature() except: raise RomanTextTranslateException( 'cannot get key from %s in line %s' % (a.src, t.src)) #insert at beginning of measure if at beginning -- for things like pickups. if m.number < 2: m._insertCore(0, thisSig) else: m._insertCore(o, thisSig) foundAKeySignatureSoFar = True elif isinstance(a, rtObjects.RTAnalyticKey): # just a change in analyzed key, not a change in anything else #try: # this sets the key, not the keysignature kCurrent, pl = _getKeyAndPrefix(a) prefixLyric += pl setKeyChangeToken = True #except: # raise RomanTextTranslateException('cannot get key from %s in line %s' % (a.src, t.src)) elif isinstance(a, rtObjects.RTBeat): # set new offset based on beat try: o = a.getOffset(tsCurrent) except ValueError: raise RomanTextTranslateException( "cannot properly get an offset from beat data %s under timeSignature %s in line %s" % (a.src, tsCurrent, t.src)) if (previousChordInMeasure is None and previousRn is not None and o > 0): # setting a new beat before giving any chords firstChord = copy.deepcopy(previousRn) firstChord.quarterLength = o firstChord.lyric = "" if previousRn.tie == None: previousRn.tie = tie.Tie('start') else: previousRn.tie.type = 'continue' firstChord.tie = tie.Tie('stop') previousRn = firstChord previousChordInMeasure = firstChord m._insertCore(0, firstChord) pivotChordPossible = False elif isinstance(a, rtObjects.RTNoChord): # use source to evaluation roman rn = note.Rest() if pivotChordPossible == False: # probably best to find duration if previousChordInMeasure is None: pass # use default duration else: # update duration of previous chord in Measure oPrevious = previousChordInMeasure.getOffsetBySite( m) newQL = o - oPrevious if newQL <= 0: raise RomanTextTranslateException( 'too many notes in this measure: %s' % t.src) previousChordInMeasure.quarterLength = newQL prefixLyric = "" m._insertCore(o, rn) previousChordInMeasure = rn previousRn = rn pivotChordPossible = False elif isinstance(a, rtObjects.RTChord): # use source to evaluation roman try: asrc = a.src # if kCurrent.mode == 'minor': # if asrc.lower().startswith('vi'): #vi or vii w/ or w/o o # if asrc.upper() == a.src: # VI or VII to bVI or bVII # asrc = 'b' + asrc cacheTuple = (asrc, kCurrent.tonicPitchNameWithCase) if USE_RN_CACHE and cacheTuple in rnKeyCache: #print "Got a match: " + str(cacheTuple) # Problems with Caches not picking up pivot chords... Not faster, see below. rn = copy.deepcopy(rnKeyCache[cacheTuple]) else: #print "No match for: " + str(cacheTuple) rn = roman.RomanNumeral( asrc, copy.deepcopy(kCurrent)) rnKeyCache[cacheTuple] = rn # surprisingly, not faster... and more dangerous #rn = roman.RomanNumeral(asrc, kCurrent) ## SLOWEST!!! #rn = roman.RomanNumeral(asrc, kCurrent.tonicPitchNameWithCase) #>>> from timeit import timeit as t #>>> t('roman.RomanNumeral("IV", "c#")', 'from music21 import roman', number=1000) #45.75 #>>> t('roman.RomanNumeral("IV", k)', 'from music21 import roman, key; k = key.Key("c#")', number=1000) #16.09 #>>> t('roman.RomanNumeral("IV", copy.deepcopy(k))', 'from music21 import roman, key; import copy; k = key.Key("c#")', number=1000) #22.49 ## key cache, does not help much... #>>> t('copy.deepcopy(r)', 'from music21 import roman; import copy; r = roman.RomanNumeral("IV", "c#")', number=1000) #19.01 if setKeyChangeToken is True: rn.followsKeyChange = True setKeyChangeToken = False else: rn.followsKeyChange = False except (roman.RomanNumeralException, common.Music21CommonException): #environLocal.printDebug('cannot create RN from: %s' % a.src) rn = note.Note() # create placeholder if pivotChordPossible == False: # probably best to find duration if previousChordInMeasure is None: pass # use default duration else: # update duration of previous chord in Measure oPrevious = previousChordInMeasure.getOffsetBySite( m) newQL = o - oPrevious if newQL <= 0: raise RomanTextTranslateException( 'too many notes in this measure: %s' % t.src) previousChordInMeasure.quarterLength = newQL rn.addLyric(prefixLyric + a.src) prefixLyric = "" m._insertCore(o, rn) previousChordInMeasure = rn previousRn = rn pivotChordPossible = True else: previousChordInMeasure.lyric += "//" + prefixLyric + a.src previousChordInMeasure.pivotChord = rn prefixLyric = "" pivotChordPossible = False elif isinstance(a, rtObjects.RTRepeat): if o == 0: if isinstance(a, rtObjects.RTRepeatStart): m.leftBarline = bar.Repeat( direction='start') else: rtt = RomanTextUnprocessedToken(a) m._insertCore(o, rtt) elif tsCurrent is not None and ( tsCurrent.barDuration.quarterLength == o or i == numberOfAtoms - 1): if isinstance(a, rtObjects.RTRepeatStop): m.rightBarline = bar.Repeat( direction='end') else: rtt = RomanTextUnprocessedToken(a) m._insertCore(o, rtt) else: # mid measure repeat signs rtt = RomanTextUnprocessedToken(a) m._insertCore(o, rtt) else: rtt = RomanTextUnprocessedToken(a) m._insertCore(o, rtt) #environLocal.warn("Got an unknown token: %r" % a) # may need to adjust duration of last chord added if tsCurrent is not None: previousRn.quarterLength = tsCurrent.barDuration.quarterLength - o m.elementsChanged() p._appendCore(m) except Exception: import traceback tracebackMessage = traceback.format_exc() raise RomanTextTranslateException( "At line %d for token %r, an exception was raised: \n%s" % (t.lineNumber, t, tracebackMessage)) p.elementsChanged() fixPickupMeasure(p) p.makeBeams(inPlace=True) p.makeAccidentals(inPlace=True) _addRepeatsFromRepeatEndings(p, repeatEndings) # 1st and second endings... s.insert(0, p) return s