hey thanks for you helpful replies @PitchTrebler @jamshark70.
i think sequencing the different subarrays with Pseq
is more a special case of using ~abDict
.
I think the usual one would be: play a specific subarray at an index and change the index at some point in time so another subarray will be played.
I didint know that both of these uses need a different control structure.
Im sorry this has not been coming to my mind yet. i think its even more complicated now:
Im using this function for generating an initial L-System and building abstractions of it and store them in a Dictionary and order them by size (lower to higher rhythmic density), so i can access the different abstractions in Pbind
at their index.
(
~rewriteWord = {
arg word, rules, iter=6;
iter.do{
word = Array.newFrom(word)
.collect{|c| rules[c.asSymbol]}
.join;
};
word
};
~getDur = {
arg word, notesDict;
// streams are created inside this function
// so every time we call ~getDur, streams are resetted
var streams = notesDict.collect{|seq| Pseq(seq,inf).asStream};
Array.newFrom(word).collect{|c| streams[c.asSymbol].next }
};
//function for cropping L-System
~sumUpTo = {
arg array, total;
var sum = 0, i = 0, result = Array.new;
while { i < array.size and: { sum < total } } {
sum = sum + array[i];
if(sum > total) {
result = result.add(array[i] - (sum - total));
} {
result = result.add(array[i]);
};
i = i + 1;
};
result
};
/*
functions for building Abstractions
creating abstractions with lesser density: reduce the density by joining two values
*/
~low_dens = {
arg array;
var ioi, result = [], min_index, neighbor_index;
ioi = array.copy();
ioi.debug("start ioi");
while {ioi.size() > 1} {
// in every step, we will reduce ioi until it has size 1
var intermediate_result = [];
min_index = ioi.minIndex;
// if first element is minimum element, the neighor can only be to the right
if (min_index == 0) {
neighbor_index = 1;
} {
// else if last element is minimum element, the neighbor can only be to the left
if (min_index == (ioi.size - 1)) {
neighbor_index = (ioi.size - 2);
} {
// else we're in the middle, so use the neighbor which is smallest (arbitrary choice)
var dir = 1;
if (ioi[min_index - 1] < ioi[min_index + 1]) {
dir = -1;
};
neighbor_index = min_index + dir;
};
};
// swap neighbor and min_index so that neighbor is always bigger than min_index
// - this makes copying easier in the next step
if (neighbor_index < min_index) {
var tmp = min_index;
min_index = neighbor_index;
neighbor_index = tmp;
};
// make a new list consisting of everything before min_index, sum of min el and its neighbor, everything after neighbor
intermediate_result = ioi.copyRange(0, min_index-1) ++ (ioi[min_index] + ioi[neighbor_index]) ++ ioi.copyRange(neighbor_index+1, (ioi.size-1));
if (intermediate_result.size >= ioi.size) {
"ERROR!".postln;
};
// add it to the list of results
result = result.add(intermediate_result);
// and update our starting point with the reduced list
ioi = intermediate_result.copy();
};
// return the complete list of reductions
result;
};
/*
creating abstractions with higher density by replacing a value with:
1. a smaller potential IOI value
2. and the difference between it and the value being replaced
*/
~high_dens = {
arg array;
var ioi, ioi_sort, pot_ioi, result = [], index_max;
ioi = array.copy();
ioi.debug("start ioi");
ioi_sort = ioi.as(Set).as(Array).sort;
pot_ioi = ioi_sort.minItem;
while { ioi.maxItem > pot_ioi } {
var intermediate_result = [];
index_max = ioi.findAll([ioi.maxItem]).choose;
intermediate_result = intermediate_result.add(ioi);
intermediate_result = intermediate_result.insert(index_max+1, [ioi[index_max] - pot_ioi, pot_ioi]).flat;
intermediate_result.removeAt(index_max);
// add it to the list of results
result = result.add(intermediate_result);
// and update our starting point with the reduced list
ioi = intermediate_result.copy();
};
result;
};
// now define rules
~rules = (A:"AB", B:"A");
// define durations for each symbol
~lsys = (
A: [0.25, 0.5],
B: [0.5, 0.125],
);
// Axiom
~axiom = "A";
// rewriting iteration
~axiom = ~rewriteWord.(~axiom, ~rules, 6);
// getting durations
~durations = ~getDur.(~axiom, ~lsys);
//crop L-System to specific length
~lsys = ~sumUpTo.(~durations, 4);
//create Dictionary for abstractions
~absDict = Dictionary.new;
//add abstractions with lower density to the dictionary
~low_abs = ~low_dens.(~lsys);
~low_abs.do({
arg el, elindex;
~absDict.put((el.size), el);
});
//add initial L-System to Dictionary
~absDict.put(~absDict.size+1, ~lsys);
//add abstractions with higher density to the dictionary
~high_abs = ~high_dens.(~lsys);
~high_abs.do({
arg el, elindex;
~absDict.put((el.size), el);
});
//get Dictionary in order array.size low -> high
~absDict = ~absDict.atAll(~absDict.order);
~absDict.debug("abs dict");
)
The last part where i put everything in ~absDict
is a bit clunky i guess and also pretty weird because the result is not a Dictionary. its an array with subarrays if im not mistaken.
Im also not sure if a Dictionary or an array with subarrays is better for my intended use in Pbind
. But this beeing said the array cannot be flattened. the whole thing is about having these different rhythmic cells independent from each other stored at their index.
EDIT: ive tried to rework the function so you can call ~getLsys and get a new Lsystem and abstractions on the fly. probably some coding flaws here but its working. But the dictionary / array confusion is still there. any ideas?
(
~rewriteWord = {
arg word, rules, iter=6;
iter.do{
word = Array.newFrom(word)
.collect{|c| rules[c.asSymbol]}
.join;
};
word
};
~getDur = {
arg word, notesDict;
// streams are created inside this function
// so every time we call ~getDur, streams are resetted
var streams = notesDict.collect{|seq| Pseq(seq,inf).asStream};
Array.newFrom(word).collect{|c| streams[c.asSymbol].next }
};
//function for cropping L-System
~sumUpTo = {
arg array, total;
var sum = 0, i = 0, result = Array.new;
while { i < array.size and: { sum < total } } {
sum = sum + array[i];
if(sum > total) {
result = result.add(array[i] - (sum - total));
} {
result = result.add(array[i]);
};
i = i + 1;
};
result
};
//creating abstractions with lesser density: reduce the density by joining two values
~low_dens = {
arg array;
var ioi, result = [], min_index, neighbor_index;
ioi = array.copy();
ioi.debug("start ioi");
while {ioi.size() > 1} {
// in every step, we will reduce ioi until it has size 1
var intermediate_result = [];
min_index = ioi.minIndex;
// if first element is minimum element, the neighor can only be to the right
if (min_index == 0) {
neighbor_index = 1;
} {
// else if last element is minimum element, the neighbor can only be to the left
if (min_index == (ioi.size - 1)) {
neighbor_index = (ioi.size - 2);
} {
// else we're in the middle, so use the neighbor which is smallest (arbitrary choice)
var dir = 1;
if (ioi[min_index - 1] < ioi[min_index + 1]) {
dir = -1;
};
neighbor_index = min_index + dir;
};
};
// swap neighbor and min_index so that neighbor is always bigger than min_index
// - this makes copying easier in the next step
if (neighbor_index < min_index) {
var tmp = min_index;
min_index = neighbor_index;
neighbor_index = tmp;
};
// make a new list consisting of everything before min_index, sum of min el and its neighbor, everything after neighbor
intermediate_result = ioi.copyRange(0, min_index-1) ++ (ioi[min_index] + ioi[neighbor_index]) ++ ioi.copyRange(neighbor_index+1, (ioi.size-1));
if (intermediate_result.size >= ioi.size) {
"ERROR!".postln;
};
// add it to the list of results
result = result.add(intermediate_result);
// and update our starting point with the reduced list
ioi = intermediate_result.copy();
};
// return the complete list of reductions
result;
};
/*
creating abstractions with higher density by replacing a value with:
1. a smaller potential IOI value
2. and the difference between it and the value being replaced
*/
~high_dens = {
arg array;
var ioi, ioi_sort, pot_ioi, result = [], index_max;
ioi = array.copy();
ioi.debug("start ioi");
ioi_sort = ioi.as(Set).as(Array).sort;
pot_ioi = ioi_sort.minItem;
while { ioi.maxItem > pot_ioi } {
var intermediate_result = [];
index_max = ioi.findAll([ioi.maxItem]).choose;
intermediate_result = intermediate_result.add(ioi);
intermediate_result = intermediate_result.insert(index_max+1, [ioi[index_max] - pot_ioi, pot_ioi]).flat;
intermediate_result.removeAt(index_max);
// add it to the list of results
result = result.add(intermediate_result);
// and update our starting point with the reduced list
ioi = intermediate_result.copy();
};
result;
};
~getLsys = {
arg rules, lsys, axiom, length;
var durations, low_abs, high_abs;
//create Dictionary for abstractions
var absDict = Dictionary.new;
// rewriting iteration
axiom = ~rewriteWord.(axiom, rules, 6);
// getting durations
durations = ~getDur.(axiom, lsys);
//crop L-System to specific length
lsys = ~sumUpTo.(durations, length);
//add abstractions with lower density to the dictionary
low_abs = ~low_dens.(lsys);
low_abs.do({
arg el, elindex;
absDict.put((el.size), el);
});
//add initial L-System to Dictionary
absDict.put(absDict.size+1, lsys);
//add abstractions with higher density to the dictionary
high_abs = ~high_dens.(lsys);
high_abs.do({
arg el, elindex;
absDict.put((el.size), el);
});
//get Dictionary in order low -> high
absDict = absDict.atAll(absDict.order);
absDict.debug("abs dict");
};
// define rules
~rules = (A:"AB", B:"A");
// define durations for each symbol
~lsys = (A: [0.25, 0.5], B: [0.5, 0.125]);
// Axiom
~axiom = "A";
~absDict = ~getLsys.(~rules, ~lsys, ~axiom, 4);
)