CC Messages during a NoteOn Message

I would like to be able to change the parameters of a synth while a note is holding - amount of modulation, etc. In the code below, I am trying something simple by trying to change the amplitude while holding a note. I am using a control bus so that I can change the volume while the note is holding, but it’s not working.

Something is going wrong between the bus and MIDIdef.

Thank you for any help!

MIDIIn.connectAll;
// MIDIClient.init;

(

// ~myControl = 0.5;

// A SynthDef with ADSR envelope
SynthDef(“quick2”, {arg freq = 440, amp = 0.1, gate = 1;
var snd, env;
env = Env.adsr(0.01, 0.1, 0.3, 2, amp).kr(2, gate);
snd = Saw.ar(freq: [freq, freq*1.5], mul: env);
Out.ar(0, snd)
}).add;
)

// Play it with a MIDI keyboard
(
var noteArray = Array.newClear(128); // array has one slot per possible MIDI note

MIDIdef.noteOn(\myKeyDown, {arg vel, note;
noteArray[note] = Synth(“quick2”, [\freq, note.midicps, \amp, vel.linlin(0, 127, 0, 1)]);
[“NOTE ON”, note].postln;
});

MIDIdef.noteOff(\myKeyUp, {arg vel, note;
noteArray[note].set(\gate, 0);
[“NOTE OFF”, note].postln;
});
)

~myControl = Bus.control(s,1);

x = Synth(\quick2)
x.free;
x.map(1, ~myControl.index);
x.map([\amp, ~myControl.index]);

// Synth(\quick2, [\amp, In.kr(~myControl).range(0.1, 0.7)]);

(

MIDIdef.cc(\myAmp, {
arg val, ccNum, channel;

val.postln;

~myControl = val.linlin(0, 127, 0.1, 0.7);

}, 2

);
)

One thing here is that you’re controlling \amp by velocity and by MIDI CC. That isn’t going to work – initially you set amplitude according to velocity, but then map to the control bus, at which point the velocity will be forgotten.

So if you want the two things to control volume, then you need two control inputs in the SynthDef.

~myControl = val.linlin(0, 127, 0.1, 0.7);

Here you need ~myControl.set(val.linlin(0, 127, 0.1, 0.7));. You’re reassigning ~myControl to a number, losing the reference to the bus. ~myControl is the bus object, not the value.

x.map(1, ~myControl.index); should be OK. x.map([\amp, ~myControl.index]); isn’t – remove the array brackets.

Synth(\quick2, [\amp, In.kr(~myControl).range(0.1, 0.7)]); – you can’t put a UGen in a synth argument list. You can do Synth(\quick2, [amp: ~myControl.asMap]) but that won’t handle the range.

hjh

Here’s a way of doing this. In this example I actually create a simple AM ring modulation synth and want to control modulator frequency through a MIDI slider or knob. I assume that I just want a single control for any and all notes.

There is some unrelated code added (filtering desired range of notes in the variable “usableNotes”) just because of the particular device I was using, but you can choose to change, ignore or delete that.

The main thing in this example is the creation of a control bus that I then hook up directly inside the SynthDef – since in this case I will not want to change or remap that bus later. This makes sense for this use case, but may not not for others, or course.

Notice that I set the control bus to the value 1 at creation time, so that’s the “default” modFreq for any notes played until I change it with my slider.

To add any additional controls, just create more control buses like the first, add them in the appropriate place in your SynthDef, and create a corresponding MIDIdef.cc for each as needed (and scale numbers accordingly).

Hope this helps,

B

MIDIIn.connectAll;
// MIDIClient.init;

(
// A single bus to control modulator frequency of all notes
~modFreqBus = Bus.control.set(1);

// A SynthDef with ADSR envelope
SynthDef("quick3", {arg freq = 440, amp = 0.1, gate = 1, modFreq = 1;
	var snd, env;
	env = Env.adsr(
		attackTime: 0.01,
		decayTime: 0.1,
		sustainLevel: 0.3,
		releaseTime: 1,
		peakLevel: amp
	).kr(doneAction: 2, gate: gate);
	snd = Saw.ar(freq: freq, mul: env);
	modFreq = In.kr(~modFreqBus);
	snd = snd * SinOsc.ar(Lag.kr(modFreq));
	Out.ar(0, snd)
}).add;
)

// Play it with a MIDI keyboard
(
var noteArray = Array.newClear(128); // array has one slot per possible MIDI note

// filter out notes out of desired range if needed
var usableNotes = (24..100); // "I only want this MIDIdef to listen for notes between midinote 24 up to 100"

MIDIdef.noteOn(\myKeyDown, {arg vel, note;
	noteArray[note] = Synth("quick3", [
		\freq, note.midicps,
		\amp, vel.linlin(0, 127, 0, 1)
	]);
	["NOTE ON", note].postln;
},
noteNum: usableNotes;
);

MIDIdef.noteOff(\myKeyUp, {arg vel, note;
	noteArray[note].set(\gate, 0);
	["NOTE OFF", note].postln;
},
noteNum: usableNotes
);

// Now simply make that change throuth a MIDIdef
MIDIdef.cc(\myModFreq, { |value, controlNumber|
	
	var modFreq = value.linlin(0, 127, 1, 50);
	["CC", controlNumber, "VALUE", value, "modFreq", modFreq.round(0.1)].postln;
	~modFreqBus.set(modFreq);
},
ccNum: 10 // pick the slider or knob you want to use for this
);
)