master
Marc Hiatt 2 years ago
commit 6aaa87b3d0
  1. BIN
      DB 1H Time distribution table V3.pdf
  2. BIN
      IMG_3122.jpg
  3. 69
      readme.md
  4. 280
      sclang/generate-score_2023.scd
  5. 224
      sclang/init.scd
  6. 124
      sclang/triggers.scd

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

@ -0,0 +1,69 @@
# distances bending
## materials
the code and its player have four different voices at their disposal: *difference tones*, *noise flute*, *breath noise*, and *future tones*.
### tones
difference and future tones are generated live by supercollider oscillators.
the **difference tones** range from 20 to 160 herz. we call them difference tones because their frequencies are defined by differences between some of the frequencies played by clara and bec.
the **future tones** range from 800 to 6000 herz. at the times when they are played, they anticipate tones that clara and bec will play in the future, meaning later in the piece.
### samples
noise flute and breath noise are samples provided by clara and bec.
**noise flute** consists of eight mono samples (the files `S2_480_mix.wav`, `S2_560_mix.wav`, `S2_640_mix.wav`, etc. are named according to frequency; in the score, these will be sample `a`, `b`, `c`, etc.).
**breath noise** is a single stereo sample that lasts for around five minutes. in one-hour versions of the piece, it is triggered once, at the 35-minute mark.
## organization
in 2023 a one-hour version of the piece was presented (at oscillation and again at p.a.s.).
this version had six sections numbered II to VII. the table in `DB 1H Time distribution table V3.pdf` sets out the framework within which the code provided the ability (via `generate-score_2023.scd`) to specify scores for playing the samples and tones.
for example, in section II, the palette is constrained to:
- difference tones at 20 40 herz,
- noise flute samples a–c,
- future tones ranging from 800 to 1100 herz.
the hand-written notes in `IMG_3122.jpg` specify minimum and maximum numbers of occurrences for each class of event.
for example, in section II, these are:
- difference tones: at least 0 events, at most 1 event,
- noise flute: at least 0, at most 2 events,
- future tones: at least 1, at most 3 events, no frequency to be played more than once.
## code
init.scd
- instructions for setting up Babyface sound card with bbfpromix (linux)
- initialize server
- set up SynthDefs `\difference`, `\future`, `\mono_sampler`, `\stereo_sampler`
- amplitudes for the samples are hard-coded as arguments to `\mono_sampler` and `\stereo_sampler`
- populate Dicts and set up TUI triggers (with durations) for tones
- load samples
- instantiate sample synths and set up TUI triggers for samples
generate-score_2023.scd
- each section of the piece gets a Dictionary containing its sound materials (frequencies, sample identifiers) and its start time and duration (in minutes)
- post the temporal contraints we're working with
- specify the events and post them: this is the score a player can play from
- re-initialize some variables (in case we want to generate a new score)
triggers.scd
- pink noise for sound checking
- code blocks for triggering events. for each block, the first line or lines functions as a play button. for tones, the amplitude is also set (and can be altered) here. example:
```
( ~play_dTone.value(
"20hz", amp: 0.18, out: 1); )
~dTone_synths["20hz"].set(\gate, 0);
```
- note that samples don't have amplitudes modifiable at in the trigger blocks; as noted above, they're in init.scd
- the last line in each trigger block is the stop button

