CLASS:: FoaDecode
summary:: First Order Ambisonic (FOA) decoder
categories:: Libraries>Ambisonic Toolkit>FOA>Decoding, UGens>Multichannel>Ambisonics
related:: Classes/FoaDecoderMatrix, Classes/FoaDecoderKernel

DESCRIPTION::
Renders (decodes) a first order ambisonic signal (B-format) to speaker feeds in a variety of configurations. link::Classes/DecodeB2:: is the SuperCollider inbuilt equivalent.


CLASSMETHODS::

METHOD:: ar

argument:: in
The B-format signal, an array: [w, x, y, z]

argument:: decoder
link::Classes/FoaDecoderMatrix:: or link::Classes/FoaDecoderKernel:: instance.

argument:: mul
Output will be multiplied by this value.

argument:: add
This value will be added to the output.

returns::
An array of channels, one for each speaker.


EXAMPLES::

The examples below are intended to briefly illustrate some of the first order decoding options made available in the Ambisonic Toolkit. The user is encouraged to carefully review the features of link::Classes/FoaDecoderMatrix:: and link::Classes/FoaDecoderKernel:: to gain a deeper understanding of the flexibility of these tools.

As the Ambisonic technique is a hierarchal system, numerous options for playback are possible. These include two channel stereo, two channel binaural, 2D horizontal only surround (pantophonic) and full 3D with height surround (periphonic). A brief introduction is explored below.


Encoded as an omnidirectional soundfield, link::Classes/PinkNoise:: is used as the example sound source. In a well aligned, dampend studio environment, this usually sounds "in the head". link::Classes/FoaPush:: is used to "push" the omnidirectional soundfield so that it becomes a planewave (infinite distance, in an anechoic environment) arriving from some direction.

The soundfield is controlled by link::Classes/MouseX:: and link::Classes/MouseY::, where link::Classes/MouseX:: specifies the incident azimuth angle (pi to -pi; left to right of display) and link::Classes/MouseY:: the link::Classes/FoaPush:: angle (0 to pi/2; bottom to top of display). With the mouse at the bottom of the display, the soundfield remains omnidirectional. Placed at the top of the display, the soundfield becomes directional, and varying left/right position will vary the incident azimuth of the resulting planewave.


subsection:: Virtual microphone stereo decoder

The soundfield may be decoded to stereo using a pair of virtual microphones.

note:: A matrix type decoder, see link::Classes/FoaDecoderMatrix#*newStereo:: for further details.::

code::
// ------------------------------------------------------------
// virtual microphone stereo decoder
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderMatrix.newStereo


// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi

(
{
    var sig;// audio signal
    var angle, azim;			// angle and azimuth control


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //            bottom        = omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

	
    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);


    // ------------------------------------------------------------
    // decode (to stereo)
    FoaDecode.ar(sig, ~decoder);

}.scope;
)

// free kernel
~decoder.free

// ------------------------------------------------------------
::


subsection:: Ambisonic UHJ stereo decoder

Ambisonic UHJ stereo footnote::See: link::http://en.wikipedia.org/wiki/Ambisonic_UHJ_format:::: is the 'native' stereo format for Ambisonics. A B-format signal (2D, with some losses) can be recovered from a UHJ decoded signal through the use of link::Classes/FoaEncoderKernel#*newUHJ::.



note:: A kernel type decoder, see link::Classes/FoaDecoderKernel#*newUHJ:: for further details.::

warning:: Kernel decoders require special care. Allow the kernel time to load before attempting to use. Additionally, the kernel buffer should be freed through the use of link::Classes/FoaDecoderKernel#-free:: after use.::



code::
// ------------------------------------------------------------
// UHJ (stereo) decoder
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderKernel.newUHJ       // kernel decoders should be freed after use!!
                                         // free below...

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi

(
{
    var sig;// audio signal
    var angle, azim;			// angle and azimuth control


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //            bottom        = omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

	
    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);


    // ------------------------------------------------------------
    // decode (to stereo)
    FoaDecode.ar(sig, ~decoder);

}.scope;
)

// free kernel
~decoder.free

// ------------------------------------------------------------
::

subsection:: Synthetic binaural decoder


The Ambisonic Tookit provides a synthetic spherical head model HRTF decoder. footnote::See: R. O. Duda, "Modeling head related transfer functions," in Proceedings of the Twenty-Seventh Annual Asilomar Conference on Signals, Systems and Computers, Asilomar, CA, 1993.:: Ten subjects with varying head sizes are available. Audition to find one that works best for you.

Additionally, HRTF decoders computed from measured HRIRs are also available: link::Classes/FoaDecoderKernel#*newListen:: & link::Classes/FoaDecoderKernel#*newCIPIC::. 


