// title: simple timeline for patterns // author: 56228375 // description: // simple timeline for scheduling start and stop of patterns at specific beats // (and an experimental extension with absolute time scheduling) // code: // // 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 | self[\internallist] = self[\internallist].add(Ptpar([start_beat, Pfin(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) // // ptl.abstimenumber(abs_start_time, number, 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[\internallist] = self[\internallist].add(Ptpar([start_beat, Pfin(no_of_events, pattern)])); }, \abstimenumber : { | 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)); ptl.abstimenumber(2, 10, Pbind(\instrument, \default, \midinote, Pseq([90], inf).trace, \amp, 0.8, \dur, 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)); ptl.start(~tc); }); )