Routing.. in a Pattern

Hello everyone,

I’m trying to understand how could I route \seqGen at one by one element of the array .
The idea is just to send \seqGen, to the first element, once it’s done , to the second and so on…
Is there a way to route \seqGen to \seq1 etc… ?

or do you know a better way to solve this “problem” ?

    \seqGen, Pseq([100,0],inf),
   ... // here something to make the routing happenn     \seqGen to \seq1..
    \sendME, Pfunc {|e| ~n1.sendMsg('/dmxP5',
    e[\seq1],
    e[\seq2],
    e[\seq3],
    e[\seq4],
    e[\seq5],
    e[\seq6],
    ) },

maybe
a way it could be to create a multichannel Pseq[[]] and with a kind of permutation move the desired pattern at the proper index…

    	\sendME, Pfunc {|e| ~n1.sendMsg('/dmxP5',
     e[\seq].at(0),
     e[\seq].at(1),
     e[\seq].at(2)
  etc..
     ) }, 

It should be simple… but I’m quite confused.

Any help it would be very apperciated.
Thanks in advance

I don’t understand the connection between your \seqGen and the other \seq entities in the pattern.

Maybe it would help to give a concrete example? Such as, starting with \seqGen = 0, what exactly should go out to the dmx? And \seqGen 5 or 100 or whatever?

Often, when you get confused about programming, there are 3 questions that can help:

  1. What information do I have right now? (What is the input?)

  2. What information or action do I want at the end? (What is the output?)

  3. What operations get from the input to the output?

Right now… 1/ I don’t understand what your input \seqGen means. 2/ I don’t understand what the outgoing message is supposed to be.

So of course 3/ is confusing. Define 1 and 2 clearly first and 3 may become more evident.

hjh

Ok, sorry I wasn’t very clear.

I’m using Sc to send to Processing data from the patterns.
I’m doing so because, in Sc 3.9.3 I can’t use the DMX quark GitHub - supercollider-quarks/DMX: A set of classes to interface with DMX devices to send and receive the DMX lightning protocol. Note: not all of this code is tested to work. Sc can’t see my ENTTEC dmxusb pro.
Anyway, I decided to use Processing via OSC and it works.

I’m using a 6 channels dimmer with 6 lightbulbs (PROEL PLDM6KN)

In processing I have :

import oscP5.*;
import netP5.*;

import dmxP512.*;
import processing.serial.*;

//OSC
OscP5 oscP5;
NetAddress myRemoteLocation;


//DMX
DmxP512 dmxOutput;
int universeSize=128;

boolean LANBOX=false;
String LANBOX_IP="192.168.1.77";

boolean DMXPRO=true;
String DMXPRO_PORT="COM4";//case matters ! on windows port must be upper cased.
int DMXPRO_BAUDRATE=115000;



// GRIGLIA.. 
int w = 100, h = w, offset = 5, green=0;

//float[] oscColors;
float oscColors0, oscColors1, oscColors2, oscColors3, oscColors4, oscColors5= 100;

void setup() {
  size(600, 50);
  // listen
  oscP5 = new OscP5(this, 12000);
  myRemoteLocation = new NetAddress("127.0.0.1", 12000);


  dmxOutput=new DmxP512(this, universeSize, false);

  if (LANBOX) {
    dmxOutput.setupLanbox(LANBOX_IP);
  }

  if (DMXPRO) {
    dmxOutput.setupDmxPro(DMXPRO_PORT, DMXPRO_BAUDRATE);
  }
}



void draw() {
  background(126, 126, 126);
  //frameRate(60);

  noStroke();


  // CANALE 1
  fill(0, oscColors0, 0);
  rect( 0, 0, w, h);
  dmxOutput.set(1, int(oscColors0));

  // CANALE 2
  fill(0, oscColors1, 0);
  rect( 100, 0, w, h);
  dmxOutput.set(2, int(oscColors1));

  // CANALE 3  
  fill(0, oscColors2, 0);
  rect( 200, 0, w, h);
  dmxOutput.set(3, int(oscColors2));

  // CANALE 4
  fill(0, oscColors3, 0);
  rect( 300, 0, w, h);
  dmxOutput.set(4, int(oscColors3));

  // CANALE 5 
  fill(0, oscColors4, 0);
  rect( 400, 0, w, h);
  dmxOutput.set(5, int(oscColors4));

  // CANALE 6 
  fill(0, oscColors5, 0);
  rect( 500, 0, w, h);
  dmxOutput.set(6, int(oscColors5));
}


// RECEIVE AN ARRAY and split it with get..
void oscEvent(OscMessage theOscMessage) {
  oscColors0 = theOscMessage.get(0).intValue();
  oscColors1 = theOscMessage.get(1).intValue();
  oscColors2 = theOscMessage.get(2).intValue();
  oscColors3 = theOscMessage.get(3).intValue();
  oscColors4 = theOscMessage.get(4).intValue();
  oscColors5 = theOscMessage.get(5).intValue();

  println(oscColors0, "   Sono OSC color 0");
}

