GUI question: Related to quantization, Tasks, Pdefs, and General SuperCollider Behavior

Hello all,

I hope I will be able to explain this well. If you are reading this and need more information, please feel free to comment and I will update my question. Github repo is here: GitHub - spyridonpallis/SuperCollider-Sampler: Boilerplate code for a real-time audio sampler built using SuperCollider.

Overview: I am working on a GUI and am trying to update the color of 8 TextFields while also being considerate of the TempoClock and quantization of Pdefs. As the beat progresses, the TextFields should each change color to let me know where I am in the sequence.

Here is a screenshot of GUI. The highlighted 1s and 0s are mapped to a Pseq for the /amp argument. These Pseqs are playing from within Pdefs which are quantized to 4:

GUI:

CODE:

I have 2 Routines running as a PauseStream (TY for your help on this jamshark70).

The first Routine (~task1) is keeping track of the time/ beat and I am factoring in Duration here because if the duration argument is say, 8, it will take longer for the Amplitude values to trigger, compared to say, the duration is 1/32, which triggers very fast, so I am speeding up and slowing down accordingly.

The second Routine (~task2) is what is actually updating the colors of the TextFields. I’m using this ‘fulldur’ value to essentially break it down into beats of 4. Once the beats trigger the 4, 8, 12, 16… etc, the colors update and move down.

~task1 = PauseStream(Routine{
        inf.do {

					if(~durbank[~t1_a_dur_state_a] == 8){~q0dur = ~q0dur + 1; ~waitTime = 8};
					if(~durbank[~t1_a_dur_state_a] == 4){~q1dur = ~q1dur + 1; ~waitTime = 4};
					if(~durbank[~t1_a_dur_state_a] == 2){~q2dur = ~q2dur + 1; ~waitTime = 2};
					if(~durbank[~t1_a_dur_state_a] == 1){~q3dur = ~q3dur + 1; ~waitTime = 1};
					if(~durbank[~t1_a_dur_state_a] == 0.5){~q4dur = ~q4dur + 1; ~waitTime = 0.5};
					if(~durbank[~t1_a_dur_state_a] == 0.25){~q5dur = ~q5dur + 1; ~waitTime = 0.25};
					if(~durbank[~t1_a_dur_state_a] == 0.125){~q6dur = ~q6dur + 1; ~waitTime = 0.125};
					if(~durbank[~t1_a_dur_state_a] == 0.0625){~q7dur = ~q7dur + 1; ~waitTime = 0.0625};
					if(~durbank[~t1_a_dur_state_a] == 0.03125){~q8dur = ~q8dur + 1; ~waitTime = 0.03125};
					~fulldur = ~q0dur+~q1dur+~q2dur+~q3dur+~q4dur+~q5dur+~q6dur+~q7dur+~q8dur;
					//(((t.beats.floor)%t.beatsPerBar+1)).postln;
					if(~fulldur >= 32){
					~q0dur=0;
					~q1dur=0;
					~q2dur=0;
					~q3dur=0;
					~q4dur=0;
					~q5dur=0;
					~q6dur=0;
					~q7dur=0;
					~q8dur=0;
					~fulldur=0;
					};
					~fulldur.postln;
			(~waitTime*(t.tempo)).wait;
    }
});
~task1.play(quant:4);

