MIDIFunc.noteOn, while SynthDef is playing on its own

Hi folks, it should be simple but it drives me crazy
How can I just extract the incoming note value of MIDIFunc.noteOn and send it to any parameters of my SynthDef while this SynthDef is playing his own stuff?

Sorry It should be simple, but I didn’t manage to go beyond this classic relation between MIDIFunc and Synthdef where a new synth is played each time MIDIFunc send a new note, which is what I want to avoid…

Also I would like to know how to extract the incoming trig of MIDIFunc.noteOn (for each incoming note) and send it to everywhere I want in my SynthDef? ( without using the note value which is link to this trig)
Thanks a lot.

  • Assuming you mean MIDIFunc.noteOn instead of noteOut, the note comes in as one of the arguments in the function that you made and you registered, and which is called when a noteOn message comes in.

[…]

MIDIFunc.new(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher)

Create a new, enabled MIDIFunc. Normally one would use one of the message type specific convenience methods below, rather than use this method directly.

Arguments:

func A Function or similar object which will respond to the incoming message. When evaluated for noteOn, noteOff, control, and polytouch messages it will be passed the arguments val, num, chan, and src, corresponding to the message value (e.g. velocity, control value, etc.), message number (e.g. note number), MIDI channel, and MIDI source uid. For touch, program change and bend messages it will be passed only val, chan, and src. For information on the args passed for the other sorts of msgType see the convenience methods below.
[…]

Thanks a lot. I got it, it works. But how would you do to do the same thing transmitting polyphony notes? I feel that I have to declare an Array like " notes = Array.newClear(128);" and to use [ ] in the a.set line but I didn’t manage to make it work.
Mono version:
(
a={| freq=440, drop=0|
var snd;
snd=Saw.ar(freq);
Out.ar(0,snd!2*drop);
}.play; )

(
MIDIFunc.noteOn({ |veloc, num, chan, src|
var notes;
a.set( \freq, num.midicps,\drop, 1);
}); )

But how would you do to do the same thing transmitting polyphony notes?

All programming questions break down into these three:

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

    • MIDI channel, note number, velocity
  2. What information/result do I want? (What is the output?)

    • OK, you want to set a node’s parameter (which parameter?) to something (which value?) – and there are multiple nodes playing (so… which node?) So there are three questions that you have to answer before you’re ready to write any code for the polyphonic version.
  3. How to get from #1 to #2? (Algorithm.)

    • Which parameter is easy (user choice).
    • Which value might call for some mapping – input = 0-127, output = ? (you decide), and then the method depends on the relationship between the input MIDI value and the output with real-world value. But you need to decide the desired output values first. E.g., velocity (0-127) might map onto amplitude 0-1: then the method is amplitude = velocity / 127. But you might want to map it onto some arbitrary value of another parameter. linlin() and linexp() are your friends here.
    • Which node – that’s also not clear to me. If you are currently holding middle C (note 60) and press E above it (note 64), what should happen in the polyphonic version?

hjh

Thanks for the linlin tip, and sorry to be that newb. I managed to make everything work, that’s great. The idea was to add some notes layout to the current value (middle C in your example). It works. My only problem is when I use the classic
(
MIDIFunc.noteOn({ |veloc, num, chan, src| Array.newClear(128)[num] = Synth.new(\latex, [\freq, num.midicps,\dur1, 0.1 ]);
});
)
That creates a new synth for every note played is to be able to set the SynthDef params while playing and without adding a new layout of synth. If i do:

