Skip to content

Instantly share code, notes, and snippets.

@catfact
Last active March 12, 2022 18:12
Show Gist options
  • Save catfact/feb365921d7f6dace86e587795daaf3d to your computer and use it in GitHub Desktop.
Save catfact/feb365921d7f6dace86e587795daaf3d to your computer and use it in GitHub Desktop.
/// u-law
(
Routine { s = Server.default.waitForBoot;
// this N determines lookup table resolution
n = 512;
~unit = Array.fill(n, {|i| i.linlin(0, n-1, -1, 1) });
///////////////////////////////
//// main bit of mu-law
~mu = 255;
~compress_curve = ~unit.collect({ |x|
x.sign * log(1 + (~mu * x.abs)) / log(1 + ~mu);
});
~expand_curve = ~unit.collect({ |y|
y.sign / ~mu * ((1+~mu)**(y.abs) - 1);
});
///////////////////////////////
{
~compress_curve.plot;
~expand_curve.plot;
}.defer;
// blah blah get the curves into Buffers
~compress_buf = Buffer.loadCollection(s, Signal.newFrom(~compress_curve).asWavetableNoWrap);
~expand_buf = Buffer.loadCollection(s, Signal.newFrom(~expand_curve).asWavetableNoWrap);
b = Bus.audio(s, 1);
~sine = { arg hz=110, amp=1;
Out.ar(b.index, amp.lag(0.1) * SinOsc.ar(hz))
}.play(s);
~adc = { arg in=0, amp=1;
Out.ar(b.index, amp.lag(0.1) * SoundIn.ar(in))
}.play(s);
s.sync;
~crush = {
arg steps = 256, compAmt=1, expAmt=1;
var src, comp, x, crush, exp;
src = In.ar(b.index);
comp = Shaper.ar(~compress_buf.bufnum, src);
x = SelectX.ar(compAmt, [src, comp]);
crush = (x.abs * steps).round * x.sign / steps;
exp = Shaper.ar(~expand_buf.bufnum, crush);
ReplaceOut.ar(b.index, SelectX.ar(expAmt, [crush, exp]));
}.play(s, target:s, addAction:\addToTail);
s.sync;
~output = { arg amp=0;
Out.ar(0, amp.lag(0.1) * In.ar(b.index).dup)
}.play(target:~crush, addAction:\addAfter);
{
b.scope;
w = Window.new("ulawcrush", 600@140);
q = w.addFlowLayout(0@0,0@0);
EZSlider(w, 600@20, "hz",
ControlSpec(20, 7040, \exponential, default: 110, units:\Hz),
{|sl| ~sine.set(\hz, sl.value) }
);
EZSlider(w, 600@20, "steps",
ControlSpec(1, 1024, step:1, default:256),
{|sl| ~crush.set(\steps, sl.value) }
);
EZSlider(w, 600@20, "compression",
ControlSpec(0, 1, step:0, default:1),
{|sl| ~crush.set(\compAmt, sl.value) }
);
EZSlider(w, 600@20, "expansion",
ControlSpec(0, 1, step:0, default:1),
{|sl| ~crush.set(\expAmt, sl.value) }
);
EZSlider(w, 600@20, "sine",
ControlSpec(-60, 0, default:-60, units:\dB),
{|sl| ~sine.set(\amp, sl.value.dbamp) }
);
EZSlider(w, 600@20, "input",
ControlSpec(-60, 0, default:-60, units:\dB),
{|sl| ~input.set(\amp, sl.value.dbamp) }
);
EZSlider(w, 600@20, "output",
ControlSpec(-60, -6, default:-60, units:\dB),
{|sl| ~output.set(\amp, sl.value.dbamp) }
);
w.front;
}.defer;
}.play;
)
// try without buffer
//
// spoiler: doesn't work great.
// aside from performance, expect numerical issues
/*
(
Routine { s = Server.default.waitForBoot;
// bit crush with u-law
SynthDef.new(\compand_crush, {
arg io=0, mu=255, steps=256;
var x = In.ar(io);
// compress
x = x.sign * log(1+mu * x.abs) / log(1 + mu);
// crush
x = (x.abs * steps).round * x.sign / steps;
// expand
x = x.sign / mu * ((1+mu)**(x.abs) - 1);
ReplaceOut.ar(io, x);
}).send(s);
//... etc
}.play;
)
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment