On 02.01.2012, at 00:21, Kassen wrote:
> On 01/01/2012, Julian Rohrhuber <rohrhuber_at_uni-hamburg.de> wrote:
>
>> There is one tracker that works like this for SC and jitlib, it's called
>> "eternal return", and you can rewrite not only code, but also your own past,
>> while all that is not changed keeps running.
>>
>
> I suspect that there is some confusion here; I think you are talking
> about a "tracker" in the sense of "something that keeps track of
> something" while we are refering to this;
>
> http://en.wikipedia.org/wiki/Tracker_(music_software)
>
> If there is no such confusion I'd really like to see a video of this
> in action, if any exist.
>
No, no confusion, I have used fast tracker. Perhaps I took it very generally, but it does what you described and has a timeline etc. I've got no video but here is the source:
// eternal return.
// nonlinear history for live and experimental just in time programming.
// Julian Rohrhuber 2007-2011
// EME version gui decoration with contributions from Tom Hall
/*
This program will give access to any ProxySpace (or subclass of LazyEnvir / EnvironmentRedirect) that is present as current environment. If none is present, it will create a ProxySpace and push it.
It creates a document window with some temporal navigations.
This document is a throw away GUI, it can be closed anytime.
- the black or otherwise coloured stripe (var timebarBG) is the current time.
- the edits are shown as red lines; the current position as a white line.
- the first number box is the current time
- the last number box represents the length of history, after which we return to beginning
Buttons:
_ navigate in time without entering it (toggles)
-> start time / stop time (toggles)
<< rewind to beginning of time
:< step back to previous edit
>: step forward to next edit
< backward 0.25 sec in time
> forward 0.25 sec in time
# post the complete history
rm remove the current entry
ld load an appropriate history
Change the size of the GUI and document via the guiScale var
*/
// navigate via code
j.value(0) // go to start
j.value(1.0) // go to end
j.value(1.0.rand) // go to random spot
r.value; // play
r.value(false); // stop
// Autopilot example
TempoClock.default.tempo = 5;
(
~x.play;
~x.fadeTime = 4;
)
(
var playDur=180, randDur = 20, fadeTime=4, randDurAdj;
Tdef(\player, {
randDurAdj = (randDur-fadeTime)/fadeTime;
{j.value(0)}.defer; // go to start
1.wait;
{ r.value }.defer; // play
playDur.do({|i|
i.postln;
1.0.wait; // test clock
});
"random playback".postln;
randDurAdj.floor.do({ |i|
if(i==0 or: {0.5.coin},
{{j.value(1.0.rand)}.defer;
}
);
fadeTime.wait;
});
(randDurAdj.frac + fadeTime).wait;
{ r.value(false) }.defer;
{j.value(1)}.defer // go to end, assume silence
}).play;
)
(
var w, txt, value = 0, mapVal, valMap, b2, loadFunc, scaledValue = 0;
var tmax = 180, delta = 0.25, checkIfEnd, initProxySpace;
var ff, fne, fla, fba, fne2, fla2, m, v, vv, mmax, task, changed, running = false;
var drawLine, playRout, windowFollow,toggleHistory, dispatch, onTime = false;
var browseBut, browseMode = false, docFont;
var nietzsche, temporaryPseudoEnvironment, proxyspace;
var guiScale=1.6, width = 700, guiFont, projectTitle="EME";
var bgColor = Color.new255(255, 255, 224); // yellow
var timebarBG = Color.new255(100, 149, 237); // blue
docFont = Font("Monaco", (11*guiScale * 1.3).floor.asInteger);
guiFont = Font("Monaco", (11*guiScale).floor.asInteger);
temporaryPseudoEnvironment = EnvironmentRedirect.new;
b2 = Rect(10, 10, 350*guiScale, 20*guiScale);
w = Window.new(projectTitle+"eternal return", Rect(128, 64*guiScale, width*guiScale, 30*guiScale));
w.view.decorator = FlowLayout( w.view.bounds ).gap_(2 _at_ 2);
running = { task.isPlaying };
toggleHistory = {
if(running.value) { task.play(AppClock) } { task.stop };
};
// get the next appropriate point in time,
// either the next delta step, or, if closer, the next timepoint
task = Task {
var nextDelta, nextIndex;
loop {
nextIndex = dispatch.timepoints.indexOfGreaterThan(scaledValue);
if(nextIndex.isNil) {
if(dispatch.timepoints.last.notNil
and: {dispatch.timepoints.last >= (scaledValue + delta)}) {
nextDelta = tmax - scaledValue;
} {
nextDelta = delta
}
} {
nextDelta = min(delta, dispatch.timepoints.at(nextIndex) - scaledValue);
};
scaledValue = (scaledValue + nextDelta % tmax);
// if(loop.not, {checkIfEnd.(scaledValue)}); // TH
valMap.(scaledValue);
nextDelta.wait;
}
};
// checkIfEnd = {|pos| if(pos>=(tmax-delta), {ff.valueAction(0)})}; // TH
if(currentEnvironment.isKindOf(EnvironmentRedirect).not) {
proxyspace = p = ProxySpace.push(s.boot);
} {
proxyspace = currentEnvironment;
};
// init for EME
initProxySpace = {
p.use {
~x.ar; ~y.ar; ~z.ar;
~a.ar; ~b.ar; ~c.ar;
~x.play; ~y.play; ~z.play;
};
};
dispatch = proxyspace.dispatch;
if(dispatch.isNil) {
proxyspace.dispatch = dispatch = HistoryDispatch.new;
};
dispatch.manual = true;
dispatch.alwaysReplace = false;
changed = { |time|
var str, envir, changed, pairs;
envir = dispatch.envir;
if(browseMode) {
pairs = dispatch.getPairsForTime(time);
if(pairs.isNil) { d.string = "" } {
d.string = pairs.collect { |pair|
format("\n~% = %;\n\n", pair[0], pair[1].asCompileString)
}.join;
};
} {
changed = dispatch.setTime(time);
if(changed) {
str = dispatch.envir.asCompileString;
d. string_( String.streamContents { arg str;
envir.storeOn(str, includeSettings: false);
});
}
};
};
drawLine = { |x, inset=0|
Pen.beginPath;
Pen.moveTo(x _at_ (5 + inset));
Pen.lineTo(x _at_ (vv.bounds.height + 5 - inset));
Pen.width = (1 * guiScale).floor.asInteger; // needed here?
Pen.stroke;
};
w.drawHook_{
Pen.use {
Pen.smoothing = false;
Pen.width = (1 * guiScale).floor.asInteger;
Color.white.set;
drawLine.(value, 0);
// Color.red.set;
Color.new255(230, 123, 155).set;
dispatch.timepoints.do {|x=(0)|
x = x.clip(0, tmax);
drawLine.(x / tmax * vv.bounds.width + vv.bounds.left, 3);
};
};
};
mapVal = {|x|
if(x.inclusivelyBetween(vv.bounds.left, vv.bounds.left + vv.bounds.width)) {
value = x;
scaledValue = x - vv.bounds.left / vv.bounds.width * tmax;
m.value = scaledValue;
//onTime = dispatch.timepoints.includes(scaledValue);
w.refresh;
}
};
valMap = {|x|
if(x.notNil) {
x = x.clip(0, tmax);
m.value = scaledValue = x;
value = x / tmax * vv.bounds.width + vv.bounds.left;
changed.value(scaledValue);
// onTime = timepoints.includes(scaledValue);
w.refresh;
}
};
loadFunc = { |path|
var file, str, delim;
protect {
file = File(path.standardizePath, "r");
str = file.readAllString;
} {
file.close;
};
str = str.stripRTF;
delim = str.findAll("// -").add(str.size - 1);
str = str.clumps(delim.differentiate).drop(1).drop(-1);
proxyspace.awake = false;
str.do { |substr|
var time = History.getTimeFromString(substr);
changed.(time);
substr.interpret;
};
proxyspace.awake = true;
proxyspace.do(_.awake_(true));
tmax = max(tmax, History.getTimeFromString(str.last));
mmax.value = tmax;
valMap.(0.0);
d.front;
};
vv = CompositeView.new(w, b2).background_(timebarBG);
v = UserView.new(vv, Rect(0, 0, w.bounds.width, w.bounds.height))
.mouseMoveAction_({|v,x| mapVal.value(x) })
.mouseUpAction_({|v,x| mapVal.value(x); changed.value(scaledValue); })
.mouseDownAction_({|v,x| mapVal.value(x); });
m = NumberBox.new(w, Rect(0, 0, 50 * guiScale, 20 * guiScale));
m.value = 0;
m.action = { valMap.value(m.value) };
m.font_(guiFont);
m.background_(bgColor);
vv.keyDownAction = { |v, char|
char.postln;
if(char == $ , toggleHistory);
};
browseBut = Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale))
.states_([["_", Color.black], ["-", Color.black]])
.action_({|b|
browseMode = b.value == 1;
if(b.value == 0) {
valMap.(scaledValue);
// currentEnvironment = proxyspace;
// proxyspace.dispatch = dispatch;
} {
// currentEnvironment = temporaryPseudoEnvironment;
// temporaryPseudoEnvironment.dispatch = dispatch;
};
// yellow colour for bg
d.background = [bgColor, Color.grey(0.8)][b.value];
})
.font_(guiFont);
ff = Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale)).states_([["->", Color.black], ["||", Color.red]]).font_(guiFont);
fba = Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale)).states_([["<<", Color.black]]).font_(guiFont);
fla = Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale)).states_([[":<", Color.black]]).font_(guiFont);
fne = Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale)).states_([[">:", Color.black]]).font_(guiFont);
fla2 = Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale)).states_([["<", Color.black]]).font_(guiFont);
fne2 = Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale)).states_([[">", Color.black]]).font_(guiFont);
ff.action = { if(ff.value == 1) { task.play(AppClock); } { task.stop } };
fba.action = { valMap.(0.0) };
fne.action = {
i = dispatch.timepoints.indexOfGreaterThan(scaledValue) ? 0;
valMap.(dispatch.timepoints.at(i))
};
fla.action = {
i = dispatch.timepoints.indexOfGreaterThan(scaledValue - 0.01);
i = i ? dispatch.timepoints.lastIndex;
if(i.notNil and: { i > 0 }) {
valMap.(dispatch.timepoints.wrapAt(i-1))
}
};
fne2.action = {
valMap.(scaledValue + delta % tmax)
};
fla2.action = {
valMap.(scaledValue - delta % tmax)
};
mmax = NumberBox.new(w, Rect(0, 0, 30*guiScale, 20*guiScale));
mmax.value = tmax;
mmax.font_(guiFont);
mmax.background_(bgColor);
mmax.action = { tmax = mmax.value; valMap.(scaledValue); };
Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale))
.states_([["#", Color.black]])
.action_({dispatch.document})
.font_(guiFont);
Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale))
.states_([["rm", Color.black]])
.action_({
dispatch.removeAllAtTime(scaledValue);
changed.(scaledValue);
w.refresh;
})
.font_(guiFont);
Button.new(w, Rect(0, 0, 20*guiScale, 20*guiScale))
.states_([["ld", Color.black]])
.action_({
Dialog.getPaths({ |paths| paths.do(loadFunc.(_)) });
})
.font_(guiFont);
// EXTERNALLY AVAILABLE FNS j and r
j = {|pos|
var map = (350 * guiScale * pos) + 4;
mapVal.value(map); // changed.value(scaledValue); // not twice!
initProxySpace.value; // EME special, keep them all running!
};
r = {arg go=true;
if(go, {ff.valueAction_(1)}, {ff.valueAction_(0)});
initProxySpace.value; // EME special, keep them all running!
};
////////////
w.front;
d = Document.new(projectTitle+"history doc")
.bounds_(Rect(20, 280, width*guiScale, 481));
d.background_(bgColor);
d.font_(docFont);
d.onClose = { w.close; windowFollow.stop; CmdPeriod.remove(playRout); };
w.onClose = { windowFollow.stop; CmdPeriod.remove(playRout); };
d.toFrontAction = d.toFrontAction.addFunc {
if(w.isClosed.not) { ff.valueAction = 0 };
};
nietzsche = #["Alles Laufenkoennen muss gelaufen sein", "Der Torweg muss auch gelaufen sein", "Der Augenblick zieht alles nach sich, auch sich selbst", "Alles Laufenkoennen muss noch laufen", "Out of damp and gloomy days, out of solitude, out of loveless words directed at us, conclusions grow in us like fungus: one morning they are there, we do not know how, and they gaze upon us.", "Woe to the thinker who is not the gardener but only the soil of the plants that grow up in him!"];
windowFollow = Routine {
var x;
loop {
0.06.wait;
if(d.bounds != x) {
w.bounds =
Rect(d.bounds.left, d.bounds.top + d.bounds.height, d.bounds.width, w.bounds.height);
if(0.3.coin) { nietzsche.choose.postln };
w.front;
0.01.wait;
d.front;
};
x = d.bounds;
0.3.wait;
}
};
playRout = { windowFollow.play(AppClock) };
CmdPeriod.add(playRout);
playRout.value;
valMap.value(0);
)
Received on Sun Jan 01 2012 - 23:50:54 GMT