from random import choice as choose
if __name__ != "__main__":
from ..SCLang import SynthDef, SynthDefs
from ..Players import Player
from ..Patterns import Sequences
from ..Patterns.Sequences import *
from types import FunctionType
patternNames = {name: obj for name, obj in vars(Sequences).items() \
if (type(obj) == FunctionType and name.startswith("P"))}
# To ignore
del patternNames['PSq']
del patternNames['PPairs']
del patternNames['PSine']
synthdefNames = [
name
for name, obj in vars(SynthDefs).items()
if isinstance(obj, SynthDef)
]
# Grammar
from random import random, randint
import re
re_player=r"\w+"
re_synthdef=r"\w+"
re_args=r".+"
re_methods=r".?" # todo
re_degree = re.compile(r"([\w()[\], /]*)\s*(?!\w*=)")
re_kwargs = re.compile(r"(\w+=" + re_degree.pattern + ")")
re_input = r"(?:\s*)(?P<player>{player}) >> (?P<synthdef>{synthdef})\((?P<args>{args})?\)(?P<methods>{methods})?"
re_input = re_input.format(player=re_player, synthdef=re_synthdef, args=re_args, methods=re_methods)
re_input = re.compile(re_input)
[docs]
def createPlayer(**kwargs):
player = kwargs['player']
synthdef = kwargs['synthdef']
degree = str(kwargs.get("degree", ""))
methods = kwargs.get("methods", "")
keywords = format_kwargs(kwargs.get("kwargs", ""))
sentence = "{player} >> {synthdef}({degree}" + (", " if (len(degree) > 0 and len(keywords) > 0) else "") + "{keywords}){methods}"
return sentence.format(player=player, synthdef=synthdef, degree=degree, methods=methods, keywords=keywords)
[docs]
def getArgs(s):
degree, kwargs = "", {}
# Find degree
match = re_degree.match(s)
if match:
degree = match.group(0)
if degree.endswith(","): degree = degree[:-1]
s = s[match.end():]
# Find kwargs
match = re_kwargs.findall(s)
if match:
for g1, g2 in match:
if g1.endswith(","): g1 = g1[:-1]
key, value = g1.split("=")
kwargs[key] = value
return degree, kwargs
[docs]
def playerData(s):
''' Returns a list of tuples of original string and dict of values '''
p = []
i = 0
while True:
match = re_input.match(s, pos=i)
if match is None:
return p
else:
d = match.groupdict()
if d['args'] is not None:
d['degree'], d['kwargs'] = getArgs(d['args'])
else:
d['degree'], d['kwargs'] = "", {}
del d['args']
p.append((match.group().strip(), d))
i = match.end()
return
# Generic actions
[docs]
def GENERATE_PATTERN(keyword=None):
pat = choose(patternNames.values())
# Make sure we get right number of args
num_defs = len(pat.__defaults__) if pat.__defaults__ is not None else 0
num_args = pat.__code__.co_argcount
# Only change default args occasionally
if random() > 0.9 and num_defs > 0:
num_defs -= randint(1, num_defs)
n = num_args - num_defs
if pat.__name__ not in patternInputs:
args = [str(GENERATE_INTEGER(keyword)) for i in range(n)]
else:
args = []
for i in range(n):
args.append(str(patternInputs[pat.__name__][i](keyword)))
return pat.__name__ + "({})".format(", ".join(args))
[docs]
def GENERATE_LIST(keyword=None):
return '[{}]'.format(", ".join([str(GENERATE_NUMBER(keyword)) for n in range(randint(2,9))]))
[docs]
def GENERATE_TUPLE(keyword=None):
return '({})'.format(", ".join([str(GENERATE_NUMBER(keyword)) for n in range(randint(2,3))]))
[docs]
def GENERATE_INTEGER(keyword=None):
if random() > 0.8:
return str(randint(1,2)) + str(randint(0,9))
else:
return randint(1,9)
[docs]
def GENERATE_NUMBER(keyword=None, _min=1, _max=9):
''' Return a random number or fraction '''
if random() > 0.5 and keyword != "amp":
return randint(_min,_max)
elif keyword == "amp":
a = randint(_min, _max)
b = randint(a, a*3)
return str(a) + "/" + str(b)
else:
return str(randint(max(_min, 1),_max)) + "/" + str(randint(max(_min, 1),_max))
[docs]
def GEN_CHOOSE_VALUE(keyword=None):
r = random()
# Choose between existing pattern
if 0.00 <= r <= 0.33:
return GENERATE_PATTERN(keyword)
# Randomly generated list of numbers
if 0.33 <= r <= 0.66:
if random() > 0.25 or keyword == "dur":
return GENERATE_LIST(keyword)
else:
return GENERATE_TUPLE(keyword)
# Single value
if 0.66 <= r <= 1.00:
return GENERATE_NUMBER(keyword)
[docs]
def GEN_CHANGE_VALUE(value, keyword=None):
value = str(value)
match = list(re.finditer(r"\d+(?:[/.]\d+)?", value))
if len(match) == 1:
return value[:match[0].start()] + str(GENERATE_NUMBER(keyword)) + value[match[0].end():]
i = 0
out = []
for num in match:
j = num.start()
new_num = str(GENERATE_NUMBER(keyword)) if random() > 0.5 else num.group()
out += [value[i:j], new_num]
i = num.end()
out.append(value[i:])
return "".join(out)
# Actions
[docs]
def ADD_NEW_KWARG(*args, **kwargs):
new_kw = [kw for kw in Keywords if kw not in kwargs['kwargs']]
if len(new_kw) > 0:
if "dur" not in new_kw and random() > 0.25:
new_kw = "dur"
else:
new_kw = choose(new_kw)
kwargs['kwargs'][new_kw] = GEN_CHOOSE_VALUE(new_kw)
return kwargs
[docs]
def CHANGE_DEGREE(*args, **kwargs):
''' Randomly generates a new degree, or edits some of the previous values '''
if len(str(kwargs['degree'])) or random() > 0.5:
# generate new
kwargs['degree'] = GEN_CHOOSE_VALUE('degree')
else:
kwargs['degree'] = GEN_CHANGE_VALUE(kwargs['degree'], 'degree')
return kwargs
[docs]
def CHANGE_KWARG(*args, **kwargs):
if kwargs['kwargs']:
kw, value = choose(kwargs['kwargs'].items())
if random() > 0.5:
kwargs['kwargs'][kw] = GEN_CHANGE_VALUE(value, kw)
else:
kwargs['kwargs'][kw] = GEN_CHOOSE_VALUE(value)
else:
kwargs = ADD_NEW_KWARG(*args, **kwargs)
return kwargs
[docs]
def ADD_NEW_METHOD(*args, **kwargs):
return kwargs
# Patterns that take patterns as input
_pat = lambda keyword: choose([GENERATE_PATTERN, GENERATE_LIST]).__call__(keyword)
_int = GENERATE_INTEGER
_num = GENERATE_NUMBER
patternInputs = {
'PStutter' : [_pat, _int],
'PShuf' : [_pat],
'PAlt' : [_pat, _pat, _pat],
'PStep' : [_int, _num, _num],
'PSum' : [_int, _num],
'PStetch' : [_pat, _int],
'PZip' : [_pat, _pat, _pat],
'PZip2' : [_pat, _pat]
}
# Constants
[docs]
def WeightedList(weights):
data = []
for item, weight in weights.items():
data += ([item] * weight)
return data
Players = [ "g1", "g2", "g3", "g4", "g5" ]
ActionWeights = { ADD_NEW_KWARG : 2,
CHANGE_DEGREE : 5,
CHANGE_KWARG : 3,
ADD_NEW_METHOD : 0 }
Actions = WeightedList(ActionWeights)
KeywordWeights = { "dur" : 4,
"amp" : 1,
"vib" : 1,
"sus" : 1,
"chop" : 1,
"bits" : 1,
"delay" : 1,
"pan" : 1 }
Keywords = WeightedList(KeywordWeights)