Here’s a voice harmonization SynthDef I’ve been working on. It’s based on the medieval practice of Fauxbourdon.
The idea is that the pitch shift synth recognizes the scale degree of the input signal using Tartini, then uses that information to pick the right interval for the lower voice (called voice2 here) based on the scale degree. voice1 is just parallel fourths with the input signal and doesn’t require pitch recognition.
The tricky bit is the spatialization at the end: As I’ve posted it here, the output is noticeably louder in the left headphone than in the right.
If I add a !2 to the parameters of Out, like so …
Out.ar(\out.kr(0), sig!2 * \amp.kr(0.8));
I get a balanced stereo output.
However, I would like to distribute the voices over the stereo field so that each voice has its own position.
What might cause this behavior? How would I go about solving this issue without just duplicating the signal?
(
~rootnum = 62; // D4
~makeBuffers = {
~diff = Dictionary.new;
~diff.add(\maj -> [ 5/8, 3/5, 3/5, 5/8, 3/5, 5/8, 3/5, 5/8, 3/5, 3/5, 5/8, 3/5 ]);
~diff.add(\min -> [ 3/5, 5/8, 3/5, 5/8, 3/5, 3/5, 5/8, 3/5, 5/8, 3/5, 5/8, 3/5 ]);
~diffbuf = Buffer(s, ~diff[\min].size, 1);
// use ~diff[maj] for major
s.listSendMsg(~diffbuf.allocMsg(~diffbuf.setnMsg(0, ~diff[\min])).postln);
};
~makebuffers.value();
SynthDef(\pitchshift, {
var freq, hasFreq, sig, voice1, voice2, ratio1, ratio2, root;
root = \rootmidinum.kr(~rootnum) %12;
ratio1 = \r1.kr(0.75);
sig = In.ar(\in.ir(0), 1); // expects mono input
# freq, hasFreq = Tartini.kr(sig, 0.93, \nsize.kr(2048), \ksize.kr(0), \overlap.kr(1024), \smallCutoff.kr(0.5));
f = freq.cpsmidi.round(1) + root;
ratio2 = WrapIndex.kr(~diffbuf.bufnum, f);
voice1 = PitchShiftPA.ar(
in: sig,
freq: freq,
pitchRatio: ratio1,
formantRatio: 1,
minFreq: 20,
maxFormantRatio: 10,
grainsPeriod: 2,
timeDispersion: 5
) * \v1amp.kr(0.dbamp);
voice2 = PitchShiftPA.ar(
in: sig,
freq: freq,
pitchRatio: ratio2,
formantRatio: 1,
minFreq: 20,
maxFormantRatio: 10,
grainsPeriod: 2,
timeDispersion: 5
) * \v2amp.kr(0.dbamp);
sig = [voice2, sig, voice1];
sig = Splay.ar(sig);
Out.ar(\out.kr(0), sig * \amp.kr(0.8));
Out.ar(\fx.kr(0), sig * \send.kr(0.25));
}).add;
~pitch = Synth(\pitchshift, [\rootmidinum, ~rootnum, \in, SoundIn.ar(0), \out, 0]);
)
(This is an abbreviated version of the code I’m actually working with, a repository with the full project is here.)