note:: A kernel type decoder, see link::Classes/FoaDecoderKernel#*newSpherical:: for further details.::

warning:: Kernel decoders require special care. Allow the kernel time to load before attempting to use. Additionally, the kernel buffer should be freed through the use of link::Classes/FoaDecoderKernel#-free:: after use.::


code::
// ------------------------------------------------------------
// Binaural (synthetic) decoder
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderKernel.newSpherical // kernel decoders should be freed after use!!
                                         // free below...

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi

(
{
    var sig;// audio signal
    var angle, azim;			// angle and azimuth control


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //            bottom        = omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

	
    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);


    // ------------------------------------------------------------
    // decode (to binaural)
    FoaDecode.ar(sig, ~decoder);

}.scope;
)

// free kernel
~decoder.free

// ------------------------------------------------------------
::


subsection:: CIPIC binaural decoder

Measured HRTF decoder, with measurements from the University of California Davis' link::http://interface.cipic.ucdavis.edu/sound/hrtf.html##CIPIC HRTF database::. footnote::See: link::http://interface.cipic.ucdavis.edu/sound/hrtf.html:::: Forty-five subjects with varying head sizes are available. Audition to find one that works best for you.


note:: A kernel type decoder, see link::Classes/FoaDecoderKernel#*newCIPIC:: for further details.::

warning:: Kernel decoders require special care. Allow the kernel time to load before attempting to use. Additionally, the kernel buffer should be freed through the use of link::Classes/FoaDecoderKernel#-free:: after use.::

code::
// ------------------------------------------------------------
// Binaural (CIPIC) decoder
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderKernel.newCIPIC     // kernel decoders should be freed after use!!
                                         // free below...

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi

(
{
    var sig;// audio signal
    var angle, azim;			// angle and azimuth control


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //            bottom        = omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

	
    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);


    // ------------------------------------------------------------
    // decode (to binaural)
    FoaDecode.ar(sig, ~decoder);

}.scope;
)

// free kernel
~decoder.free

// ------------------------------------------------------------
::


subsection:: Quadraphonic decoder

The Ambisonic Toolkit provides an optimised quadraphonic decoder with variable loudspeaker angle. The below example uses the default settings, which results in a square layout, code::'single':: band type ( code::'energy':: ) decoder. This sort of decoder is suitable for mid-scale playback, though, for best results for an audience, the use of a larger array (5+ loudspeakers) is advised. link::Classes/FoaDecoderMatrix#*newPanto:: or link::Classes/FoaDecoderMatrix#*newDiametric:: would be appropriate.

A psychoacoustically optimised (dual-band) near-field compensated decoder, suitable for studio monitoring, is demonstrated link::#psychoacoustically_optimised_quadraphonic_decoder#below::.

note:: A matrix type decoder, see link::Classes/FoaDecoderMatrix#*newQuad:: for further details.::

code::
// ------------------------------------------------------------
// quad decoder
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderMatrix.newQuad

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi

(
{
    var sig;				// audio signal
    var angle, azim;			// angle and azimuth control
    var fl, bl, br, fr;			// quad output channels


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //		  bottom	= omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);



    // ------------------------------------------------------------
    // decode (to quad)
    #fl, bl, br, fr = FoaDecode.ar(sig, ~decoder);
    [fl, fr, bl, br]	// reorder output to match speaker arrangement

}.scope;
)
// ------------------------------------------------------------
::

subsection:: SuperCollider's inbuilt decoder

By default, SuperCollider includes a pantophonic (2D) decoder, link::Classes/DecodeB2::. This inbuilt decoder provides functionality similar to the Ambisonic Toolkit's link::Classes/FoaDecoderMatrix#*newPanto::, with the exceptions of a variable strong::k:: argument and the documentation features of link::Classes/FoaDecoderMatrix::, e.g. link::Classes/FoaDecoderMatrix#-dirChannels::.

