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

DESCRIPTION::
Generates decoding matrices required by the Ambisonic Toolkit's first order decoder, link::Classes/FoaDecode::.



CLASSMETHODS::


METHOD:: newMono
Virtual microphone monophonic decoder.

argument:: theta
Azimuth angle, in radians: pi to pi.neg.

argument:: phi
Elevation angle, in radians: pi/2 to pi.neg/2.

argument:: pattern
Virtual microphone pattern, 0 to 1:

table::
## strong::pattern:: || strong::virtual microphone::
## 0.0 || Omni
## 0.5 || Cardioid
## 1.0 || Bi-directional
::

discussion:: The virtual microphone decoder returns a single monophonic channel, and can be used to "listen in" to the soundfield at the specified azimuth and elevation.



METHOD:: newStereo
Virtual microphone stereophonic decoder.

argument:: angle
The half angle of the stereo pair, in radians: 0 to pi., Default: pi/2

argument:: pattern
Virtual microphone pattern, 0 to 1:

table::
## strong::pattern:: || strong::virtual microphone::
## 0.0 || Omni
## 0.5 || Cardioid
## 1.0 || Bi-directional
::

discussion:: Standard coincident stereo microphone configurations footnote::Sengpiel Audio provides a useful tool to visualise resulting imaging: link::http://www.sengpielaudio.com/Visualization-Blumlein-E.htm:::::

code::
// Default: Cardioids at 180 deg
~decoder = FoaDecoderMatrix.newStereo

// Blumlein
~angle = pi/2
~pattern = 1.0
~decoder = FoaDecoderMatrix.newStereo(~angle, ~pattern)

// Cardioids at 131 deg
~angle = 131/2 * pi/180
~pattern = 0.5
~decoder = FoaDecoderMatrix.newStereo(~angle, ~pattern)

// Super-cardioids at 115 deg
~angle = 115/2 * pi/180
~pattern = 0.63
~decoder = FoaDecoderMatrix.newStereo(~angle, ~pattern)

// Hyper-cardioids at 105 deg
~angle = 105/2 * pi/180
~pattern = 0.75
~decoder = FoaDecoderMatrix.newStereo(~angle, ~pattern)

::

