#!/usr/bin/python #### pystepseq-2.0 (A small sequencer application by Aaron K. Johnson) ##### modules needed: from sys import * from Tkinter import * from time import * from random import * import string from thread import * from sched import * from copy import * ####### my modules: from midi_functions import * from scales import * from microtonal import * from pink_noise import * ###### midi setup: if len(argv)<3: print "Usage: \n pystepseq \n" exit() device_arg=str(argv[1]) open_port(device_arg) channel_choice=int(argv[2])-1 if channel_choice < 0 or channel_choice > 15: print 'Bad midi channel number' exit() channel=0 ###### init some variables: text='blah' virgin=1 which=1 note_autocounter=0 vol_autocounter=0 note=60 old_note=60 ####### scales and custon scales: normal_scale_choices=['pentatonic','modal','chromatic', \ 'whole_tone','octatonic','gypsy',\ 'overtone','tabla','hydrogen_drums'] microtonal_scale_choices=['five_tet','seven_tet','jorgensen_5and7','seventeen_tet', 'seventeen_rast',\ 'harm_hexatonic','harm_octatonic','beaty', 'efg5577', 'hexany'] all_scales=copy(normal_scale_choices) all_scales.extend(microtonal_scale_choices) ##### cents values go here: five_tet_cents=(51.429, 291.429, 531.429, 771.429, 1011.429) seven_tet_cents=(0, 171.429, 342.857, 514.286, 685.714, 857.143, 1028.571) jorgensen_5and7_cents=(0, 51.429, 171.429, 291.429, 342.857, 514.286, 531.429, 685.714, 771.429, 857.143, 1011.429, 1028.571) seventeen_tet_cents=(0, 70.588, 141.176, 211.765, 282.353, 352.941, 423.529, 494.118, 564.706, 635.294, 705.882, 776.471, 847.059, 917.647, 988.235, 1058.824, 1129.412) seventeen_rast_cents=(0, 211.765, 352.941, 494.118, 705.882, 847.059, 988.235) harm_hexatonic_cents=(0, 266.871, 498.045, 701.955, 884.359, 1049.363) harm_octatonic_cents=(0, 203.910, 386.314, 551.318, 701.955, 840.528, 968.826, 1088.269) beaty_cents=(0, 27.264, 203.910, 231.174, 386.314, 413.578, 701.955, 729.219, 968.826, 996.090) efg5577_cents=(0, 196.198, 351.338, 427.373, 582.512, 737.652, 813.686, 968.826, 1165.024) hexany_cents=(0,266.871,386.314,653.185,884.359,968.826) ######## end custon scales ######## global master scale arrays for sc in normal_scale_choices[:-2]: exec("""globals()['%s_scale']=create_scale(0,120, %s)"""% (sc,sc)) for sc in microtonal_scale_choices: exec("""globals()['%s_scale']=create_scale_from_cents(%s_cents,0,10)""" % (sc,sc)) ########## def scale_config(): global scale_choice, scale_string, scale_size, low_oct, high_oct, normal_scale_choices, microtonal_scale_choices lowest=low_oct.get() highest=high_oct.get() if highest < lowest: highest=lowest high_oct.set(lowest+1) #### get full set length: full_scale=eval("%s_scale" % scale_string.get()) if (scale_string.get() in microtonal_scale_choices): notes_per_octave=(len(full_scale[0])-1)/10 #### divided by 10 because we already multiplied by 10 above exec("""scale_notes=locals()['full_scale'][0][%i:%i]""" % \ (lowest*notes_per_octave, highest*notes_per_octave+1)) exec("""scale_bends=locals()['full_scale'][1][%i:%i]""" % \ (lowest*notes_per_octave, highest*notes_per_octave+1)) scale_choice=(scale_notes[:],scale_bends[:]) scale_size=len(scale_notes)-1 elif (scale_string.get() in ['tabla', 'hydrogen_drums'] ): scale_notes=full_scale[:] scale_choice=scale_notes[:] scale_size=len(scale_notes)-1 else: notes_per_octave=full_scale.index(12) exec("scale_notes=locals()['full_scale'][%i:%i]" % \ (lowest*notes_per_octave, highest*notes_per_octave+1)) scale_choice=scale_notes[:] scale_size=len(scale_choice)-1 for x in range(1,33): exec("""note_slider%i.config(from_=%i)""" % (x, scale_size)) ##### scheduler stuff: def rel_time(): global start_time return time.time() - start_time seq=scheduler(rel_time,sleep) ##### evoked on start/stop button def reverse(): global runstate, old_note, which, virgin, start_time, seqtime if runstate=="START": runstate="STOP!" RUN.config(text=runstate) seqtime=0 start_new_thread(main,()) start_time=time.time() seq.run() start_new_thread(updateGUI,()) else: runstate="START" virgin=1 which=1 RUN.config(text=runstate) ######## randomize function: def note_white(): global scale_size, scale_choice var=note_randomness.get() chance=note_space.get() scale_midpoint=scale_size/2 for blah in range(1,33): randnum=scale_midpoint+randint(-var,var) if (randnum>scale_size): randnum=scale_size-(randnum-scale_size) if (randnum<-2): randnum=-2+abs(-2-randnum) if (chance>=randint(1,100)): #### for space randnum = -1 text="""note_slider%i.set(randnum)""" % blah exec(text) def note_brown(): global scale_size, scale_choice var=note_randomness.get() chance=note_space.get() for blah in range(2,33): offset=randint(-var,var) text="""note_slider%i.get()""" % (blah-1) current=eval(text) new=(current+offset) if (new>scale_size): new=(current-offset) if (new<-2): new=(current-offset) newtext="""note_slider%i.set(new)""" % blah exec(newtext) for gah in range(2,33): #### for space: if (chance>=randint(1,100)): newbie=randint(-2,-1) newertext="""note_slider%i.set(newbie)""" % gah exec(newertext) def note_pink(): global scale_size, scale_choice var=note_randomness.get() chance=note_space.get() scale_midpoint=scale_size/2 result_array=pink_noise(5,var) offset = -1*(max(result_array)/2) for blah in range(1,33): randnum=scale_midpoint+(result_array[blah-1]+offset) if (randnum>scale_size): randnum=scale_size-(randnum-scale_size) if (randnum<-2): randnum=-2+abs(-2-randnum) if (chance>=randint(1,100)): #### for space randnum = -1 text="""note_slider%i.set(randnum)""" % blah exec(text) def volume_white(): var=vol_randomness.get() chance=vol_space.get() for blah in range(1,33): randnum=64+randint(-var,var) ### 64 is half of 127 if (randnum>127): randnum=127-(randnum-127) if (randnum<0): randnum=abs(randnum) if (chance>=randint(1,100)): #### for space randnum = 0 text="""vol_slider%i.set(randnum)""" % blah exec(text) def volume_brown(): var=vol_randomness.get() chance=vol_space.get() for blah in range(2,33): offset=randint(-var,var) text="""vol_slider%i.get()""" % (blah-1) current=eval(text) new=(current+offset) if (new>127): new=(current-offset) if (new<0): new=(current-offset) newtext="""vol_slider%i.set(new)""" % blah exec(newtext) for gah in range(2,33): #### for space: if (chance>=randint(1,100)): newertext="""vol_slider%i.set(0)""" % gah exec(newertext) def volume_pink(): var=vol_randomness.get() chance=vol_space.get() result_array=pink_noise(5,var) offset = -1*(max(result_array)/2) for blah in range(1,33): randnum=64+(result_array[blah-1]+offset) if (randnum>127): randnum=127-(randnum-127) if (randnum<0): randnum=abs(randnum) if (chance>=randint(1,100)): #### for space randnum = 0 text="""vol_slider%i.set(randnum)""" % blah exec(text) #### (for the evolver functions): def note_singlerandom(): global scale_size, scale_choice end=endpoint.get() var=note_randomness.get() randindex=randint(1,end) text="""note_slider%i.get()""" % randindex current=eval(text) randnum=randint(-var,var) if (randnum>scale_size): offset=randint(-1,0) else: offset=(current+randnum) if (offset>scale_size): offset=randint(int(scale_size*.33),int(scale_size*.66)) if (offset<-2): offset=abs(offset) saying="""note_slider%i.set(offset)""" % randindex exec(saying) def volume_singlerandom(): end=endpoint.get() var=note_randomness.get() randindex=randint(1,end) text="""vol_slider%i.get()""" % randindex current=eval(text) randnum=randint(-var,var) if (randnum>127): offset=0 else: offset=(current+randnum) if (offset>127): offset=127-(offset-127) if (offset<0): offset=abs(offset) saying="""vol_slider%i.set(offset)""" % randindex exec(saying) ##################################################################### def updateGUI(): global runstate while runstate=="STOP!": sleep(.1) root.update() def main(): global runstate, endpoint, tempo, virgin, channel, old_channel, channel_choice, note_index, note, vol, old_note,\ which, note_autocounter, vol_autocounter, root, note_evolution, vol_evolution, note_randomness, vol_randomness, \ scale_choice, scale_size, scale_string, microtonal_scale_choices, transpose, mode, bend, \ seq, seqtime, start_time while runstate=="STOP!": ###### getting global controlled variables: if len(seq.queue) < 3: word='note%i.get()' % which note_index=eval(word) if note_index in (-1,-2): note=note_index else: if (scale_string.get() in microtonal_scale_choices): microtonal=1 note=eval("scale_choice[0][%i]" % bounce(note_index+mode.get(),scale_size)) bend=eval("scale_choice[1][%i]" % bounce(note_index+mode.get(),scale_size)) else: microtonal=0 note=eval("scale_choice[%i]" % bounce(note_index+mode.get(),scale_size)) bend=8192 vol_word='vol%i.get()' % which vol=eval(vol_word) end=endpoint.get() realtempo=tempo.get() note_evil=note_evolution.get() vol_evil=vol_evolution.get() ################################## if not virgin and (note != -1): seq.enterabs(seqtime,1,note_off,(old_channel,old_note)) if microtonal: channel=((old_channel+1) % 2) + channel_choice else: channel=channel_choice if note==-2: note=60 seq.enterabs(seqtime,1,note_on,(channel,note,0)) virgin=0 old_note=note old_channel=channel which=which+1 seqtime=seqtime+(15.0/realtempo) elif note==-1: which=which+1 seqtime=seqtime+(15.0/realtempo) else: note=note+transpose.get() seq.enterabs(seqtime,1,pitch_bend,(channel, bend)) seq.enterabs(seqtime,2,note_on,(channel, note, vol)) virgin=0 old_channel=channel old_note=note which=which+1 seqtime=seqtime+(15.0/realtempo) ###### update cycle count, etc. if which>end: which=1 note_autocounter=note_autocounter+1 vol_autocounter=vol_autocounter+1 if (note_autocounter>(note_evil-1)): note_autocounter=0 if (note_evil>0): note_singlerandom() if (vol_autocounter>(vol_evil-1)): vol_autocounter=0 if (vol_evil>0): volume_singlerandom() else: seq.run() note_off(channel,old_note) return ##### init some GUI-related variables: root=Tk() root.title("pystepseq") runstate="START" endpoint=IntVar(root) ##### maximum sequencer step variable for num in range(1,33): phrase="""note%i=IntVar(root)""" % num exec(phrase) for num in range(1,33): phrase="""vol%i=IntVar(root)""" % num exec(phrase) for num in range(1,33): phrase="""note%i.set(3)""" % num exec(phrase) for num in range(1,33): phrase="""vol%i.set(96)""" % num exec(phrase) note_randomness=IntVar(root) note_randomness.set(5) note_space=IntVar(root) note_evolution=IntVar(root) vol_randomness=IntVar(root) vol_randomness.set(16) vol_space=IntVar(root) vol_evolution=IntVar(root) tempo=IntVar(root) tempo.set(120) mode=IntVar(root) transpose=IntVar(root) scale_string=StringVar(root) scale_string.set("modal") low_oct=IntVar(root) low_oct.set(4) high_oct=IntVar(root) high_oct.set(6) ############ GUI stuff: menu=Menu(root) scale_menu=Menu(menu) menu.add_cascade(label="Scales", menu=scale_menu) for scale in all_scales: exec("""scale_menu.add_radiobutton(label=\"%s\", variable=scale_string, value=\"%s\", command=scale_config)""" % (scale,scale)) root.config(menu=menu) sliderframe=Frame(root) sliderframe.grid(row=0, sticky=W+E+N+S, padx=5) hiloframe=Frame(root) hiloframe.grid(row=1) noterandomframe=Frame(root) noterandomframe.grid(row=2) volrandomframe=Frame(root) volrandomframe.grid(row=3) tempoframe=Frame(root) tempoframe.grid(row=4) buttonframe=Frame(root) buttonframe.grid(row=5) ######### Widgets: for num in range(1,33): phrase="radio%i = Radiobutton(sliderframe, variable=endpoint, indicatoron=0, width=2, value=%i)" % (num,num) exec(phrase) radio16.select() ##### maximum sequencer step variable initialized to 16 for num in range(1,33): phrase="""note_slider%i = Scale(sliderframe, from_=%i, to=-2, orient=VERTICAL, length=100, width=16, variable=note%i, showvalue=0)""" % (num,50,num) exec(phrase) for num in range(1,33): phrase="""vol_slider%i = Scale(sliderframe, from_=%i, to=0, orient=VERTICAL, length=100, width=16, variable=vol%i, showvalue=0)""" % (num,127,num) exec(phrase) low_oct_slider= Scale(hiloframe, from_=2, to=6, orient=HORIZONTAL, length=80, variable=low_oct) high_oct_slider = Scale(hiloframe, from_=3, to=7, orient=HORIZONTAL, length=80, variable=high_oct) note_white = Button(noterandomframe,text='NOTE WHITE',command=note_white) note_brown = Button(noterandomframe,text='NOTE BROWN',foreground='brown',command=note_brown) note_pink = Button(noterandomframe,text='NOTE PINK', foreground='pink', command=note_pink) note_rlabel = Label(noterandomframe,text="random\ndepth") note_rscale = Scale(noterandomframe, length=80, from_=1, to=30, orient=HORIZONTAL, variable=note_randomness) note_slabel = Label(noterandomframe,text="space") note_sscale = Scale(noterandomframe, length=80, from_=1, to=100, orient=HORIZONTAL, variable=note_space) note_elabel = Label(noterandomframe,text="evolver") note_escale = Scale(noterandomframe, length=80, from_=0, to=6, orient=HORIZONTAL, variable=note_evolution) vol_white = Button(volrandomframe,text='VOL WHITE',command=volume_white) vol_brown = Button(volrandomframe,text='VOL BROWN',foreground='brown',command=volume_brown) vol_pink = Button(volrandomframe,text='VOL PINK', foreground='pink', command=volume_pink) vol_rlabel = Label(volrandomframe,text="random\ndepth") vol_rscale = Scale(volrandomframe, length=80, from_=1, to=30, orient=HORIZONTAL, variable=vol_randomness) vol_slabel = Label(volrandomframe,text="space") vol_sscale = Scale(volrandomframe, length=80, from_=1, to=100, orient=HORIZONTAL, variable=vol_space) vol_elabel = Label(volrandomframe,text="evolver") vol_escale = Scale(volrandomframe, length=80, from_=0, to=6, orient=HORIZONTAL, variable=vol_evolution) tempo_label = Label(tempoframe, text="tempo") tempo_scale = Scale(tempoframe, length=120, from_=5, to=300, orient=HORIZONTAL, variable=tempo) mode_label = Label(tempoframe, text="mode") mode_scale = Scale (tempoframe, length=120, from_=-24, to=24, orient=HORIZONTAL, variable=mode) transpose_label = Label(tempoframe,text="transpose") transpose_scale = Scale (tempoframe, length=120, from_=-12, to=12, orient=HORIZONTAL, variable=transpose) RUN = Button(buttonframe, text=runstate, command=reverse) QUIT = Button(buttonframe, text=' QUIT ', foreground='red', command=root.quit) ####### widget geometry: for num in range(1,33): phrase= """radio%i.grid(row=0, column=%i, sticky=W+E+N+S)""" % (num,num-1) exec(phrase) for num in range(1,33): phrase= """note_slider%i.grid(row=1, column=%i, sticky=W+E+N+S)""" % (num,num-1) exec(phrase) for num in range(1,33): phrase= """vol_slider%i.grid(row=2, column=%i, sticky=W+E+N+S)""" % (num,num-1) exec(phrase) low_oct_slider.pack(side=LEFT) high_oct_slider.pack(side=LEFT) note_white.pack(side=LEFT) note_brown.pack(side=LEFT) note_pink.pack(side=LEFT) note_rlabel.pack(side=LEFT) note_rscale.pack(side=LEFT) note_slabel.pack(side=LEFT) note_sscale.pack(side=LEFT) note_elabel.pack(side=LEFT) note_escale.pack(side=LEFT) vol_white.pack(side=LEFT) vol_brown.pack(side=LEFT) vol_pink.pack(side=LEFT) vol_rlabel.pack(side=LEFT) vol_rscale.pack(side=LEFT) vol_slabel.pack(side=LEFT) vol_sscale.pack(side=LEFT) vol_elabel.pack(side=LEFT) vol_escale.pack(side=LEFT) tempo_label.pack(side=LEFT) tempo_scale.pack(side=LEFT) mode_label.pack(side=LEFT) mode_scale.pack(side=LEFT) transpose_label.pack(side=LEFT) transpose_scale.pack(side=LEFT) RUN.pack(side=LEFT) QUIT.pack(side=LEFT) scale_config() ####### run the thing!!!! root.mainloop()