( ~orbitStyleChanger = { arg switchEvery = 16, modulationRate = 0.50, neoStream = Prand([\P, \R, \L], inf).asStream; var orbits = [~classicOrbit.(modulationRate: modulationRate), ~neoOrbit.(stream: neoStream) ]; var idx = 0; var n = 1; var curTriad = orbits[idx].next(); var prev = curTriad; var isMajor = { |triad| var f = triad[1]-triad[0]; // first interval var s = triad[2]-triad[1]; // second interval case {(f==4 && s==3) || (f==3 && s==5) || (f==5 && s==4)} {true} {(f==3 && s==4) || (f==5 && s==3) || (f==4 && s==5)} {false} {true}{false} }; // getRoot will find the root of a major or minor triad which might be in // any inversion. var getRoot = { |triad| var a = triad[0]; var b = triad[1]; var c = triad[2]; case {c-a == 7}{ a } {b-a == 5}{ b } {c-b == 5}{ c } {true}{triad.debug("[getRoot] unknown root"); a} }; // dist and findBestInversion are helper functions find good voice leading. var dist = {arg tr1, tr2; (tr2-tr1).sum({|i| i**2}).sqrt(); }; var findBestInversion = { arg tr1, tr2; var inversions = [ tr2, (tr2 + [0 , -12, -12]).sort, (tr2 + [0 , 0, -12]).sort, tr2 - 12, ]; var distances = inversions.collect({|chord| dist.(tr1, chord)}); var minIdx = distances.minIndex; inversions[minIdx]; }; Routine { postln("starting orbitStyleChanger with triad "++curTriad++ ", switching orbits on the first major chord "++ "after "++switchEvery++" chords "); while {curTriad != nil} { curTriad = findBestInversion.(prev, curTriad); curTriad.yield; prev = curTriad; n = n + 1; if ((n >= switchEvery) && isMajor.(curTriad)) { idx = (idx + 1) % 2; orbits[idx].stop; if (idx == 0) { postln("stopping neoOrbit, starting new classicOrbit at "+curTriad); orbits[idx] = ~classicOrbit.(center: getRoot.(curTriad), position: 0, modulationRate: modulationRate); } { postln("stopping classicOrbit, starting new neoOrbit at "+curTriad); orbits[idx] = ~neoOrbit.(chord: curTriad, stream: neoStream); }; n = 1; // The new orbit starts at curTriad, but we had that already, so skip it orbits[idx].next; }; curTriad = orbits[idx].next; } } }; ) ( s.record(24); Pbind(\scale, Scale.chromatic, \degree, ~orbitStyleChanger.(switchEvery: 8, neoStream: Pseq([\R, \L], inf).asStream), \dur, 0.5, ).play; )