# «simple timeline for patterns» by56228375

on 29 Apr'20 18:20 in

simple timeline for scheduling start and stop of patterns at specific beats (and an experimental extension with absolute time scheduling)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//
// Did you ever wonder how to easily schedule starting/stopping patterns at specific times
// as part of building up and evolving a piece from a smaller building blocks?
// Here's a simple approach.
//
// It can be kept very basic because it specifies start and stop in beats,
// and not in absolute time. This means that the TempoClock used for playing the pattern
// will influence the timeline.
//
// Nevertheless, it can already be useful.
//
//
// Manual:
//
// schedule pattern to start on beat a and run until beat b with:
//
// ptl.beat(a, b, pattern)
//
// schedule pattern to start on beat a, and stop after producing N events with:
//
// ptl.number(a, N, pattern)
//
// finally, after configuring your timeline, it's time to play it. Do:
//
// ptl.asPattern.play
//
// don't pay attention to the stupid patterns in the example below, they are just
// for demoing the timeline
//

(
s.waitForBoot({
var ptl = (
\internallist : [],
\beat : {
| self, start_beat, stop_beat, pattern |
self[\internallist] = self[\internallist].add(Ptpar([start_beat, Pfindur(stop_beat - start_beat, pattern)]));
},
\number : {
| self, start_beat, no_of_events, pattern |
},
\asPattern : {
|self|
Ppar(self[\internallist]);
}
);

~bpm = 60; // at 60 beats per minute, one beat takes one second
~tc = TempoClock(~bpm/60);

// now schedule patterns in any order
ptl.beat(0, 10, // first pattern to run from beat 0 to beat 10
Pbind(\instrument, \default, \midinote, Pseq([65], inf).trace, \amp, 0.8, \dur, 1));
ptl.beat(5.7,  7.3, // second pattern to run from beat 5.7 to beat 7.3
Pbind(\instrument, \default, \midinote, Pseq([70], inf).trace, \amp, 0.8, \dur, 0.1));
ptl.beat(3, 10, // fourth pattern to run from beat 3 and produce 10 notes
Pbind(\instrument, \default, \midinote, Pseq([53], inf).trace, \amp, 0.8, \dur, 0.3));

// don't start multiple instances of the same timeline simultaneously
if (~player.notNil) {
~player.stop;
};
~player = ptl.asPattern.play(~tc);
});
)

// here's an experimental (not very well tested) extension of the system that also supports
// scheduling using absolute time moments (start/stop time independent of tempoclock)
//
// note that the patterns scheduled with absolute time still obey the tempoclock
//
// extra calls are
//
// ptl.abstime(abs_start_time, abs_duration, pattern)
//
//
// other changes:
//
// asPattern call is removed and timeline self-manages its players
// ptl is now equipped with a "start" and "end" api call to start/end playing

(
s.waitForBoot({
var ptl = (
\internalcounter : 0,
\internalnewcounter : {
| self |
var cnt = self[\internalcounter];
self[\internalcounter] = self[\internalcounter] + 1;
cnt;
},
\internallist : [],
\abssched : [],
\internalplayers : (),
\beat : {
| self, start_beat, stop_beat, pattern |
self[\internallist] = self[\internallist].add(Ptpar([start_beat, Pfindur(stop_beat - start_beat, pattern)]));
},
\abstime : {
| self, delta_time, duration, pattern |
self[\abssched] = self[\abssched].add((\dt: delta_time, \dur: duration, \p : pattern, \type : \time));
},
\number : {
| self, start_beat, no_of_events, pattern |
},
| self, delta_time, no_of_events, pattern |
self[\abssched] = self[\abssched].add((\dt: delta_time, \dur: no_of_events, \p : pattern, \type : \number));
},
\start : {
|self, tempoclock |
if (tempoclock.isNil) {
tempoclock = TempoClock.default;
};
self[\abssched].do({
|el|
var cnt = self.internalnewcounter;
var type = el.type;
if (type == \time) {
SystemClock.sched(el.dt, { self[\internalplayers][cnt.asSymbol] = el.p.play(tempoclock); nil});
SystemClock.sched(el.dt + el.dur, { self[\internalplayers][cnt.asSymbol].stop; nil});
} {
SystemClock.sched(el.dt, { self[\internalplayers][cnt.asSymbol] = Pfin(el.dur, el.p).play(tempoclock); nil});
};
});
self[\internalplayers][self.internalnewcounter.asSymbol] = Ppar(self[\internallist]).play(tempoclock);
},
\end : {
| self |
self[\internalplayers].keysValuesDo({
| key, value |
value.stop;
});
self[\internalplayers] = ();
};
);

~bpm = 120; // at 60 beats per minute, one beat takes one second
~tc = TempoClock(~bpm/60);

// now schedule patterns in any order
ptl.beat(0, 10, // first pattern to run from beat 0 to beat 10
Pbind(\instrument, \default, \midinote, Pseq([65], inf).trace, \amp, 0.8, \dur, 1));
ptl.beat(5.7,  7.3, // second pattern to run from beat 5.7 to beat 7.3
Pbind(\instrument, \default, \midinote, Pseq([70], inf).trace, \amp, 0.8, \dur, 0.1));
)