/*############################################################################*/
/*#                                                                          #*/
/*#  Ambisonic C++ Library                                                   #*/
/*#  AmbisonicDecoder - Ambisonic Decoder                                   #*/
/*#  Copyright © 2007 Aristotel Digenis                                      #*/
/*#  Copyright © 2017 Videolabs                                              #*/
/*#                                                                          #*/
/*#  Filename:      AmbisonicDecoder.cpp                                     #*/
/*#  Version:       0.2                                                      #*/
/*#  Date:          19/05/2007                                               #*/
/*#  Author(s):     Aristotel Digenis, Peter Stitt                           #*/
/*#  Licence:       LGPL                                                     #*/
/*#                                                                          #*/
/*############################################################################*/


#include "AmbisonicDecoder.h"
#include "AmbisonicDecoderPresets.h"
#include <iostream>
#include <assert.h>

namespace spaudio {

    AmbisonicDecoder::AmbisonicDecoder()
    {
        m_nSpeakerSetUp = Amblib_SpeakerSetUps::kAmblib_Mono;
        m_nSpeakers = 0;
        m_pAmbSpeakers = nullptr;
        m_bPresetLoaded = false;
    }

    AmbisonicDecoder::~AmbisonicDecoder()
    {
        if (m_pAmbSpeakers)
            delete[] m_pAmbSpeakers;
    }

    bool AmbisonicDecoder::Configure(unsigned nOrder, bool b3D, unsigned nBlockSize, unsigned sampleRate, Amblib_SpeakerSetUps nSpeakerSetUp, unsigned nSpeakers)
    {
        bool success = AmbisonicBase::Configure(nOrder, b3D, 0);
        if (!success)
            return false;

        m_nBlockSize = nBlockSize;
        m_sampleRate = sampleRate;

        // Set up the ambisonic shelf filters
        m_shelfFilters.Configure(nOrder, b3D, nBlockSize, sampleRate);

        m_pBFSrcTmp.Configure(nOrder, b3D, nBlockSize);

        SpeakerSetUp(nSpeakerSetUp, nSpeakers);
        Refresh();

        return true;
    }