While the virtual microphone stereophonic decoder is very easy and convenient, for production work the authors advise using the UHJ ( link::Classes/FoaDecoderKernel#*newUHJ:: ) decoder. footnote::See link::http://en.wikipedia.org/wiki/Ambisonic_UHJ_format:: for further information.::



METHOD:: newQuad
Optimised quadraphonic decoder with variable loudspeaker angle.

argument:: angle
The half angle of the front (and rear) loudspeaker pair. Defaults to pi/4, a square. In radians.

argument:: k
The k factor of the decoder. May be specified as a float: 0.5 to 1.0. Or more conviently by name:

list::
## code::'single'::
## code::'dual'::
## code::'velocity'::
## code::'energy'::
## code::'controlled'::
::

discussion:: The outputs are in anti-clockwise order. The position of the first speaker is left of centre. link::#-dirChannels:: may be used to query resulting loudspeaker directions.

Varying strong::angle:: allows a number of flexible quadraphonic decoders to be generated. E.g, strong::angle:: = pi/6 returns a decoder with loudspeakers at [ 30, 150, -150, 30 ] degrees.

Please see link::#decoder_k:: for further discussion on strong::k::.



METHOD:: new5_0
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.:: 

argument:: irregKind
Three kinds are available:

list::
## code::'focused'::
## code::'equal'::
## code::'four'::
::

discussion:: 
The outputs are in anti-clockwise order. The position of the first speaker is centre. link::#-dirChannels:: may be used to query resulting loudspeaker directions.

strong::irregKind:: code:: = 'focused':: returns a focused, but front weighted image. code::'equal':: equalises the angles across the image. code::'four':: is similar to code::'equal'::, but doesn't use the centre speaker.




METHOD:: newPanto
Pantophonic (2D) regular polygon decoder.

argument:: numChans
number of loudspeaker feeds

argument:: orientation
code::'flat':: or code::'point'::

argument:: k
The k factor of the decoder. May be specified as a float: 0.5 to 1.0. Or more conviently by name:

list::
## code::'single'::
## code::'dual'::
## code::'velocity'::
## code::'energy'::
## code::'controlled'::
::

discussion:: The outputs are in anti-clockwise order. The position of the first speaker is either centre or left of centre. link::#-dirChannels:: may be used to query resulting loudspeaker directions.

Used in conjunction with link::Classes/FoaDecode#*ar:: the resulting decoder is equivalent to link::Classes/DecodeB2#*ar:: (SuperCollider's inbuilt decoder), albeit with the addition of variable strong::k:: and dual-band optimised psychoacoustic decoding. link::Classes/DecodeB2#*ar:: is a "controlled opposites" decoder ( strong::k:: = code::'controlled':: ) with a gain of 6dB greater than link::#*newPanto::.

Please see link::#decoder_k:: for further discussion on strong::k::.


METHOD:: newPeri
Periphonic (3D) dual ring, regular cylindrical decoder.

argument:: numChanPairs
Number of channel pairs. (Half the total number of loudspeaker feeds.)

argument:: elevation
Elevation of the upper ring, measured from the origin. In radians.

argument:: orientation
code::'flat':: or code::'point'::

argument:: k
The k factor of the decoder. May be specified as a float: 0.5 to 1.0. Or more conviently by name:

list::
## code::'single'::
## code::'dual'::
## code::'velocity'::
## code::'energy'::
## code::'controlled'::
::

discussion::  The outputs are in anti-clockwise order, beginning with the top ring. The position of the first speaker is either centre or left of centre. link::#-dirChannels:: may be used to query resulting loudspeaker directions.

Please see link::#decoder_k:: for further discussion on strong::k::.


METHOD:: newDiametric
Gerzon's classic diametric decoder, suitable for varied periphonic and pantophonic loudspeaker arrays. footnote::See: M. A. Gerzon, "Practical Periphony: The Reproduction of Full-Sphere Sound," in Proceedings of the 65th Audio Engineering Engineering Society Convention, London, 1980, p. 10.::

argument:: directions
An array of directions for half of the loudspeaker feeds for the desired decoder. Specify in radians.

Rank 1 arrays return pantophonic, while rank 2 arrays return periphonic. E.g.,
code::
// 2D:
~directions = [ theta0, theta1, ... thetaN ];
::
code::
// 3D:
~directions = [ [ theta0, phi0 ], [ theta1, phi1 ], ... [ thetaN, phiN ] ];
::

argument:: k
The k factor of the decoder. May be specified as a float: 0.5 to 1.0. Or more conviently by name:

list::
## code::'single'::
## code::'dual'::
## code::'velocity'::
## code::'energy'::
## code::'controlled'::
::

discussion:: 
strong::directions:: specifies only half of the loudspeakers for the resulting array, the remaining loudspeakers are diametrically opposite (through the origin). link::#-dirChannels:: may be used to query resulting loudspeaker directions.

code::
// Pantophonic (2D) decoder with four channels arranged in a rectangle:
// [ 30, -30, -150, 150 ]

// specify 1/2 the desired directions
~directions = pi/180 * [ 30, -30 ];
~decoder = FoaDecoderMatrix.newDiametric(~directions);

// inspect
~decoder.dirChannels * 180/pi
::
code::
// Periphonic (3D) decoder with eight channels arranged in a bi-rectangle:
// [ [ 30, 0 ], [ -30, 0 ], [ 90, 35.3 ], [ -90, 35.3 ],
//   [ -150, -0 ], [ 150, -0 ], [ -90, -35.3 ], [ 90, -35.3 ] ]

// specify 1/2 the desired directions
~directions = pi/180 * [ [ 30, 0 ], [ -30, 0 ], [ 90, 35.3 ], [ -90, 35.3] ];
~decoder = FoaDecoderMatrix.newDiametric(~directions);

// inspect
~decoder.dirChannels * 180/pi
::

Please see link::#decoder_k:: for further discussion on strong::k::.


METHOD:: newBtoA
B-format to A-format decoder. Decodes to a variety of tetrahedral orientations and W channel weights.

argument:: orientation
Orientation of the A-format tetrahedron.

table::
## strong::orientation:: || strong::angles:: ([theta, phi] in degrees)
## code::'flu':: || code:: [[45.0,  35.3], [-45.0, -35.3], [135.0, -35.3], [-135.0,  35.3]] ::
## code::'fld':: || code:: [[45.0, -35.3], [-45.0 , 35.3], [135.0,  35.3], [-135.0, -35.3]] ::
## code::'flr':: || code:: [[54.7,   0.0], [-54.7,   0.0], [180.0,  54.7], [ 180.0, -54.7]] ::
## code::'fud':: || code:: [[ 0.0,  54.7], [  0.0, -54.7], [125.7,   0.0], [-125.7,   0.0]] ::
## code::'fbd':: || code:: [[ 0.0,   0.0], [180.0, -70.5], [112.2,  28.1], [-112.2,  28.1]] ::
## code::'fbu':: || code:: [[ 0.0,   0.0], [180.0,  70.5], [112.2, -28.1], [-112.2, -28.1]] ::
## code::'flru':: || code:: [[67.8,  28.1], [-67.8,  28.1], [  0.0, -70.5], [ 180.0,   0.0]] ::
## code::'flrd':: || code:: [[67.8, -28.1], [-67.8, -28.1], [  0.0,  70.5], [ 180.0,   0.0]] ::
::

argument:: weight
The W weight factor.

table::
## strong::weight:: || strong::kind:: || strong::W scale::
## code::'dec':: || Decorrelated (on the sphere) || 1/sqrt(3)
## code::'can':: || Canonical (B-format) || 1/sqrt(2)
## code::'uns':: || Unscaled || 1
## code::'car':: || Cardioid || sqrt(3)
::

discussion::
anchor::btoadiscuss::
The B-format to A-format decoder is primarily intended to be used in conjuction with the A-format to B-format encoder, link::Classes/FoaEncoderMatrix#*newAtoB::. Though the use of BtoA/AtoB decoder/encoder pairs, signal processing networks can be constructed which preserve the spatial encoding found in the original input B-format signal.

code::
// ------------------------------------------------------------
// Single stage allpass ambience in A-format
//
// mono pink noise source
// FoaPanB encoder
//
// quad decoder


// decoder matrix
~decoder = FoaDecoderMatrix.newQuad

// b-to-a, a-to-b matrices
~b2a = FoaDecoderMatrix.newBtoA
~a2b = FoaEncoderMatrix.newAtoB

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

(
{
    var sig;                            // audio signal
    var azim;                    	// azimuth control
    var fl, bl, br, fr;                 // quad output channels
    var delaytime, decaytime;		// allpass parameters


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

    // ------------------------------------------------------------
    // parameters
    delaytime = 0.1;
    decaytime = 0.2;

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

    // ------------------------------------------------------------
    // test sig
    sig = PinkNoise.ar * LFPulse.ar(2.0); // enveloped mono pink noise


    // ------------------------------------------------------------
    // encode to b-format
    sig = FoaPanB.ar(sig, azim);

    // ------------------------------------------------------------
    // b-to-a --> allpass --> a-to-b
    sig = FoaDecode.ar(sig, ~b2a);
    sig = AllpassN.ar(sig, delaytime, Array.fill(4, {delaytime}).rand, decaytime);
    sig = FoaEncode.ar(sig, ~a2b);


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

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


SUBSECTION:: Decoder k
The default strong::k:: ( code::'single':: ), for decoders accepting strong::k:: as an argument, returns an code::'energy':: optimised (aka "max rE") decoder. code::'dual':: returns a dual-band psychoacoustically optimised decoder. footnote::See: E. Benjamin, R. Lee, and A. Heller, "Is My Decoder Ambisonic?," in Proceedings of the 125th Audio Engineering Society Convention, San Francisco, 2008.:: The code::'dual':: decoder is the optimum choice for small scale studio or domestic settings. code::'single':: is suitable for larger, mid-scale environments. code::'velocity':: returns "strict soundfield" (aka "basic") decoding, and is not preferred for first order Ambisonics in most circumstances. code::'controlled':: returns "controlled opposites" decoding (aka "in phase"), which is often preferred in large-scale, concert environments. footnote::See: D. G. Malham, "Experience with Large Area 3-D Ambisonic Sound Systems," Proceedings of the Institute of Acoustics, vol. 14, no. 5, pp. 209–215, Nov. 1992.:: (SuperCollider's inbuilt decoder, link::Classes/DecodeB2#*ar::, is a "controlled opposites" decoder. See link::Classes/FoaDecode#supercollider#this::.)

The strong::k:: keywords translate to numerical values as follows:

table::
## strong::keyword:: || strong::k::
## code::'velocity':: || 1
## code::'energy':: || 1/sqrt(2)
## code::'controlled':: || 1/2
::

note:: For large-scale concert presentation, the authors advise auditioning values of strong::k:: between 1/2 and 1/sqrt(2).::



INSTANCEMETHODS::
private:: initPeri, initK2D, initStereo, initBtoA, initPanto, initK3D, initQuad, printOn, initMono, init5_0, initDiametric

METHOD:: kind
Answers the kind of decoder

discussion::

code::
// decoder
~decoder = FoaDecoderMatrix.newQuad

// inspect
~decoder.kind
::

code::
// decoder
~decoder = FoaDecoderMatrix.newPeri

// inspect
~decoder.kind
::



METHOD:: dim
Answers the number of decoder dimensions: 2D or 3D.

discussion::

code::
// decoder
~decoder = FoaDecoderMatrix.newQuad

// inspect
~decoder.dim
::

code::
// decoder
~decoder = FoaDecoderMatrix.newPeri

// inspect
~decoder.dim
::

METHOD:: numChannels
Answers the number of loudspeaker feeds (output channels).

discussion::

code::
// decoder (narrow quad)
~decoder = FoaDecoderMatrix.newQuad(pi/6)

// inspect
~decoder.numChannels
::

code::
// decoder (two rings of 6)
~decoder = FoaDecoderMatrix.newPeri(6)

// inspect
~decoder.numChannels
::


METHOD:: dirChannels
Answers the direction of loudspeaker feeds, with angles in radians.

returns:: A rank 1 array for pantophonic, and rank 2 array for periphonic. E.g.,
code::
// 2D:
[ theta0, theta1, ... thetaN ]
::
code::
// 3D
[ [ theta0, phi0 ], [ theta1, phi1 ], ... [ thetaN, phiN ] ]
::

discussion:: Loudspeakers should be place at the angles returned (with equal radii).

code::
// decoder (narrow quad)
~decoder = FoaDecoderMatrix.newQuad(pi/6)

// inspect
~decoder.dirChannels * 180/pi
::

code::
// decoder (two rings of 6)
~decoder = FoaDecoderMatrix.newPeri(6)

// inspect
~decoder.dirChannels * 180/pi
::

code::
// Pantophonic (2D) decoder with four channels arranged in a rectangle:
~directions = pi/180 * [ 30, -30 ]
~decoder = FoaDecoderMatrix.newDiametric(~directions)

// inspect
~decoder.dirChannels * 180/pi
::

code::
// Periphonic (3D) decoder with eight channels arranged in a bi-rectangle:
~directions = pi/180 * [ [ 30, 0 ], [ -30, 0 ], [ 90, 35.3 ], [ -90, 35.3] ]
~decoder = FoaDecoderMatrix.newDiametric(~directions)

// inspect
~decoder.dirChannels * 180/pi
::


METHOD:: matrix
Answers the decoding matrix



METHOD:: shelfFreq
If the decoder is dual-band psychoacoustically optimised, get or set the shelf filter frequency, in Hz.

discussion:: strong::shelfFreq:: defaults to 400Hz, which is suitable for a well aligned domestic or studio space. strong::shelfFreq:: may be set up to 700Hz, for a tight sweet-spot, and very focused single user listening.


METHOD:: shelfK
If the decoder is dual-band psychoacoustically optimised, get or set the shelf k.

discussion:: If set, strong::shelfK:: is optimised for the chosen decoder, and should be left at the assigned value unless one is an expert user.



METHOD:: numOutputs
A synonym for link::#-numChannels::


METHOD:: dirOutputs
A synonym for link::#-dirChannels::



METHOD:: numInputs
Answers the number of inputs for the decoder. 3 for 2D and 4 for 3D.


METHOD:: dirInputs
A convenience method providing polymorphism with link::Classes/FoaEncoderMatrix#-dirInputs::.

returns::

list::
## for 2D: code:: [ inf, inf, inf ] ::
## for 3D: code:: [ inf, inf, inf , inf ] ::
::


EXAMPLES::

Please see link::Classes/FoaDecode#Examples::.
