@ -1,11 +1,11 @@
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
scriptName = " Auto-Techno Generator "
version = " 0.1.1 "
version = " 0.1.2 "
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
# and it spits out some techno ready to play in tidalcycles
#
# use it in emacs like this:
# it will generate a random seed
@ -15,6 +15,15 @@ author_email = "chris@chrisbeckstrom.com"
@@ -15,6 +15,15 @@ author_email = "chris@chrisbeckstrom.com"
# (insert (shell-command-to-string (concat "/usr/bin/python ~/tidal-auto-techno/auto-techno.py " (number-to-string(random))))))
#
##################################################
################# SETUP #################
## import stuff
import sys
import random
import math
import re
import time
import decimal
from decimal import Decimal
########### SETTINGS #############################
# what are the lowest and highest bpm values you want?
lowestBpm = 92
@ -24,10 +33,11 @@ highestBpm = 135
@@ -24,10 +33,11 @@ highestBpm = 135
# swing
# see https://tidalcycles.org/docs/patternlib/tour/time/#swing
minSwing = 0
maxSwing = 0.2
maxSwing = 0.1
# force everything to happen within 1 bar
oneBar = 1
oneBar = random . choice ( [ 0 , 1 ] )
# stack name
# this doesn't matter, can be any string
@ -116,15 +126,6 @@ midiCCChannel = 15
@@ -116,15 +126,6 @@ midiCCChannel = 15
midiCCs = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
################# SETUP #################
## import stuff
import sys
import random
import math
import re
import time
import decimal
from decimal import Decimal
############## PARSE ARGUMENT(S) ###############
@ -187,6 +188,50 @@ bpm = "setcps ("+str(random.randrange(lowestBpm, highestBpm, 1))+"/60/4)"
@@ -187,6 +188,50 @@ bpm = "setcps ("+str(random.randrange(lowestBpm, highestBpm, 1))+"/60/4)"
####################### FUNCTIONS ###################
def markovBinary ( count ) :
"""
argument : how many values to generate
output : a string like 0 1 1 0 0 0 1 1 1 1 0 0
trying to figure out how to use markov chains
to make more organic rhythms
where the on / off aren ' t evenly distributed,
if there is an event , it ' s more likely to be
followed by another event
"""
# choose a random starting value
startingValue = random . choice ( [ 0 , 1 ] )
#print("start: "+str(startingValue))
# now that we have a starting value, the likelihood
# of getting that same value next should be higher
# than a different value
value = startingValue
fullPat = " "
for i in range ( count ) :
#print("value: "+str(value))
# this is a bit of a hack, but it works!
# basically if the value is 0, there's more of a chance
# the next value will also be zero
# and if the value is 1, there's an extremely big change
# the next value will also be one
if ( value == 0 ) :
value = random . choice ( [ 0 , 0 , 0 , 1 ] )
elif ( value == 1 ) :
value = random . choice ( [ 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 ] )
fullPat = str ( value ) + " " + str ( fullPat )
value = random . choice ( [ 0 , 1 ] )
return fullPat
#print("fullpat: "+str(fullPat))
def swing ( minSwing , maxSwing ) :
"""
Generate a swing value for the entire stack
@ -196,7 +241,7 @@ def swing(minSwing,maxSwing):
@@ -196,7 +241,7 @@ def swing(minSwing,maxSwing):
#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 )
firstSwing = str ( decimal . Decimal ( random . randrange ( 0 , 1 2) ) / 100 )
secondSwing = 8 # if 8: swing 16th notes, if 4: swing 8th notes
return " swingBy " + str ( firstSwing ) + " " + str ( secondSwing )
@ -333,6 +378,7 @@ def genNotes(melodicOneBar):
@@ -333,6 +378,7 @@ def genNotes(melodicOneBar):
melodyLength = random . randrange ( shortestMelodic , longestMelodic )
melody = " "
for i in range ( melodyLength ) :
melody = str ( random . randrange ( 0 , random . choice ( noteRange ) ) ) + " " + str ( melody )
return ( " n " + " \" [ { " + melody + " } % " + str ( random . choice ( melodicDivisions ) ) + " ] \" " )
@ -441,11 +487,11 @@ def sometimesBeQuiet():
@@ -441,11 +487,11 @@ def sometimesBeQuiet():
"""
#def genOnOff(limit,busyness,struct):
# decide whether to do this at all
if ( random . randint ( 0 , 10 ) > 0 ) :
if ( random . randint ( 0 , 10 ) > 1 ) :
# for now, 8 bar phrases
# (# gain "[0 1 0 0 1]/8")
# how many cycles to make?
quietCycles = random . choice ( [ 8 ] )
quietCycles = random . choice ( [ 8 , 16 ] )
# this generates a sequence like 011010110
quietBinary = genOnOff ( quietCycles , 4 , 0 )
@ -476,7 +522,10 @@ print("do")
@@ -476,7 +522,10 @@ print("do")
#print("")
# create "let" statements
print ( indent + " let fourToTheFloor = 0 " )
print ( indent + " let fourToTheFloor = 0 -- set to 0 to hear it " )
print ( indent + " -- extra stuff " )
print ( indent + " extraCP = \" [0] \" " )
print ( indent + " extraHH = \" [0] \" " )
print ( indent + " -- control density per track " )
for i in percNames :
#print("let "+i+"M = \"["+genOnOff(4,5,0)+"]\"")
@ -524,7 +573,9 @@ print(indent+indent+"$ stack [")
@@ -524,7 +573,9 @@ 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 + " degradeBy fourToTheFloor $ struct \" [t*4] \" $ n \" 0 \" # midichan 0, " )
print ( indent + indent + " mask \" t \" " )
sometimesBeQuiet ( )
print ( indent + indent + " $ degradeBy fourToTheFloor $ struct \" [t*4] \" $ n \" 0 \" # midichan 0, " )
# generate rhythm tracks
# set the starting midi channel
@ -549,6 +600,7 @@ for i in percNames:
@@ -549,6 +600,7 @@ for i in percNames:
#if (i != "kick"):
# sometimesBeQuiet()
sometimesBeQuiet ( )
#### VARIATIONS #####
# insert some funStuff, maybe
if ( random . randint ( 0 , 10 ) > 3 ) :
@ -573,7 +625,12 @@ for i in percNames:
@@ -573,7 +625,12 @@ for i in percNames:
# choose between random, type-specific (bd,hh,etc),
# rhythm library, or euclidean rhythms
rChoice = random . choice ( [ 0 , 1 , 1 , 1 , 2 , 3 ] )
# 0 = prebaked hard-coded rhythms
# 1 = totally random 0's and 1's
# 2 = euclidean rhythms
# 3 = markov chain-based rhythms
# 4 = something random from the rhythm library
rChoice = random . choice ( [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 2 , 2 , 2 , 2 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 4 ] )
rhythm = " " # placeholder for the rhythm
if ( rChoice == 0 ) :
# if choice = 0, choose hardcoded rhythms
@ -646,6 +703,12 @@ for i in percNames:
@@ -646,6 +703,12 @@ for i in percNames:
rhythm = str ( euclid ( random . choice ( dividers ) , maxEuclidEvents ) )
#print(indent+indent+euclid(random.choice(dividers)))
#
elif ( rChoice == 3 ) :
# generate a pattern using a markov chain
# basically make it more likely for an ON value (1) to repeat
markovLength = 16
rhythm = " [ { " + str ( markovBinary ( markovLength ) ) . rstrip ( ) + " } % " + str ( markovLength ) + " ] "
else :
# print("LIBRARY")
rhythm = str ( random . choice ( rhythmLibrary ) )
@ -862,29 +925,30 @@ for i in midiCCs:
@@ -862,29 +925,30 @@ for i in midiCCs:
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
# 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 ) + " , " )
############ SEND SOME MIDI CC TO CHANGE SOUNDS ########
# a sort of way for the algorithm to choose presets
# the bass patch
# sends MIDI cc to a chooser on the bassline track in Bitwig
# chooses between 4 bass patches
bassPatch = str ( random . choice ( [ 30 , 60 , 100 , 127 ] ) )
print ( indent + indent + indent + " ------- bass patch ---- " )
print ( indent + indent + indent + " struct \" t \" $ ccn 101 # ccv " + bassPatch + " # midichan 10, " )
# pads/chords patch
padsPatch = str ( random . choice ( [ 30 , 60 , 100 , 127 ] ) )
print ( indent + indent + indent + " ------- pads patch ---- " )
print ( indent + indent + indent + " struct \" t \" $ ccn 102 # ccv " + padsPatch + " # midichan 11, " )
############### EXTRA STUFF #################
# sometimes this will generate a great groove
# that could be perfect with one or two extra things,
@ -895,8 +959,8 @@ print(indent+indent+indent+"ccn 126 # ccv "+str(random.randint(0,126))+"] # midi
@@ -895,8 +959,8 @@ print(indent+indent+indent+"ccn 126 # ccv "+str(random.randint(0,126))+"] # midi
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 " )
print ( indent + indent + indent + " (# gain extraCP ) $ struct \" [~ t ~ t] \" $ n \" 0 \" , " )
print ( indent + indent + indent + " (# gain extraHH ) $ struct \" [~t~t~t~t] \" $ n \" 1 \" ] # midichan 12 " )
## put the finishing touches on the beautiful stack! :-) :-)
print ( indent + indent + " ] # sunvox " )