Detecting amplitude changes in a audio buffer

I am working with an audio file in a buffer containing a piano piece. I wanted to detect the time points where a note is played in the audio, to pan each performed note in the file to a different channel. I am not sure Amplitude is the right tool for this, since it doesn’t allow me to define a threshold for the amplitudes ?
Can any one help?

You might want to check out Flucoma for this, BufNoveltySlice (I think thats what its call, or one of the alternatives) is pretty good at this short of thing. They all produce another buffer with the samples points of where the event happened.

2 Likes

The general philosophy of audio programming environments is to give you the components, and you build what you need.

An amplitude measurement is one component of a threshold operation.

The other is a comparator (> or >=), or a Schmitt trigger (misspelled in SC). You use the Amplitude as one input to one of these.

It would be counterproductive if Amplitude did the thresholding internally because it’s useful to have access to the raw Amplitude value.

hjh

2 Likes

Try Onsets and Coyote for onset detection!

To demonstrate:

// easiest SC piano: sc3-plugins
(
SynthDef(\mda, { |out, gate = 1, freq = 440, amp = 0.6|
	var sig = MdaPiano.ar(freq, gate, amp * 127);
	DetectSilence.ar(sig.sum, 0.001, 0.1, doneAction: 2);
	Out.ar(out, sig);
}).add;
)

// Let's get an audio source running

(
Ndef(\p, Pbind(
	\instrument, \mda,
	\midinote, Pwhite(24, 84, inf),
	\dur, Pexprand(0.08, 1.8, inf),
	\legato, Pexprand(0.3, 4.0, inf),
	\amp, Pwhite(0.0, 1.0, inf).lincurve(0, 1, 0.25, 0.8, 2)
)).play;
)

/*
For any kind of threshold, you need to know the source values.
Always look at the data before trying to make decisions based on them!
*/

(
Ndef(\amp, {
	var sig = Ndef(\p).ar(2).sum;  // mono is OK
	var amp = Amplitude.kr(sig, 0.01, 0.8);
	amp.poll;  // find out what values you're getting
});
)

Ndef(\amp).free;

// average level between notes ~= 0.04, 0.05
// strong notes go as high as 0.6 or more

(
Ndef(\threshold, {
	var sig = Ndef(\p).ar(2).sum;  // mono is OK
	var amp = Amplitude.kr(sig, 0.01, 0.8);
	// In practice, I had to fiddle with these numbers quite a lot
	var trig = Schmidt.kr(amp, 0.07, 0.2);
	SendReply.kr(trig, '/hit', amp);
	amp
});
)

// Visualize the onsets with a blinky button

(
var nextOff;

b = Button(nil, Rect(800, 200, 110, 30)).front;
b.states_([["", Color.white, Color.gray(0.2)], ["hit", Color.white, Color(0.3, 0.8, 0.3)]]);

OSCdef(\hit, { |msg|
	nextOff = SystemClock.seconds + 0.25;
	defer { b.value = 1 };
	AppClock.sched(0.25, {
		if(SystemClock.seconds - nextOff > -0.05) {
			b.value = 0
		}
	});
}, '/hit', s.addr);
)

/*
This is quite bad at detecting quiet notes
right after a loud note. semiquaver is right
that Onsets is *much* better.
*/

(
Ndef(\threshold, {
	var sig = Ndef(\p).ar(2).sum;  // mono is OK
	var fft = FFT(LocalBuf(512), sig);
	var trig = Onsets.kr(fft);
	SendReply.kr(trig, '/hit');
	trig
});
)

// done:
Ndef(\threshold).free;
OSCdef(\hit).free;
Ndef(\p).free;
b.close;

hjh

1 Like

a simple efficient silent removal example is in FluidBufAmpGate - set your thresholds and you can quickly get the Schmidt-triggered gate points.