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:2)
		)
	}
).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:2)
		)
	}
).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:2)
		)
	}
).add;
)
::

