def __do_load_grooves(self, grooves_list, path): """ Load grooves and recurse into subdirectories. """ for dirname, dirnames, filenames in os.walk(path): #@UnusedVariable for name in filenames: if fnmatch.fnmatch(name, '*.mma'): full_name = os.path.join(dirname, name) song_data = self.__parseGrooves(full_name) if not song_data: continue song_bar_info = song_data.get_bar_info_all() doc = author = time = '' for line in song_bar_info[0].get_lines(): action = line[0] if action == Glob.A_BEGIN_BLOCK and line[1] == Glob.A_DOC: doc = BarInfo.get_doc_value(line) elif action == Glob.A_AUTHOR: author = BarInfo.get_author_value(line) elif action == Glob.A_TIME: time = BarInfo.get_time_value(line) elif action == Glob.A_DEF_GROOVE: gname, gdesc = BarInfo.get_defgroove_value(line) grooves_list.append([gname, doc, gdesc, author, time, full_name]) for name in dirnames: path = os.path.join(dirname, name) self.__do_load_grooves(grooves_list, path)
def __do_load_grooves(self, grooves_list, path): """ Load grooves and recurse into subdirectories. """ for dirname, dirnames, filenames in os.walk(path): #@UnusedVariable for name in filenames: if fnmatch.fnmatch(name, '*.mma'): full_name = os.path.join(dirname, name) song_data = self.__parseGrooves(full_name) if not song_data: continue song_bar_info = song_data.get_bar_info_all() doc = author = time = '' for line in song_bar_info[0].get_lines(): action = line[0] if action == Glob.A_BEGIN_BLOCK and line[ 1] == Glob.A_DOC: doc = BarInfo.get_doc_value(line) elif action == Glob.A_AUTHOR: author = BarInfo.get_author_value(line) elif action == Glob.A_TIME: time = BarInfo.get_time_value(line) elif action == Glob.A_DEF_GROOVE: gname, gdesc = BarInfo.get_defgroove_value(line) grooves_list.append( [gname, doc, gdesc, author, time, full_name]) for name in dirnames: path = os.path.join(dirname, name) self.__do_load_grooves(grooves_list, path)
def on_spinbutton2_value_changed_callback(self, spinbutton): """ Tempo changed """ count = str(int(spinbutton.get_value())) event = BarInfo.create_event(Glob.A_TEMPO) BarInfo.set_tempo_value(event, count) self.set_label_from_event(self.__toggled_button, event) self.__new_event = event
def on_comboboxentry1_changed_callback(self, widget): """ RepeatEnd value changed. """ # called also when initializing the entries list if not self.__toggled_button: return try: i = int(widget.child.get_text()) except ValueError: return count = str(i) event = BarInfo.create_event(Glob.A_REPEAT_END) BarInfo.set_repeat_end_value(event, count) self.set_label_from_event(self.__toggled_button, event) self.__newEvent = event
def init_window(self, button, event): self.__toggled_button = button self.__curr_event = event self.__newEvent = None text = BarInfo.get_repeat_end_value(event) self.__entry.set_text(text) self.__combobox_entry.grab_focus()
def set_label_from_event(self, button, event): """ Set the label of tempo button correctly even if the event in the self.__song.get_data().get_bar_info(0) is missing """ if event: label = BarInfo.get_tempo_value(event) + ' BPM' button.set_label(label) else: button.set_label(EventTempo.__TEMPO_UNDEFINED)
def setUp(self): song_data = self.__song_data = SongData([ BarInfo() ], [], 0) self.__bar_chords1 = BarChords() self.__bar_chords1.set_song_data(song_data) self.__bar_chords1.set_chords([['CM7', ' trail1 '], [ 'Am', ' trail2 ']]) self.__bar_chords2 = BarChords() self.__bar_chords2.set_song_data(song_data) self.__bar_chords2.set_chords([['CM7', ' trail1 '], [ 'Am', ' trail2 '], [ '/', ' '], ['G', ' trail3 ']])
def __add_event(self, key): """ Add selected event and open its window """ barNum = self.__gui.get_current_bar_number() event = BarInfo.create_event(key) self.__song.get_data().get_bar_info(barNum).add_event(event) self.refresh_all() self.__gui.refresh_current_field() # repetition # open the new event window for triple in self.__triples: if triple[3] is event: gobject.idle_add(triple[0].clicked) break
def init_window(self, button, event): # hide back, forward, remove buttons if button is self.__togglebutton2: self.__alignment15.hide() else: self.__alignment15.show() self.__toggled_button = button self.__curr_event = event self.__new_event = None if event: self.__spinbutton2.set_value(int(BarInfo.get_tempo_value(event))) else: self.on_spinbutton2_value_changed_callback(self.__spinbutton2) self.__spinbutton2.grab_focus()
def __clear_song(self): """ Called when parsing of mma data failed. E.g. during opening the new file or parsing data from the source editor. The song must have minimum one BarInfo on which the cursor is located. bar_count = number of bar_chords in the song, number of bar_info is bar_count + 1 """ self.__song_data = SongData([ BarInfo() ], [], 0) self.__invalid_mma_data = None self.__pending_mma_data = None self.__last_compile_result = None
def set_label_from_event(self, button, event): """ Sets the label of groove button correctly even if the event in the self.__song.get_data().get_bar_info(0) is missing. """ if event: button.set_label(BarInfo.get_groove_value(event)) else: button.set_label(EventGroove.__GROOVE_UNDEFINED)
def __update_groove_event(self, groove): event = BarInfo.create_event(Glob.A_GROOVE) BarInfo.set_groove_value(event, groove) self.set_label_from_event(self.__toggled_button, event) self.__new_event = event
def create_bar_info(self): bar_info = BarInfo() bar_info.set_song_data(self) return bar_info
def parse(inpath): """ Process a mma input file. """ song_bar_info = [] song_bar_chords = [] song_bar_count = 0 bar_number = 0 bar_info = BarInfo() bar_chords = BarChords() while True: curline = inpath.readline() # EOF if not curline: song_bar_info.append( bar_info ) # song_bar_info has always one element more then song_bar_chords song_bar_count = bar_number return SongData(song_bar_info, song_bar_chords, song_bar_count) """ convert 0xa0 (non-breakable space) to 0x20 (regular space). """ curline = curline.replace('\xa0', '\x20') # empty line if curline.rstrip('\n').strip() == '': bar_info.add_line([Glob.A_UNKNOWN, curline]) continue l = curline.split() # line beginning with macro if l[0][0] == '$': wline = get_wrapped_line(inpath, curline) wline.insert(0, Glob.A_UNKNOWN) bar_info.add_line(wline) continue """ Handle BEGIN and END here. This is outside of the Repeat/End and variable expand loops so SHOULD be pretty bullet proof. Note that the beginData stuff is global to this module ... the Include/Use directives check to make sure we're not doing that inside a Begin/End. beginData[] is a list which we append to as more Begins are encountered. The placement here is pretty deliberate. Variable expand comes later so you can't macroize BEGIN ... I think this makes sense. The tests for 'begin', 'end' and the appending of the current begin[] stuff have to be here, in this order. """ action = l[0].upper() # 1st arg in line # parse BEGIN and END block if action == 'BEGIN': block_action = l[1].upper() begin_block = parse_begin_block(inpath, curline) if block_action in supported_block_actions: tokens = parse_supported_block_action(block_action, begin_block) begin_block = tokens begin_block.insert(0, Glob.A_BEGIN_BLOCK) begin_block.insert(1, block_action) bar_info.add_line(begin_block) continue # parse MSET block if action == 'MSET': mset_block = parse_mset_block(inpath, curline) mset_block.insert(0, Glob.A_UNKNOWN) bar_info.add_line(mset_block) continue # parse IF - ENDIF block if action == 'IF': if_block = parse_if_block(inpath, curline) if_block.insert(0, Glob.A_UNKNOWN) bar_info.add_line(if_block) continue # supported commands if action in supported_actions: wline = get_wrapped_line_join(inpath, curline) tokens = parse_supported_action(action, wline) tokens.insert(0, action) bar_info.add_line(tokens) continue # if the command is in the simple function table if action in simple_funcs: wline = get_wrapped_line(inpath, curline) wline.insert(0, Glob.A_UNKNOWN) bar_info.add_line(wline) continue """ We have several possibilities ... 1. The command is a valid assigned track name, 2. The command is a valid track name, but needs to be dynamically allocated, 3. It's really a chord action """ # track function BASS/DRUM/APEGGIO/CHORD ... if '-' in action: trk_class, ext = action.split('-', 1) #@UnusedVariable else: trk_class = action if trk_class in trk_classes: # parsing track sequence ? parse_seq = len(l) >= 1 and l[1].upper() == 'SEQUENCE' wline = [] while True: wline.extend(get_wrapped_line(inpath, curline)) if not parse_seq: break """ Count the number of { and } and if they don't match read more lines and append. If we get to the EOF then we're screwed and we error out. """ wline2 = ''.join(wline) if wline2.count('{') == wline2.count('}'): break curline = inpath.readline() if not curline: raise ValueError("Reached EOF, Sequence {}s do not match") wline.insert(0, Glob.A_UNKNOWN) bar_info.add_line(wline) continue # join the wrapped line into one line wline = get_wrapped_line_join(inpath, curline) if wline[0].replace('\\\n', '').strip() == '': # line is a comment or empty wrapped line act = Glob.A_REMARK if wline[1].strip() else Glob.A_UNKNOWN bar_info.add_line([act, wline[0], wline[1]]) continue l, eol = wline ### Gotta be a chord data line! """ A data line can have an optional bar number at the start of the line. Makes debugging input easier. The next block strips leading integers off the line. Note that a line number on a line by itself it okay. """ before_number = '' if action.isdigit(): # isdigit() matches '1', '1234' but not '1a'! l2 = l.lstrip() before_number_len = len(l) - len(l2) before_number = l[0:before_number_len] l = l2 numstr = l.split()[0] bar_chords.set_number(int(numstr)) l = l[len(numstr):] # remove number if len(l.strip()) == 0: # ignore empty lines bar_info.add_line([Glob.A_UNKNOWN, wline[0] + wline[1]]) continue """ We now have a valid line. It'll look something like: 'Cm', '/', 'z', 'F#@4.5' { lyrics } [ solo ] * 2 Special processing in needed for 'z' options in chords. A 'z' can be of the form 'CHORDzX', 'z!' or just 'z'. """ after_number = None last_chord = [] ctable = [] i = 0 solo_count = 0 lyrics_count = 0 mismatched_solo = "Mismatched {}s for solo found in chord line" mismatched_lyrics = "Mismatched []s for lyrics found in chord line" while True: chars = '' while i < len(l): ch = l[i] if ch == '{': """ Extract solo(s) from line ... this is anything in {}s. The solo data is pushed into RIFFs and discarded from the current line. """ solo_count += 1 elif ch == '[': """ Set lyrics from [stuff] in the current line. NOTE: lyric.extract() inserts previously created data from LYRICS SET and inserts the chord names if that flag is active. """ lyrics_count += 1 elif ch == '}': solo_count -= 1 if solo_count < 0: raise ValueError(mismatched_solo) elif ch == ']': lyrics_count -= 1 if lyrics_count < 0: raise ValueError(mismatched_lyrics) elif ch == '*': """ A bar can have an optional repeat count. This must be at the end of bar in the form '* xx'. """ pass elif ch in '\t\n\\ 0123456789': # white spaces, \ and repeat count pass elif solo_count == 0 and lyrics_count == 0: # found beginning of the chord break chars += ch i += 1 if i == len(l): # no more chord is coming if solo_count != 0: raise ValueError(mismatched_solo) if lyrics_count != 0: raise ValueError(mismatched_lyrics) if after_number == None: after_number = chars else: last_chord.append(chars) ctable.append(last_chord) break else: # chord beginning if after_number == None: after_number = chars else: last_chord.append(chars) ctable.append(last_chord) chord_begin = i # find the end of the chord while i < len(l): if l[i] in '{}[]*\t\n\\ ': break i += 1 # chord examples: '/', 'z', 'Am7@2', 'Am6zC@3' c = l[chord_begin:i] last_chord = [c] # the trailing string of the last chord can possibly include '\n' after which # it would be difficult to add further chords. Therefore move the trailing string # of the last chord to eol eol = last_chord[1] + eol last_chord[1] = '' bar_chords.set_before_number(before_number) bar_chords.set_after_number(after_number) bar_chords.set_eol(eol) bar_chords.set_chords(ctable) song_bar_info.append(bar_info) song_bar_chords.append(bar_chords) bar_number = bar_number + 1 bar_info = BarInfo() bar_chords = BarChords()
def parse(inpath): """ Process a mma input file. """ song_bar_info = [] song_bar_chords = [] song_bar_count = 0 bar_number = 0 bar_info = BarInfo() bar_chords = BarChords() while True: curline = inpath.readline() # EOF if not curline: song_bar_info.append(bar_info) # song_bar_info has always one element more then song_bar_chords song_bar_count = bar_number return SongData(song_bar_info, song_bar_chords, song_bar_count) """ convert 0xa0 (non-breakable space) to 0x20 (regular space). """ curline = curline.replace('\xa0', '\x20') # empty line if curline.rstrip('\n').strip() == '': bar_info.add_line([Glob.A_UNKNOWN, curline]); continue l = curline.split() # line beginning with macro if l[0][0] == '$': wline = get_wrapped_line(inpath, curline) wline.insert(0, Glob.A_UNKNOWN) bar_info.add_line(wline) continue """ Handle BEGIN and END here. This is outside of the Repeat/End and variable expand loops so SHOULD be pretty bullet proof. Note that the beginData stuff is global to this module ... the Include/Use directives check to make sure we're not doing that inside a Begin/End. beginData[] is a list which we append to as more Begins are encountered. The placement here is pretty deliberate. Variable expand comes later so you can't macroize BEGIN ... I think this makes sense. The tests for 'begin', 'end' and the appending of the current begin[] stuff have to be here, in this order. """ action = l[0].upper() # 1st arg in line # parse BEGIN and END block if action == 'BEGIN': block_action = l[1].upper() begin_block = parse_begin_block(inpath, curline) if block_action in supported_block_actions: tokens = parse_supported_block_action(block_action, begin_block) begin_block = tokens begin_block.insert(0, Glob.A_BEGIN_BLOCK) begin_block.insert(1, block_action) bar_info.add_line(begin_block) continue # parse MSET block if action == 'MSET': mset_block = parse_mset_block(inpath, curline) mset_block.insert(0, Glob.A_UNKNOWN) bar_info.add_line(mset_block) continue # parse IF - ENDIF block if action == 'IF': if_block = parse_if_block(inpath, curline) if_block.insert(0, Glob.A_UNKNOWN) bar_info.add_line(if_block) continue # supported commands if action in supported_actions: wline = get_wrapped_line_join(inpath, curline) tokens = parse_supported_action(action, wline) tokens.insert(0, action) bar_info.add_line(tokens) continue # if the command is in the simple function table if action in simple_funcs: wline = get_wrapped_line(inpath, curline) wline.insert(0, Glob.A_UNKNOWN) bar_info.add_line(wline) continue """ We have several possibilities ... 1. The command is a valid assigned track name, 2. The command is a valid track name, but needs to be dynamically allocated, 3. It's really a chord action """ # track function BASS/DRUM/APEGGIO/CHORD ... if '-' in action: trk_class, ext = action.split('-', 1) #@UnusedVariable else: trk_class = action if trk_class in trk_classes: # parsing track sequence ? parse_seq = len(l) >= 1 and l[1].upper() == 'SEQUENCE' wline = [] while True: wline.extend(get_wrapped_line(inpath, curline)) if not parse_seq: break """ Count the number of { and } and if they don't match read more lines and append. If we get to the EOF then we're screwed and we error out. """ wline2 = ''.join(wline) if wline2.count('{') == wline2.count('}'): break curline = inpath.readline() if not curline: raise ValueError("Reached EOF, Sequence {}s do not match") wline.insert(0, Glob.A_UNKNOWN) bar_info.add_line(wline) continue # join the wrapped line into one line wline = get_wrapped_line_join(inpath, curline) if wline[0].replace('\\\n', '').strip() == '': # line is a comment or empty wrapped line act = Glob.A_REMARK if wline[1].strip() else Glob.A_UNKNOWN bar_info.add_line([act , wline[0], wline[1]]) continue l, eol = wline ### Gotta be a chord data line! """ A data line can have an optional bar number at the start of the line. Makes debugging input easier. The next block strips leading integers off the line. Note that a line number on a line by itself it okay. """ before_number = '' if action.isdigit(): # isdigit() matches '1', '1234' but not '1a'! l2 = l.lstrip() before_number_len = len(l) - len(l2) before_number = l[0:before_number_len] l = l2 numstr = l.split()[0] bar_chords.set_number(int(numstr)) l = l[len(numstr):] # remove number if len(l.strip()) == 0: # ignore empty lines bar_info.add_line([ Glob.A_UNKNOWN, wline[0] + wline[1] ]) continue """ We now have a valid line. It'll look something like: 'Cm', '/', 'z', 'F#@4.5' { lyrics } [ solo ] * 2 Special processing in needed for 'z' options in chords. A 'z' can be of the form 'CHORDzX', 'z!' or just 'z'. """ after_number = None last_chord = [] ctable = [] i = 0 solo_count = 0 lyrics_count = 0 mismatched_solo = "Mismatched {}s for solo found in chord line" mismatched_lyrics = "Mismatched []s for lyrics found in chord line" while True: chars = '' while i < len(l): ch = l[i] if ch == '{': """ Extract solo(s) from line ... this is anything in {}s. The solo data is pushed into RIFFs and discarded from the current line. """ solo_count += 1 elif ch == '[': """ Set lyrics from [stuff] in the current line. NOTE: lyric.extract() inserts previously created data from LYRICS SET and inserts the chord names if that flag is active. """ lyrics_count += 1 elif ch == '}': solo_count -= 1 if solo_count < 0: raise ValueError(mismatched_solo) elif ch == ']': lyrics_count -= 1 if lyrics_count < 0: raise ValueError(mismatched_lyrics) elif ch == '*': """ A bar can have an optional repeat count. This must be at the end of bar in the form '* xx'. """ pass elif ch in '\t\n\\ 0123456789': # white spaces, \ and repeat count pass elif solo_count == 0 and lyrics_count == 0: # found beginning of the chord break chars += ch i += 1 if i == len(l): # no more chord is coming if solo_count != 0: raise ValueError(mismatched_solo) if lyrics_count != 0: raise ValueError(mismatched_lyrics) if after_number == None: after_number = chars else: last_chord.append(chars) ctable.append(last_chord) break else: # chord beginning if after_number == None: after_number = chars else: last_chord.append(chars) ctable.append(last_chord) chord_begin = i # find the end of the chord while i < len(l): if l[i] in '{}[]*\t\n\\ ': break i += 1 # chord examples: '/', 'z', 'Am7@2', 'Am6zC@3' c = l[chord_begin:i] last_chord = [ c ] # the trailing string of the last chord can possibly include '\n' after which # it would be difficult to add further chords. Therefore move the trailing string # of the last chord to eol eol = last_chord[1] + eol last_chord[1] = '' bar_chords.set_before_number(before_number) bar_chords.set_after_number(after_number) bar_chords.set_eol(eol) bar_chords.set_chords(ctable) song_bar_info.append(bar_info) song_bar_chords.append(bar_chords) bar_number = bar_number + 1 bar_info = BarInfo() bar_chords = BarChords()
def set_label_from_event(self, button, event): """ Sets the label of RepeatEnd button when the count has changed. """ count = BarInfo.get_repeat_end_value(event) if count == "2": label = "RepeatEnd" else: label = "RepeatEnd " + count button.set_label(label)