#!/usr/bin/python """ A sort of drum'n'bass thing. Starting to get interesting, if incoherent. TO DO: --add global volume controls --spacey random pad sounds --fix pysco to allow syncing tracks by clock-time --fix pysco to allow shared tempo maps --scratch that and fix pysco in general :) """ from pysco import * import sys import os # import whrandom # not using this right now # Redirect stdout, as per python FAQ entry 5.8 import StringIO sys.stdout = StringIO.StringIO() orcpath = "." orcname = orcpath + "/song1.orc" sconame = orcpath + "/song1.sco" output = open(sconame, 'w') ######## SET UP INSTRUMENTS ################ tempo = 185 ########### A SILLY STEP SEQUENCER ######################### def step_seq(chunk, irawstring, ticksize, notes, modifier=1): """ a really dumb one-parameter step sequencer. This is given as a fairly trivial example of making a custom compositional interface within pysco. It works kind of like a dumb drum machine. See examples below. Usage: ticksize gives rhythmic type: e.g. ticksize= 8 means ticks are 8th notes. irawstring is expected to refer to a raw string containing three %f's: one for start time, one for duration, one for volume. notes is a (multiline) string containing note volumes for each tick. If a value is zero, that tick is left out. modifier is applied to volume (the last %f in irawstring). """ chunk_new(chunk) notes = string.split(notes) ## ticks = len(values) ticksize = 4.0 / ticksize beat = 0.0 ticks = len(notes) endbeat = (ticks -1) * ticksize for v in notes[:-1]: if v == '0': pass # skip notes with volume zero. else: v = float(v) v = v * modifier istring = irawstring % (beat, ticksize, v) add_note(chunk, istring) beat = beat + ticksize # add last note, even if silent, so chunk ends at right place. lastv = float(notes[-1]) * modifier istring = irawstring % (endbeat, ticksize, lastv) add_note(chunk, istring) ############# SET UP MUSICAL CHUNKS ######################### # These are note format strings to be used by step_seq() # use these first two with drum from H Mikelson's tomtom.orc kick = r"i15 %f %f %f 5.05 100 100 .4 20 .5" tom = r"i15 %f %f %f 6.07 30 800 .08 85 .5" snare = r"i16 %f %f %f" hat = r"i3 %f %f 12 %f .1 .8 " step_seq("kick1", kick, 8, '10 0 0 6 0 4 0 8 0 0 5 0 5 0 0 0', 3200) step_seq("tom1", tom, 8, '3 2 0 0 0 0 3 0 0 0 0 0 0 0 3 0', 3200) step_seq("hat1", hat, 16, \ ''' 10 0 3 1 7 5 3 0 3 1 3 1 7 5 10 1 10 6 3 2 10 0 0 1 0 1 0 1 0 1 0 1 ''', .06) step_seq("snare1", snare, 16, \ ''' 5 2 1 .5 3 1 .5 .5 0 0 .5 0 8 0 0 0 ''', 50) chunk_new("bass1", """ i5 0 1 6.05 i5 2 .5 6.05 i5 2.5 .5 6.01 i5 3 .5 6.03 i5 3.5 1 6.03 i5 4.5 .5 6.00 i5 5 1 5.09 """) chunk_new("mouthsn1", """ i3 1 .5 11 0.5000 0.6000 1.1892 i3 3 1 11 0.5000 0.3000 1.0000 """ ) chunk_new("ringnoise1", """ ;p2=time 3=dur 4=atck 5=sus 6=dB 7=bw% 8=pch i1 0 1.3 .1 0.001 76 10 7.00 i1 1 .5 .05 . 79 12 7.02 i1 1.5 .7 .02 .1 81 8 7.03 i1 1.9 .8 .2 0.001 83 7 6.09 i1 2.6 1 .01 . 66 10 8.09 i1 3.2 1.8 .005 . 73 15 8.10 i1 4 5 .005 .1 78 30 8.07 i1 6 5 1 1 90 80 5.06 i1 8.3 .5 .3 0.001 60 10 10.09 i1 7.4 5 1.5 2 37 60 11.05 i1 7.8 7 1 2.5 33 60 11.08 """ ) ######## FUNCTION TABLES FOR CSOUND ############## rawplay(""" f2 0 512 -7 30000 256 30000 0 -30000 256 -30000 ;square wave, !normalized f1 0 65536 10 1 f5 0 512 7 -1 512 1 """) # soundfiles must be in $SSDIR or $SFDIR rawplay('f11 0 8192 -1 "drum-like/mouthnoise1.wav" 0 4 1') rawplay('f12 0 8192 -1 "drumachines/cyms+hats/hihat1.wav" 0 4 1') rawplay("i97 0 10 0") # not using this for anything interesting yet rawplay("i97 0 10 1") ############## START OF COMPOSITION ################################## # This demonstrates that the current tempo system is too hard to set # multiple tracks to one tempo. for t in ("mouthsnares", "noises", "bass", "toms", "kicks", "snares", "hats"): tempo_marker(tempo, when=0, track=t) # Start with a roll. set_track("snares") play("snare1") set_now(0) set_track("bass") rest(8) ## # Randomly set pulse width for PWM... ## for i in range(10): ## pulsewidth = whrandom.whrandom() ## rawplay("i97 %f %f %d" % (get_now() * i * 5, 5, pulsewidth.randint(0, 100))) rawplay("i99 0 90 1 2") # this controls LFO for the PWM bass instrument oldtime = get_now() for i in range(2): for i2 in range(2): play("bass1") rest(10) # bass1 is a 6-beat phrase; this should rest until bar 5 rest(4) rawplay("i105 %f %f 4000 1000 .4 .5 0 0 ; Filter for bass" % (oldtime, get_now())) start_rest = 8 # toms. set_track("toms") rest(start_rest) for i in range(8): play("tom1") # kicks. set_track("kicks") rest(start_rest) for i in range(4): play("kick1") rest(8) play("kick1") rest(8) play("kick1") rest(8) # hi-hats. set_track('hats') rest(16) # starts at 16th beat. for i in range(8): play('hat1') # pseudo-snare thing set_track('snares') rest(start_rest + 12) play("snare1") playlines(beat_range(0, 2)) playlines(beat_range(0, 2)) rest(2) playlines(beat_range(3,4)) playlines(beat_range(3,4)) rest(4) for i in range(3): playlines(beat_range(0,1)) rest(.75) ############ SYNC POINT ########### set_track("kicks") comment("<<<< sync all tracks to kicks here...beat %f >>>>>>" \ % Now["kicks"]) for t in Now.keys(): set_now(Now["kicks"], t) tempo_marker(tempo, track=t) # Noises start and gradually get slower... # notice that this is independent of tempo in other tracks, cool! set_track("noises") # comment("WHAT TIME IS IT??") comment("beat = %f" % Now[Track]) for i in range(6): # repeat 6 times play("ringnoise1") tempo_marker(tempo / 4, track="noises") # That slows tempo to 1/4 of starting value at end of the repeats set_track("kicks") play("kick1") play("kick1") for i in range(4): playlines(beat_range(0,2)) for i in range(2): for i in range(3): play() rest(4) set_track("bass") oldtime= Now[Track] for i in range(4): play("bass1") play(); rest(2) rawplay("i105 %f %f 4000 500 .3 .9 30 40 ; Filters" % (oldtime, Now[Track])) set_now(Now["bass"], "kicks") # sync kicks to this point for i in range(8): playlines(beat_range(0, 2.5)) rest(1) set_track("kicks") comment( "****** time is now: %f" % Now[Track]) for i in range(16): play("kick1") tempo_marker(tempo) set_track("mouthsnares") for i in range(8): play("mouthsn1") tempo_marker(tempo) set_track("toms") rest(3) play("tom1") rest(3) play() rest(3) playlines(beat_range(0,2)) rest(8) play() play() rest(2); playlines(beat_range(0,2)) set_track("snares") play("snare1") rest(2) play() set_now(Now["kicks"] - 16, "snares") tempo_marker(tempo) play() play() tempo_marker(tempo) tempo_marker(tempo * 2) for i in range(6): playlines(beat_range(0, 2.5)) ########## GLOBAL REVERB INSTRUMENT ################# # We put this in after everything so we can get duration long enough # automatically. # p4 = reverb time ; p5 = random modulation; p6 = filter cutoffs rawplay("i125 0 %f .4 1 8000 ; GLOBAL REVERB" % get_now()) ############## post-processing sort_Out() warp_Out() play_Out() ## print_Tempos() ## print_Out() ################### write the score, and call csound for thing in sys.stdout.buflist: output.write(thing) output.close() ## # Here's a bunch of different ways to call csound when done with orc... ## # Render a wave file into SFDIR ## os.system("nice csound -dm0 -Wo song1.wav %s %s" % (orcname, sconame)) ## # Realtime audio output os.system("csound -dm0 -o devaudio %s %s" % (orcname, sconame)) ## # Render a wave file and then play it, assuming we have sox installed ## os.system("csound -dm0 -Wo ./song1.wav %s %s" % (orcname, sconame)) #os.system("play ./song1.wav")