Our drum program is now functional. You can load drum samples and define a beat pattern and our drum machine will play it out. Let us now extend our drum program so that we are able to create more than one pattern in the same program.
Rather than a single drum pattern, now we will have a list of patterns. While playing the patterns, a user will be able to switch between many different beat patterns. This will allow the drummer to add variations to the performance.
This is added to the create_top_bar
method (refer to the code in 3.08.py
):
Label(top_bar_frame, text='Pattern Number:').grid(row=0, column=1) self.patt = IntVar() self.patt.set(0) self.prevpatvalue = 0 # to trace last click Spinbox(top_bar_frame, from_=0, to=9, width=5, textvariable=self.patt, command=self.record_pattern).grid(row=0, column=2) self.pat_name = Entry(top_bar_frame) self.pat_name.grid(row=0, column=3, padx=7,pady=2) self.pat_name.insert(0, 'Pattern %s'%self.patt.get()) self.pat_name.config(state='readonly')
The description of the code is listed as follows:
self.patt
.self.pat_name
. This widget is marked as "read only", as we do not want to allow the user to modify the name.command
callback to a new method record_pattern
.record_pattern
method. The role of this method is to keep track of the state of a given pattern. Thus, for every pattern it needs to track the pattern number, units, BPU, drum samples loaded, and the beat pattern defined by the user for that pattern number. We will store this information in a list named self.pattern_list
.Our pattern spin box allows for adding 10 patterns. Therefore, we first initialize self.pattern_list
as an empty list comprising of 10 empty spaces.
We initialize it in our class __init__
method as follows (also seen in the code 3.08.py
):
self.pattern_list = [None]*10
Let us now code the record_pattern
method:
def record_pattern(self): pattern_num, bpu, units = self.patt.get(),self.bpu.get(), self.units.get() self.pat_name.config(state='normal') self.pat_name.delete(0, END) self.pat_name.insert(0, 'Pattern %s'%pattern_num) self.pat_name.config(state='readonly') prevpval = self.prevpatvalue self.prevpatvalue = pattern_num c = bpu*units self.buttonpickleformat =[[0] * c for x in range MAX_DRUM_NUM)] for i in range(MAX_DRUM_NUM): for j in range(c): if self.button[i][j].config('bg')[-1] == 'green': self.buttonpickleformat[i][j] = 'active' self.pattern_list[prevpval] = {'df': self.widget_drum_file_name, 'bl': self.buttonpickleformat, 'bpu':bpu, 'units':units} self.reconstruct_pattern(pattern_num, bpu, units)
The description of the code is listed as follows:
normal
to allow us to enter text into the Entry widget. We then delete anything that might already be written in the widget and enter the new pattern name with the Python string formatting of pattern_num'Pattern %s'%pattern_num
. Finally, we restore the entry widget to a read only
state.self.buttonpickleformat
. The list is first initialized to an empty two-dimensional matrix, taking into consideration the size of the pattern maker.0
. If the button is selected (green), the value at the corresponding place is changed from 0
to active
. Using this list we can then easily reproduce the user-defined pattern later on.self.pattern_list[prevpval] = {'df': self.widget_drum_file_name, 'bl': self.buttonpickleformat, 'bpu':bpu, 'units':units}
df
stores the list of drum filenames. The key bl
stores the pattern defined by the button. The key bpu
stores the BPU for that pattern, and the key units
stores the units for that pattern.reconstruct_pattern()
, which actually does the reconstruction for us.reconstruct_pattern
to handle it, as shown in the following code see the code in 3.08.py
):def reconstruct_pattern(self,pattern_num, bpu, units): self.widget_drum_file_name = [0]*MAX_DRUM_NUM try: self.df = self.pattern_list[pattern_num]['df'] for i in range(len(self.df)): file_name = self.df[i] if file_name == 0: self.widget_drum_name[i].delete(0, END) continue self.widget_drum_file_name.insert(i, file_name) drum_name = os.path.basename(file_name) self.widget_drum_name[i].delete(0, END) self.widget_drum_name[i].insert(0, drum_name) except: for i in range(MAX_DRUM_NUM): try: self.df except:self.widget_drum_name[i].delete(0, END) try: bpu = self.pattern_list[pattern_num]['bpu'] units = self.pattern_list[pattern_num]['units'] except: return self.bpu_widget.delete(0, END) self.bpu_widget.insert(0, bpu) self.units_widget.delete(0, END) self.units_widget.insert(0, units) self.create_right_pad() c = bpu * units self.create_right_pad() try: for i in range(MAX_DRUM_NUM): for j in range(c): if self.pattern_list[pattern_num]['bl'][i][j] == 'active': self.button[i][j].config(bg='green') except:return
This code can be broken into three broad parts:
Having reconstructed these three things, we can easily replay any beat pattern. A brief description of each of these is as follows:
self.pattern_list[pattern_num]['df']
. We then iterate through items in this list and fill up the Entry widgets with each drum sample's filename.self.pattern_list[pattern_num]['bpu']
and self.pattern_list[pattern_num]['units']
. We insert these values in their respective Spinbox widgets and then call the create_right_pad()
method, which places the desired number of buttons on the right pad.self.pattern_list[pattern_num]['bl']
, which gives us the position of the green buttons. Iterating through a loop, we check if a particular button is to be set to active
. If yes, we change the color of the button to green.Hit the Play button and the drum machine will start rolling sound. Change the pattern number and define a new beat pattern. The new pattern will start playing. Revert to older patterns and the older patterns start playing again (refer to the code in 3.08.py
).
We've completed coding our drum machine to support the storing of multiple beat patterns, and the ability to play these patterns simply by changing the pattern number. This gives the user the ability to make different beats for the intro, verse, chorus, bridge, and other parts of a song.
In the process, we saw how to use Python's built-in data types to store custom data and to reproduce them in any required way.
18.188.37.136