Recursive Analysis of Amplitude

Hi there - I’m attempting to figure out how to make a function that switches an input signal when the audio has remained unchanged for a specified duration of time.

The general idea is as follows (although I didn’t set up a particular dynamic input state in this example, so you might have to just imagine the “signal” varying wildly);

{
	var sig, sel, latch, trig, stored, which=0;
	
	sig = Select.ar(which, [SinOsc.ar, Saw.ar, WhiteNoise.ar, SoundIn.ar(0)]);
	sig = Amplitude.ar(sig);
	sel =  Changed.ar(sig, 0.01) > 0.5;
	trig = Trig1.ar(sel, 5); 
	stored = Latch.ar(sig, trig);
	stored = Changed.ar(latch, 0.1) > 0.5;
	which = TRand.kr(0, 4, stored);
}.play;

This throws an interesting error (ERROR: HPZ1 first input is not audio rate: nil nil) that I don’t entirely understand… but I do understand you can’t do this in SuperCollider without some additional bells and whistles, since I’m changing something that has already been declared.

Does anyone know of a workaround? Thanks!

Did you mean ‘latch = Latch.ar…’ rather than ‘stored = Latch.ar…’? As it is, latch is undefined which is why the SynthDef throws an error.

Ah yes, sorry about that, @Thor_Madsen.
I’ve revised the code a little more to better explain what the issue is:
Here, there is an array of different oscillators being used with a Select.ar.
This Select.ar uses the variable “busSet” to choose which oscillator - and defaults to 0.
Oscillator “0” is then analyzed for amplitude changes - and if it changes, it triggers a new busSet - but this busSet cannot be recursively applied to the Select.ar, as far as I know… although, I am hoping there is a workaround.
Thanks!


{
	var sig, sel, array, analysis, trig, stored, newTrig, busSet=0;
	array = [
		SinOsc.ar(LFDNoise0.ar(3).range(100, 900))*SawDPW.ar(LFDNoise1.ar(1).range(1, 20))*LFDNoise1.ar(0.3).range(0, 3),
		Saw.ar(LFDNoise1.ar(300).range(900, 1200))*LFDNoise1.ar(0.1),
		LFTri.ar(Latch.ar(SinOsc.ar(0.4), Impulse.ar(1)).range(100, 1200))*LFDNoise1.ar(0.3)];

	sig = Select.ar(busSet, array);
	analysis = Amplitude.ar(sig);
	sel =  Changed.ar(analysis, 0.0001) > 0.5;
	trig = Trig1.ar(sel, 5);
	stored = Latch.ar(sig, trig);
	newTrig= Changed.ar(stored, 0.0001) > 0.5;
	newTrig = Trig.ar(newTrig, 1);
	busSet = TRand.kr(0, 2, newTrig).round(1);
	Out.ar(0, sig);
}.play;

Does this do what you are after?

(
{
	var sig, sel, array, analysis, trig, stored, newTrig, busSet=0;
	array = [
		SinOsc.ar(LFDNoise0.ar(3).range(100, 900))*SawDPW.ar(LFDNoise1.ar(1).range(1, 20))*LFDNoise1.ar(0.3).range(0, 3),
		Saw.ar(LFDNoise1.ar(300).range(900, 1200))*LFDNoise1.ar(0.1),
		LFTri.ar(Latch.ar(SinOsc.ar(0.4), Impulse.ar(1)).range(100, 1200))*LFDNoise1.ar(0.3)];

	// sig = Select.ar(busSet, array);
	sig = array[0];
	analysis = Amplitude.ar(sig);
	sel =  Changed.ar(analysis, 0.0001) > 0.5;
	trig = Trig1.ar(sel, );
	stored = Latch.ar(sig, trig);
	newTrig= Changed.ar(stored, 0.0001) > 0.5;
	newTrig = Trig.ar(newTrig, 1);
	busSet = TRand.kr(0, 2, newTrig).round(1);
	sig = Select.ar(busSet.poll, array);
	Out.ar(0, sig);
}.play;
)

No, the issue here is that the signal being analyzed is permanently set as array[0]. I’d like it to analyze whatever is currently being switched on, via Select.ar.

“Recursive” isn’t really the word you’re looking for. The word is: feedback. (Well, sort of… sometimes filters with feedback terms are called “recursive filters”! So I overstated it a bit.)

Anytime you have a DSP operation that depends on the result of the operation, you need feedback.

Here, the analysis depends on the signal that has been chosen, but the choice of signal depends on the analysis. There is no other way to do this except to feed some data back from a later part of the graph toward an earlier part.

Try like this:

(
{
	var sig, sel, array, analysis, trig, stored, newTrig, busSet=0;
	array = [
		SinOsc.ar(LFDNoise0.ar(3).range(100, 900))*SawDPW.ar(LFDNoise1.ar(1).range(1, 20))*LFDNoise1.ar(0.3).range(0, 3),
		Saw.ar(LFDNoise1.ar(300).range(900, 1200))*LFDNoise1.ar(0.1),
		LFTri.ar(Latch.ar(SinOsc.ar(0.4), Impulse.ar(1)).range(100, 1200))*LFDNoise1.ar(0.3)
	];

	// this is initially 0
	// after the first block, it will be the result calculated below
	busSet = LocalIn.kr(1);

	sig = Select.ar(busSet, array);
	analysis = Amplitude.ar(sig);
	sel = Changed.ar(analysis, 0.0001) > 0.5;
	trig = Trig1.ar(sel, 5);
	stored = Latch.ar(sig, trig);
	newTrig = Changed.ar(stored, 0.0001) > 0.5;
	newTrig = Trig.ar(newTrig, 1);
	busSet = TRand.kr(0, 2, newTrig).round(1);

	// throw the busSet result back up to where it's needed
	LocalOut.kr(busSet);

	Out.ar(0, (sig * 0.1).dup);
}.play;
)

hjh

1 Like