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