I’m moving the first steps in this kind of communication using the patterns to control something that is not a SynthDef.

I was trying

// OscSender~
~n1 = NetAddr("127.0.0.1", 12000);


(
~n1.sendMsg('/test',100,100,100,100,100,100);
)
//OFF turn off the lights
(
~n1.sendMsg('/test',0,0,0,0,0,0);
)


// trivial Example 
(
Pbindef(\singMultiSEQ,
	\stretch,1,
	\seq1, Pseq([100,0],inf),
	\seq2, Pseq([100,0],inf),
	\seq3, Pseq([100,0],inf),
	\seq4, Pseq([100,0],inf),
	\seq5, Pseq([100,0],inf),
	\seq6, Pseq([100,0],inf),
	\bla, Pfunc {|e| ~n1.sendMsg('/test',
		e[\seq1],
		e[\seq2],
		e[\seq3],
		e[\seq4],
		e[\seq5],
		e[\seq6],
	) },
	\dur,Pseq([~metroBase.tempo],inf)// ~metroBase is just a general metro im referring to 
).play(~metroBase,quant:[~metroBase.timeToNextBeat,0]); 

)

(
Pbindef(\singMultiSEQ).stop;
~n1.sendMsg('/test',0,0,0,0,0,0);
)

it’s super simple and it works, in this way I can write all the sequences indipendently,
and every \seq is related to a channel in Processing and in the dimmer.

Pseq([100,0],inf)

Now I was trying to find a way to route a single sequence, for instance a Pseq([100, 0],inf),
to every element of the array one after the other:

e[\seq1],
e[\seq2],
e[\seq3],
e[\seq4],
e[\seq5],
e[\seq6]

it would be nice to have the Pseq([100,0],inf) turn On the first light, then turn it off and switch to the second light and repeat the same action untill the end of the array and start again.

Like a first step it could be nice just to find a way to make it works.

Hope now it’s clearer.
If anyone have an hint, it would be really appreciated.

Thanks

ps:
I just found a way to do it with a routine

(
r = Routine({
	6.do({
		|i|
	
		~c = [100,0,0,0,0,0].permute(i);
		~n1.sendMsg('/test',~c[0],~c[1],~c[2],~c3],~c[4],~c[5]);

		~metroBase.tempo.yield;
		~n1.sendMsg('/test',0,0,0,0,0,0); // turn all OFF
	})

}).reset.play(~metroBase,quant:[~metroBase.timeToNextBeat,0]);
)

I thought about two strategies to accomplish this, keeping in mind that you always send state values for all of the lights at once:

  1. Write parallel sequences, but make them as long as needed to cover a full repetition cycle. In the case of three lights, they would be like [0,100,0,0,0,0], [0,0,100,0,0,0], [0,0,0,0,100,0].
  2. More expressive IMO: keep track of the current state and just sequence variations. In the case of three lights, your sequence would be [[0,100],[0,0],[1,100],[1,0],[2,100],[2,0]], expressing each step as [light_id,value].
// 1. parallel sequences
// quickly generate your on/off sequence
// we'll use 6 rotations of this same pattern
~seq = [100]++(0!11);
(
Pbindef(\singParSEQ,
	\seq1, Pseq(~seq,inf),
	\seq2, Pseq(~seq.rotate(2),inf),
	\seq3, Pseq(~seq.rotate(4),inf),
	\seq4, Pseq(~seq.rotate(6),inf),
	\seq5, Pseq(~seq.rotate(8),inf),
	\seq6, Pseq(~seq.rotate(10),inf),
	\dur,Pseq([1],inf),
	\amp,0
).collect{|e|
	var msg = ['/test']++(1..6).collect{|n|e[(\seq++n).asSymbol]};
	//~n1.sendMsg(*msg)
	msg.postln;
	e // need to return the event
}.play; 
)

// 2. single sequence of state variations
~state = 0!6;

~seq = 6.collect{|light_id|
	// for each light, sequence an on/off
	[[light_id,100],[light_id,0]]
}.flatten;

(
Pbindef(\singVarSEQ,
	\change, Pseq(~seq,inf),
	\dur,Pseq([1],inf),
	\amp,0
).collect{|e|
	var msg;
	e.change !? {
		var light_id,val;
		# light_id,val = e.change.postln;
		~state[light_id] = val;
		msg = ['/test']++~state;
		//~n1.sendMsg(*msg)
		msg.postln;
	};
	
	e // need to return the event
}.play; 
)

Notes:

  • I commented out metronome and networking parts just because it was quicker for me to test
  • I use .collect on a pattern stream to execute a function on every streamed event. I think it’s up to personal taste to decide whether this is more or less elegant than using Pfunc inside the pattern
