Amplitude analysis

Hey,

I dont know why this code is not working. I would like to start a process, if the amplitude is above a certain threshold. My if statement doesnt work, it only takes condition two in the if statement.
Can someone help? Thank you :slight_smile:

(
SynthDef(\analy, {
	var input, trig, amp, freq;
	input = Impulse.ar(1);
	amp = Amplitude.kr(input);
	trig = Impulse.kr(10);
	SendReply.kr(trig, '/amp', amp);
	Out.ar(0, input);
}).add;
)

~analy = Synth(\analy);

(
OSCdef(\analy, {|msg, time|
	var amp, freq;
	amp = msg[3];
	amp.postln;
	if(amp > 0.5, {"yes".postln;}, {"no".postln;});
}, '/amp');
)

Let’s have a look at the post window output:

0.0
no
1.360460463902e-12
no
0.0
no

Your if statement says: if the amplitude is > 0.5, say “yes,” otherwise “no.”

But the values coming back are on the order of 0.00000000000136046 – so, the if statement is doing exactly what it should do.

There are two problems:

  1. Amplitude is actually a type of low pass filter which, when abs(signal) is going up, slides upward according to a coefficient, and when it’s going down, slides downward according to a different coefficient. Usually the coefficients are set so that the rise is very fast and the decay is slower (but the default values are equal).

    It does not measure peak values or RMS.

    So you might be expecting when a 1.0 impulse gets the input, that Amplitude would output 1.0 for some time. That’s not how it works. Impulses are the worst case for this UGen because the output value has time to rise only a fraction of the input value.

  2. What if an impulse occurs between SendReply triggers? The OSC messages will not reflect the impulse in the middle.

So you should a/ set the threshold much lower, empirically and b/ use the threshold condition itself as the trigger for SendReply (and remove the if in the OSCdef).

(
SynthDef(\analy, {
	var input, trig, amp, freq;
	input = Impulse.ar(1);
	amp = Amplitude.kr(input);
	SendReply.kr(amp >= 1e-13, '/amp', amp);
	Out.ar(0, input);
}).add;
)

~analy = Synth(\analy);

(
OSCdef(\analy, {|msg, time|
	var amp, freq;
	"yes".postln;
}, '/amp');
)

hjh

2 Likes

one small tip: it’s generally much better to use Amplitude.ar and not Amplitude.kr. transients in percussive signals are often faster than control rate, so kr is potentially discarding useful information. also, the default settings are a bit silly, setting both attack and release at 10ms, which isn’t very musical.

a good start for analysis would be something like Amplitude.ar(input, 0.001, 1.0). feel free to tweak the parameters as needed for your application.

1 Like

Not that much information actually – I checked earlier: Amplitude has a calculation function for ar input and kr output. It does calculate the filter function internally at audio rate (all input samples), so it shouldn’t miss transients. But the output timing resolution is lower than ar.

That’s correct about the parameters – the defaults are not useful at all. Most responsive will be a short attack and long decay, as you said.

hjh

1 Like

IMO Amplitude is buggy in a doubled sense. I’ve mentioned this several times and filed on the issue tracker, the discussion is still open.

I think I have a better example now:

(
x = { LFPulse.ar(1) };
a = { Amplitude.ar(x.(), 0.01, 0.3) };
b = { Amplitude.kr(x.(), 0.01, 0.3) };
{ 1; a.() }.plot(1);
{ 2; b.() }.plot(1);
)

Both release times (60 db convergence resp. 1/1000) are wrong, the first one obviously, and the second one also if you check like this:

o = OSCFunc({ |msg| msg.postln }, '/amp');

(
{ 
	var src = Amplitude.kr(LFPulse.ar(1), 0.01, 0.3);
	var trig = TDuty.kr(Dseq([0.51]), 0, DC.kr(1));
	SendReply.kr(trig, '/amp', src);
	Silent.ar
}.play
)

I never understood why release times should have a different meaning in ar and kr, moreover they both seem to be wrong in either case.

To be a bit more clear: after LFPulse’s starting level of 1 Amplitude should go back to level 0.001 within 0.3 seconds (so with trigger at 0.8 sec), no ? It does this neither with ar nor kr. But meanwhile I found this:

(
{ 
	var src = Amplitude.ar(LFPulse.ar(1), 0.01, 0.3);
	var trig = TDuty.ar(Dseq([0.8]), 0, DC.ar(1));
	SendReply.ar(trig, '/amp', src);
	Silent.ar
}.play
)

-> 

[ /amp, 1009, -1, 1 ]
[ /amp, 1009, -1, 0.099958881735802 ]

which is suspiciously close to 0.01 !
So might there be a digit error with ar ?

In the kr case an ar source goes totally wrong:

(
{ 
	var src = Amplitude.kr(LFPulse.ar(1), 0.01, 0.3);
	var trig = TDuty.kr(Dseq([0.8]), 0, DC.kr(1));
	SendReply.kr(trig, '/amp', src);
	Silent.ar
}.play
)

-> 

[ /amp, 1012, -1, 1 ]
[ /amp, 1012, -1, 0 ]

whereas a kr source shows up nearly the same fall to ca. 0.01.

(
{ 
	var src = Amplitude.kr(LFPulse.kr(1), 0.01, 0.3);
	var trig = TDuty.kr(Dseq([0.8]), 0, DC.kr(1));
	SendReply.kr(trig, '/amp', src);
	Silent.ar
}.play
)

-> 

[ /amp, 1013, -1, 1 ]
[ /amp, 1013, -1, 0.097491025924683 ]

So the same error as in case 1 ?

meant 0.1 of course !
As in the kr case.