From 465c97c8d2e588904666b96efc4d213c8e224ac7 Mon Sep 17 00:00:00 2001 From: Julian Rohrhuber Date: Tue, 4 Jan 2022 21:53:38 +0100 Subject: [PATCH 1/3] cosmetics and update of outdated method calls --- Ch 8 Object Modeling/Ch8codefigures1.scd | 182 +++++++++--------- Ch 8 Object Modeling/Ch8codefigures2.scd | 195 +++++++++---------- Ch 8 Object Modeling/ObjMod1_shout_qcd.scd | 214 ++++++++++----------- 3 files changed, 293 insertions(+), 298 deletions(-) diff --git a/Ch 8 Object Modeling/Ch8codefigures1.scd b/Ch 8 Object Modeling/Ch8codefigures1.scd index 6bec58d..8a95003 100644 --- a/Ch 8 Object Modeling/Ch8codefigures1.scd +++ b/Ch 8 Object Modeling/Ch8codefigures1.scd @@ -1,16 +1,16 @@ -/////////////////////// Object Modeling code figures /////////////////// +/////////////////////// Object Modeling code figures /////////////////// // figure 8.1 - a Puppet class, and tests for it. - -Puppet { + +Puppet { var <>myfreq; // an instance variable with a getter and a setter method - + // a method for creating a new object of this kind *new { |myfreq=50| ^super.new.myfreq_(myfreq) } - + // a simple method that uses 'myfreq' for something audible. blip { { Blip.ar(myfreq, 11) * XLine.kr(1, 0.01, 0.6, doneAction: 2) }.play; } } @@ -35,7 +35,7 @@ m.blip; // should sound differently m = (); // make an empty event m.myfreq_(50); // put something in it with a setter method: a pseudo-instance variable m.myfreq; // look it up with a getter method - // put a function into it with a setter: + // put a function into it with a setter: // this becomes a pseudo-method m.blip_({ |ev| { Blip.ar(ev.myfreq, 11) * XLine.kr(1, 0.01, 0.6, doneAction: 2) }.play; }); m.blip; // execute the function with a pseudo-method call (same name) @@ -49,9 +49,9 @@ m.blip; // execute the function with a pseudo-method call (same name) m.numHarms_(20); // a new instvar m.decay_(0.3); // and another // update the blip method to use them: -m.blip_({ |ev| - { Blip.ar(ev.myfreq, ev.numHarms) - * XLine.kr(1, 0.01, ev.decay, doneAction: 2) }.play; +m.blip_({ |ev| + { Blip.ar(ev.myfreq, ev.numHarms) + * XLine.kr(1, 0.01, ev.decay, doneAction: 2) }.play; }); ) m.blip; // test @@ -83,8 +83,8 @@ z.win.close; // close when done ( z.makeWin = { |z, message="Shout this!"| z.win = Window("Shout", Rect(0, 900,1200, 100)).front; - z.win.alpha_(0.7); - z.win.view.background_(Color.clear); + z.win.alpha_(0.7); + z.win.view.background_(Color.clear); z.win.alwaysOnTop_(true); z.txtView = TextView(z.win, Rect(0, 0,1200, 100)); @@ -93,8 +93,8 @@ z.makeWin = { |z, message="Shout this!"| z.txtView.background_(Color.clear); }; ) -z.makeWin; -z.makeWin("Try showing that."); +z.makeWin; +z.makeWin("Try showing that."); @@ -124,17 +124,17 @@ z.shout("Do we get this too?"); // also when window has closed? z.txtView.stringColor_(Color.red); // try a single color ( -z.animate = { |z, dt=0.2, n = 6| +z.animate = { |z, dt=0.2, n = 6| var colors = [Color.red, Color.green, Color.black]; - Task { - n.do { |i|Ê - dt.wait; + Task { + n.do { |i| + dt.wait; z.txtView.stringColor_(colors.wrapAt(i)) } }.play(AppClock) }; ) -z.animate; // test with default values +z.animate; // test with default values z.animate(0.1, 24); // and test with arguments given @@ -147,7 +147,7 @@ z.animate(0.1, 24); // and test with arguments given - // figure 8.9 - using codeDump to shout + // figure 8.9 - using codeDump to shout this.codeDump = { |str, result, func| [str, result, func].printAll }; @@ -164,22 +164,22 @@ this.codeDump = { |str| if (str.beginsWith(z.shoutTag)) { z.shout(str.drop(z.sho // figure 8.10 - updated setMessage flashes text. ( -z.setMessage = { |z, str| - var messSize = str.size; +z.setMessage = { |z, str| + var messSize = str.size; var fontsize = (1.64 * z.txtView.bounds.width) / max(messSize, 32); z.txtView.font_(GUI.font.new("Monaco", fontsize)); z.txtView.string_(str); z.animate; }; ) -//!! a long comment gets scaled down to a rather smaller font size, minimally fontsize 32! +//!! a long comment gets scaled down to a rather smaller font size, minimally fontsize 32! //!! short is big! ( z.makeWin = { |q, message="Shout this!"| z.win = Window("Shout", Rect(0, 900,1200, 100)).front; - z.win.alpha_(0.7); - z.win.view.background_(Color.clear); + z.win.alpha_(0.7); + z.win.view.background_(Color.clear); z.win.alwaysOnTop_(true); z.txtView = TextView(z.win, Rect(0, 0,1200, 100)); @@ -200,16 +200,16 @@ z.makeWin("shout."); Shout { classvar <>tag="//!!"; var tag="//!!", <>width=1250, <>defaultCodeDumpFunc; + classvar <>tag="//!!", <>width=1250, <>defaultCodeDumpFunc; var myfreq; // an instance variable with a getter and a setter method - + // a method for creating a new object of this kind *new { |myfreq=50| ^super.new.myfreq_(myfreq) } - + // a simple method that uses 'myfreq' for something audible. - blip { { Blip.ar(myfreq, 11) * XLine.kr(1, 0.01, 0.6, doneAction: 2) }.play; } + blip { { Blip.ar(myfreq, 11) * XLine.kr(1, 0.01, 0.6, doneAction: 2) }.play } } // tests for the behavior implemented so far: @@ -56,12 +56,12 @@ m.blip; // should sound differently // figure 8.2 - a puppet modeled as an event. -m = (); // make an empty event +m = (); // make an empty event m.myfreq_(50); // put something in it with a setter method: a pseudo-instance variable -m.myfreq; // look it up with a getter method - // put a function into it with a setter: +m.myfreq; // look it up with a getter method + // put a function into it with a setter: // this becomes a pseudo-method -m.blip_({ |ev| { Blip.ar(ev.myfreq, 11) * XLine.kr(1, 0.01, 0.6, doneAction: 2) }.play; }); +m.blip_({ |ev| { Blip.ar(ev.myfreq, 11) * XLine.kr(1, 0.01, 0.6, doneAction: 2) }.play }); m.blip; // execute the function with a pseudo-method call (same name) @@ -70,11 +70,11 @@ m.blip; // execute the function with a pseudo-method call (same name) // figure 8.3 - add more instance variables, change the blip method. ( m.numHarms_(20); // a new instvar -m.decay_(0.3); // and another +m.decay_(1); // and another // update the blip method to use them: -m.blip_({ |ev| - { Blip.ar(ev.myfreq, ev.numHarms) - * XLine.kr(1, 0.01, ev.decay, doneAction: 2) }.play; +m.blip_({ |ev| + { Blip.ar(ev.myfreq, ev.numHarms) + * XLine.kr(1, 0.01, ev.decay, doneAction: 2) }.play; }); ) m.blip; // test @@ -108,8 +108,8 @@ z.win.close; // close when done ( z.makeWin = { |z, message="Shout this!"| z.win = Window("Shout", Rect(0, 900,1200, 100)).front; - z.win.alpha_(0.7); - z.win.view.background_(Color.clear); + z.win.alpha_(0.7); + z.win.view.background_(Color.clear); z.win.alwaysOnTop_(true); z.txtView = TextView(z.win, Rect(0, 0,1200, 100)); @@ -118,8 +118,8 @@ z.makeWin = { |z, message="Shout this!"| z.txtView.background_(Color.clear); }; ) -z.makeWin; -z.makeWin("Try showing that."); +z.makeWin; +z.makeWin("Try showing that."); @@ -132,7 +132,7 @@ z.setMessage = { |z, str| z.txtView.string_(str) }; z.setMessage("Does this update?"); // test ( z.shout = { |z, str| - if (z.win.isNil or: { z.win.isClosed }) { z.makeWin }; + if(z.win.isNil or: { z.win.isClosed }) { z.makeWin }; z.setMessage(str); }; ) @@ -149,30 +149,30 @@ z.shout("Do we get this too?"); // also when window has closed? z.txtView.stringColor_(Color.red); // try a single color ( -z.animate = { |z, dt=0.2, n = 6| +z.animate = { |z, dt=0.2, n = 6| var colors = [Color.red, Color.green, Color.black]; - Task { - n.do { |i|Ê - dt.wait; + Task { + n.do { |i| + dt.wait; z.txtView.stringColor_(colors.wrapAt(i)) } }.play(AppClock) }; ) -z.animate; // test with default values +z.animate; // test with default values z.animate(0.1, 24); // and test with arguments given - // figure 8.8 is an image // + // figure 8.8 is an image // - // figure 8.9 - using codeDump to shout + // figure 8.9 - using codeDump to shout this.codeDump = { |str, result, func| [str, result, func].printAll }; @@ -189,22 +189,22 @@ this.codeDump = { |str| if (str.beginsWith(z.shoutTag)) { z.shout(str.drop(z.sho // figure 8.10 - updated setMessage flashes text. ( -z.setMessage = { |z, str| - var messSize = str.size; +z.setMessage = { |z, str| + var messSize = str.size; var fontsize = (1.64 * z.txtView.bounds.width) / max(messSize, 32); z.txtView.font_(GUI.font.new("Monaco", fontsize)); z.txtView.string_(str); z.animate; }; ) -//!! a long comment gets scaled down to a rather smaller font size, minimally fontsize 32! +//!! a long comment gets scaled down to a rather smaller font size, minimally fontsize 32! //!! short is big! ( z.makeWin = { |q, message="Shout this!"| z.win = Window("Shout", Rect(0, 900,1200, 100)).front; - z.win.alpha_(0.7); - z.win.view.background_(Color.clear); + z.win.alpha_(0.7); + z.win.view.background_(Color.clear); z.win.alwaysOnTop_(true); z.txtView = TextView(z.win, Rect(0, 0,1200, 100)); @@ -218,7 +218,7 @@ z.makeWin("shout."); // class files can be put into one of these locations: -Platform.userExtensionDir; +Platform.userExtensionDir; Platform.systemExtensionDir; @@ -228,16 +228,16 @@ Platform.systemExtensionDir; Shout { classvar <>tag="//!!"; var tag="//!!", <>width=1250, <>defaultCodeDumpFunc; + classvar <>tag="//!!", <>width=1250, <>defaultCodeDumpFunc; var Date: Wed, 5 Jan 2022 18:36:15 +0100 Subject: [PATCH 2/3] add gaussian curve for grain shape and width parameter --- Ch 8 Object Modeling/Ch8codefigures2.scd | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Ch 8 Object Modeling/Ch8codefigures2.scd b/Ch 8 Object Modeling/Ch8codefigures2.scd index 64bf04f..f9dbe15 100644 --- a/Ch 8 Object Modeling/Ch8codefigures2.scd +++ b/Ch 8 Object Modeling/Ch8codefigures2.scd @@ -3,9 +3,10 @@ // figure 8.23 - two granular synthdefs and tests ( // a gabor (approx. gaussian-shaped) grain -SynthDef(\gab1st, { |out, amp=0.1, freq=440, sustain=0.01, pan| +SynthDef(\gab1st, { |out, amp=0.1, freq=440, sustain=0.01, width = 0.3, pan| var snd = FSinOsc.ar(freq); - var env = EnvGen.ar(Env.sine(sustain, amp * AmpComp.ir(freq) * 0.5), doneAction: 2); + var env = LFGauss.ar(sustain, width, loop: 0, doneAction: 2).range; + snd = snd * amp * AmpComp.ir(freq) * 0.5; OffsetOut.ar(out, Pan2.ar(snd * env, pan)); }, \ir ! 5).add; @@ -60,6 +61,7 @@ Tdef(\cloud0) ampRange: [0.1, 1], durRange: [0.001, 0.01], densRange: [1, 1000], + widthRange: [0.1, 0.3], panRange: [-1.0, 1.0] ) ); @@ -73,6 +75,7 @@ Tdef(\cloud0, { |e| "/s_new", e.synName ? \gab1st, -1, 0, 0, \freq, exprand(e.current.freqRange[0], e.current.freqRange[1]), + \width, exprand(e.current.widthRange[0], e.current.widthRange[1]), \amp, exprand(e.current.ampRange[0], e.current.ampRange[1]) * e.vol, \sustain, exprand(e.current.durRange[0], e.current.durRange[1]), \pan, rrand(e.current.panRange[0], e.current.panRange[1]) @@ -101,6 +104,7 @@ Tdef(\cloud0).envir.current.put('densRange', [ 30, 30 ]); // synchronous d = Tdef(\cloud0).envir; d.current.put('freqRange', [ 800, 1200 ]); d.current.put('durRange', [ 0.02, 0.02 ]); +d.current.put('durRange', [ 0.001, 0.08 ]); d.current.put('ampRange', [ 0.1, 0.1 ]); @@ -108,9 +112,10 @@ d.current.put('panRange', [ 1.0, 1.0 ]); d.current.put('panRange', [ -1.0, 1.0 ]); d.current.put('densRange', [ 30, 60 ]); +d.current.put('widthRange', [ 0.001, 0.7 ]); + d.synName = \percSin; d.synName = \gab1st; -d.current.put('durRange', [ 0.001, 0.08 ]); From c30d12eea009fde610ce09ed43819f1eb192d781 Mon Sep 17 00:00:00 2001 From: Alberto de Campo Date: Wed, 25 Jun 2025 22:17:21 +0200 Subject: [PATCH 3/3] update microsound chapter --- Ch 16 Microsound/CloudGenMini.scd | 407 ++++++++++++++++++ .../c16_micro_figures1_perception.scd | 82 ++-- .../c16_micro_figures2_anatomy.scd | 133 +++--- .../c16_micro_figures3_grainugens.scd | 42 +- .../c16_micro_figures4_synthesis.scd | 86 ++-- .../c16_micro_figures5_pulsar.scd | 41 +- .../c16_micro_figures5b_pulsar_sieves.scd | 151 +++++++ .../c16_micro_figures6_time_constQ.scd | 57 +-- .../c16_micro_figures7_waveset.scd | 332 +++++++------- .../c16_micro_figures7_waveset_work.scd | 390 +++++++++++++++++ .../wavesets/Wavesets.html | 358 --------------- .../extensionsMicrosound/wavesets/Wavesets.sc | 245 ----------- 12 files changed, 1347 insertions(+), 977 deletions(-) create mode 100755 Ch 16 Microsound/CloudGenMini.scd mode change 100644 => 100755 Ch 16 Microsound/c16_micro_figures1_perception.scd mode change 100644 => 100755 Ch 16 Microsound/c16_micro_figures2_anatomy.scd mode change 100644 => 100755 Ch 16 Microsound/c16_micro_figures3_grainugens.scd mode change 100644 => 100755 Ch 16 Microsound/c16_micro_figures4_synthesis.scd mode change 100644 => 100755 Ch 16 Microsound/c16_micro_figures5_pulsar.scd create mode 100644 Ch 16 Microsound/c16_micro_figures5b_pulsar_sieves.scd mode change 100644 => 100755 Ch 16 Microsound/c16_micro_figures6_time_constQ.scd mode change 100644 => 100755 Ch 16 Microsound/c16_micro_figures7_waveset.scd create mode 100755 Ch 16 Microsound/c16_micro_figures7_waveset_work.scd delete mode 100644 Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.html delete mode 100755 Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.sc diff --git a/Ch 16 Microsound/CloudGenMini.scd b/Ch 16 Microsound/CloudGenMini.scd new file mode 100755 index 0000000..6d4f82e --- /dev/null +++ b/Ch 16 Microsound/CloudGenMini.scd @@ -0,0 +1,407 @@ +// CloudGenMini is based on CloudGenerator, a granular synthesis program +// by Curtis Roads and John Alexander. +// This partial miniature version was implemented by Alberto de Campo, 2007, +// and updated for SC book 2022. +// see also chapter 8, Object modeling, +// for more discussion on implementation details. + + + + + // figure 8.23 - some granular synthdefs and tests +( + // a gabor (approx. gaussian-shaped) grain +SynthDef(\gab1st, { |out, amp=0.1, freq=440, sustain=0.01, pan| + var snd = FSinOsc.ar(freq); + var env = EnvGen.ar(Env.sine(sustain, amp * AmpComp.ir(freq) * 0.5), doneAction: 2); + OffsetOut.ar(out, Pan2.ar(snd * env, pan)); +}, \ir ! 5).add; + + // wider, quasi-gaussian envelope, with a hold time in the middle. +SynthDef(\gabWide, { |out, amp=0.1, freq=440, sustain=0.01, pan, width=0.5| + var holdT = sustain * width; + var fadeT = 1 - width * sustain * 0.5; + var snd = FSinOsc.ar(freq); + var env = EnvGen.ar(Env([0, 1, 1, 0], [fadeT, holdT, fadeT], \sin), + levelScale: amp * AmpComp.ir(freq) * 0.5, + doneAction: 2); + OffsetOut.ar(out, Pan2.ar(snd * env, pan)); +}, \ir ! 5).add; + + // a simple percussive envelope +SynthDef(\percSin, { |out, amp=0.1, freq=440, sustain=0.01, pan| + var snd = FSinOsc.ar(freq); + var env = EnvGen.ar( + Env.perc(0.1, 0.9, amp * AmpComp.ir(freq) * 0.5), + timeScale: sustain, doneAction: 2 + ); + OffsetOut.ar(out, Pan2.ar(snd * env, pan)); +}, \ir ! 5).add; + + // a reversed percussive envelope +SynthDef(\percSinRev, { |out, amp=0.1, freq=440, sustain=0.01, pan| + var snd = FSinOsc.ar(freq); + var env = EnvGen.ar( + Env.perc(0.9, 0.1, amp * AmpComp.ir(freq) * 0.5, 4), + timeScale: sustain, doneAction: 2 + ); + OffsetOut.ar(out, Pan2.ar(snd * env, pan)); +}, \ir ! 5).add; + + // a noise band grain with percussive envelope +SynthDef(\percNoise, { |out, amp=0.2, freq=440, sustain=0.01, pan, rq=0.1| + var snd = BPF.ar(GrayNoise.ar, freq, rq, 3); + var env = EnvGen.ar(Env.perc, timeScale: sustain, doneAction: 2); + OffsetOut.ar(out, + Pan2.ar(snd * env, pan, amp) + ); +}, \ir ! 6).add; + +/* + // tests for the synthdefs: +Synth(\gab1st); +Synth(\gabWide); +Synth(\percSin); +Synth(\percSinRev); + +Synth(\percSin, [\amp, 0.2, \sustain, 0.1]); +Synth(\percNoise, [\amp, 0.2, \sustain, 0.1]); +Synth(\percNoise, [\amp, 0.2, \freq, 2000, \sustain, 0.1]); + +Synth(\gab1st, [\out, 0, \amp, 0.2, \freq, 2000, \sustain, 0.05] ); +Synth(\gab1st, [\out, 0, \amp, 0.2, \freq, 20, \sustain, 0.05] ); +*/ +); + + + + + + // figure 8.24 - global setup and a player Tdef for the cloud. +( +q = q ? (); + + // some globals +q.paramRNames = [\freqRange, \durRange, \densRange, \ampRange, \panRange]; +q.paramNames = [\freq, \grDur, \dens, \amp, \pan]; +q.syndefNames = [\gab1st, \gabWide, \percSin, \percSinRev, \percNoise]; + + // specs for some parameters +Spec.add(\xfadeTime, [0.001, 1000, \exp]); +Spec.add(\ring, [0.03, 30, \exp]); +Spec.add(\grDur, [0.0001, 1, \exp]); +Spec.add(\dens, [1, 1000, \exp]); + + // make an empty tdef that plays it, + // and put the cloud parameter ranges in the tdef's environment +Tdef(\cloud0) + .set( + \synName, \gab1st, + \vol, 0.25, + \current, ( + freqRange: [200, 2000], + ampRange: [0.1, 1], + durRange: [0.001, 0.01], + densRange: [1, 1000], + panRange: [-1.0, 1.0] + ) +); + + // make the tdef that plays the cloud of sound particles here, + // based on parameter range settings. +Tdef(\cloud0, { |e| + + loop { + s.sendBundle(s.latency, [ + "/s_new", e.synName ? \gab1st, + -1, 0, 0, + \freq, exprand(e.current.freqRange[0], e.current.freqRange[1]), + \amp, exprand(e.current.ampRange[0], e.current.ampRange[1]) * e.vol, + \sustain, exprand(e.current.durRange[0], e.current.durRange[1]), + \pan, rrand(e.current.panRange[0], e.current.panRange[1]) + ]); + exprand(e.current.densRange[0].reciprocal, e.current.densRange[1].reciprocal).wait; + } +}).quant_(0); +); + + + + +/* + // figure 8.25 - tests for the cloud + +Tdef(\cloud0).play; + + // try changing various things from outside the loop. + // change its playing settings + +Tdef(\cloud0).envir.current.put('densRange', [ 50, 200 ]); // dense, async +Tdef(\cloud0).envir.current.put('densRange', [ 1, 10 ]); // sparse, async +Tdef(\cloud0).envir.current.put('densRange', [ 30, 30 ]); // synchronous + + // for faster access, call the tdef's envir d +d = Tdef(\cloud0).envir; +d.current.put('freqRange', [ 800, 1200 ]); +d.current.put('durRange', [ 0.02, 0.02 ]); + +d.current.put('ampRange', [ 0.1, 0.1 ]); + +d.current.put('panRange', [ 1.0, 1.0 ]); +d.current.put('panRange', [ -1.0, 1.0 ]); + +d.current.put('densRange', [ 30, 60 ]); +d.synName = \percSin; +d.synName = \gab1st; +d.synName = \gabWide; +d.synName = \percSinRev; +d.synName = \percNoise; +d.synName = \percSinRev; +d.synName = \gab1st; +d.current.put('durRange', [ 0.001, 0.08 ]); + + +*/ + + + + + // figure 8.26 - making random settings, and 8 random presets to switch between +( + // make the Tdef's envir a global variable for easier experimenting +d = Tdef(\cloud0).envir; + // a pseudo-method to make random settings, kept in the Tdef's environment + // randomize could also do limited variation on existing setting. +d.randSet = { |d| + var randSet = (); + q.paramRNames.do { |pName, i| + randSet.put(pName, + q.paramNames[i].asSpec.map([1.0.rand, 1.0.rand].sort) + ); + }; + randSet; +}; + +/* test randSet: +d.current = d.randSet; +*/ + +// make 8 sets of parameter range settings: +d.setNames = (1..8).collect { |i| ("set" ++ i).asSymbol }; +d.setNames.do { |key| d[key] = d.randSet; } + +/* test switching to the random presets +d.current = d.set1.copy; // copy to avoid writing into a stored setting when it is current. +d.current = d.set3.copy; +d.current = d.set8.copy; +*/ +); + + + + + // ex. 8.27 - crossfading between different settings with a taskproxy + +( + // and some parameters for controlling the fade +d.stopAfterFade = false; +d.xfadeTime = 5; + +d.morphtask = TaskProxy({ + var startSet = d[\current], endSet = d[\target]; + var stepsPerSec = 20; + var numSteps = d.xfadeTime * stepsPerSec; + var blendVal, morphSettings; + + if (d.target.notNil) { + (numSteps).do { |i| + // ["numSteps", i].postln; + blendVal = (i + 1) / numSteps; + morphSettings = endSet.collect({ |val, key| + (startSet[key] ? val).blend(val, blendVal) + }); + d.current_(morphSettings); + (1/stepsPerSec).wait; + }; + d.current_(d.target.copy); + "morph done.".postln; + if (d.stopAfterFade) { Tdef(\cloud0).stop; }; + }; +}).quant_(0); // no quantization so the task starts immediately + +/* test morphing +( +Tdef(\cloud0).play; +d.target = d.set6.copy; +d.morphtask.play; +) +Tdef(\cloud0).stop; + + // playing a finite cloud with tendency mask: +( +Tdef(\cloud0).play; // begin playing +d.stopAfterFade = true; // end cloud when crossfade ends +d.xfadeTime = 10; // set fade time +d.target = d.set8.copy; // and target +d.morphtask.play; // and start crossfade. +) +*/ + + // put fading into its own method, with optional stop. +d.fadeTo = { |d, start, end, time, autoStop| + d.current = d[start] ? d.current; + d.target = d[end]; + d.xfadeTime = time ? d.xfadeTime; + if (autoStop.notNil) { d.stopAfterFade = autoStop }; + d.morphtask.stop.play; +}; + +/* // tests fadeTo: +Tdef(\cloud0).play; +d.fadeTo(\current, \set2, 20); +d.fadeTo(\current, \set6, 10); +d.fadeTo(\current, \set5, 3, true); + +Tdef(\cloud0).play; +d.fadeTo(\current, \set1, 3, false); +*/ +); + + + + // figure 8.28 - screenshot of the CloudGenMini GUI // + + + + + // figure 8.29 - a lightweight graphical user interface for CloudGenMini +( +q.makeCloudGui = { |q, tdef, posPoint| + var w, ezRangers, fdBox; + var setMinis, skipjack; + + posPoint = posPoint ? 400@400; // where to put the gui window + + w = Window.new("CloudGenMini", + Rect.fromPoints(posPoint, (posPoint + (400@320)))).front; + w.view.decorator_(FlowLayout(w.bounds.copy.moveTo(0, 0))); + + w.view.decorator.nextLine; + // a JIT-Gui for the Tdef + TdefGui(tdef, 0, parent: w, bounds: 390@20); + +/* Some extras: + a volume slider for simple mixing, + a popup menu for switching syndefnames; + a button to stop/start the skipjack for refreshing, + so one can use numberboxes to enter values. +*/ + EZSlider(w, 245@20, "vol", \amp, { |sl|tdef.set(\vol, sl.value) }, + 0.25, false, 20, 36); + + StaticText.new(w, 55@20).string_("synthdef:").align_(\right); + PopUpMenu.new(w, Rect(0,0,80,20)) + .items_([\gab1st, \gabWide, \percSin, \percSinRev, \percNoise]) + .action_({ |pop| tdef.envir.synName = pop.items[pop.value] }); + + w.view.decorator.nextLine; + + Button.new(w, 90@20).states_([[\randomize]]) + .action_({ + tdef.envir.target_(d.randSet); + tdef.envir.morphtask.stop.play; + }); + + fdBox = EZNumber.new(w, 90@20, \fadeTime, [0, 100, \amp, 1], + { |nbx| tdef.envir.xfadeTime = nbx.value }, + tdef.envir.xfadeTime, false, 60, 30); + + Button.new(w, 90@20).states_([[\continuous], [\fadeStops]]) + .value_(tdef.envir.stopAfterFade.binaryValue) + .action_({ |btn| + tdef.set(\stopAfterFade, btn.value == 1) + }); + + Button.new(w, 90@20).states_([[\skipWatching], [\skipWaiting]]) + .action_({ |btn| + [ { skipjack.play }, { skipjack.stop }][btn.value].value + }); + + w.view.decorator.nextLine.shift(0, 10); + + // the range sliders display the current values + ezRangers = (); + + q.paramRNames.do { |name, i| + var step = [0.1, 0.00001, 0.0001, 0.0001, 0.01][i]; + var maxDecimals = [1, 5, 4, 4, 2][i]; + var ranger = EZRanger(w, 400@20, name, q.paramNames[i], + { |sl| tdef.envir.current[name] = sl.value; }, + tdef.envir.current[name], labelWidth: 70, numberWidth: 50, + unitWidth: 10); + ranger.round_(step); + ranger.hiBox.minDecimals_(0).maxDecimals_(maxDecimals); + ranger.loBox.minDecimals_(0).maxDecimals_(maxDecimals); + + ezRangers.put(name, ranger); + }; + + + // skipjack is a task that survives cmd-period: + // used here for lazy-updating the control views. + skipjack = SkipJack({ + q.paramRNames.do { |name| ezRangers[name].value_(tdef.envir.current[name]) }; + fdBox.value_(tdef.envir.xfadeTime); + + // mark last settings that were used by color? + // a separate color when changed? + + }, 0.5, { w.isClosed }, name: tdef.key); + + w.view.decorator.nextLine.shift(0, 10); + + // make a new layoutView for the 8 presets; + // put button to switch to that preset, + // a button to save current settings to that place, + // and a miniview of the settings as a visual reminder in it. + + // make 8 setButtons + tdef.envir.setNames.do { |setname, i| + var minisliders, setMinis; + var zone = CompositeView.new(w, Rect(0,0,45, 84)); + zone.decorator = FlowLayout(zone.bounds, 0@0, 5@0); + zone.background_(Color.white); + + Button.new(zone, Rect(0,0,45,20)).states_([[setname]]) + .action_({ + // just switch: // tdef.envir.current.putAll(d[setname] ? ()) + tdef.envir.target = tdef.envir[setname]; + tdef.envir.morphtask.stop.play; + }); + + Button.new(zone, Rect(0,0,45,20)) + .states_([["save" ++ (i + 1)]]) + .action_({ + d[setname] = tdef.envir.current.copy; + setMinis.value; + }); + + minisliders = q.paramRNames.collect { |paramRname| + RangeSlider.new(zone, 45@8).enabled_(false); + }; + setMinis = { + q.paramRNames.do { |paramRname, i| + var paramName = q.paramNames[i]; + var myrange = d[setname][paramRname]; + var unmapped = paramName.asSpec.unmap(myrange); + minisliders[i].lo_(unmapped[0]).hi_(unmapped[1]); + } + }; + setMinis.value; + }; + +}; +q.makeCloudGui(Tdef(\cloud0)) +); + + diff --git a/Ch 16 Microsound/c16_micro_figures1_perception.scd b/Ch 16 Microsound/c16_micro_figures1_perception.scd old mode 100644 new mode 100755 index 2e0fb4c..a83281d --- a/Ch 16 Microsound/c16_micro_figures1_perception.scd +++ b/Ch 16 Microsound/c16_micro_figures1_perception.scd @@ -2,38 +2,38 @@ // perception at the micro time scale // - - // pulses, transition from rhythm to pitch -{ Impulse.ar (XLine.kr(12, 48, 6, doneAction: 2)) * 0.1 ! 2 }.play; // up -{ Impulse.ar (XLine.kr(48, 12, 6, doneAction: 2)) * 0.1 ! 2 }.play; // down + // pulses, transition from rhythm to pitch +{ Impulse.ar (XLine.kr(12, 48, 6)) * 0.1 ! 2 }.play; // up -{ Impulse.ar (MouseX.kr(12, 48, 1)) * 0.1 ! 2 }.play; // mouse-controlled +{ Impulse.ar (XLine.kr(48, 12, 6)) * 0.1 ! 2 }.play; // down +{ Impulse.ar (MouseX.kr(12, 48, 1)) * 0.1 ! 2 }.play; // cursor-controlled - // figure 16.1 short grain durations - pitch to colored click + + // figure 16.1 short grain durations - pitch to colored click ( // a gabor grain, gaussian-shaped envelope -SynthDef(\gabor, { |out, freq = 440, sustain = 1, pan, amp = 0.1, width = 0.25 | +SynthDef(\gabor, { |out, freq = 440, sustain = 1, pan, amp = 0.1, width = 0.25| var env = LFGauss.ar(sustain, width, loop: 0, doneAction: 2); - var son = FSinOsc.ar(freq, 0.5pi, env); - OffsetOut.ar(out, Pan2.ar(son, pan, amp)); + var snd = FSinOsc.ar(freq, 0.5pi, env); + OffsetOut.ar(out, Pan2.ar(snd, pan, amp)); -}, \ir ! 6).memStore; +}, \ir ! 6).add; // or an approximation with a sine-shaped envelope -SynthDef(\gabor1, { |out, amp=0.1, freq=440, sustain=0.01, pan| - var snd = FSinOsc.ar(freq); +SynthDef(\gabor1, { |out, amp=0.1, freq=440, sustain=0.01, pan| var env = EnvGen.ar(Env.sine(sustain, amp), doneAction: 2); + var snd = FSinOsc.ar(freq); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; ) ( Pbindef(\grain, - \instrument, \gabor, \freq, 1000, + \instrument, \gabor, \freq, 1000, \dur, 0.5, \sustain, 20/1000, \amp, 0.2 ).play; ) @@ -52,14 +52,14 @@ Pbindef(\grain, \sustain, Pbrown(1, 10, 3) / Pkey(\freq), \dur, 0.1).play - // short grains seem softer + // short grains seem softer ( -Pbindef(\grain, +Pbindef(\grain, \instrument, \gabor, \freq, 1000, \dur, 1, - [\sustain, \amp], Pseq([[0.001, 0.1], [0.1, 0.1]], inf) + [\sustain, \amp], Pseq([[0.001, 0.1], [0.1, 0.1]], inf) ).play; ) - // short grain 2x louder + // short grain 2x louder Pbindef(\grain, [\sustain, \amp], Pseq([[0.001, 0.2], [0.1, 0.1]], inf)); // short grain 4x louder @@ -69,20 +69,20 @@ Pbindef(\grain, [\sustain, \amp], Pseq([[0.001, 0.4], [0.1, 0.1]], inf)); // a grain with quasi-rectangular envelope, short grain 6x louder. ( -SynthDef(\pip, { |out, freq=440, sustain=0.02, amp=0.2, pan=0| - OffsetOut.ar(out, - Pan2.ar(SinOsc.ar(freq) +SynthDef(\pip, { |out, freq=440, sustain=0.02, amp=0.2, pan=0| + OffsetOut.ar(out, + Pan2.ar(SinOsc.ar(freq) * EnvGen.ar(Env.linen(0.0005, sustain - 0.001, 0.0005, amp), doneAction: 2), pan) - ); -}).memStore; + ); +}).add; // is this equal loudness? Pbindef(\grain).clear; -Pbindef(\grain, +Pbindef(\grain, \instrument, \pip, \freq, 1000, \dur, 1, - \sustain, Pseq([0.001, 0.1], inf), - \amp, Pseq([0.6, 0.1], inf) + \sustain, Pseq([0.001, 0.1], inf), + \amp, Pseq([0.6, 0.1], inf) ).play; ) @@ -94,46 +94,46 @@ Pbindef(\grain, p = ProxySpace.push; ~source = { SinOsc.ar * 0.1 }; -~silence = { |silDur=0.01| +~silence = { |silDur=0.01| EnvGen.ar( - Env([0, 1, 1, 0, 0, 1, 1, 0], [0.01, 2, 0.001, silDur, 0.001, 2, 0.01]), + Env([0, 1, 1, 0, 0, 1, 1, 0], [0.01, 2, 0.001, silDur, 0.001, 2, 0.01]), doneAction: 2) ! 2 }; ~listen = ~source * ~silence; ~listen.play; ) -~silence.spawn([\silDur, 0.001]); // sounds like an added pulse +~silence.spawn([\silDur, 0.001]); // sounds like an added pulse ~silence.spawn([\silDur, 0.003]); ~silence.spawn([\silDur, 0.01]); ~silence.spawn([\silDur, 0.03]); // a pause in the sound // try the same examples with noise: -~source = { WhiteNoise.ar * 0.1 }; +~source = { WhiteNoise.ar * 0.1 }; -p.clear.pop; +p.clear.pop; - // figure 16.3 - order confusion with sounds in fast succession. - // as grains move closer and closer together, their order becomes ambiguous. + // figure 16.3 - order confusion with sounds in fast succession. + // as grains move closer and closer together, their order becomes ambiguous. ( // a simple percussive envelope -SynthDef(\percSin, { |out, amp=0.1, freq=440, sustain=0.01, pan| +SynthDef(\percSin, { |out, amp=0.1, freq=440, sustain=0.01, pan| var snd = FSinOsc.ar(freq); var env = EnvGen.ar( Env.perc(0.1, 0.9, amp), timeScale: sustain, doneAction: 2); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; ) ( -Pbindef(\lo, - \instrument, \percSin, \sustain, 0.05, +Pbindef(\lo, + \instrument, \percSin, \sustain, 0.05, \freq, 250, \amp, 0.2, \dur, 0.5, \lag, 0 ).play; -Pbindef(\hi, - \instrument, \percSin, \sustain, 0.05, +Pbindef(\hi, + \instrument, \percSin, \sustain, 0.05, \freq, 875, \amp, 0.1, \dur, 0.5, \lag, 0 ).play; ) @@ -158,12 +158,12 @@ Pbindef(\hi, \pan, 0); Pbindef(\lo, \pan, 0); // figure 16.4: multiple grains fuse into one composite. // when their order changes, the sound is subtly different. ( -Pbindef(\grain4, +Pbindef(\grain4, \instrument, \percSin, \sustain, 0.03, \amp, 0.2, \freq, Pshuf([1000, 600, 350, 250]), // random every each time \dur, 0.005 ).play; - // repeat grain cluster + // repeat grain cluster Tdef(\grain, { loop { Pbindef(\grain4).play; 1.wait } }).play; ) // fixed order diff --git a/Ch 16 Microsound/c16_micro_figures2_anatomy.scd b/Ch 16 Microsound/c16_micro_figures2_anatomy.scd old mode 100644 new mode 100755 index 8249aa6..595bbca --- a/Ch 16 Microsound/c16_micro_figures2_anatomy.scd +++ b/Ch 16 Microsound/c16_micro_figures2_anatomy.scd @@ -3,9 +3,9 @@ // waveform, envelope, grain plotted -e = Env.sine.asSignal(400).as(Array); +e = Env.sine.asSignal(400).as(Array); w = Array.fill(400, { |i| (i * 2pi / 40).sin }); -g = e * w; +g = e * w; [e, w, g].flop.flat.plot("envelope, wave, grain", Rect(0,0,408,600), numChannels: 3); @@ -24,7 +24,7 @@ SynthDef(\gabor0, {|out, freq=440, sustain=0.02, amp=0.2, pan| var env = EnvGen.ar(Env.sine(sustain, amp), doneAction: 2); var sound = SinOsc.ar(freq) * env; OffsetOut.ar(out, Pan2.ar(sound, pan)) -}, \ir.dup(5)).memStore; +}, \ir.dup(5)).add; ) // test with synth Synth(\gabor0); // defaults from SynthDef @@ -36,15 +36,17 @@ Synth(\gabor0, [\freq, 1000, \sustain, 0.005, \amp, 0.1, \pan, 0.5]); (instrument: \gabor0, sustain: 0.002, freq: 1500, amp: 0.3, pan: 0.5).play; (instrument: \gabor0, sustain: 0.001, freq: 2500, amp: 0.05, pan: -0.5).play; -Synth.grain(\gabor0, [\freq, 2000, \sustain, 0.003]) // higher efficiency, as no NodeID is kept +// higher efficiency, as no NodeID is kept +Synth.grain(\gabor0, [\freq, 2000, \sustain, 0.003]) -s.sendMsg("s_new", \gabor0, -1, 0, 0, \freq, 2000, \sustain, 0.003); // even more efficient, as no Synth object is created. +// even more efficient, as no Synth object is created. +s.sendMsg("s_new", \gabor0, -1, 0, 0, \freq, 2000, \sustain, 0.003); - // figure 16.6 - making different envelope shapes + // Figure 16.6 - making different envelope shapes -Env.sine.plot; // approx. gaussian +Env.sine(0.1).test.plot; // approx. gaussian Env([0, 1, 1, 0], [0.25, 0.5, 0.25] * 0.1, \sin).test.plot; // quasi-gaussian Env([0, 1, 1, 0], [0.25, 0.5, 0.25] * 0.1, \lin).test.plot; // 3 stage line segments. Env([0, 1, 1, 0], [0.25, 0.5, 0.25] * 0.1, \welch).test.plot; // welch curve interpolation @@ -52,33 +54,33 @@ Env([1, 0.001], [0.1], \exp).test.plot; // expoDec (exponential decay); Env([0.001, 1], [0.1], \exp).test.plot; // revExpoDec (reverse exponential decay); Env.perc(0.01, 0.09).test.plot; -( // a sinc function envelope + + // figure 16.7 - a sinc function envelope as array + +( q = q ? (); -q.makeSinc = { |q, num=1, size=400| +q.makeSinc = { |q, num=1, size=400| dup({ |x| x = x.linlin(0, size-1, -pi, pi) * num; sin(x) / x }, size); -}; +}; a = q.makeSinc(6); a.plot(bounds: Rect(0,0,409,200), minval: -1, maxval: 1); ) - - - - -( // more envelopes plotted -[ Env.sine, - Env([0, 1, 1, 0], [0.33, 0.34, 0.33], \sin), + // figure 16.8 - // more envelopes plotted +( +[ Env.sine, + Env([0, 1, 1, 0], [0.33, 0.34, 0.33], \sin), Env([0, 1, 1, 0], [0.33, 0.34, 0.33], \lin), Env([0, 1, 1, 0], [0.33, 0.34, 0.33], \welch), Env([1, 0.001], [1], \exp), - Env([0.001, 1], [1], \exp), + Env([0.001, 1], [1], \exp), Env.perc(0.05, 0.95) ] .collect(_.discretize(400)) - .add(q.makeSinc(6)).clump(4).collect { |gr4, i| + .add(q.makeSinc(6)).clump(4).collect { |gr4, i| gr4.flop.flat.plot( - ["gauss, quasi-gauss, line, welch", - "expodec, rexpodec, perc, sinc" ][i], + ["gauss, quasi-gauss, line, welch", + "expodec, rexpodec, perc, sinc" ][i], Rect(420 * i + 100, 300, 408, 400), numChannels: 4) }; ) @@ -88,64 +90,64 @@ a.plot(bounds: Rect(0,0,409,200), minval: -1, maxval: 1); // figure 16.9 - SynthDefs with different envelopes ( // a gabor (approx. gaussian-shaped) grain -SynthDef(\gabor1, { |out, amp=0.1, freq=440, sustain=0.01, pan| +SynthDef(\gabor1, { |out, amp=0.1, freq=440, sustain=0.01, pan| var snd = FSinOsc.ar(freq); var amp2 = amp * AmpComp.ir(freq.max(50)) * 0.5; var env = EnvGen.ar(Env.sine(sustain, amp2), doneAction: 2); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; - // wider, quasi-gaussian envelope, with a hold time in the middle. -SynthDef(\gabWide, { |out, amp=0.1, freq=440, sustain=0.01, pan, width=0.5| + // wider, quasi-gaussian envelope, with a hold time in the middle. +SynthDef(\gabWide, { |out, amp=0.1, freq=440, sustain=0.01, pan, width=0.5| var holdT = sustain * width; var fadeT = 1 - width * sustain * 0.5; var snd = FSinOsc.ar(freq); var amp2 = amp * AmpComp.ir(freq.max(50)) * 0.5; - var env = EnvGen.ar(Env([0, 1, 1, 0], [fadeT, holdT, fadeT], \sin), - levelScale: amp2, + var env = EnvGen.ar(Env([0, 1, 1, 0], [fadeT, holdT, fadeT], \sin), + levelScale: amp2, doneAction: 2); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; - // a simple percussive envelope -SynthDef(\percSin, { |out, amp=0.1, freq=440, sustain=0.01, pan| + // a simple percussive envelope +SynthDef(\percSin, { |out, amp=0.1, freq=440, sustain=0.01, pan| var snd = FSinOsc.ar(freq); var amp2 = amp * AmpComp.ir(freq.max(50)) * 0.5; var env = EnvGen.ar( - Env.perc(0.1, 0.9, amp2), - timeScale: sustain, + Env.perc(0.1, 0.9, amp2), + timeScale: sustain, doneAction: 2); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; - // a reversed percussive envelope -SynthDef(\percSinRev, { |out, amp=0.1, freq=440, sustain=0.01, pan| + // a reversed percussive envelope +SynthDef(\percSinRev, { |out, amp=0.1, freq=440, sustain=0.01, pan| var snd = FSinOsc.ar(freq); var amp2 = amp * AmpComp.ir(freq.max(50)) * 0.5; var env = EnvGen.ar( - Env.perc(0.9, 0.1, amp2), - timeScale: sustain, + Env.perc(0.9, 0.1, amp2), + timeScale: sustain, doneAction: 2 ); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; - // an exponential decay envelope -SynthDef(\expodec, { |out, amp=0.1, freq=440, sustain=0.01, pan| + // an exponential decay envelope +SynthDef(\expodec, { |out, amp=0.1, freq=440, sustain=0.01, pan| var snd = FSinOsc.ar(freq); var amp2 = AmpComp.ir(freq.max(50)) * 0.5 * amp; var env = XLine.ar(amp2, amp2 * 0.001, sustain, doneAction: 2); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; - // a reversed exponential decay envelope -SynthDef(\rexpodec, { |out, amp=0.1, freq=440, sustain=0.01, pan| + // a reversed exponential decay envelope +SynthDef(\rexpodec, { |out, amp=0.1, freq=440, sustain=0.01, pan| var snd = FSinOsc.ar(freq); var amp2 = amp * AmpComp.ir(freq.max(50)) * 0.5; - var env = XLine.ar(amp2 * 0.001, amp2, sustain, doneAction: 2) + var env = XLine.ar(amp2 * 0.001, amp2, sustain, doneAction: 2) * (AmpComp.ir(freq) * 0.5); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 5).memStore; +}, \ir ! 5).add; ) @@ -155,8 +157,8 @@ SynthDef(\rexpodec, { |out, amp=0.1, freq=440, sustain=0.01, pan| // figure 16.10 - changing grain duration, frequency, envelope ( Pbindef(\grain0, - \instrument, \gabor1, \freq, 500, - \sustain, 0.01, \dur, 0.2 + \instrument, \gabor1, \freq, 500, + \sustain, 0.01, \dur, 0.2 ).play; ) // change grain durations @@ -191,21 +193,22 @@ Pbindef(\grain0, \instrument, Prand([\gabWide, \percSin, \percSinRev], inf)); // bonus track - adjusting phase for attack color - + ( // an expodec envelope sine grain with adjustable phase -SynthDef(\expodecPH, { |out, amp=0.1, freq=440, click=0, sustain=0.01, pan| +SynthDef(\expodecPH, { |out, amp=0.1, freq=440, click=0, sustain=0.01, pan| var snd = FSinOsc.ar(freq, click * 0.5pi); var env = XLine.ar(amp, amp * 0.001, sustain, doneAction: 2) * (AmpComp.ir(freq) * 0.5); OffsetOut.ar(out, Pan2.ar(snd * env, pan)); -}, \ir ! 6).memStore; +}, \ir ! 6).add; ) ( Pbindef(\grain0).play; -Pbindef(\grain0, - \instrument, \expodecPH, - \sustain, 0.1, +Pbindef(\grain0, + \instrument, \expodecPH, + \sustain, 0.1, \freq, [100, 300], - \click, Pseq((0..20)/20, inf) // add more and more click + // add more and more attack click + \click, Pseq((0..20)/20, inf) ).play; ) @@ -215,19 +218,19 @@ Pbindef(\grain0, // figure 16.11 - different control strategies applied to density - + ( // synchronous - regular time intervals Pbindef(\grain0).clear; Pbindef(\grain0).play; -Pbindef(\grain0, - \instrument, \expodec, +Pbindef(\grain0, + \instrument, \expodec, \freq, Pn(Penv([200, 1200], [10], \exp), inf), \dur, 0.1, \sustain, 0.06 ); ) // different fixed values Pbindef(\grain0, \dur, 0.06) // rhythm -Pbindef(\grain0, \dur, 0.035) +Pbindef(\grain0, \dur, 0.035) Pbindef(\grain0, \dur, 0.02) // fundamental frequency 50 Hz // time-changing values: accelerando/ritardando @@ -242,7 +245,7 @@ Pbindef(\grain0, \dur, 0.03 * Pwhite(0.8, 1.2)) Pbindef(\grain0, \dur, 0.03 * Pbrown(0.6, 1.4, 0.1)) // slower drift Pbindef(\grain0, \dur, 0.03 * Pwhite(0.2, 1.8)) - // average density constant, vary degree of irregularity + // average density constant, vary degree of irregularity Pbindef(\grain0, \dur, 0.02 * Pfunc({ (0.1.linrand * 3) + 0.9 })); Pbindef(\grain0, \dur, 0.02 * Pfunc({ (0.3.linrand * 3) + 0.3 })); Pbindef(\grain0, \dur, 0.02 * Pfunc({ (1.0.linrand * 3) + 0.0 })); @@ -250,7 +253,7 @@ Pbindef(\grain0, \dur, 0.02 * Pfunc({ 2.45.linrand.squared })); // very irregula ( // coupling - duration depends on freq parameter -Pbindef(\grain0, +Pbindef(\grain0, \freq, Pn(Penv([200, 1200], [10], \exp), inf), \dur, Pfunc({ |ev| 20 / ev.freq }) ); @@ -260,7 +263,7 @@ Pbindef(\grain0, Pbindef(\grain0, \freq, Pbrown(48.0, 96.0, 12.0).midicps); ( // duration depends on freq, with some variation - tendency mask -Pbindef(\grain0, +Pbindef(\grain0, \freq, Pn(Penv([200, 1200], [10], \exp), inf), \dur, Pfunc({ |ev| 20 / ev.freq * rrand(0.5, 1.5) }) ); @@ -270,8 +273,8 @@ Pbindef(\grain0, // figure 16.12 - control strategies applied to different parameters ( Pbindef(\grain0).clear; -Pbindef(\grain0, - \instrument, \expodec, +Pbindef(\grain0, + \instrument, \expodec, \freq, 200, \sustain, 0.05, \dur, 0.07 ).play; @@ -299,13 +302,13 @@ Pbindef(\grain0, \dur, 0.025 * Prand([0, 1, 1, 2, 4], inf)); // could be denser // random amplitude envelopes with Pseg ( -Pbindef(\grain0, +Pbindef(\grain0, \amp, Pseg( Pxrand([-50, -20, -30, -40] + 10, inf), // level pattern Pxrand([0.5, 1, 2, 3], inf), // time pattern Prand([\step, \lin], inf) // curve pattern ).dbamp -); +); ) // grain sustain time coupled to freq Pbindef(\grain0, \sustain, Pkey(\freq).reciprocal * 20).play; diff --git a/Ch 16 Microsound/c16_micro_figures3_grainugens.scd b/Ch 16 Microsound/c16_micro_figures3_grainugens.scd old mode 100644 new mode 100755 index 3f929f7..3d47c09 --- a/Ch 16 Microsound/c16_micro_figures3_grainugens.scd +++ b/Ch 16 Microsound/c16_micro_figures3_grainugens.scd @@ -1,11 +1,11 @@ - + // GrainSin.help example as nodeproxy - + p = ProxySpace.push; ( ~grain.play; ~grain = { arg envbuf = -1, density = 10, graindur=0.1, amp=0.2; - var pan, env, freqdev; + var pan, env, freqdev; var trig = Impulse.kr(density); pan = MouseX.kr(-1, 1); // use mouse x to control panning // use WhiteNoise and mouse y to control deviation from center @@ -14,12 +14,14 @@ p = ProxySpace.push; }; ) +p.clear.pop; + // switching envelopes while playing - + q = q ? (); // make a dict to keep things around -q.envs = (); // e.g. some envelopes +q.envs = (); // e.g. some envelopes q.bufs = (); // and some buffers // make an envelope, and convert it to a buffer q.envs.perc1 = Env([0, 1, 0], [0.1, 0.9], -4); @@ -39,8 +41,8 @@ q.bufs.perc1 = Buffer.sendCollection(s, q.envs.perc1.discretize, 1); ~grdur = 0.1; ~grain.map(\graindur, ~grdur); ~grdur = { LFNoise1.kr(1).range(0.01, 0.1) }; -~grdur = { SinOsc.kr(0.3).range(0.01, 0.1) }; -~grdur = 0.01; +~grdur = { SinOsc.kr(0.3).exprange(0.01, 0.1) }; +~grdur = { 0.01 }; // create random densities from 2 to 2 ** 6, exponentially distributed ~grdensity = { 2 ** LFNoise0.kr(1).range(0, 6) }; @@ -61,7 +63,7 @@ q.bufs.perc1 = Buffer.sendCollection(s, q.envs.perc1.discretize, 1); var freqdev = WhiteNoise.kr(MouseY.kr(0, 400)); var freq = 440 + freqdev; var moddepth = LFNoise1.kr.range(1, 10); - GrainFM.ar(2, trig, graindur, freq, modfreq, moddepth, pan, envbuf) + GrainFM.ar(2, trig, graindur, freq, modfreq, moddepth, pan, envbuf) * 0.2 }; ) @@ -74,7 +76,7 @@ q.bufs.perc1 = Buffer.sendCollection(s, q.envs.perc1.discretize, 1); var freq = 440 + freqdev; var modrange = MouseX.kr(1, 10); var moddepth = LFNoise1.kr.range(1, modrange); - GrainFM.ar(2, trig, graindur, freq, modfreq, moddepth, pan, envbuf) + GrainFM.ar(2, trig, graindur, freq, modfreq, moddepth, pan, envbuf) * 0.2 }; ) @@ -89,13 +91,13 @@ q.bufs.perc1 = Buffer.sendCollection(s, q.envs.perc1.discretize, 1); ~graindur = 0.1; ~grain = { arg envbuf = -1; - GrainFM.ar(2, ~trig.kr, ~graindur.kr, - ~freq.kr, ~modfreq.kr, ~moddepth.kr, - pan: WhiteNoise.kr, envbuf: envbuf) * 0.2 + GrainFM.ar(2, ~trig.kr, ~graindur.kr, + ~freq.kr, ~modfreq.kr, ~moddepth.kr, + pan: WhiteNoise.kr, envbufnum: envbuf).postln * 0.2 }; ~grain.play; ) - // change control ugens: + // change control ugens: ~modfreq = { ~freq.kr * LFNoise2.kr(1).range(0.5, 2.0) }; // modfreq roughly follows freq ~trig = { |dens=10| Dust.kr(dens)}; // random triggering, same density ~freq = { LFNoise0.kr(0.3).range(200, 800) }; @@ -112,15 +114,14 @@ q.bufs.perc1 = Buffer.sendCollection(s, q.envs.perc1.discretize, 1); ProxyMixer(p); +p.clear.pop; + // figure 16.14 - GrainBuf and control proxies p = ProxySpace.push; - - // figure 16.14 - GrainBuf and control proxies - -b = Buffer.read(s, "sounds/a11wlk01-44_1.aiff"); +b = Buffer.read(s, String.scDir +/+ "sounds/a11wlk01-44_1.aiff"); ( ~grain.set(\wavebuf, b.bufnum); ~trig = { |dens=10| Impulse.kr(dens) }; @@ -129,8 +130,8 @@ b = Buffer.read(s, "sounds/a11wlk01-44_1.aiff"); ~rate = { LFNoise1.kr.range(0.5, 1.5) }; ~grain = { arg envbuf = -1, wavebuf = 0; - GrainBuf.ar(2, ~trig.kr, ~graindur.kr, wavebuf, - ~rate.kr, ~filepos.kr, 2, WhiteNoise.kr, envbuf) * 0.2 + GrainBuf.ar(2, ~trig.kr, ~graindur.kr, wavebuf, + ~rate.kr, ~filepos.kr, 2, WhiteNoise.kr, envbuf).postln * 0.2 }; ~grain.play; ) @@ -143,9 +144,10 @@ b = Buffer.read(s, "sounds/a11wlk01-44_1.aiff"); ~trig = { |dens=50| Dust.kr(dens) }; c = Buffer.sendCollection(s, Env.perc(0.01, 0.99).discretize, 1); -~grain.set(\envbuf, c.bufnum); +~grain.set(\envbuf, c.bufnum); ~grain.set(\envbuf, -1); ~trig = { |dens=50| Impulse.kr(dens) }; ~graindur = 0.05; +p.clear.pop; diff --git a/Ch 16 Microsound/c16_micro_figures4_synthesis.scd b/Ch 16 Microsound/c16_micro_figures4_synthesis.scd old mode 100644 new mode 100755 index f8e4c94..ef57c1f --- a/Ch 16 Microsound/c16_micro_figures4_synthesis.scd +++ b/Ch 16 Microsound/c16_micro_figures4_synthesis.scd @@ -2,39 +2,39 @@ // FM grain as synthdef ( -SynthDef(\grainFM0, {|out, carfreq=440, modfreq=200, moddepth = 1, +SynthDef(\grainFM0, {|out, carfreq=440, modfreq=200, moddepth = 1, sustain=0.02, amp=0.2, pan| - + var env = EnvGen.ar(Env.sine(sustain, amp), doneAction: 2); var sound = SinOsc.ar(carfreq, SinOsc.ar(modfreq) * moddepth) * env; OffsetOut.ar(out, Pan2.ar(sound, pan)) -}, \ir.dup(7)).memStore; +}, \ir.dup(7)).add; ) (instrument: \grainFM0, sustain: 0.1, amp: 0.2).play; - // to use buffer envelopes: Osc1 + // to use buffer envelopes: Osc1 ( q = q ? (); q.envbuf = Buffer.sendCollection(s, Env.perc(0.1, 0.9).discretize, 1); -SynthDef(\grainFM1, {|out, envbuf, carfreq=440, modfreq=200, moddepth = 1, +SynthDef(\grainFM1, {|out, envbuf, carfreq=440, modfreq=200, moddepth = 1, sustain=0.02, amp=0.2, pan| - + var env = Osc1.ar(envbuf, sustain, doneAction: 2); var sound = SinOsc.ar(carfreq, SinOsc.ar(modfreq) * moddepth) * env; OffsetOut.ar(out, Pan2.ar(sound, pan, amp)) -}, \ir.dup(8)).memStore; +}, \ir.dup(8)).add; ) (instrument: \grainFM1, sustain: 0.1, envbuf: q.envbuf).play; ( -Pbindef(\fm, - \instrument, \grainFM1, - \carfreq, Pbrown(300, 1200, 300), - \modfreq, 200, - \modindex, Pbrown(1.0, 10.0, 2.5), - \sustain, 0.1, +Pbindef(\fm, + \instrument, \grainFM1, + \carfreq, Pbrown(300, 1200, 300), + \modfreq, 200, + \modindex, Pbrown(1.0, 10.0, 2.5), + \sustain, 0.1, \dur, 0.1, \envbuf, q.envbuf, \pan, Pwhite(-0.8, 0.8) @@ -50,25 +50,25 @@ q = q ? (); q.envbuf = Buffer.sendCollection(s, Env.perc(0.1, 0.9).discretize, 1); q.apollo = Buffer.read(s,"sounds/a11wlk01.wav"); -SynthDef(\grainBuf1, {|out, envbuf, wavebuf, filepos, rate=1, +SynthDef(\grainBuf1, {|out, envbuf, wavebuf, filepos, rate=1, sustain=0.02, amp=0.2, pan| - + var env = Osc1.ar(envbuf, sustain, doneAction: 2); - var sound = PlayBuf.ar(1, wavebuf, + var sound = PlayBuf.ar(1, wavebuf, rate * BufRateScale.ir(wavebuf), 1, - startPos: BufFrames.ir(wavebuf) * filepos) + startPos: BufFrames.ir(wavebuf) * filepos) * env; - + OffsetOut.ar(out, Pan2.ar(sound, pan, amp)) -}, \ir.dup(8)).memStore; +}, \ir.dup(8)).add; ) (instrument: \grainBuf1, sustain: 0.1, envbuf: q.envbuf, wavebuf: q.apollo).play; ( -Pdef(\buf1, +Pdef(\buf1, Pbind( - \instrument, \grainBuf1, + \instrument, \grainBuf1, \envbuf, q.envbuf, \wavebuf, q.apollo, \sustain, 0.1, \dur, 0.05, \pan, Pwhite(-0.8, 0.8), @@ -84,23 +84,23 @@ q.envbuf.sendCollection(Env.perc.discretize); // figure 16.15 - Glisson synthesis ( -SynthDef("glisson", +SynthDef("glisson", { arg out = 0, envbuf, freq=800, freq2=1200, sustain=0.001, amp=0.2, pan = 0.0; var env = Osc1.ar(envbuf, sustain, 2); var freqenv = XLine.ar(freq, freq2, sustain); - OffsetOut.ar(out, + OffsetOut.ar(out, Pan2.ar(SinOsc.ar(freqenv) * env, pan, amp) ) -}, \ir!7).memStore; +}, \ir!7).add; ) ( Tdef(\gliss0, { |e| 100.do({ arg i; - s.sendBundle(s.latency, ["/s_new", "glisson", -1, 0, 0, + s.sendBundle(s.latency, ["/s_new", "glisson", -1, 0, 0, \freq, i % 10 * 100 + 1000, \freq2, i % 13 * -100 + 3000, - \sustain, 0.05, + \sustain, 0.05, \amp, 0.1, \envbuf, q.envbuf.bufnum ]); @@ -113,38 +113,38 @@ Tdef(\gliss0, { |e| /* Magnetization patterns can be: - bidirectional, shallow, - bidirectional, deep, + bidirectional, shallow, + bidirectional, deep, unidir, up, unidir, down, - converging to center, - diverging from center. + converging to center, + diverging from center. */ ( -SynthDef(\glisson0, { |out, freq1=440, freq2=660, sustain=0.05, amp=0.2, pan, envwide=0.5| +SynthDef(\glisson0, { |out, freq1=440, freq2=660, sustain=0.05, amp=0.2, pan, envwide=0.5| var slopetime = (1 - envwide) * 0.5; var sound = SinOsc.ar(XLine.ar(freq1, freq2, sustain)); var env = EnvGen.ar( - Env([0, amp, amp, 0], [slopetime, envwide, slopetime], \sin), + Env([0, amp, amp, 0], [slopetime, envwide, slopetime], \sin), timeScale: sustain, doneAction: 2); OffsetOut.ar(out, Pan2.ar(sound * env, pan)); -}).memStore; +}).add; ) ( Tdef(\biGliss).set(\fmin, 100, \fmax, 5000, \fratio, 1.0); // non-gliss, wide freq range Tdef(\biGliss, { |envir| - - var f1, f2, temp; - inf.do { + + var f1, f2, temp; + inf.do { f1 = exprand(envir.fmin, envir.fmax); f2 = f1 * (envir.fratio ** 1.0.rand2); // [f1, f2].postln; - ( instrument: \glisson0, - freq1: f1, + ( instrument: \glisson0, + freq1: f1, freq2: f2, pan: 1.0.rand2, sustain: 0.05 @@ -165,13 +165,13 @@ Tdef(\biGliss).set(\fmin, 600, \fmax, 600, \fratio, 2); // diverging ( Tdef(\biGliss).set(\fmin, 1800, \fmax, 600, \fratio, 2.0); // converging Tdef(\biGliss, { |envir| - - var f1, f2, temp; - 500.do { + + var f1, f2, temp; + 500.do { f1 = exprand(envir.fmin, envir.fmax); f2 = f1 * (envir.fratio ** 1.0.rand2); - - ( instrument: \glisson0, + + ( instrument: \glisson0, freq1: f2, // swap f1 and f2 here for converging freq2: f1, pan: 1.0.rand2, diff --git a/Ch 16 Microsound/c16_micro_figures5_pulsar.scd b/Ch 16 Microsound/c16_micro_figures5_pulsar.scd old mode 100644 new mode 100755 index 3ebae44..f55b19b --- a/Ch 16 Microsound/c16_micro_figures5_pulsar.scd +++ b/Ch 16 Microsound/c16_micro_figures5_pulsar.scd @@ -5,7 +5,7 @@ q = (); q.curr = (); // make a dict for the set of tables q.curr.tab = (); // random tables for pulsaret and envelope waveforms: -q.curr.tab.env = Env.perc.discretize; +q.curr.tab.env = Env.perc.discretize; q.curr.tab.pulsaret = Signal.sineFill(1024, { 1.0.rand }.dup(7)); // random tables for the control parameters: @@ -25,35 +25,35 @@ q.bufs.pulsaret.plot("a pulsaret"); // figure 16.17 - Pulsars as nodeproxies using GrainBuf ( -p = ProxySpace.push; +p = ProxySpace.push; // fund, form, amp, pan -~controls = [ 16, 100, 0.5, 0]; +~controls = [ 16, 100, 0.5, 0]; ~pulsar1.set(\wavebuf, q.bufs.pulsaret.bufnum); ~pulsar1.set(\envbuf, q.bufs.env.bufnum); -~pulsar1 = { |wavebuf, envbuf = -1| +~pulsar1 = { |wavebuf, envbuf = -1| var ctls = ~controls.kr; var trig = Impulse.ar(ctls[0]); var grdur = ctls[1].reciprocal; var rate = ctls[1] * BufDur.kr(wavebuf); - + GrainBuf.ar(2, trig, grdur, wavebuf, rate, 0, 4, ctls[3], envbuf); }; ~pulsar1.play; ) // crossfade between control settings -~controls.fadeTime = 3; +~controls.fadeTime = 3; ~controls = [ 16, 500, 0.5, 0]; // change formfreq ~controls = [ 50, 500, 0.5, 0]; // change fundfreq ~controls = [ 16, 100, 0.5, 0]; // change both -~controls = [ rrand(12, 100), rrand(100, 1000)]; +~controls = [ rrand(12, 100), rrand(100, 1000)]; ( // control parameters from looping tables -~controls = { |looptime = 10| - var rate = BufDur.kr(q.bufs.pulsaret.bufnum) / looptime; - A2K.kr(PlayBuf.ar(1, [\fund, \form, \amp, \pan].collect(q.bufs[_]), +~controls = { |looptime = 10| + var rate = BufDur.kr(q.bufs.pulsaret.bufnum) / looptime; + A2K.kr(PlayBuf.ar(1, [\fund, \form, \amp, \pan].collect(q.bufs[_]), rate: rate, loop: 1)); }; ) @@ -63,7 +63,7 @@ p = ProxySpace.push; // make new pulsaret tables and send them to the buffer: q.bufs.pulsaret.sendCollection(Array.linrand(1024, -1.0, 1.0)); // noise burst -q.bufs.pulsaret.read("sounds/a11wlk01.wav", 44100 * 1.5); // sample +q.bufs.pulsaret.read(Platform.resourceDir +/+ "sounds/a11wlk01.wav", 44100 * 1.5); // sample q.bufs.pulsaret.sendCollection(Pbrown(-1.0, 1.0, 0.2).asStream.nextN(1024)); // make a new random fundfreq table, and send it @@ -81,7 +81,7 @@ q.bufs.form.sendCollection(q.curr.tab.form); // Pulsar synthesis with client-side control - + // a pulsaret and an envelope a = Signal.sineFill(1024, 1/(1..10).scramble).putLast(0); b = Env.perc.discretize(1024).putLast(0); @@ -91,24 +91,24 @@ y = Buffer.sendCollection(s, b, 1); // a pulsar synthdef ( -SynthDef(\pulsar1, {|out, wavebuf, envbuf, form=200, amp=0.2, pan| +SynthDef(\pulsar1, {|out, wavebuf, envbuf, form=200, amp=0.2, pan| var grDur = 1/form; var pulsaret = Osc1.ar(wavebuf, grDur); var env = Osc1.ar(envbuf, grDur, doneAction: 2); - + OffsetOut.ar(out, Pan2.ar(pulsaret * env, pan, amp)); -}, \ir ! 6).memStore; +}, \ir ! 6).add; ) Synth(\pulsar1, [\wavebuf, x, \envbuf, y]); // a simple pattern ( -Pbindef(\pulsar1, - \instrument, \pulsar1, - \wavebuf, x, \envbuf, y, - \form, Pn(Penv([20, 1200], [4], \exp)).loop, +Pbindef(\pulsar1, + \instrument, \pulsar1, + \wavebuf, x, \envbuf, y, + \form, Pn(Penv([20, 1200], [4], \exp)).loop, \amp, 0.2, \pan, 0, - \fund, 12, + \fund, 12, \dur, Pfunc({ |ev| ev.fund.reciprocal }) ).play; ) @@ -129,4 +129,3 @@ f.plot; Pbindef(\pulsar1, \fund, Pseg(f, 0.01, \lin, inf)).play; - diff --git a/Ch 16 Microsound/c16_micro_figures5b_pulsar_sieves.scd b/Ch 16 Microsound/c16_micro_figures5b_pulsar_sieves.scd new file mode 100644 index 0000000..9c94e05 --- /dev/null +++ b/Ch 16 Microsound/c16_micro_figures5b_pulsar_sieves.scd @@ -0,0 +1,151 @@ +/* +Quarks.install("miSCellaneous_lib"); +*/ + +// Make sieves, combine them and play them +// These examples use the Sieve class from Daniel Meyer's miSCellaneous_lib + +~size = 24; //size limit for all generated sieves + +// make 4 sieves to play with +a = Sieve(3, `~size); +b = Sieve(5, `~size); +c = Sieve(7, `~size); +d = Sieve(11, `~size); + +//two methods for transforming sieves to pulsar masks + +// sequential bit mask +~pointsToSeqBits = { |points| + var extractPoints = points.list; + var bits = 0.dup(extractPoints.last); + // put 1 at all points indices + extractPoints.drop(-1).do(bits.put(_, 1)); + bits +}; + +~pointsToSeqBits.(a); // multiples of 3, as 1 0 0, 1 0 0 + + +// alternating segments bit mask: +~pointsToSegments = { |points| + points.list.differentiate.collect { |interval, index| + // at every interval, switch between 1 and 0 + Array.fill(interval, { index.odd.asInteger }) + }.flatten; +}; + +~pointsToSegments.(a); // multiples of 3, as burst/rest pattern + +// set operations on sieves as masking sequences + +// union of a and b: all multiples of 3 and 5 +~pointsToSeqBits.(a|b); +// symmetrical difference of 3 and 5 - removes common multiples 0 and 15 +~pointsToSeqBits.(a--b); + +//union of all four - all multiples of 3, 5, 7, 11 +~pointsToSeqBits.(a|b|c|d); // as sequence +-> [ 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0 ] + +~pointsToSegments.(a|b|c|d); // as segments +-> [ 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1 ] + + +///// TODO: explain correctly and/or explain as material for live codable experimentation + +//an intersection (&) of symmetrical difference (--) of a and b with a union (|) of a and d +~pointsToSeqBits.(a--b&a|d); +-> [ 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 ] + +~pointsToSegments.(a--b&a|d); +-> [ 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1 ] + +//a difference of union of a and b with a union of a and d +~pointsToSeqBits.(a|b-c|a|d); +-> [ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0 ] + +~pointsToSegments.(a|b-c|a|d); +-> [ 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1 ] + +//symmetrical difference of a and d +~pointsToSeqBits.(a--d); +-> [ 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0 ] + +~pointsToSegments.(a--d); +-> [ 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1 ] + +//union of symmetrical difference of a and c with a symmetrical dufference of b and d +~pointsToSeqBits.(a--c|b--d); +-> [ 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0 ] + +~pointsToSegments.(a--c|b--d); +-> [ 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1 ] + + +///// bonus track 1: post bit patterns + +~bitprint = { |bitsArray| bitsArray.collect (".1".at(_)).join.postln; }; +~bitprint.(~pointsToSeqBits.(a)); + + +///// bonus track 2: play bit patterns while live coding masks + +//define a simple pulse synth +SynthDef(\pulse, { |amp = 0.1, freq = 440| + Out.ar(0, XLine.ar(amp, 0.001, 0.1, 1,0,2) * SinOsc.ar(freq) ! 2) +}).add; + +//test it +(instrument: \pulse).play; + +//an example sieve-mask +~sieveMask = ~pointsToSegments.(a--d); + +//set the speed +~dt = 0.2; +//define a Tdef to play the pulse synth with mapped sieve-based mask +Tdef(\sivPat, { + loop { + var points = ~sieveMask.postln; + // then play the inter + ~sieveMask.size.do { |i| + if (points[i] == 1) { + (instrument: \pulse, note: 12 ).play; + points[i] .post; + } { ".".post; }; + ~dt.wait; + }; + "".postln; + }; +}).play; + + +// try different speeds +~dt = 0.1; +~dt = 0.05; +~dt = 0.025; +//and different sieve masks +~sieveMask = ~pointsToSeqBits.(a|b); +~sieveMask = ~pointsToSeqBits.(a|b|c|d); +~sieveMask = ~pointsToSegments.(a|b|c|d); +~sieveMask = ~pointsToSegments.(a|b-c|a|d); +~sieveMask = ~pointsToSegments.(a|d); +~sieveMask = ~pointsToSegments.(a--c|b--d); + +//modify the Tdef to map the structure of the sieve-mask to note values +Tdef(\sivPat, { + loop { + var points = ~sieveMask.postln; + // then play the inter + ~sieveMask.size.do { |i| + if (points[i] == 1) { + (instrument: \pulse, note: 12 + i ).play; + points[i] .post; + } { ".".post; }; + ~dt.wait; + }; + "".postln; + }; +}).play; + diff --git a/Ch 16 Microsound/c16_micro_figures6_time_constQ.scd b/Ch 16 Microsound/c16_micro_figures6_time_constQ.scd old mode 100644 new mode 100755 index d5a2bb3..d874cde --- a/Ch 16 Microsound/c16_micro_figures6_time_constQ.scd +++ b/Ch 16 Microsound/c16_micro_figures6_time_constQ.scd @@ -1,19 +1,20 @@ - // time-pitch changing // + // time-pitch changing // - // figure 16.19 time-pitch changing + // figure 16.21 time-pitch changing p = ProxySpace.push(s.boot); -b = Buffer.read(s, "sounds/a11wlk01-44_1.aiff"); +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff"); + ( -~timepitch = {arg sndbuf, pitchRatio=1, pitchRd=0.01, grainRate=10, overlap=2, +~timepitch = {arg sndbuf, pitchRatio=1, pitchRd=0.01, grainRate=10, overlap=2, posSpeed=1, posRd=0.01; - + var graindur = overlap / grainRate; var pitchrate = pitchRatio + LFNoise0.kr(grainRate, pitchRd); - var position = LFSaw.kr(posSpeed / BufDur.kr(sndbuf)).range(0, 1) + var position = LFSaw.kr(posSpeed / BufDur.kr(sndbuf)).range(0, 1) + LFNoise0.kr(grainRate, posRd); - + GrainBuf.ar(2, Impulse.kr(grainRate), graindur, sndbuf, pitchrate, position, 4, 0, -1) }; @@ -27,7 +28,7 @@ Spec.add(\grainRate, [1, 100, \exp]); Spec.add(\overlap, [0.25, 16, \exp]); Spec.add(\posSpeed, [-2, 2]); Spec.add(\posRd, [0, 0.5, \amp]); -NodeProxyEditor(~timepitch, 10); +NdefGui(~timepitch, 10); // reconstruct original ~timepitch.set(\pitchRatio, 1, \pitchRd, 0, \grainRate, 20, \overlap, 4, \posSpeed, 1, \posRd, 0); @@ -43,27 +44,27 @@ NodeProxyEditor(~timepitch, 10); - // examples constantQ granulation + // examples constantQ granulation - // figure 16.20 - A constant-Q Synthdef. + // figure 16.22 - A constant-Q Synthdef. -b = Buffer.read(s, "sounds/a11wlk01-44_1.aiff"); +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff"); ( -SynthDef(\constQ, { |out, bufnum=0, amp=0.1, pan, centerPos=0.5, sustain=0.1, - rate=1, freq=400, rq=0.3| +SynthDef(\constQ, { |out, bufnum=0, amp=0.1, pan, centerPos=0.5, sustain=0.1, + rate=1, freq=400, rq=0.3| var ringtime = (2.4 / (freq * rq) * 0.66).min(0.5); // estimated - var ampcomp = (rq ** -1) * (400 / freq ** 0.5); + var ampcomp = (rq ** -1) * (400 / freq ** 0.5); var envSig = EnvGen.ar(Env([0, amp, 0], [0.5, 0.5] * sustain, \welch)); var cutoffEnv = EnvGen.kr(Env([1, 1, 0], [sustain+ringtime,0.01]), doneAction: 2); - var grain = PlayBuf.ar(1, bufnum, rate, 0, - centerPos - (sustain * rate * 0.5) * BufSampleRate.ir(bufnum), - 1) * envSig; - var filtered = BPF.ar( grain, freq, rq, ampcomp ); + var grain = PlayBuf.ar(1, bufnum, rate, 0, + centerPos - (sustain * rate * 0.5) * BufSampleRate.ir(bufnum), + 1) * envSig; + var filtered = BPF.ar( grain, freq, rq, ampcomp ); OffsetOut.ar(out, Pan2.ar(filtered, pan, cutoffEnv)) -}, \ir.dup(8)).memStore; +}, \ir.dup(8)).add; ) Synth(\constQ, [\bufnum, b, \freq, exprand(100, 10000), \rq, exprand(0.01, 0.1), \sustain, 0.01]); @@ -72,13 +73,13 @@ Synth(\constQ, [\bufnum, b, \freq, exprand(100, 10000), \rq, exprand(0.01, 0.1), // parameter tests for constant Q granulation - + Synth(\constQ, [\bufnum, b]); Synth(\constQ, [\bufnum, b, \centerPos, 0.5]); // centerPos = where in soundfile (seconds) -Synth(\constQ, [\bufnum, b, \centerPos, 1]); -Synth(\constQ, [\bufnum, b, \centerPos, 1.5]); - - // sustain is sustain of exciter grain: +Synth(\constQ, [\bufnum, b, \centerPos, 1]); +Synth(\constQ, [\bufnum, b, \centerPos, 1.5]); + + // sustain is sustain of exciter grain: Synth(\constQ, [\bufnum, b, \centerPos, 0.5, \sustain, 0.01]); Synth(\constQ, [\bufnum, b, \centerPos, 0.5, \sustain, 0.03]); Synth(\constQ, [\bufnum, b, \centerPos, 0.5, \sustain, 0.1]); @@ -97,14 +98,14 @@ Synth(\constQ, [\bufnum, b, \freq, 600, \rq, 0.003]); - // figure 16.21 - a stream of constant Q grains + // figure 16.23 - a stream of constant Q grains ( -Pbindef(\gr1Q, +Pbindef(\gr1Q, \instrument, \constQ, \bufnum, b.bufnum, \sustain, 0.01, \amp, 0.2, - \centerPos, Pn(Penv([1, 2.0], [10], \lin)), + \centerPos, Pn(Penv([1, 2.0], [10], \lin)), \dur, Pn(Penv([0.01, 0.09, 0.03].scramble, [0.38, 0.62] * 10, \exp)), - \rate, Pwhite(0.95, 1.05), + \rate, Pwhite(0.95, 1.05), \freq, Pbrown(64.0, 120, 8.0).midicps, \pan, Pwhite(-1, 1, inf), \rq, 0.03 diff --git a/Ch 16 Microsound/c16_micro_figures7_waveset.scd b/Ch 16 Microsound/c16_micro_figures7_waveset.scd old mode 100644 new mode 100755 index 74fce50..3c85b3c --- a/Ch 16 Microsound/c16_micro_figures7_waveset.scd +++ b/Ch 16 Microsound/c16_micro_figures7_waveset.scd @@ -1,7 +1,20 @@ +Quarks.install("WavesetsEvent"); - // figure 16.22 - a Wavesets object -w = Wavesets.from("sounds/a11wlk01.wav"); +// figure 16.22 - a Wavesets object + +// using WavesetsEvent quark: +s.waitForBoot { + ~wsev = WavesetsEvent.read(Platform.resourceDir +/+ "sounds/a11wlk01.wav", + onComplete: { + w = ~wsev.wavesets; + b = ~wsev.buffer; + }); +}; + +// ~wsev is the WavesetsEvent, +// w is the Wavesets2 object + w.xings; // all integer indices of the zero crossings found w.numXings; // the total number of zero crossings @@ -11,214 +24,221 @@ w.maxima; // index of positive maximum value in every waveset w.minima; // index of negative minimum value in every waveset w.fracXings; // fractional zerocrossing points -w.fracLengths; // and lengths: allows more precise looping. - +w.fracLengths; // and lengths: allows more precise looping / tuning. + w.lengths.plot; // show distribution of lengths w.amps.plot; - // get data for a single waveset: frameIndex, length (in frames), dur -w.frameFor(140, 1); -w.ampFor(140, 1); // peak amplitude of that waveset or group - - // extract waveset by hand -w.signal.copyRange(w.xings[150], w.xings[151]).plot("waveset 150"); -w.plot(140, 1); // convenience plotting -w.plot(1510, 1); - - // plot a group of 5 adjacent wavesets -w.plot(1510, 5) +// get data for a single waveset: frameIndex, length (in frames), dur +w.frameFor(140, 1); +w.maximumAmp(140, 1); // maximum amplitude of that waveset or group +// prepare a playable wavesets event from an inevent with: +// start: (waveset index), +// num: number of wavesets to use as group +// repeats: number of repeats of that waveset group +// amp, pan, behave as expected +~playme = ~wsev.asEvent((start: 2300, num: 5, repeats: 20)); +~playme.play; - // figure 16.23 and 24 are screenshots +// extract a waveset by hand +w.signal.copyRange(w.xings[150], w.xings[151]).plot("waveset 150"); +w.plot(140, 1); // convenience plotting +w.plot(1510, 1); +// plot a group of 5 adjacent wavesets +w.plot(1510, 5); +// figure 16.23 and 24 are screenshots - // figure 16.25 - wavesets and buffers +// figure 16.25 - wavesets and buffers - // A Synthdef to play a waveset (or group) n times. +// A Synthdef to play a waveset (or group) n times. ( - // A wavesets loads the file into a buffer by default. - b = w.buffer; - // Wavesets.prepareSynthDefs loads this synthdef: - SynthDef(\wvst0, { arg out = 0, buf = 0, start = 0, length = 441, playRate = 1, sustain = 1, amp=0.2, pan; - var phasor = Phasor.ar(0, BufRateScale.ir(buf) * playRate, 0, length) + start; - var env = EnvGen.ar(Env([amp, amp, 0], [sustain, 0]), doneAction: 2); - var snd = BufRd.ar(1, buf, phasor) * env; - - OffsetOut.ar(out, Pan2.ar(snd, pan)); - }, \ir.dup(8)).memStore; +// Wavesets.prepareSynthDefs loads this synthdef: +SynthDef(\wvst0, { arg out = 0, buf = 0, start = 0, length = 441, playRate = 1, sustain = 1, amp=0.2, pan; + var phasor = Phasor.ar(0, BufRateScale.ir(buf) * playRate, 0, length) + start; + var env = EnvGen.ar(Env([amp, amp, 0], [sustain, 0]), doneAction: 2); + var snd = BufRd.ar(1, buf, phasor) * env; + + OffsetOut.ar(out, Pan2.ar(snd, pan)); +}, \ir.dup(8)).add; ) -// play from frame 0 to 440, looped for 0.1 secs, so ca 10 repeats. -(instrument: \wvst0, bufnum: b.bufnum, start: 0, length: 440, amp: 1, sustain: 0.1).play; +// make a wavesets play-event from scratch: +// play from frame 0 to 440, looped for 0.1 secs, so ca 10 repeats. +(instrument: \wvst0, buf: b.bufnum, start: 0, length: 440, amp: 1, sustain: 0.1).play; - // get data from waveset +// get raw data from waveset ( -var start, length, sustain, repeats = 20; -#start, length, sustain = w.frameFor(150, 5); - -( instrument: \wvst0, bufnum: b.bufnum, amp: 1, - start: start, length: length, sustain: sustain * repeats +var start, length, sustain, repeats = 20, playRate = 0.5; +#start, length = w.frameFor(150, 5); +sustain = length / b.sampleRate / playRate; + +( instrument: \wvst0, buf: b.bufnum, amp: 1, +start: start, length: length, +playRate: playRate, +sustain: sustain * repeats ).play; ) - // or even simpler: -w.eventFor(startWs: 150, numWs: 5, repeats: 20, playRate: 1).put(\amp, 0.5).play; - +XXX still broken: +// or even simpler: +~wsev.eventFor(startWs: 2300, numWs: 5, repeats: 20, playRate: 1).put(\amp, 0.5).play; - // figure 16.26 - a pattern to play wavesets +// figure 16.26 - a pattern to play wavesets - // by default, this pattern reconstructs a soundfile segment. +// by default, this pattern reconstructs a soundfile segment. ( Pbindef(\ws1).clear; -Pbindef(\ws1, +Pbindef(\ws1, \instrument, \wvst0, - \startWs, Pn(Pseries(0, 1, 3000), 1), - \numWs, 1, - \playRate, 1, - \bufnum, b.bufnum, - \repeats, 1, + \startWs, Pn(Pseries(0, 1, 3000), 1), + \numWs, 1, + \playRate, 1, + \buf, b.bufnum, + \repeats, 1, \amp, 0.4, - [\start, \length, \sustain], Pfunc({ |ev| - var start, length, wsDur; - + [\start, \length, \sustain], Pfunc({ |ev| + var start, length, wsDur; #start, length, wsDur = w.frameFor(ev[\startWs], ev[\numWs]); + wsDur = [start, length, wsDur * ev[\repeats] / ev[\playRate].abs] - }), + }), \dur, Pkey(\sustain) ).play; ) - // figure 16.27 - some of wishart's transforms +// figure 16.27 - some of wishart's transforms - // waveset transposition: every second waveset, half speed +// waveset transposition: every second waveset, half speed Pbindef(\ws1, \playRate, 0.5, \startWs, Pn(Pseries(0, 2, 500), 1)).play; - // reverse every single waveset +// reverse every single waveset Pbindef(\ws1, \playRate, -1, \startWs, Pn(Pseries(0, 1, 1000), 1)).play; - // reverse every 2 wavesets +// reverse every 2 wavesets Pbindef(\ws1, \numWs, 2, \playRate, -1, \startWs, Pn(Pseries(0, 2, 1000), 1)).play; - // reverse every 20 wavesets +// reverse every 20 wavesets Pbindef(\ws1, \numWs, 20, \playRate, -1, \startWs, Pn(Pseries(0, 20, 1000), 1)).play; - // restore +// restore Pbindef(\ws1, \numWs, 1, \playRate, 1, \startWs, Pn(Pseries(0, 1, 1000), 1)).play; - // time stretching +// time stretching Pbindef(\ws1, \playRate, 1, \repeats, 2).play; Pbindef(\ws1, \playRate, 1, \repeats, 4).play; Pbindef(\ws1, \playRate, 1, \repeats, 6).play; Pbindef(\ws1, \repeats, 1).play; // restore - // waveset omission: drop every second +// waveset omission: drop every second Pbindef(\ws1, \numWs, 1, \freq, Pseq([1, \], inf) ).play; Pbindef(\ws1, \numWs, 1, \freq, Pseq([1,1, \, \], inf) ).play; Pbindef(\ws1, \numWs, 1, \freq, Pfunc({ if (0.25.coin, 1, \) }) ).play; // drop randomly Pbindef(\ws1, \numWs, 1, \freq, 1, \startWs, Pn(Pseries(0, 1, 1000)) ).play; // restore - // waveset shuffling (randomize waveset order +- 5, 25, 125) +// waveset shuffling (randomize waveset order +- 5, 25, 125) Pbindef(\ws1, \startWs, Pn(Pseries(0, 1, 1000), 1) + Pfunc({ 5.rand2 })).play; Pbindef(\ws1, \startWs, Pn(Pseries(0, 1, 1000), 1) + Pfunc({ 25.rand2 })).play; -Pbindef(\ws1, \startWs, Pn(Pseries(0, 1, 1000), 1) + Pfunc({ 125.rand2 })).play; +Pbindef(\ws1, \startWs, Pn(Pseries(0, 1, 1000), 1) + Pfunc({ 125.rand2 })).play; + +// figure 16.28 - waveset substitution - // figure 16.28 - waveset substitution - - // the waveform to substitute -c = Buffer.alloc(s, 512); c.sendCollection(Signal.sineFill(512, [1])); +// the waveform to substitute +c = Buffer.alloc(s, 512); c.loadCollection(Signal.sineFill(512, [1])); ( Pbindef(\ws1).clear; -Pbindef(\ws1, +Pbindef(\ws1, \instrument, \wvst0, - \startWs, Pn(Pseries(0, 1, 1000), 5), - \numWs, 1, \playRate, 1, + \startWs, Pn(Pseries(0, 1, 1000), 5), + \numWs, 1, \playRate, 1, \buf, c.bufnum, // sine wave - \repeats, 1, + \repeats, 1, \amp, 1, - [\start, \length, \sustain], Pfunc({ |ev| - var start, length, wsDur, origRate; + [\start, \length, \sustain], Pfunc({ |ev| + var start, length, wsDur, origRate; origRate = ev[\playRate]; - - // get orig waveset specs + + // get orig waveset specs #start, length, wsDur = w.frameFor(ev[\startWs], ev[\numWs]); - // adjust playrate for different length of substituted wave - ev[\playRate] = origRate * (512 / length); + // adjust playrate for different length of substituted wave + ev[\playRate] = origRate * (512 / length); - // get amplitude from waveset, to scale full volume sine wave + // get amplitude from waveset, to scale full volume sine wave ev[\amp] = ev[\amp] * w.ampFor(ev[\startWs], ev[\numWs]); - + [0, 512, wsDur * ev[\repeats] / origRate.abs] - }), + }), \dur, Pkey(\sustain) ).play; ) - // clearer sinewave-ish segments +// clearer sinewave-ish segments Pbindef(\ws1, \playRate, 1, \repeats, 2).play; Pbindef(\ws1, \playRate, 1, \repeats, 6).play; Pbindef(\ws1).stop; - // different waveforms -c.sendCollection(Signal.sineFill(512, 1/(1..4).squared.scramble)); -c.sendCollection(Signal.rand(512, -1.0, 1.0)); -c.sendCollection(Signal.sineFill(512, [1])); +// different waveforms +c.loadCollection(Signal.sineFill(512, 1/(1..4).scramble)); +c.loadCollection(Signal.rand(512, -1.0, 1.0)); // white noise +c.loadCollection(Signal.sineFill(512, [1])); // sine c.plot; - // waveset interpolation - web examples only +// waveset interpolation - web examples only ( -SynthDef("wsInterp", { arg out = 0, - buf1 = 0, start1 = 0, len1 = 1000, - buf2 = 0, start2 = 0, len2 = 500, - playRate = 1, sustain = 1, - amp=0.2, pan; - +SynthDef("wsInterp", { arg out = 0, + buf1 = 0, start1 = 0, len1 = 1000, + buf2 = 0, start2 = 0, len2 = 500, + playRate = 1, sustain = 1, + amp=0.2, pan; + var lenRatio = (len1 / len2); - var playRateLine = Line.ar(playRate, playRate * lenRatio, sustain); - + var playRateLine = Line.ar(playRate, playRate * lenRatio, sustain); + var phasor1 = Phasor.ar(0, BufRateScale.ir(buf1) * playRateLine, 0, len1); - var phasor2 = phasor1 / lenRatio; + var phasor2 = phasor1 / lenRatio; var xfade = Line.ar(0, 1, sustain); - - var snd = (BufRd.ar(1, [buf1, buf2], - [phasor1 + start1, phasor2 + start2], + + var snd = (BufRd.ar(1, [buf1, buf2], + [phasor1 + start1, phasor2 + start2], interpolation: 4) - * [1 - xfade, xfade]).sum; - - OffsetOut.ar(out, + * [1 - xfade, xfade]).sum; + + OffsetOut.ar(out, Pan2.ar( snd * EnvGen.ar(Env([amp, amp, 0], [sustain, 0]), doneAction: 2), - pan - ) - ); -}, \ir.dup(12)).memStore; + pan + ) + ); +}, \ir.dup(12)).add; ) ( q = q ? (); -q.playInterp = { |q, start1, len1, start2, len2, numWs=200| +q.playInterp = { |q, start1, len1, start2, len2, numWs=200| var set1 = w.frameFor(start1, len1).postln; - var set2 = w.frameFor(start2, len2).postln; + var set2 = w.frameFor(start2, len2).postln; var sustain = (set2[2] + set1[2] * 0.5 * numWs).postln; (instrument: \wsInterp, buf1: b.bufnum, buf2: b.bufnum, amp: 0.5, - + start1: set1[0], len1: set1[1], playRate: 1, start2: set2[0], len1: set2[1], sustain: sustain - ).play; + ).play; }; ) - // some interpolations +// some interpolations q.playInterp(200, 1, 500, 1, 400); q.playInterp(400, 8, 600, 3, 100); q.playInterp(200, 1, 500, 5, 600); @@ -227,26 +247,26 @@ q.playInterp(200, 1, 500, 5, 600); - // figure 16.29 - wavesets played with Tdef - - // very simple first pass, fixed repeat time +// figure 16.29 - wavesets played with Tdef + +// very simple first pass, fixed repeat time ( Tdef(\ws1).set(\startWs, 400); Tdef(\ws1).set(\numWs, 5); Tdef(\ws1).set(\repeats, 5); Tdef(\ws1, { |ev| - var startFrame, length, wsSustain; + var startFrame, length, wsSustain; - loop { + loop { #startFrame, length, wsSustain = w.frameFor(ev.startWs.next, ev.numWs); - (instrument: \wvst0, bufnum: b.bufnum, amp: 1, - start: startFrame, length: length, + (instrument: \wvst0, buf: b.bufnum, amp: 1, + start: startFrame, length: length, sustain: wsSustain * ev.repeats; ).play; - - 0.1.wait; + + 0.1.wait; } }).play; ) @@ -255,46 +275,46 @@ Tdef(\ws1).set(\startWs, 420); Tdef(\ws1).set(\repeats, 3); Tdef(\ws1).set(\numWs, 2); - // drop in a pattern for starting waveset +// drop in a pattern for starting waveset Tdef(\ws1).set(\startWs, Pn(Pseries(0, 5, 400) + 500, inf).asStream); - // figure 16.30 - waittime from waveset duration, gap +// figure 16.30 - waittime from waveset duration, gap ( Tdef(\ws1).set(\gap, 3); Tdef(\ws1, { |ev| - var startFrame, length, wsSustain, reps; + var startFrame, length, wsSustain, reps; - loop { + loop { reps = ev.repeats.next; - #startFrame, length, wsSustain = - w.frameFor(ev.startWs.next, ev.numWs.next); - - (instrument: \wvst0, bufnum: b.bufnum, amp: 1, - start: startFrame, length: length, + #startFrame, length, wsSustain = + w.frameFor(ev.startWs.next, ev.numWs.next); + + (instrument: \wvst0, buf: b.bufnum, amp: 1, + start: startFrame, length: length, sustain: wsSustain * reps, pan: 1.0.rand2 ).play; - - // derive waittime from waveset sustain time - // add gap based on waveset sustain time + + // derive waittime from waveset sustain time + // add gap based on waveset sustain time (wsSustain * (reps + ev.gap.next)).wait; } }).play; ) - // experiment with dropping in patterns: - // very irregular gaps +// experiment with dropping in patterns: +// very irregular gaps Tdef(\ws1).set(\gap, { exprand(0.1, 20) }); - // sometimes continuous, sometimes gaps +// sometimes continuous, sometimes gaps Tdef(\ws1).set(\gap, Pbrown(-10.0, 20, 2.0).max(0).asStream); - // random repeats +// random repeats Tdef(\ws1).set(\repeats, { exprand(1, 20).round }); - // randomize number of wavesets per group +// randomize number of wavesets per group Tdef(\ws1).set(\numWs, { exprand(3, 20).round }); Tdef(\ws1).set(\numWs, 3, \repeats, { rrand(2, 5) }); @@ -302,7 +322,7 @@ Tdef(\ws1).stop; - // figure 16.31 - add pitch contour and dropout rate +// figure 16.31 - add pitch contour and dropout rate ( Tdef(\ws1).set(\startWs, Pn(Pseries(0, 5, 400) + 500, inf).asStream); @@ -313,55 +333,55 @@ Tdef( 'ws1' ).set( 'repeats' , 5 ); Tdef( 'ws1' ).set( 'numWs' , 3 ); Tdef(\ws1, { |ev| - var startFrame, length, wsSustain, reps, numWs, len2Avg; + var startFrame, length, wsSustain, reps, numWs, len2Avg; var squeezer, playRate; - loop { + loop { reps = ev.repeats.next; numWs = ev.numWs.next; - - #startFrame, length, wsSustain = - w.frameFor(ev.startWs.next, numWs); - - len2Avg = length / numWs / w.avgLength; + + #startFrame, length, wsSustain = + w.frameFor(ev.startWs.next, numWs); + + len2Avg = length / numWs / w.avgLength; squeezer = len2Avg ** ev.pitchContour.next; - wsSustain = wsSustain / squeezer; + wsSustain = wsSustain / squeezer; playRate = 1 * squeezer; - if (ev.keepCoin.next.coin) { - (instrument: \wvst0, bufnum: b.bufnum, amp: 1, - start: startFrame, length: length, + if (ev.keepCoin.next.coin) { + (instrument: \wvst0, buf: b.bufnum, amp: 1, + start: startFrame, length: length, sustain: wsSustain * reps, - playRate: playRate, + playRate: playRate, pan: 1.0.rand2 ).play; }; - + (wsSustain * (reps + ev.gap.next)).wait; } }).play; ) - // try different pitch Contours: +// try different pitch Contours: Tdef(\ws1).set(\pitchContour, 0); // original pitch Tdef(\ws1).set(\pitchContour, 0.5); // flattened contour - // waveset overtone singing - all equal length -Tdef(\ws1).set(\pitchContour, 1.0); +// waveset overtone singing - all equal length +Tdef(\ws1).set(\pitchContour, 1.0); - // inversion of contour +// inversion of contour Tdef(\ws1).set(\pitchContour, 1.5); Tdef(\ws1).set(\pitchContour, 2); -Tdef(\ws1).set(\repeats, 3); +Tdef(\ws1).set(\repeats, 3); - // waveset omission +// waveset omission Tdef(\ws1).set(\keepCoin, 0.75); Tdef(\ws1).set(\keepCoin, 1); - // fade out by omission over 13 secs, pause 2 secs +// fade out by omission over 13 secs, pause 2 secs Tdef(\ws1).set(\keepCoin, Pn(Penv([1, 0, 0], [13, 2])).asStream).play; - // add a pitch contour envelope +// add a pitch contour envelope Tdef(\ws1).set(\pitchContour, Pn(Penv([0, 2, 0], [21, 13])).asStream); diff --git a/Ch 16 Microsound/c16_micro_figures7_waveset_work.scd b/Ch 16 Microsound/c16_micro_figures7_waveset_work.scd new file mode 100755 index 0000000..4173841 --- /dev/null +++ b/Ch 16 Microsound/c16_micro_figures7_waveset_work.scd @@ -0,0 +1,390 @@ +// attempt to get all examples working with Wavesets2/Event ... + +Quarks.install("WavesetsEvent"); + + +// figure 16.24 - a Wavesets object + +// using WavesetsEvent quark: +// ~wsev is the WavesetsEvent which does the Wavesets event interface, +// w is the Wavesets2 object which contains all the wavesets data. +~wsev = WavesetsEvent.read(Platform.resourceDir +/+ "sounds/a11wlk01.wav", + onComplete: { + w = ~wsev.wavesets; + b = ~wsev.buffer; +}); + +w.xings; // all integer indices of the zero crossings found +w.numXings; // the total number of zero crossings +w.lengths; // lengths of all wavesets +w.amps; // peak amplitude of every waveset +w.maxima; // index of positive maximum value in every waveset +w.minima; // index of negative minimum value in every waveset + +w.fracXings; // fractional zerocrossing points +w.fracLengths; // and lengths: allows more precise looping / tuning. + +w.lengths.plot; // show distribution of lengths +w.amps.plot; + +// get data for a single waveset: startFrame, length (in frames), duration +w.frameFor(140, 1); + +w.maximumAmp(140, 1); // maximum amplitude of that waveset or group +w.ampFor(140, 1); + + +// extract a waveset by hand +w.signal.copyRange(w.xings[150], w.xings[151]).plot("waveset 150"); +w.plot(140, 1); // convenience plotting +w.plot(1510, 1); + +// plot a group of 5 adjacent wavesets +w.plot(1510, 5); + + + +// figure 16.25 shows screenshots of the two plots above + + +// figure 16.26 - wavesets and buffers + +// A Synthdef to play a waveset (or group) n times. +( +// Wavesets.prepareSynthDefs loads this synthdef: +SynthDef(\wvst0, { | out = 0, buf = 0, startFrame = 0, numFrames = 441, rate = 1, sustain = 1, amp = 0.1, pan, interpolation = 2 | + var phasor = Phasor.ar(0, BufRateScale.ir(buf) * rate * sign(numFrames), 0, abs(numFrames)) + startFrame; + var env = EnvGen.ar(Env([amp, amp, 0], [sustain, 0]), doneAction: 2); + var snd = BufRd.ar(1, buf, phasor, 1, interpolation) * env; + + OffsetOut.ar(out, Pan2.ar(snd, pan)); +}, \ir.dup(9)).add; +) + +// make a wavesets play-event with hand-set parameters: +// play from frame 0 to 440, looped for 0.1 secs, so ca 10 repeats. +(instrument: \wvst0, buf: b.bufnum, startFrame: 0, numFrames: 440, amp: 1, sustain: 0.1).play; + +// get frame data from wavesets with frameFor: +( +var startFrame, numFrames, sustain, repeats = 20, rate = 1; +#startFrame, numFrames, sustain = w.frameFor(2300, 5); +( +instrument: \wvst0, buf: b.bufnum, amp: 1, +startFrame: startFrame, numFrames: numFrames, +rate: rate, +sustain: sustain * repeats +).postln.play; +) + +// or even use eventFor : +~wsev.eventFor(startWs: 2300, numWs: 5, repeats: 20, rate: 1).put(\amp, 0.5).play; + +// WavesetsEvent:asEvent prepares a playable event from an inevent, +// which supports specifying many waveset parameters. The basic ones are: +// start: (waveset index), +// num: number of wavesets to use as group +// repeats: number of repeats of that waveset group +~playme = ~wsev.asEvent((start: 2300, num: 5, repeats: 20, amp: 1)); +~playme.play; + + +// figure 16.27 - a pattern to play wavesets + +// by default, this pattern reconstructs a soundfile segment. +( +Pbindef(\ws1).clear; +Pbindef(\ws1, + \instrument, \wvst0, + \startWs, Pn(Pseries(0, 1, w.size - 1), 1), + \numWs, 1, + \rate, 1, + \buf, b.bufnum, + \repeats, 1, + \amp, 1, + [\startFrame, \numFrames, \sustain], Pfunc({ |ev| + var start, length, wsDur; + #start, length, wsDur = w.frameFor(ev[\startWs], ev[\numWs]); + if (length > 0) { + [start, length, wsDur * ev[\repeats] / ev[\rate].abs] + } + }), + \dur, Pkey(\sustain) +).play; +) + + + +// figure 16.28 - some of Wishart's transforms + +// waveset transposition: every second waveset, half speed +Pbindef(\ws1, \rate, 0.5, \startWs, Pn(Pseries(0, 2, w.size / 2), 1)).play; + +// reverse every single waveset +Pbindef(\ws1, \rate, -1, \startWs, Pn(Pseries(0, 1, w.size), 1)).play; +// reverse every 2 wavesets +Pbindef(\ws1, \numWs, 3, \rate, -1, \startWs, Pn(Pseries(0, 3, w.size / 3), 1)).play; +// reverse every 20 wavesets +Pbindef(\ws1, \numWs, 10, \rate, -1, \startWs, Pn(Pseries(0, 10, w.size / 10), 1)).play; +// reverse every 200 wavesets +Pbindef(\ws1, \numWs, 30, \rate, -1, \startWs, Pn(Pseries(0, 30, w.size / 30), 1)).play; +// reverse every 200 wavesets +Pbindef(\ws1, \numWs, 100, \rate, -1, \startWs, Pn(Pseries(0, 100, w.size / 100), 1)).play; +// restore +Pbindef(\ws1, \numWs, 1, \rate, 1, \startWs, Pn(Pseries(0, 1, w.size), 1)).play; + +// time stretching +Pbindef(\ws1, \rate, 1, \repeats, 2).play; +Pbindef(\ws1, \rate, 1, \repeats, 4).play; +Pbindef(\ws1, \rate, 1, \repeats, 6).play; +Pbindef(\ws1, \repeats, 1).play; // restore + +// waveset omission: drop every second waveset +Pbindef(\ws1, \numWs, 1, \freq, Pseq([1, \], inf) ).play; +Pbindef(\ws1, \numWs, 1, \freq, Pseq([1,1, \, \], inf) ).play; +Pbindef(\ws1, \numWs, 1, \freq, Pfunc({ if (0.25.coin, 1, \) }) ).play; // drop randomly +Pbindef(\ws1, \numWs, 1, \freq, 1 ).play; // restore + +// waveset shuffling (randomize waveset order +- 5, 25, 125) +Pbindef(\ws1, \startWs, (Pn(Pseries(0, 1, w.size), 1) + Pfunc({ 5.rand2 })).max(0)).play; +Pbindef(\ws1, \startWs, (Pn(Pseries(0, 1, w.size), 1) + Pfunc({ 25.rand2 })).max(0)).play; +Pbindef(\ws1, \startWs, (Pn(Pseries(0, 1, w.size), 1) + Pfunc({ 125.rand2 })).max(0)).play; + + + + + +// figure 16.29 - waveset substitution + +// the waveform to substitute +c = Buffer.alloc(s, 512); c.loadCollection(Signal.sineFill(512, [1])); +( +Pbindef(\ws1).clear; +Pbindef(\ws1, + \instrument, \wvst0, + \startWs, Pn(Pseries(0, 1, w.size), 5), + \numWs, 1, \rate, 1, + \buf, c.bufnum, // sine wave + \repeats, 1, + \amp, 1, + [\startFrame, \numFrames, \sustain], Pfunc({ |ev| + var start, length, wsDur, origRate; + origRate = ev[\rate]; + + // get orig waveset specs + #start, length, wsDur = w.frameFor(ev[\startWs], ev[\numWs]); + + // adjust rate for different length of substituted wave + ev[\rate] = origRate * (512 / length); + + // get amplitude from waveset, to scale full volume sine wave + ev[\amp] = ev[\amp] * w.ampFor(ev[\startWs], ev[\numWs]); + + [0, 512, wsDur * ev[\repeats] / origRate.abs] + }), + \dur, Pkey(\sustain) +).play; +) + +// clearer sinewave-ish segments +Pbindef(\ws1, \rate, 1, \repeats, 2).play; +Pbindef(\ws1, \rate, 1, \repeats, 6).play; +Pbindef(\ws1).stop; + +// different substitution waveforms to try: +c.loadCollection(Signal.sineFill(512, 1/(1..4))); c.plot; // sawish +// fibonacci overtones +c.loadCollection(Signal.sineFill(512, [1, 1, 1, 0, 1, 0, 0, 1])); c.plot; +c.loadCollection(Signal.rand(512, -1.0, 1.0)); c.plot; // white noise +c.loadCollection(Signal.sineFill(512, [1])); c.plot;// sine + + +// waveset interpolation - web examples only +( +SynthDef("wsInterp", { arg out = 0, + buf1 = 0, startFrame1 = 0, numFrames1 = 1000, + buf2 = 0, startFrame2 = 0, numFrames2 = 500, + rate = 1, sustain = 1, + amp=0.2, pan; + + var lenRatio = (numFrames1 / numFrames2); + var rateLine = Line.ar(rate, rate * lenRatio, sustain); + + var phasor1 = Phasor.ar(0, BufRateScale.ir(buf1) * rateLine, 0, numFrames1); + var phasor2 = phasor1 / lenRatio; + var xfade = Line.ar(0, 1, sustain); + + var snd = (BufRd.ar(1, [buf1, buf2], + [phasor1 + startFrame1, phasor2 + startFrame2], + interpolation: 4) + * [1 - xfade, xfade]).sum; + + OffsetOut.ar(out, + Pan2.ar( + snd * EnvGen.ar(Env([amp, amp, 0], [sustain, 0]), doneAction: 2), + pan + ) + ); +}, \ir.dup(12)).add; +) + +( +q = q ? (); +q.playInterp = { |q, startFrame1, numFrames1, startFrame2, numFrames2, numWs=200| + var set1 = w.frameFor(startFrame1, numFrames1).postln; + var set2 = w.frameFor(startFrame2, numFrames2).postln; + var sustain = (set2[2] + set1[2] * 0.5 * numWs).postln; + + (instrument: \wsInterp, buf1: b.bufnum, buf2: b.bufnum, amp: 0.5, + + startFrame1: set1[0], numFrames1: set1[1], rate: 1, + startFrame2: set2[0], numFrames1: set2[1], sustain: sustain + ).play; +}; +) +// some interpolations +q.playInterp(200, 1, 500, 1, 400); +q.playInterp(400, 8, 600, 3, 100); +q.playInterp(200, 1, 500, 5, 600); + + + + + +// figure 16.30 - wavesets played with Tdef + +// very simple first pass, fixed repeat time +( +Tdef(\ws1).set(\startWs, 400); +Tdef(\ws1).set(\numWs, 5); +Tdef(\ws1).set(\repeats, 5); + +Tdef(\ws1, { |ev| + var startFrame, numFrames, wsSustain; + + loop { + #startFrame, numFrames, wsSustain = w.frameFor(ev.startWs.next, ev.numWs); + + (instrument: \wvst0, buf: b.bufnum, amp: 1, + start: startFrame, numFrames: numFrames, + sustain: wsSustain * ev.repeats; + ).play; + + 0.1.wait; + } +}).play; +) + +Tdef(\ws1).set(\startWs, 420); +Tdef(\ws1).set(\repeats, 3); +Tdef(\ws1).set(\numWs, 2); + +// drop in a pattern for starting waveset +Tdef(\ws1).set(\startWs, Pn(Pseries(0, 5, 400) + 500, inf).asStream); + + + + + +// figure 16.31 - waittime from waveset duration, gap added +( +Tdef(\ws1).set(\gap, 3); +Tdef(\ws1, { |ev| + var startFrame, numFrames, wsSustain, reps; + + loop { + reps = ev.repeats.next; + + #startFrame, numFrames, wsSustain = + w.frameFor(ev.startWs.next, ev.numWs.next); + + (instrument: \wvst0, buf: b.bufnum, amp: 1, + start: startFrame, numFrames: numFrames, + sustain: wsSustain * reps, + pan: 1.0.rand2 + ).play; + + // derive waittime from waveset sustain time + // add gap based on waveset sustain time + (wsSustain * (reps + ev.gap.next)).wait; + } +}).play; +) +// experiment with dropping in patterns: +// very irregular gaps +Tdef(\ws1).set(\gap, { exprand(0.1, 20) }); +// sometimes continuous, sometimes gaps +Tdef(\ws1).set(\gap, Pbrown(-10.0, 20, 2.0).max(0).asStream); + +// random repeats +Tdef(\ws1).set(\repeats, { exprand(1, 20).round }); +// randomize number of wavesets per group +Tdef(\ws1).set(\numWs, { exprand(3, 20).round }); +Tdef(\ws1).set(\numWs, 3, \repeats, { rrand(2, 5) }); + +Tdef(\ws1).stop; + + +// figure 16.32 - add pitch contour and dropout rate +( +Tdef(\ws1).set(\startWs, Pn(Pseries(0, 5, 400) + 500, inf).asStream); + +Tdef(\ws1).set(\gap, 0); +Tdef(\ws1).set(\pitchContour, 0); +Tdef(\ws1).set(\keepCoin, 1.0); +Tdef( 'ws1' ).set( 'repeats' , 5 ); +Tdef( 'ws1' ).set( 'numWs' , 3 ); + +Tdef(\ws1, { |ev| + var startFrame, numFrames, wsSustain, reps, numWs, numFrames2Avg; + var squeezer, rate; + loop { + reps = ev.repeats.next; + numWs = ev.numWs.next; + + #startFrame, numFrames, wsSustain = + w.frameFor(ev.startWs.next, numWs); + + numFrames2Avg = numFrames / numWs / w.avgLength; + squeezer = numFrames2Avg ** ev.pitchContour.next; + wsSustain = wsSustain / squeezer; + rate = 1 * squeezer; + + if (ev.keepCoin.next.coin) { + (instrument: \wvst0, buf: b.bufnum, amp: 1, + start: startFrame, numFrames: numFrames, + sustain: wsSustain * reps, + rate: rate, + pan: 1.0.rand2 + ).play; + }; + + (wsSustain * (reps + ev.gap.next)).wait; + } +}).play; +) + +// try different pitch Contours: +Tdef(\ws1).set(\pitchContour, 0); // original pitch + +Tdef(\ws1).set(\pitchContour, 0.5); // flattened contour + +// waveset overtone singing - all equal duration +Tdef(\ws1).set(\pitchContour, 1.0); + +// inversion of contour +Tdef(\ws1).set(\pitchContour, 1.5); +Tdef(\ws1).set(\pitchContour, 2); +Tdef(\ws1).set(\repeats, 3); + +// waveset omission +Tdef(\ws1).set(\keepCoin, 0.75); +Tdef(\ws1).set(\keepCoin, 1); + +// fade out by omission over 13 secs, pause 2 secs +Tdef(\ws1).set(\keepCoin, Pn(Penv([1, 0, 0], [13, 2])).asStream).play; + +// add a pitch contour envelope +Tdef(\ws1).set(\pitchContour, Pn(Penv([0, 2, 0], [21, 13])).asStream); + + diff --git a/Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.html b/Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.html deleted file mode 100644 index 20e6622..0000000 --- a/Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.html +++ /dev/null @@ -1,358 +0,0 @@ - - - - - - - - - - - -

