Quad to binaural?

I wish to record my quadriphonic work (square plan type LeftFront, RightFront, LeftBack, RightBack) toward binaural involving consequently some kind of HRTF filter. Is there a way, an existing Ugen able to do that on SuperCollider?

Have you checked out the ambisonic toolkit plugin? its in sc3-plugin. See if you have FoaDecode installed?

I have the latest version 3.13.0, but no FoaDecode.
It is weird because it is listed on GitHub code source.
Maybe I should compile it from the source?

Jumping straight to compiling from source is quite extreme, and wouldn’t fix your issue here.

sc3-plugins is a separate download. What operating system are you on, and how did you install supercollider?

Once you have it installed, you use it like this…

s.waitForBoot {
	~decoder = FoaDecoderKernel.newListen(1002, s);
		var ambi = ....some ambi signal;
		FoaDecode.ar(ambi, ~decoder);

OS X 12.6.7 (Monterey)
SuperCollider 3.13.0 downloaded from this page (3.13.0 universal binary, macOS 10.14 and later).

What do you mean by ‘some ambi signal’?
I do not use any ambisonic tools. I have four channels output as the UGen Pan4 does.
I am not too familiar with ambisonic as you can guess.
The little I understand seems we have to convert the 4-channel array to some kind of ambisonic matrix. Am I right and is it possible?

You have to install the quark as well:

Ok! Super Thanks! I got it (I mean the ATK quark :slightly_smiling_face:).

s.waitForBoot {
	~binaural_decoder = FoaDecoderKernel.newListen(1002, s);
		var quad = ....some quad signal;
		var ambi = FoaEncode.ar(quad, FoaEncoderMatrix.newPanto(4));
		var binaural = FoaDecode.ar(ambi, ~binaural_decoder);

There are many subtitles to this, but the above is the basic use case.

1 Like

Your last code does not work!
I did it with the first one but there is some kind of confusion with the speakers’ order according to the panoramic of Pan4:

s.waitForBoot {
	~encoder = FoaEncoderMatrix.newQuad;
	//~encoder.dirChannels = [ -45, 45, -135, 135 ].degrad;
	~decoder = FoaDecoderKernel.newListen(1002, s);
		var ambi = FoaEncode.ar(Pan4.ar(PinkNoise.ar(0.5), MouseX.kr(-1,1), MouseY.kr(-1,1)), ~encoder);
		FoaDecode.ar(ambi, ~decoder);

Maybe I do not use the right encoder/decoder …

1 Like

Yup, Pan4 uses a non ambisonic friendly LF, RF, LR, RR format. Use this instead…

s.waitForBoot {
	~binaural_decoder = FoaDecoderKernel.newListen(1002, s);
		var mono = PinkNoise.ar(0.2);
		var ambi = FoaPanB.ar( 
			MouseX.kr(-pi, pi)
		var binaural = FoaDecode.ar(ambi, ~binaural_decoder);

The fact is my work outputs a four channels array as Pan4 does.
Is there any doc about the ambisonic format?
I am going to study all that, and if I find a happy outcome I will send feedback.
Meanwhile, any help is welcome.

In that case, you can convert to the expected quad format like this…

var oldPan ....;
var newPan = [oldPan[0], oldPan[1], oldPan[3], oldPan[2]];

It just swaps the back speakers.
Now you can use that with FoaEncoderMatrix.newQuad.

And yes, the ambisonic toolkit has a (perhaps too) indepth tutorial in the help docs.

Yes, I know about that.
Maybe is the pink noise making the confusion. I did it with sine waves and it sounds like it is ok.
Now, the issue is to bring my outputs to the encoder.

~encodeBus=Bus.audio(s, 4);
a = Synth(\mainOutputSynth, [\out, ~encodeBus]);


   var ambi = FoaEncode.ar(~encodeBus, ~encoder);

But no sound!

Now, it looks like it is ok with:

   var ambi = FoaEncode.ar(InBus.ar(~encodeBus, 4), ~encoder);


Thanks for sharing this. I’ve also been wanting to get into binaural for some time. This code is a great place to start.

Apologies if I’m hijacking here, but I’m trying to wrap my head around how this binaural might work with multichannel expansion. I.e., let’s say that I have an array of 8 signals. Right now, I’ve got code that looks like this:
verbSend = Pan2.ar(signals, pan).sum;
Where verbSend is the panned and summed stereo signal, signals is an array of mono signals, and pan is an array of pan values.
Now if I wanted to replace the panned values with an array of binaural values, I’m guessing that naively adding .sum isn’t going to return the 2-channel binaural output I’m expecting.
var binaural = FoaDecode.ar(signals, binaural_pan).sum;
isn’t going to do what I want, correct?
What would be the best way to do that?

TLDR: yes that works fine, but you might have your encode/decode the wrong way around.

Here is how to quickly test it.

Not in any SynthDef nor in any function, just right in the main window, evaluate this line by line and watch the post window, it will show you the layout of the signal.

~sig = SinOsc.ar()!8; //  [ a SinOsc, a SinOsc, a SinOsc, a SinOsc, a SinOsc, a SinOsc, a SinOsc, a SinOsc ]
~pan = Pan2.ar(~sig, 0).sum; // [ a BinaryOpUGen, a BinaryOpUGen ]

Here above how the Pan2 line returned an array of two channels, thats your stereo.

~bin = FoaPanB.ar(~sig, 0).sum; //  [ a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ]

And here it turns the mono signals into one ambisonic channel.
If you remove the .sum it gives you 8*4 channels in an array.

This is the wrong way around, you encode into ambisonic, and decode out. If you want to pan mono signals around a 2D circle, FoaPanB is your friend. It is an encoder, although not explicitly stated. Now to move the sound closer to the centre, check out FoaProximity, but that works on an ambisonic signal.

1 Like

On the “perhaps too indepth” :wink: front, the documentation includes a fairly verbose review of FOA decoding.

Two use cases very close to the problem raised by @Yann_Ics can be found in the Binaural discussion. (BTW, Discussion of FOA encoding can be found on this page.)

For this exact transcoding problem (Quad to Binaural) we could use FoaEncoderMatrix: *newQuad for the job; though, we’d need to sort the initial quad channel ordering to match that expected by *newQuad.

In this case, the easiest way would be to use FoaEncoderMatrix: *newDirections:

// define encoder matrix
~directions = [ 45, -45, 135, -135 ].degrad;  // quad: LeftFront, RightFront, LeftBack, RightBack
~encoder = FoaEncoderMatrix.newDirections(~directions);  // choose for Yann_Ics
~decoder = FoaDecoderKernel.newSpherical;  // kernel decoders should be freed after use!!

... presume ~myQuad is available...

// transcode...
~myBinaural = FoaDecode.ar(FoaEncode.ar(~myQuad, ~encoder), ~decoder);
1 Like