/*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can
 *  be found in the License.html file in the root of the source tree.
 */
 
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_MPEGH3DA_YES)
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#include "MediaInfo/Audio/File_Mpegh3da.h"
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
#include <cmath>
 
using namespace ZenLib;
using namespace std;
//---------------------------------------------------------------------------
 
namespace MediaInfoLib
{
 
//***************************************************************************
// Info
//***************************************************************************
 
extern const size_t Aac_sampling_frequency_Size_Usac; // USAC expands Aac_sampling_frequency[]
extern const int32u Aac_sampling_frequency[];
struct coreSbrFrameLengthIndex_mapping
{
    int8u    sbrRatioIndex;
    int8u    outputFrameLengthDivided256;
};
extern const size_t coreSbrFrameLengthIndex_Mapping_Size;
extern coreSbrFrameLengthIndex_mapping coreSbrFrameLengthIndex_Mapping[];
extern int8u Aac_Channels_Get(int8u ChannelLayout);
extern string Aac_Channels_GetString(int8u ChannelLayout);
extern string Aac_ChannelConfiguration_GetString(int8u ChannelLayout);
extern string Aac_ChannelConfiguration2_GetString(int8u ChannelLayout);
extern string Aac_ChannelLayout_GetString(const Aac_OutputChannel* const OutputChannels, size_t OutputChannels_Size);
extern string Aac_ChannelLayout_GetString(int8u ChannelLayout, bool IsMpegh3da=false);
extern string Aac_ChannelLayout_GetString(const vector<Aac_OutputChannel>& OutputChannels);
extern string Aac_ChannelMode_GetString(int8u ChannelLayout, bool IsMpegh3da=false);
extern string Aac_ChannelMode_GetString(const vector<Aac_OutputChannel>& OutputChannels);
extern string Aac_OutputChannelPosition_GetString(int8u OutputChannelPosition);
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_Profile[]=
{
    "Main",
    "High",
    "LC",
    "BL",
};
static size_t Mpegh3da_Profile_Size=sizeof(Mpegh3da_Profile)/sizeof(const char* const);
extern string Mpegh3da_Profile_Get(int8u mpegh3daProfileLevelIndication)
{
    if (!mpegh3daProfileLevelIndication)
        return string();
    if (mpegh3daProfileLevelIndication>=5*Mpegh3da_Profile_Size)
        return Ztring::ToZtring(mpegh3daProfileLevelIndication).To_UTF8(); // Raw value
    return string(Mpegh3da_Profile[(mpegh3daProfileLevelIndication-1)/5])+"@L"+char('1'+((mpegh3daProfileLevelIndication-1)%5));
}
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_MHASPacketType[]=
{
    "FILLDATA",
    "MPEGH3DACFG",
    "MPEGH3DAFRAME",
    "AUDIOSCENEINFO",
    "",
    "",
    "SYNC",
    "SYNCGAP",
    "MARKER",
    "CRC16",
    "CRC32",
    "DESCRIPTOR",
    "USERINTERACTION",
    "LOUDNESS_DRC",
    "BUFFERINFO",
    "GLOBAL_CRC16",
    "GLOBAL_CRC32",
    "AUDIOTRUNCATION",
    "GENDATA",
};
static const size_t Mpegh3da_MHASPacketType_Size=sizeof(Mpegh3da_MHASPacketType)/sizeof(const char* const);
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_contentKind[]=
{
    "",
    "Complete Main",
    "Dialogue",
    "Music",
    "Effect",
    "Mixed",
    "LFE",
    "Voiceover",
    "Spoken Subtitle",
    "Visually Impaired or Audio Description",
    "Commentary",
    "Hearing Impaired",
    "Emergency",
};
static const size_t Mpegh3da_contentKind_Size=sizeof(Mpegh3da_contentKind)/sizeof(const char* const);
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_groupPresetKind[]=
{
    "",
    "Integrated TV Loudspeaker",
    "High Quality Loudspeaker",
    "Mobile Loudspeakers",
    "Mobile Headphones",
    "Hearing Impaired (light)",
    "Hearing Impaired (heavy)",
    "Visually Impaired or Audio Description",
    "Spoken Subtitles",
    "Loudness or DRC",
};
static const size_t Mpegh3da_groupPresetKind_Size=sizeof(Mpegh3da_groupPresetKind)/sizeof(const char* const);
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_signalGroupType[]=
{
    "Channels",
    "Object",
    "SAOC",
    "HOA",
};
static const size_t Mpegh3da_signalGroupType_Size=sizeof(Mpegh3da_signalGroupType)/sizeof(const char* const);
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_marker_byte[]=
{
    "",
    "Configuration change marker",
    "Random access or Immediate playout marker",
    "Program boundary marker",
};
static const size_t Mpegh3da_marker_byte_Size=sizeof(Mpegh3da_marker_byte)/sizeof(const char* const);
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_usacElementType[4]=
{
    "SCE",
    "CPE",
    "LFE",
    "EXT",
};
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_usacExtElementType[]=
{
    "FILL",
    "MPEGS",
    "SAOC",
    "AUDIOPREROLL",
    "UNI_DRC",
    "OBJ_METADATA",
    "SAOC_3D",
    "HOA",
    "FMT_CNVRTR",
    "MCT",
    "TCC",
    "HOA_ENH_LAYER",
    "HREP",
    "ENHANCED_OBJ_METADATA",
};
static const size_t Mpegh3da_usacExtElementType_Size=sizeof(Mpegh3da_usacExtElementType)/sizeof(const char* const);
 
//---------------------------------------------------------------------------
static const char* const Mpegh3da_usacConfigExtType[]=
{
    "FILL",
    "DOWNMIX",
    "LOUDNESS_INFO",
    "AUDIOSCENE_INFO",
    "HOA_MATRIX",
    "ICG",
    "SIG_GROUP_INFO",
    "COMPATIBLE_PROFILE_LEVEL_SET",
};
static const size_t Mpegh3da_usacConfigExtType_Size=sizeof(Mpegh3da_usacConfigExtType)/sizeof(const char* const);
 
//---------------------------------------------------------------------------
static const size_t Mpegh3da_SpeakerInfo_Size=43;
static const speaker_info Mpegh3da_SpeakerInfo[Mpegh3da_SpeakerInfo_Size]=
{
    {CH_M_L030,  30, false,  0, false, false},
    {CH_M_R030,  30, true ,  0, false, false},
    {CH_M_000 ,   0, false,  0, false, false},
    {CH_LFE   ,   0, false, 15, true ,  true},
    {CH_M_L110, 110, false,  0, false, false},
    {CH_M_R110, 110, true ,  0, false, false},
    {CH_M_L022,  22, false,  0, false, false},
    {CH_M_R022,  22, true ,  0, false, false},
    {CH_M_L135, 135, false,  0, false, false},
    {CH_M_R135, 135, true ,  0, false, false},
    {CH_M_180 , 180, false,  0, false, false},
    {CH_M_LSD , 135, false,  0, false, false},
    {CH_M_RSD , 135, true ,  0, false, false},
    {CH_M_L090,  90, false,  0, false, false},
    {CH_M_R090,  90, true ,  0, false, false},
    {CH_M_L060,  60, false,  0, false, false},
    {CH_M_R060,  60, true ,  0, false, false},
    {CH_U_L030,  30, false, 35, false, false},
    {CH_U_R030,  30, true , 35, false, false},
    {CH_U_000 ,   0, false, 35, false, false},
    {CH_U_L135, 135, false, 35, false, false},
    {CH_U_R135, 135, true , 35, false, false},
    {CH_U_180 , 180, false, 35, false, false},
    {CH_U_L090,  90, false, 35, false, false},
    {CH_U_R090,  90, true , 35, false, false},
    {CH_T_000 ,   0, false, 90, false, false},
    {CH_LFE2  ,  45, false, 15, true ,  true},
    {CH_L_L045,  45, false, 15, true , false},
    {CH_L_R045,  45, true , 15, true , false},
    {CH_L_000 ,   0, false, 15, true , false},
    {CH_U_L110, 110, false, 35, false, false},
    {CH_U_R110, 110, true , 35, false, false},
    {CH_U_L045,  45, false, 35, false, false},
    {CH_U_R045,  45, true , 35, false, false},
    {CH_M_L045,  45, false,  0, false, false},
    {CH_M_R045,  45, true ,  0, false, false},
    {CH_LFE3  ,  45, true , 15, true ,  true},
    {CH_M_LSCR,   2, false,  0, false, false},
    {CH_M_RSCR,   2, true ,  0, false, false},
    {CH_M_LSCH,   1, false,  0, false, false},
    {CH_M_RSCH,   1, true ,  0, false, false},
    {CH_M_L150, 150, false,  0, false, false},
    {CH_M_R150, 150, true ,  0, false, false},
};
 
//***************************************************************************
// Constructor/Destructor
//***************************************************************************
 
//---------------------------------------------------------------------------
File_Mpegh3da::File_Mpegh3da()
:File_Usac()
{
    //Configuration
    #if MEDIAINFO_TRACE
        Trace_Layers_Update(8); //Stream
    #endif //MEDIAINFO_TRACE
 
    //In
    MustParse_mhaC=false;
    MustParse_mpegh3daFrame=false;
 
    //Temp
    audioSceneInfoID=0;
    isMainStream=(int8u)-1;
}
 
//***************************************************************************
// Streams management
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Streams_Fill()
{
    Stream_Prepare(Stream_Audio);
    Fill(Stream_Audio, 0, Audio_Format, "MPEG-H 3D Audio");
    string Format_Profile=Mpegh3da_Profile_Get(mpegh3daProfileLevelIndication);
    for (size_t Pos=0; Pos<mpegh3daCompatibleProfileLevelSet.size(); Pos++)
    {
        Format_Profile+=", ";
        Format_Profile+=Mpegh3da_Profile_Get(mpegh3daCompatibleProfileLevelSet[Pos]);
    }
    Fill(Stream_Audio, 0, Audio_Format_Profile, Format_Profile);
    Fill(Stream_Audio, 0, Audio_SamplingRate, usacSamplingFrequency);
    Fill(Stream_Audio, 0, Audio_SamplesPerFrame, coreSbrFrameLengthIndex_Mapping[coreSbrFrameLengthIndex].outputFrameLengthDivided256<<8);
    if (isMainStream!=(int8u)-1)
        Fill(Stream_Audio, 0, "Type", isMainStream?"Main":"Auxiliary");
    Fill_SetOptions(Stream_Audio, 0, "Type", "N NTY");
    for (set<int32u>::iterator Label=MHASPacketLabels.begin(); Label!=MHASPacketLabels.end(); ++Label)
        Fill(Stream_Audio, 0, "Label", *Label);
    Fill_SetOptions(Stream_Audio, 0, "Label", "N NTY");
    Ztring LabelString=Retrieve(Stream_Audio, 0, "Label");
    const Ztring& Type=Retrieve_Const(Stream_Audio, 0, "Type");
    if (!LabelString.empty() && !Type.empty())
    {
        if (!LabelString.empty())
            LabelString+=__T(' ');
        LabelString+=__T('(')+Type+__T(')');
    }
    if (LabelString.empty())
        Fill(Stream_Audio, 0, "Label/String", LabelString);
    Fill_SetOptions(Stream_Audio, 0, "Label/String", "Y NTN");
    if (audioSceneInfoID)
        Fill(Stream_Audio, 0, "AudioSceneInfoID", audioSceneInfoID);
    Streams_Fill_ChannelLayout(string(), referenceLayout);
    if (!GroupPresets.empty())
    {
        Fill(Stream_Audio, 0, "GroupPresetCount", GroupPresets.size());
        Fill_SetOptions(Stream_Audio, 0, "GroupPresetCount", "N NIY");
    }
    if (!SwitchGroups.empty())
    {
        Fill(Stream_Audio, 0, "SwitchGroupCount", SwitchGroups.size());
        Fill_SetOptions(Stream_Audio, 0, "SwitchGroupCount", "N NIY");
    }
    if (!Groups.empty())
    {
        Fill(Stream_Audio, 0, "GroupCount", Groups.size());
        Fill_SetOptions(Stream_Audio, 0, "GroupCount", "N NIY");
    }
    if (!SignalGroups.empty())
    {
        Fill(Stream_Audio, 0, "SignalGroupCount", SignalGroups.size());
        Fill_SetOptions(Stream_Audio, 0, "SignalGroupCount", "N NIY");
    }
 
    // Filling
    if (!Mpegh3da_drcInstructionsUniDrc_Data[0].empty())
    {
        drcInstructionsUniDrc_Data=Mpegh3da_drcInstructionsUniDrc_Data[0].begin()->second;
        Fill_DRC();
        drcInstructionsUniDrc_Data.clear();
    }
    /*
    if (!Mpegh3da_loudnessInfo_Data[0].empty() && !GroupPresets.empty())
    {
        int8u ID=(int8u)-1;
        for (size_t i=0; i<GroupPresets.size(); i++)
        {
            const group_preset& P=GroupPresets[i];
            if (P.ID<ID)
                ID=P.ID;
            if (Mpegh3da_loudnessInfo_Data[3][ID].Data[0].empty())
            {
                Mpegh3da_loudnessInfo_Data[3][ID].Data[0]=Mpegh3da_loudnessInfo_Data[0].begin()->second.Data[0];
                Mpegh3da_loudnessInfo_Data[0].begin()->second.Data[0].clear();
            }
        }
    }
    */
    bool NoLoudnesConch;
    if (!Mpegh3da_loudnessInfo_Data[0].empty())
    {
        loudnessInfo_Data[0]=Mpegh3da_loudnessInfo_Data[0].begin()->second.Data[0];
        loudnessInfo_Data[1]=Mpegh3da_loudnessInfo_Data[0].begin()->second.Data[1];
        loudnessInfoSet_Present=true;
        Fill_Loudness(NULL);
        loudnessInfo_Data[0].clear();
        loudnessInfo_Data[1].clear();
        NoLoudnesConch=true;
    }
    else
        NoLoudnesConch=false;
 
    for (size_t i=0; i<GroupPresets.size(); i++)
    {
        const group_preset& P=GroupPresets[i];
 
        string Summary;
        if (!P.Description.empty())
            Summary+=P.Description.begin()->second;
        if (Summary.empty())
            Summary="Yes";
 
        string p=string("GroupPreset")+Ztring::ToZtring(i).To_UTF8();
        Fill(Stream_Audio, 0, p.c_str(), Summary);
        Fill(Stream_Audio, 0, (p+" Pos").c_str(), i);
        Fill_SetOptions(Stream_Audio, 0, (p+" Pos").c_str(), "N NIY");
        Fill(Stream_Audio, 0, (p+" ID").c_str(), P.ID);
        for (map<string, string>::const_iterator Desc=P.Description.begin(); Desc!=P.Description.end(); ++Desc)
        {
            Ztring Language=MediaInfoLib::Config.Iso639_1_Get(Ztring().From_UTF8(Desc->first));
            if (Language.empty())
                Language=Ztring().From_UTF8(Desc->first);
            Fill(Stream_Audio, 0, (p+" Title").c_str(), '('+MediaInfoLib::Config.Language_Get_Translate(__T("Language_"), Language).To_UTF8() + ") "+Desc->second);
        }
        if (P.Kind<Mpegh3da_groupPresetKind_Size)
            Fill(Stream_Audio, 0, (p+" Kind").c_str(), Mpegh3da_groupPresetKind[P.Kind]);
        if (!Mpegh3da_drcInstructionsUniDrc_Data[3].empty())
        {
            std::map<int8u, std::map<int16u, drc_info> >::iterator drcInstructionsUniDrc=Mpegh3da_drcInstructionsUniDrc_Data[3].find(P.ID);
            if (drcInstructionsUniDrc!=Mpegh3da_drcInstructionsUniDrc_Data[3].end())
            {
                drcInstructionsUniDrc_Data=drcInstructionsUniDrc->second;
                Fill_DRC(p.c_str());
                drcInstructionsUniDrc_Data.clear();
            }
        }
        if (!Mpegh3da_loudnessInfo_Data[3].empty())
        {
            std::map<int8u, loudness_info_data>::iterator Loudness=Mpegh3da_loudnessInfo_Data[3].find(P.ID);
            if (Loudness!=Mpegh3da_loudnessInfo_Data[3].end())
            {
                loudnessInfo_Data[0]=Loudness->second.Data[0];
                loudnessInfoSet_Present=true;
                Fill_Loudness(p.c_str(), NoLoudnesConch);
                loudnessInfo_Data[0].clear();
            }
        }
        /* Disabled for the moment, need more details about how to show it
        ZtringList IDs;
        if (P.Conditions.size()==1 && P.Conditions[0].ReferenceID==127 && !P.Conditions[0].ConditionOnOff)
        {
            Fill(Stream_Audio, 0, (p+" LinkedTo_Group_Pos").c_str(), "Full user interactivity");
            Fill_SetOptions(Stream_Audio, 0, (p+" LinkedTo_Group_Pos").c_str(), "N NTY");
            Fill(Stream_Audio, 0, (p+" LinkedTo_Group_Pos/String").c_str(), "Full user interactivity");
            Fill_SetOptions(Stream_Audio, 0, (p+" LinkedTo_Group_Pos/String").c_str(), "Y NTN");
        }
        else
        {
            ZtringList GroupPos, GroupNum;
            for (size_t i=0; i<P.Conditions.size(); i++)
            {
                for (size_t j=0; j<Groups.size(); j++)
                    if (Groups[j].ID==P.Conditions[i].ReferenceID)
                    {
                        GroupPos.push_back(Ztring::ToZtring(j));
                        GroupNum.push_back(Ztring::ToZtring(j+1));
                    }
            }
            GroupPos.Separator_Set(0, __T(" + "));
            Fill(Stream_Audio, 0, (p+" LinkedTo_Group_Pos").c_str(), GroupPos.Read());
            Fill_SetOptions(Stream_Audio, 0, (p+" LinkedTo_Group_Pos").c_str(), "N NTY");
            GroupNum.Separator_Set(0, __T(" + "));
            Fill(Stream_Audio, 0, (p+" LinkedTo_Group_Pos/String").c_str(), GroupNum.Read());
            Fill_SetOptions(Stream_Audio, 0, (p+" LinkedTo_Group_Pos/String").c_str(), "Y NTN");
        }
        */
    }
 
    for (size_t i=0; i<SwitchGroups.size(); i++)
    {
        const switch_group& S=SwitchGroups[i];
 
        string Summary;
        if (!S.Description.empty())
            Summary+=S.Description.begin()->second;
        if (Summary.empty())
            Summary="Yes";
 
        string s=string("SwitchGroup")+Ztring::ToZtring(i).To_UTF8();
        Fill(Stream_Audio, 0, s.c_str(), Summary);
        Fill(Stream_Audio, 0, (s+" Pos").c_str(), i);
        Fill_SetOptions(Stream_Audio, 0, (s+" Pos").c_str(), "N NIY");
        Fill(Stream_Audio, 0, (s+" ID").c_str(), S.ID);
        for (map<string, string>::const_iterator Desc=S.Description.begin(); Desc!=S.Description.end(); ++Desc)
        {
            Ztring Language=MediaInfoLib::Config.Iso639_1_Get(Ztring().From_UTF8(Desc->first));
            if (Language.empty())
                Language=Ztring().From_UTF8(Desc->first);
            Fill(Stream_Audio, 0, (s+" Title").c_str(), '('+MediaInfoLib::Config.Language_Get_Translate(__T("Language_"), Language).To_UTF8() + ") "+Desc->second);
        }
        Fill(Stream_Audio, 0, (s+" Allow").c_str(), S.allowOnOff?"Yes":"No");
        if (S.allowOnOff)
            Fill(Stream_Audio, 0, (s+" Default").c_str(), S.defaultOnOff?"Yes":"No");
        Fill(Stream_Audio, 0, (s+" DefaultGroupID").c_str(), S.DefaultGroupID);
 
        ZtringList GroupPos, GroupNum;
        for (size_t i=0; i<S.MemberID.size(); i++)
        {
            for (size_t j=0; j<Groups.size(); j++)
                if (Groups[j].ID==S.MemberID[i])
                {
                    GroupPos.push_back(Ztring::ToZtring(j));
                    GroupNum.push_back(Ztring::ToZtring(j+1));
                }
        }
        GroupPos.Separator_Set(0, __T(" + "));
        Fill(Stream_Audio, 0, (s+" LinkedTo_Group_Pos").c_str(), GroupPos.Read());
        Fill_SetOptions(Stream_Audio, 0, (s+" LinkedTo_Group_Pos").c_str(), "N NTY");
        GroupNum.Separator_Set(0, __T(" + "));
        Fill(Stream_Audio, 0, (s+" LinkedTo_Group_Pos/String").c_str(), GroupNum.Read());
        Fill_SetOptions(Stream_Audio, 0, (s+" LinkedTo_Group_Pos/String").c_str(), "Y NTN");
    }
 
    for (size_t i=0; i<Groups.size(); i++)
    {
        const group& G=Groups[i];
 
        string Summary;
        if (!G.Description.empty())
            Summary+=G.Description.begin()->second;
        if (Summary.empty())
            Summary="Yes";
 
        string g=string("Group")+Ztring::ToZtring(i).To_UTF8();
        Fill(Stream_Audio, 0, g.c_str(), Summary);
        Fill(Stream_Audio, 0, (g+" Pos").c_str(), i);
        Fill_SetOptions(Stream_Audio, 0, (g+" Pos").c_str(), "N NIY");
        Fill(Stream_Audio, 0, (g+" ID").c_str(), G.ID);
        for (map<string, string>::const_iterator Desc=G.Description.begin(); Desc!=G.Description.end(); ++Desc)
        {
             Ztring Language=MediaInfoLib::Config.Iso639_1_Get(Ztring().From_UTF8(Desc->first));
            if (Language.empty())
                Language=Ztring().From_UTF8(Desc->first);
            Fill(Stream_Audio, 0, (g+" Title").c_str(), '('+MediaInfoLib::Config.Language_Get_Translate(__T("Language_"), Language).To_UTF8() + ") "+Desc->second);
        }
        if (!G.Language.empty())
        {
            Fill(Stream_Audio, 0, (g+" Language").c_str(), G.Language);
            Fill(Stream_Audio, 0, (g+" Language/String").c_str(), MediaInfoLib::Config.Iso639_Translate(Ztring().From_UTF8(G.Language)));
            Fill_SetOptions(Stream_Audio, 0, (g+" Language").c_str(), "N NTY");
            Fill_SetOptions(Stream_Audio, 0, (g+" Language/String").c_str(), "Y NTN");
        }
        if (G.Kind<Mpegh3da_contentKind_Size)
            Fill(Stream_Audio, 0, (g+" Kind").c_str(), Mpegh3da_contentKind[G.Kind]);
        Fill(Stream_Audio, 0, (g+" Allow").c_str(), G.allowOnOff?"Yes":"No");
        if (G.allowOnOff)
            Fill(Stream_Audio, 0, (g+" Default").c_str(), G.defaultOnOff?"Yes":"No");
        if (!Mpegh3da_drcInstructionsUniDrc_Data[1].empty())
        {
            std::map<int8u, std::map<int16u, drc_info> >::iterator drcInstructionsUniDrc=Mpegh3da_drcInstructionsUniDrc_Data[1].find(G.ID);
            if (drcInstructionsUniDrc!=Mpegh3da_drcInstructionsUniDrc_Data[1].end())
            {
                drcInstructionsUniDrc_Data=drcInstructionsUniDrc->second;
                Fill_DRC(g.c_str());
                drcInstructionsUniDrc_Data.clear();
            }
        }
        if (!Mpegh3da_loudnessInfo_Data[1].empty())
        {
            std::map<int8u, loudness_info_data>::iterator Loudness=Mpegh3da_loudnessInfo_Data[1].find(G.ID);
            if (Loudness!=Mpegh3da_loudnessInfo_Data[1].end())
            {
                loudnessInfo_Data[0]=Loudness->second.Data[0];
                loudnessInfoSet_Present=true;
                Fill_Loudness(g.c_str(), NoLoudnesConch);
                loudnessInfo_Data[0].clear();
            }
        }
        if (!Mpegh3da_drcInstructionsUniDrc_Data[2].empty()) // Not sure
        {
            std::map<int8u, std::map<int16u, drc_info> >::iterator drcInstructionsUniDrc=Mpegh3da_drcInstructionsUniDrc_Data[2].find(G.ID);
            if (drcInstructionsUniDrc!=Mpegh3da_drcInstructionsUniDrc_Data[2].end())
            {
                drcInstructionsUniDrc_Data=drcInstructionsUniDrc->second;
                Fill_DRC(g.c_str());
                drcInstructionsUniDrc_Data.clear();
            }
        }
        if (!Mpegh3da_loudnessInfo_Data[2].empty()) // Not sure
        {
            std::map<int8u, loudness_info_data>::iterator Loudness=Mpegh3da_loudnessInfo_Data[2].find(G.ID);
            if (Loudness!=Mpegh3da_loudnessInfo_Data[2].end())
            {
                loudnessInfo_Data[0]=Loudness->second.Data[0];
                loudnessInfoSet_Present=true;
                Fill_Loudness(g.c_str(), NoLoudnesConch);
                loudnessInfo_Data[0].clear();
            }
        }
 
        ZtringList GroupPos, GroupNum;
        for (size_t i=0; i<G.MemberID.size(); i++)
        {
            size_t Signal_Count=0;
            for (size_t j=0; j<SignalGroups.size(); j++)
            {
                const signal_group& E=SignalGroups[j];
                if (Signal_Count==G.MemberID[i])
                {
                    GroupPos.push_back(Ztring::ToZtring(j));
                    GroupNum.push_back(Ztring::ToZtring(j+1));
                }
                if (Aac_Channels_Get(E.Layout.ChannelLayout))
                    Signal_Count+=Aac_Channels_Get(E.Layout.ChannelLayout);
                else if (E.Layout.numSpeakers)
                    Signal_Count+=E.Layout.numSpeakers;
            }
        }
        GroupPos.Separator_Set(0, __T(" + "));
        Fill(Stream_Audio, 0, (g+" LinkedTo_SignalGroup_Pos").c_str(), GroupPos.Read());
        Fill_SetOptions(Stream_Audio, 0, (g+" LinkedTo_SignalGroup_Pos").c_str(), "N NTY");
        GroupNum.Separator_Set(0, __T(" + "));
        Fill(Stream_Audio, 0, (g+" LinkedTo_SignalGroup_Pos/String").c_str(), GroupNum.Read());
        Fill_SetOptions(Stream_Audio, 0, (g+" LinkedTo_SignalGroup_Pos/String").c_str(), "Y NTN");
    }
 
    for (size_t i=0; i<SignalGroups.size(); i++)
    {
        const signal_group& E=SignalGroups[i];
 
        string e=string("SignalGroup")+Ztring::ToZtring(i).To_UTF8();
        Fill(Stream_Audio, 0, e.c_str(), "Yes");
        Fill(Stream_Audio, 0, (e+" Pos").c_str(), i);
        Fill_SetOptions(Stream_Audio, 0, (e+" Pos").c_str(), "N NIY");
        if (E.Type<Mpegh3da_signalGroupType_Size)
            Fill(Stream_Audio, 0, (e+" Type").c_str(), Mpegh3da_signalGroupType[E.Type]);
        Streams_Fill_ChannelLayout(e+' ', E.Layout, E.Type);
 
        string Summary;
        switch (E.Type)
        {
            case 0 : //Channels
                    Summary+=Retrieve_Const(Stream_Audio, 0, (e+" Channel(s)/String").c_str()).To_UTF8();
                    break;
            case 1 : // Objects
                    Summary+=Retrieve_Const(Stream_Audio, 0, (e+" NumberOfObjects/String").c_str()).To_UTF8();
                    break;
            default:
                    Summary+=Mpegh3da_signalGroupType[E.Type];
        }
        if (!Summary.empty())
            Fill(Stream_Audio, 0, e.c_str(), Summary, true, true);
    }
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Streams_Finish()
{
}
 
//***************************************************************************
// Buffer - Global
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Read_Buffer_Continue()
{
    if (MustParse_mhaC)
    {
        mhaC();
        MustParse_mhaC=false;
        MustParse_mpegh3daFrame=true;
        Skip_XX(Element_Size-Element_Offset,                    "Unknown");
        return;
    }
    if (MustParse_mpegh3daFrame)
    {
        mpegh3daFrame();
    }
}
 
//***************************************************************************
// Buffer - Per element
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Header_Parse()
{
    //Parsing
    int32u MHASPacketType, MHASPacketLabel, MHASPacketLength;
    BS_Begin();
    escapedValue(MHASPacketType, 3, 8, 8,                       "MHASPacketType");
    escapedValue(MHASPacketLabel, 2, 8, 32,                     "MHASPacketLabel");
    escapedValue(MHASPacketLength, 11, 24, 24,                  "MHASPacketLength");
    BS_End();
 
    FILLING_BEGIN();
        if (MHASPacketLabel)
            MHASPacketLabels.insert(MHASPacketLabel);
        Header_Fill_Code(MHASPacketType, MHASPacketType<Mpegh3da_MHASPacketType_Size?Ztring().From_UTF8(Mpegh3da_MHASPacketType[MHASPacketType]):Ztring().From_CC3(MHASPacketType));
        Header_Fill_Size(Element_Offset+MHASPacketLength);
    FILLING_END();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Data_Parse()
{
    //Parsing
    switch (Element_Code)
    {
        case  1 : mpegh3daConfig(); break;
        case  2 : mpegh3daFrame(); break;
        case  3 : BS_Begin(); mae_AudioSceneInfo(); BS_End(); break;
        case  6 : Sync(); break;
        case  8 : Marker(); break;
        case  9 : Crc16(); break;
        case 14 : BufferInfo(); break;
        case 17 : audioTruncationInfo(); break;
        default : Skip_XX(Element_Size-Element_Offset,          "Data");
    }
 
    if (!Trusted_Get())
        Fill(Stream_Audio, 0, "NOK", "NOK", Unlimited, true, true);
}
 
//***************************************************************************
// Elements
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daConfig()
{
    Element_Begin1("mpegh3daConfig");
    BS_Begin();
    int8u usacSamplingFrequencyIndex;
    Get_S1(8, mpegh3daProfileLevelIndication, "mpegh3daProfileLevelIndication"); Param_Info1(Mpegh3da_Profile_Get(mpegh3daProfileLevelIndication));
    Get_S1 (5, usacSamplingFrequencyIndex,                      "usacSamplingFrequencyIndex");
    if (usacSamplingFrequencyIndex==0x1f)
        Get_S3 (24, usacSamplingFrequency,                      "usacSamplingFrequency");
    else
    {
        if (usacSamplingFrequencyIndex<Aac_sampling_frequency_Size_Usac)
            usacSamplingFrequency=Aac_sampling_frequency[usacSamplingFrequencyIndex];
        else
            usacSamplingFrequency=0;
    }
    Get_S1 (3, coreSbrFrameLengthIndex,                         "coreSbrFrameLengthIndex");
    Skip_SB(                                                    "cfg_reserved");
    Skip_SB(                                                    "receiverDelayCompensation");
    SpeakerConfig3d(referenceLayout);
    FrameworkConfig3d();
    mpegh3daDecoderConfig();
    TEST_SB_SKIP(                                               "usacConfigExtensionPresent");
        mpegh3daConfigExtension();
    TEST_SB_END();
    BS_End();
    Element_End0();
 
    FILLING_BEGIN();
        //Filling
        if (!Status[IsAccepted])
            Accept("MPEG-H 3D Audio");
    FILLING_END();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::SpeakerConfig3d(speaker_layout& Layout)
{
    int8u speakerLayoutType;
    Element_Begin1("SpeakerConfig3d");
    Get_S1(2, speakerLayoutType,                                "speakerLayoutType");
    if (speakerLayoutType==0)
    {
        Get_S1 (6, Layout.ChannelLayout,                        "CICPspeakerLayoutIdx"); Param_Info2(Aac_Channels_Get(Layout.ChannelLayout), " channels");
    }
    else
    {
        int32u numSpeakers;
        escapedValue(numSpeakers, 5, 8, 16,                     "numSpeakers");
        numSpeakers++;
        Layout.numSpeakers=numSpeakers;
 
        if (speakerLayoutType==1)
        {
            Layout.CICPspeakerIdxs.resize(numSpeakers);
            for (size_t Pos=0; Pos<numSpeakers; Pos++)
            {
                int8u CICPspeakerIdx;
                Get_S1(7, CICPspeakerIdx,                       "CICPspeakerIdx");
                Layout.CICPspeakerIdxs[Pos]=(Aac_OutputChannel)CICPspeakerIdx;
            }
        }
        else if (speakerLayoutType==2)
        {
            mpegh3daFlexibleSpeakerConfig(Layout);
        }
    }
    Element_End0();
 
    FILLING_BEGIN();
        //Finish
        if (Status[IsAccepted])
            Finish("MPEG-H 3D Audio");
    FILLING_END();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daFlexibleSpeakerConfig(speaker_layout& Layout)
{
    bool angularPrecision;
    Element_Begin1("mpegh3daFlexibleSpeakerConfig");
    Get_SB(angularPrecision,                                    "angularPrecision");
    for (size_t Pos=0; Pos<Layout.numSpeakers; Pos++)
    {
        Layout.SpeakersInfo.push_back(speaker_info());
        speaker_info& SpeakerInfo=Layout.SpeakersInfo[Layout.SpeakersInfo.size()-1];
        mpegh3daSpeakerDescription(SpeakerInfo, angularPrecision);
        if (SpeakerInfo.AzimuthAngle && SpeakerInfo.AzimuthAngle!=180)
        {
            bool alsoAddSymmetricPair;
            Get_SB (alsoAddSymmetricPair,                       "alsoAddSymmetricPair");
            if (alsoAddSymmetricPair)
                Pos++;
        }
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daSpeakerDescription(speaker_info& SpeakerInfo, bool angularPrecision)
{
    Element_Begin1("mpegh3daSpeakerDescription");
    TESTELSE_SB_SKIP(                                           "isCICPspeakerIdx");
    {
        int8u CICPspeakerIdx;
        Get_S1 (7, CICPspeakerIdx,                              "CICPspeakerIdx");
        if (CICPspeakerIdx<Mpegh3da_SpeakerInfo_Size)
            SpeakerInfo=Mpegh3da_SpeakerInfo[CICPspeakerIdx];
        else
            SpeakerInfo.CICPspeakerIdx=(Aac_OutputChannel)CICPspeakerIdx;
    }
    TESTELSE_SB_ELSE(                                           "isCICPspeakerIdx");
        int8u ElevationClass;
        Get_S1(2, ElevationClass,                               "ElevationClass");
 
        switch (ElevationClass)
        {
        case 0:
            SpeakerInfo.ElevationAngle=0;
            break;
        case 1:
            SpeakerInfo.ElevationAngle=35;
            SpeakerInfo.ElevationDirection=false;
            break;
        case 2:
            SpeakerInfo.ElevationAngle=15;
            SpeakerInfo.ElevationDirection=true;
            break;
        case 3:
            int8u ElevationAngleIdx;
            Get_S1(angularPrecision?7:5, ElevationAngleIdx, "ElevationAngleIdx");
            SpeakerInfo.ElevationAngle=ElevationAngleIdx*(angularPrecision?1:5);
 
            if (SpeakerInfo.ElevationAngle)
                Get_SB(SpeakerInfo.ElevationDirection,         "ElevationDirection");
            break;
        }
 
        int8u AzimuthAngleIdx;
        Get_S1(angularPrecision?8:6, AzimuthAngleIdx, "AzimuthAngleIdx");
        SpeakerInfo.AzimuthAngle=AzimuthAngleIdx*(angularPrecision?1:5);
 
        if (SpeakerInfo.AzimuthAngle && SpeakerInfo.AzimuthAngle!=180)
            Get_SB(SpeakerInfo.AzimuthDirection, "AzimuthDirection");
 
        Get_SB(SpeakerInfo.isLFE,                "isLFE");
 
        SpeakerInfo.CICPspeakerIdx=(Aac_OutputChannel)-1;
    TESTELSE_SB_END();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daFrame()
{
    Skip_XX(Element_Size,                                       "mpegh3daFrame");
 
    FILLING_BEGIN();
        //Filling
        if (Status[IsAccepted])
            Finish("MPEG-H 3D Audio");
    FILLING_END();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Sync()
{
    //Parsing
    Skip_B1(                                                    "syncword");
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Marker()
{
    //Parsing
    Info_B1(marker_byte,                                        "marker_byte"); Param_Info1C(marker_byte<Mpegh3da_marker_byte_Size, Mpegh3da_marker_byte[marker_byte]);
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Crc16()
{
    //Parsing
    Skip_B2(                                                    "mhasParity16Data");
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::BufferInfo()
{
    //Parsing
    BS_Begin();
    bool mhas_buffer_fullness_present;
    Get_SB (mhas_buffer_fullness_present,                       "mhas_buffer_fullness_present");
    if (mhas_buffer_fullness_present)
    {
        int32u mhas_buffer_fullness;
        escapedValue(mhas_buffer_fullness, 15, 39, 71,          "mhas_buffer_fullness");
    }
    BS_End();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::FrameworkConfig3d()
{
    numAudioChannels=0;
    numAudioObjects=0;
    numSAOCTransportChannels=0;
    numHOATransportChannels=0;
 
    Element_Begin1("FrameworkConfig3d");
    Element_Begin1("Signals3d");
    Get_S1(5, bsNumSignalGroups,                                "bsNumSignalGroups");
    bsNumSignalGroups++; Param_Info2(bsNumSignalGroups, " signals");
    SignalGroups.resize(bsNumSignalGroups);
    for (int8u Pos=0; Pos<bsNumSignalGroups; Pos++)
    {
        signal_group& E=SignalGroups[Pos];
        Element_Begin1("signalGroup");
        Get_S1(3, E.Type,                                       "signalGroupType");
        escapedValue(E.bsNumberOfSignals, 5, 8, 16,             "bsNumberOfSignals");
        E.bsNumberOfSignals++;
 
        if (E.Type==SignalGroupTypeChannels)
        {
            numAudioChannels+=E.bsNumberOfSignals;
            TESTELSE_SB_SKIP(                                   "differsFromReferenceLayout");
                SpeakerConfig3d(E.Layout);
            TESTELSE_SB_ELSE(                                   "differsFromReferenceLayout");
                E.Layout=referenceLayout;
            TESTELSE_SB_END();
        }
        else if (E.Type==SignalGroupTypeObject)
        {
            numAudioObjects+=E.bsNumberOfSignals;
            E.Layout.numSpeakers=E.bsNumberOfSignals;
        }
        else if (E.Type==SignalGroupTypeSAOC)
        {
            numSAOCTransportChannels+=E.bsNumberOfSignals;
            TEST_SB_SKIP(                                       "saocDmxLayoutPresent");
                SpeakerConfig3d(E.Layout);
            TEST_SB_END();
        }
        else if (E.Type==SignalGroupTypeHOA)
        {
            numHOATransportChannels+=E.bsNumberOfSignals;
            E.Layout.numSpeakers=E.bsNumberOfSignals;
        }
        Element_End0();
    }
    Element_End0();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daDecoderConfig()
{
    Elements.clear();
 
    Element_Begin1("mpegh3daDecoderConfig");
    escapedValue(numElements, 4, 8, 16,                         "numElements");
    numElements++;
 
    bool elementLengthPresent;
    Get_SB (elementLengthPresent,                               "elementLengthPresent");
 
    for (size_t Pos=0; Pos<numElements; Pos++)
    {
        Element_Begin1("Element");
        int8u usacElementType;
        Get_S1(2, usacElementType,                              "usacElementType"); Element_Info1(Mpegh3da_usacElementType[usacElementType]);
        switch (usacElementType)
        {
        case ID_USAC_SCE:
            mpegh3daSingleChannelElementConfig(coreSbrFrameLengthIndex_Mapping[coreSbrFrameLengthIndex].sbrRatioIndex);
            Elements.push_back(usac_element(ID_USAC_SCE));
            break;
        case ID_USAC_CPE:
            mpegh3daChannelPairElementConfig(coreSbrFrameLengthIndex_Mapping[coreSbrFrameLengthIndex].sbrRatioIndex);
            Elements.push_back(usac_element(ID_USAC_CPE));
            break;
        case ID_USAC_LFE:
            Elements.push_back(usac_element(ID_USAC_LFE));
            break;
        case ID_USAC_EXT:
            mpegh3daExtElementConfig();
            Elements.push_back(usac_element(ID_USAC_EXT));
            break;
        }
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daSingleChannelElementConfig(int8u sbrRatioIndex)
{
    Element_Begin1("mpegh3daSingleChannelElementConfig");
    mpegh3daCoreConfig();
    if (sbrRatioIndex)
        SbrConfig();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daChannelPairElementConfig(int8u sbrRatioIndex)
{
    int32u nBits=floor(log2(numAudioChannels+numAudioObjects+numHOATransportChannels+numSAOCTransportChannels-1))+1;
    int8u stereoConfigIndex=0;
    int8u qceIndex;
    Element_Begin1("mpegh3daChannelPairElementConfig");
    bool enhancedNoiseFilling=mpegh3daCoreConfig();
    if(enhancedNoiseFilling)
        Skip_SB(                                                "igfIndependentTiling");
 
    if (sbrRatioIndex)
    {
        SbrConfig();
        Get_S1(2, stereoConfigIndex,                            "stereoConfigIndex");
    }
 
    if (stereoConfigIndex) {
        Mps212Config(stereoConfigIndex);
    }
 
    Get_S1(2, qceIndex,                                         "qceIndex");
    if (qceIndex)
    {
        TEST_SB_SKIP(                                           "shiftIndex0");
            Skip_BS(nBits,                                      "shiftChannel0");
        TEST_SB_END();
    }
 
    TEST_SB_SKIP(                                               "shiftIndex1");
        Skip_BS(nBits,                                          "shiftChannel1");
    TEST_SB_END();
 
    if (sbrRatioIndex==0 && qceIndex==0)
        Skip_SB(                                                "lpdStereoIndex");
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daExtElementConfig()
{
    Element_Begin1("mpegh3daExtElementConfig");
    int32u usacExtElementType;
    escapedValue(usacExtElementType, 4, 8, 16,                  "usacExtElementType"); Element_Level--; Element_Info1C(usacExtElementType<Mpegh3da_usacExtElementType_Size, Mpegh3da_usacExtElementType[usacExtElementType]); Element_Level++;
 
    int32u usacExtElementConfigLength;
    escapedValue(usacExtElementConfigLength, 4, 8, 16,          "usacExtElementConfigLength");
 
    int32u usacExtElementDefaultLength=0;
    TEST_SB_SKIP(                                               "usacExtElementDefaultLengthPresent");
        escapedValue(usacExtElementDefaultLength, 8, 16, 0,     "usacExtElementDefaultLength"); //TODO: check if call is valid
        usacExtElementDefaultLength++;
    TEST_SB_END();
 
    Skip_SB(                                                    "usacExtElementPayloadFrag");
 
    size_t Remain_Before=BS->Remain();
    switch (usacExtElementType)
    {
    case ID_EXT_ELE_FILL:
        break; // No configuration element
    //case ID_EXT_ELE_MPEGS:
        //TODO: SpatialSpecificConfig();
        //break;
    //case ID_EXT_ELE_SAOC:
        //TODO: SAOCSpecificConfig();
        //break;
    case ID_EXT_ELE_AUDIOPREROLL:
        break; // No configuration element
    case ID_EXT_ELE_UNI_DRC:
        //if (referenceLayout.ChannelLayout!=19) //TEMP
            mpegh3daUniDrcConfig();
        break;
    case ID_EXT_ELE_OBJ_METADATA:
        ObjectMetadataConfig();
        break;
    //case ID_EXT_ELE_SAOC_3D:
    //    SAOC3DSpecificConfig();
    //    break;
    //case ID_EXT_ELE_HOA:
        //HOAConfig();
        //break;
    case ID_EXT_ELE_FMT_CNVRTR:
        break; // No configuration element
    //case ID_EXT_ELE_MCT:
        //MCTConfig();
        //break;
    case ID_EXT_ELE_TCC:
        TccConfig();
        break;
    //case ID_EXT_ELE_HOA_ENH_LAYER:
        //HOAEnhConfig();
        //break;
    //case ID_EXT_ELE_HREP:
        //HREPConfig(current_signal_group);
        //break;
    //case ID_EXT_ELE_ENHANCED_OBJ_METADATA:
        //EnhancedObjectMetadataConfig();
        //break;
        break;
    default:
        if (usacExtElementConfigLength)
            Skip_BS(usacExtElementConfigLength*8,               "reserved");
        break;
    }
    if (BS->Remain()+usacExtElementConfigLength*8>Remain_Before)
    {
        size_t Size=BS->Remain()+usacExtElementConfigLength*8-Remain_Before;
        int8u Padding=1;
        if (Size<8)
            Peek_S1((int8u)Size, Padding);
 
        if (Padding && Remain_Before!=BS->Remain() && usacExtElementType!=ID_EXT_ELE_OBJ_METADATA)
            Fill(Stream_Audio, 0, "NOK", "NOK", Unlimited, true, true);
        Skip_BS(Size, Padding?"(Unknown)":"Padding");
    }
 
    Element_End0();
}
 
//---------------------------------------------------------------------------
bool File_Mpegh3da::mpegh3daCoreConfig()
{
    bool enhancedNoiseFilling;
    Element_Begin1("mpegh3daCoreConfig");
    Skip_SB(                                                    "tw_mdct");
    Skip_SB(                                                    "fullbandLpd");
    Skip_SB(                                                    "noiseFilling");
    TEST_SB_GET(enhancedNoiseFilling,                           "enhancedNoiseFilling");
        Skip_SB(                                                "igfUseEnf");
        Skip_SB(                                                "igfUseHighRes");
        Skip_SB(                                                "igfUseWhitening");
        Skip_SB(                                                "igfAfterTnsSynth");
        Skip_S1(5,                                              "igfStartIndex");
        Skip_S1(4,                                              "igfStopIndex");
    TEST_SB_END();
    Element_End0();
 
    return enhancedNoiseFilling;
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daUniDrcConfig()
{
    Element_Begin1("mpegh3daUniDrcConfig");
    int8u drcCoefficientsUniDrcCount;
    Get_S1(3, drcCoefficientsUniDrcCount,                       "drcCoefficientsUniDrcCount");
 
    int8u drcInstructionsUniDrcCount;
    Get_S1(6, drcInstructionsUniDrcCount,                       "drcInstructionsUniDrcCount");
 
    Element_Begin1("mpegh3daUniDrcChannelLayout");
    Get_S1 (7, baseChannelCount,                                "baseChannelCount");
    Element_End0();
    if (!drcCoefficientsUniDrcCount)
        Fill(Stream_Audio, 0, "TEMP_drcCoefficientsUniDrcCount", drcCoefficientsUniDrcCount); //TEMP
 
    for (int8u Pos=0; Pos<drcCoefficientsUniDrcCount; Pos++)
        drcCoefficientsUniDrc(); // in File_USAC.cpp
 
    for (int8u Pos=0; Pos<drcInstructionsUniDrcCount; Pos++)
    {
        int8u drcInstructionsType;
        Get_S1(Peek_SB()?2:1, drcInstructionsType,              "drcInstructionsType");
 
        int8u ID;
        if (drcInstructionsType==2)
            Get_S1 (7, ID,                                      "mae_groupID");
        else if (drcInstructionsType==3)
            Get_S1 (5, ID,                                      "mae_groupPresetID");
        else
            ID=0;
 
        drcInstructionsUniDrc(false, true); // in File_USAC.cpp
        Mpegh3da_drcInstructionsUniDrc_Data[drcInstructionsType][ID][drcInstructionsUniDrc_Data.begin()->first]=drcInstructionsUniDrc_Data.begin()->second;
        drcInstructionsUniDrc_Data.clear();
    }
 
    TEST_SB_SKIP(                                               "uniDrcConfigExtPresent");
        uniDrcConfigExtension(); // in File_USAC.cpp
    TEST_SB_END();
 
    TEST_SB_SKIP(                                               "loudnessInfoSetPresent");
        mpegh3daLoudnessInfoSet();
    TEST_SB_END();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::downmixConfig()
{
    Element_Begin1("downmixConfig");
    int8u downmixConfigType;
    Get_S1 (2, downmixConfigType,                                "downmixConfigType");
    switch (downmixConfigType)
    {
        case 0:
        case 2:
        {
            bool passiveDownmixFlag;
            Get_SB (passiveDownmixFlag,                         "passiveDownmixFlag");
            if (!passiveDownmixFlag)
                Skip_S1(3,                                      "phaseAlignStrength");
            Skip_SB(                                            "immersiveDownmixFlag");
        }
        break;
    }
    switch (downmixConfigType)
    {
        case 1:
        case 2:
        {
            Skip_S1(5,                                           "DownmixMatrixSet - TODO");
        }
        break;
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daLoudnessInfoSet()
{
    Element_Begin1("mpegh3daLoudnessInfoSet");
    int8u loudnessInfoCount;
    Get_S1(6, loudnessInfoCount,                                "loudnessInfoCount");
    for(int8u Pos=0; Pos<loudnessInfoCount; Pos++)
    {
        int8u loudnessInfoType;
        Get_S1(2, loudnessInfoType,                             "loudnessInfoType");
 
        int8u ID;
        if (loudnessInfoType==1 || loudnessInfoType==2)
            Get_S1 (7, ID,                                      "mae_groupID");
        else if (loudnessInfoType==3)
            Get_S1 (5, ID,                                      "mae_groupPresetID");
        else
            ID=0;
 
        bool IsNOK=loudnessInfo(false); // in File_USAC.cpp
        Mpegh3da_loudnessInfo_Data[loudnessInfoType][ID].Data[0][loudnessInfo_Data[0].begin()->first]=loudnessInfo_Data[0].begin()->second;
        loudnessInfo_Data[0].clear();
        if (IsNOK)
        {
            Element_End0();
            return;
        }
    }
 
    TEST_SB_SKIP(                                               "loudnessInfoAlbumPresent");
        int8u loudnessInfoAlbumCount;
        Get_S1(6, loudnessInfoAlbumCount,                       "loudnessInfoAlbumCount");
        for (int8u Pos=0; Pos<loudnessInfoAlbumCount; Pos++)
        {
            loudnessInfo(true); // in File_USAC.cpp
            Mpegh3da_loudnessInfo_Data[0][0].Data[1][loudnessInfo_Data[1].begin()->first]=loudnessInfo_Data[1].begin()->second;
            loudnessInfo_Data[1].clear();
        }
    TEST_SB_END();
 
    TEST_SB_SKIP(                                               "loudnessInfoSetExtensionPresent");
        loudnessInfoSetExtension(); // in File_USAC.cpp
    TEST_SB_END();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::ObjectMetadataConfig()
{
    Element_Begin1("ObjectMetadataConfig");
    Skip_SB(                                                    "lowDelayMetadataCoding");
    TESTELSE_SB_SKIP(                                           "hasCoreLength");
    TESTELSE_SB_ELSE(                                           "hasCoreLength");
        Skip_S1(6,                                              "frameLength");
    TESTELSE_SB_END();
 
    TEST_SB_SKIP("hasScreenRelativeObjects");
        size_t num_objects=num_objects_Get();
        for (int16u Pos=0; Pos<num_objects; Pos++)
        {
            Skip_SB(                                            "isScreenRelativeObject");
        }
    TEST_SB_END();
    Skip_SB(                                                    "hasDynamicObjectPriority");
    Skip_SB(                                                    "hasUniformSpread");
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::SAOC3DSpecificConfig()
{
    int8u bsSamplingFrequencyIndex, bsNumSaocDmxChannels, bsNumSaocDmxObjects, bsNumSaocObjects;
    int32u NumSaocChannels=0, NumInputSignals=0;
    Element_Begin1("SAOC3DSpecificConfig");
    Get_S1(4, bsSamplingFrequencyIndex,                         "bsSamplingFrequencyIndex");
    if (bsSamplingFrequencyIndex==15)
        Skip_S3(24,                                             "bsSamplingFrequency");
 
    Skip_S1(3,                                                  "bsFreqRes");
    Skip_SB(                                                    "bsDoubleFrameLengthFlag");
    Get_S1(5, bsNumSaocDmxChannels,                             "bsNumSaocDmxChannels");
    Get_S1(5, bsNumSaocDmxObjects,                              "bsNumSaocDmxObjects");
    Skip_SB(                                                    "bsDecorrelationMethod");
 
    if (bsNumSaocDmxChannels)
    {
        speaker_layout saocChannelLayout;
        SpeakerConfig3d(saocChannelLayout);
        NumSaocChannels=SAOC3DgetNumChannels(saocChannelLayout);
        NumInputSignals+=NumSaocChannels;
    }
 
    Get_S1(8, bsNumSaocObjects ,                                "bsNumSaocObjects");
    NumInputSignals+=bsNumSaocObjects;
 
    for (int8u Pos=0; Pos<NumSaocChannels; Pos++)
    {
        for(int8u Pos2=Pos+1; Pos2<NumSaocChannels; Pos2++)
            Skip_SB(                                            "bsRelatedTo");
    }
 
    for (int8u Pos=NumSaocChannels; Pos<NumInputSignals; Pos++)
    {
        for(int8u Pos2=Pos+1; Pos2<NumInputSignals; Pos2++)
            Skip_SB(                                            "bsRelatedTo");
    }
 
    Skip_SB(                                                    "bsOneIOC");
    TEST_SB_SKIP(                                               "bsSaocDmxMethod");
        SAOC3DgetNumChannels(referenceLayout);
    TEST_SB_END();
 
    TEST_SB_SKIP(                                               "bsDualMode");
        Skip_S1(5,                                              "bsBandsLow");
    TEST_SB_END();
 
    TEST_SB_SKIP(                                               "bsDcuFlag");
        Skip_SB(                                                "bsDcuMandatory");
        TEST_SB_SKIP(                                           "bsDcuDynamic");
            Skip_SB(                                            "bsDcuMode");
            Skip_S1(4,                                          "bsDcuParam");
        TEST_SB_END();
    TEST_SB_END();
    Skip_S1(BS->Remain()%8,                                     "byte_align");
    //TODO: SAOC3DExtensionConfig();
    Element_End0();
}
 
//---------------------------------------------------------------------------
int32u File_Mpegh3da::SAOC3DgetNumChannels(speaker_layout Layout)
{
    int32u ToReturn=Layout.numSpeakers;
 
    for (int32u Pos=0; Pos<Layout.numSpeakers; Pos++)
    {
        if (Layout.SpeakersInfo.size()>Pos && Layout.SpeakersInfo[Pos].isLFE)
            ToReturn--;
    }
 
    return ToReturn;
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::MCTConfig()
{
    Element_Begin1("MCTConfig");
    for(int32u chan=0; chan<numAudioChannels; ++chan) // bsNumberOfSignals[grp] but which grp?
    {
        Skip_SB(                                                "mctChanMask");
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::TccConfig()
{
    Element_Begin1("TccConfig");
    for(int32u Pos=0; Pos<numElements; Pos++)
    {
        if(Elements.size()>Pos && (Elements[Pos].Type==ID_USAC_SCE || Elements[Pos].Type==ID_USAC_CPE))
            Skip_S1(2,                                       "tccMode");
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::EnhancedObjectMetadataConfig()
{
    Element_Begin1("EnhancedObjectMetadataConfig");
    bool hasCommonGroupExcludedSectors=false;
    TEST_SB_SKIP(                                               "hasDiffuseness");
        Skip_SB(                                                "hasCommonGroupDiffuseness");
    TEST_SB_END();
 
    TEST_SB_SKIP(                                               "hasExcludedSectors");
        TEST_SB_GET(hasCommonGroupExcludedSectors,              "hasCommonGroupExcludedSectors");
            Skip_SB(                                            "useOnlyPredefinedSectors");
        TEST_SB_END();
    TEST_SB_END();
 
    TEST_SB_SKIP(                                               "hasClosestSpeakerCondition");
        Skip_S1(7,                                              "closestSpeakerThresholdAngle");
    TEST_SB_END();
 
    size_t num_objects=num_objects_Get();
    for (int8u Pos=0; Pos<num_objects; Pos++)
    {
        TEST_SB_SKIP(                                           "hasDivergence");
            Skip_S1(6,                                          "divergenceAzimuthRange");
        TEST_SB_END();
 
        if (!hasCommonGroupExcludedSectors)
            Skip_SB(                                            "useOnlyPredefinedSectors");
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mpegh3daConfigExtension()
{
    Element_Begin1("mpegh3daConfigExtension");
    int32u numConfigExtensions;
    escapedValue(numConfigExtensions, 2, 4, 8,                  "numConfigExtensions");
    numConfigExtensions++;
 
    for (int32u Pos=0; Pos<numConfigExtensions; Pos++)
    {
        Element_Begin1("configExtension");
        int32u usacConfigExtType;
        escapedValue(usacConfigExtType, 4, 8, 16,               "usacConfigExtType"); Element_Info1C(usacConfigExtType<Mpegh3da_usacConfigExtType_Size, Mpegh3da_usacConfigExtType[usacConfigExtType]);
 
        int32u usacConfigExtLength;
        escapedValue(usacConfigExtLength, 4, 8, 16,             "usacConfigExtLength");
        if (!usacConfigExtLength)
        {
            Element_End0();
            continue;
        }
 
        size_t Remain_Before=BS->Remain();
        switch ((UsacConfigExtType)usacConfigExtType)
        {
        case ID_CONFIG_EXT_FILL:
            while (usacConfigExtLength)
            {
                usacConfigExtLength--;
                Skip_S1(8,                                      "fill_byte"); // should be '10100101'
            }
            break;
        case ID_CONFIG_EXT_DOWNMIX:
            downmixConfig();
            break;
        case ID_CONFIG_EXT_LOUDNESS_INFO:
            mpegh3daLoudnessInfoSet();
            break;
        case ID_CONFIG_EXT_AUDIOSCENE_INFO:
            mae_AudioSceneInfo();
            break;
        //case ID_CONFIG_EXT_HOA_MATRIX:
        //    HoaRenderingMatrixSet();
        //    break;
        case ID_CONFIG_EXT_ICG:
            ICGConfig();
            break;
        case ID_CONFIG_EXT_SIG_GROUP_INFO:
            SignalGroupInformation();
            break;
        case ID_CONFIG_EXT_COMPATIBLE_PROFILE_LEVEL_SET:
            CompatibleProfileLevelSet();
            break;
        default:
            Skip_BS(usacConfigExtLength*8,                      "reserved");
        }
        if (BS->Remain()+usacConfigExtLength*8>Remain_Before)
        {
            size_t Size=BS->Remain()+usacConfigExtLength*8-Remain_Before;
            int8u Padding=1;
            if (Size<8)
                Peek_S1((int8u)Size, Padding);
 
            if (Padding && Remain_Before!=BS->Remain() && usacConfigExtType!=ID_CONFIG_EXT_DOWNMIX && usacConfigExtType!=ID_CONFIG_EXT_HOA_MATRIX)
                Fill(Stream_Audio, 0, "NOK", "NOK", Unlimited, true, true);
            Skip_BS(Size, Padding?"(Unknown)":"Padding");
        }
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::SignalGroupInformation()
{
    Element_Begin1("SignalGroupInformation");
    for (int8u Pos=0; Pos<bsNumSignalGroups+1; Pos++)
    {
        Skip_S1(3,                                              "groupPriority");
        Skip_SB(                                                "fixedPosition");
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::CompatibleProfileLevelSet()
{
    Element_Begin1("CompatibleProfileLevelSet");
    int8u bsNumCompatibleSets;
    Get_S1 (4, bsNumCompatibleSets,                             "bsNumCompatibleSets");
    Skip_S1(4,                                                  "reserved");
    mpegh3daCompatibleProfileLevelSet.resize(bsNumCompatibleSets+1);
    for (int8u Pos=0; Pos<=bsNumCompatibleSets; Pos++)
    {
        Get_S1(8, mpegh3daCompatibleProfileLevelSet[Pos], "CompatibleSetIndication"); Param_Info1(Mpegh3da_Profile_Get(mpegh3daCompatibleProfileLevelSet[Pos]));
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::HoaRenderingMatrixSet()
{
    Element_Begin1("HoaRenderingMatrixSet - TODO");
    Skip_S1(5,                                                  "numOfHoaRenderingMatrices");
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::ICGConfig()
{
    Element_Begin1("ICGConfig");
    TEST_SB_SKIP(                                               "ICPresent");
        for (int32u Pos=0; Pos<numElements; Pos++)
        {
            if (Elements.size()>Pos && Elements[Pos].Type==ID_USAC_CPE)
                Skip_SB(                                        "ICinCPE");
        }
 
        TEST_SB_SKIP(                                           "ICGPreAppliedPresent");
            for (int32u Pos=0; Pos<numElements; Pos++)
            {
                if (Elements.size()>Pos && Elements[Pos].Type==ID_USAC_CPE)
                    Skip_SB(                                     "ICGPreAppliedCPE");
            }
        TEST_SB_END();
    TEST_SB_END();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_AudioSceneInfo()
{
    Groups.clear();
    SwitchGroups.clear();
    GroupPresets.clear();
 
    Element_Begin1("mae_AudioSceneInfo");
    bool mae_isMainStream;
    TESTELSE_SB_GET (mae_isMainStream,                          "mae_isMainStream");
        TEST_SB_SKIP(                                           "mae_audioSceneInfoIDPresent");
            Get_S1 (8, audioSceneInfoID,                        "mae_audioSceneInfoID");
        TEST_SB_END();
        int8u mae_numGroups;
        Get_S1(7, mae_numGroups,                                "mae_numGroups");
        mae_GroupDefinition(mae_numGroups);
        int8u mae_numSwitchGroups;
        Get_S1(5, mae_numSwitchGroups,                          "mae_numSwitchGroups");
        mae_SwitchGroupDefinition(mae_numSwitchGroups);
        int8u mae_numGroupPresets;
        Get_S1(5, mae_numGroupPresets,                          "mae_numGroupPresets");
        mae_GroupPresetDefinition(mae_numGroupPresets);
        mae_Data(mae_numGroups, mae_numGroupPresets);
        Skip_S1(7,                                              "mae_metaDataElementIDmaxAvail");
    TESTELSE_SB_ELSE(                                           "mae_isMainStream");
        Skip_S1(7,                                              "mae_bsMetaDataElementIDoffset");
        Skip_S1(7,                                              "mae_metaDataElementIDmaxAvail");
    TESTELSE_SB_END();
    Element_End0();
 
    isMainStream=mae_isMainStream;
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_GroupDefinition(int8u numGroups)
{
    Element_Begin1("mae_GroupDefinition");
    Groups.resize(numGroups);
    for (int8u Pos=0; Pos<numGroups; Pos++)
    {
        Element_Begin1("mae_group");
        group& G=Groups[Pos];
        Get_S1 (7, G.ID,                                        "mae_groupID");
        Element_Info1(Ztring::ToZtring(G.ID));
        Get_SB (G.allowOnOff,                                   "mae_allowOnOff");
        Get_SB (G.defaultOnOff,                                 "mae_defaultOnOff");
 
        TEST_SB_SKIP(                                           "mae_allowPositionInteractivity");
            Skip_S1(7,                                          "mae_interactivityMinAzOffset");
            Skip_S1(7,                                          "mae_interactivityMaxAzOffset");
            Skip_S1(5,                                          "mae_interactivityMinElOffset");
            Skip_S1(5,                                          "mae_interactivityMaxElOffset");
            Skip_S1(4,                                          "mae_interactivityMinDistFactor");
            Skip_S1(4,                                          "mae_interactivityMaxDistFactor");
        TEST_SB_END();
 
        TEST_SB_SKIP(                                           "mae_allowGainInteractivity");
            Skip_S1(6,                                          "mae_interactivityMinGain");
            Skip_S1(5,                                          "mae_interactivityMaxGain");
        TEST_SB_END();
 
        int8u mae_bsGroupNumMembers;
        Get_S1(7, mae_bsGroupNumMembers,                        "mae_bsGroupNumMembers");
        mae_bsGroupNumMembers++;
        G.MemberID.resize(mae_bsGroupNumMembers);
        TESTELSE_SB_SKIP(                                       "mae_hasConjunctMembers");
            int8u mae_startID;
            Get_S1 (7, mae_startID,                             "mae_startID");
            for (int8u Pos2=0; Pos2<mae_bsGroupNumMembers; Pos2++)
                G.MemberID[Pos2]=mae_startID++;
        TESTELSE_SB_ELSE(                                       "mae_hasConjunctMembers");
            for (int8u Pos2=0; Pos2<mae_bsGroupNumMembers; Pos2++)
                Get_S1 (7, G.MemberID[Pos2],                    "mae_metaDataElementID");
        TESTELSE_SB_END();
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_SwitchGroupDefinition(int8u numSwitchGroups)
{
    Element_Begin1("mae_SwitchGroupDefinition");
    SwitchGroups.resize(numSwitchGroups);
    for(int8u Pos=0; Pos<numSwitchGroups; Pos++)
    {
        Element_Begin1("mae_switchGroup");
        switch_group& S=SwitchGroups[Pos];
        Get_S1 (5, S.ID,                                        "mae_switchGroupID");
        Element_Info1(Ztring::ToZtring(S.ID));
 
        TESTELSE_SB_GET(S.allowOnOff,                           "mae_switchGroupAllowOnOff");
            Get_SB (S.defaultOnOff,                             "mae_switchGroupDefaultOnOff");
        TESTELSE_SB_ELSE(                                       "mae_switchGroupAllowOnOff");
            S.defaultOnOff=false;
        TESTELSE_SB_END();
 
        int8u mae_bsSwitchGroupNumMembers;
        Get_S1(5, mae_bsSwitchGroupNumMembers,                  "mae_bsSwitchGroupNumMembers");
        mae_bsSwitchGroupNumMembers++;
        S.MemberID.resize(mae_bsSwitchGroupNumMembers);
        for (int8u Pos2=0; Pos2<mae_bsSwitchGroupNumMembers; Pos2++)
            Get_S1 (7, S.MemberID[Pos2],                        "mae_switchGroupMemberID");
 
        Get_S1 (7, S.DefaultGroupID,                            "mae_switchGroupDefaultGroupID");
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_GroupPresetDefinition(int8u numGroupPresets)
{
    Element_Begin1("mae_GroupPresetDefinition");
    GroupPresets.resize(numGroupPresets);
    for (int8u Pos=0; Pos<numGroupPresets; Pos++)
    {
        Element_Begin1("mae_groupPreset");
        group_preset& P=GroupPresets[Pos];
        Get_S1 (5, P.ID,                                        "mae_groupPresetID");
        Element_Info1(Ztring::ToZtring(P.ID));
        Get_S1 (5, P.Kind,                                      "mae_groupPresetKind");
 
        int8u mae_bsGroupPresetNumConditions;
        Get_S1 (4, mae_bsGroupPresetNumConditions,              "mae_bsGroupPresetNumConditions");
        mae_bsGroupPresetNumConditions++;
        P.Conditions.resize(mae_bsGroupPresetNumConditions);
        for (int8u Pos2=0; Pos2<mae_bsGroupPresetNumConditions; Pos2++)
        {
            Element_Begin1("mae_groupPreset");
            Get_S1 (7, P.Conditions[Pos2].ReferenceID,          "mae_groupPresetReferenceID");
            Element_Info1(P.Conditions[Pos2].ReferenceID);
            TEST_SB_GET (P.Conditions[Pos2].ConditionOnOff,     "mae_groupPresetConditionOnOff");
                Skip_SB(                                        "mae_groupPresetDisableGainInteractivity");
                TEST_SB_SKIP(                                   "mae_groupPresetGainFlag");
                    Skip_S1(8,                                  "mae_groupPresetGain");
                TEST_SB_END();
                Skip_SB(                                        "mae_groupPresetDisablePositionInteractivity");
                TEST_SB_SKIP(                                   "mae_groupPresetPositionFlag");
                    Skip_S1(8,                                  "mae_groupPresetAzOffset");
                    Skip_S1(6,                                  "mae_groupPresetElOffset");
                    Skip_S1(4,                                  "mae_groupPresetDistFactor");
                TEST_SB_END();
            TEST_SB_END();
            Element_End0();
        }
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_Data(int8u numGroups, int8u numGroupPresets)
{
    Element_Begin1("mae_Data");
    int8u mae_numDataSets;
    Get_S1(4, mae_numDataSets,                                  "mae_numDataSets");
    for (int8u Pos=0; Pos<mae_numDataSets; Pos++)
    {
        Element_Begin1("mae_data");
        int8u mae_dataType;
        Get_S1(4, mae_dataType,                                 "mae_dataType");
        int16u mae_dataLength;
        Get_S2(16, mae_dataLength,                              "mae_dataLength");
 
        size_t Remain_Before=BS->Remain();
        switch ((MaeDataType)mae_dataType)
        {
        case ID_MAE_GROUP_DESCRIPTION:
        case ID_MAE_SWITCHGROUP_DESCRIPTION:
        case ID_MAE_GROUP_PRESET_DESCRIPTION:
            mae_Description((MaeDataType)mae_dataType);
            break;
        case ID_MAE_GROUP_CONTENT:
            mae_ContentData();
            break;
        case ID_MAE_GROUP_COMPOSITE:
            mae_CompositePair();
            break;
        case ID_MAE_SCREEN_SIZE:
            mae_ProductionScreenSizeData();
            break;
        case ID_MAE_DRC_UI_INFO:
            mae_DrcUserInterfaceInfo(mae_dataLength);
            break;
        case ID_MAE_SCREEN_SIZE_EXTENSION:
            mae_ProductionScreenSizeDataExtension();
            break;
        case ID_MAE_GROUP_PRESET_EXTENSION:
            mae_GroupPresetDefinitionExtension(numGroupPresets);
            break;
        case ID_MAE_LOUDNESS_COMPENSATION:
            mae_LoudnessCompensationData(numGroups, numGroupPresets);
            break;
        default:
            Skip_BS(mae_dataLength*8,                           "reserved");
        }
        if (BS->Remain()+mae_dataLength*8>Remain_Before)
        {
            size_t Size=BS->Remain()+mae_dataLength*8-Remain_Before;
            int8u Padding=1;
            if (Size<8)
                Peek_S1((int8u)Size, Padding);
 
            if (Padding)
                Fill(Stream_Audio, 0, "NOK", "NOK", Unlimited, true, true);
            Skip_BS(Size, Padding?"(Unknown)":"Padding");
        }
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_Description(MaeDataType type)
{
    Element_Info1("mae_Description");
    Element_Begin1("mae_Description");
    int8u mae_bsNumDescriptionBlocks;
    Get_S1(7, mae_bsNumDescriptionBlocks,                       "mae_bsNumDescriptionBlocks");
    mae_bsNumDescriptionBlocks++;
    for (int8u Pos=0; Pos<mae_bsNumDescriptionBlocks; Pos++)
    {
        Element_Begin1("mae_descriptionGroup");
        int8u ID;
        switch (type)
        {
            case ID_MAE_GROUP_DESCRIPTION:
                Get_S1 (7, ID,                                  "mae_descriptionGroupID");
                break;
            case ID_MAE_SWITCHGROUP_DESCRIPTION:
                Get_S1 (5, ID,                                  "mae_descriptionSwitchGroupID");
                break;
            case ID_MAE_GROUP_PRESET_DESCRIPTION:
                Get_S1 (5, ID,                                  "mae_descriptionGroupPresetID");
                break;
            default:;
        }
        Element_Info1(ID);
 
        int8u mae_bsNumDescLanguages;
        Get_S1(4, mae_bsNumDescLanguages,                       "mae_bsNumDescLanguages");
        mae_bsNumDescLanguages++;
        for (int8u Pos2=0; Pos2<mae_bsNumDescLanguages; Pos2++)
        {
            Element_Begin1("mae_bsDescription");
            int32u mae_bsDescriptionLanguage;
            Get_S3 (24, mae_bsDescriptionLanguage,              "mae_bsDescriptionLanguage");
            string Language;
            for (int i=16; i>=0; i-=8)
            {
                char LanguageChar=char(mae_bsDescriptionLanguage>>i);
                if (LanguageChar)
                    Language+=LanguageChar;
            }
            Param_Info1(Language);
            Element_Info1(Language);
 
            int8u mae_bsDescriptionDataLength;
            Get_S1(8, mae_bsDescriptionDataLength,              "mae_bsDescriptionDataLength");
            mae_bsDescriptionDataLength++;
            string Description;
            Description.reserve(mae_bsDescriptionDataLength);
            for (int8u Pos3=0; Pos3<mae_bsDescriptionDataLength; Pos3++)
            {
                int8u mae_descriptionData;
                Get_S1 (8, mae_descriptionData,                 "mae_descriptionData");
                Description+=(char)mae_descriptionData;
            }
            Param_Info1(Ztring().From_UTF8(Description));
            Element_Info1(Ztring().From_UTF8(Description));
            switch (type)
            {
                case ID_MAE_GROUP_DESCRIPTION:
                    for (size_t i=0; i<Groups.size(); i++)
                        if (Groups[i].ID==ID)
                            Groups[i].Description[Language]=Description;
                    break;
                case ID_MAE_SWITCHGROUP_DESCRIPTION:
                    for (size_t i=0; i<SwitchGroups.size(); i++)
                        if (SwitchGroups[i].ID==ID)
                            SwitchGroups[i].Description[Language]=Description;
                    break;
                case ID_MAE_GROUP_PRESET_DESCRIPTION:
                    for (size_t i=0; i<GroupPresets.size(); i++)
                        if (GroupPresets[i].ID==ID)
                            GroupPresets[i].Description[Language]=Description;
                    break;
                default: ;
            }
            Element_End0();
        }
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_ContentData()
{
    Element_Info1("mae_ContentData");
    Element_Begin1("mae_ContentData");
    int8u mae_bsNumContentDataBlocks;
    Get_S1(7, mae_bsNumContentDataBlocks,                       "mae_bsNumContentDataBlocks");
    for (int8u Pos=0; Pos<mae_bsNumContentDataBlocks+1; Pos++)
    {
        Element_Begin1("mae_ContentDataGroup");
        int8u ID, Kind;
        Get_S1 (7, ID,                                          "mae_ContentDataGroupID");
        Element_Info1(ID);
        Get_S1 (4, Kind,                                        "mae_contentKind");
        Param_Info1C(Kind<Mpegh3da_contentKind_Size, Mpegh3da_contentKind[Kind]);
        Element_Info1C(Kind<Mpegh3da_contentKind_Size, Mpegh3da_contentKind[Kind]);
        string Language;
        TEST_SB_SKIP(                                           "mae_hasContentLanguage");
            int32u mae_contentLanguage;
            Get_S3 (24, mae_contentLanguage,                    "mae_contentLanguage");
            for (int i=16; i>=0; i-=8)
            {
                char LanguageChar=char(mae_contentLanguage>>i);
                if (LanguageChar)
                    Language+=LanguageChar;
            }
            Param_Info1(Language);
            Element_Info1(Language);
            TEST_SB_END();
 
        for (size_t i=0; i<Groups.size(); i++)
            if (Groups[i].ID==ID)
            {
                Groups[i].Language=Language;
                Groups[i].Kind=Kind;
            }
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_CompositePair()
{
    Element_Begin1("mae_CompositePair");
    int8u mae_bsNumCompositePairs;
    Get_S1(7, mae_bsNumCompositePairs,                          "mae_bsNumCompositePairs");
    for (int8u Pos=0; Pos<mae_bsNumCompositePairs+1; Pos++)
    {
        Skip_S1(7,                                              "mae_CompositeElementID0");
        Skip_S1(7,                                              "mae_CompositeElementID1");
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_ProductionScreenSizeData()
{
    Element_Begin1("mae_ProductionScreenSizeData");
    TEST_SB_SKIP(                                               "hasNonStandardScreenSize");
        Skip_S2(9,                                              "bsScreenSizeAz");
        Skip_S2(9,                                              "bsScreenSizeTopEl");
        Skip_S2(9,                                              "bsScreenSizeBottomEl");
    TEST_SB_END();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_DrcUserInterfaceInfo(int16u dataLength)
{
    Element_Begin1("mae_DrcUserInterfaceInfo");
    int8u version;
    Get_S1(2, version,                                          "version");
    if (version==0)
    {
        int8u bsNumTargetLoudnessConditions;
        Get_S1(3, bsNumTargetLoudnessConditions,                "bsNumTargetLoudnessConditions");
        int16u bsNumTargetLoudnessConditions_Real;
        if (dataLength>2)
            bsNumTargetLoudnessConditions_Real=(dataLength*8-(2+3))/(6+16);
        else
            bsNumTargetLoudnessConditions_Real=0;
        if (bsNumTargetLoudnessConditions!=bsNumTargetLoudnessConditions_Real)
            Param_Info1("Error");
        for (int16u Pos=0; Pos<bsNumTargetLoudnessConditions_Real; Pos++)
        {
            Skip_S1(6,                                          "bsTargetLoudnessValueUpper");
            Skip_S2(16,                                         "drcSetEffectAvailable");
        }
    }
    else
    {
        Skip_BS((dataLength-2)*8,                               "reserved");
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_ProductionScreenSizeDataExtension()
{
    Element_Begin1("mae_ProductionScreenSizeDataExtension");
    TEST_SB_SKIP(                                               "mae_overwriteProductionScreenSizeData");
        Skip_S2(10,                                             "bsScreenSizeLeftAz");
        Skip_S2(10,                                             "bsScreenSizeRightAz");
    TEST_SB_END();
    int8u mae_NumPresetProductionScreens;
    Get_S1(5, mae_NumPresetProductionScreens,                   "mae_NumPresetProductionScreens");
    for (int8u Pos=0; Pos< mae_NumPresetProductionScreens; Pos++)
    {
        Skip_S1(5,                                              "mae_productionScreenGroupPresetID");
        TEST_SB_SKIP(                                           "mae_hasNonStandardScreenSize");
            TESTELSE_SB_SKIP(                                   "isCenteredInAzimuth");
                Skip_S2(9,                                      "bsScreenSizeAz");
            TESTELSE_SB_ELSE(                                   "isCenteredInAzimuth");
                Skip_S2(10,                                     "bsScreenSizeLeftAz");
                Skip_S2(10,                                     "bsScreenSizeRightAz");
            TESTELSE_SB_END();
            Skip_S2(9,                                          "bsScreenSizeTopEl");
            Skip_S2(9,                                          "bsScreenSizeBottomEl");
        TEST_SB_END();
    }
    Element_End0();
}
 
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_GroupPresetDefinitionExtension(int8u numGroupPresets)
{
    Element_Begin1("mae_GroupPresetDefinitionExtension");
    for (int8u Pos=0; Pos<numGroupPresets; Pos++)
    {
        TEST_SB_SKIP(                                           "mae_hasSwitchGroupConditions");
            int8u Temp=Pos<GroupPresets.size()?GroupPresets[Pos].Conditions.size():0;
            for(int8u Pos2=0; Pos2<Temp; Pos2++)
                Skip_SB(                                        "mae_isSwitchGroupCondition");
        TEST_SB_END();
 
        TEST_SB_SKIP(                                           "mae_hasDownmixIdGroupPresetExtensions");
            int8u mae_numDownmixIdGroupPresetExtensions;
            Get_S1(5, mae_numDownmixIdGroupPresetExtensions,    "mae_numDownmixIdGroupPresetExtensions");
            for (int8u Pos2=1; Pos2<mae_numDownmixIdGroupPresetExtensions+1; Pos2++)
            {
                Skip_S1(7,                                      "mae_groupPresetDownmixId");
 
                int8u mae_bsGroupPresetNumConditions;
                Get_S1(4, mae_bsGroupPresetNumConditions,       "mae_bsGroupPresetNumConditions");
                for (int8u Pos3=0; Pos3<mae_bsGroupPresetNumConditions+1; Pos3++)
                {
                    TESTELSE_SB_SKIP(                           "mae_isSwitchGroupCondition");
                        Skip_S1(5,                              "mae_groupPresetSwitchGroupID");
                    TESTELSE_SB_ELSE(                           "mae_isSwitchGroupCondition");
                        Skip_S1(7,                              "mae_groupPresetGroupID");
                    TESTELSE_SB_END();
                    TEST_SB_SKIP(                               "mae_groupPresetConditionOnOff");
                        Skip_SB(                                "mae_groupPresetDisableGainInteractivity");
                        TEST_SB_SKIP(                           "mae_groupPresetGainFlag");
                            Skip_S1(8,                          "mae_groupPresetGain");
                        TEST_SB_END();
                        Skip_SB(                                "mae_groupPresetDisablePositionInteractivity");
                        TEST_SB_SKIP(                           "mae_groupPresetPositionFlag");
                            Skip_S1(8,                          "mae_groupPresetAzOffset");
                            Skip_S1(6,                          "mae_groupPresetElOffset");
                            Skip_S1(4,                          "mae_groupPresetDistFactor");
                        TEST_SB_END();
                    TEST_SB_END();
                }
            }
        TEST_SB_END();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mae_LoudnessCompensationData(int8u numGroups, int8u numGroupPresets)
{
    Element_Begin1("mae_LoudnessCompensationData");
    TEST_SB_SKIP(                                               "mae_loudnessCompGroupLoudnessPresent");
        for (int8u Pos=0; Pos<numGroups; Pos++)
            Skip_S1(8,                                          "mae_bsLoudnessCompGroupLoudness");
    TEST_SB_END();
 
    TEST_SB_SKIP(                                               "mae_loudnessCompDefaultParamsPresent");
        for (int8u Pos=0; Pos<numGroups; Pos++)
            Skip_SB(                                            "mae_loudnessCompDefaultIncludeGroup");
 
        TEST_SB_SKIP(                                           "mae_loudnessCompDefaultMinMaxGainPresent");
            Skip_S1(4,                                          "mae_bsLoudnessCompDefaultMinGain");
            Skip_S1(4,                                          "mae_bsLoudnessCompDefaultMaxGain");
        TEST_SB_END();
    TEST_SB_END();
 
    for (int8u Pos=0; Pos<numGroupPresets; Pos++)
    {
        TEST_SB_SKIP(                                           "mae_loudnessCompPresetParamsPresent");
            for (int8u Pos2=0; Pos2<numGroups; Pos2++)
                Skip_SB(                                        "mae_loudnessCompPresetIncludeGroup");
 
            TEST_SB_SKIP(                                       "mae_loudnessCompPresetMinMaxGainPresent");
                Skip_S1(4,                                      "mae_bsLoudnessCompPresetMinGain");
                Skip_S1(4,                                      "mae_bsLoudnessCompPresetMaxGain");
            TEST_SB_END();
        TEST_SB_END();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::audioTruncationInfo()
{
    Element_Begin1("audioTruncationInfo");
    BS_Begin();
    Skip_SB(                                                    "isActive");
    Skip_SB(                                                    "ati_reserved");
    Skip_SB(                                                    "truncFromBegin");
    Skip_S2(13,                                                 "nTruncSamples");
    BS_End();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_Mpegh3da::mhaC()
{
    Element_Begin1("MHADecoderConfigurationRecord");
    Skip_B1(                                                    "configurationVersion");
    Skip_B1(                                                    "mpegh3daProfileLevelIndication");
    Skip_B1(                                                    "referenceChannelLayout");
    Skip_B2(                                                    "mpegh3daConfigLength");
    mpegh3daConfig();
    Element_End0();
}
 
//***************************************************************************
// Helpers
//***************************************************************************
 
//---------------------------------------------------------------------------
size_t File_Mpegh3da::num_objects_Get()
{
    size_t num_signals_InElements=0;
    for (size_t i=0; i<Elements.size(); i++)
        if (Elements[i].Type==ID_USAC_SCE || Elements[i].Type==ID_USAC_CPE)
            num_signals_InElements++;
    size_t num_signals_InSignals=0;
    for (size_t i=0; i<SignalGroups.size(); i++)
    {
        if (num_signals_InElements==num_signals_InSignals)
        {
            return SignalGroups[i].bsNumberOfSignals;
            break;
        }
        num_signals_InSignals+=SignalGroups[i].bsNumberOfSignals;
    }
 
    return 0; //Problem
}
 
 
//---------------------------------------------------------------------------
void File_Mpegh3da::Streams_Fill_ChannelLayout(const string& Prefix, const speaker_layout& Layout, int8u speakerLayoutType)
{
    if (Aac_Channels_Get(Layout.ChannelLayout))
    {
        Fill(Stream_Audio, 0, (Prefix+"Channel(s)").c_str(), Aac_Channels_GetString(Layout.ChannelLayout));
        if (!Prefix.empty())
            Fill_SetOptions(Stream_Audio, 0, (Prefix + "Channel(s)").c_str(), "N NTY");
        if (!Prefix.empty())
        {
            string ChannelString=MediaInfoLib::Config.Language_Get(Ztring().From_UTF8(Aac_Channels_GetString(Layout.ChannelLayout)), __T(" channel")).To_UTF8();
            string ChannelMode=Aac_ChannelMode_GetString(Layout.ChannelLayout, true);
            if (ChannelMode.size()>3 || (ChannelMode.size()==3 && ChannelMode[2]!='0'))
                ChannelString+=" ("+Aac_ChannelMode_GetString(Layout.ChannelLayout, true)+")";
            Fill(Stream_Audio, 0, (Prefix+"Channel(s)/String").c_str(), ChannelString);
            Fill_SetOptions(Stream_Audio, 0, (Prefix + "Channel(s)/String").c_str(), "Y NTN");
        }
        Fill(Stream_Audio, 0, (Prefix+"ChannelPositions").c_str(), Aac_ChannelConfiguration_GetString(Layout.ChannelLayout));
        if (!Prefix.empty())
            Fill_SetOptions(Stream_Audio, 0, (Prefix+"ChannelPositions").c_str(), "N YTY");
        Fill(Stream_Audio, 0, (Prefix+"ChannelPositions/String2").c_str(), Aac_ChannelConfiguration2_GetString(Layout.ChannelLayout));
        if (!Prefix.empty())
            Fill_SetOptions(Stream_Audio, 0, (Prefix+"ChannelPositions/String2").c_str(), "N YTY");
        Fill(Stream_Audio, 0, (Prefix+"ChannelMode").c_str(), Aac_ChannelMode_GetString(Layout.ChannelLayout, true));
        Fill_SetOptions(Stream_Audio, 0, (Prefix+"ChannelMode").c_str(), "N NTY");
        Fill(Stream_Audio, 0, (Prefix+"ChannelLayout").c_str(), Aac_ChannelLayout_GetString(Layout.ChannelLayout, true));
    }
    else if (Layout.numSpeakers)
    {
        if (speakerLayoutType==1) // Objects
        {
            Fill(Stream_Audio, 0, (Prefix+"NumberOfObjects").c_str(), Layout.numSpeakers);
            Fill_SetOptions(Stream_Audio, 0, (Prefix+"NumberOfObjects").c_str(), "N YTY");
            Fill(Stream_Audio, 0, (Prefix + "NumberOfObjects/String").c_str(), MediaInfoLib::Config.Language_Get(Ztring::ToZtring(Layout.numSpeakers), __T(" object")));
            Fill_SetOptions(Stream_Audio, 0, (Prefix+"NumberOfObjects/String").c_str(), "Y YTN");
        }
        else
        {
            Fill(Stream_Audio, 0, (Prefix+"Channel(s)").c_str(), Layout.numSpeakers);
            Fill_SetOptions(Stream_Audio, 0, (Prefix+"Channel(s)").c_str(), "N YTY");
            Fill(Stream_Audio, 0, (Prefix + "Channel(s)/String").c_str(), MediaInfoLib::Config.Language_Get(Ztring::ToZtring(Layout.numSpeakers), __T(" channel")));
            Fill_SetOptions(Stream_Audio, 0, (Prefix+"Channel(s)/String").c_str(), "Y YTN");
        }
        if (!Layout.CICPspeakerIdxs.empty())
        {
            Fill(Stream_Audio, 0, (Prefix+"ChannelMode").c_str(), Aac_ChannelMode_GetString(Layout.CICPspeakerIdxs));
            Fill(Stream_Audio, 0, (Prefix+"ChannelLayout").c_str(), Aac_ChannelLayout_GetString(Layout.CICPspeakerIdxs));
        }
        else
        {
            vector<Aac_OutputChannel> CICPspeakerIdxs;
            string ChannelLayout;
            for (size_t i=0; i<Layout.SpeakersInfo.size(); i++)
            {
                if (i)
                    ChannelLayout+=' ';
                if (Layout.SpeakersInfo[i].CICPspeakerIdx!=(Aac_OutputChannel)-1)
                {
                    ChannelLayout+=Aac_ChannelLayout_GetString(&Layout.SpeakersInfo[i].CICPspeakerIdx, 1);
                    CICPspeakerIdxs.push_back(Layout.SpeakersInfo[i].CICPspeakerIdx);
                }
                else
                {
                    if (Layout.SpeakersInfo[i].ElevationAngle==0)
                        ChannelLayout+='M';
                    else
                        ChannelLayout+=Layout.SpeakersInfo[i].ElevationDirection?'B':'U';
                    ChannelLayout+='_';
                    if (Layout.SpeakersInfo[i].AzimuthAngle!=0 && Layout.SpeakersInfo[i].AzimuthAngle!=180)
                        ChannelLayout+=Layout.SpeakersInfo[i].AzimuthDirection?'L':'R';
                    string AzimuthAngleString=Ztring::ToZtring(Layout.SpeakersInfo[i].AzimuthAngle).To_UTF8();
                    AzimuthAngleString.insert(0, 3-AzimuthAngleString.size(), '0');
                    ChannelLayout.append(AzimuthAngleString);
                }
            }
            if (CICPspeakerIdxs.size()==Layout.SpeakersInfo.size())
            {
                Fill(Stream_Audio, 0, (Prefix+"ChannelMode").c_str(), Aac_ChannelMode_GetString(CICPspeakerIdxs));
                Fill(Stream_Audio, 0, (Prefix+"ChannelLayout").c_str(), Aac_ChannelLayout_GetString(CICPspeakerIdxs));
            }
            else
                Fill(Stream_Audio, 0, (Prefix+"ChannelLayout").c_str(), ChannelLayout);
        }
    }
    else if (Layout.ChannelLayout)
    {
        Fill(Stream_Audio, 0, (Prefix+"ChannelLayout").c_str(), Layout.ChannelLayout);
    }
}
 
//***************************************************************************
// C++
//***************************************************************************
 
} //NameSpace
 
#endif //MEDIAINFO_MPEGH3DA_YES
 

V1037 Two or more case-branches perform the same actions. Check lines: 1775, 1778

V547 Expression '!LabelString.empty()' is always true.

V571 Recurring check. The '!LabelString.empty()' condition was already verified in line 297.

V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 2104, 2106.

V525 The code contains the collection of similar blocks. Check items '7', '5', '5' in lines 1772, 1775, 1778.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: numElements, numAudioChannels, numAudioObjects, numSAOCTransportChannels, numHOATransportChannels, bsNumSignalGroups, ...

V760 Two identical blocks of text were found. The second block begins from line 1098.

V823 Decreased performance. Object may be created in-place in the 'Layout.SpeakersInfo' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V823 Decreased performance. Object may be created in-place in the 'Elements' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V823 Decreased performance. Object may be created in-place in the 'Elements' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V823 Decreased performance. Object may be created in-place in the 'Elements' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V823 Decreased performance. Object may be created in-place in the 'Elements' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V813 Decreased performance. The 'Layout' argument should probably be rendered as a constant reference.

V823 Decreased performance. Object may be created in-place in the 'GroupPos' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V823 Decreased performance. Object may be created in-place in the 'GroupNum' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V823 Decreased performance. Object may be created in-place in the 'GroupPos' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V823 Decreased performance. Object may be created in-place in the 'GroupNum' container. Consider replacing methods: 'push_back' -> 'emplace_back'.

V807 Decreased performance. Consider creating a reference to avoid using the 'P.Conditions[Pos2]' expression repeatedly.

V807 Decreased performance. Consider creating a reference to avoid using the 'Layout.SpeakersInfo[i]' expression repeatedly.