1
0
Fork 0
mirror of https://github.com/kastdeur/bwwtolily.git synced 2024-11-25 05:03:31 +01:00

Hella refactored some shit

This commit is contained in:
Jezra 2013-08-13 22:49:09 -07:00
parent 01ea98f703
commit dce0309a22

View file

@ -7,400 +7,403 @@ GPL v3
from optparse import OptionParser from optparse import OptionParser
import sys,os,re,subprocess import sys,os,re,subprocess
version = "0.4.0" version = "0.4.1"
#make a print function to handle various version of python
def do_print(string):
try:
eval("print "+string)
except:
print(string)
#define the class that will convert a bww file to a lilypond file #define the class that will convert a bww file to a lilypond file
class bwwtolily : class bwwtolily :
def __init__(self,addmidi=False):
self.tune_elements = []
self.most_recent_note = 0
self.in_note_group=False
self.slur_tie_pending = False
self.last_group_close=0
'''compile a few regex queries'''
#make a regex to determine if something is a lilypond note
self.regex_lilynote= re.compile("[abcdefgAG][0-9]*")
#try to determine the time signature
self.sig_regex = re.compile("([0-9])_([0-9])")
#a regex to find notes
self.regex_note_info=re.compile("(?P<note>[A-Z]+)(?P<dir>[a-z]*)_(?P<time>[0-9]{1,2})")
#a regex to find grace notes
self.regex_grace_note = re.compile("([h|l]*[abcdefgt])g")
#a regex to parse doublings
self.regex_doubling = re.compile("^db([h|l]*[g|a|b|c|d|e|f]{1})")
#a regex to parse half_doublings
self.regex_half_doubling = re.compile("^hdb([h|l]*[g|a|b|c|d|e|f]{1})")
#a regex for finding strikes
self.regex_strike = re.compile("str([h|l]*[abcdefg])")
#a regex to find dots
self.regex_dot = re.compile("'[h|l]*[abcdefg]")
#a regex to find sub repeats
self.regex_sub_repeat = re.compile("'([0-9]+)")
#a regex to find note slurs, not slur embellishments
self.regex_slur = re.compile("\^(?P<note_count>[0-9])(?P<end_note>[a-z]*)")
#we need a list to ignore
self.ignore_elements = ("sharpf","sharpc","space","&")
#create a dictionary of common bww elements and their lily counterparts
self.transpose_dict = {
"!":"\\bar \"|\"\n",
"!I":"\\bar \".|\" \\break \n",
#"''!I":"\\set Score.repeatCommands = #'( end-repeat ) \\break \n",
"''!I":"\\bar \":|\" \\break\n",
#"''!I":"} \\break \n",
"I!''":"\\bar \"|:\"",
"I!":"\\bar \"|.\"",
#"I!''":"\\set Score.repeatCommands = #'( start-repeat )\n",
def __init__(self,addmidi=False): #"I!''":"\n\\repeat volta 2 {\n",
self.tune_elements = [] "_'":"\\set Score.repeatCommands = #'((volta #f)) \\bar \"|\"\n",
self.most_recent_note = 0 "!t":"\\bar \"|\" \\break\n\n",
self.in_note_group=False "thrd":"\\thrwd",
self.slur_tie_pending = False "gbr":"\\gbirl",
self.last_group_close=0 "brl":"\\wbirl",
'''compile a few regex queries''' "abr":"\\birl",
#make a regex to determine if something is a lilypond note "lgstd":"\\dbld",
self.regex_lilynote= re.compile("[abcdefgAG][0-9]*") "gste":"\\slure",
#try to determine the time signature "grp":"\\grip",
self.sig_regex = re.compile("([0-9])_([0-9])") "tar":"\\taor",
#a regex to find notes "gstd":"\\slurd",
self.regex_note_info=re.compile("(?P<note>[A-Z]+)(?P<dir>[a-z]*)_(?P<time>[0-9]{1,2})") "tdbf":"\\tdblf"
#a regex to find grace notes }
self.regex_grace_note = re.compile("([h|l]*[abcdefgt])g") #are we adding midi?
#a regex to parse doublings if addmidi:
self.regex_doubling = re.compile("^db([h|l]*[g|a|b|c|d|e|f]{1})") self.lily_midi="\midi{}"
#a regex to parse half_doublings else:
self.regex_half_doubling = re.compile("^hdb([h|l]*[g|a|b|c|d|e|f]{1})") self.lily_midi=''
#a regex for finding strikes
self.regex_strike = re.compile("str([h|l]*[abcdefg])")
#a regex to find dots
self.regex_dot = re.compile("'[h|l]*[abcdefg]")
#a regex to find sub repeats
self.regex_sub_repeat = re.compile("'([0-9]+)")
#a regex to find note slurs, not slur embellishments
self.regex_slur = re.compile("\^(?P<note_count>[0-9])(?P<end_note>[a-z]*)")
#we need a list to ignore
self.ignore_elements = ("sharpf","sharpc","space","&")
#create a dictionary of common bww elements and their lily counterparts
self.transpose_dict = {
"!":"\\bar \"|\"\n",
"!I":"\\bar \".|\" \\break \n",
#"''!I":"\\set Score.repeatCommands = #'( end-repeat ) \\break \n",
"''!I":"\\bar \":|\" \\break\n",
#"''!I":"} \\break \n",
"I!''":"\\bar \"|:\"",
"I!":"\\bar \"|.\"",
#"I!''":"\\set Score.repeatCommands = #'( start-repeat )\n",
#"I!''":"\n\\repeat volta 2 {\n", def set_file(self,file_path):
"_'":"\\set Score.repeatCommands = #'((volta #f)) \\bar \"|\"\n", #determine the absolute path to the file
"!t":"\\bar \"|\" \\break\n\n", abs_file = os.path.join(os.getcwd(),file_path)
"thrd":"\\thrwd", file_name = os.path.basename(abs_file)
"gbr":"\\gbirl", (self.name,ext) = file_name.split(".")
"brl":"\\wbirl",
"abr":"\\birl",
"lgstd":"\\dbld",
"gste":"\\slure",
"grp":"\\grip",
"tar":"\\taor",
"gstd":"\\slurd",
"tdbf":"\\tdblf"
}
#are we adding midi?
if addmidi:
self.lily_midi="\midi{}"
else:
self.lily_midi=''
#does the file exist?
if os.path.isfile(abs_file):
self.original_file = abs_file
self.file_dir = os.path.dirname(abs_file)
else:
raise Exception(bww_file_path+" is not a file")
def set_file(self,file_path): def quit(self,string=""):
#determine the absolute path to the file if string!="":
abs_file = os.path.join(os.getcwd(),file_path) do_print( string)
file_name = os.path.basename(abs_file) sys.exit()
(self.name,ext) = file_name.split(".")
#does the file exist? def parse(self):
if os.path.isfile(abs_file): '''reate a string that represents the converted
self.original_file = abs_file contents of the file'''
self.file_dir = os.path.dirname(abs_file) #open the file read only
else: file_handle = open(self.original_file,"r")
raise Exception(bww_file_path+" is not a file") #read the contents of the file
file_text = file_handle.read()
#get the title,type,author of the file, these are in quotes
quote_regex = re.compile("\"(.*)\"")
tune_info = quote_regex.findall(file_text)
self.tune_title = tune_info[0]
self.tune_type = tune_info[1]
self.tune_author = tune_info[2]
#try to determine the time signature
result = self.sig_regex.search(file_text)
if result:
self.tune_time_sig = result.group(1)+"/"+result.group(2)
else:
self.tune_time_sig = "4/4"
def quit(self,string=""): #get the tunes note info
if string!="": '''greedy, multiline, from first ampersand to !'''
print string notes_regex = re.compile("&.*!I",re.S)
sys.exit() result = notes_regex.search(file_text)
def parse(self): try:
'''reate a string that represents the converted tune_notes = result.group()
contents of the file''' except:
#open the file read only #no notes were found, what kind of file is this
file_handle = open(self.original_file,"r") self.quit("No notes were found.\nIs this a valid input file?")
#read the contents of the file #replace all whitespace characters with spaces
file_text = file_handle.read() tune_notes = tune_notes.replace("\r"," ")
#get the title,type,author of the file, these are in quotes tune_notes = tune_notes.replace("\n"," ")
quote_regex = re.compile("\"(.*)\"") tune_notes = tune_notes.replace("\t"," ")
tune_info = quote_regex.findall(file_text) '''determine if there is a partial'''
self.tune_title = tune_info[0] '''
self.tune_type = tune_info[1] #get the first part of the notes from & to !
self.tune_author = tune_info[2] partial_regex = re.compile("&.*?[ |\r\n]!",re.S)
#try to determine the time signature #partial_regex = re.compile("&.*?!",re.S)
result = self.sig_regex.search(file_text) result = partial_regex.findall(tune_notes)
if result: search_string = result[0]
self.tune_time_sig = result.group(1)+"/"+result.group(2) #find the time of the notes in the search string
else: partial_regex = re.compile("[A-Z]+[a-z]*_([0-9]{1,2})")
self.tune_time_sig = "4/4" result = partial_regex.findall(search_string)
self.tune_partial_string = ""
if result:
partial_time = 0
#TODO: determine the proper way to determine the partial
for number in result:
partial_time += (1.0/int(number))
print "time: %s" % ( partial_time )
#how many quarter notes is this?
quarters = partial_time/.25
print "quarters: %d" % quarters
#self.tune_partial_string = "\\partial %d" % quarters
#self.tune_partial_string = "\\partial "+str(part)
'''
#split the string into it's constituents elements
elements = tune_notes.split()
for element in elements:
self.transpose(element)
#get the tunes note info def lilynote(self,bwwname):
'''greedy, multiline, from first ampersand to !''' #convert a bww notename to a lilypond notename
notes_regex = re.compile("&.*!I",re.S) #make the notename lowercase
result = notes_regex.search(file_text) notename = bwwname.lower()
try:
tune_notes = result.group()
except:
#no notes were found, what kind of file is this
self.quit("No notes were found.\nIs this a valid input file?")
#replace all whitespace characters with spaces
tune_notes = tune_notes.replace("\r"," ")
tune_notes = tune_notes.replace("\n"," ")
tune_notes = tune_notes.replace("\t"," ")
'''determine if there is a partial'''
'''
#get the first part of the notes from & to !
partial_regex = re.compile("&.*?[ |\r\n]!",re.S)
#partial_regex = re.compile("&.*?!",re.S)
result = partial_regex.findall(tune_notes)
search_string = result[0]
#find the time of the notes in the search string
partial_regex = re.compile("[A-Z]+[a-z]*_([0-9]{1,2})")
result = partial_regex.findall(search_string)
self.tune_partial_string = ""
if result:
partial_time = 0
#TODO: determine the proper way to determine the partial
for number in result:
partial_time += (1.0/int(number))
print "time: %s" % ( partial_time )
#how many quarter notes is this?
quarters = partial_time/.25
print "quarters: %d" % quarters
#self.tune_partial_string = "\\partial %d" % quarters
#self.tune_partial_string = "\\partial "+str(part)
'''
#split the string into it's constituents elements
elements = tune_notes.split()
for element in elements:
self.transpose(element)
#print self.tune_notes
if notename =="lg":
lilynote = "G"
elif notename == "la":
lilynote ="a"
elif notename == "hg":
lilynote = "g"
elif notename == "ha" or notename =="t":
lilynote = "A"
else:
lilynote = notename
return lilynote
def lilynote(self,bwwname): def transpose(self,element):
#convert a bww notename to a lilypond notename '''receive a bww element and return a lilypond equivelent'''
#make the notename lowercase
notename = bwwname.lower()
if notename =="lg": #is the element a note?
lilynote = "G" note_result = self.regex_note_info.search(element)
elif notename == "la": if note_result:
lilynote ="a" note = self.lilynote( note_result.group("note") )+note_result.group("time")
elif notename == "hg": #is a tie slur pending?
lilynote = "g" if self.slur_tie_pending:
elif notename == "ha" or notename =="t": self.slur_tie_pending=False
lilynote = "A" note+="~"
else: self.tune_elements.append(note)
lilynote = notename self.most_recent_note = len(self.tune_elements)-1
return lilynote if note_result.group("dir") == "r" and not self.in_note_group:
self.in_note_group=True
self.tune_elements.append("[")
elif note_result.group("dir") == "l":
if self.in_note_group:
self.in_note_group=False
self.tune_elements.append("]")
self.last_group_close = len(self.tune_elements)-1
else:
#delete the last group close
del(self.tune_elements[self.last_group_close])
#decrement the most recent note
self.most_recent_note-=1
self.tune_elements.append("]")
return
#is the element a grace note?
grace_result=self.regex_grace_note.search(element)
if grace_result:
grace = "\\gr"+self.lilynote( grace_result.group(1) )
self.tune_elements.append(grace)
return
#is the element a doubling?
doubling_result=self.regex_doubling.search(element)
if doubling_result:
doubling = "\\dbl"+self.lilynote( doubling_result.group(1) )
self.tune_elements.append(doubling)
return
#is the element a half doubling?
hdoubling_result=self.regex_half_doubling.search(element)
if hdoubling_result:
half_doubling = "\\hdbl"+self.lilynote( hdoubling_result.group(1) )
self.tune_elements.append(half_doubling)
return
#is the element a strike?
strike_result=self.regex_strike.search(element)
if strike_result:
strike = "\\slur"+self.lilynote( strike_result.group(1) )
#if the strike is on low g \\slurG
if strike =="\\slurG":
#let the strike be a low g grace note
strike = "\\grG"
#if the strike is on high G
elif strike =="\\slurg":
#let the strike be a grace note on the high g
strike = "\\grg"
#if hte strike is on low a
elif strike == "\\slura":
#let the strike be a grace not on low a
strike = "\\gra"
self.tune_elements.append(strike)
return
#is the element a dot?
dot_result=self.regex_dot.search(element)
if dot_result:
#add a dot to the last note
note = self.tune_elements[self.most_recent_note]
if note[-1]=="~":
self.tune_elements[self.most_recent_note].replace("~",".~")
else:
self.tune_elements[self.most_recent_note]+="."
return
def transpose(self,element): #is the element a slur?
'''receive a bww element and return a lilypond equivelent''' slur_result = self.regex_slur.search(element)
if slur_result:
#get the matching elements
note_count = slur_result.group("note_count")
end_note = slur_result.group("end_note")
#get the length of the slur as an integer
slur_len = int(note_count)
'''find the position of the note that is slur_len from the end'''
#get the tune_elements lenght
elem_index = len(self.tune_elements)-1
note_count = 0
while note_count<2:
element = self.tune_elements[elem_index]
#is this element a note?
is_note = self.regex_lilynote.search(element)
if is_note:
#increment the note count
note_count+=1
#decrease the element index
elem_index-=1
#is the element a note? #add the slur start just after the start note
note_result = self.regex_note_info.search(element) self.tune_elements.insert(elem_index+1,"(")
if note_result: #add the slur end
note = self.lilynote( note_result.group("note") )+note_result.group("time") self.tune_elements.append(")")
#is a tie slur pending? return
if self.slur_tie_pending: #is this a bww tie slur?
self.slur_tie_pending=False if element == "^ts":
note+="~" self.slur_tie_pending = True
self.tune_elements.append(note) return
self.most_recent_note = len(self.tune_elements)-1
if note_result.group("dir") == "r" and not self.in_note_group:
self.in_note_group=True
self.tune_elements.append("[")
elif note_result.group("dir") == "l":
if self.in_note_group:
self.in_note_group=False
self.tune_elements.append("]")
self.last_group_close = len(self.tune_elements)-1
else:
#delete the last group close
del(self.tune_elements[self.last_group_close])
#decrement the most recent note
self.most_recent_note-=1
self.tune_elements.append("]")
return
#is the element a grace note?
grace_result=self.regex_grace_note.search(element)
if grace_result:
grace = "\\gr"+self.lilynote( grace_result.group(1) )
self.tune_elements.append(grace)
return
#is the element a doubling?
doubling_result=self.regex_doubling.search(element)
if doubling_result:
doubling = "\\dbl"+self.lilynote( doubling_result.group(1) )
self.tune_elements.append(doubling)
return
#is the element a half doubling?
hdoubling_result=self.regex_half_doubling.search(element)
if hdoubling_result:
half_doubling = "\\hdbl"+self.lilynote( hdoubling_result.group(1) )
self.tune_elements.append(half_doubling)
return
#is the element a strike?
strike_result=self.regex_strike.search(element)
if strike_result:
strike = "\\slur"+self.lilynote( strike_result.group(1) )
#if the strike is on low g \\slurG
if strike =="\\slurG":
#let the strike be a low g grace note
strike = "\\grG"
#if the strike is on high G
elif strike =="\\slurg":
#let the strike be a grace note on the high g
strike = "\\grg"
#if hte strike is on low a
elif strike == "\\slura":
#let the strike be a grace not on low a
strike = "\\gra"
self.tune_elements.append(strike)
return
#is the element a dot?
dot_result=self.regex_dot.search(element)
if dot_result:
#add a dot to the last note
note = self.tune_elements[self.most_recent_note]
if note[-1]=="~":
self.tune_elements[self.most_recent_note].replace("~",".~")
else:
self.tune_elements[self.most_recent_note]+="."
return
#is the element a slur? #is the element the start of a sub_repeat?
slur_result = self.regex_slur.search(element) sub_repeat_result = self.regex_sub_repeat.search(element)
if slur_result: if sub_repeat_result:
#get the matching elements sub_repeat = "\\set Score.repeatCommands = #'((volta \"%s\")) " % ( sub_repeat_result.group(1) )
note_count = slur_result.group("note_count") self.tune_elements.append(sub_repeat)
end_note = slur_result.group("end_note") return
#get the length of the slur as an integer else:
slur_len = int(note_count) #is the element in the ignore list?
'''find the position of the note that is slur_len from the end''' if element in self.ignore_elements:
#get the tune_elements lenght return
elem_index = len(self.tune_elements)-1 '''
note_count = 0 if the element is an start double,
while note_count<2: check if the previous element was a end double
element = self.tune_elements[elem_index] '''
#is this element a note? if len(self.tune_elements):
is_note = self.regex_lilynote.search(element) last_element = self.tune_elements[-1]
if is_note: if element=="I!''" and last_element.find(":|"):
#increment the note count #replace the last element with a double double
note_count+=1 self.tune_elements[-1] = "\\bar \":|:\" \\break\n\n"
#decrease the element index return
elem_index-=1 #is this a time sig?
result = self.sig_regex.search(element)
if result:
return
try:
#add the slur start just after the start note dict_result = self.transpose_dict[element]
self.tune_elements.insert(elem_index+1,"(") if dict_result:
#add the slur end self.tune_elements.append(dict_result)
self.tune_elements.append(")") return
return except:
#is this a bww tie slur? do_print( "unparsed: "+element)
if element == "^ts": return
self.slur_tie_pending = True
return
#is the element the start of a sub_repeat? #handle writing the output
sub_repeat_result = self.regex_sub_repeat.search(element) def create_output_file(self):
if sub_repeat_result: #determine the output file
sub_repeat = "\\set Score.repeatCommands = #'((volta \"%s\")) " % ( sub_repeat_result.group(1) ) output_file = os.path.join(self.file_dir,self.name+".ly")
self.tune_elements.append(sub_repeat) #open the file for writing
return file_handle = open(output_file,"w")
else: #write the data to the file
#is the element in the ignore list? text = self.get_lilypond_text()
if element in self.ignore_elements: file_handle.write(text)
return #close the handle
''' file_handle.close()
if the element is an start double, #return the string of the path to the file
check if the previous element was a end double return output_file
'''
if len(self.tune_elements):
last_element = self.tune_elements[-1]
if element=="I!''" and last_element.find(":|"):
#replace the last element with a double double
self.tune_elements[-1] = "\\bar \":|:\" \\break\n\n"
return
#is this a time sig?
result = self.sig_regex.search(element)
if result:
return
try:
dict_result = self.transpose_dict[element] def get_lilypond_text(self):
if dict_result: tune_text = " ".join(self.tune_elements)
self.tune_elements.append(dict_result) lptext ='''\\include "bagpipe.ly"
return
except:
print "unparsed: "+element
return
#handle writing the output
def create_output_file(self):
#determine the output file
output_file = os.path.join(self.file_dir,self.name+".ly")
#open the file for writing
file_handle = open(output_file,"w")
#write the data to the file
text = self.get_lilypond_text()
file_handle.write(text)
#close the handle
file_handle.close()
#return the string of the path to the file
return output_file
def get_lilypond_text(self):
tune_text = " ".join(self.tune_elements)
lptext ='''\\include "bagpipe.ly"
melody = { melody = {
\\set Staff.midiInstrument = #"bagpipe" \\set Staff.midiInstrument = #"bagpipe"
\\hideKeySignature \\hideKeySignature
\\cadenzaOn \\cadenzaOn
\\once \\override Score.BreakAlignment #'break-align-orders = \\once \\override Score.BreakAlignment #'break-align-orders =
#(make-vector 3 '(instrument-name #(make-vector 3 '(instrument-name
left-edge left-edge
ambitus ambitus
span-bar span-bar
breathing-sign breathing-sign
clef clef
key-signature key-signature
time-signature time-signature
staff-bar staff-bar
custos custos
span-bar)) span-bar))
\\time %s \\time %s
%s %s
} }
\\score { \\score {
\\melody \\melody
\\layout { \\layout {
indent = 0.0\\cm indent = 0.0\\cm
\\context { \\Score \\remove "Bar_number_engraver" } \\context { \\Score \\remove "Bar_number_engraver" }
} }
\\header { \\header {
title = "%s" title = "%s"
meter = "%s" meter = "%s"
arranger = "%s" arranger = "%s"
} }
%s %s
} }
''' % (self.tune_time_sig, ''' % (self.tune_time_sig,
tune_text, tune_text,
self.tune_title, self.tune_title,
self.tune_type, self.tune_type,
self.tune_author, self.tune_author,
self.lily_midi self.lily_midi
) )
return lptext return lptext
#use the bww2lily class #use the bww2lily class
if __name__ == "__main__" : if __name__ == "__main__" :
parser = OptionParser() parser = OptionParser()
parser.add_option("-i", "--in", dest="input", parser.add_option("-i", "--in", dest="input",
help="the FILE to convert", metavar="FILE") help="the FILE to convert", metavar="FILE")
parser.add_option("-l", "--lilypond", parser.add_option("-l", "--lilypond",
action="store_true", dest="runlilypond", default=False, action="store_true", dest="runlilypond",default=False,
help="run lilypond after converting the file") help="run lilypond after converting the file")
parser.add_option("-m", "--midi", parser.add_option("-m", "--midi",
action="store_true", dest="addmidi", default=False, action="store_true", dest="addmidi",default=False,
help="add midi output to the lilypond file") help="add midi output to the lilypond file")
parser.add_option("-v","--version",dest='version', default=False, parser.add_option("-v","--version",dest='version',default=False,
action="store_true",help="print version information and quit") action="store_true",help="print version information and quit")
#parse the args #parse the args
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
if options.version: if options.version:
print "bwwtolily: ",version do_print( "bwwtolily: ",version)
sys.exit() sys.exit()
if options.input!=None: if options.input!=None:
b2l = bwwtolily(options.addmidi) b2l = bwwtolily(options.addmidi)
b2l.set_file(options.input) b2l.set_file(options.input)
b2l.parse() b2l.parse()
new_file = b2l.create_output_file() new_file = b2l.create_output_file()
#are we running lilypond? #are we running lilypond?
if options.runlilypond: if options.runlilypond:
#try to run lilypond as a subprocess #try to run lilypond as a subprocess
subprocess.check_call("lilypond \""+new_file+"\"",shell=True) subprocess.check_call("lilypond \""+new_file+"\"",shell=True)
else: else:
parser.print_help() parser.print_help()
sys.exit() sys.exit()