Wavesets analyse soundfiles into short fragments for granular synthesis

-


-

Inherits from: Object

-


-

Wavesets analyses soundfiles into short fragments called wavesets, and keeps these waveset data.

-

It can support a variety of waveset based synthesis instruments.

-


-

By Trevor Wishart's definition, a waveset is a segment of an audio signal between 

-

one non-positive to positive zero crossing and the next. [ see T. Wishart (1994): Audible Design. ]

-

Note that this definition only applies to mono signals.

-

-

Creation / Class Methods

-


-

*from (path, name, toBuffer)

-

-

Read a soundfile, analyse the signal, read the file to a buffer

-

path - the path to the soundfile

-

name - a name by which to keep the new wavesets in a global dictionary

-

toBuffer - a boolean whether to load the file to a buffer immediately.

-

-

// a first little example

-

Server.default = s = Server.internal; s.boot;

-

-

// make a wavesets from a soundfile

-

w = Wavesets.from("sounds/a11wlk01.wav");

-

w.dump; // contains mainly analysis data 

-

-

w.plot(200, 1); // plot a single waveset

-

w.signal.copyRange(w.xings[600], w.xings[601]).plot;

-


-

w.plot(600, 1); // a single

-

w.plot(600, 5); // a group of five contiguous wavesets

-

w.buffer;

-

w.buffer.play;

-

-

Wavesets.prepareSynthDefs;

-

// startWs

-

w.eventFor(startWs: 600, length: 5, repeats: 2).postln.play;

-

w.eventFor(startWs: 600, length: 2, playRate: 1, repeats: 5).postln.play;

-

w.eventFor(startWs: 600, length: 2, playRate: 0.5, repeats: 5).postln.play;

-

w.eventFor(700, 20, 5, 1).play;

-

-

(

-

fork { 

-

666.do { |i| 

-

var ev = w.eventFor(i * 5, 2, 5, exprand(0.5, 1.0)); 

-

ev.put(\pan, 1.0.rand2).play;

-

ev.sustain.wait;

-

}

-

};

-

)

-


-

-

*new (name, sig, sampleRate)

-

-

Make a Wavesets from a signal directly - not typical usage, but may be needed sometimes.

-

name - a name by which to keep the new wavesets in a global dictionary

-

sig - the signal to be analysed Explanation of sig. Default value is nil. Other information.

-

sampleRate - Explanation of sampleRate. Default value is nil. Other information.

-


-


-

all A dictionary where all the wavesets are kept by name. 

-

Existing wavesets in .all are not re-analysed.

-

Wavesets.all;

-

-

*at (key) Access a Wavesets by its name. 

