Triggering custom code with pattern note on events

I’m using patterns to trigger sounds.

Sometimes, I want to run a custom action at the same time as the note on event (e.g. sending OSC messages to a 3rd party application).

To do this I’ve defined a custom event which runs the function defined by the \action key, and then modifies the event type back to \note and plays it, ie delegates to the default event player:

Event.addEventType(\actionInstrument, {
	var noteEvent;
	~action.value();

	noteEvent = currentEnvironment.copy;
	noteEvent.use { 
		~type = \note;
	};
	noteEvent.play;
});

Is this the correct way of achieving what I’m trying to do?

One problem I’m having is that my custom code runs in the event’s environment, so while all the event’s keys are accessible as globals, my code no longer has access to the program’s original global environment, so I can’t use any utility functions I’ve defined on the global scope. If I want to access them, I have to bind them in the pattern, e.g.

Pbind(
  \type, actionInstrument,
  \utilityFunction1, ~utilityFunction1,
  \utilityFunction2, ~utilityFunction2,
  \utilityFunction3, ~utilityFunction3,
);

Is there a way around this?

Event.addEventType(\actionInstrument, {

Is this the correct way of achieving what I’m trying to do?

seems it’d be easier to just use a Pfunc. did you consider it?

(
~myfunc= {|ev| (ev.degree+1000).postln};  //some function
Pbind(
    \dur, 0.5,
    \degree, Pseq([0, 5, 4, 3, 2, 3, 2, 1], inf),
    \action, Pfunc(~myfunc),
).play;
)

//similar but only evaluate the function some times.
(
~hej= 1000;
Pbind(
    \dur, 0.5,
    \degree, Pseq([0, 5, 4, 3, 2, 3, 2, 1], inf),
    \action, Prand([0, 0, Pfuncn({(~hej*100.rand).postln}, 1)], inf),
).play;
)

should also solve your environment variable issues - see second example.

also, since you mentioned sending osc, if you want to compensate for server latency you can do something like this…

(
n= NetAddr("127.0.0.1", 9999);
Pbind(
    \dur, 0.5,
    \degree, Pseq([0, 5, 4, 3, 2, 3, 2, 1], inf),
    \action, Pfunc({
        SystemClock.sched(s.latency, {
            n.sendMsg(\test, 1, 2, 3);
            nil;
        });
    }),
).play;
)

hope this is useful,
_f

#|
fredrikolofsson.com musicalfieldsforever.com

1 Like

Exactly what I was looking for, thanks!

I can provide a few alternatives.

Event doesn’t support parent and proto instance variables, but the currentEnvironment does.

If using Pbind works with in your current setup, then perhaps it’s no longer an issue.

At any length, you can set the currentEnvironment global namespace to lookup anything it doesn’t know to it’s parent or proto environment… which in this case could be your event… perhaps needing Environment.newfrom(event) or event.asDict, which returns an IdentityDicitionary… it’s helpfile explains parent & proto, near the top I believe.

Basically, the environment / namespace will search in this order:

  • itself
  • it’s proto
  • it’s parent

There are also 3-4 ‘super-global’ namespaces instantiated at every compile (I sometimes call them ‘cosmic’ … ) there’s 4 if you count the interpreter’s a-z variables, which will all show up in the post window if you call the .dump method on the instance of the interpreter (which is simply known as this)

The 3 main alternate global namespaces are:

  • Archive.global (reads archive.scxtar from user app support directory on every compile, and writes here on every shutdown… basically inescapable)
  • Library.global (just as global, but is cleared on compile… both Library & Archive inherit from LibraryBase (Abstract) which inherits from MultiLevelIdentityDictionary, and that sums up the entirety of this branch of Collection)
  • ObjectTable.global (useful in certain cases… it allows you to add objects as values which are automatically bound to successive integer keys as the values unique ID (or it’s key) and it also behaves a little differently in certain measures, to serve a specific need… so it may be a luxury, in certain cases… it’s integer keys always start at 1000, and this is controlled by class UniqueID, which exists solely to serve ObjectTable, I believe… it has .initClass called on it to zero it out back to 1000, at compile and not when clearing the main object table… and maybe not even when creating a new one)

Just some things to consider… the final global namespaces to mention as an afterthought are SynthDescLib.global (you’ll have to look it up as I never use it… but all of our SynthDefs do) and also topEnvironment, which is directly synonymous with currentEnvironment at a fresh start… you may find decent usage with .push (makes the Environment the currentEnvironment) .pop (gets rid of the environment from the stack, deletes it) .proto_(set namespace) and .parent_(set namespace)

and Environment.stack // → ( [ an array of all accessible environments ] ) which also supports ._(set to this value) … but returns nil if there’s only one environment / namespace, instead of returning an array with only one instance (it’s just not ‘a stack’ with only one, it seems…)

The other way is topEnvironment:

(
~a = { |ev| ("utility function, has event by argument " ++ ev).postln; 0 };

Event.addEventType(\custom, {
	topEnvironment[\a].value(currentEnvironment);
	currentEnvironment.copy.put(\type, \note).play;
});
)

(type: \custom).play;

hjh