The inbuilt decoder is a code::'controlled':: strong::k:: decoder. (See link::Classes/FoaDecoderMatrix#decoder_k#this discussion:: on strong::k::.) The below code includes a function, code::funK::, to add variable strong::k:: functionality to link::Classes/DecodeB2::. So, this example is realised as a code::'single':: band type ( code::'energy':: ) decoder, matching the link::Classes/FoaDecoderMatrix#*newQuad:: example link::#quadraphonic_decoder#above::.

note:: See link::Classes/DecodeB2#*ar:: for further details.::


code::
// ------------------------------------------------------------
// compare to SuperCollider's inbuilt DecodeB2 (as quad decoder)
//
// mono pink noise source
// omni encoder


// define encoder matrix and decoder channels
~encoder = FoaEncoderMatrix.newOmni
~numChans = 4

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~numChans

// function to adjust k of DecodeB2.ar
(
var funK;

funK = { arg k;
    if ( k.isNumber, {
            k
        }, {
	    switch ( k,
		'velocity', 	{ [1, 2, 2, 2] },
		'energy', 	{ [1, 2.sqrt, 2.sqrt, 2.sqrt] },
		'controlled',   { [1, 1, 1, 1] },
		'single', 	{ [1, 2.sqrt, 2.sqrt, 2.sqrt] }
	    )
	}
    )
};

~kScale = funK.value('single');         // specify ATK's default,
)                                       // a single band ('energy') decoder

(
{
    var sig;                            // audio signal
    var angle, azim;                    // angle and azimuth control
    var fl, bl, br, fr;                 // quad output channels
    var w, x, y, z;                 	// b-format channels (split)


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format("inbuilt").postln;

    // angle ---> top           = push to plane wave
    //            bottom        = omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left     = back
    //            centre        = centre
    //            hard right    = back
    azim = MouseX.kr(pi, -pi);

    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar;                         // mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);

    // ------------------------------------------------------------
    // split to w, x, y, z, and scale k
    #w, x, y, z = sig * ~kScale;

    // ------------------------------------------------------------
    // decode (to quad), and match gain to ATK decoders
    #fl, fr, br, bl = DecodeB2.ar(~numChans, w, x, y) * 6.neg.dbamp;
    [fl, fr, bl, br]    // reorder output to match speaker arrangement

}.scope;
)
// ------------------------------------------------------------
::


subsection:: Psychoacoustically optimised quadraphonic decoder

The decoder presented here is an example of a dual-band ( code::'dual':: ) psychoacoustically optmisied, near-field compensated decoder described by Gerzon. footnote::M. A. Gerzon, "Multi-system ambisonic decoder," Wireless World, pp. 43-47, 69-73, July/Aug. 1977.:: This sort of decoder is considered the ideal for first order Ambisonics, meeting all the criteria outlined by Gerzon to qualify as Ambisonic, footnote::E. Benjamin, R. Lee, and A. Heller, "Is My Decoder Ambisonic?," in Proceedings of the 125th Audio Engineering Society Convention, San Francisco, 2008.:: and is the choice for critical studio listening.

Additionally, this decode is rendered as a 'narrow quadraphonic' layout, with loudspeaker angles at code:: [ 30.0, 150.0, -150.0, -30.0 ] ::. For studio based work, this can be convenient, as the front pair is at the correct angle for two channel stereo monitoring. The narrow layout gives increased localisation and stabilised images at front and back, at the expense of reduced stability at the sides. 

Near-field compensation, filtering for the near-field effects of loudspeaker placement, is made through the use of link::Classes/FoaNFC::.


note:: A matrix type decoder, see link::Classes/FoaDecoderMatrix#*newQuad:: and link::Classes/FoaNFC:: for further details.::

code::
// ------------------------------------------------------------
// narrow quad decoder, psychocacousticly optimised, & with NFC
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderMatrix.newQuad(pi/6, 'dual')
~distance = 1.2                 // louspeaker distance, for NFC, in meters


// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi
~distance

(
{
    var sig;                            // audio signal
    var angle, azim;                    // angle and azimuth control
    var fl, bl, br, fr;                 // quad output channels


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;


    // angle ---> top           = push to plane wave
    //            bottom        = omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left     = back
    //            centre        = centre
    //            hard right    = back
    azim = MouseX.kr(pi, -pi);

    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar;                         // mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);



    // ------------------------------------------------------------
    // nfc & decode (to quad)
    sig = FoaTransform.ar(sig, 'nfc', ~distance);
    #fl, bl, br, fr = FoaDecode.ar(sig, ~decoder);
    [fl, fr, bl, br]    // reorder output to match speaker arrangement

}.scope;
)
// ------------------------------------------------------------
::



subsection:: ITU 5.0 decoder

The Ambisonic Toolkit includes link::http://www.brucewiggins.co.uk/?page_id=78##Bruce Wiggins':: optimised ITU 5.0 decoders. footnote::Irregular decoders in the Ambisonic Toolkit are kindly provided by Bruce Wiggins: link::http://www.brucewiggins.co.uk/::. See also: B. Wiggins, I. Paterson-Stephens, V. Lowndes, and S. Berry, "The design and optimisation of surround sound decoders using heuristic methods," in Proceedings of UKSIM 2003: Conference on Computer Simulation, Cambridge, England, 2003.:: 

note:: A matrix type decoder, see link::Classes/FoaDecoderMatrix#*new5_0:: for further details.::


code::
// ------------------------------------------------------------
// 5.0 decoder
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderMatrix.new5_0

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi

(
{
    var sig;// audio signal
    var angle, azim;			// angle and azimuth control
    var fc, fl, bl, br, fr;		// 5.0 output channels
    var lo;	 			// low freq channel place holder


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //            bottom        = omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

	
    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);


    // ------------------------------------------------------------
    // decode (to 5.0)
    #fc, fl, bl, br, fr = FoaDecode.ar(sig, ~decoder);
    lo = Silent.ar;

    [fl, fr, fc, lo, bl, br]	// reorder output to match speaker arrangement

}.scope;
)
// ------------------------------------------------------------
::