    void AmbisonicDecoder::Reset()
    {
        for (unsigned niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            m_pAmbSpeakers[niSpeaker].Reset();
        m_shelfFilters.Reset();
        m_pBFSrcTmp.Reset();
    }

    void AmbisonicDecoder::Refresh()
    {
        for (unsigned niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            m_pAmbSpeakers[niSpeaker].Refresh();

        // Configure the decoder matrix to ensure optimal decoding of 3D HOA to 2D arrays
        ConfigureDecoderMatrix();
        // Check if the speaker setup is one which has a preset
        CheckSpeakerSetUp();
        // Load the preset
        LoadDecoderPreset();

        m_shelfFilters.Refresh();
        m_pBFSrcTmp.Refresh();
    }

    void AmbisonicDecoder::Process(BFormat* pBFSrc, unsigned nSamples, float** ppfDst)
    {
        // If a preset is not loaded then use optimisation shelf filters
        if (!m_bPresetLoaded)
        {
            // Process a copy of the input to avoid overwriting it
            m_pBFSrcTmp = *pBFSrc;
            m_shelfFilters.Process(&m_pBFSrcTmp, nSamples);

            for (unsigned niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                m_pAmbSpeakers[niSpeaker].Process(&m_pBFSrcTmp, nSamples, ppfDst[niSpeaker]);
            }
        }
        else
        {
            for (unsigned niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                m_pAmbSpeakers[niSpeaker].Process(pBFSrc, nSamples, ppfDst[niSpeaker]);
            }
        }
    }

    Amblib_SpeakerSetUps AmbisonicDecoder::GetSpeakerSetUp()
    {
        return m_nSpeakerSetUp;
    }

    unsigned AmbisonicDecoder::GetSpeakerCount()
    {
        return m_nSpeakers;
    }

    void AmbisonicDecoder::SetPosition(unsigned nSpeaker, PolarPosition<float> polPosition)
    {
        m_pAmbSpeakers[nSpeaker].SetPosition(polPosition);
    }

    PolarPosition<float> AmbisonicDecoder::GetPosition(unsigned nSpeaker)
    {
        return m_pAmbSpeakers[nSpeaker].GetPosition();
    }

    void AmbisonicDecoder::SetOrderWeight(unsigned nSpeaker, unsigned nOrder, float fWeight)
    {
        m_pAmbSpeakers[nSpeaker].SetOrderWeight(nOrder, fWeight);
    }

    float AmbisonicDecoder::GetOrderWeight(unsigned nSpeaker, unsigned nOrder)
    {
        return m_pAmbSpeakers[nSpeaker].GetOrderWeight(nOrder);
    }

    float AmbisonicDecoder::GetCoefficient(unsigned nSpeaker, unsigned nChannel)
    {
        return m_pAmbSpeakers[nSpeaker].GetCoefficient(nChannel);
    }

    void AmbisonicDecoder::SetCoefficient(unsigned nSpeaker, unsigned nChannel, float fCoeff)
    {
        m_pAmbSpeakers[nSpeaker].SetCoefficient(nChannel, fCoeff);
    }

    bool AmbisonicDecoder::GetPresetLoaded()
    {
        return m_bPresetLoaded;
    }

    void AmbisonicDecoder::SpeakerSetUp(Amblib_SpeakerSetUps nSpeakerSetUp, unsigned nSpeakers)
    {
        m_nSpeakerSetUp = nSpeakerSetUp;

        if (m_pAmbSpeakers)
            delete[] m_pAmbSpeakers;

        PolarPosition<float> polPosition = { 0.f, 0.f, 1.f };
        unsigned niSpeaker = 0;
        float fSpeakerGain = 0.f;

        m_bPresetLoaded = false;

        switch (m_nSpeakerSetUp)
        {
        case Amblib_SpeakerSetUps::kAmblib_CustomSpeakerSetUp:
            m_nSpeakers = nSpeakers;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                m_pAmbSpeakers[niSpeaker].Configure(m_nOrder, m_b3D, 0);
            }
            break;
        case Amblib_SpeakerSetUps::kAmblib_Mono:
            m_nSpeakers = 1;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_Stereo:
            m_nSpeakers = 2;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = DegreesToRadians(30.f);
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-30.f);
            m_pAmbSpeakers[1].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_LCR:
            m_nSpeakers = 3;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = DegreesToRadians(30.f);
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(0.f);
            m_pAmbSpeakers[1].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-30.f);
            m_pAmbSpeakers[2].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[2].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_Quad:
            m_maxLayoutOrder = 1;
            m_nSpeakers = 4;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = DegreesToRadians(45.f);
            m_pAmbSpeakers[0].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-45.f);
            m_pAmbSpeakers[1].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(135.f);
            m_pAmbSpeakers[2].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
            m_pAmbSpeakers[2].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-135.f);
            m_pAmbSpeakers[3].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
            m_pAmbSpeakers[3].SetPosition(polPosition);
            m_is2dLayout = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_50:
            m_nSpeakers = 5;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = DegreesToRadians(30.f);
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-30.f);
            m_pAmbSpeakers[1].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(110.f);
            m_pAmbSpeakers[2].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[2].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-110.f);
            m_pAmbSpeakers[3].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[3].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(0.f);
            m_pAmbSpeakers[4].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[4].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_70:
            m_nSpeakers = 7;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = DegreesToRadians(30.f);
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-30.f);
            m_pAmbSpeakers[1].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(110.f);
            m_pAmbSpeakers[2].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[2].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-110.f);
            m_pAmbSpeakers[3].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[3].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(145.f);
            m_pAmbSpeakers[4].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[4].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-145.f);
            m_pAmbSpeakers[5].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[5].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(0.f);
            m_pAmbSpeakers[6].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[6].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_51:
            m_nSpeakers = 6;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = DegreesToRadians(30.f);
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-30.f);
            m_pAmbSpeakers[1].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(110.f);
            m_pAmbSpeakers[2].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[2].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-110.f);
            m_pAmbSpeakers[3].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[3].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(0.f);
            m_pAmbSpeakers[4].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[4].SetPosition(polPosition);
            // LFE channel
            m_pAmbSpeakers[5].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[5].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_71:
            m_nSpeakers = 8;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = DegreesToRadians(30.f);
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-30.f);
            m_pAmbSpeakers[1].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(110.f);
            m_pAmbSpeakers[2].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[2].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-110.f);
            m_pAmbSpeakers[3].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[3].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(145.f);
            m_pAmbSpeakers[4].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[4].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(-145.f);
            m_pAmbSpeakers[5].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[5].SetPosition(polPosition);
            polPosition.azimuth = DegreesToRadians(0.f);
            m_pAmbSpeakers[6].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[6].SetPosition(polPosition);
            // LFE channel
            m_pAmbSpeakers[7].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[7].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_Pentagon:
            m_maxLayoutOrder = 1;
            m_nSpeakers = 5;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / m_nSpeakers);
                m_pAmbSpeakers[niSpeaker].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            m_is2dLayout = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_Hexagon:
            m_maxLayoutOrder = 2;
            m_nSpeakers = 6;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / m_nSpeakers + 30.f);
                m_pAmbSpeakers[niSpeaker].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            m_is2dLayout = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_HexagonWithCentre:
            m_maxLayoutOrder = 2;
            m_nSpeakers = 6;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / m_nSpeakers);
                m_pAmbSpeakers[niSpeaker].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            m_is2dLayout = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_Octagon:
            m_nSpeakers = 8;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / m_nSpeakers);
                m_pAmbSpeakers[niSpeaker].Configure(m_nOrder, m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            m_is2dLayout = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_Decadron:
            m_nSpeakers = 10;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / m_nSpeakers);
                m_pAmbSpeakers[niSpeaker].Configure(m_nOrder, m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            m_is2dLayout = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_Dodecadron:
            m_nSpeakers = 12;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / m_nSpeakers);
                m_pAmbSpeakers[niSpeaker].Configure(m_nOrder, m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            m_is2dLayout = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_Cube:
            m_maxLayoutOrder = 1;
            m_nSpeakers = 8;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.elevation = DegreesToRadians(45.f);
            for (niSpeaker = 0; niSpeaker < m_nSpeakers / 2; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / (m_nSpeakers / 2) + 45.f);
                m_pAmbSpeakers[niSpeaker].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            polPosition.elevation = DegreesToRadians(-45.f);
            for (niSpeaker = m_nSpeakers / 2; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians((niSpeaker - 4) * 360.f / (m_nSpeakers / 2) + 45.f);
                m_pAmbSpeakers[niSpeaker].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            break;
        case Amblib_SpeakerSetUps::kAmblib_Dodecahedron:
            // This arrangement is used for second and third orders
            m_nSpeakers = 20;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            // Loudspeaker 1
            polPosition.elevation = DegreesToRadians(-69.1f);
            polPosition.azimuth = DegreesToRadians(90.f);
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            // Loudspeaker 2
            polPosition.azimuth = DegreesToRadians(-90.f);
            m_pAmbSpeakers[1].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[1].SetPosition(polPosition);

            // Loudspeaker 3
            polPosition.elevation = DegreesToRadians(-35.3f);
            polPosition.azimuth = DegreesToRadians(45.f);
            m_pAmbSpeakers[2].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[2].SetPosition(polPosition);
            // Loudspeaker 4
            polPosition.azimuth = DegreesToRadians(135.f);
            m_pAmbSpeakers[3].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[3].SetPosition(polPosition);
            // Loudspeaker 5
            polPosition.azimuth = DegreesToRadians(-45.f);
            m_pAmbSpeakers[4].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[4].SetPosition(polPosition);
            // Loudspeaker 6
            polPosition.azimuth = DegreesToRadians(-135.f);
            m_pAmbSpeakers[5].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[5].SetPosition(polPosition);

            // Loudspeaker 7
            polPosition.elevation = DegreesToRadians(-20.9f);
            polPosition.azimuth = DegreesToRadians(180.f);
            m_pAmbSpeakers[6].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[6].SetPosition(polPosition);
            // Loudspeaker 8
            polPosition.azimuth = DegreesToRadians(0.f);
            m_pAmbSpeakers[7].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[7].SetPosition(polPosition);

            // Loudspeaker 9
            polPosition.elevation = DegreesToRadians(0.f);
            polPosition.azimuth = DegreesToRadians(69.1f);
            m_pAmbSpeakers[8].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[8].SetPosition(polPosition);
            // Loudspeaker 10
            polPosition.azimuth = DegreesToRadians(110.9f);
            m_pAmbSpeakers[9].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[9].SetPosition(polPosition);
            // Loudspeaker 11
            polPosition.azimuth = DegreesToRadians(-69.1f);
            m_pAmbSpeakers[10].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[10].SetPosition(polPosition);
            // Loudspeaker 12
            polPosition.azimuth = DegreesToRadians(-110.9f);
            m_pAmbSpeakers[11].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[11].SetPosition(polPosition);

            // Loudspeaker 13
            polPosition.elevation = DegreesToRadians(20.9f);
            polPosition.azimuth = DegreesToRadians(180.f);
            m_pAmbSpeakers[12].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[12].SetPosition(polPosition);
            // Loudspeaker 14
            polPosition.azimuth = DegreesToRadians(0.f);
            m_pAmbSpeakers[13].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[13].SetPosition(polPosition);

            // Loudspeaker 15
            polPosition.elevation = DegreesToRadians(35.3f);
            polPosition.azimuth = DegreesToRadians(45.f);
            m_pAmbSpeakers[14].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[14].SetPosition(polPosition);
            // Loudspeaker 16
            polPosition.azimuth = DegreesToRadians(135.f);
            m_pAmbSpeakers[15].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[15].SetPosition(polPosition);
            // Loudspeaker 17
            polPosition.azimuth = DegreesToRadians(-45.f);
            m_pAmbSpeakers[16].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[16].SetPosition(polPosition);
            // Loudspeaker 18
            polPosition.azimuth = DegreesToRadians(-135.f);
            m_pAmbSpeakers[17].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[17].SetPosition(polPosition);

            // Loudspeaker 19
            polPosition.elevation = DegreesToRadians(69.1f);
            polPosition.azimuth = DegreesToRadians(90.f);
            m_pAmbSpeakers[18].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[18].SetPosition(polPosition);
            // Loudspeaker 20
            polPosition.azimuth = DegreesToRadians(-90.f);
            m_pAmbSpeakers[19].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[19].SetPosition(polPosition);
            break;
        case Amblib_SpeakerSetUps::kAmblib_Cube2:
            // This configuration is a standard for first order decoding
            m_maxLayoutOrder = 1;
            m_nSpeakers = 8;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.elevation = DegreesToRadians(35.2f);
            for (niSpeaker = 0; niSpeaker < m_nSpeakers / 2; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians(niSpeaker * 360.f / (m_nSpeakers / 2) + 45.f);
                m_pAmbSpeakers[niSpeaker].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            polPosition.elevation = DegreesToRadians(-35.2f);
            for (niSpeaker = m_nSpeakers / 2; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = -DegreesToRadians((niSpeaker - 4) * 360.f / (m_nSpeakers / 2) + 45.f);
                m_pAmbSpeakers[niSpeaker].Configure(std::min(m_maxLayoutOrder, m_nOrder), m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);
            }
            break;
        case Amblib_SpeakerSetUps::kAmblib_MonoCustom:
            m_nSpeakers = 17;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            polPosition.azimuth = 0.f;
            polPosition.elevation = 0.f;
            polPosition.distance = 1.f;
            for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            {
                polPosition.azimuth = DegreesToRadians(0.f);
                m_pAmbSpeakers[niSpeaker].Configure(m_nOrder, m_b3D, 0);
                m_pAmbSpeakers[niSpeaker].SetPosition(polPosition);

            }
            break;
        default:
            m_nSpeakers = 1;
            m_pAmbSpeakers = new AmbisonicSpeaker[m_nSpeakers];
            m_pAmbSpeakers[0].Configure(m_nOrder, m_b3D, 0);
            m_pAmbSpeakers[0].SetPosition(polPosition);
            break;
        };

        fSpeakerGain = 1.f / (float)m_nSpeakers;
        for (niSpeaker = 0; niSpeaker < m_nSpeakers; niSpeaker++)
            m_pAmbSpeakers[niSpeaker].SetGain(fSpeakerGain);
    }

    void AmbisonicDecoder::CheckSpeakerSetUp()
    {
        // If the speaker set up is defined as a custom layout then check if it matches
        // one with a preset
        if (m_nSpeakerSetUp == Amblib_SpeakerSetUps::kAmblib_CustomSpeakerSetUp)
        {
            int speakerMatchCount = 0;
            float azimuthStereo[] = { 30.f, -30.f };
            float azimuth50[] = { 30.f, -30.f, 110.f, -110.f, 0.f };
            float azimuth51[] = { 30.f, -30.f, 110.f, -110.f, 0.f, 0.f };
            float azimuth70[] = { 30.f, -30.f, 110.f, -110.f, 145.f, -145.f, 0.f };
            float azimuth71[] = { 30.f, -30.f, 110.f, -110.f, 145.f, -145.f, 0.f, 0.f };

            switch (GetSpeakerCount())
            {
            case 1: // Mono speaker setup
                m_nSpeakerSetUp = Amblib_SpeakerSetUps::kAmblib_Mono;
                break;
            case 2:
                for (int iSpeaker = 0; iSpeaker < 2; ++iSpeaker)
                {
                    PolarPosition<float> speakerPos = m_pAmbSpeakers[iSpeaker].GetPosition();
                    if (speakerPos.elevation == 0.f)
                        if (fabsf(speakerPos.azimuth - DegreesToRadians(azimuthStereo[iSpeaker])) < 1e-6)
                            speakerMatchCount++;
                }
                if (speakerMatchCount == 2)
                    m_nSpeakerSetUp = Amblib_SpeakerSetUps::kAmblib_Stereo;
                break;
            case 5: // 5.0
                for (int iSpeaker = 0; iSpeaker < 5; ++iSpeaker)
                {
                    PolarPosition<float> speakerPos = m_pAmbSpeakers[iSpeaker].GetPosition();
                    if (speakerPos.elevation == 0.f)
                        if (fabsf(speakerPos.azimuth - DegreesToRadians(azimuth50[iSpeaker])) < 1e-6)
                            speakerMatchCount++;
                }
                if (speakerMatchCount == 5)
                    m_nSpeakerSetUp = Amblib_SpeakerSetUps::kAmblib_50;
                break;
            case 6: // 5.1
                for (int iSpeaker = 0; iSpeaker < 6; ++iSpeaker)
                {
                    PolarPosition<float> speakerPos = m_pAmbSpeakers[iSpeaker].GetPosition();
                    if (speakerPos.elevation == 0.f)
                        if (fabsf(speakerPos.azimuth - DegreesToRadians(azimuth51[iSpeaker])) < 1e-6)
                            speakerMatchCount++;
                }
                if (speakerMatchCount == 6)
                    m_nSpeakerSetUp = Amblib_SpeakerSetUps::kAmblib_51;
                break;
            case 7:
                for (int iSpeaker = 0; iSpeaker < 7; ++iSpeaker)
                {
                    PolarPosition<float> speakerPos = m_pAmbSpeakers[iSpeaker].GetPosition();
                    if (speakerPos.elevation == 0.f)
                        if (fabsf(speakerPos.azimuth - DegreesToRadians(azimuth70[iSpeaker])) < 1e-6)
                            speakerMatchCount++;
                }
                if (speakerMatchCount == 7)
                    m_nSpeakerSetUp = Amblib_SpeakerSetUps::kAmblib_70;
                break;
            case 8:
                for (int iSpeaker = 0; iSpeaker < 8; ++iSpeaker)
                {
                    PolarPosition<float> speakerPos = m_pAmbSpeakers[iSpeaker].GetPosition();
                    if (speakerPos.elevation == 0.f)
                        if (fabsf(speakerPos.azimuth - DegreesToRadians(azimuth71[iSpeaker])) < 1e-6)
                            speakerMatchCount++;
                }
                if (speakerMatchCount == 8)
                    m_nSpeakerSetUp = Amblib_SpeakerSetUps::kAmblib_71;
                break;
            default:
                break;
            }
        }
    }

    void AmbisonicDecoder::LoadDecoderPreset()
    {
        // If one of the layouts with a preset available has been selected then load it
        int nAmbiComponents = OrderToComponents(m_nOrder, m_b3D);
        switch (m_nSpeakerSetUp)
        {
        case Amblib_SpeakerSetUps::kAmblib_Mono:
            // Use the coefficients set based on the speaker position.
            // Preset loaded
            m_bPresetLoaded = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_Stereo:
            // Load the stereo decoder preset
            for (int iSpeaker = 0; iSpeaker < 2; ++iSpeaker)
                for (int iCoeff = 0; iCoeff < nAmbiComponents; ++iCoeff)
                {
                    m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_stereo[iSpeaker][iCoeff]);
                }
            // Preset loaded
            m_bPresetLoaded = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_50:
            // Load the 5.0 decoder preset
            for (int iSpeaker = 0; iSpeaker < 5; ++iSpeaker)
                for (int iCoeff = 0; iCoeff < nAmbiComponents; ++iCoeff)
                {
                    if (m_nOrder == 1)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_first_5_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 2)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_second_5_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 3)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_third_5_1[iSpeaker][iCoeff]);
                }
            // Preset loaded
            m_bPresetLoaded = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_70:
            // Load the 7.0 decoder preset
            for (int iSpeaker = 0; iSpeaker < 7; ++iSpeaker)
                for (int iCoeff = 0; iCoeff < nAmbiComponents; ++iCoeff)
                {
                    if (m_nOrder == 1)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_first_7_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 2)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_second_7_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 3)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_third_7_1[iSpeaker][iCoeff]);
                }
            // Preset loaded
            m_bPresetLoaded = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_51:
            // Load the 5.1 decoder preset
            for (int iSpeaker = 0; iSpeaker < 6; ++iSpeaker)
                for (int iCoeff = 0; iCoeff < nAmbiComponents; ++iCoeff)
                {
                    if (m_nOrder == 1)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_first_5_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 2)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_second_5_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 3)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_third_5_1[iSpeaker][iCoeff]);
                }
            // Preset loaded
            m_bPresetLoaded = true;
            break;
        case Amblib_SpeakerSetUps::kAmblib_71:
            // Load the 7.1 decoder preset
            for (int iSpeaker = 0; iSpeaker < 8; ++iSpeaker)
                for (int iCoeff = 0; iCoeff < nAmbiComponents; ++iCoeff)
                {
                    if (m_nOrder == 1)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_first_7_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 2)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_second_7_1[iSpeaker][iCoeff]);
                    else if (m_nOrder == 3)
                        m_pAmbSpeakers[iSpeaker].SetCoefficient(iCoeff, ambi_decs::decoder_coefficient_third_7_1[iSpeaker][iCoeff]);
                }
            // Preset loaded
            m_bPresetLoaded = true;
            break;
        }
    }

    void AmbisonicDecoder::ConfigureDecoderMatrix()
    {
        if (m_b3D)
        {
            if (m_is2dLayout)
            {
                for (unsigned iSpk = 0; iSpk < m_nSpeakers; ++iSpk)
                    m_pAmbSpeakers[iSpk].SetOrderWeight(1, 2.f);

                auto processingOrder = std::min(m_nOrder, m_maxLayoutOrder);
                auto maxRe2dGains = m_shelfFilters.GetMaxReGains(processingOrder, false);
                m_shelfFilters.Configure(processingOrder, m_b3D, m_nBlockSize, m_sampleRate);
                m_shelfFilters.SetHighFrequencyGains(maxRe2dGains);
                if (processingOrder >= 2) // SN3D to SN2D conversion
                {
                    for (unsigned iSpk = 0; iSpk < m_nSpeakers; ++iSpk)
                        m_pAmbSpeakers[iSpk].SetOrderWeight(2, 2.f / (3.f / 4.f)); // 2/(sqrt(3)/2)^2

                    if (processingOrder == 3)
                    {
                        for (unsigned iSpk = 0; iSpk < m_nSpeakers; ++iSpk)
                            m_pAmbSpeakers[iSpk].SetOrderWeight(3, 2.f / (5.f / 8.f)); // 2/(sqrt(5/8)^2)
                    }
                }

                // Refresh so that the weights are applied to the coefficients
                for (unsigned iSpk = 0; iSpk < m_nSpeakers; ++iSpk)
                    m_pAmbSpeakers[iSpk].Refresh();

                // Set all non-horizontal components to zero
                for (int iSpk = 0; iSpk < (int)m_nSpeakers; ++iSpk)
                    for (int iOrder = 0; iOrder < (int)processingOrder + 1; ++iOrder)
                        for (int iDegree = -iOrder + 1; iDegree < iOrder; ++iDegree)
                            m_pAmbSpeakers[iSpk].SetCoefficient(OrderAndDegreeToComponent(iOrder, iDegree, true), 0.f);
            }
            else
            {
                for (unsigned iSpk = 0; iSpk < m_nSpeakers; ++iSpk)
                    for (unsigned i = 0; i <= m_nOrder; ++i)
                        m_pAmbSpeakers[iSpk].SetOrderWeight(i, 2.f * (float)i + 1.f);
            }
        }
        else
        {
            for (unsigned iSpk = 0; iSpk < m_nSpeakers; ++iSpk)
                for (unsigned i = 0; i <= m_nOrder; ++i)
                    m_pAmbSpeakers[iSpk].SetOrderWeight(i, 2.f);
        }
    }

} // namespace spaudio
