|
|
|
@ -1,4 +1,8 @@
@@ -1,4 +1,8 @@
|
|
|
|
|
#!/usr/bin/env python3 |
|
|
|
|
scriptName = "Auto-Techno Generator" |
|
|
|
|
version = 0.1 |
|
|
|
|
author = "Chris Beckstrom" |
|
|
|
|
author_email = "chris@chrisbeckstrom.com" |
|
|
|
|
# a deterministic techno generator for tidalcycles |
|
|
|
|
# input an integer value as a seed |
|
|
|
|
# and it spits out some techno ready to play |
|
|
|
@ -27,7 +31,8 @@ stackName = "techno"
@@ -27,7 +31,8 @@ stackName = "techno"
|
|
|
|
|
####### PERCUSSION ############################## |
|
|
|
|
# for euclidean rhythms, define the choices for the 2nd number |
|
|
|
|
# eg "[t(3,NN)]" |
|
|
|
|
dividers = [4,8,16] |
|
|
|
|
#dividers = [4,8,16] |
|
|
|
|
dividers = [16] |
|
|
|
|
# how many percussive/rhythmic elements to generate? |
|
|
|
|
percCount = 5 |
|
|
|
|
percMidiChanStart = 0 |
|
|
|
@ -38,7 +43,8 @@ percMidiChanStart = 0
@@ -38,7 +43,8 @@ percMidiChanStart = 0
|
|
|
|
|
# set some notes for percussion tracks |
|
|
|
|
percNotes = [0,1,2,3] |
|
|
|
|
# perc track names |
|
|
|
|
percNames = ["kick", "hh", "sd", "cp", "five"] |
|
|
|
|
# a percussion track will be generated for each item in this list |
|
|
|
|
percNames = ["kick", "hh", "sd", "cp", "ohh","ride","cymbal"] |
|
|
|
|
# for use generating sequences of 0's and 1's |
|
|
|
|
# |
|
|
|
|
# |
|
|
|
@ -81,19 +87,50 @@ import time
@@ -81,19 +87,50 @@ import time
|
|
|
|
|
################################################## |
|
|
|
|
# this is the 1st argument given via command line |
|
|
|
|
# this is used to calculate everything |
|
|
|
|
# seeds should be 6 digits or longer |
|
|
|
|
input = sys.argv[1] |
|
|
|
|
|
|
|
|
|
rawinput = sys.argv[1] |
|
|
|
|
|
|
|
|
|
# let's assume it's a hex value |
|
|
|
|
# convert to decimal |
|
|
|
|
#input = int(hex,int(89)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
################ SEED HEX ##################### |
|
|
|
|
# make sure the seed is a hex value |
|
|
|
|
# why? it doesn't really matter technically |
|
|
|
|
# I just think it would be cool |
|
|
|
|
# plus, hex values for long numbers are shorter |
|
|
|
|
# and easier to read/type |
|
|
|
|
# and might be good track names |
|
|
|
|
# TODO - this is in progress |
|
|
|
|
# if the input is a hex value, we'll know because it starts with 0x |
|
|
|
|
if (rawinput.startswith("0x")): |
|
|
|
|
# the input is a hex number |
|
|
|
|
#print("string starts with 0x") |
|
|
|
|
seed = rawinput.upper() |
|
|
|
|
else: |
|
|
|
|
# the input is NOT a hex number |
|
|
|
|
# convert to a hex number |
|
|
|
|
seed = hex(int(rawinput)).upper() |
|
|
|
|
|
|
|
|
|
# by now the seed should start with 0X |
|
|
|
|
# let's change that X to lowercase |
|
|
|
|
seed = seed.replace("X","x") |
|
|
|
|
#print("seed: "+seed) |
|
|
|
|
|
|
|
|
|
decimalSeed = int(seed,16) |
|
|
|
|
|
|
|
|
|
### FRONTMATTER ### |
|
|
|
|
print("-- seed: " + str(input)) |
|
|
|
|
print("-- raw input: " + str(rawinput)) |
|
|
|
|
print("-- hex seed: "+seed) |
|
|
|
|
print("-- decimal seed: "+str(decimalSeed)) |
|
|
|
|
print("-- generated at "+str(time.time())) |
|
|
|
|
print("-- "+scriptName+" v"+str(version)) |
|
|
|
|
print("") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########### THIS CHANGES EVERYTHING! ############# |
|
|
|
|
# set the random seed based on user input |
|
|
|
|
random.seed(input) |
|
|
|
|
random.seed(decimalSeed) |
|
|
|
|
################################################## |
|
|
|
|
|
|
|
|
|
indent = " " |
|
|
|
@ -159,7 +196,8 @@ def genOnOff(limit,busyness,struct):
@@ -159,7 +196,8 @@ def genOnOff(limit,busyness,struct):
|
|
|
|
|
# ['12', '34', '56', '78', '9'] |
|
|
|
|
#zeroesOnes = re.findall('.{1,2}', zeroesOnes) |
|
|
|
|
if (struct == 1): |
|
|
|
|
zeroesOnes = "struct \"{"+str(zeroesOnes)+"}%16\"" |
|
|
|
|
#zeroesOnes = "struct \"{"+str(zeroesOnes)+"}%16\"" |
|
|
|
|
zeroesOnes = "[{"+str(zeroesOnes)+"}%16]" |
|
|
|
|
return(zeroesOnes) |
|
|
|
|
else: |
|
|
|
|
return(zeroesOnes) |
|
|
|
@ -194,7 +232,8 @@ def euclid(divider):
@@ -194,7 +232,8 @@ def euclid(divider):
|
|
|
|
|
secondEuclid = "<"+str(random.choice(dividers))+" "+str(random.choice(dividers))+">" |
|
|
|
|
# [t(<3 8>,16)] |
|
|
|
|
|
|
|
|
|
return ("struct \"[t("+str(firstEuclid)+","+str(secondEuclid)+")]\"") |
|
|
|
|
#return ("struct \"[t("+str(firstEuclid)+","+str(secondEuclid)+")]\"") |
|
|
|
|
return ("[t("+str(firstEuclid)+","+str(secondEuclid)+")]") |
|
|
|
|
|
|
|
|
|
def genNotes(melodicOneBar): |
|
|
|
|
""" |
|
|
|
@ -212,7 +251,7 @@ def genNotes(melodicOneBar):
@@ -212,7 +251,7 @@ def genNotes(melodicOneBar):
|
|
|
|
|
melody = "" |
|
|
|
|
for i in range(melodyLength): |
|
|
|
|
melody = str(random.randrange(0,random.choice(noteRange))) + " " + str(melody) |
|
|
|
|
return ("n "+"\"{"+melody+"}%"+str(random.choice(melodicDivisions))+"\"") |
|
|
|
|
return ("n "+"\"[{"+melody+"}%"+str(random.choice(melodicDivisions))+"]\"") |
|
|
|
|
|
|
|
|
|
def offset(): |
|
|
|
|
amounts = ["1/4","1/8","1/16","1/16","1/16"] |
|
|
|
@ -239,7 +278,7 @@ def stut():
@@ -239,7 +278,7 @@ def stut():
|
|
|
|
|
""" |
|
|
|
|
# choose how many repeats |
|
|
|
|
repeats = random.choice([1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,5]) |
|
|
|
|
time = random.choice(["1/4","1/8","1/16","1/16","1/16"]) |
|
|
|
|
time = random.choice([0.125,0.125,0.125,0.0625,0.0625]) |
|
|
|
|
return("stut "+str(repeats)+" 1 \""+str(time)+"\"") |
|
|
|
|
|
|
|
|
|
def bite(): |
|
|
|
@ -253,7 +292,7 @@ def bite():
@@ -253,7 +292,7 @@ def bite():
|
|
|
|
|
# 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,16)) |
|
|
|
|
length = int(random.randint(4,8)) |
|
|
|
|
bit = "" |
|
|
|
|
for i in range(length): |
|
|
|
|
bit = str(random.randint(0,int(number)))+"*"+str(random.choice(repeatChoices))+" "+bit |
|
|
|
@ -280,22 +319,21 @@ def fills():
@@ -280,22 +319,21 @@ def fills():
|
|
|
|
|
fills = "" |
|
|
|
|
for i in range(1,10): |
|
|
|
|
# roll the dice |
|
|
|
|
whenmods = ["8 7","4 3","16 15","16 14","32 31"] |
|
|
|
|
whenmods = ["8 7","16 15","16 14","32 31","64 62","64 63","32 30"] |
|
|
|
|
fills = str(random.choice(fillChoices))+" "+fills |
|
|
|
|
return "whenmod "+str(random.choice(whenmods))+" ("+fills.rstrip()+")" |
|
|
|
|
|
|
|
|
|
########################### LISTS ########################## |
|
|
|
|
|
|
|
|
|
# a collection of typical-ish kick drum patterns |
|
|
|
|
bdPats= ["[t*4]","[t t t t*2]","[t t t t*2?]","t(3,8)","[t~~~ ~~<~t~~>~ ~~t~ ~<~t>~~]"] |
|
|
|
|
hhPats= ["[t*16]","[t t*2]*4", "[~t]*4","[t*16]"] |
|
|
|
|
sdPats= ["[~ t]*2","[t(3,8)]","[[~~~t][~t]]*2"] |
|
|
|
|
cpPats= ["[~ t]*2","[t(3,8)]","[[~~~t][~t]]*2"] |
|
|
|
|
cyPats = ["[t*4]","[t*8]","[t~~~]","[t~~[~~~t]]","[<t~~~>~~~]"] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bdPats=["[t*4]","[tttt*2]","[tttt*2?]","t(3,8)","[t~~~~~<~t~~>~~~t~~<~t>~~]"] |
|
|
|
|
hhPats=["[t*16]","[tt*2]*4","[~t]*4","[t*16]"] |
|
|
|
|
sdPats=["[~t]*2","[t(3,8)]","[[~~~t][~t]]*2"] |
|
|
|
|
cpPats=["[~t]*2","[t(3,8)]","[[~~~t][~t]]*2"] |
|
|
|
|
cyPats=["[t*4]","[t*8]","[t~~~]","[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"] |
|
|
|
|
|
|
|
|
|
############### It's assemble time!! ################### |
|
|
|
|
|
|
|
|
@ -314,7 +352,7 @@ for i in percNames:
@@ -314,7 +352,7 @@ for i in percNames:
|
|
|
|
|
print(indent+"p \""+stackName+"\" ") |
|
|
|
|
|
|
|
|
|
# stack stuff like bite, scramble, etc. will go here |
|
|
|
|
|
|
|
|
|
print(indent+indent+"-- fills") |
|
|
|
|
# roll the dice up to N times |
|
|
|
|
for i in range(1,8): |
|
|
|
|
if (random.randint(0,10) > 4): |
|
|
|
@ -323,7 +361,8 @@ for i in range(1,8):
@@ -323,7 +361,8 @@ for i in range(1,8):
|
|
|
|
|
# how many "fun" things to add? |
|
|
|
|
# a list of things to do to patterns to make them more interesting |
|
|
|
|
# these happen BEFORE struct, note, etc. |
|
|
|
|
interestingThings = [stut(),bite(),"rev",scramble(),offset()] |
|
|
|
|
#interestingThings = [stut(),bite(),"rev",scramble(),offset()] |
|
|
|
|
interestingThings = [stut(),"rev",scramble(),offset()] |
|
|
|
|
def funThings(): |
|
|
|
|
wholeEnchilada = "" |
|
|
|
|
# add between 1 and 4 "fun" things |
|
|
|
@ -335,25 +374,18 @@ def funThings():
@@ -335,25 +374,18 @@ def funThings():
|
|
|
|
|
# for each iteration, choose an interestingThing |
|
|
|
|
# indent + "every N" + ( rev ) |
|
|
|
|
wholeEnchilada = str(every()+"("+str(random.choice(interestingThings))+")\n"+wholeEnchilada).rstrip() |
|
|
|
|
|
|
|
|
|
# using "every" |
|
|
|
|
#return (every()+"("+bite()+")") |
|
|
|
|
return wholeEnchilada |
|
|
|
|
|
|
|
|
|
# uncomment this for some wildness |
|
|
|
|
#funThings() |
|
|
|
|
|
|
|
|
|
# the top of the stack |
|
|
|
|
print(indent+indent+"$ stack [") |
|
|
|
|
|
|
|
|
|
# this is an empty track to make dealing with commas easier |
|
|
|
|
# just 4 to the floor in case you want it |
|
|
|
|
print(indent+indent+"-- four to the floor") |
|
|
|
|
print(indent+indent+"(#gain 0) $ struct \"[t*4]\" $ n \"0\" # midichan 0,") |
|
|
|
|
|
|
|
|
|
#print(",struct \"[t*4]\" $ n 0 # midichan 0") |
|
|
|
|
print(indent+indent+"(#gain 1) $ struct \"[t*4]\" $ n \"0\" # midichan 0,") |
|
|
|
|
|
|
|
|
|
# generate rhythm tracks |
|
|
|
|
# set the starting midi channel |
|
|
|
|
percMidiChan = 0 |
|
|
|
|
|
|
|
|
|
######################################################## |
|
|
|
@ -362,74 +394,93 @@ percMidiChan = 0
@@ -362,74 +394,93 @@ percMidiChan = 0
|
|
|
|
|
## will be created |
|
|
|
|
for i in percNames: |
|
|
|
|
|
|
|
|
|
print(indent+indent+"-- "+percNames[percMidiChan]+" -----------------") |
|
|
|
|
|
|
|
|
|
# print the name of the track (from the percNames list) |
|
|
|
|
print(indent+indent+"-------------- "+i+" ---------------") |
|
|
|
|
|
|
|
|
|
# this is here so there is the same thing at the start of |
|
|
|
|
# each new track (so we know "whenmod" for instance always |
|
|
|
|
# starts with $ and not ,) |
|
|
|
|
#print(indent+indent+"degradeBy 0 ") |
|
|
|
|
# MASK: make this so once the entire thing is generated, it's easy to |
|
|
|
|
# alter how dense the pattern is, by altering "let nnM = "[1111] |
|
|
|
|
print(indent+indent+"mask "+i+"M") |
|
|
|
|
|
|
|
|
|
#### FUN STUFF ##### |
|
|
|
|
#### VARIATIONS ##### |
|
|
|
|
# insert some funStuff, maybe |
|
|
|
|
if (random.randint(0,10) > 8): |
|
|
|
|
if (random.randint(0,10) > 3): |
|
|
|
|
print(funThings()) |
|
|
|
|
|
|
|
|
|
#### OFFSET #### |
|
|
|
|
#### OFFSET ### |
|
|
|
|
# offset the pattern by a certain amount |
|
|
|
|
# this can make things much funkier |
|
|
|
|
if (random.randint(0,10) > 6): |
|
|
|
|
print(indent+indent+"$ "+offset()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
##### THE RHYTHMS ##### |
|
|
|
|
# choose between random, hardcoded, or euclidean rhythms |
|
|
|
|
rChoice = random.choice([0,1,2]) |
|
|
|
|
rhythm = "" |
|
|
|
|
if (rChoice == 0): |
|
|
|
|
# if choice = 0, choose hardcoded rhythms |
|
|
|
|
# if this is the first iteration, ie "kick" |
|
|
|
|
# choose from some hard-coded kick rhythms |
|
|
|
|
# 2nd? same for hh |
|
|
|
|
# 3rd? same for sd |
|
|
|
|
if (i == 0): |
|
|
|
|
rhythm = str(random.choice(bdPats)) |
|
|
|
|
elif (i == 1): |
|
|
|
|
rhythm = str(random.choice(hhPats)) |
|
|
|
|
elif (i == 2): |
|
|
|
|
rhythm = str(random.choice(sdPats)) |
|
|
|
|
elif (i == 3): |
|
|
|
|
rhythm = str(random.choice(cpPats)) |
|
|
|
|
elif (i == 4): |
|
|
|
|
rhythm = str(random.choice(cyPats)) |
|
|
|
|
else: |
|
|
|
|
rhythm = "t*4" |
|
|
|
|
rhythm = "struct \""+rhythm+"\"" |
|
|
|
|
|
|
|
|
|
elif (rChoice == 1): |
|
|
|
|
# GENERATE A SERIES OF 0's and 1's |
|
|
|
|
# choose how long the rhythm will be |
|
|
|
|
if (oneBar == 1): |
|
|
|
|
percLength = 16 |
|
|
|
|
##### THE RHYTHMS ##### |
|
|
|
|
# how many bars to create? |
|
|
|
|
rhythmLengthChoices = [1,1,1,2] |
|
|
|
|
rhythmLength = random.choice(rhythmLengthChoices) |
|
|
|
|
|
|
|
|
|
# for the number of bars, do this: |
|
|
|
|
fullRhythm = "" |
|
|
|
|
for i in range(rhythmLength): |
|
|
|
|
|
|
|
|
|
# choose between random, type-specific (bd,hh,etc), |
|
|
|
|
# rhythm library, or euclidean rhythms |
|
|
|
|
rChoice = random.choice([0,1,2,3]) |
|
|
|
|
rhythm = "" # placeholder for the rhythm |
|
|
|
|
if (rChoice == 0): |
|
|
|
|
# if choice = 0, choose hardcoded rhythms |
|
|
|
|
# if this is the first iteration, ie "kick" |
|
|
|
|
# choose from some hard-coded kick rhythms |
|
|
|
|
# 2nd? same for hh |
|
|
|
|
# 3rd? same for sd |
|
|
|
|
if (i == 0): |
|
|
|
|
rhythm = str(random.choice(bdPats)) |
|
|
|
|
elif (i == 1): |
|
|
|
|
rhythm = str(random.choice(hhPats)) |
|
|
|
|
elif (i == 2): |
|
|
|
|
rhythm = str(random.choice(sdPats)) |
|
|
|
|
elif (i == 3): |
|
|
|
|
rhythm = str(random.choice(cpPats)) |
|
|
|
|
elif (i == 4): |
|
|
|
|
rhythm = str(random.choice(cyPats)) |
|
|
|
|
else: |
|
|
|
|
# print("LIBRARY") |
|
|
|
|
rhythm = str(random.choice(rhythmLibrary)) |
|
|
|
|
#rhythm = "struct \""+rhythm+"\"" |
|
|
|
|
|
|
|
|
|
elif (rChoice == 1): |
|
|
|
|
# GENERATE A SERIES OF 0's and 1's |
|
|
|
|
# choose how long the rhythm will be |
|
|
|
|
# if (oneBar == 1): |
|
|
|
|
# percLength = 16 |
|
|
|
|
# else: |
|
|
|
|
# percLength = int(random.randrange(4,8)) |
|
|
|
|
percLength = int(random.randrange(4,10)) |
|
|
|
|
#percSequence = genOnOff(percLength) |
|
|
|
|
#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) |
|
|
|
|
generateBinary(busyness) |
|
|
|
|
rhythm = str(genOnOff(percLength,busyness,1)) |
|
|
|
|
|
|
|
|
|
elif (rChoice == 2): |
|
|
|
|
# GENERATE EUCLIDEAN RHYTHMS |
|
|
|
|
rhythm = str(euclid(random.choice(dividers))) |
|
|
|
|
#print(indent+indent+euclid(random.choice(dividers))) |
|
|
|
|
# |
|
|
|
|
else: |
|
|
|
|
percLength = int(random.randrange(4,16)) |
|
|
|
|
#percSequence = genOnOff(percLength) |
|
|
|
|
#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) |
|
|
|
|
generateBinary(busyness) |
|
|
|
|
rhythm = str(genOnOff(percLength,busyness,1)) |
|
|
|
|
|
|
|
|
|
elif (rChoice == 2): |
|
|
|
|
# GENERATE EUCLIDEAN RHYTHMS |
|
|
|
|
rhythm = str(euclid(random.choice(dividers))) |
|
|
|
|
#print(indent+indent+euclid(random.choice(dividers))) |
|
|
|
|
# |
|
|
|
|
else: |
|
|
|
|
rhythm = "struct [t*4]" |
|
|
|
|
# print("LIBRARY") |
|
|
|
|
rhythm = str(random.choice(rhythmLibrary)) |
|
|
|
|
#rhythm = "struct \""+rhythm+"\"" |
|
|
|
|
|
|
|
|
|
fullRhythm = str(rhythm) + " " + fullRhythm |
|
|
|
|
# remove the trailing space |
|
|
|
|
fullRhythm = fullRhythm.rstrip() |
|
|
|
|
|
|
|
|
|
print(indent+indent+"$ "+rhythm) |
|
|
|
|
# add "struct" to the beginning |
|
|
|
|
# and enclose in < > |
|
|
|
|
print(indent+indent+"$ struct \"<"+fullRhythm+">\"") |
|
|
|
|
print (indent+indent+"$ n "+str(random.choice(percNotes))+" # midichan "+str(percMidiChan)+",") |
|
|
|
|
percMidiChan = percMidiChan + 1 |
|
|
|
|
|
|
|
|
@ -442,12 +493,12 @@ for i in range(melodyCount):
@@ -442,12 +493,12 @@ for i in range(melodyCount):
|
|
|
|
|
#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+"$ "+genOnOff(16,(busyness),1)) |
|
|
|
|
print(indent+indent+"$ struct \""+genOnOff(16,(busyness),1)+"\"") |
|
|
|
|
print(indent+indent+"$ "+genNotes(1)) |
|
|
|
|
else: |
|
|
|
|
genOnOff(32,(busyness)) |
|
|
|
|
print(indent+indent+"$ "+genNotes(1)) |
|
|
|
|
print(indent+indent+"# midichan "+str(melodicMidiChan)+" + n (-36)") |
|
|
|
|
print(indent+indent+"$ struct \""+genNotes(1))+"\"" |
|
|
|
|
print(indent+indent+"# midichan "+str(melodicMidiChan)+" + n (-42)") |
|
|
|
|
melodicMidiChan = melodicMidiChan + 1 |
|
|
|
|
|
|
|
|
|
print(indent+indent+"] # sunvox") |
|
|
|
|