diff --git a/.gitignore b/.gitignore index 1371b13..ca272b4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,8 @@ *.jpeg *.wav *.mp3 +delete* +*delete +scratch* +test* +*test diff --git a/.sunvox_clipboard.sunsynth b/.sunvox_clipboard.sunsynth new file mode 100644 index 0000000..9a3dfae Binary files /dev/null and b/.sunvox_clipboard.sunsynth differ diff --git a/.sunvox_prev_session.sunvox b/.sunvox_prev_session.sunvox new file mode 100644 index 0000000..1ac43a1 Binary files /dev/null and b/.sunvox_prev_session.sunvox differ diff --git a/auto-techno.py b/auto-techno.py index bf7d471..6267775 100644 --- a/auto-techno.py +++ b/auto-techno.py @@ -21,7 +21,7 @@ lowestBpm = 92 highestBpm = 135 # force everything to happen within 1 bar -oneBar = 0 +oneBar = 1 # stack name # this doesn't matter, can be any string @@ -39,12 +39,14 @@ percMidiChanStart = 0 -# perc note +# perc notes # set some notes for percussion tracks -percNotes = [0,1,2,3] +# these notes correspond to notes (C3, C#3, etc) +percNotes = [0,1,2,3,4,5,6,7] + # perc track names # a percussion track will be generated for each item in this list -percNames = ["kick", "hh", "sd", "cp", "ohh","ride","cymbal"] +percNames = ["kick", "hh","sd","ohh","rim","cp","tom","ride","cymbal"] # for use generating sequences of 0's and 1's # # @@ -57,7 +59,7 @@ percNames = ["kick", "hh", "sd", "cp", "ohh","ride","cymbal"] # for melodic sequences, define the choices for range of the notes # eg 12 is one octave, 24 is two, etc -noteRange = [12,24,36] +noteRange = [12,24] # define the shortest and longest sequence you want # eg if shortest is 7, there will be no melodic sequences shorter than 7 shortestMelodic = 3 @@ -73,7 +75,15 @@ melodicDivisions = [8,16,16,16] # how many # how many melodic elements to generate? melodyCount = 1 -melodyMidiChanStart = 7 +melodyMidiChanStart = 10 + + + +############### PADS ########################### +padCount = 1 +padMidiChanStart = melodyMidiChanStart + 1 + + ## import stuff @@ -119,9 +129,11 @@ else: seed = seed.replace("X","x") #print("seed: "+seed) +# here is the seed in decimal format (eg 3892423) decimalSeed = int(seed,16) ### FRONTMATTER ### +# put some stuff at the top of the tidal code print("-- raw input: " + str(rawinput)) print("-- hex seed: "+seed) print("-- decimal seed: "+str(decimalSeed)) @@ -170,9 +182,9 @@ def generateBinary(busyness): elif (busyness == 7): binary = [0,0,1,1,1,1,1,1,1] elif (busyness == 8): - binary = [0,0,1,1,1,1,1,1,1,1] + binary = [0,0,0,0,0,0,1,1,1,1,1,1,1] elif (busyness == 9): - binary = [0,0,1,1,1,1,1,1,1,1,1] + binary = [0,0,0,0,1,1,1,1,1,1,1,1,1] return binary def genOnOff(limit,busyness,struct): @@ -205,16 +217,36 @@ def genOnOff(limit,busyness,struct): else: return(zeroesOnes) +def genVelocity(limit): + """ + Generate a series of decimals < 1 to control velocity + """ + velocities = "" + for i in range(limit): + #velocities = str((decimal.Decimal(random.choice([0.5,0.75,1],[0.5,75,1]]))).quantize(Decimal('.01')))+" "+str(velocities) + velocities = str(random.choice([0.85,0.9,1]))+" "+velocities + + degradeAmt = (decimal.Decimal(random.random())).quantize(Decimal('.01')) + # remove trailing space + velocities = str(velocities).rstrip() + return velocities -def euclid(divider): - "generate a euclidean rhythm for tidalcycles" + + +def euclid(divider,max): + """ + generate a euclidean rhythm for tidalcycles + the "divider" argument is the 2nd number, and + the "max" argument is the maximum integer of the 1st number + ... because we don't always want a super dense rhythm! + """ # like struct "[t(3,8)]" # 3,8 10,16 4,16 #secondEuclid = str(random.choice(dividers)) secondEuclid = divider - firstEuclid = str(random.randrange(2,divider)) + firstEuclid = str(random.randrange(2,max)) #print("firstEuclid:"+firstEuclid) #print("secondEuclid:"+str(secondEuclid)) @@ -228,7 +260,9 @@ def euclid(divider): else: # the first euclid number WILL sequence (eg <4,8>) #firstEuclid = "<"+str(random.randrange(2,int(random.choice(dividers)))+" "+str(random.randrange(2,int(random.choice(dividers))))+">" - firstEuclid = "<"+str(random.randrange(2,divider))+" "+str(random.randrange(2,16))+">" + if (percName == "kick"): + divider = 5 # limit kicks to 5 + firstEuclid = "<"+str(random.randrange(2,max))+" "+str(random.randrange(2,16))+">" # decide whether the second number will seq if (random.random() > random.random()): # we will do like [t(??,<16,8>)] @@ -291,26 +325,43 @@ def stut(): time = random.choice([0.125,0.125,0.125,0.0625,0.0625]) return("stut "+str(repeats)+" 1 \""+str(time)+"\"") -def bite(): +def bite(style): """ Generate something like bite 16 "{2*4 1*2 10*4 8*8}%16 + if style = nice, just do quarter note repeats + if style = wild, do whatever """ - divisions = [4,8] - number = str(random.choice(divisions)) - - # generate like 1*4 8*4 2*2 1*2 9*1 - repeatChoices = [1,2,4] - # how long should it be? - length = int(random.randint(4,8)) - bit = "" - for i in range(length): - bit = str(random.randint(0,int(number)))+"*"+str(random.choice(repeatChoices))+" "+bit - - # since bite will always be encapsulated by ( ) - # eg whenmod 8 7 (bite 4 "2*4") - # we don't need to prepend with $ or , - return ("bite "+number+ " \"{"+bit+"}%"+number+"\"") + + if (style == "nice"): + # do something nice here + # either repeat half notes or quarter notes + number = random.choice([2,4]) + # which beat to repeat? + beat = random.choice([0,1,2,3]) + # the goal is something like + # bite 4 "{0 0 0 0}%4" + bit = beat+beat+beat+beat + bit = str(bit) + + else: + divisions = [4,8] + # number = the divisions to use + # like 'bite 16' where 16 is the 'number' + number = str(random.choice(divisions)) + + # generate like 1*4 8*4 2*2 1*2 9*1 + repeatChoices = [1,2,4] + # how long should it be? + length = int(random.randint(4,8)) + bit = "" + for i in range(length): + bit = str(random.randint(0,int(number)))+"*"+str(random.choice(repeatChoices))+" "+bit + + # since bite will always be encapsulated by ( ) + # eg whenmod 8 7 (bite 4 "2*4") + # we don't need to prepend with $ or + return ("bite "+str(number)+ " \"{"+bit+"}%"+str(number)+"\"") def offset(): offsetChoices = [0.125,0.125,0.125,0.125,0.1875,0.1875,0.1875] @@ -327,12 +378,12 @@ def fills(): But nothing too intense """ # like whenmod 8 7 - fillChoices = [stut(),"rev",scramble()] + fillChoices = ["rev","rev",scramble(),bite("nice"),bite("nice")] # how many filles do we want? fills = "" for i in range(1,10): # roll the dice - whenmods = ["8 7","16 15","16 14","32 31","64 62","64 63","32 30"] + whenmods = ["8 7","16 15","16 14","32 31","64 63","32 30","8 7"] fills = str(random.choice(fillChoices))+" "+fills return "whenmod "+str(random.choice(whenmods))+" ("+fills.rstrip()+")" @@ -343,17 +394,21 @@ def sometimesBeQuiet(): """ #def genOnOff(limit,busyness,struct): # decide whether to do this at all - if (random.randint(0,10) > 5): + if (random.randint(0,10) > 0): # for now, 8 bar phrases # (# gain "[0 1 0 0 1]/8") # how many cycles to make? - quietCycles = random.choice([2,3,4]) - quietBinary = genOnOff(quietCycles,2,0) + quietCycles = random.choice([8]) + + # this generates a sequence like 011010110 + quietBinary = genOnOff(quietCycles,4,0) # split 001001 into 0 0 1 0 0 1 quietBinary = list(quietBinary) finalQuietBinary = "" for i in quietBinary: finalQuietBinary = i + " " + finalQuietBinary + + # now the pattern looks like 0 1 0 0 1 0 0 1 print(indent+indent+"$ degradeBy \"<"+str(finalQuietBinary).rstrip()+">/8\"") ########################### LISTS ########################## @@ -366,7 +421,7 @@ cpPats=["[~t]*2","[t(3,8)]","[[~~~t][~t]]*2"] cyPats=["[t*4]","[t*8]","[t~~~]","[t~~[~~~t]]","[~~~]"] # a collection of misc rhythms -rhythmLibrary = ["[1*4]","[1*8]","[1*9]","[01]*2","[0101]","[1*16]","[1*16?]","[111111[1001][01]]/2","[{100}%16]","[{101011010101}%16]","[{10100}%16]","[{10001}%16]","[{10101}%16]","[{1001010}%16]","[{1011010}%16]","[{10101000100}%16]","[{10101001010}%16]","[{10010010010}%16]","[{1010101001010}%16]","[{1001001001010}%16]","[11*2]*4","[100000<0100>000100<01>00]","[1[10000001]]","[1111*2]","[[0001][01]]*2","[0010000100000001]","[[0001][01]]*2","[[01]1[1001][01]]","[[01]1[1001][0001]]","[11000010]*2","[[0001]000]","[[0001]]*4"] +rhythmLibrary = ["[1*4]","[1*8]","[1*16]","[01]*2","[0101]","[1*16]","[1*16?]","[111111[1001][01]]/2","[{100}%16]","[{101011010101}%16]","[{10100}%16]","[{10001}%16]","[{10101}%16]","[{1001010}%16]","[{1011010}%16]","[{10101000100}%16]","[{10101001010}%16]","[{10010010010}%16]","[{1010101001010}%16]","[{1001001001010}%16]","[11*2]*4","[100000<0100>000100<01>00]","[1[10000001]]","[1111*2]","[[0001][01]]*2","[0010000100000001]","[[0001][01]]*2","[[01]1[1001][01]]","[[01]1[1001][0001]]","[11000010]*2","[[0001]000]","[[0001]]*4"] ############### It's assemble time!! ################### @@ -390,7 +445,7 @@ print(indent+"p \""+stackName+"\" ") print(indent+indent+"-- fills") # roll the dice up to N times for i in range(1,8): - if (random.randint(0,10) > 4): + if (random.randint(0,10) > 3): print(indent+indent+"$ "+fills()) # how many "fun" things to add? @@ -429,6 +484,8 @@ percMidiChan = 0 ## will be created for i in percNames: + percName = i + # print the name of the track (from the percNames list) print(indent+indent+"-------------- "+i+" ---------------") @@ -499,14 +556,42 @@ for i in percNames: #print(","+str(genOnOff(10))) #print(str(percSequence)) # how busy will it be?a - # choose a random value between 0 and 9 - busyness = random.randrange(0,7) + # choose a random value + # guide the random value based on the name + # since we don't really want a kick on every 16th note + # lots of hh = good, lots of cymbal = maybe not + if (percName == "kick"): + busyness = random.randrange(1,3) + elif (percName == "sd"): + busyness = random.randrange(0,4) + elif (percName == "cp"): + busyness = random.randrange(0,4) + elif (percName == "hh"): + busyness = random.randrange(3,9) + elif (percName == "ride"): + busyness = random.randrange(0,6) + elif (percName == "cymbal"): + busyness = random.randrange(0,1) + else: + busyness = random.randrange(0,7) generateBinary(busyness) rhythm = str(genOnOff(percLength,busyness,1)) elif (rChoice == 2): # GENERATE EUCLIDEAN RHYTHMS - rhythm = str(euclid(random.choice(dividers))) + + # treating the kick like the other tracks often generates + # way too many events (like [t(11,16)]) which is usually + # way too much for a techno kick! + # here's I'm trying to moderate that + if (percName == "kick"): + # do something + maxEuclidEvents = 5 + else: + maxEuclidEvents = 13 + + #print("MAX EUCLID EVENTS: "+str(maxEuclidEvents)) + rhythm = str(euclid(random.choice(dividers),maxEuclidEvents)) #print(indent+indent+euclid(random.choice(dividers))) # else: @@ -521,27 +606,87 @@ for i in percNames: # add "struct" to the beginning # and enclose in < > print(indent+indent+"$ struct \"<"+fullRhythm+">\"") - print (indent+indent+"$ n "+str(random.choice(percNotes))+" # midichan "+str(percMidiChan)+",") + + # roll the dice to get a length of the velocity pattern + velocityLength = random.randint(2,16) + + notes = indent+indent+"$ n "+str(random.choice(percNotes)) + velocity = indent+indent+"# gain \"{"+genVelocity(int(random.randrange(2,16)))+"}%16\"" + midi = indent+indent+"# midichan "+str(percMidiChan) + #print (# midichan "+str(percMidiChan)+" \n"+indent+indent"# gain \"{"+genVelocity(int(random.randrange(2,16)))+"}%16"+",") + print(notes+"\n"+velocity+"\n"+midi+",") percMidiChan = percMidiChan + 1 # generate melodic tracks melodicMidiChan = melodyMidiChanStart #melodyMidiChanStart = 7 for i in range(melodyCount): - print(indent+indent+"-----------------------") + print(indent+indent+"------------ bassline -------") print(indent+indent+"degradeBy 0 ") #print(", ") busyness = random.choice([0,1,2,3,4,5,6,6,6,6,7,7,7,7,8,8,8,9,9]) if (oneBar == 1): print(indent+indent+"$ struct \""+genOnOff(16,(busyness),1)+"\"") - print(indent+indent+"$ "+genNotes(1)) + notes = genNotes(1) + print(indent+indent+"$ "+notes) else: # how many 16th notes? thisLength = random.randint(3,16) #genOnOff(8,(busyness)) print(indent+indent+"$ struct \""+genOnOff(thisLength,(busyness),1)+"\"") - print(indent+indent+"$ "+genNotes(0)) - print(indent+indent+"# midichan "+str(melodicMidiChan)+" + n (-42)") + notes = genNotes(0) + print(indent+indent+"$ "+str(notes)) + print(indent+indent+"# midichan "+str(melodicMidiChan)+" + n (-24),") melodicMidiChan = melodicMidiChan + 1 + + +#### generate pads/chords +# method: grab some of the notes from the last melodic/bassline +# and build some chords with them that go with the bassline +# sort of like the reverb trick, to create a pad from any melodic material + +# after we're done making the bassline, the melodicMidiChan will be +# the last midi channel +1, so we can use that for the pad channel +padMidiChan = melodicMidiChan +for i in range(padCount): + print(indent+indent+"------------ pad -------") + + # the goal: + # n "[3,12,20,34]" + # where all those numbers are played by the bassline + + # get the numbers from the bassline + # it's already stored in the variable "notes" + # but with brackets like this: [{23 6 5 23}%16] + # we'll use regex to remove the extra stuff + padNotes = notes.replace('[{','') + padNotes = padNotes.replace(']','') + padNotes = padNotes.replace('}','') + padNotes = padNotes.replace('"','') + padNotes = padNotes.replace('%16','') + padNotes = padNotes.replace('n ','') + padNotes = padNotes.rstrip() + + + #print("notes:"+str(padNotes)) + + # now that we have the notes, as strings, put them into a list + padNotesList = padNotes.split(" ") + # convert this list to a dictionary so there aren't duplicates + # https://www.w3schools.com/python/python_howto_remove_duplicates.asp + padNotes = list(dict.fromkeys(padNotes)) + + #print(str(padNotesList)) + + # make a chord + chord = "\"["+random.choice(padNotesList)+","+random.choice(padNotesList)+","+random.choice(padNotesList)+","+random.choice(padNotesList)+"]\"" + + + # build the track + print(indent+indent+"degradeBy 0 ") + print(indent+indent+"$ n "+chord) + print(indent+indent+"# midichan "+str(padMidiChan)+" + n (0)") + +## put the finishing touches on the beautiful stack print(indent+indent+"] # sunvox") diff --git a/deleteme.tidal b/deleteme.tidal index 428a644..d4890e9 100644 --- a/deleteme.tidal +++ b/deleteme.tidal @@ -1,7 +1,7 @@ --- raw input: 268982558670766555 --- hex seed: 0x3BB9E364458C1DB --- decimal seed: 268982558670766555 --- generated at 1633539511.09 +-- raw input: -1654319963628270068 +-- hex seed: -0x16F5533DF0E671F4 +-- decimal seed: -1654319963628270068 +-- generated at 1633644744.38 -- Auto-Techno Generator v0.1 do @@ -10,65 +10,107 @@ do kickM = "[1111]" hhM = "[1111]" sdM = "[1111]" - cpM = "[1111]" ohhM = "[1111]" + rimM = "[1111]" + cpM = "[1111]" + tomM = "[1111]" rideM = "[1111]" cymbalM = "[1111]" p "techno" -- fills - $ whenmod 32 30 (rev) - $ whenmod 32 31 (stut 2 1 "0.125") - $ whenmod 32 30 (scramble 16) - $ whenmod 16 15 (rev) - $ whenmod 64 62 (rev) + $ whenmod 32 31 (rev) + $ whenmod 16 15 (scramble 8) + $ whenmod 16 15 (bite 4 "{4}%4") + $ whenmod 8 7 (bite 2 "{8}%2") + $ whenmod 32 31 (rev) + $ whenmod 32 30 (bite 2 "{8}%2") + $ whenmod 32 31 (rev) $ stack [ -- four to the floor degradeBy fourToTheFloor $ struct "[t*4]" $ n "0" # midichan 0, -------------- kick --------------- mask kickM - $ every 6 ((0.125 ~>)) - $ (0.125 ~>) - $ struct "<[{0101011}%16]>" - $ n 1 # midichan 0, + $ every 9 ((0.125 ~>)) + $ every 9 ((0.125 ~>)) + $ struct "<[{101000010}%16]>" + $ n 5 + # gain "{0.85 1 0.85}%16" + # midichan 0, -------------- hh --------------- mask hhM - $ every 16 (scramble 8) - $ every 15 ((0.125 ~>)) - $ struct "<[1*16]>" - $ n 3 # midichan 1, + $ degradeBy "<0 1 0 0 0 0 0 0>/8" + $ struct "<[t*4]>" + $ n 3 + # gain "{0.9 0.85 0.9}%16" + # midichan 1, -------------- sd --------------- mask sdM - $ every 15 (stut 5 1 "0.0625") + $ degradeBy "<0 0 0 0 0 1 1 0>/8" + $ every 11 ((0.125 ~>)) + $ every 12 ((0.125 ~>)) + $ every 8 (scramble 8) + $ struct "<[t(7,16)]>" + $ n 6 + # gain "{0.85 0.9 1}%16" + # midichan 2, + -------------- ohh --------------- + mask ohhM + $ degradeBy "<0 0 0 0 0 1 0 0>/8" + $ every 6 ((0.125 ~>)) + $ struct "<[{0000}%16] [{00001}%16]>" + $ n 4 + # gain "{0.85 0.85 0.85 0.9 0.85 1 1 0.9 1 1 1}%16" + # midichan 3, + -------------- rim --------------- + mask rimM + $ degradeBy "<0 1 0 1 0 1 0 0>/8" + $ every 14 ((0.125 ~>)) + $ every 5 (scramble 8) $ (0.125 ~>) - $ struct "<[t(15,16)]>" - $ n 2 # midichan 2, + $ struct "<[{111010}%16]>" + $ n 1 + # gain "{0.9 1 0.85}%16" + # midichan 4, -------------- cp --------------- mask cpM - $ degradeBy "[0 0 1]/8" - $ struct "<[{0000100}%16]>" - $ n 3 # midichan 3, - -------------- ohh --------------- - mask ohhM - $ struct "<[tt*2]*4 [[0001]]*4>" - $ n 0 # midichan 4, + $ every 3 ((0.125 ~>)) + $ every 11 (scramble 8) + $ every 10 (stut 1 1 "0.0625") + $ (0.125 ~>) + $ struct "<[{0000}%16] [{10010010010}%16]>" + $ n 2 + # gain "{0.9 0.9 0.9 1 0.9 0.9 1}%16" + # midichan 5, + -------------- tom --------------- + mask tomM + $ every 12 (stut 1 1 "0.0625") + $ (0.1875 ~>) + $ struct "<[{10011100}%16] [t(11,16)]>" + $ n 0 + # gain "{0.9 0.85 0.85 0.9 0.9 1 1 0.85 0.9 1 1 0.85 0.9 0.85}%16" + # midichan 6, -------------- ride --------------- mask rideM - $ every 10 (scramble 8) - $ every 5 (rev) - $ every 13 (scramble 8) - $ (0.1875 ~>) - $ struct "<[t(4,16)]>" - $ n 0 # midichan 5, + $ degradeBy "<0 0 0 0 0 0 0 0>/8" + $ every 15 (stut 1 1 "0.0625") + $ struct "<[t(10,16)]>" + $ n 7 + # gain "{0.9 1 0.9 0.85 0.85 0.9 0.85 1 0.9 0.9 1 0.9 0.9}%16" + # midichan 7, -------------- cymbal --------------- mask cymbalM - $ every 8 (rev) - $ every 7 (rev) - $ every 12 (stut 5 1 "0.0625") - $ struct "<[{10011000}%16]>" - $ n 3 # midichan 6, - ----------------------- + $ degradeBy "<1 0 0 0 0 0 0 1>/8" + $ struct "<[{1011010}%16]>" + $ n 5 + # gain "{0.85 1 0.9 0.85 0.9 0.85 1 0.9}%16" + # midichan 8, + ------------ bassline ------- + degradeBy 0 + $ struct "[{00111110101011110000}%16]" + $ n "[{4 11 0 22 3 10 10 3 11 1 5 10 5 10 4 4 }%16]" + # midichan 10 + n (-24), + ------------ pad ------- degradeBy 0 - $ struct "[{0010100000000000}%16]" - $ n "[{18 6 8 7 19 35 8 24 11 7 7 8 3 23 8 33 }%16]" - # midichan 7 + n (-42) + $ n "[1,4,3,3]" + # midichan 11 + n (0) ] # sunvox diff --git a/sunvox-techno.sunvox b/sunvox-techno.sunvox index 7fb1f25..8c43c41 100644 Binary files a/sunvox-techno.sunvox and b/sunvox-techno.sunvox differ diff --git a/sunvox_config.ini b/sunvox_config.ini new file mode 100644 index 0000000..01333a8 --- /dev/null +++ b/sunvox_config.ini @@ -0,0 +1,9 @@ +midi_kbd "Midi Through Port-0" +midi_kbd_ch 1 +audiodriver jack +hdiv1_y 0 +hdiv2_y 805 +vdiv1_x 481 +width 1554 +height 831 +builtin_theme 17 diff --git a/test.tidal b/test.tidal index 3d99026..5658a36 100644 --- a/test.tidal +++ b/test.tidal @@ -18,7 +18,8 @@ some seeds I've liked: 1014891741240181557 -- pretty funky 850750894594826640 -- great if the kick is t(3,8) instead of t(3,4) 2195224328517994763 -- funnnnkkky --- seed: -2138151558308434562 +0x508DFAD3954D1C9 -- cooool +seed: -2138151558308434562 setcps = (18/60/4)