169 lines
3.1 KiB
Text
169 lines
3.1 KiB
Text
class:: AmpComp
|
|
summary:: Basic psychoacoustic amplitude compensation.
|
|
related:: Classes/AmpCompA
|
|
categories:: UGens>Analysis>Amplitude
|
|
|
|
|
|
Description::
|
|
|
|
Implements the (optimized) formula:
|
|
|
|
code::
|
|
compensationFactor = (root / freq) ** exp
|
|
::
|
|
|
|
|
|
Higher frequencies are normally perceived as louder, which AmpComp
|
|
compensates.
|
|
|
|
|
|
classmethods::
|
|
|
|
method::ar, kr, ir
|
|
|
|
argument::freq
|
|
|
|
Input frequency value. For freq == root, the output is 1.0.
|
|
|
|
|
|
argument::root
|
|
|
|
Root freq relative to which the curve is calculated
|
|
(usually lowest freq).
|
|
|
|
|
|
argument::exp
|
|
|
|
Exponent: how steep the curve decreases for increasing freq.
|
|
|
|
discussion::
|
|
Note that for frequencies very much smaller than root the amplitudes can become very high.
|
|
In this case limit the freq with code::freq.max(minval)::, or use AmpCompA.
|
|
|
|
Examples::
|
|
|
|
code::
|
|
// compare a sine without compensation
|
|
|
|
{ SinOsc.ar(MouseX.kr(300, 15000, 1)) * 0.1 }.play;
|
|
|
|
// with one that uses amplitude compensation
|
|
(
|
|
{
|
|
var freq;
|
|
freq = MouseX.kr(300, 15000, 1);
|
|
SinOsc.ar(freq) * 0.1 * AmpComp.kr(freq, 300)
|
|
}.play;
|
|
)
|
|
|
|
|
|
// different sounds cause quite different loudness perception,
|
|
// and the desired musical behavior can vary, so the exponent can be tuned:
|
|
(
|
|
{
|
|
var freq;
|
|
freq = MouseX.kr(300, 15000, 1);
|
|
Pulse.ar(freq) * 0.1 * AmpComp.kr(freq, 300, 1.3)
|
|
}.play;
|
|
)
|
|
|
|
// the curves:
|
|
|
|
// exp = 0.3333
|
|
(200,210..10000).collect {|freq| (200/freq) ** 0.3333 }.plot;
|
|
|
|
// nearly linear for semitone steps:
|
|
|
|
(48..72).midicps.collect {|freq| (48.midicps/freq) ** 0.3333 }.plot;
|
|
{ AmpComp.ar(Line.ar(48, 72, 1).midicps, 48.midicps) }.plot(1.0);
|
|
|
|
// exp = 1.2
|
|
(200,210..10000).collect {|freq| (200/freq) ** 1.2 }.plot;
|
|
(48..72).midicps.collect {|freq| (200/freq) ** 1.2 }.plot;
|
|
{ AmpComp.ar(Line.ar(48, 72, 1).midicps, 48.midicps, 1.2) }.plot(1.0);
|
|
|
|
|
|
// amplitude compensation in frequency modulation
|
|
(
|
|
{
|
|
var freq;
|
|
freq = MouseX.kr(300, 15000, 1);
|
|
freq = freq * SinOsc.ar(MouseY.kr(3, 200, 1), 0, 0.5, 1);
|
|
SinOsc.ar(freq) * 0.1 * AmpComp.ar(freq, 300)
|
|
}.play;
|
|
)
|
|
|
|
// without amplitude compensation
|
|
(
|
|
{
|
|
var freq;
|
|
freq = MouseX.kr(300, 15000, 1);
|
|
freq = freq * SinOsc.ar(MouseY.kr(3, 200, 1), 0, 0.5, 1);
|
|
SinOsc.ar(freq) * 0.1
|
|
}.play;
|
|
)
|
|
|
|
// in granular synthesis:
|
|
(
|
|
SynthDef("pgrain",
|
|
{ arg out = 0, sustain=0.01, amp=0.5, pan = 0;
|
|
var freq = MouseX.kr(300, 7000, 1);
|
|
var window = Env.sine(sustain, amp * AmpComp.ir(freq));
|
|
Out.ar(out,
|
|
Pan2.ar(
|
|
SinOsc.ar(freq),
|
|
pan
|
|
) * EnvGen.ar(window, doneAction: Done.freeSelf)
|
|
)
|
|
}
|
|
).add;
|
|
)
|
|
|
|
// send grains
|
|
(
|
|
fork {
|
|
loop {
|
|
s.sendBundle(0.1, [\s_new, \pgrain, -1,1,1]);
|
|
0.02.wait;
|
|
};
|
|
}
|
|
)
|
|
|
|
|
|
// try different synth defs:
|
|
|
|
|
|
// without AmpComp:
|
|
|
|
(
|
|
SynthDef("pgrain",
|
|
{ arg out = 0, sustain=0.01, amp=0.5, pan = 0;
|
|
var freq = MouseX.kr(300, 7000, 1);
|
|
var window = Env.sine(sustain, amp);
|
|
Out.ar(out,
|
|
Pan2.ar(
|
|
SinOsc.ar(freq),
|
|
pan
|
|
) * EnvGen.ar(window, doneAction: Done.freeSelf)
|
|
)
|
|
}
|
|
).add;
|
|
)
|
|
|
|
// with AmpCompA
|
|
(
|
|
SynthDef("pgrain",
|
|
{ arg out = 0, sustain=0.01, amp=0.5, pan = 0;
|
|
var freq = MouseX.kr(300, 7000, 1);
|
|
var window = Env.sine(sustain, amp * AmpCompA.ir(freq));
|
|
Out.ar(out,
|
|
Pan2.ar(
|
|
SinOsc.ar(freq),
|
|
pan
|
|
) * EnvGen.ar(window, doneAction: Done.freeSelf)
|
|
)
|
|
}
|
|
).add;
|
|
)
|
|
::
|
|
|