~task2 = PauseStream(Routine {
    inf.do {
            {
					x = case
					//{(t.beats.floor+1) <= 4}
					//{(t.bar) == 1}
					{(~fulldur) <= 3}
					{~t1_a_amp_1_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_3_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{(t.beats.floor+1) <= 8}
					//{(t.bar) == 2}
					{(~fulldur) <= 7}
					{~t1_a_amp_1_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
					~t1_a_amp_3_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{(t.beats.floor+1) <= 12}
					//{(t.bar) == 3}
					{(~fulldur) <= 11}
					{~t1_a_amp_3_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
				    ~t1_a_amp_1_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{(t.beats.floor+1) <= 16}
					//{(t.bar) == 4}
					{(~fulldur) <= 15}
					{~t1_a_amp_4_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
					~t1_a_amp_3_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_1_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{(t.beats.floor+1) <= 20}
					//{(t.bar) == 5}
					{(~fulldur) <= 19}
					{~t1_a_amp_5_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_1_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{(t.beats.floor+1) <= 24}
					//{(t.bar) == 6}
					{(~fulldur) <= 23}
					{~t1_a_amp_6_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_1_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{(t.beats.floor+1) <= 28}
					//{(t.bar) == 7}
					{(~fulldur) <= 27}
					{~t1_a_amp_7_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_1_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{(t.beats.floor+1) <= 32}
					//{(t.bar) == 8}
					{(~fulldur) <= 31}
					{~t1_a_amp_8_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_1_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
					}
					//{t.beats.floor <= 36};
					{(t.beats.floor+1) < 31};

			}.defer;
	0.01.wait
    }
});
~task2.play(quant: 4);

Now, this is all working as expected. But where I am having trouble is getting this perfectly sync’d/ quantized with the Pdefs that is playing the Pseqs. Here is what one of my Pdefs looks like. This is in a separate function called ~kicks.value().

(
Pdef(\a1, Pbind(
\instrument, \mainbuf, \group, ~sources,
	\patch, ~patch1a,
	\amp, ~t1_a_pseq_1_amp_main_a,
	\dur, ~a1dur * ~t1_a_pseq_1_dur_var,
    \atk, ~a1atk * ~t1_a_pseq_1_atk_var,
    \rel, ~a1rel * ~t1_a_pseq_1_rel_var,
	\rate, ~a1rate * ~t1_a_pseq_1_rate_var,
    \pos, ~a1pos * ~t1_a_pseq_1_pos_var * ~rrand1,
	\bank, ~a1buf,
)).quant_(4);
);

Every time I update the duration, I need the .stringColor_ method to reset exactly with the Pdefs. I am handling this now with an approach that is ALMOST working, but not exactly. When I change the Duration, everything is being re-triggered at once. I am doing this:

  1. I am updating the waitTime based on the Duration.
  2. I am rescheduling the Task.
  3. I am triggering the function that contains the Pdef.
{
if(~durbank[~t1_a_dur_state_a] == 8){~waitTime = 8};
if(~durbank[~t1_a_dur_state_a] == 4){~waitTime = 4};
if(~durbank[~t1_a_dur_state_a] == 2){~waitTime = 2};
if(~durbank[~t1_a_dur_state_a] == 1){~waitTime = 1};
if(~durbank[~t1_a_dur_state_a] == 0.5){~waitTime = 0.5};
if(~durbank[~t1_a_dur_state_a] == 0.25){~waitTime = 0.25};
if(~durbank[~t1_a_dur_state_a] == 0.125){~waitTime = 0.125};
if(~durbank[~t1_a_dur_state_a] == 0.0625){~waitTime = 0.0625};
if(~durbank[~t1_a_dur_state_a] == 0.03125){~waitTime = 0.03125};
~task1 = ~reschedulePauseStream.(~task1, 4);
~task2 = ~reschedulePauseStream.(~task2, 4);
~kicks.value();
}.value();

The Routines are quantized to 4. The Pdefs are quantized to 4. But when I do this, sometimes the Pdef is being triggered way before.

For example,

If I am playing a beat on 1/4 duration and get to the fifth TextField, then update the beat to 1/8 duration, I will hear the duration pick up speed, but the color updating lags sometimes up to a full beat.

Does anyone have any better ideas on how to solve this problem? I am afraid I am spinning my wheels here and am just looking for some feedback/ ideas.

Thank you!

Off topic, but whenever you find yourself writing several variable names with numbers in it + lots of code duplication, consider using Arrays + loops instead!

~q0dur=0;
~q1dur=0;
~q2dur=0;
~q3dur=0;
~q4dur=0;
~q5dur=0;
~q6dur=0;
~q7dur=0;
~q8dur=0;

~q[0..] = 0;
{~t1_a_amp_1_a.stringColor_(Color.grey, rand(3,1), rrand(3, 17));
~t1_a_amp_2_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
~t1_a_amp_3_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
~t1_a_amp_4_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
~t1_a_amp_5_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
~t1_a_amp_6_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
~t1_a_amp_7_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
~t1_a_amp_8_a.stringColor_(Color.white, rand(3,1), rrand(3, 17));
}

~t1_a_amps_a.do { |item, i|
    var color = if (i == 0) { Color.grey } { Color.white };
    item.stringColor_(color, rand(3, 1), rrand(3, 17));
};

etc.

1 Like

I can’t quite understand your code, but my general suggestion would be to unify everything for one layer into a single Routine / PauseStream, and then each layer has its own instance of that.

This code design looks like one thread for all layers’ durations, another thread for all layers’ graphics etc… I think this is asking for difficulty.

hjh

1 Like

Hey @jamshark70,

Edit: Sorry, I am rephrasing this.

Is this quant (4):

~task1 = ~reschedulePauseStream.(~task1, 4);

the same as this quant (4)?

(
Pdef(\a1, Pbind(
\instrument, \mainbuf, \group, ~sources,
\patch, ~patch1a,
\amp, ~t1_a_pseq_1_amp_main_a,
\dur, ~a1dur * ~t1_a_pseq_1_dur_var,
\atk, ~a1atk * ~t1_a_pseq_1_atk_var,
\rel, ~a1rel * ~t1_a_pseq_1_rel_var,
\rate, ~a1rate * ~t1_a_pseq_1_rate_var,
\pos, ~a1pos * ~t1_a_pseq_1_pos_var * ~rrand1,
\bank, ~a1buf,
)).quant_(4);
);

And if I execute both in the same block like this:

(
~task1 = ~reschedulePauseStream.(~task1, 4);
(
Pdef(\a1, Pbind(
\instrument, \mainbuf, \group, ~sources,
\patch, ~patch1a,
\amp, ~t1_a_pseq_1_amp_main_a,
\dur, ~a1dur * ~t1_a_pseq_1_dur_var,
\atk, ~a1atk * ~t1_a_pseq_1_atk_var,
\rel, ~a1rel * ~t1_a_pseq_1_rel_var,
\rate, ~a1rate * ~t1_a_pseq_1_rate_var,
\pos, ~a1pos * ~t1_a_pseq_1_pos_var * ~rrand1,
\bank, ~a1buf,
)).quant_(4);
);
)

Would they both be quantized the same?

Got it figured out.

I was using (t, quant: 4) for the Pdefs and was only using (quant: 4) for the Tasks. When I added ‘t’ to the Task arg, it sync’d the Task and Pdef.