diff --git a/Field_D_SupportingClasses.py b/Field_D_SupportingClasses.py index f89690e..08b86eb 100644 --- a/Field_D_SupportingClasses.py +++ b/Field_D_SupportingClasses.py @@ -5,6 +5,8 @@ # check out www.github.com/dan-field/Text2SATB for info and rights # #################################################################### +import prompt +import lxml.etree as tree class DF_Syllables: def __init__(self): @@ -45,8 +47,7 @@ def b(self, word): adjusted_ending = self.breakDownFromBack(rest, ending) adjusted_endLength = 0 for syllable in adjusted_ending: - for letter in syllable: - adjusted_endLength += 1 + adjusted_endLength = len(syllable) if adjusted_endLength > endLength: rest = rest[:len(rest)+endLength-adjusted_endLength] restBrokenDown = self.r(rest) @@ -149,7 +150,7 @@ def breakDownFromBack(self, word, suffix): # now check for 'e' endings (on the whole word, or the word with suffix removed, as applicable) if w[-1] == "e": # last letter is 'e' if w[-2] not in self.vowels and w[-3] in self.vowels_y: # second-last is consonant and third-last is vowel - ending = word[-3]+word[-2]+word[-1] # this is a Vowel-Consonant-e ending + if w[-4] not in self.vowels and len(w) == 4: # it's cons-vowel-cons-'e' so it's a single syllable (note the 'le' ending has already been dealt with in the 'searchSuffix' function) if suffix == "": return [word] @@ -456,20 +457,24 @@ def __init__(self): lineText = [] inputText = [] infile = None - usrFileName = raw_input("please enter the filename of the text input file, e.g. 'mypoem.txt'\n: ") - try: - infile = open(usrFileName, "r") - except (OSError, IOError): - infile = None - print "\nThere was an issue attempting to open the file." - print "Please double-check your filename; "+usrFileName+"\n" - print "Note your input text file must be in the same" - print "folder as the Python files." - return + + while infile is None: + usrFileName = prompt.string("please enter the filename of the text input file, e.g. 'mypoem.txt': ") + + try: + infile = open(usrFileName, "r") + except (OSError, IOError): + infile = None + print("\nThere was an issue attempting to open the file.") + print("Please double-check your filename; "+usrFileName+"\n") + print("Note your input text file must be in the same") + print("folder as the Python files.") + if infile is not None: for line in infile.readlines(): inputText.append(line) infile.close() + # The input file has been read, and all of the text is in 'inputText' lineText.append("---BREAK---") # this will be used to signify a new verse noted = True # this is a flag to avoid multiple 'new verse' indications in a row @@ -526,11 +531,11 @@ def __init__(self): syllables[-1] = syllables[-1]+punctuation # add the punctuation back on where it came from if punctuation in ".;:!?*": # these punctuation marks will create new lines self.lastWord = True # this will be the last word in this line - positions = range(len(syllables)) # start an 'empty' list with as many members as there are syllables in the word + positions = list(range(len(syllables))) # start an 'empty' list with as many members as there are syllables in the word if len(positions) == 1: # this means there's only one syllable in the word positions[0] = "single" # this follows the MusicXML 'syllabic' convention else: # there are two or more syllables in the word - for index, position in enumerate(positions): + for index, _ in enumerate(positions): if index == 0: # first syllable positions[index] = "begin" elif index == len(positions)-1: # last syllable @@ -553,13 +558,13 @@ def provideScrabbleScores(self): return self.scrabbleScores def provideTitle(self): - usrTitle = raw_input("please enter a Title for the work\n: ") + usrTitle = prompt.string("please enter a Title for the work: ") if usrTitle == "": usrTitle = None return usrTitle def provideLyricist(self): - usrLyricist = raw_input("please enter the Lyricist's name\n: ") + usrLyricist = prompt.string("please enter the Lyricist's name: ") if usrLyricist == "": usrLyricist = None return usrLyricist @@ -627,7 +632,7 @@ def __init__(self, verses, positions, scores): def planHarmonicStructure(self): # looks at the incoming verse structure and generates a chord sequence # Try to keep the chord references numeric/relative for easy adaptation - key = 0 # number zero will represent the 'home' key + verseKey = 0 verseKeys = [] lineKey = 0 @@ -646,7 +651,7 @@ def planHarmonicStructure(self): # but if there are more than 240 syllables, it goes back to zero and starts over again verseKey += keyshift verseKeys.append(verseKey) - for i, line in enumerate(verse): # similarly, we might change key from line to line using the same sort of logic + for i, _ in enumerate(verse): # similarly, we might change key from line to line using the same sort of logic if i == 0: # first line lineKey = verseKey # first line of each verse is in the verse's key elif index == len(self.verses)-1 and i == len(self.verses[index])-1: # this is the last line of the last verse @@ -671,7 +676,7 @@ def determineChordSequence(self): for i, line in enumerate(verse): chordBase[index].append([]) chordType[index].append([]) - for j, syllable in enumerate(line): + for j, _ in enumerate(line): cycleMove = 0 chordComplexity = 0 # we want to set a chord degree and type that reflects the Scrabble score of the word @@ -811,7 +816,7 @@ def getBassPart(self, homeKey): count = 0 barNo = -1 for i, line in enumerate(verse): - for j, syllable in enumerate(line): + for j, _ in enumerate(line): keyRef = self.chordPlan[index][i] chordDegree = self.chordBase[index][i][j] # this is just the step number - it needs to be converted to a chromatic degree number chordDegree = self.convertChordDegree(chordDegree) @@ -828,7 +833,7 @@ def getBassPart(self, homeKey): differences = [abs(loose_target - float(note)) for note in bassTargets] closest = differences.index(min(differences)) thisNote = bassTargets[closest] # pick the bass note that is closest to the comfortable mid point - thisDuration = 4 + # we have all the info we need to start assembling the Bass part if count%16 == 0: # the previous bar is full bassNotes[index].append([]) @@ -842,7 +847,6 @@ def getBassPart(self, homeKey): bassPositions[index][barNo].append(self.positions[index][i][j]) if len(self.durations[index][i][j]) == 1: # the note does not cross a barline L = self.durations[index][i][j][0] - R = self.sylRests[index][i][j][0] bassRhythms[index][barNo].append(L) bassTies[index][barNo].append(None) count += L @@ -895,7 +899,7 @@ def getTenorPart(self, homeKey): count = 0 barNo = -1 for i, line in enumerate(verse): - for j, syllable in enumerate(line): + for j, _ in enumerate(line): keyRef = self.chordPlan[index][i] chordDegree = self.chordBase[index][i][j] # this is just the step number - it needs to be converted to a chromatic degree number chordDegree = self.convertChordDegree(chordDegree) @@ -914,7 +918,7 @@ def getTenorPart(self, homeKey): differences = [abs(loose_target - float(note)) for note in tenTargets] closest = differences.index(min(differences)) thisNote = tenTargets[closest] # pick the tenor note that is closest to the comfortable mid point - thisDuration = 4 + # we have all the info we need to start assembling the Tenor part if count%16 == 0: # the previous bar is full tenNotes[index].append([]) @@ -1030,7 +1034,7 @@ def getAltoPart(self, homeKey): count = 0 barNo = -1 for i, line in enumerate(verse): - for j, syllable in enumerate(line): + for j, _ in enumerate(line): keyRef = self.chordPlan[index][i] chordDegree = self.chordBase[index][i][j] # this is just the step number - it needs to be converted to a chromatic degree number chordDegree = self.convertChordDegree(chordDegree) @@ -1049,7 +1053,7 @@ def getAltoPart(self, homeKey): differences = [abs(loose_target - float(note)) for note in altoTargets] closest = differences.index(min(differences)) thisNote = altoTargets[closest] # pick the alto note that is closest to the comfortable mid point - thisDuration = 4 + # we have all the info we need to start assembling the Alto part if count%16 == 0: # the previous bar is full altoNotes[index].append([]) @@ -1189,9 +1193,7 @@ def getSopPart(self, homeKey): shiftedScale = [] for note in scale: shiftedScale.append(homeKey+keyRef+note) - sopScaleTargets = self.buildFullRange(shiftedScale, self.rangeSop[0], self.rangeSop[1]) - if i == len(verse)-1 and j == len(line)-1: - sopScaleTargets = sopChordTargets + # LOOSE TARGET needs to be modified so that it's a melody note ------------------------------- loose_target = 70.4 # seek out a comfortable mid-point in the soprano range if chordType == 0: @@ -1201,7 +1203,7 @@ def getSopPart(self, homeKey): differences = [abs(loose_target - float(note)) for note in sopChordTargets] closest = differences.index(min(differences)) thisNote = sopChordTargets[closest] # pick the soprano note that is closest to the comfortable mid point - thisDuration = 4 + # we have all the info we need to start assembling the Soprano part if count%16 == 0: # the previous bar is full sopNotes[index].append([]) @@ -1268,7 +1270,6 @@ def getSopPart(self, homeKey): self.sopLatestNote = thisNote else: L = self.durations[index][i][j][0] - R = self.sylRests[index][i][j][0] sopRhythms[index][barNo].append(L) sopTies[index][barNo].append(None) sopNotes[index][barNo].append(thisNote) @@ -1324,9 +1325,7 @@ def getLinesAndSyllables(self, verses): for verse in verses: resultVerse = [] for line in verse: - syllableCount = 0 - for syllable in line: - syllableCount += 1 + syllableCount = len(line) resultVerse.append(syllableCount) resultSong.append(resultVerse) return resultSong @@ -1587,325 +1586,244 @@ def __init__(self, usrTitle=None, progID=None, usrAuthor=None): self.measureNo = 0 self.voice = 1 # 1 Soprano, 2 Alto, 3 Tenor, 4 Bass self.flats = True # assumes black notes should be written as flats - will need to be updated once a 'key' functionality is added - usrFileName = raw_input("please enter a name for the output file\n: ") + usrFileName = prompt.string("please enter a name for the output file: ") self.fileName = usrFileName+".musicxml" - try: - self.file = open(self.fileName, "w") - except (OSError, IOError): - self.file = None - print "\nThere was an issue with the file operation." - print "Please double-check your filename.\n" - print "Note the MusicXML file will attempt to save in the" - print "same folder as the Python files; please ensure you" - print "have write permission for that folder." - return - self.file.write('\n') - self.file.write('\n') - self.file.write('\n') - self.file.write(' \n') - self.file.write(' '+str(self.Title)+'\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' '+str(self.progID)+'\n') - self.file.write(' '+str(self.Author)+'\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' Soprano\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' Alto\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' Tenor\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' Bass\n') - self.file.write(' \n') - self.file.write(' \n') + + # building up the xml tree + self.root = tree.Element('score-partwise', attrib={'version':'3.1'}) + work = tree.SubElement(self.root, 'work') + tree.SubElement(work, 'work-title').text = self.Title + + # author + ident = tree.SubElement(self.root,'identification') + tree.SubElement(ident, 'creator', attrib={'type':'composer'}).text = self.progID + tree.SubElement(ident, 'creator', attrib={'type':'lyricist'}).text = self.Author + + # part list + part_list = tree.SubElement(self.root, 'part-list') + score_info = {'P1':'Soprano', 'P2':'Alto', 'P3':'Tenor', 'P4':'Bass'}.items() + for score_id, score_type in score_info: + score_part = tree.SubElement(part_list, 'score-part', attrib={'id': score_id}) + tree.SubElement(score_part, 'part-name').text = score_type + + # this writes a section + # returns a tuple with the part and the current measure + def startSection(self, section): + if self.root is not None: + + part_id = str(section['part_id']) + clef_sign = str(section['clef_sign']) + clef_line = str(section['clef_line']) + + part = tree.SubElement(self.root, 'part', attrib={'id': part_id}) + measure = tree.SubElement(part, 'measure', attrib={'number': str(self.measureNo)}) + attributes = tree.SubElement(measure, 'attributes') + tree.SubElement(attributes, 'divisions').text = str(self.DIVISIONS) + + key = tree.SubElement(attributes, 'key') + tree.SubElement(key, 'fifths').text = '-3' + + time = tree.SubElement(attributes, 'time') + tree.SubElement(time, 'beats').text = '4' + tree.SubElement(time, 'beat-type').text = '4' + + clef = tree.SubElement(attributes, 'clef', attrib= {'number': '1'}) + tree.SubElement(clef, 'sign').text = clef_sign + tree.SubElement(clef, 'line').text = clef_line + + # tenors get an octave change? + if part_id == 'P3': + tree.SubElement(clef, 'clef-octave-change').text = '-1' + + return (part, measure) def startSoprano(self): - if self.file is not None: - self.measureNo = 0 - self.voice = 1 - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' '+str(self.DIVISIONS)+'\n') - self.file.write(' \n') - self.file.write(' -3\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' G\n') - self.file.write(' 2\n') - self.file.write(' \n') - self.file.write(' \n') + self.measureNo = 0 + self.voice = 1 + + # writing the soprano elements to the xml tree + section = {} + section['part_id'] = 'P1' + section['clef_sign'] = 'G' + section['clef_line'] = '2' + return self.startSection(section) + def startAlto(self): - if self.file is not None: - self.measureNo = 0 - self.voice = 2 - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' '+str(self.DIVISIONS)+'\n') - self.file.write(' \n') - self.file.write(' -3\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' G\n') - self.file.write(' 2\n') - self.file.write(' \n') - self.file.write(' \n') + self.measureNo = 0 + self.voice = 2 + + # writing the alto elements to the xml tree + section = {} + section['part_id'] = 'P2' + section['clef_sign'] = 'G' + section['clef_line'] = '2' + return self.startSection(section) def startTenor(self): - if self.file is not None: - self.measureNo = 0 - self.voice = 3 - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' '+str(self.DIVISIONS)+'\n') - self.file.write(' \n') - self.file.write(' -3\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' G\n') - self.file.write(' 2\n') - self.file.write(' -1\n') - self.file.write(' \n') - self.file.write(' \n') + self.measureNo = 0 + self.voice = 3 + + # writing the tenor elements to the xml tree + section = {} + section['part_id'] = 'P3' + section['clef_sign'] = 'G' + section['clef_line'] = '2' + return self.startSection(section) def startBass(self): - if self.file is not None: - self.measureNo = 0 - self.voice = 4 - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' '+str(self.DIVISIONS)+'\n') - self.file.write(' \n') - self.file.write(' -3\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' F\n') - self.file.write(' 4\n') - self.file.write(' \n') - self.file.write(' \n') - - def endPart(self): - if self.file is not None: - self.file.write(' \n') - self.file.write(' light-heavy\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') + self.measureNo = 0 + self.voice = 4 + + # writing the bass elements to the xml tree + section = {} + section['part_id'] = 'P4' + section['clef_sign'] = 'F' + section['clef_line'] = '4' + return self.startSection(section) + + def endPart(self, measure): + barline = tree.SubElement(measure, 'barline', attrib={'location': 'right'}) + tree.SubElement(barline, 'bar-style').text = 'light-heavy' def endXMLFile(self): - if self.file is not None: - self.file.write('\n') - self.file.close() - - def addMeasure(self): - if self.file is not None: - self.file.write(' \n') - self.measureNo += 1 - self.file.write(' \n') - - def addMeasureDbl(self): - if self.file is not None: - self.file.write(' \n') - self.measureNo += 1 - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' light-light\n') - self.file.write(' \n') - - def addMeasureKeyChange(self, newKey): - if self.file is not None: - self.file.write(' \n') - self.measureNo += 1 - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' light-light\n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' '+str(newKey)+'\n') - self.file.write(' \n') - self.file.write(' \n') - - def addNote(self, MIDI_No, duration=None, lyric=None, position=None, tie=None): # duration: 4 = crotchet, 16 = semi-breve - if self.file is not None: - if duration is None: - duration = 4 # default to a crotchet - if lyric is None: - lyric = "" # default to no text - note_type, dotted, tiedToMinim = self.MIDI.Duration2Type(duration) - self.file.write(' \n') + string = tree.tostring(self.root, encoding = 'UTF-8', pretty_print = True, standalone = False, doctype = '') + + try: + with open(self.fileName, "wb") as text_file: + text_file.write(string) + except (OSError, IOError): + print("\nThere was an issue with the file operation.") + print("Please double-check your filename and folder permissions.\n") + + + # takes in the current part + # returns the new measure + def addMeasure(self, part): + self.measureNo += 1 + measure = tree.SubElement(part, 'measure', attrib={'number':str(self.measureNo)}) + return measure + + # takes in the current part + # returns the new measure + def addMeasureDbl(self, part): + self.measureNo += 1 + measure = tree.SubElement(part, 'measure',attrib={'number': str(self.measureNo)}) + barline = tree.SubElement(measure, 'barline', attrib={'location':'left'}) + tree.SubElement(barline, 'bar-style').text = 'light-light' + + return measure + + def addNote(self, measure, MIDI_No, duration=None, lyric=None, position=None, tie=None): # duration: 4 = crotchet, 16 = semi-breve + if duration is None: + duration = 4 # default to a crotchet + if lyric is None: + lyric = "" # default to no text + note_type, dotted, tiedToMinim = self.MIDI.Duration2Type(duration) + + note = tree.SubElement(measure, 'note') + + if MIDI_No != "RRR": + octave, step, alter = self.MIDI.MIDI2Note(MIDI_No, self.flats) + + pitch = tree.SubElement(note, 'pitch') + tree.SubElement(pitch, 'step').text = str(step) + + if alter != 0: + tree.SubElement(pitch, 'alter').text = str(alter) + + tree.SubElement(pitch, 'octave').text = str(octave) + else: + tree.SubElement(note, 'rest') + + if tiedToMinim is False: + tree.SubElement(note, 'duration').text = str(duration) + else: + tree.SubElement(note, 'duration').text = str(duration - 8) + + tree.SubElement(note, 'voice').text = str(self.voice) + tree.SubElement(note, 'type').text = note_type + + if dotted is True: + tree.SubElement(note, 'dot') + + if tie is not None and tiedToMinim is False: + notations = tree.SubElement(note, 'notations') + tree.SubElement(notations, 'tied', attrib={'type': str(tie)}) + + if tiedToMinim is True: + notations = tree.SubElement(note, 'notations') + + if tie == "stop": + tree.SubElement(notations, 'tied', attrib={'type': 'stop'}) + + tree.SubElement(notations, 'tied', attrib={'type': 'start'}) + + lyric_node = tree.SubElement(note, 'lyric') + + if position is not None: + tree.SubElement(lyric_node, 'syllabic').text = str(position) + + tree.SubElement(lyric_node, 'text').text = lyric + + if tiedToMinim is True: + note = tree.SubElement(measure, 'note') if MIDI_No != "RRR": - octave, step, alter = self.MIDI.MIDI2Note(MIDI_No, self.flats) - self.file.write(' \n') - self.file.write(' '+str(step)+'\n') + + pitch = tree.SubElement(note, 'pitch') + tree.SubElement(pitch, 'step').text = str(step) + if alter != 0: - self.file.write(' '+str(alter)+'\n') - self.file.write(' '+str(octave)+'\n') - self.file.write(' \n') - else: - self.file.write(' \n') - if tiedToMinim is False: - self.file.write(' '+str(duration)+'\n') + tree.SubElement(pitch, 'alter').text = str(alter) + + tree.SubElement(pitch, 'octave').text = str(octave) else: - self.file.write(' '+str(duration-8)+'\n') - self.file.write(' '+str(self.voice)+'\n') - self.file.write(' '+str(note_type)+'\n') - if dotted is True: - self.file.write(' \n') - if tie is not None and tiedToMinim is False: - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - if tiedToMinim is True: - self.file.write(' \n') - if tie == "stop": - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - if position is not None: - self.file.write(' '+str(position)+'\n') - self.file.write(' '+str(lyric)+'\n') - self.file.write(' \n') - self.file.write(' \n') - if tiedToMinim is True: - self.file.write(' \n') - if MIDI_No != "RRR": - self.file.write(' \n') - self.file.write(' '+str(step)+'\n') - if alter != 0: - self.file.write(' '+str(alter)+'\n') - self.file.write(' '+str(octave)+'\n') - self.file.write(' \n') - else: - self.file.write(' \n') - self.file.write(' 8\n') - self.file.write(' '+str(self.voice)+'\n') - self.file.write(' half\n') - self.file.write(' \n') - self.file.write(' \n') - if tie == "start": - self.file.write(' \n') - self.file.write(' \n') - self.file.write(' \n') - - - def backOneBar(self): - if self.file is not None: - self.file.write(' \n') - self.file.write(' '+str(int(self.DIVISIONS*4))+'\n') - self.file.write(' \n') + tree.SubElement(note, 'rest') + + tree.SubElement(note, 'duration').text = '8' + tree.SubElement(note, 'voice').text = str(self.voice) + tree.SubElement(note, 'type').text = 'half' - def writeSop(self, notes, durations, lyrics=None, positions=None, ties=None): - self.startSoprano() + notations = tree.SubElement(note, 'notations') + tree.SubElement(notations, 'tied', attrib={'type':'stop'}) + if tie == "start": + tree.SubElement(notations, 'tied', attrib={'tied':'start'}) + + def writeSection(self, part, measure, notes, durations, lyrics=None, positions=None, ties=None): for Vindex, verse in enumerate(notes): for Bindex, bar in enumerate(verse): if Bindex == len(verse)-1 and Vindex != len(notes)-1: # it's the last bar of the verse but not the last verse - self.addMeasureDbl() + measure = self.addMeasureDbl(part) elif Bindex == len(verse)-1 and Vindex == len(notes)-1: # this is the very last bar; don't add a new bar pass elif Bindex != 0: - self.addMeasure() + measure = self.addMeasure(part) if lyrics is not None and positions is not None and ties is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex], positions[Vindex][Bindex][Nindex], ties[Vindex][Bindex][Nindex]) + for Nindex, _ in enumerate(bar): + self.addNote(measure, bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex], positions[Vindex][Bindex][Nindex], ties[Vindex][Bindex][Nindex]) elif lyrics is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex]) + for Nindex, _ in enumerate(bar): + self.addNote(measure, bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex]) else: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex]) - self.endPart() + for Nindex, _ in enumerate(bar): + self.addNote(measure, bar[Nindex], durations[Vindex][Bindex][Nindex]) + self.endPart(measure) + + def writeSop(self, notes, durations, lyrics=None, positions=None, ties=None): + part, measure = self.startSoprano() + self.writeSection(part, measure, notes, durations, lyrics, positions, ties) def writeAlto(self, notes, durations, lyrics=None, positions=None, ties=None): - self.startAlto() - for Vindex, verse in enumerate(notes): - for Bindex, bar in enumerate(verse): - if Bindex == len(verse)-1 and Vindex != len(notes)-1: # it's the last bar of the verse but not the last verse - self.addMeasureDbl() - elif Bindex == len(verse)-1 and Vindex == len(notes)-1: # this is the very last bar; don't add a new bar - pass - elif Bindex != 0: - self.addMeasure() - if lyrics is not None and positions is not None and ties is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex], positions[Vindex][Bindex][Nindex], ties[Vindex][Bindex][Nindex]) - elif lyrics is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex]) - else: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex]) - self.endPart() + part, measure = self.startAlto() + self.writeSection(part, measure, notes, durations, lyrics, positions, ties) def writeTenor(self, notes, durations, lyrics=None, positions=None, ties=None): - self.startTenor() - for Vindex, verse in enumerate(notes): - for Bindex, bar in enumerate(verse): - if Bindex == len(verse)-1 and Vindex != len(notes)-1: # it's the last bar of the verse but not the last verse - self.addMeasureDbl() - elif Bindex == len(verse)-1 and Vindex == len(notes)-1: # this is the very last bar; don't add a new bar - pass - elif Bindex != 0: - self.addMeasure() - if lyrics is not None and positions is not None and ties is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex], positions[Vindex][Bindex][Nindex], ties[Vindex][Bindex][Nindex]) - elif lyrics is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex]) - else: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex]) - self.endPart() + part, measure = self.startTenor() + self.writeSection(part, measure, notes, durations, lyrics, positions, ties) def writeBass(self, notes, durations, lyrics=None, positions=None, ties=None): - self.startBass() - for Vindex, verse in enumerate(notes): - for Bindex, bar in enumerate(verse): - if Bindex == len(verse)-1 and Vindex != len(notes)-1: # it's the last bar of the verse but not the last verse - self.addMeasureDbl() - elif Bindex == len(verse)-1 and Vindex == len(notes)-1: # this is the very last bar; don't add a new bar - pass - elif Bindex != 0: - self.addMeasure() - if lyrics is not None and positions is not None and ties is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex], positions[Vindex][Bindex][Nindex], ties[Vindex][Bindex][Nindex]) - elif lyrics is not None: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex], lyrics[Vindex][Bindex][Nindex]) - else: - for Nindex, note in enumerate(bar): - self.addNote(bar[Nindex], durations[Vindex][Bindex][Nindex]) - self.endPart() + part, measure = self.startBass() + self.writeSection(part, measure, notes, durations, lyrics, positions, ties) def MIDI2Fifths(self, MIDI_Key): MIDI_Key = int(MIDI_Key)