FoaDecode: table of contents
Filter:
Classes (extension) | Libraries > Ambisonic Toolkit > FOA > Decoding | UGens > Multichannel > Ambisonics

FoaDecode : FoaUGen : Object
ExtensionExtension

First Order Ambisonic (FOA) decoder
Source: ATK.sc

Description

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

Class Methods

FoaDecode.ar(in, decoder, mul: 1, add: 0)

Arguments:

in

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

decoder

FoaDecoderMatrix or FoaDecoderKernel instance.

mul

Output will be multiplied by this value.

add

This value will be added to the output.

Returns:

An array of channels, one for each speaker.

Inherited class methods

Instance Methods

Inherited instance methods

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 FoaDecoderMatrix and 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, PinkNoise is used as the example sound source. In a well aligned, dampend studio environment, this usually sounds "in the head". 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 MouseX and MouseY, where MouseX specifies the incident azimuth angle (pi to -pi; left to right of display) and MouseY the 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.

Before exploring the examples below, it is suggested you confirm your Server has enough output channels to support your chosen decoder. You can query the server:

myServer.options.numOutputBusChannels

An example Function, ~checkMyServerOutputs, can be found here. ~checkMyServerOutputs throws a warning if myServer.options.numOutputBusChannels < myDecoder.numOutputs. If you need to update your Server's number of output bus channels, review the example found here.

Virtual microphone stereo decoder

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

NOTE: A matrix type decoder, see FoaDecoderMatrix: *newStereo for further details.
// ------------------------------------------------------------
// 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.raddeg

(
{
    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

// ------------------------------------------------------------

Ambisonic UHJ stereo decoder

Ambisonic UHJ stereo1 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 FoaEncoderKernel: *newUHJ.

NOTE: A kernel type decoder, see 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 FoaDecoderKernel: -free after use.
// ------------------------------------------------------------
// 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.raddeg

(
{
    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

// ------------------------------------------------------------

Synthetic binaural decoder

The Ambisonic Tookit provides a synthetic spherical head model HRTF decoder.2 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: FoaDecoderKernel: *newListen & FoaDecoderKernel: *newCIPIC.

NOTE: A kernel type decoder, see 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 FoaDecoderKernel: -free after use.
// ------------------------------------------------------------
// 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.raddeg

(
{
    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

// ------------------------------------------------------------

CIPIC binaural decoder

Measured HRTF decoder, with measurements from the University of California Davis' CIPIC HRTF database.3 Forty-five subjects with varying head sizes are available. Audition to find one that works best for you.

NOTE: A kernel type decoder, see 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 FoaDecoderKernel: -free after use.
// ------------------------------------------------------------
// 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.raddeg

(
{
    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

// ------------------------------------------------------------

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, 'single' band type ( '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. FoaDecoderMatrix: *newPanto or FoaDecoderMatrix: *newDiametric would be appropriate.

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

NOTE: A matrix type decoder, see FoaDecoderMatrix: *newQuad for further details.
// ------------------------------------------------------------
// 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.raddeg

(
{
    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;
)
// ------------------------------------------------------------

SuperCollider's inbuilt decoder

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

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

NOTE: See DecodeB2: *ar for further details.
// ------------------------------------------------------------
// 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;
)
// ------------------------------------------------------------

Psychoacoustically optimised quadraphonic decoder

The decoder presented here is an example of a dual-band ( 'dual' ) psychoacoustically optmisied, near-field compensated decoder described by Gerzon.4 This sort of decoder is considered the ideal for first order Ambisonics, meeting all the criteria outlined by Gerzon to qualify as Ambisonic, 5 and is the choice for critical studio listening.

Additionally, this decode is rendered as a 'narrow quadraphonic' layout, with loudspeaker angles at [ 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 FoaNFC.

NOTE: A matrix type decoder, see FoaDecoderMatrix: *newQuad and FoaNFC for further details.
// ------------------------------------------------------------
// 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.raddeg
~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;
)
// ------------------------------------------------------------

ITU 5.0 decoder

The Ambisonic Toolkit includes Bruce Wiggins' optimised ITU 5.0 decoders.6

NOTE: A matrix type decoder, see FoaDecoderMatrix: *new5_0 for further details.
// ------------------------------------------------------------
// 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.raddeg

(
{
    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;
)
// ------------------------------------------------------------

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 below for a minimal arrangement appropriate for full 3D studio monitoring.

NOTE: A matrix type decoder, see FoaDecoderMatrix: *newPeri for further details.
// ------------------------------------------------------------
// 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.raddeg

(
{
    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;
)
// ------------------------------------------------------------

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 ( '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 [ 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 FoaNFC.

NOTE: A matrix type decoder, see FoaDecoderMatrix: *newDiametric and FoaNFC for further details.
// ------------------------------------------------------------
// 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(
    [[30, 0], [-30, 0], [90, 35.3], [-90, 35.3]].degrad,
    'dual'
)
~distance = 1.2                 // louspeaker distance, for NFC, in meters

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

~decoder.kind
~decoder.numChannels
~decoder.dirChannels.raddeg
~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;
)
// ------------------------------------------------------------

[2] - 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.
[4] - M. A. Gerzon, "Multi-system ambisonic decoder," Wireless World, pp. 43-47, 69-73, July/Aug. 1977.
[5] - E. Benjamin, R. Lee, and A. Heller, "Is My Decoder Ambisonic?," in Proceedings of the 125th Audio Engineering Society Convention, San Francisco, 2008.
[6] - Irregular decoders in the Ambisonic Toolkit are kindly provided by Bruce Wiggins: 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.