@ -0,0 +1,280 @@
(
// generate score II - V (unsorted)
// sound parameters - dif[ference tones], noi[se flute], fut[ure tones]
//
~ii = Dictionary.newFrom(
[\label, "II ", \dif, [[20,40], [1,1]], \noi, [[\a,\b,\c,], [0,1,2]],
\fut, [[800,880,900,1000,1100],[1,2,3]]]);
~iii = Dictionary.newFrom(
[\label, "III", \dif, [[40,80],[1,1]], \noi, [[\d,\e,\f],[0,1,2]],
\fut, [[1760,1800,2020],[1,2,3]]]);
~iv = Dictionary.newFrom(
[\label, "IV ", \dif, [[80,160],[0,1,2]], \noi, [[nil],[0]],
\fut, [[3000,3300,4200],[1,2,3]]]);
~v = Dictionary.newFrom(
[\label, "V ", \dif, [[0],[0]], \noi, [[\g,\h],[0,1,2]],
\fut, [[6000],[1]]]);
// temporal bounds
"".postln; "Temporal constraints".postln;
[~ii, ~iii, ~iv, ~v].do({ arg item, i; var a, b, c;
// minutes [start, duration]
a = [[0, 19], [20, 13], [40, 8], [49, 5]];
// .put: Associate two objects and add them to the Dictionary.
item.put(\start, a[i].at(0));
item.put(\duration, a[i].at(1));
item.put(\end, a[i].at(1)); // unused?
item.put(\bounds, a.at(i)); // unused?
// seconds - unused for now
b = a.deepCollect(2, { arg j; j * 60});
c = b.collect({ arg k; k[1] = k[0] + k[1]});
// post
(item[\label].asString + item[\bounds].asString + "minutes").postln });
// generate scores II - V (unsorted)
"".postln; " s c o r e".postln;
[~ii, ~iii, ~iv, ~v].do({ arg section, i;
"".postln;
(" * *" + section[\label]).postln;
// difference tones and noise samples
// may repeat
[\dif, \noi].do({ arg it; // the types of event we want
n = section[it][1].choose; // the no. of events of each type
if ( n > 0, {("##" + it.asString ++ ": ").postln}, {}); // label: type
n.do({ arg i;
(section[it][0].choose.asString ++ ": ").post; // freq / sample code
((section[\duration].rand + section[\start]).asString
+ "min." + 60.rand.asString).post; // timestamp
"".postln; "".postln;
});
});
// future tones
// one use per frequency
x = section[\fut][0];
n = section[\fut][1].choose; // the no. of events of each type
if ( n > 0, {("##" + \fut.asString ++ ": ").postln}, {}); // label: type
n.do ({ arg i;
// choose a frequency, return it and delete it from the array
(x.removeAt(x.size.rand).asString ++ ": ").post;
((section[\duration].rand + section[\start]).asString
+ "min." + 60.rand.asString).postln; // timestamp
});
// "".postln;
});
// since removeAt has deleted items from \fut,
// (because we didn't want future tones to be repeatable)
// restore the dictionary so that we can generate
// a new score when we want to
// TODO: make an unrepeating stream up to n and
// use that to select \fut items rather than removing them
f = [
[[800,880,900,1000,1100],[1,2,3]],
[[1760,1800,2020],[1,2,3]],
[[3000,3300,4200],[1,2,3]],
[[6000],[1]]];
// .add: if the key value pair already exists in the Dictionary, the key's value will be replaced
[~ii, ~iii, ~iv, ~v].do({ arg section, i; section.add(\fut -> f[i].value ) });
"";
)
// current code ends here
// below here, only scraps of earlier code
)
x = ~ii[\fut][0];
y = x.size.rand;
x.removeAt(x.size.choose);
x;
(
// generate scores II - V (unsorted, repeats allowed)
"".postln;
[~ii, ~iii, ~iv, ~v].do({ arg item, i;
("*" + item[\label]).postln;
[\dif, \noi, \fut].do({ arg it; // the types of event we want
n = item[it][1].choose; // the no. of events of each type
if ( n > 0, {("##" + it.asString ++ ": ").postln}, {}); // label: type
//"".postln;
n.do({ arg i;
(item[it][0].choose.asString ++ ": ").post; // freq / sample code
((item[\duration].rand + item[\start]).asString
+ "min." + 60.rand.asString).post; // timestamp
"".postln; "".postln;
});
});
"".postln;
});
"".postln;
)
(
// generate score II
"".postln; "* II *".postln;
"".postln;
[\dif, \noi, \fut].do({ arg it; // the types of event we want
n = ~ii[it][1].choose; // the no. of events of each type
if ( n > 0, {("##" + it.asString ++ ": ").postln}, {}); // label: type
//"".postln;
n.do({ arg i;
(~ii[it][0].choose.asString ++ ": ").post; // freq / sample code
(~ii[\duration].rand.asString
+ "min." + 60.rand.asString).post; // timestamp
"".postln; "".postln;
});
});
"".postln;
)
(
"".postln;
"~lengthII = ".post; ~lengthII = (11 * 60).postln; // total section time
"n = ".post; n = [1,2,3].choose.postln; // no. of rests
"~lengthRstsII = ".post; ~lengthRstsII = ( ~lengthII - (n * 31.5) ).postln;
// rest lengths will go in here
a = Array.fill([2,n], 0);
// iterate over n rest events
n.do({ arg i; var j, k;
j = i - 1;
// for this rest, put duration in row 0
k = if( i > 0, // after first iteration
{ a[1].at(j) }, { 0 }); //
a[0].put( i, (~lengthRstsII - k).rand.asInteger.max(30) );
// put sum of durations through n in row 1
l = if( i > 0,
{ a[0].at(i) + a[1].at(j) }, { a[0].at(i) } );
a[1].put( i, l);
});
a;
)
a;
a[0] = ~lengthRstsII.rand.asInteger;
a[1] = (~lengthRstsII - a[0]).rand.asInteger ;
a[2] = (~lengthRstsII - a[0])
~lengthRstsII - a[1];
(
n = 3;
n.do({
arg i;
j = i - 1;
a[i] = (~lengthRstsII - ( a.at(j) )).rand.asInteger;
});
)
(
j = 5;
while( {j > 0}, {
j.postln;
j = j - 1;
} )
)
(
n = 3;
n.do({ arg i; var j;
j = n - i - 1;
{j > -1}.while( {
j.postln;
j = (j - 1)
} )
});
)
(
n.do( { arg i; r = r.put( i, Rest().asString ) } )
)
// number of total elements
g = Array.fill(( n * 2 + 1 ), "rest");
// set the note durations to 30s
// n.do({ arg i; g = g.put( (i * 2 + 1), 30) });
g.size.do( { arg i; g = g.put(i, if( i.even, "Rest()", 30) ) } ); g
)
p = (n * 2 + 1);
d = Array.fill(p, { arg i;
if(
i.even, Rest
)
})
"total = ".postln; (~r3 + ~r1 + ~r2 + ~lengthEvtsII);
)
~durations = [Rest(~r1), 30, Rest(~r2), 30, Rest(~r3), 30];
(
"n = ".post; n = [1,2,3].choose.postln;
p = (n * 2 + 1);
f = Array.fill( p, { [20,40].choose }).postln;
d = Array.fill( p, { arg i; if(i.even, Rest(30.rand).asString, 30)});
)
(
p = Pbind (
\instrument, "sine",
\freq, Pseq([800, 0, 1100, 600], 1),
\dur, Pseq([2, Rest(1), 2, 2], 1),
\amp, 4
// \futrrelease, 2,
// \legato, 0.1
).play;
)

