ScoreClock seems to be broken, but I was able to get it working by making a few minor changes (change every instance of server.serverRunning_ to server.statusWatcher.serverRunning_). This is a known issue, but it was filed 5 years ago with no response, so I think the repository might be abandoned. I’ll file a bug fix anyway and hopefully get it merged. It’s really a brilliant and elegant quark, and maybe could even be the canonical way of generating a Score because it can also be used on Routines or anywhere else a clock is used to schedule OSC messages.
I also came up with another solution that works specifically for ‘phrase’ Pbinds. This overwrites ScoreStreamPlayer.makeScore and inserts a special case for the ‘phrase’ type, basically copying the code from the ‘phrase’ eventType function, but instead of playing the subphrases in a new thread, it converts them to Scores and merges them with the outer Score:
+ ScoreStreamPlayer {
makeScore { | stream, duration = 1, event, timeOffset = 0 |
var ev, startTime, proto;
proto = (
server: this,
schedBundle: { | lag, offset, server ... bundle |
this.add(offset * tempo + lag + beats, bundle)
},
schedBundleArray: { | lag, offset, server, bundle |
this.add(offset * tempo + lag + beats, bundle)
}
);
event = event ?? { Event.default };
event = event.copy.putAll(proto);
beats = timeOffset;
tempo = 1;
bundleList = [];
maxTime = timeOffset + duration;
// call from a routine so that the stream has this as clock
Routine {
thisThread.clock = this;
while {
thisThread.beats = beats;
ev = stream.next(event.copy);
(maxTime >= beats) and: { ev.notNil }
} {
ev.putAll(proto);
// added this to get scores from \phrase patterns:
if(ev[\type] === \phrase) {
var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest;
var phraseScore;
ev.use {
embeddingLevel = ~embeddingLevel ? 0; // infinite recursion catch
freq = ~freq.value;
rest = freq.isKindOf(Symbol); // check for outer rests
if(rest) { ~freq = freq };
pat = (~repository ? Pdef.all).at(~instrument);
if(pat.notNil and: { embeddingLevel < 8 })
{
pat = pat.pattern; // optimization. outer pattern takes care for replacement
// preserve information from outer pattern, but not delta.
recursionLevel = ~recursionLevel;
if(~transparency.isNil or:
{ ~transparency > (recursionLevel ? 0) }
) {
outerEvent = ev.copy;
} {
outerEvent = Event.default;
outerEvent.use {
~type = \phrase;
~recursionLevel = recursionLevel;
};
};
if(recursionLevel.notNil) {
if(recursionLevel > 0) {
// in recursion, some inner values have to be overridden
instrument = ~instrument;
pat = pat.collect { |inval|
inval.use {
~instrument = instrument;
~parent = outerEvent;
~recursionLevel = recursionLevel - 1;
};
inval
};
} {
// play pattern in the ordinary way
~type = \note;
};
} { // avoid recursion, if instrument not set.
outerEvent.put(\embeddingLevel, embeddingLevel + 1);
outerEvent.parent_(Event.parentEvents.default);
};
// maybe add a Pprotect here.
// pat.asProtected
pat = Pfindur(~sustain.value, pat);
outerEvent.put(\delta, nil); // block delta modification by Ppar
outerEvent.put(\instrument, ~synthDef ? \default);
phraseScore = pat.asScore(~sustain.value, beats, outerEvent);
bundleList = bundleList ++ phraseScore.score.drop(1).drop(-1);
} {
~type = \note;
~play.value;
}
}
} {
ev.play;
};
// original version is just this:
// ev.play;
beats = ev.delta * tempo + beats
}
}.next;
bundleList = bundleList.sort { | a, b | b[0] >= a[0] };
if((startTime = bundleList[0][0]) < 0) {
timeOffset = timeOffset - startTime;
};
// bundleList.do { | b | b[0] = b[0] + timeOffset }
^Score(bundleList.add([duration + timeOffset, [\c_set, 0, 0]]))
}
}
Now the typical way of making a Score from Pbind.asScore works on phrases (I think… needs more testing though):
(
Pdef(\phraze, { arg sustain = 1;
Pbind(
\degree, Pseq(~degree),
\dur, sustain.value / ~degree.size.max(1),
)
});
x = Pfindur(2, Pbind(
\type, \phrase,
\instrument, \phraze,
\degree, [0, 2, 4, 6],
\legato, 1
)).asScore(2, timeOffset: 0.001);
x.score.printAll;
)
Prints the correct score:
[ 0.0, [ /g_new, 1, 0, 0 ] ]
[ 0.001, [ 9, default, 1000, 0, 1, out, 0, freq, 261.6255653006, amp, 0.1, pan, 0.0 ] ]
[ 0.251, [ 15, 1000, gate, 0 ] ]
[ 0.251, [ 9, default, 1001, 0, 1, out, 0, freq, 329.62755691287, amp, 0.1, pan, 0.0 ] ]
[ 0.501, [ 15, 1001, gate, 0 ] ]
[ 0.501, [ 9, default, 1002, 0, 1, out, 0, freq, 391.99543598175, amp, 0.1, pan, 0.0 ] ]
[ 0.751, [ 15, 1002, gate, 0 ] ]
[ 0.751, [ 9, default, 1003, 0, 1, out, 0, freq, 493.88330125612, amp, 0.1, pan, 0.0 ] ]
[ 1.001, [ 15, 1003, gate, 0 ] ]
[ 1.001, [ 9, default, 1000, 0, 1, out, 0, freq, 261.6255653006, amp, 0.1, pan, 0.0 ] ]
[ 1.251, [ 15, 1000, gate, 0 ] ]
[ 1.251, [ 9, default, 1001, 0, 1, out, 0, freq, 329.62755691287, amp, 0.1, pan, 0.0 ] ]
[ 1.501, [ 15, 1001, gate, 0 ] ]
[ 1.501, [ 9, default, 1002, 0, 1, out, 0, freq, 391.99543598175, amp, 0.1, pan, 0.0 ] ]
[ 1.751, [ 15, 1002, gate, 0 ] ]
[ 1.751, [ 9, default, 1003, 0, 1, out, 0, freq, 493.88330125612, amp, 0.1, pan, 0.0 ] ]
[ 2.001, [ 15, 1003, gate, 0 ] ]
[ 2.001, [ c_set, 0, 0 ] ]