1 Like

OK, I think I see.

Now I was trying to find a way to route a single sequence, for instance a Pseq([100, 0],inf),
to every element of the array one after the other…

So if you have a stream outputting values x0, x1, x2, x3, x4, x5…, then you want

Event 0: \seq1, x0
Event 1: \seq2, x1
Event 2: \seq3, x2

One aspect that is undefined in this problem statement: in Event 1, what is \seq1? If you’re “routing” successive elements of the stream to different keys, then what happens to the other keys? (You do have to define this, or the problem statement is incomplete.)

The catch here is that Pbind doesn’t allow the keys (names) to be variable. But you can embed a custom function, e.g.:

(
// you can delete Pfin; I want to make it stop after 6
p = Pfin(6, Pbind(
	\key, Pseq(
		Array.fill(6, { |i| ("seq" ++ (i+1)).asSymbol }),
		inf
	),
	\value, Pseq([100, 0], inf),
	\assign, Pfunc { |ev|
		ev.put(ev[\key], ev[\value]);
		ev[\value]  // shouldn't return 'ev' here
	},
	\dur, 0.1,
	\message, Pfunc { |ev|
		(['/test'] ++ Array.fill(6, { |i|
			ev[("seq" ++ (i+1)).asSymbol]
		})).postln;
	},
	\type, \rest // suppress note playing
)).play;
)

[ /test, 100, nil, nil, nil, nil, nil ]
[ /test, nil, 0, nil, nil, nil, nil ]
[ /test, nil, nil, 100, nil, nil, nil ]
[ /test, nil, nil, nil, 0, nil, nil ]
[ /test, nil, nil, nil, nil, 100, nil ]
[ /test, nil, nil, nil, nil, nil, 0 ]

2 problems: First is all of those nil values (because the default is not defined). Second is now that every other “routed” value is 0 (coming from Pseq([100, 0], inf)). So I think maybe you got yourself mixed up by conflating two different approaches to the problem: a/ the lights have to be turned off (which you tried to do in Pseq), and b/ routing. Those two solutions are in conflict.

If you want to do it by routing in this way, then you need to a/ set default values in the event and b/ include only “on” values in the source pattern.

(
var eventDefaults = Event(
	proto: (seq1: 0, seq2: 0, seq3: 0, 
		seq4: 0, seq5: 0, seq6: 0)
);

// you can delete Pfin; I want to make it stop after 6
p = Pfin(6, Pbind(
	\key, Pseq(
		Array.fill(6, { |i| ("seq" ++ (i+1)).asSymbol }),
		inf
	),
	\value, 100,
	\assign, Pfunc { |ev|
		ev.put(ev[\key], ev[\value]);
		ev[\value]  // shouldn't return 'ev' here
	},
	\dur, 0.1,
	\message, Pfunc { |ev|
		(['/test'] ++ Array.fill(6, { |i|
			ev[("seq" ++ (i+1)).asSymbol]
		})).postln;
	},
	\type, \rest // suppress note playing
)).play(protoEvent: eventDefaults);
)

[ /test, 100, 0, 0, 0, 0, 0 ]
[ /test, 0, 100, 0, 0, 0, 0 ]
[ /test, 0, 0, 100, 0, 0, 0 ]
[ /test, 0, 0, 0, 100, 0, 0 ]
[ /test, 0, 0, 0, 0, 100, 0 ]
[ /test, 0, 0, 0, 0, 0, 100 ]

But you’re also crashing into a rule of thumb of programming: if you’re attaching numbers to names to denote a sequence, probably you should just use an array instead – as elgiano suggested.

So maybe:

(
// you can delete Pfin; I want to make it stop after 6
p = Pfin(6, Pbind(
	\values, Prout { |ev|
		var source = [100, 0, 0, 0, 0, 0];
		inf.do { |i|
			ev = source.permute(i.asInteger).yield;
		};
	},
	\dur, 0.1,
	\message, Pfunc { |ev|
		// much easier to write!
		(['/test'] ++ ev[\values]).postln;
	},
	\type, \rest // suppress note playing
)).play;
)

hjh

2 Likes

Thanks for the fast reply,
I need to study a bit your code to fully understand how you did it.

e.g. ~n1.sendMsg(*msg); the * is used for ?

also this one was new to me: !?
Object | SuperCollider 3.12.2 Help?

Anyway, for now I can only say: " it works great!"

Thanks!,
I see a lot of new things to me in these approaches.
e.g. routine pattern…

I need some times to fully understand :slight_smile:

To pass arguments as an array… it’s like it unpacks the array into separate arguments. So someMethod(*[a,b,c]) is equivalent to someMethod(a,b,c). You find a clue about it in NetAddr | SuperCollider 3.12.2 Help, but it’s a Syntax Shortcut that you can use everywhere in SC.

1 Like