@ -0,0 +1,224 @@
/* to do -
- Stop self-abolition of samples
- triangle waves
- Gui
- DONE Make synths auto-gate after defined periods
- DONE that period randomized for future tones
*/
/*
4-ch Babyface setup:
- start Babyface in CC mode (hold SELECT + DIM while plugging in)
- pavucontrol, configuration tab: select Pro Audio profile
- pavucontrol, output devices tab: set babyface as fallback
- bbfpromix: select Hardware output AN1/2, then fade up AN1/2 software playback channels and pan 1 to L and 2 R.
- select Hardware output PH3/4, then fade up PH3/4 software playback channels and pan 3 to L and 4 R.
- Server.default.options.numOutputBusChannels = 4;
- boot sc server
*/
"reboot";
if ( s.serverRunning, {s.quit} );
(
o = Server.default.options;
o.numOutputBusChannels = 4;
)
"initialize";
(
// initialize synths
s.waitForBoot({
// init stages:
// 1 (add synth defs) **
// 2 ( Gui ) **
// 3 (populate SinOsc Dicts, provide ~play_) **
// 4 (populate sampler Array, provide ~play_) **
s.makeBundle(nil, {
// ** init: stage 1 (add synth defs) **
// # difference tones
SynthDef(\difference,
{ arg freq = 20, gate = 0, out = 1,
attack = 0.5, amp = 0.2, release = 1.0;
var env, audio;
env = Env.asr(attack, amp, release).kr(gate: gate);
audio = SinOsc.ar(freq, mul: env);
Out.ar(out, audio)
}).add;
// # future tones
SynthDef(\future,
{ arg freq = 800, amp = 0.01, gate = 0, out = 1,
attack = 0.5, sustain = 0.5, release = 0.5;
var audio, env;
env = Env.asr(attack, sustain, release).kr(gate: gate);
audio = 0.5 * (LFTri.ar(freq, mul: env * amp) + LFTri.ar(freq, mul: env * amp));
Out.ar(out, audio) }).add;
// # sampler
SynthDef(\mono_sampler,
{ arg sample, gate = 0, trigger = -1,
attack = 0.5, amp = 0.3, release = 0.5;
var env, audio;
env = Env.asr(attack, amp, release).kr(gate: gate);
audio = PlayBuf.ar(1, sample, trigger: trigger, loop: 1);
Out.ar(0, audio * env)
}).add;
// # sampler
SynthDef(\stereo_sampler,
{ arg sample, gate = 0, trigger = -1,
attack = 0.5, amp = 0.3, release = 0.5;
var env, audio;
env = Env.asr(attack, amp, release).kr(gate: gate);
audio = PlayBuf.ar(2, sample, trigger: trigger, loop: 1);
Out.ar([2], audio * env)
}).add;
// ** init: stage 2 ( Gui ) **
// wait for stage 1 to complete
s.sync;
s.makeGui;
//s.plotTree;
// ** init: stage 3 (populate Dicts, provide ~play_) **
// # 3.1 populate difference tone Dict
~dTone_synths = Dictionary.new(4);
[20,40,80,160].do({ arg item, i;
~dTone_synths.add( (item.asString ++ "hz") ->
Synth("difference", [
freq: item,
amp: [0.4, 0.1, 0.02, 0.01].at(i),
gate: 0 ]) ) });
// and set up triggers
~play_dTone = { arg key, amp = 0.2, out = 0;
{~dTone_synths[key].set(
\gate, 1,
\amp, amp,
\out, out );
~dTone_synths[key].get(\freq, { arg val;
( val.asInteger.asString + "Hz, 30 sec.").postln });
30.wait; // play for 30 sec
~dTone_synths[key].set(\gate, 0) // then release
}.fork
};
// # 3.2 populate future tone Dict
~fTone_synths = Dictionary.new(12);
[800, 880, 900, 1000, 1100, 1760,
1800, 2020, 3000, 3300, 4200, 6000].do({ arg item, i;
~fTone_synths.add( (item.asString ++ "hz") ->
Synth ("future", [
freq: item,
amp: 0.01,
gate: 0 ])
)});
// and set up triggers
~play_fTone = { arg key, amp = 0.01, out = 2;
{
// trigger the synth we want
~fTone_synths[key].set(
\gate, 1,
\amp, amp,
\out, out);
~futDuration = (16.rand + 15);
// post feedback
~fTone_synths[key].get(\freq, { arg val;
(val.asInteger.asString + "Hz,"
+ ~futDuration.value.asString + "seconds.")
.asString.postln });
// wait and then release
~futDuration.value.wait;
~fTone_synths[key].set(\gate, 0)
}.fork
};
// 3.3 load samples
~filenames = ["S2_480_mix.wav", "S2_560_mix.wav", "S2_640_mix.wav", "S2_720_mix.wav",
"S3_960_mix.wav", "S3_1120_mix.wav", "S3_1280_mix.wav", "S3_1440_mix.wav"];
~sampleBuffer = ~filenames.collect({
arg filename;
Buffer.read(s, Platform.userHomeDir.asString ++ "/a/23/audio/samples/Oscillation/samples/" ++ filename)});
~breathSample = ( Buffer.read(s,
Platform.userHomeDir.asString ++ "/a/23/audio/samples/Oscillation/breath_filter_stereo.wav") );
// ** init: stage 4 (populate sampler Array, provide ~play_) **
// wait for stage 3 to complete
s.sync;
// instantiate synths
~samp = Array.fill(8, {arg i; Synth(\mono_sampler, [\sample, ~sampleBuffer[i]])});
~playSamp = { arg key; //
// provide Dict associations ["a" -> 0, "b" -> 1... "h" -> 7]
d = Dictionary.new(8);
["a", "b", "c", "d", "e", "f", "g", "h"].do({ arg item, i;
d.add( item.asString -> i.asInteger );
});
// trigger samp
{ ~samp[d.at(key)].set(\trigger, 1, \gate, 1);
t = (180.rand + 120);
("Sample" + key.asString + "," + t.asString ++ "sec.").postln;
t.wait; ~samp[d.at(key)].set(\trigger, -1, \gate, 0)}.fork;
};
~breathSampleSynth = Synth(\stereo_sampler, [\sample, ~breathSample]);
~playBreathSamp = { ~breathSampleSynth.set(\trigger, 1, \gate, 1) };
~stopBreathSamp = { ~breathSampleSynth.set(\trigger, -1, \gate, 0) };
}) // end makeBundle
})
)
~dTone_synths.getPairs;
~dTone_synths["20hz"]; // return 20hz synth
d.getPairs;
d.at("d").postln;
d.postln;

@ -0,0 +1,124 @@
// test speakers
// ch 0, 1
y = { Out.ar (0, Pan2.ar(PinkNoise.ar, MouseX.kr(-1, 1), 0.05)) }.play;
y.free;
// ch 2, 3
z = { Out.ar (2, Pan2.ar(PinkNoise.ar, MouseX.kr(-1, 1), 0.05)) }.play;
z.free;
s.freeAll;
// * let's play *
// difference tones
( ~play_dTone.value(
"20hz", amp: 0.18, out: 1); )
~dTone_synths["20hz"].set(\gate, 0);
( ~play_dTone.value(
"40hz", amp: 0.025, out: 1); )
~dTone_synths["40hz"].set(\gate, 0);
( ~play_dTone.value(
"80hz", amp: 0.008, out: 1); )
~dTone_synths["80hz"].set(\gate, 0);
( ~play_dTone.value(
"160hz", amp: 0.005, out: 1); )
~dTone_synths["160hz"].set(\gate, 0);
// samples
( ~playSamp.value("a"); )
~samp[0].set(\trigger, -1, \gate, 0);
( "b";
~playSamp.value("b"); )
~samp[1].set(\trigger, -1, \gate, 0);
( "c";
~playSamp.value("c"); )
~samp[2].set(\trigger, -1, \gate, 0);
( "d";
~playSamp.value("d"); )
~samp[3].set(\trigger, -1, \gate, 0);
( "e";
~playSamp.value("e"); )
~samp[4].set(\trigger, -1, \gate, 0);
( "f";
~playSamp.value("f"); )
~samp[5].set(\trigger, -1, \gate, 0);
( "g";
~playSamp.value("g"); )
~samp[6].set(\trigger, -1, \gate, 0);
( "h";
~playSamp.value("h"); )
~samp[7].set(\trigger, -1, \gate, 0);
( "breath noise";
"start"; ~playBreathSamp.value; )
"stop"; ~stopBreathSamp.value;
// future tones
( ~play_fTone.value("800hz",
amp: 0.026, out: 1); )
~fTone_synths["800hz"].set(\gate, 0);
( ~play_fTone.value("880hz",
amp: 0.025, out: 1); )
~fTone_synths["880hz"].set(\gate, 0);
( ~play_fTone.value("900hz",
amp: 0.025, out: 1); )
~fTone_synths["900hz"].set(\gate, 0);
( ~play_fTone.value("1000hz",
amp: 0.025, out: 1); )
~fTone_synths["1000hz"].set(\gate, 0);
( ~play_fTone.value("1100hz",
amp: 0.024, out: 1); )
~fTone_synths["1100hz"].set(\gate, 0);
( ~play_fTone.value("1760hz",
amp: 0.023, out: 1); )
~fTone_synths["1760hz"].set(\gate, 0);
( ~play_fTone.value("1800hz",
amp: 0.023, out: 1); )
~fTone_synths["1800hz"].set(\gate, 0);
( ~play_fTone.value("2020hz",
amp: 0.023, out: 1); )
~fTone_synths["2020hz"].set(\gate, 0);
( ~play_fTone.value("3000hz",
amp: 0.023, out: 1); )
~fTone_synths["3000hz"].set(\gate, 0);
( ~play_fTone.value("3300hz",
amp: 0.023, out: 1); )
~fTone_synths["3300hz"].set(\gate, 0);
( ~play_fTone.value("4200hz",
amp: 0.022, out: 1); )
~fTone_synths["4200hz"].set(\gate, 0);
( ~play_fTone.value("6000hz",
amp: 0.021, out: 1); )
~fTone_synths["6000hz"].set(\gate, 0);
Loading…
Cancel
Save