|
// wait and hear, denmark |
|
// accompanying 4-channel soundscape for the wait and hear presentation at Sound Art Lab in 2024 |
|
// 2024, Till Bovermann |
|
|
|
( |
|
q = q ? (); |
|
q.numChans = 4; // needs to be an even number |
|
q.playbackDurationRange = ControlSpec(30, 50); // seconds |
|
q.freqRange = ControlSpec(10, 20000, \exp); // Hz |
|
// s.options.memSize_(600 * 1024 * 1024); |
|
s.options.numOutputBusChannels = q.numChans; |
|
// s.options.device = "Scarlett 6i6 USB"; |
|
s.options.device = "BlackHole 64ch"; |
|
s.reboot; |
|
s.meter; |
|
s.makeWindow; |
|
|
|
/////////// load buffers |
|
q.dirname = thisProcess.nowExecutingPath.dirname; |
|
q.fileNames = (q.dirname +/+ "_renders/*.wav").pathMatch; |
|
q.loopFileNames = (q.dirname +/+ "_renders/loops/*.wav").pathMatch; |
|
// assume all soundfiles to have the same length, samplerate and number of channels |
|
s.waitForBoot{ |
|
q.soundfile = SoundFile.openRead(q.fileNames[0]); |
|
q.soundfile.close; |
|
// cue all the files at random positions |
|
q.allBuffers = q.fileNames.collect({ |path| |
|
Buffer.cueSoundFile(s, path, rrand(0, q.playbackDurationRange.map(1)), q.soundfile.numChannels) |
|
}); |
|
q.loopBuffers = q.loopFileNames.collect({ |path| |
|
Buffer.read(s, path) |
|
}); |
|
} |
|
) |
|
|
|
|
|
|
|
|
|
////////////////// synthesis definitions |
|
( |
|
// a Synthdef that plays back a buffer for a given duration using DiskIn |
|
SynthDef(\playback, {|out = 0, buf = 0, dur = 10, attack = 0.5, decay = 0.5, pos = 0, amp = 0.1, spread= 0.5, width = 2| |
|
var snd, env; |
|
var lpFreq = \lpFreq.kr(2000); |
|
var lpRq = \lpRq.kr(1); |
|
|
|
var hpFreq = \hpFreq.kr(200); |
|
var hpRq = \hpRq.kr(1); |
|
|
|
// create an envelope with attack and decay |
|
env = Env.linen(attack, dur - (attack + decay), decay).ar(doneAction: 2); |
|
|
|
// buffer playback |
|
snd = DiskIn.ar(q.soundfile.numChannels, buf); |
|
|
|
// simple musical filter |
|
// snd = RLPF.ar(snd, lpFreq, lpRq); |
|
// snd = RHPF.ar(snd, hpFreq, hpRq); |
|
|
|
// panning |
|
snd = SplayAz.ar( |
|
numChans: q.numChans, |
|
inArray: snd * env, |
|
spread: spread, |
|
center: pos, |
|
level: amp, |
|
width: width, |
|
orientation: 0.5 |
|
); |
|
|
|
Out.ar(out, snd.tanh); |
|
}).add; |
|
|
|
|
|
// some sines |
|
SynthDef(\sine, {|out = 0, freq = 100, dur = 10, attack = 0.5, decay = 0.5, pos = 0, amp = 0.1, spread= 0.5, width = 2| |
|
var snd, env, rev, revEnv; |
|
var lpFreq = \lpFreq.kr(2000); |
|
var lpRq = \lpRq.kr(1); |
|
|
|
var hpFreq = \hpFreq.kr(200); |
|
var hpRq = \hpRq.kr(1); |
|
|
|
// create an envelope with attack and decay |
|
env = Env.linen(attack, dur - (attack + decay), decay).ar(doneAction: 0); |
|
revEnv = Env.linen(attack, dur - (attack + decay), decay + 20).ar(doneAction: 2); |
|
freq = (freq + [0, \freqSpread.kr(10)]) * [LFNoise2.kr(Rand(0.5, 2)).range(0.95, 1.0), LFNoise2.kr(Rand(0.5, 2)).range(0.95, 1.0)]; |
|
|
|
freq = freq * AmpCompA.kr(freq, 25); |
|
// sinewave |
|
snd = SinOscFB.ar(freq, \fb.kr(0.5)); |
|
|
|
|
|
// simple musical filter |
|
// snd = RLPF.ar(snd, lpFreq, lpRq); |
|
// snd = RHPF.ar(snd, hpFreq, hpRq); |
|
|
|
// panning |
|
snd = SplayAz.ar( |
|
numChans: q.numChans, |
|
inArray: snd * env, |
|
spread: spread, |
|
center: pos, |
|
level: amp, |
|
width: width, |
|
orientation: 0.5 |
|
); |
|
|
|
rev = AdCVerb.ar(snd, 10, nOuts: q.numChans) * revEnv; |
|
|
|
Out.ar(out, (snd + (0.25 * rev)).tanh * 0.5); |
|
}).add; |
|
|
|
// some noise |
|
SynthDef(\noise, {|out = 0, dur = 10, attack = 0.5, decay = 0.5, pos = 0, amp = 0.1, spread= 0.5, width = 2| |
|
var snd, env; |
|
var lpFreq = \lpFreq.kr(2000); |
|
var lpRq = \lpRq.kr(1); |
|
|
|
var hpFreq = \hpFreq.kr(200); |
|
var hpRq = \hpRq.kr(1); |
|
|
|
// create an envelope with attack and decay |
|
env = Env.linen(attack, dur - (attack + decay), decay).ar(doneAction: 2); |
|
|
|
// noise |
|
snd = {PinkNoise.ar}!2; |
|
|
|
// simple musical filter |
|
snd = LPF.ar(snd, lpFreq); |
|
snd = HPF.ar(snd, hpFreq); |
|
|
|
// panning |
|
snd = SplayAz.ar( |
|
numChans: q.numChans, |
|
inArray: snd * env, |
|
spread: spread, |
|
center: pos, |
|
level: amp, |
|
width: width, |
|
orientation: 0.5 |
|
); |
|
|
|
Out.ar(out, snd.tanh); |
|
}).add; |
|
|
|
) |
|
|
|
|
|
//////////////// playback routine for oneshots |
|
( |
|
q.playingBuffers = Set[]; |
|
q.allIndices = Array.iota(q.allBuffers.size).asSet; |
|
q.cueAndPlay = { |
|
var idx, buf, bufFilename, dur, pos, lpFreqStart, lpFreq, hpFreq; |
|
|
|
idx = (q.allIndices - q.playingBuffers).choose; |
|
idx.isNil.if({ "all buffers currently playing".postln; }, { |
|
buf = q.allBuffers[idx]; |
|
bufFilename = q.fileNames[idx]; |
|
dur = q.playbackDurationRange.map(1.0.rand); |
|
pos = rrand(0, 2.0); |
|
lpFreqStart = 1.0.sum3rand + 1 /2; // randomize the filter frequency |
|
|
|
// where to cut lows |
|
hpFreq = q.freqRange.map(lpFreqStart); |
|
|
|
// where to cut highs |
|
lpFreq = q.freqRange.map(rrand(lpFreqStart, 1.0)); |
|
|
|
r { |
|
q.playingBuffers.add(idx); |
|
"playing buffer % % for % seconds at %".format(idx, bufFilename.basename, dur, pos).postln; |
|
// buf.cueSoundFile(bufFilename, (q.soundfile.duration - dur).rand * q.soundfile.sampleRate, q.soundfile.numChannels); |
|
buf.cueSoundFile(bufFilename, 0, q.soundfile.numChannels); |
|
1.wait; // wait for the buffer to be ready |
|
Synth(\playback, [ |
|
\buf, buf, |
|
\dur, dur, |
|
\attack, 10, |
|
\decay, 15, |
|
\pos, pos.round(0.25), |
|
// \amp, ((1.0.sum3rand + 1 /2) * 0.5 + 0.5).postln, |
|
\amp, rrand(0.7, 1), |
|
\spread, 0.75, |
|
\width, 2, |
|
// \lpFreq, lpFreq, |
|
// \hpFreq, hpFreq, |
|
// \lpRq, [0.1, 2].asSpec.map(1.0.sum3rand + 1 /2), |
|
// \hpRq, [0.1, 2].asSpec.map(1.0.sum3rand + 1 /2), |
|
]); |
|
// wait for a longer period to make sure the sound will show up again only after a longer period of time |
|
(dur * 5).wait; |
|
q.playingBuffers.remove(idx); |
|
}.play; |
|
}); |
|
|
|
}; |
|
) |
|
|
|
///////////////////////// playback loops |
|
( |
|
// all one-shots randomly |
|
Tdef(\playback, { |
|
loop{ |
|
rrand(15, 25).wait; |
|
q.cueAndPlay |
|
} |
|
}).play; |
|
) |
|
Tdef(\playback).stop |
|
// play back a random buffer for a random duration |
|
// Synth(\playback, [\buf, q.allBuffers.choose, \dur, q.playbackDurationRange.map(1.0.rand).postln]); |
|
|
|
( |
|
// a low sine |
|
Tdef(\sineplayerLow, { |
|
loop{ |
|
var freq = exprand(25, 100.0); |
|
"Sine Low".postln; |
|
Synth(\sine, [\dur, q.playbackDurationRange.map(1.0.rand), \pos, 2.0.rand, \freq, freq, \amp, rrand(0.01, 0.06), \spread, 1, \attack, 20, \decay, 20, \fb, rrand(0.2, 0.7), \freqSpread, rrand(1.0, 7)].postln); |
|
rrand(60, 120.0).wait; |
|
} |
|
}).play |
|
) |
|
Tdef(\sineplayerLow).stop |
|
|
|
( |
|
// a high sine |
|
Tdef(\sineplayerHigh, { |
|
loop{ |
|
var freq = exprand(1000, 1500.0); |
|
"Sine High".postln; |
|
Synth(\sine, [\dur, q.playbackDurationRange.map(1.0.rand), \pos, 2.0.rand, \freq, freq, \amp, rrand(0.005, 0.008) * 0.5, \spread, 1, \attack, 20, \decay, 20, \fb, rrand(0.2, 0.7), \freqSpread, rrand(1.0, 7)].postln); |
|
rrand(120, 160.0).wait; |
|
} |
|
}).play |
|
) |
|
Tdef(\sineplayerHigh).stop |
|
|
|
|
|
// q.playingBuffers = q.allIndices.copy; |
|
|
|
( |
|
// some noise |
|
Tdef(\noiseplayer, { |
|
loop{ |
|
rrand(60, 120.0).wait; |
|
"Noise".postln; |
|
Synth(\noise, [\dur, q.playbackDurationRange.map(1.0.rand), \pos, 2.0.rand, \amp, rrand(0.01, 0.08), \spread, 1, \attack, 20, \decay, 20, \fb, rrand(0.2, 0.7), \freqSpread, rrand(1.0, 7), \hpFreq, exprand(20, 600.0), \lpFreq, exprand(500, 5000.0)].postln); |
|
} |
|
}).play |
|
) |
|
Tdef(\noiseplayer).play |
|
Tdef(\noiseplayer).stop |
|
|
|
( |
|
// a rumble loop |
|
Ndef(\looper).fadeTime = 10; |
|
Ndef(\looper, { |
|
var trigMute = Impulse.ar(1/LFNoise1.kr(0.1).range(220, 400)); |
|
( |
|
{PlayBuf.ar(2, q.loopBuffers.first, 1, loop: 1, startPos: 0.125 * q.loopBuffers.first.numFrames)}!(q.numChans/2)).flat |
|
* ({LFNoise2.kr(LFNoise1.kr(0.1).range(0.1, 0.03)).range(0.5, 1.0)}!q.numChans |
|
) * Env([1, 0, 0, 1], [20, 60, 60]).ar(gate: trigMute.poll(trigMute)) |
|
}); |
|
Ndef(\looper).play; |
|
Ndef(\looper).vol = 0.25; |
|
) |
|
|
|
// s.makeWindow; |
|
|
|
Ndef(\looper).edit |