-

key - The name to look up

-

g = Wavesets.at('a11wlk01.wav');

-


-

*clear Clear the global dictionary

-

Wavesets.clear;

-


-

*minLength set the shortest length (in frames) that will still be treated as a waveset. Shorter 

-

wavesets are merged with their neighbors.

-


-

*prepareSynthDefs  

-

prepare some Synthdefs to be used with wavesets. 

-

-


-

Accessing Instance and Class Variables

-

-

signal the audio signal that was analysed. 

-

name the wavesets name in the global dictionary

-

buffer a buffer on the server that was created from the same soundfile

-

numFrames the number of frames

-

sampleRate the sample rate of the signal, default is Server.default.sampleRate

-

    

-


-

The following variables are analysis result lists

-

xings all integer indices of the zero crossings found

-

numXings total number of zero crossings found

-

lengths lengths of all wavesets

-

amps peak amplitude of every waveset

-

maxima indices of positive maximum value in every waveset

-

minima indices of negative minimum value in every waveset

-

-

fracXings the calculated fractional zerocrossing points.

-

this allows for more precise pitch information

-

and waveset transitions, resulting in smoother sound.

-

fracLengths fractional lengths - in effect, waveset 'pitch'.

-

-

These are overall statistics of the entire wavesets

-

minSet shorted waveset

