Last active
March 2, 2023 04:00
-
-
Save dmorgan-github/d469788f3e3c5752793b88b61a75ec29 to your computer and use it in GitHub Desktop.
SuperCollider mini dsl for durations and values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Pbind(\degree, Pdv.parse("0 1 2 3")) | |
-> Pbind(\degree, Pseq([0 1 2 3], inf), \dur, 1) | |
Pbind(\degree, Pdv.parse("0 1 [2 3]")) | |
-> Pbind(\degree, Pseq([0 1 2 3], inf), \dur, Pseq([1, 1, 0.5, 0.5], inf)) | |
Pbind(\degree, Pdv.parse("0 1 [2 [3, 4]]")) | |
-> Pbind(\degree, Pseq([0 1 2 3, 4], inf), \dur, Pseq([1, 1, 0.5, 0.25, 0.25], inf)) | |
Pbind(\degree, Pdv.parse("0 1 [2 3, 4]")) | |
-> Pbind(\degree, Pseq([0 1 2 3, 4], inf), \dur, Pseq([1, 1, 1/3, 1/3, 1/3], inf)) | |
Pbind(\degree, Pdv.parse("0 1 [2 3, 4]^2")) | |
-> Pbind(\degree, Pseq([0 1 2 3, 4], inf), \dur, Pseq([1, 1, 1/1.5, 1/1.5, 1/1.5], inf)) | |
Pbind(\degree, Pdv.parse("0!4 1")) | |
-> Pbind(\degree, Pseq([0, 0, 0, 0, 1], inf), \dur, Pseq([0.25, 0.25, 0.25, 0.25, 1], inf)) | |
// shuffle | |
Pbind(\degree, Pdv.parse("[0 1 2 3]$")) | |
-> Pbind(\degree, Pshuf([ 1, 3, 2, 0 ], inf), \dur, 0.25) | |
// choose | |
Pbind(\degree, Pdv.parse("[0 1 2 3]#")) | |
-> Pbind(\degree, Prand([0, 1, 2, 3], inf), \dur, 1) | |
*/ | |
Pdv { | |
*new { | |
} | |
*rout {|list| | |
var gate = nil; | |
^Prout({|inval| | |
var parse; | |
parse = {|obj, div=1, stretch=1| | |
var val, rep, shuf; | |
var result = (); | |
val = obj['val'].value; | |
stretch = (obj['stretch'] ?? stretch).value.asFloat; | |
rep = (obj['rep'] ?? 1).value.asInteger; | |
if (val.isSequenceableCollection) { | |
var size = val.size; | |
val.do({|item| | |
parse.(item, div * size.reciprocal, stretch); | |
}); | |
}{ | |
if (obj['type'] == \value) { | |
if (val.isRest) { | |
div = Rest(div); | |
}; | |
rep.do({|i| | |
inval[\g1] = gate; | |
inval['dur'] = div * rep.reciprocal * stretch; | |
inval = val.embedInStream(inval); | |
gate = nil | |
}) | |
} { | |
parse.(val, div, stretch) | |
} | |
}; | |
}; | |
inf.do({ | |
gate = true; | |
list.asArray.do({|val| | |
parse.(val); | |
}); | |
}); | |
inval; | |
}); | |
} | |
*sequence {|list| | |
var parse; | |
parse = {|list, result| | |
list.do({|item| | |
var val = item['val']; | |
if (item['type'] == \group) { | |
var mylist = List.new; | |
mylist = parse.(val, mylist); | |
result.add( | |
item['val'] = if (item['shuf'] == true) { | |
{ mylist.asArray.scramble } | |
} { | |
if (item['choose'] == true) { | |
{ mylist.asArray.choose } | |
} { | |
mylist | |
} | |
}; | |
); | |
} { | |
if (item['type'] == \alt) { | |
var mylist = List.new; | |
mylist = parse.(val, mylist); | |
result.add( | |
item['val'] = Routine({ | |
var cnt = 0; | |
inf.do({|i| | |
if (item['shuf'] == true) { | |
mylist = mylist.asArray.scramble; | |
}; | |
mylist.wrapAt(i).yield; | |
cnt = cnt + 1; | |
}); | |
}) | |
); | |
}{ | |
result.add( item ) | |
} | |
} | |
}); | |
result; | |
}; | |
^parse.(list, List.new); | |
} | |
*tokenize {|str| | |
var exec, match; | |
var getNextToken; | |
var hasMoreTokens; | |
var spec; | |
var cursor = 0; | |
// as pairs | |
spec = [ | |
'number', "^[+-]?([0-9]*[.])?[0-9]+", | |
'stretch', "^\\^", | |
'rep', "^\!", | |
'rest', "^\~", | |
'shuf', "^\\$", | |
'choose', "^\#", | |
'[', "^\\[", | |
']', "^\\]", | |
'<', "^\<", | |
'>', "^\>", | |
nil, "^\\s+", | |
nil, "^\,", | |
]; | |
hasMoreTokens = { | |
cursor < str.size; | |
}; | |
match = {|regex, str| | |
var val = nil; | |
var m = str.findRegexp(regex); | |
if (m.size > 0) { | |
val = m[0][1]; | |
cursor = cursor + val.size; | |
}; | |
val; | |
}; | |
getNextToken = { | |
var getNext; | |
var result = nil; | |
getNext = { | |
if (hasMoreTokens.()) { | |
spec.pairsDo({|k, v| | |
if (result.isNil) { | |
var val = match.(v, str[cursor..]); | |
if (val.notNil) { | |
if (k.isNil) { | |
getNext.() | |
}{ | |
result = ( | |
type: k, | |
val: val | |
); | |
} | |
} | |
} | |
}); | |
}; | |
}; | |
getNext.(); | |
if (result.isNil) { | |
"unexpected token %".format(str[cursor]).throw | |
}; | |
result; | |
}; | |
exec = {|list| | |
var exit = false; | |
while ({ hasMoreTokens.() and: { exit.not } }, { | |
var token = getNextToken.(); | |
switch(token['type'], | |
// entities | |
'number', { | |
list.add( (val:token['val'].asFloat, type:\value) ) | |
}, | |
'rest', { | |
list.add( (val:\rest, type:\value) ) | |
}, | |
// modifiers | |
'stretch', { | |
list.last['stretch'] = getNextToken.()['val'].asFloat | |
}, | |
'rep', { | |
list.last['rep'] = getNextToken.()['val'].asInteger; | |
}, | |
'shuf', { | |
list.last['shuf'] = true; | |
}, | |
'choose', { | |
list.last['choose'] = true; | |
}, | |
// grouping delimiters | |
'[', { | |
var result; | |
list.add( () ); | |
result = exec.(List.new); | |
list.last['val'] = result; | |
list.last['type'] = 'group' | |
}, | |
']', { | |
exit = true | |
}, | |
'<', { | |
var result; | |
list.add( () ); | |
result = exec.(List.new); | |
list.last['val'] = result; | |
list.last['type'] = 'alt' | |
}, | |
'>', { | |
exit = true | |
} | |
); | |
}); | |
list; | |
}; | |
^exec.(List.new); | |
} | |
*parse {|str| | |
var list, seq; | |
list = Pdv.tokenize(str); | |
seq = Pdv.sequence(list) | |
^Pdv.rout(seq); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment