Can I send values in a Pbind that are interpreted like midinote or degree? - supercollider

I'm not sure whether SuperCollider can deliver moons on sticks, but I'd really like to be able to specify values in my Pbind that are interpreted in the same way as midinote or degree: i.e. converted automatically to a frequency.
So, an excerpt of such a Pbind, which produces a TB-303-style slide from one frequency to another:
b = Pbind(*[
out: 0,
instrument: \acid,
stepsPerOctave: 19,
scale: [0, 3, 5, 8, 11, 14, 17],
octave: 3,
degree: Pseq([0, \, 3, 3, 4, 4, 9, 4, 4]),
prevFreq: Pseq([\, \, 0, 3, 3, 4, 4, 9, 4]),
dur: Pseq([0.4, 0.4, 0.1, 0.1, 0.1, 0.1, 0.2, 0.1, 0.1]),
]);
...it would be super-duper if prevFreq were interpreted as containing degree values in the same way as degree.
In the absence of some kind of automatic conversion, I assume I need to do some kind of calculation within the synth itself in order to convert my values from a degree-type value to an actual frequency. I'm aware I can use foo.midicps to convert midinote-type values to a frequency, but is there a similar convenience function to convert degree-type values to a frequency (presumably also using the current scale and octave values)?

If you look at the helpfile for Event, you can see how it computes the frequency from the degree and scale:
note: #{    // note is the note in halftone steps from the root
    (~degree + ~mtranspose).degreeToKey(~scale, ~stepsPerOctave);
}
midinote: #{    // midinote is the midinote (continuous intermediate values)
    ((~note.value + ~gtranspose + ~root) / ~stepsPerOctave + ~octave) * 12.0;
}
freq: #{
    (~midinote.value + ~ctranspose).midicps * ~harmonic;
}
detunedFreq: #{    // finally sent as "freq" to the synth as a parameter, if given
    ~freq.value + ~detune
}
Event is an associative array and those ~variables can also be used as keys to the array (something which will hopefully become clear in a moment. It's also possible to get access to the events in a Pbind, by using a Pfunc. Let's say we want to calculate the current frequency for your Pbind:
b = Pbind(*[
out: 0,
instrument: \default,
stepsPerOctave: 19,
scale: [0, 3, 5, 8, 11, 14, 17],
octave: 3,
degree: Pseq([0, \, 3, 3, 4, 4, 9, 4, 4]),
dur: Pseq([0.4, 0.4, 0.1, 0.1, 0.1, 0.1, 0.2, 0.1, 0.1]),
foo: Pfunc({|evt|
var note, midinote, freq, detuned, result;
note = (evt[\degree] + evt[\mtranspose]).degreeToKey(evt[\scale], evt[\stepsPerOctave]);
midinote = ((note + evt[\gtranspose] + evt[\root]) / evt[\stepsPerOctave] + evt[\octave]) * 12.0;
freq = (midinote + evt[\ctranspose]).midicps * evt[\harmonic];
detuned = freq + evt[\detune];
detuned.postln;
})
]).play
Those calculations for note, midinote, freq and detuned freq are the same calculations we saw in the event helpfile. Therefore, this Pbind will now print out the frequency that you are currently playing.
What you actually want is the frequency you were previously playing, which we could figure out from your array of previous degrees. Or we could just keep track of the previous frequency in a variable. This will be a lot easier to keep track of!
(
var prev;
b = Pbind(*[
out: 0,
instrument: \default,
stepsPerOctave: 19,
scale: [0, 3, 5, 8, 11, 14, 17],
octave: 3,
degree: Pseq([0, \rest, 3, 3, 4, 4, 9, 4, 4]),
dur: Pseq([0.4, 0.4, 0.1, 0.1, 0.1, 0.1, 0.2, 0.1, 0.1]),
prevFreq: Pfunc({|evt|
var note, midinote, freq, detuned, result;
if (evt[\degree] == \rest, { detuned = \rest} , {
note = (evt[\degree] + evt[\mtranspose]).degreeToKey(evt[\scale], evt[\stepsPerOctave]);
midinote = ((note + evt[\gtranspose] + evt[\root]) / evt[\stepsPerOctave] + evt[\octave]) * 12.0;
freq = (midinote + evt[\ctranspose]).midicps * evt[\harmonic];
detuned = freq + evt[\detune];
});
//detuned.postln;
if (prev.isNil(), {
result = \rest;
} ,
{
result = prev;
});
prev = detuned
})
]).play
)

Related

d3 Date Axis "Rounding"

I'm attempting to have an X-Axis with dates. Unfortunately my data can be have a small range resulting in a date showing multiple times as an xTick. Is there a way to force it to only show "each date" exactly once as a tick.
For example, I don't want to see 12/29/2020 as a tick more than once.
Depends on your version of d3, but d3.timeDay might be what you need.
var input = [new Date(1999, 11, 31, 0),new Date(2000, 0, 1, 0), new Date(2000, 0, 1, 2), new Date(2000, 0, 1, 3), new Date(2000, 0, 2, 2), new Date(2000, 0, 2, 5)];
console.log(input);
var x = d3.scaleTime().domain(input);
var xTick = x.ticks(d3.timeDay.every(1));
console.log(xTick)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

D3 Chord Diagram Not Rendering Correctly

Using the excellent guide by Nadieh Bremer I'm making a stretched chord diagram.
However, with certain data inputs the rendering goes awry.
I've made a demo to demonstrate my issue here:
https://codepen.io/benmayocode/pen/MPEwdr
Specifically, in the .js file lines 269 to 281 file I have:
var respondents = 40,
emptyPerc = 0.4,
emptyStroke = Math.round(respondents*emptyPerc);
var Names = ['BEN', 'ROSE', '', '1', '2', '6', ''];
var matrix = [
[0, 0, 0, 10, 10, 0, 0] ,
[0, 0, 0, 0, 10, 10, 0] ,
[0, 0, 0, 0, 0, 0, 24] ,
[10, 0, 0, 0, 0, 0, 0] ,
[10, 10, 0, 0, 0, 0, 0] ,
[0, 10, 0, 0, 0, 0, 0] ,
[0, 0, 0, 24, 0, 0, 0] ,
];
This renders incorrectly - but if I change it to...
var respondents = 40,
emptyPerc = 0.4,
emptyStroke = Math.round(respondents*emptyPerc);
var Names = ['BEN', 'LIB', 'ROSE', '', '1', '2', '6', ''];
var matrix = [
[0, 0, 0, 0, 10, 10, 0, 0] ,
[0, 0, 0, 0, 0, 10, 0, 0] ,
[0, 0, 0, 0, 0, 10, 10, 0] ,
[0, 0, 0, 0, 0, 0, 0, 24] ,
[10, 0, 0, 0, 0, 0, 0, 0] ,
[10, 10, 10, 0, 0, 0, 0, 0] ,
[0, 0, 10, 0, 0, 0, 0, 0] ,
[0, 0, 0, 0, 24, 0, 0, 0] ,
];
Then it works great. I obviously see the difference between the two blocks of code, but why are they producing different results, and is it possible to modify my code to accommodate both examples?
If you examine the dodgy arc, you will see you can flip it into the right place by altering the sign on the transform from (50,0) to (-50,0). If you then look at the code that assigns the transform, it is
.attr("transform", function(d, i) {
d.pullOutSize = pullOutSize * ( d.startAngle + 0.01 > Math.PI ? -1 : 1);
return "translate(" + d.pullOutSize + ',' + 0 + ")";
});
with a note in the original text to say that "the 0.01 is for rounding errors". Given that the startAngle is already 3.13--i.e. very close to Pi--it looks like this is an edge case where the value fell just the wrong side of the cutoff. Changing the allowable rounding error value to 0.02 puts the arc in the correct place, or you could do something like
d.pullOutSize = pullOutSize * (
// is the start angle less than Pi?
d.startAngle + 0.01 < Math.PI ? 1 :
// if yes, is the end angle also less than Pi?
d.endAngle < Math.PI ? 1 : -1 );
to prevent edge cases like that in your dataset.

Not getting the correct EigenValue/EigenVector (eigenVV)

I'm trying to figure out how to use eigenVV, but no luck so far. The following is my code and could anyone of you point out what I'm doing wrong? Thanks.
CvMat* A2;
A2 = cvCreateMat(3, 3, CV_32FC1);
cvmSet(A2, 0, 0, 1);
cvmSet(A2, 0, 1, 3);
cvmSet(A2, 0, 2, -3);
cvmSet(A2, 1, 0, -3);
cvmSet(A2, 1, 1, 7);
cvmSet(A2, 1, 2, -3);
cvmSet(A2, 2, 0, -6);
cvmSet(A2, 2, 1, 6);
cvmSet(A2, 2, 1, -2);
CvMat* transp_A2;
CvMat* ATA2;
CvMat* eigenVal2;
CvMat* eigenVec2;
transp_A2 = cvCreateMat(3, 3, CV_32FC1);
ATA2 = cvCreateMat(3, 3, CV_32FC1);
eigenVal2 = cvCreateMat(3, 1, CV_32FC1);
eigenVec2 = cvCreateMat(3, 3, CV_32FC1);
cvTranspose(A2, transp_A2);
cvMatMul(transp_A2, A2, ATA2);
cvEigenVV(ATA2, eigenVec2, eigenVal2);
Edit :
This is output of eigenVec2 variable
-0.236854,0.862897,-0.446442,
0.969565,0.239264,-0.0519332,
-0.0620046,0.445154,0.893304,
I wanted to use that EigenVector EigenValue to create projection matrix and comparing it against with projection matrix I have. I thought my answer was wrong when it doesn't match with the one I have. But I figured out the resulting projection matrix is not exactly the same. It is differed by scale factor. So the way I use eigenvv is correct.

Why can't I do an equality test of a synth parameter?

I'm mystified. In this code:
SynthDef(\acid,
{
|out, gate = 1, freq, myParam, amp, cutoff, resonance, filtEnvAmt|
var env, audio, filtEnv;
if (myParam == \something, { freq = 200; });
env = Linen.kr(gate, 0, 1, 0, doneAction: 2);
audio = LFSaw.ar(freq, mul: amp);
filtEnv = Line.kr(midicps(cutoff + filtEnvAmt), midicps(cutoff), 0.2);
audio = RLPFD.ar(audio, ffreq: filtEnv + MouseX.kr(0, 5000), res: MouseY.kr(0, 1), dist: 0);
Out.ar(out, audio * env);
}
).add;
b = Pbind(*[
out: 0,
instrument: \acid,
stepsPerOctave: 19,
scale: [0, 3, 5, 8, 11, 14, 17],
octave: 3,
degree: Pseq([0, \, 3, 3, 4, 4, 9, 4, 4]),
myParam: \something,
//prevFreq: Pseq([0, 0, 0, 0, 9, 0, 0, 0, 0]),
dur: Pseq([0.4, 0.4, 0.1, 0.1, 0.1, 0.1, 0.2, 0.1, 0.1]),
cutoff: Pseq([60, \, 50, 60, 80, 60, 90, 80, 50]),
filtEnvAmt: Pseq([20, \, 20, 20, 20, 20, -10, 20, 20]),
resonance: Pseq([0.5, \, 0.5, 0.5, 0.5, 0.5, 0.3, 0.5, 0.5])
]);
b.play;
..the equality test myParam == \something never returns true, despite the fact that the Pbind is clearly sending \something each time. No other value will work either: 0, nil etc.
The equality tests myParam == myParam and \something == \something do work however, so in these cases I get a monotone melody.
I can only guess that a Pbind sends each value in some kind of wrapper, but I've no idea how to then check them from inside the synth. Can anyone help?
First: you can't send symbols to a synth control. You can only send numbers.
Second: your example doesn't say what freq should be if the test is false. In fact, you should write it in more of a dataflow style such as:
freq = if(testcondition, 200, 300);
That's the kind of thing that will work in a synthdef.
Third is a slightly frustrating thing in sc language, which is that the == message is always evaluated at synthdef compile time - the equality is checked once, and then never again. In order to have "live" equality checking, you can use this slightly clunky expression:
BinaryOpUGen("==", thinga, thingb)
So in summary you might write
freq = if(BinaryOpUGen("==", myParam, 1), 200, 300);

Why does it matter how I execute this code?

I have built the following simple synth structure which creates a synth and routes its output through an effects unit:
b = Bus.audio(numChannels: 2);
SynthDef(
"mySynth",
{
|freq, amp, gate = 1|
var vol = 0.5;
var audio = Pulse.ar(freq, 0.5);
var env = EnvGen.kr(Env.perc, doneAction:2);
audio = Pan2.ar(audio, MouseX.kr(-1, 1));
Out.ar(b, audio * env);
}
).add;
SynthDef(
"effects",
{
var audio = In.ar(b, 2);
audio = LPF.ar(audio, MouseY.kr(200, 1000));
//TODO: Implement some crazy, revolutionary effects
Out.ar(0, audio);
}
).add;
// **** Dividing line for executing the code ****
e = Synth(\effects);
p = Pbind(*[
instrument: \mySynth,
scale: #[0, 2, 4, 5, 7, 9, 11],
degree: Pseq([3, 3, 9, 9, 2, 9, 9, 3, 5, 7], inf),
dur: Pseq([0.2, 0.2, 0.2, 0.1, 0.1, 0.2, 0.2, 0.2, 0.1, 0.1], inf),
amp: Pseq([1, 0.6, 0.9, 0.3, 0.4, 0.9, 0.6, 0.85, 0.3, 0.4], inf),
]);
p.play;
This only produces audible output when I execute the code in a particular way:
I can execute each block individually, in order, and I get audible output.
I can execute the first blocks up to the 'dividing line' comment, then the following blocks, and I get audible output.
If I execute all the code together, I don't get audible output.
I'm guessing there has to be some delay between declaring a SynthDef and then instantiate it using Synth(), while the server does set setup stuff. Can anyone shed any light?
I usually get around this with the Server.sync() method. It pauses execution of the enclosing thread (e.g. a Routine) until all asynchronous server commands have been completed. This includes sending SynthDefs and allocating Buffers. You can pass a Condition argument to Server.sync() for more explicit control.
so for example, you can execute this block in one go:
s = Server.local;
s.boot;
s.doWhenBooted({
Routine {
SynthDef.new(\sine, {
arg out=0, hz=220, dur=4.0;
var snd, amp;
snd = SinOsc.ar(hz);
amp = EnvGen.ar(Env.linen(0.1, dur, 0.1), doneAction:2);
Out.ar(out, (amp*snd).dup);
}).send(s);
s.sync; // waits here
x = Synth.new(\sine);
}.play;
});
It is because you can't just "add" SynthDefs to the server and create an instance of said synth in the same execution. If you "play" the synths as they are executed then an instance of them gets added to the server so that when you call the Synth up for execution it will already be loaded. Working code is included below.
(
b = Bus.audio(numChannels: 2);
SynthDef(
"mySynth",
{
|freq, amp, gate = 1|
var vol = 0.5;
var audio = Pulse.ar(freq, 0.5);
var env = EnvGen.kr(Env.perc, doneAction:2);
audio = Pan2.ar(audio, MouseX.kr(-1, 1));
Out.ar(b, audio * env);
}
).play;
SynthDef(
"effects",
{
var audio = In.ar(b, 2);
audio = LPF.ar(audio, MouseY.kr(200, 1000));
//TODO: Implement some crazy, revolutionary effects
Out.ar(0, audio);
}
).play;
// **** Dividing line for executing the code ****
e = Synth(\effects);
p = Pbind(*[
instrument: \mySynth,
scale: #[0, 2, 4, 5, 7, 9, 11],
degree: Pseq([3, 3, 9, 9, 2, 9, 9, 3, 5, 7], inf),
dur: Pseq([0.2, 0.2, 0.2, 0.1, 0.1, 0.2, 0.2, 0.2, 0.1, 0.1], inf),
amp: Pseq([1, 0.6, 0.9, 0.3, 0.4, 0.9, 0.6, 0.85, 0.3, 0.4], inf),
]);
p.play;
)
I'm sure you're right that it's to do with the delay between declaring the synthdef and it being ready.
I'm not really experienced enough with sclang to immediately tell you exactly how you should change your code (I generally use scsynth via OSC, only using sclang to write SynthDefs), but you should be able to do something with the optional completionMsg argument to SynthDef.add.

Resources