-

maxSet longest waveset

-

avgLength average length of all wavesets

-

sqrAvgLength weighted average length - so bigger wavesets have a larger impact

-

minAmp softest waveset amplitude

-

maxAmp loudest waveset amplitude

-

avgAmp average amplitude of the entire waveset

-

sqrAvgAmp weighted average of (squared) amplitude of the entire waveset

-


-


-

Some utility instance methods:

-


-

toBuffer(server) load the analysed soundfile to the buffer.

-

-

frameFor(startWs, numWs, useFrac)

-

calculate startFrame, length in frames, and duration 

-

for a waveset or group. useFrac = true means use fractional crossings. 

-

true by default.

-

-

ampFor(startWs, length) maximum amp that occurs in a waveset or group.

-

-

eventFor(startWs, numWs, repeats, playRate, useFrac)

-

generate an event for a given combination of start waveset index,

-

number of wavesets, repeats, playback rate, and use of fractional crossings.

-


-

plot (startWs, length) plot a waveset or group for a given waveset index and length.

-

-

-

Examples

-


-

The simplest usage is to ask the waveset to prepare an event for you.

-


-

w = Wavesets.from("sounds/a11wlk01.wav");

-


-

(

-

fork { 

-

666.do { |i| 

-

var ev = w.eventFor((i * 5).postln, 2, 5, exprand(0.5, 1.0)); 

-

ev.put(\pan, 1.0.rand2).play;

-

ev.sustain.wait;

-

}

-

};

-

)

-


-


-


-


-

// play a single waveset or waveset group by hand

-

(

-

{ var startFr, endFr, dur; 

-

startFr = w.xings[800]; 

-

endFr = w.xings[820];

-

-

dur = endFr - startFr / w.buffer.sampleRate;

-

dur.postln;

-

BufRd.ar(1, w.buffer, Line.ar(startFr, endFr, dur, doneAction: 2)) 

-

}.play;

-

)

-


-

loop buffer segments with a Phasor:

-

(

-

x = { arg start = 0, end = 128, playRate = 1; 

-

BufRd.ar(1, w.buffer, 

-

Phasor.ar(0, playRate * BufRateScale.kr(w.buffer), start, end)

-

)

-

}.scope(zoom: 8);

-

)

-


-

x.set(\start, 0, \end, 1024); // just some random length, may buzz

-

x.set(\start, w.xings.at(100), \end, w.xings.at(101));

-

x.set(\start, w.xings.at(101), \end, w.xings.at(102));

-

x.set(\start, w.xings.at(100), \end, w.xings.at(200));

-

x.set(\start, w.xings.at(780), \end, w.xings.at(800));

-

x.set(\playRate, 0.25);

-

x.set(\playRate, 1);

-


-

x.release;

-


-


-


-

Doing Some Task (optional)

-


-

-

Examples

-


-

To play a waveset (or group) for a precise number of repetitions, 

-

one can use a SynthDef with a hard cutoff envelope, as below.

-

Note that adding an offset outside the phasor works better; 

-

Phasor.ar(0, playRate, start, end) is sometimes off by a few samples.

-

This is likely a 32bit float precision problem.

-


-

( // the simplest synthdef as implented in *prepareSynthDefs: 

-

SynthDef(\wvst0, { arg out = 0, buf = 0, start = 0, length = 441, playRate = 1, sustain = 1, amp=0.2, pan; 

-

var phasor = Phasor.ar(0, BufRateScale.ir(buf) * playRate, 0, length) + start;

-

var env = EnvGen.ar(Env([amp, amp, 0], [sustain, 0]), doneAction: 2);

-

var snd = BufRd.ar(1, buf, phasor) * env;

-

-

OffsetOut.ar(out, Pan2.ar(snd, pan));

-

}, \ir.dup(8)).memStore;

-

)

-


-

Synth("wvst0", [\bufnum, b, \start, 0, \length, 5000, \sustain, 0.1]);

-


-

// do the math by hand to understand it:

-

(

-

var startWs = 100, length = 6, rep = 10, playRate = 0.5;

-

var startframe, endframe, sustain;

-


-

startframe = w.xings[startWs];

-

endframe = w.xings[startWs + length];

-

sustain = (endframe - startframe) * rep / playRate / w.sampleRate; 

-


-

Synth("wvst0", [

-

\bufnum, w.buffer, 

-

\start, startframe, 

-

\length, endframe - startframe, 

-

\playRate, playRate,

-

\sustain, sustain, 

-

\amp, 1

-

]);

-

)

-

// the same done with eventFor:

-

w.eventFor(100, 6, repeats: 10, playRate: 0.5).put(\amp, 1).play;

-


-

(

-

Task({ 

-

300.do({ arg i; 

-

var ev = w.eventFor(100 + i, 2, 10, 1);

-

-

ev.putPairs([\pan, [-1, 1].choose, \amp, 0.5]);

-

ev.play;

-

(ev.sustain).wait; 

-

});

-

}).play;

-

)

-


-

x = a11wlk01-44_1.aiff; 

-


-

// compare fractional and integer xings:

-

// especially for high frequency signals, 

-

// fractional does slightly cleaner looping and finer pitch gradations.

-

// - better test below with ordering wavesets by length -

-

(

-

Task({ 

-

[true, false].do { |usefrac| 

-

(1250..1500).do { arg i; 

-

var ev = w.eventFor(i, 1, 100, 1, useFrac: usefrac);

-

-

ev.putPairs([/*\pan, 0 [-1, 1].choose,*/ \amp, 0.5]);

-

ev.play;

-

(ev.sustain).wait; 

-

};

-

1.wait;

-

};

-

}).play;

-

)

-


-

// some variants waveset timestretch

-

(

-

Task({ 

-

// segments of 10 wavesets, step by 1 => 10x longer

-

(300, 301 .. 900).do { arg start; 

-

var ev = w.eventFor(start, numWs: 10, repeats: 1);

-

-

ev.putPairs([\amp, 0.5]);

-

ev.play;

-

(ev.sustain).wait; 

-

};

-

1.wait; 

-


-

// 1 waveset at a time, loop 10 times - much more 'pitch beads'-like

-

(300, 301 .. 900).do { arg start; 

-

var ev = w.eventFor(start, numWs: 1, repeats: 10);

-

-

ev.putPairs([\amp, 0.5]);

-

ev.play;

-

(ev.sustain).wait; 

-

};

-

-

}).play;

-

)

-


-


-


-

// play them sorted by (integer) waveset length: 

-

w.lengths.plot; // lengths are very irregular

-

o = w.lengths.order;

-


-

(

-

Task({ 

-

var start, end, startFr, endFr, dur, repeats;

-

var order; 

-

order = w.lengths.order;

-

-

[\false, \true].do { |useFrac| 

-

if (useFrac) { "fractional crossings - better pitch resolution" } {

-

"integer waveset borders - pitches quantized to integer sample lengths"

-

}.postln;

-

-

order.do({ arg start; 

-

var ev = w.eventFor(start, numWs: 1, repeats: 5);

-

-

ev.putPairs([\amp, 0.5]);

-

ev.play;

-

(ev.sustain).wait; 

-

});

-

}).play;

-

)

- - diff --git a/Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.sc b/Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.sc deleted file mode 100755 index 1f172f9..0000000 --- a/Ch 16 Microsound/extensionsMicrosound/wavesets/Wavesets.sc +++ /dev/null @@ -1,245 +0,0 @@ -// Analyses a soundfile's data into wavesets. -// keep all wavesets objects in global var all. -// only works for single channels. - -/***** - to do: - analyse turnaround points better: - reverse-interpolate where between samples - the actual turnaround point would be, - and store them in vars minLength = 10; // reasonable? => 4.4 kHz maxFreq. - classvar defaultInst = \wvst0; - - var path, 1) { - ("File" + path + "has" + f.numChannels + "chans." - "Wavesets only works on mono signals, so please ...").warn; - // could also take first chan... - ^nil - }; - // sampleFormat is not updated correctly in SoundFile. - - sig = (formatDict[f.sampleFormat] ? Signal).newClear(f.numFrames); - name = name ?? { PathName(path).fileName.basename; }; - f.readData(sig); - f.close; - - ws = this.new(name.asSymbol, sig, f.sampleRate); - ws.path = path; - if (toBuffer) { ws.toBuffer(server) }; - - ^ws - } - - toBuffer { |server| - server = server ? Server.default; - buffer = buffer ?? { Buffer(server) }; - buffer.allocRead(path, completionMessage: {|buf|["/b_query",buf.bufnum]}); - } - - init { arg argName, argSig, argSampleRate; - - if (all.at(argName).notNil and: { all.at(argName).signal.size == argSig.size }, - { - ("// waveset" + argName + "seems identical to existing.\n" - "// ignored.").postln; - ^all.at(argName); - } - ); - name = argName; - signal = argSig; - numFrames = argSig.size; - sampleRate = argSampleRate ? Server.default.sampleRate; - all.put(name, this); - this.analyse; - } - - *clear { all = IdentityDictionary.new; } - - *initClass { - this.clear; - formatDict = ( - 'int8': Int16Array, - 'int16': Int16Array, - 'mulaw': Int16Array, - 'alaw': Int16Array, - 'int24': Int32Array, - 'int32': Int32Array, - 'float': Signal - ); - } - - *at { |key| ^all[key] } - - analyse { arg finishFunc; - // var chunkSize = 400, pause = 0.01; // not used yet - - xings = Array.new; - amps = Array.new; - lengths = Array.new; - fracXings = Array.new; - maxima = Array.new; // indices of possible turnaround points - minima = Array.new; // - ( "Analysing" + name + "...").postcln; - - this.analyseFromTo; - this.calcAverages; - ("Finished signal" + name + ":" + numXings + " xings.").postcln; - } - - calcAverages { // useful statistics. - // calculate maxAmp, minAmp, avgAmp, sqAvgAmp; - // and maxSet, minSet, avgLength, sqAvgLength; - - numXings = xings.size; - minSet = lengths.minItem; - maxSet = lengths.maxItem; - minAmp = amps.minItem; - maxAmp = amps.maxItem; - - fracLengths = fracXings.drop(1) - fracXings.drop(-1); - - avgLength = xings.last - xings.first / numXings; - sqrAvgLength = ( lengths.squared.sum / ( numXings - 1) ).sqrt; - - avgAmp = amps.sum / numXings; - sqrAvgAmp = (amps.squared.sum / numXings).sqrt; - - ^this; - } - - // should eventually support analysis in blocks in realtime. - - analyseFromTo { arg startFrame = 0, endFrame; - var lengthCount = 0, prevSample = 0.0, - maxSamp = 0.0, minSamp = 0.0, - maxAmpIndex, minAmpIndex, - wavesetAmp, frac; - - // find xings, store indices, lengths, and amps. - - startFrame = max(0, startFrame); - endFrame = (endFrame ? signal.size - 1).min(signal.size - 1); - - ( startFrame to: endFrame ).do({ arg i; - var thisSample; - thisSample = signal.at(i); - - // if Xing from non-positive to positive: - if ( (prevSample <= 0.0) and: (thisSample > 0.0) and: (lengthCount >= minLength), - - { - - if (xings.notEmpty, { - // if we already have a first waveset, - // keep results from analysis: - wavesetAmp = max(maxSamp, minSamp.abs); - amps = amps.add(wavesetAmp); - lengths = lengths.add(lengthCount); - maxima = maxima.add(maxAmpIndex); - minima = minima.add(minAmpIndex); - }); - - xings = xings.add(i); - - // lin interpol for fractional crossings - frac = prevSample / (prevSample - thisSample); - fracXings = fracXings.add( i - 1 + frac ); - - // reset vars for next waveset - maxSamp = 0.0; - minSamp = 0.0; - lengthCount = 0; - } - ); - lengthCount = lengthCount + 1; - if (thisSample > maxSamp) { maxSamp = thisSample; maxAmpIndex = i }; - if (thisSample < minSamp) { minSamp = thisSample; minAmpIndex = i }; - prevSample = thisSample; - }); - } - // convenience methods: - plot { |startWs=0, length=1| - var data = this.frameFor(startWs, length, false).postln; - var segment = signal.copyRange(data[0], data[0] + data[1] - 1); - var peak = max(segment.maxItem, segment.minItem.abs); - segment.plot("Waveset" + name + - ": startWs" + startWs + - ", length" + length + - ", sustain" + data[2].round(0.000001), - minval: peak.neg, - maxval: peak); - } - - eventFor { |startWs=0, numWs=5, repeats=3, playRate=1, useFrac = true| - var start, length, sustain1; - #start, length, sustain1 = this.frameFor(startWs, numWs, useFrac); - ^(start: start, - length: length, - sustain: sustain1 / playRate * repeats, - playRate: playRate, - buf: buffer, - instrument: defaultInst, - wsAmp: this.ampFor(startWs, numWs) - ) - } - - ampFor { |startWs, length=1| - ^amps.copyRange(startWs, startWs + length - 1).maxItem; - } - - frameFor { arg startWs, numWs = 1, useFrac = true; - var whichXings = if (useFrac) { fracXings } { xings }; - var startFrame = whichXings.clipAt(startWs); - var endFrame = whichXings.clipAt(startWs + numWs); - var length = endFrame - startFrame; - var sustain = length / sampleRate; - - ^[startFrame, length, sustain] - } - -}