(
MIDIFunc.noteOn({ |veloc, num, chan, src| Array.newClear(128)[num] = Synth.new(\latex, [\freq, num.midicps,\dur1, 0.1 etc… ]);
});
It launch a new synth abose the one that’s playing.
And if I do:

(
MIDIFunc.noteOn({ |veloc, num, chan, src| Array.newClear(128)[num] = Synth.new(\saw, [\freq, num.midicps]);
});

x=Synth(\saw);
x.set(\dur1, 0.5);
It doesn’t update the SynthDef value of the ones created by the MIDIFunc
How can I do?
Thanks

Sorry the following is completely untested, and just a possible approach rather than a fully worked out solution, but how about something like this:

~synths = Array.newClear(128);

MIDIFunc.noteOn({ 
    |veloc, num, chan, src| 
    // only create a synth if one isn't created yet
    if (~synths[num].isNil) {
        ~synths[num] = Synth.new(\latex, [\freq, num.midicps, \dur1, 0.1 ]);   
    };
});

// after a note on with note 60, there's a synth running that you now can update
~synths[60].set(\freq, 440);

Usage tip for the forum: Please write your code examples in triple-backtick blocks:

```
your code
```

(It doesn’t affect your examples here, but leaving out the backticks is known to break quote marks, for one thing.)

(
MIDIFunc.noteOn({ |veloc, num, chan, src| Array.newClear(128)[num] = Synth.new(\latex, [\freq, num.midicps,\dur1, 0.1 ]);
});
)

That creates a new synth for every note played is to be able to set the SynthDef params while playing and without adding a new layout of synth…

Yes, it’s doing exactly what you asked it to do.

The function evaluates anew for every incoming note-on.

The first thing in your function creates a new array. That’s a new array for every note. The new array, of course, has no memory of any previous notes that you played.

So you do have to do it like shiihs demonstrated: create the array outside of the MIDIFunc and save it in a variable that exists outside of the function.

hjh

Yes, guys thanks and I got it. But it’s still the same problem with the shiihs code. The .set(args) doesn’t update the nodes played by the MIDIFunc, the only way to update the SynthDef values is to change it directly on the SynthDef and to recompile it while it s playing (add;). Simply how can I use the really convenient x=Synth(\name); x.set(\args) in this set up? I didn’t find any example of that online.

Again, I’d highly recommend stepping back a bit and going carefully through the tutorial series. Maybe “tutorials are boring” but in the tutorials, many of these questions are answered.

http://doc.sccode.org/Tutorials/Getting-Started/10-SynthDefs-and-Synths.html#Creating%20Variety%20with%20SynthDefs

The most common way of creating varia[tion] is through putting arguments into the UGen Graph Function. This allows you to set different values when the synth is created.

So that section illustrates SynthDef arguments. Then, the next section demonstrates .set.

hjh

My feeling is you have a fundamental misunderstanding getting in the way of getting where you want.

You cannot update the synth definition after it’s compiled, you can only change the parameters of a synth that is currently instantiated (usually it’s playing then). If your synth has already stopped playing and has freed itself there’s nothing left to update.

Are you sure your synth didn’t free itself before you try to modify the parameters? You may need to take some precautions to keep it from freeing itself (things like specifying DoneAction:0).

To be honest, it’s not so easy for us to guess what you are actually hoping to accomplish. Maybe instead of asking for detailed code suggestions you could sketch what your final goal is so we can suggest an approach that better fits your needs.

Also, Eli fieldsteel has some excellent beginner tutorials about using MIDI on youtube. Be sure to take a look.

I know thant I am a kind of an annoying newbie. I am gone try to explain really simply what I would like to do: The Synth is playing notes and chords sent by Max ( thru MIDIFunc.noteOut). My SynthDef has different args that I would like to change “on the fly” (typically the envelope params, filter values etc…). It s easy to do that manually by changing the args values directly on the SynthDef and recompile it while its running: it does exactly what I want. BUT my goal is to have different “presets” ( each preset correspond to a different SynthDef state that I like, different values for the same args) and to recall while it’s running. I would like to play live with thoses different presets, while the MIDI datas send its stuff to the SynthDef autonomously. I know it should appears insane to be blocked at this level. But it’s really where I am.

You’re struggling because this is not, in fact, a basic feature. It’s possible to do, not even that hard, but you’re skipping over basics in order to get to the fun stuff and struggling because you don’t have the fundamentals. I’d really suggest slowing down and learning the basics rather than getting frustrated because non-obvious things are not obvious.

Now, an interesting non-basic feature is synthdef variants.

http://doc.sccode.org/Classes/SynthDef.html#Variants

(
SynthDef(\variant, { |out, gate = 1, freq = 440, ffreq = 2000, rq = 1, amp = 0.1|
	var sig = Saw.ar(freq),
	eg = EnvGen.kr(Env.adsr, gate, doneAction: 2);
	sig = RLPF.ar(sig, ffreq, rq);
	Out.ar(out, (sig * (amp * eg)).dup);
},
variants: (
	a: [ffreq: 1000, rq: 0.1],
	b: [freq: 60, ffreq: 200, rq: 0.5],
	c: [freq: 60, ffreq: 8000, rq: 0.05],
	d: [freq: 120, ffreq: 1600, rq: 0.2]
)).add;
)

a = Synth('variant.a');
a.release;

a = Synth('variant.c');
a.release;

a = Synth('variant.d');

// note that this is NOT OBVIOUS
// you should not expect as a new user to figure this out by yourself
a.set(*SynthDescLib.at(\variant).def.variants[\c]);
a.set(*SynthDescLib.at(\variant).def.variants[\d]);

a.release;

hjh

Hi Raphael, here’s an example that does the following:

  • whenever a note on is received, a new synth is instantiated and all notes that are currently playing are updated with a new wobble frequency and amplitude
  • whenever a note off is received the corresponding synth is stopped and freed

Based on your description, I hope it comes in the neighborhood of what you want to achieve:

(
// run this block once to connect supercollider to your midi devices
MIDIdef.freeAll;
MIDIClient.init;
MIDIIn.connectAll;
)

(
s.waitForBoot({
    // specify a simple synth with a wobble
    // the parameters of the wobble will be updated as the synth is playing
    SynthDef(\simple, {
        | out=0, freq=440, amp=0.1, gate=1, wobblefreq=3, wobbleamp=0.1 |
        var env = EnvGen.ar(Env.adsr(0.02, 5.0, amp), gate:gate, doneAction:2);
        var sig = amp*SinOsc.kr(wobblefreq).range(1-wobbleamp, 1+wobbleamp)*LFSaw.ar(freq);
        Out.ar(out, env*sig!2);
    }).add;

    // wait until the synth is available on the server before continuing 
    s.sync;

    // create array to remember synths for each midi note that comes in
    ~synths = Array.newClear(128);

    // define what should happen if a note on is received:
    // 1) print a debug message, 2) instantiate a synth to perform the note,
    // and 3) update wobble parameters on all currently playing notes
    MIDIdef.noteOn(\polyNoteOn, {
        | vel, nn, chan, src |
        // start new synth for the new note on
        ("note on for note "+nn+"with velocity"+vel).postln;
        ~synths[nn] = Synth(\simple, [
            \freq, nn.midicps,
            \amp, vel.linexp(1,127,0.01,0.3)]);
        // and change the parameters in all running synths
        // = all synths for which a note on but no note off was received yet
        ~synths.do({
            |synth, nn|
            if (synth.notNil) {
                synth.set(\wobblefreq, 10.rrand(1));
                synth.set(\wobbleamp, 0.8.rrand(0.1));
            };
        });
    });

    // when note off is received, switch off the synth running for that note
    // and print a debug message
    MIDIdef.noteOff(\polyNoteOff, {
        | vel, nn, chan, src |
        ("note off for note "+nn).postln;
        ~synths[nn].set(\gate, 0);
        ~synths[nn] = nil;
    });
})
)

Better design for the variants example:

(
~presets = (
	a: [freq: 200, ffreq: 1000, rq: 0.1],
	b: [freq: 60, ffreq: 200, rq: 0.5],
	c: [freq: 60, ffreq: 8000, rq: 0.05],
	d: [freq: 120, ffreq: 1600, rq: 0.2]
);

SynthDef(\variant, { |out, gate = 1, freq = 440, ffreq = 2000, rq = 1, amp = 0.1|
	var sig = Saw.ar(freq),
	eg = EnvGen.kr(Env.adsr, gate, doneAction: 2);
	sig = RLPF.ar(sig, ffreq, rq);
	Out.ar(out, (sig * (amp * eg)).dup);
}, variants: ~presets).add;
)

a = Synth('variant.a');
a.release;

a = Synth('variant.c');
a.release;

a = Synth('variant.d');

// note that this is NOT OBVIOUS
// you should not expect as a new user to figure this out by yourself
a.set(* ~presets[\c]);
a.set(* ~presets[\d]);

a.release;

hjh

Thank you both of you for taking the time for that. Shiihs, your code works perfectly but the workflow is kind of the same that updating the values directly on the SynthDef and recompile it while it s running. The only way I found to be able to play with “different prepared presets” using your code was: I declare environemental variables for each args and use them inside the MIDIdef function: like

if (synth.notNil) {
                synth.set(\arg1, ~valuearg1);
                synth.set(\arg2, ~valuearg2);

And then I can create different blocks outside the SynthDef and the MIDIDef functions like

(
~valuearg1= 3;
~valuearg2= 0.5;
)

So this way I can run each block (preset) on the fly, and change a bunch of values in a one shot, whithout adding new notes, while the synth is running ie “presets” in Max/Msp.
But I feel it’s stupid, heavy and not elegant. How would you do?

Jamshark70, thanks for this discovery of variants, it’s really minimal and convenient but I didn’t manage to implement it with the MIDIdef.noteOn. When I use Pdef/Pbind, it does exactly what I want:

(
~presets = (
	a: [freq: 200, ffreq: 1000, rq: 0.1],
	b: [freq: 60, ffreq: 200, rq: 0.5],
	c: [freq: 60, ffreq: 8000, rq: 0.05],
	d: [freq: 120, ffreq: 1600, rq: 0.2]
);

SynthDef(\variant, { |out, gate = 1, freq = 440, ffreq = 2000, rq = 1, amp = 0.1|
	var sig = Saw.ar(freq),
	eg = EnvGen.kr(Env.adsr, gate, doneAction: 2);
	sig = RLPF.ar(sig, ffreq, rq);
	Out.ar(out, (sig * (amp * eg)).dup);
}, variants: ~presets).add;
)

(
Pdef(\test, Pbind(	\instrument, \variant,
					\freq, Pseq([62, 64, 67, 69, 71, 74], inf).midicps,
                  	\dur,  Pseq([0.25, 0.5, 0.25, 0.25, 0.5, 0.5], inf)
));
)

a = Pdef(\test).play;
a.set(* ~presets[\c]);
a.set(* ~presets[\a]);

Updating values while it’s running the Pseq sequence, I would like the same with the polyphonic result of MIDIdef.noteOn…I tried hard.
By the way is it possible to implement the MIDIFunc.noteOn incoming value into the \freq arg of this Pbind?

You should be able to prepare presets and set them as follows (the \1, \2, \3 are random symbols, you can name them anything you like). The \freq, \amp, \att, \rel are arguments of the synth whose parameters we will modify while it’s running.

~presets = ();
~presets[\1] = [\freq, 220, \amp, 0.5, \att, 0.1, \rel, 3.0];
~presets[\2] = [\freq, 440, \amp, 0.2, \att, 1.0, \rel, 6.0];
~presets[\3] = [\freq, 110, \amp, 0.7, \att, 0.01, \rel, 1.0];

You can now set all keys at once:
(mind the star, which “unpacks” the list of arguments)

x = Synth(\test);

x.set(*~presets[\2]);
//... make sure the synth is still running to hear the parameter change
x.set(*~presets[\1]);

Or if you want to pass presets during synth instantiation:

x = Synth(\test, ~presets[\3]);

Again (I said this before, but I’m not sure it was heard or understood) – when you’re throwing code against the wall and it isn’t doing what you wanted, sometimes a better strategy is to step back and be very clear about the desired result.

So you want a/ MIDI, b/ polyphony, and c/ something to be changed by presets.

Which parameters should be controlled by MIDI? I mean, list them. By name.

Which parameters should be controlled by presets? Again, specifics: arg names.

Do you want incoming MIDI messages to a/ play new notes or b/ change parameters of existing notes or c/ both (i.e. use MIDI note number to play new notes, and at the same time change other parameters based on the presets)?

Do you want the same change to be applied to all polyphonic nodes, or should it be different per node? If different, how is that determined?

By the way is it possible to implement the MIDIFunc.noteOn incoming value into the \freq arg of this Pbind?

Then, what should the Pbind do if it needs to play two or three or four notes but you haven’t provided new pitches from the keyboard? Repeat the last note, or generate something else on its own?

I’m pointing these questions out because your question here is a good brainstorm, but it’s vague about the details. Being vague about details means that you will struggle with the implementation. I’m suggesting a new habit of thinking, where you start to ask detail questions of your own ideas. (Admittedly, some of that comes from experience – and also, digital art usually involves some uncertainty and some surprise – so it isn’t as simple as “decide all the details and just do it.” But at present you’re a bit on the vague side, and that is going to cause you problems.)

hjh