|
|
|
@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
|
|
|
|
|
#!/usr/bin/env python3 |
|
|
|
|
scriptName = "Auto-Techno Generator" |
|
|
|
|
version = 0.1 |
|
|
|
|
version = "0.1.1" |
|
|
|
|
author = "Chris Beckstrom" |
|
|
|
|
author_email = "chris@chrisbeckstrom.com" |
|
|
|
|
# a deterministic techno generator for tidalcycles |
|
|
|
@ -20,6 +20,12 @@ author_email = "chris@chrisbeckstrom.com"
@@ -20,6 +20,12 @@ author_email = "chris@chrisbeckstrom.com"
|
|
|
|
|
lowestBpm = 92 |
|
|
|
|
highestBpm = 135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# swing |
|
|
|
|
# see https://tidalcycles.org/docs/patternlib/tour/time/#swing |
|
|
|
|
minSwing = 0 |
|
|
|
|
maxSwing = 0.2 |
|
|
|
|
|
|
|
|
|
# force everything to happen within 1 bar |
|
|
|
|
oneBar = 1 |
|
|
|
|
|
|
|
|
@ -29,27 +35,27 @@ stackName = "techno"
@@ -29,27 +35,27 @@ stackName = "techno"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
####### PERCUSSION ############################## |
|
|
|
|
# some settings for rhythm generation |
|
|
|
|
# for euclidean rhythms, define the choices for the 2nd number |
|
|
|
|
# eg "[t(3,NN)]" |
|
|
|
|
#dividers = [4,8,16] |
|
|
|
|
dividers = [16] |
|
|
|
|
# how many percussive/rhythmic elements to generate? |
|
|
|
|
percCount = 5 |
|
|
|
|
percMidiChanStart = 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# perc notes |
|
|
|
|
# set some notes for percussion tracks |
|
|
|
|
# these notes correspond to notes (C3, C#3, etc) |
|
|
|
|
percNotes = [0,1,2,3,4,5,6,7] |
|
|
|
|
percCount = 5 # this setting doesn't do anything right now, use "percNames" instead |
|
|
|
|
percMidiChanStart = 0 # the starting MIDI channel for perc tracks |
|
|
|
|
|
|
|
|
|
# perc track names |
|
|
|
|
# a percussion track will be generated for each item in this list |
|
|
|
|
percNames = ["kick", "hh","sd","ohh","rim","cp","tom","ride","cymbal"] |
|
|
|
|
# for use generating sequences of 0's and 1's |
|
|
|
|
# |
|
|
|
|
# |
|
|
|
|
# perc notes |
|
|
|
|
# set some notes for percussion tracks |
|
|
|
|
# these notes correspond to notes (C3, C#3, etc) |
|
|
|
|
# each percussion track will pick one of these notes |
|
|
|
|
# and send note on/off messages to that note |
|
|
|
|
percNotes = [0,1,2,3,4,5,6,7] |
|
|
|
|
|
|
|
|
|
# |
|
|
|
|
####### MELODY ################################# |
|
|
|
|
# NOTE about melodic sequences: |
|
|
|
@ -78,14 +84,32 @@ melodyCount = 1
@@ -78,14 +84,32 @@ melodyCount = 1
|
|
|
|
|
melodyMidiChanStart = 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############### PADS ########################### |
|
|
|
|
# how many pad tracks to generate |
|
|
|
|
padCount = 1 |
|
|
|
|
|
|
|
|
|
# how long should the pad pattern be? |
|
|
|
|
# tip: add more of the same number to weight that choice |
|
|
|
|
padBars = [1,1,1,2,4] |
|
|
|
|
# at what speed should the pad chord change? |
|
|
|
|
# every bar = 1, every 2 bars = 2, etc |
|
|
|
|
padSpeed = [1,2,2,2,4] |
|
|
|
|
|
|
|
|
|
# what MIDI channel should it start on? |
|
|
|
|
# padMidiChanStart = melodyMidiChanStart + 1 will start it right after the last melody track |
|
|
|
|
padMidiChanStart = melodyMidiChanStart + 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
################ MIDI ##################### |
|
|
|
|
# on what channel should we send MIDI CC messages? |
|
|
|
|
midiCCChannel = 15 |
|
|
|
|
# list of MIDI CC's: |
|
|
|
|
midiCCs = [0,1,2,3,4,5,6] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
################# SETUP ################# |
|
|
|
|
## import stuff |
|
|
|
|
import sys |
|
|
|
|
import random |
|
|
|
@ -96,7 +120,7 @@ import decimal
@@ -96,7 +120,7 @@ import decimal
|
|
|
|
|
from decimal import Decimal |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
################################################## |
|
|
|
|
############## PARSE ARGUMENT(S) ############### |
|
|
|
|
# this is the 1st argument given via command line |
|
|
|
|
# this is used to calculate everything |
|
|
|
|
rawinput = sys.argv[1] |
|
|
|
@ -105,7 +129,6 @@ rawinput = sys.argv[1]
@@ -105,7 +129,6 @@ rawinput = sys.argv[1]
|
|
|
|
|
# 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 |
|
|
|
@ -132,7 +155,7 @@ seed = seed.replace("X","x")
@@ -132,7 +155,7 @@ seed = seed.replace("X","x")
|
|
|
|
|
# here is the seed in decimal format (eg 3892423) |
|
|
|
|
decimalSeed = int(seed,16) |
|
|
|
|
|
|
|
|
|
### FRONTMATTER ### |
|
|
|
|
#################### FRONTMATTER ### |
|
|
|
|
# put some stuff at the top of the tidal code |
|
|
|
|
print("-- raw input: " + str(rawinput)) |
|
|
|
|
print("-- hex seed: "+seed) |
|
|
|
@ -148,6 +171,7 @@ random.seed(decimalSeed)
@@ -148,6 +171,7 @@ random.seed(decimalSeed)
|
|
|
|
|
################################################## |
|
|
|
|
|
|
|
|
|
indent = " " |
|
|
|
|
|
|
|
|
|
# generate a bpm between lowest and highest |
|
|
|
|
# looks like: cps = (BPM/60/4) |
|
|
|
|
bpm = "setcps ("+str(random.randrange(lowestBpm, highestBpm, 1))+"/60/4)" |
|
|
|
@ -156,6 +180,20 @@ bpm = "setcps ("+str(random.randrange(lowestBpm, highestBpm, 1))+"/60/4)"
@@ -156,6 +180,20 @@ bpm = "setcps ("+str(random.randrange(lowestBpm, highestBpm, 1))+"/60/4)"
|
|
|
|
|
|
|
|
|
|
####################### FUNCTIONS ################### |
|
|
|
|
|
|
|
|
|
def swing(minSwing,maxSwing): |
|
|
|
|
""" |
|
|
|
|
Generate a swing value for the entire stack |
|
|
|
|
""" |
|
|
|
|
# the goal is something like this: |
|
|
|
|
# swingBy 0.2 8 |
|
|
|
|
#print("We will swing!!") |
|
|
|
|
# choose the first value |
|
|
|
|
#firstSwing = (decimal.Decimal(random.range())).quantize(Decimal('.001')) |
|
|
|
|
firstSwing = str(decimal.Decimal(random.randrange(0, 20))/100) |
|
|
|
|
secondSwing = 8 # if 8: swing 16th notes, if 4: swing 8th notes |
|
|
|
|
return "swingBy "+str(firstSwing)+" "+str(secondSwing) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generateBinary(busyness): |
|
|
|
|
""" |
|
|
|
|
busy-ness (sic) |
|
|
|
@ -220,6 +258,8 @@ def genOnOff(limit,busyness,struct):
@@ -220,6 +258,8 @@ def genOnOff(limit,busyness,struct):
|
|
|
|
|
def genVelocity(limit): |
|
|
|
|
""" |
|
|
|
|
Generate a series of decimals < 1 to control velocity |
|
|
|
|
This adds a more organic feel to the rhythms, and makes |
|
|
|
|
them about 20% funkier |
|
|
|
|
""" |
|
|
|
|
velocities = "" |
|
|
|
|
for i in range(limit): |
|
|
|
@ -441,6 +481,11 @@ for i in percNames:
@@ -441,6 +481,11 @@ for i in percNames:
|
|
|
|
|
# actually assemble the stack! |
|
|
|
|
print(indent+"p \""+stackName+"\" ") |
|
|
|
|
|
|
|
|
|
# swing? |
|
|
|
|
# roll the dice to see if there will be any swing at all |
|
|
|
|
if (random.randint(0,10) > 7): |
|
|
|
|
print(indent+indent+"$ "+str(swing(minSwing,maxSwing))) |
|
|
|
|
|
|
|
|
|
# stack stuff like bite, scramble, etc. will go here |
|
|
|
|
print(indent+indent+"-- fills") |
|
|
|
|
# roll the dice up to N times |
|
|
|
@ -494,8 +539,8 @@ for i in percNames:
@@ -494,8 +539,8 @@ for i in percNames:
|
|
|
|
|
print(indent+indent+"mask "+i+"M") |
|
|
|
|
|
|
|
|
|
# sometimes be quiet |
|
|
|
|
if (i != "kick"): |
|
|
|
|
sometimesBeQuiet() |
|
|
|
|
#if (i != "kick"): |
|
|
|
|
# sometimesBeQuiet() |
|
|
|
|
|
|
|
|
|
#### VARIATIONS ##### |
|
|
|
|
# insert some funStuff, maybe |
|
|
|
@ -680,13 +725,126 @@ for i in range(padCount):
@@ -680,13 +725,126 @@ for i in range(padCount):
|
|
|
|
|
#print(str(padNotesList)) |
|
|
|
|
|
|
|
|
|
# make a chord |
|
|
|
|
chord = "\"["+random.choice(padNotesList)+","+random.choice(padNotesList)+","+random.choice(padNotesList)+","+random.choice(padNotesList)+"]\"" |
|
|
|
|
padsStart = "\"<" |
|
|
|
|
padsEnd = ">" |
|
|
|
|
|
|
|
|
|
# generate a sequence of chords |
|
|
|
|
|
|
|
|
|
pads = "" |
|
|
|
|
for i in range(random.choice(padBars)): |
|
|
|
|
pads = "["+ random.choice(padNotesList)+","+random.choice(padNotesList)+","+random.choice(padNotesList)+","+random.choice(padNotesList)+"]"+str(pads) |
|
|
|
|
|
|
|
|
|
# build the track |
|
|
|
|
print(indent+indent+"degradeBy 0 ") |
|
|
|
|
print(indent+indent+"$ n "+chord) |
|
|
|
|
print(indent+indent+"# midichan "+str(padMidiChan)+" + n (0)") |
|
|
|
|
sometimesBeQuiet() |
|
|
|
|
|
|
|
|
|
# roll the dice, choose between a long steady pad |
|
|
|
|
# or more rhythmic hits |
|
|
|
|
#if (random.choice([1,1]) == 1): |
|
|
|
|
if (random.choice([0,1]) == 1): |
|
|
|
|
print(indent+indent+"$ struct \""+genOnOff(16,(busyness),1)+"\"") |
|
|
|
|
|
|
|
|
|
print(indent+indent+"$ n "+padsStart+pads+padsEnd+"/"+str(random.choice(padSpeed))+"\"") |
|
|
|
|
print(indent+indent+"# midichan "+str(padMidiChan)+" + n (0),") |
|
|
|
|
|
|
|
|
|
############## MIDI CCS ############# |
|
|
|
|
# the goal: |
|
|
|
|
# do reverb/delay throws |
|
|
|
|
# basically create step sequences that modulate CC values |
|
|
|
|
# this assumes that midi cc 0-N are mapped to effects wherever |
|
|
|
|
# the output of this script is being sent |
|
|
|
|
# eg delay sends, reverb sends, etc |
|
|
|
|
|
|
|
|
|
print(indent+indent+"--------- MIDI CC's ---------------") |
|
|
|
|
print(indent+indent+"stack [") |
|
|
|
|
|
|
|
|
|
# the kick is on midi channel 0 |
|
|
|
|
# don't do midi CC's for that track, start at 1 |
|
|
|
|
midiChannel = 1 |
|
|
|
|
|
|
|
|
|
for i in midiCCs: |
|
|
|
|
# generate MIDI ccs rhythmically or over time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# how many steps? |
|
|
|
|
ccLength = random.randint(2,16) |
|
|
|
|
|
|
|
|
|
if (random.randint(0,1) == 0): |
|
|
|
|
# don't do any CC value > 0 |
|
|
|
|
ccFinalPattern = "struct \"t*1\" $ ccn "+str(i)+" # ccv \"{0}%" |
|
|
|
|
ccEndNumber = 1 |
|
|
|
|
|
|
|
|
|
else: |
|
|
|
|
ccPattern = str(genOnOff(ccLength,5,0)) |
|
|
|
|
|
|
|
|
|
ccPattern = "" |
|
|
|
|
for v in range(ccLength): |
|
|
|
|
ccPattern = str(random.randint(0,127))+" "+str(ccPattern) |
|
|
|
|
|
|
|
|
|
# how fast should these change? |
|
|
|
|
ccSpeed = random.choice([0,0,0,0,0,0,0]) |
|
|
|
|
if (ccSpeed == 0): |
|
|
|
|
# slow |
|
|
|
|
ccEndNumber = random.choice([1,2,3,4,5]) |
|
|
|
|
else: |
|
|
|
|
# fast |
|
|
|
|
ccEndNumber = random.choice([8,16]) |
|
|
|
|
|
|
|
|
|
ccPattern = ccPattern.rstrip() |
|
|
|
|
# right now ccPattern looks like this: |
|
|
|
|
# 0 12 78 127 45 60 |
|
|
|
|
|
|
|
|
|
#ccPattern = list(ccPattern) |
|
|
|
|
# |
|
|
|
|
|
|
|
|
|
ccPatStruct = "struct \"[t*"+str(ccEndNumber)+"]\" $ " |
|
|
|
|
|
|
|
|
|
# how likely is it that we'll actually send MIDI cc's for this value? |
|
|
|
|
if (random.randint(0,10) > 4): |
|
|
|
|
ccPatStart = ccPatStruct+"ccn "+str(i)+" # ccv \"{" |
|
|
|
|
else: |
|
|
|
|
#ccPatStart = "-- "+ccPatStruct+"ccn "+str(i)+" # ccv \"{" |
|
|
|
|
ccPatStart = ""+ccPatStruct+"ccn "+str(i)+" # ccv \"{" |
|
|
|
|
|
|
|
|
|
ccPatEnd = "}%" |
|
|
|
|
|
|
|
|
|
ccFinalPattern = ccPatStart+ccPattern+ccPatEnd |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ccs = ccPatStart+ccFinalPattern+ccPatEnd+str(ccEndNumber)+"\" # midichan "+str(midiChannel)+"," |
|
|
|
|
ccs = ccFinalPattern+str(ccEndNumber)+"\"," |
|
|
|
|
|
|
|
|
|
print(indent+indent+indent+str(ccs)) |
|
|
|
|
|
|
|
|
|
midiChannel = midiChannel + 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# end result like |
|
|
|
|
# ccn 0 # ccv "{0 1 0 1 1 0 0 1 0}%N" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# make one more line so we don't have to worry about the trailing comma |
|
|
|
|
# in the last ccs line |
|
|
|
|
|
|
|
|
|
print(indent+indent+indent+"ccn 126 # ccv "+str(random.randint(0,126))+"] # midichan "+str(midiCCChannel)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############### EXTRA STUFF ################# |
|
|
|
|
# sometimes this will generate a great groove |
|
|
|
|
# that could be perfect with one or two extra things, |
|
|
|
|
# like a handclap or extra kick |
|
|
|
|
# This section creates an area to easily add that |
|
|
|
|
# extra something |
|
|
|
|
# hardcoded to midi channel 12 |
|
|
|
|
|
|
|
|
|
print(indent+indent+"-- EXTRA STUFF ---") |
|
|
|
|
print(indent+indent+"stack [") |
|
|
|
|
print(indent+indent+indent+"(# gain 0) $ struct \"[~ t ~ t]\" $ n \"0\",") |
|
|
|
|
print(indent+indent+indent+"(# gain 0) $ struct \"[~t~t~t~t]\" $ n \"1\"] # midichan 12") |
|
|
|
|
|
|
|
|
|
## put the finishing touches on the beautiful stack |
|
|
|
|
## put the finishing touches on the beautiful stack! :-) :-) |
|
|
|
|
print(indent+indent+"] # sunvox") |
|
|
|
|