subsection:: Periphonic (cube) decoder

A full 3D decoder, with eight loudspeakers arranged in upper and lower rings of four. This small eight channel array is not optimal for large scale playback. For public performance, a 10 or 12 channel arrangement (two rings of 5 or 6) is more suitable.

The loudspeaker layout specified by this decoder is more suited to a small-scale situation. See link::#Diametric_(bi-rectangle)_decoder#below:: for a minimal arrangement appropriate for full 3D studio monitoring.

note:: A matrix type decoder, see link::Classes/FoaDecoderMatrix#*newPeri:: for further details.::


code::
// ------------------------------------------------------------
// periphonic (3D) decoder (8-channels arranged as a cube)
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderMatrix.newPeri

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi

(
{
    var sig;				// audio signal
    var angle, azim;			// angle and azimuth control
    var flu, blu, bru, fru;		// cube output channels
    var fld, bld, brd, frd;


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //		  bottom	= omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);



    // ------------------------------------------------------------
    // decode (to cube)
    #flu, blu, bru, fru, fld, bld, brd, frd = FoaDecode.ar(sig, ~decoder);
    [flu, fru, blu, bru, fld, frd, bld, brd]	// reorder output to match speaker arrangement

}.scope;
)
// ------------------------------------------------------------
::

subsection:: Psychoacoustically optimised diametric (bi-rectangle) decoder

This bi-rectangular decoder has been described by Gerzon as optimal for small-scale, full 3D listening. The decoder presented is an example of a dual-band ( code::'dual':: ) psychoacoustically optmisied, near-field compensated decoder. Meeting all the criteria outlined by Gerzon to qualify as Ambisonic, this decoder is a good choice for full 3D critical studio listening.

The frontal loudspeaker pair is arranged at code:: [ 30.0, -30.0 ] :: degrees. For studio based work, this can be convenient, as the front pair is at the correct angle for two channel stereo monitoring.

Near-field compensation, filtering for the near-field effects of loudspeaker placement, is made through the use of link::Classes/FoaNFC::.





note:: A matrix type decoder, see link::Classes/FoaDecoderMatrix#*newDiametric:: and link::Classes/FoaNFC:: for further details.::

code::
// ------------------------------------------------------------
// diametric 3d decoder (8-channels in a bi-rectangle)
//             psychocacousticly optimised, & with NFC
//
// mono pink noise source
// omni encoder


// define encoder / decoder matrices
~encoder = FoaEncoderMatrix.newOmni
~decoder = FoaDecoderMatrix.newDiametric(
    pi/180 * [[30, 0], [-30, 0], [90, 35.3], [-90, 35.3]],
    'dual'
)
~distance = 1.2                 // louspeaker distance, for NFC, in meters

// inspect
~encoder.kind
~encoder.numChannels
~encoder.dirChannels

~decoder.kind
~decoder.numChannels
~decoder.dirChannels * 180/pi
~distance

(
{
    var sig;				// audio signal
    var angle, azim;			// angle and azimuth control
    var fl, fr, bl, br;			// bi-rectangle output channels
    var slu, sru, sld, srd;


    // display encoder and decoder
    "Ambisonic encoding via % encoder".format(~encoder.kind).postln;
    "Ambisonic decoding via % decoder".format(~decoder.kind).postln;

    // angle ---> top 		= push to plane wave
    //		  bottom	= omni-directional
    angle = MouseY.kr(pi/2, 0);

    // azimuth -> hard left 	= back
    //		  centre 	= centre
    //		  hard right 	= back
    azim = MouseX.kr(pi, -pi);

    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar; 			// mono pink noise


    // ------------------------------------------------------------
    // encode
    sig = FoaEncode.ar(sig, ~encoder);

    // ------------------------------------------------------------
    // transform
    sig = FoaTransform.ar(sig, 'push', angle, azim);



    // ------------------------------------------------------------
    // nfc & decode (to bi-rectangle)
    sig = FoaTransform.ar(sig, 'nfc', ~distance);
    #fl, fr, slu, sru, br, bl, srd, sld = FoaDecode.ar(sig, ~decoder);
    [fl, fr, bl, br, slu, sru, sld, srd]	// reorder output to match speaker arrangement

}.scope;
)
// ------------------------------------------------------------
::
