//CtkScore to Task //Converting score-based (non-realtime - NRT) examples to realtime for live performance etc. //first, review how tasks and routines work "Routines and Tasks".help; //choose "15. Sequencing with Routines and Tasks" // ------------------------------------------ // ---------------- 1 - score ----------------- // ------------------------------------------ //for conversion - a simple score example s.boot; ( var score, synth, options; // create a CtkScore to fill with notes score = CtkScore.new; synth = CtkSynthDef(\oscili, {arg freq, amp, envDur; var osc, osc2, env; osc = SinOsc.ar(freq, 0, amp); env = Line.kr(1, 0, envDur, doneAction: 0); Out.ar(0, osc * env) }); score.add(synth.note(0.2, 2.0).freq_(420).envDur_(2).amp_(0.5)); //first start time is 0.2 to allow for loading the synthdef score.add(synth.note(1.0, 2.0).freq_(440).envDur_(2).amp_(0.5)); score.add(synth.note(3.0, 2.0).freq_(440).envDur_(2).amp_(0.2)); score.add(synth.note(3.1, 2.0).freq_(548).envDur_(2).amp_(0.2)); score.add(synth.note(3.2, 2.0).freq_(332).envDur_(2).amp_(0.2)); score.add(synth.note(3.3, 2.0).freq_(656).envDur_(2).amp_(0.2)); score.add(synth.note(3.4, 2.0).freq_(424).envDur_(2).amp_(0.2)); score.add(synth.note(3.5, 2.0).freq_(564).envDur_(2).amp_(0.2)); score.add(synth.note(3.6, 2.0).freq_(816).envDur_(2).amp_(0.2)); score.add(synth.note(5.1, 2.0).freq_(412).envDur_(2).amp_(0.2)); score.play; ) // -------------------------------------------- // ---------------- 1 - realtime ----------------- // -------------------------------------------- //now, to convert it to realtime, we need to //- schdule notes using a Task //- .wait (or .yield) between events/notes; we need to provide time differences, as opposed to absolute times since the start of the piece //- play notes right away (start time = 0 and ".play" it) s.reboot; ( var task, synth, options; synth = CtkSynthDef(\oscili, {arg freq, amp, envDur; var osc, osc2, env; osc = SinOsc.ar(freq, 0, amp); env = Line.kr(1, 0, envDur, doneAction: 0); Out.ar(0, osc * env) }); task = Task({ 0.2.wait; //wait for 0.2 seconds; same as 0.2.yield synth.note(0, 2.0).freq_(420).envDur_(2).amp_(0.5).play; //play the note right away 0.8.wait; //wait synth.note(0, 2.0).freq_(440).envDur_(2).amp_(0.5).play; //play etc. 2.wait; synth.note(0, 2.0).freq_(440).envDur_(2).amp_(0.2).play; 0.1.wait; synth.note(0, 2.0).freq_(548).envDur_(2).amp_(0.2).play; 0.1.wait; synth.note(0, 2.0).freq_(332).envDur_(2).amp_(0.2).play; 0.1.wait; synth.note(0, 2.0).freq_(656).envDur_(2).amp_(0.2).play; 0.1.wait; synth.note(0, 2.0).freq_(424).envDur_(2).amp_(0.2).play; 0.1.wait; synth.note(0, 2.0).freq_(564).envDur_(2).amp_(0.2).play; 0.1.wait; synth.note(0, 2.0).freq_(816).envDur_(2).amp_(0.2).play; 1.5.wait; synth.note(0, 2.0).freq_(412).envDur_(2).amp_(0.2).play; }); task.play; //play the task ) // ------------------------------------------ // ---------------- 2 - score ----------------- // ------------------------------------------ //when you use functions to generate notes, it makes it even easier to conert code from NRT to realtime //score example ( var score, synth, options, startTimes, freqs, amps; // create a CtkScore to fill with notes score = CtkScore.new; synth = CtkSynthDef(\oscili, {arg freq, amp, envDur; var osc, osc2, env; osc = SinOsc.ar(freq, 0, amp); env = Line.kr(1, 0, envDur, doneAction: 0); Out.ar(0, osc * env) }); startTimes = [0.2, 1.0, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 5.1]; freqs = [420, 440, 440, 548, 332, 656, 424, 564, 816, 412]; amps = [0.5, 0.5, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]; startTimes.size.do({arg inc; score.add(synth.note(startTimes[inc], 2.0).freq_(freqs[inc]).envDur_(2).amp_(amps[inc])); }); score.play; ) // -------------------------------------------- // ---------------- 2 - realtime----------------- // -------------------------------------------- //now converted to realtime //if needed, conversion to relative times can be done programmatically //(but often times you'll already have that data, see below) ( var task, synth, options, startTimes, startTimesRelative, freqs, amps; synth = CtkSynthDef(\oscili, {arg freq, amp, envDur; var osc, osc2, env; osc = SinOsc.ar(freq, 0, amp); env = Line.kr(1, 0, envDur, doneAction: 0); Out.ar(0, osc * env) }); startTimes = [0.2, 1.0, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 5.1]; freqs = [420, 440, 440, 548, 332, 656, 424, 564, 816, 412]; amps = [0.5, 0.5, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]; startTimesRelative = startTimes.collect({arg thisStartTime, inc; var thisRelativeStartTime, previousStartTime; previousStartTime = startTimes[inc-1]; if(previousStartTime.notNil, {//first check if we can get precious start time thisRelativeStartTime = thisStartTime - previousStartTime; //if so, subtract previous from current }, { thisRelativeStartTime = thisStartTime; //if not, treat it as the first start time, don's subtract anythin }); thisRelativeStartTime; //important - return thisRelativeStartTime, so .collect will return an array of these }); "startTimesRelative: ".post; startTimesRelative.postln; task = Task({ startTimesRelative.size.do({arg inc; "waiting ".post; startTimesRelative[inc].post; "s".postln; //since we're in realtime, we can post things as they happen startTimesRelative[inc].wait; //wait "playing sound at frequency ".post; freqs[inc].post; "Hz".postln; synth.note(0, 2.0).freq_(freqs[inc]).envDur_(2).amp_(amps[inc]).play; //startTime = 0, .play right away }); }); task.play; //play the task ) // ------------------------------------------ // ---------------- 3 - score ----------------- // ------------------------------------------ //often we think about time in relative values between items, for example: ( var score, synth, options, times, freqs, amps, now; // create a CtkScore to fill with notes score = CtkScore.new; synth = CtkSynthDef(\oscili, {arg freq, amp, envDur; var osc, osc2, env; osc = SinOsc.ar(freq, 0, amp); env = Line.kr(1, 0, envDur, doneAction: 0); Out.ar(0, osc * env) }); times = [ 0.2, 0.8, 2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 1.5 ]; //relative times freqs = [420, 440, 440, 548, 332, 656, 424, 564, 816, 412]; amps = [0.5, 0.5, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]; now = 0; //initialize variable to keep the time information times.size.do({arg inc; now = now + times[inc]; //increment time by the relative time value "now: ".post; now.postln; score.add(synth.note(now, 2.0).freq_(freqs[inc]).envDur_(2).amp_(amps[inc])); //use now as startTime }); score.play; ) // -------------------------------------------- // ---------------- 3 - realtime ----------------- // -------------------------------------------- //and then coverting to realtime is even easier ( var task, synth, options, times, freqs, amps; synth = CtkSynthDef(\oscili, {arg freq, amp, envDur; var osc, osc2, env; osc = SinOsc.ar(freq, 0, amp); env = Line.kr(1, 0, envDur, doneAction: 0); Out.ar(0, osc * env) }); times = [ 0.2, 0.8, 2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 1.5 ]; //relative times freqs = [420, 440, 440, 548, 332, 656, 424, 564, 816, 412]; amps = [0.5, 0.5, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]; //no need to have incremented "now" variable task = Task({ times.size.do({arg inc; "current time diff: ".post; times[inc].postln; times[inc].wait; //wait for a specified time synth.note(0, 2.0).freq_(freqs[inc]).envDur_(2).amp_(amps[inc]).play; //0 as startTime, and .play it }); }); task.play;//play the dask )