Skip to content

Instantly share code, notes, and snippets.

@catfact
Last active July 29, 2021 04:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save catfact/a659f515a64a6c8852b7b500760c3e74 to your computer and use it in GitHub Desktop.
Save catfact/a659f515a64a6c8852b7b500760c3e74 to your computer and use it in GitHub Desktop.
Routine {
s = Server.default;
s.waitForBoot;
// max buffer duration
~dur = 8.0;
// 4-channel buffer: pitch, clar, amp, flatness
~buf = Buffer.alloc(s, s.sampleRate * ~dur, 4);
// record analysis
SynthDef.new(\ana_rec, {
arg buf, in=0, trig=0, loop=1, run=0;
var snd, hz, clar, amp, flat;
snd = SoundIn.ar(in);
amp = Amplitude.kr(snd);
#hz, clar = Pitch.kr(snd, clar:1);
flat = SpecFlatness.kr(FFT(LocalBuf(1024), snd));
// just record everything at audio rate
RecordBuf.ar(K2A.ar([amp, hz, clar, flat]), buf, trigger:trig, loop:loop, run:run);
}).send(s);
// play back analysis results as oscilaltor parameters
// NB: in var/arg names, "pitch" is logarithmic frequency and "hz" is linear.
SynthDef.new(\ana_play, {
//--- arguments
arg buf, // analysis buffer
out=0, pan=0, level=0.25,
// playback controls
rate=1, trig=0, pos=0, loop=1,
// quantization amounts
pitch_quant=0, amp_quant=0, timbre_quant=0,
// logarithmic pitch must be scaled around an arbitray reference point
pitch_center = 60, // A440
// amp scaling (pre-quantization)
amp_mul=0.0, amp_add=0.5,
// pitch scaling (pre-quantization)
pitch_mul=1.0, pitch_add=0.0,
// clarity threhsold for pitch hysteresis
clar_thresh = 0.9;
//--- variables
var pb; // playback from analysis buffer
var amp, hz, clar, flat; // analysis channels
var osc; // oscaillator output
var pitch; // intermediate log pitch value
// quantized parameters
var quant_hz, quant_amp, quant_timbre;
pb = PlayBuf.ar(4, buf, rate, trig, pos, loop);
amp = pb[0];
hz = pb[1];
clar = pb[2];
flat = pb[3];
// hold pitch when clarity is low
//////////////////////
// reader exercise:
// might want more sophisticated pitch hysteresis / filtering algorithm
hz = Gate.ar(hz, clar > clar_thresh);
// apply quantizations
quant_amp = (amp * amp_mul + amp_add).round(amp_quant);
pitch = hz.cpsmidi;
pitch = (pitch - pitch_center) * pitch_mul + pitch_center + pitch_add;
quant_hz = pitch.round(pitch_quant).midicps.min(12000).max(0);
/////////////////////
// reader exercise:
// linear spectral flatness doesn't really map well to this "timbre" parameter.
// might want to put in log scale and clamp bounds
quant_timbre = flat.round(timbre_quant);
//////////////////////
// reader exercise 2:
// smoothing times could be parameterized
quant_amp = LagUD.ar(quant_amp, 0.001, 0.1);
quant_hz = Lag.ar(quant_hz, 0.01);
quant_timbre = Lag.ar(quant_timbre, 0.1);
osc = SelectX.ar(quant_timbre, [SinOsc.ar(quant_hz), Saw.ar(quant_hz)]) * quant_amp;
Out.ar(out, Pan2.ar(osc, pan, level));
}).send(s);
s.sync;
~ana_rec_s = Synth.new(\ana_rec, [\buf, ~buf], s);
~ana_play_s = Synth.new(\ana_play, [\buf, ~buf], s);
////////////////
////////////////
//// a very basic GUI
{
~specs = [
(name: \rate, min:-8, max:8, step:0.125),
(name: \pitch_add, min:-24, max:24, step:1),
(name: \pitch_quant, min:0, max:12, step:0.125),
(name: \pitch_center, min:1, max:100, step:0.1),
(name: \pitch_mul, min:-2.0, max:2.0, step:0.0625)
//... add more entries here for more param controls
];
w = Window.new(bounds:Rect(0, 0, 300, 400));
l = FlowLayout( w.view.bounds, 10@10, 20@5 );
w.view.decorator = l;
Button(w, 80@40)
.states_([["record", Color.black, Color.green], ["stop", Color.white, Color.red]])
.action_({ |v|
v.value.postln;
if(v.value > 0, {
~ana_rec_s.set(\run, 1);
~ana_rec_s.set(\trig, 0);
~ana_rec_s.set(\trig, 1);
}, {
~ana_rec_s.set(\run, 0);
});
});
// collection of number boxes
n = ();
~specs.do({ |spec|
l.nextLine;
StaticText(w, 80@40).string_(spec.name.asString);
n[spec.name] = NumberBox(w, 120@40)
.action_({|v| ~ana_play_s.set(spec.name, v.value); })
.clipLo_(spec.min)
.clipHi_(spec.max)
.decimals_(3)
.step_(spec.step)
});
w.front;
// initialize numboxes to current values
~specs.do({ |spec|
spec.postln;
~ana_play_s.get(spec.name, {|val|
val.postln;
{ n[spec.name].value_(val); }.defer;
});
});
}.defer;
}.play;
@catfact
Copy link
Author

catfact commented Sep 9, 2018

basic demonstration in supercollider of:

  • recording analysis parameters (pitch, &c) to a buffer
  • playing back the analysis buffer as oscillator parameters
  • applying "musical" transformations to the parameters (particularly pitch)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment