/*  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.
 */
 
//---------------------------------------------------------------------------
// https://developer.dolby.com/globalassets/professional/dolby-e/dolby-e-high-level-frame-description.pdf
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_DOLBYE_YES)
//---------------------------------------------------------------------------
 
//---------------------------------------------------------------------------
#include "MediaInfo/Audio/File_DolbyE.h"
#include "MediaInfo/Audio/File_Aac.h"
#include <cmath>
 
#if !defined(INT8_MAX)
#define INT8_MAX (127)
#endif //!defined(INT8_MAX)
#if !defined(INT8_MIN)
#define INT8_MIN (-128)
#endif //!defined(INT8_MIN)
 
using namespace std;
//---------------------------------------------------------------------------
 
namespace MediaInfoLib
{
 
//---------------------------------------------------------------------------
//CRC computing, with incomplete first and last bytes
//Inspired by http://zorc.breitbandkatze.de/crc.html
extern const int16u CRC_16_Table[256];
int16u CRC_16_Compute(const int8u* Buffer_Begin, size_t Buffer_Size, int8u SkipBits_Begin, int8u SkipBits_End)
{
    int16u CRC_16=0x0000;
    const int8u* Buffer=Buffer_Begin;
    const int8u* Buffer_End=Buffer+Buffer_Size;
    if (SkipBits_End)
        Buffer_End--; //Not handling completely the last byte
 
    //First partial byte
    if (SkipBits_Begin)
    {
        for (int8u Mask=(1<<(7-SkipBits_Begin)); Mask; Mask>>=1)
        {
            bool NewBit=(CRC_16&0x8000)?true:false;
            CRC_16<<=1;
            if ((*Buffer)&Mask)
                NewBit=!NewBit;
            if (NewBit)
                CRC_16^=0x8005;
        }
 
        Buffer++;
    }
 
    //Complete bytes
    while (Buffer<Buffer_End)
    {
        CRC_16=(CRC_16<<8) ^ CRC_16_Table[(CRC_16>>8)^(*Buffer)];
        Buffer++;
    }
 
    //Last partial byte
    if (SkipBits_End)
    {
        for (int8u Mask=0x80; Mask>(1<<(SkipBits_End-1)); Mask>>=1)
        {
            bool NewBit=(CRC_16&0x8000)?true:false;
            CRC_16<<=1;
            if ((*Buffer)&Mask)
                NewBit=!NewBit;
            if (NewBit)
                CRC_16^=0x8005;
        }
 
        Buffer++;
    }
 
    return CRC_16;
}
 
//***************************************************************************
// Infos
//***************************************************************************
 
//---------------------------------------------------------------------------
static const int8u DolbyE_Programs[64]=
{2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 8, 1, 2, 3, 3, 4, 5, 6, 1, 2, 3, 4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
//---------------------------------------------------------------------------
static const int8u DolbyE_Channels[64]=
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
//---------------------------------------------------------------------------
const int8u DolbyE_Channels_PerProgram(int8u program_config, int8u program)
{
    switch (program_config)
    {
        case  0 :   switch (program)
                    {
                        case  0 :   return 6;
                        default :   return 2;
                    }
        case  1 :   switch (program)
                    {
                        case  0 :   return 6;
                        default :   return 1;
                    }
        case  2 :
        case 18 :   return 4;
        case  3 :
        case 12 :   switch (program)
                    {
                        case  0 :   return 4;
                        default :   return 2;
                    }
        case  4 :   switch (program)
                    {
                        case  0 :   return 4;
                        case  1 :   return 2;
                        default :   return 1;
                    }
        case  5 :
        case 13 :   switch (program)
                    {
                        case  0 :   return 4;
                        default :   return 1;
                    }
        case  6 :
        case 14 :
        case 19 :   return 2;
        case  7 :   switch (program)
                    {
                        case  0 :
                        case  1 :
                        case  2 :   return 2;
                        default :   return 1;
                    }
        case  8 :
        case 15 :   switch (program)
                    {
                        case  0 :
                        case  1 :   return 2;
                        default :   return 1;
                    }
        case  9 :
        case 16 :
        case 20 :   switch (program)
                    {
                        case  0 :   return 2;
                        default :   return 1;
                    }
        case 10 :
        case 17 :
        case 21 :   return 1;
        case 11 :   return 6;
        case 22 :   return 8;
        case 23 :   return 8;
        default :   return 0;
    }
};
 
//---------------------------------------------------------------------------
const char*  DolbyE_ChannelPositions[64]=
{
    "Front: L C R, Side: L R, LFE / Front: L R",
    "Front: L C R, Side: L R, LFE / Front: C / Front: C",
    "Front: L C R, LFE / Front: L C R, LFE",
    "Front: L C R, LFE / Front: L R / Front: L R",
    "Front: L C R, LFE / Front: L R / Front: C / Front: C",
    "Front: L C R, LFE / Front: C / Front: C / Front: C / Front: C",
    "Front: L R / Front: L R / Front: L R / Front: L R",
    "Front: L R / Front: L R / Front: L R / Front: C / Front: C",
    "Front: L R / Front: L R / Front: C / Front: C / Front: C / Front: C",
    "Front: L R / Front: C / Front: C / Front: C / Front: C / Front: C / Front: C",
    "Front: C / Front: C / Front: C / Front: C / Front: C / Front: C / Front: C / Front: C",
    "Front: L C R, Side: L R, LFE",
    "Front: L C R, LFE / Front: L R",
    "Front: L C R, LFE / Front: C / Front: C",
    "Front: L R / Front: L R / Front: L R",
    "Front: L R / Front: L R / Front: C / Front: C",
    "Front: L R / Front: C / Front: C / Front: C / Front: C",
    "Front: C / Front: C / Front: C / Front: C / Front: C / Front: C",
    "Front: L C R, LFE",
    "Front: L R / Front: L R",
    "Front: L R / Front: C / Front: C",
    "Front: C / Front: C / Front: C / Front: C",
    "Front: L C R, Side: L R, Rear: L R, LFE",
    "Front: L C C C R, Side: L R, LFE",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
};
 
//---------------------------------------------------------------------------
const char*  DolbyE_ChannelPositions_PerProgram(int8u program_config, int8u program)
{
    switch (program_config)
    {
        case  0 :   switch (program)
                    {
                        case  0 :   return "Front: L C R, Side: L R, LFE";
                        default :   return "Front: L R";
                    }
        case  1 :   switch (program)
                    {
                        case  0 :   return "Front: L C R, Side: L R, LFE";
                        default :   return "Front: C";
                    }
        case  2 :
        case 18 :   return "Front: L C R, LFE";
        case  3 :
        case 12 :   switch (program)
                    {
                        case  0 :   return "Front: L C R, LFE";
                        default :   return "Front: L R";
                    }
        case  4 :   switch (program)
                    {
                        case  0 :   return "Front: L C R, LFE";
                        case  1 :   return "Front: L R";
                        default :   return "Front: C";
                    }
        case  5 :
        case 13 :   switch (program)
                    {
                        case  0 :   return "Front: L C R, LFE";
                        default :   return "Front: C";
                    }
        case  6 :
        case 14 :
        case 19 :   return "Front: L R";
        case  7 :   switch (program)
                    {
                        case  0 :
                        case  1 :
                        case  2 :   return "Front: L R";
                        default :   return "Front: C";
                    }
        case  8 :
        case 15 :   switch (program)
                    {
                        case  0 :
                        case  1 :   return "Front: L R";
                        default :   return "Front: C";
                    }
        case  9 :
        case 16 :
        case 20 :   switch (program)
                    {
                        case  0 :   return "Front: L R";
                        default :   return "Front: C";
                    }
        case 10 :
        case 17 :
        case 21 :   return "Front: C";
        case 11 :   return "Front: L C R, Side: L R, LFE";
        case 22 :   return "Front: L C R, Side: L R, Rear: L R, LFE";
        case 23 :   return "Front: L C C C R, Side: L R, LFE";
        default :   return "";
    }
};
 
//---------------------------------------------------------------------------
const char*  DolbyE_ChannelPositions2[64]=
{
    "3/2/0.1 / 2/0/0",
    "3/2/0.1 / 1/0/0 / 1/0/0",
    "3/0/0.1 / 3/0/0.1",
    "3/0/0.1 / 2/0/0 / 2/0/0",
    "3/0/0.1 / 2/0/0 / 1/0/0 / 1/0/0",
    "3/0/0.1 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0",
    "2/0/0 / 2/0/0 / 2/0/0 / 2/0/0",
    "2/0/0 / 2/0/0 / 2/0/0 / 1/0/0 / 1/0/0",
    "2/0/0 / 2/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0",
    "2/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0",
    "1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0",
    "3/2/0.1",
    "3/0/0.1 / 2/0/0",
    "3/0/0.1 / 1/0/0 / 1/0/0",
    "2/0/0 / 2/0/0 / 2/0/0",
    "2/0/0 / 2/0/0 / 1/0/0 / 1/0/0",
    "2/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0",
    "1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0 / 1/0/0",
    "3/0/0.1",
    "2/0/0 / 2/0/0",
    "2/0/0 / 1/0/0 / 1/0/0",
    "1/0/0 / 1/0/0 / 1/0/0 / 1/0/0",
    "3/2/2.1",
    "5/2/0.1",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
};
 
//---------------------------------------------------------------------------
const char*  DolbyE_ChannelPositions2_PerProgram(int8u program_config, int8u program)
{
    switch (program_config)
    {
        case  0 :   switch (program)
                    {
                        case  0 :   return "3/2/0.1";
                        default :   return "2/0/0";
                    }
        case  1 :   switch (program)
                    {
                        case  0 :   return "3/2/0.1";
                        default :   return "1/0/0";
                    }
        case  2 :
        case 18 :   return "3/0/0.1";
        case  3 :
        case 12 :   switch (program)
                    {
                        case  0 :   return "3/0/0.1";
                        default :   return "2/0/0";
                    }
        case  4 :   switch (program)
                    {
                        case  0 :   return "3/0/0.1";
                        case  1 :   return "2/0/0";
                        default :   return "1/0/0";
                    }
        case  5 :
        case 13 :   switch (program)
                    {
                        case  0 :   return "3/0/0.1";
                        default :   return "1/0/0";
                    }
        case  6 :
        case 14 :
        case 19 :   return "Front: L R";
        case  7 :   switch (program)
                    {
                        case  0 :
                        case  1 :
                        case  2 :   return "2/0/0";
                        default :   return "1/0/0";
                    }
        case  8 :
        case 15 :   switch (program)
                    {
                        case  0 :
                        case  1 :   return "2/0/0";
                        default :   return "1/0/0";
                    }
        case  9 :
        case 16 :
        case 20 :   switch (program)
                    {
                        case  0 :   return "2/0/0";
                        default :   return "1/0/0";
                    }
        case 10 :
        case 17 :
        case 21 :   return "1/0/0";
        case 11 :   return "3/2/0.1";
        case 22 :   return "3/2/2.1";
        case 23 :   return "5/2/0.1";
        default :   return "";
    }
};
 
extern const char*  AC3_Surround[];
 
//---------------------------------------------------------------------------
const char*  DolbyE_ChannelLayout_PerProgram(int8u program_config, int8u ProgramNumber)
{
    switch (program_config)
    {
        case  0 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C Ls X R LFE Rs X";
                        default :   return "X X X L X X X R";
                    }
        case  1 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C Ls X R LFE Rs X";
                        case  1 :   return "X X X C X X X X";
                        default :   return "X X X X X X X C";
                    }
        case  2 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C X X R S X X";
                        default :   return "X X L C X X R S";
                    }
        case  3 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C X X R S X X";
                        case  1 :   return "X X L X X X R X";
                        default :   return "X X X L X X X R";
                    }
        case  4 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C X X R S X X";
                        case  1 :   return "X X L X X X R X";
                        case  2 :   return "X X X C X X X X";
                        default :   return "X X X X X X X C";
                    }
        case  5 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C X X R S X X";
                        case  1 :   return "X X C X X X X X";
                        case  2 :   return "X X X X X X C X";
                        case  3 :   return "X X X C X X X X";
                        default :   return "X X X X X X X C";
                    }
        case  6 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X X X R X X X";
                        case  1 :   return "X L X X X R X X";
                        case  2 :   return "X X L X X X R X";
                        default :   return "X X X L X X X R";
                    }
        case  7 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X X X R X X X";
                        case  1 :   return "X L X X X R X X";
                        case  2 :   return "X X L X X X R X";
                        case  3 :   return "X X X C X X X X";
                        default :   return "X X X X X X X C";
                    }
        case  8 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X X X R X X X";
                        case  1 :   return "X L X X X R X X";
                        case  2 :   return "X X C X X X X X";
                        case  3 :   return "X X X X X X C X";
                        case  4 :   return "X X X C X X X X";
                        default :   return "X X X X X X X C";
                    }
        case  9 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X X X R X X X";
                        case  1 :   return "X C X X X X X X";
                        case  2 :   return "X X X X X C X X";
                        case  3 :   return "X X C X X X X X";
                        case  4 :   return "X X X X X X C X";
                        case  5 :   return "X X X C X X X X";
                        default :   return "X X X X X X X C";
                    }
        case 10 :   switch (ProgramNumber)
                    {
                        case  0 :   return "C X X X X X X X";
                        case  1 :   return "X X X X C X X X";
                        case  2 :   return "X C X X X X X X";
                        case  3 :   return "X X X X X C X X";
                        case  4 :   return "X X C X X X X X";
                        case  5 :   return "X X X X X X C X";
                        case  6 :   return "X X X C X X X X";
                        default :   return "X X X X X X X C";
                    }
        case 11 :   return "L C Ls R LFE Rs";
        case 12 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C X R S X";
                        default :   return "X X L X X R";
                    }
        case 13 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L C X R S X";
                        case  1 :   return "X X C X X X";
                        default :   return "X X X X X C";
                    }
        case 14 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X X R X X";
                        case  1 :   return "X L X X R X";
                        default :   return "X X L X X R";
                    }
        case 15 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X X R X X";
                        case  1 :   return "X L X R X";
                        case  2 :   return "X X C X X X";
                        default :   return "X X X X X C";
                    }
        case 16 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X X R X X";
                        case  1 :   return "X C X X X X";
                        case  2 :   return "X X X X C X";
                        case  3 :   return "X X C X X X";
                        default :   return "X X X X X C";
                    }
        case 17 :   switch (ProgramNumber)
                    {
                        case  0 :   return "C X X X X X";
                        case  1 :   return "X X X C X X";
                        case  2 :   return "X C X X X X";
                        case  3 :   return "X X X X C X";
                        case  4 :   return "X X C X X X";
                        default :   return "X X X X X C";
                    }
        case 18 :   return "L C R S";
        case 19 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X R X";
                        default :   return "X L X R";
                    }
        case 20 :   switch (ProgramNumber)
                    {
                        case  0 :   return "L X R X";
                        case  1 :   return "X C X X";
                        default :   return "X X X C";
                    }
        case 21 :   switch (ProgramNumber)
                    {
                        case  0 :   return "C X X X";
                        case  1 :   return "X X C X";
                        case  2 :   return "X C X X";
                        default :   return "X X X C";
                    }
        case 22 :   return "L C Ls Lrs R LFE Rs Rrs";
        case 23 :   return "L C Ls Lc R LFE Rs Rc";
        default :   return "";
    }
};
 
extern const float64 Mpegv_frame_rate[16];
 
const bool Mpegv_frame_rate_type[16]=
{false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false};
 
 
//---------------------------------------------------------------------------
static const int8u intermediate_spatial_format_object_count[8]=
{
    4,
    8,
    10,
    14,
    15,
    30,
    0,
    0,
};
 
const char* sound_category_Values[4]=
{
    "",
    "Dialog",
    "",
    "",
};
const char* hp_render_mode_Values[4]=
{
    "Bypassed",
    "Near",
    "Far",
    "",
};
string default_target_device_config_Value(int32u config)
{
    string Value;
    if (config&0x1)
        Value+="Stereo / ";
    if (config&0x2)
        Value+="Surround / ";
    if (config&0x4)
        Value+="Immersive / ";
    if (!Value.empty())
        Value.resize(Value.size()-3);
    return Value;
}
struct pos3d
{
    int8u x;
    int8u y;
    int8u z;
    const char* Value;
};
int16u mgi_6bit_unsigned_to_oari_Q15[0x40]=
{
      0,   529,  1057,  1586,  2114,  2643,  3171,  3700,
   4228,  4757,  5285,  5814,  6342,  6871,  7399,  7928,
   8456,  8985,  9513, 10042, 10570, 11099, 11627, 12156,
  12684, 13213, 13741, 14270, 14798, 15327, 15855, 16384,
  16913, 17441, 17970, 18498, 19027, 19555, 20084, 20612,
  21141, 21669, 22198, 22726, 23255, 23783, 24312, 24840,
  25369, 25897, 26426, 26954, 27483, 28011, 28540, 29068,
  29597, 30125, 30654, 31182, 31711, 32239, 32767, 32767
};
 
int16u mgi_4bit_unsigned_to_oari_Q15[0x10]=
{
      0,  2185,  4369,  6554,  8738, 10923, 13107, 15292,
  17476, 19661, 21845, 24030, 26214, 28399, 30583, 32767
};
 
int mgi_bitstream_val_to_Q15(int value, int8u num_bits)
{
    bool sign;
    if (value<0)
    {
        sign=true;
        value=-value;
    }
    else
        sign=false;
 
    int16u* Table;
    switch (num_bits)
    {
        case 4: Table=mgi_4bit_unsigned_to_oari_Q15; break;
        case 6: Table=mgi_6bit_unsigned_to_oari_Q15; break;
        default: return 0; //Problem
    }
    int decoded=Table[value];
 
    return sign?-decoded:decoded;
}
 
int mgi_bitstream_pos_z_to_Q15(bool pos_z_sign, int8u pos_z_bits)
{
    if (pos_z_bits == 0xf) {
        if (pos_z_sign == 1) /* +1 */ {
            return(32767);
        }
        else /* -1 */ {
            return(-32768);
        }
    }
    else {
        /* Negative number */
        if (pos_z_sign == 1) {
            return(mgi_bitstream_val_to_Q15(pos_z_bits, 4));
        }
        else {
            return(mgi_bitstream_val_to_Q15(-pos_z_bits, 4));
        }
    }
}
 
//---------------------------------------------------------------------------
struct speaker_info
{
    int8u AzimuthAngle; //0 to 180 (right, if AzimuthDirection is false) or 179 (left, if AzimuthDirection is true)
    int8s ElevationDirectionAngle; //-90 (bottom) to 90 (top)
    enum flag
    {
        None                            = 0,
        AzimuthDirection                = 1<<0, // true = right
        isLFE                           = 1<<1,
    };
    int8u Flags;
};
bool operator== (const speaker_info& L, const speaker_info& R)
{
    return L.AzimuthAngle==R.AzimuthAngle
        && L.ElevationDirectionAngle==R.ElevationDirectionAngle
        && L.Flags==R.Flags;
}
extern string Aac_ChannelLayout_GetString(const Aac_OutputChannel* const OutputChannels, size_t OutputChannels_Size);
static const size_t SpeakerInfos_Size=43;
static const speaker_info SpeakerInfos[SpeakerInfos_Size] =
{
    {  30,   0, speaker_info::AzimuthDirection },
    {  30,   0, speaker_info::None },
    {   0,   0, speaker_info::None },
    {   0, -15,                                 speaker_info::isLFE },
    { 110,   0, speaker_info::AzimuthDirection },
    { 110,   0, speaker_info::None },
    {  22,   0, speaker_info::AzimuthDirection },
    {  22,   0, speaker_info::None },
    { 135,   0, speaker_info::AzimuthDirection },
    { 135,   0, speaker_info::None },
    { 180,   0, speaker_info::None },
    { 135,   0, speaker_info::AzimuthDirection },
    { 135,   0, speaker_info::None },
    {  90,   0, speaker_info::AzimuthDirection },
    {  90,   0, speaker_info::None },
    {  60,   0, speaker_info::AzimuthDirection },
    {  60,   0, speaker_info::None },
    {  30,  35, speaker_info::AzimuthDirection },
    {  30,  35, speaker_info::None },
    {   0,  35, speaker_info::None },
    { 135,  35, speaker_info::AzimuthDirection },
    { 135,  35, speaker_info::None },
    { 180,  35, speaker_info::None },
    {  90,  35, speaker_info::AzimuthDirection },
    {  90,  35, speaker_info::None },
    {   0,  90, speaker_info::None },
    {  45, -15,                                 speaker_info::isLFE },
    {  45, -15, speaker_info::AzimuthDirection },
    {  45, -15, speaker_info::None },
    {   0, -15, speaker_info::None },
    { 110,  35, speaker_info::AzimuthDirection },
    { 110,  35, speaker_info::None },
    {  45,  35, speaker_info::AzimuthDirection },
    {  45,  35, speaker_info::None },
    {  45,   0, speaker_info::AzimuthDirection },
    {  45,   0, speaker_info::None },
    {  45, -15, speaker_info::None | speaker_info::isLFE },
    {   2,   0, speaker_info::AzimuthDirection },
    {   2,   0, speaker_info::None },
    {   1,   0, speaker_info::AzimuthDirection },
    {   1,   0, speaker_info::None },
    { 150,   0, speaker_info::AzimuthDirection },
    { 150,   0, speaker_info::None }, 
};
 
struct angles
{
    int Azimuth;
    int Elevation;
 
    angles()
    {}
 
    angles(int theta, int phi) :
        Azimuth(theta),
        Elevation(phi)
    {}
};
Aac_OutputChannel AnglesToChannelName(angles Angles)
{
    speaker_info SpeakerInfo;
    if (Angles.Azimuth<0)
    {
        SpeakerInfo.AzimuthAngle=(int8u)-Angles.Azimuth;
        SpeakerInfo.Flags=speaker_info::AzimuthDirection;
    }
    else
    {
        SpeakerInfo.AzimuthAngle=Angles.Azimuth;
        SpeakerInfo.Flags=speaker_info::None;
    }
    SpeakerInfo.ElevationDirectionAngle=Angles.Elevation;
 
    for (size_t i=0; i<SpeakerInfos_Size; i++)
        if (SpeakerInfos[i]==SpeakerInfo)
            return (Aac_OutputChannel)i;
    return CH_MAX;
}
string ToAngle3Digits(int Value)
{
    string ToReturn=Ztring::ToZtring(Value).To_UTF8();
    ToReturn.insert(0, 3-ToReturn.size(), '0');
    return ToReturn;
}
angles mgi_bitstream_pos_ToAngles(int x_int, int y_int, int z_int)
{
    //Center becomes 0 and edges +/-1
    float x=(((float)x_int)*2-32768)/32768;
    float y=(((float)y_int)*2-32768)/32768;
    float z=((float)z_int)/32768;
 
    if (!x && !y)
    {
        if (z>0)
            return angles(0, 90);
        else if (z<0)
            return angles(0, -90);
        return angles(0, 0); // Should not happen (is the center in practice)
    }
    else
    {
        angles ToReturn;
        
        float radius=sqrt(x*x+y*y+z*z);
        float theta_float=round(atan2(y, x)*180/3.14159265359/5)*5;
        ToReturn.Azimuth=float32_int32s(theta_float);
        float phi_float=round(acos(z/radius)*180/3.14159265359);
        ToReturn.Elevation=float32_int32s(phi_float);
 
        //Reference is front of viewer
        if (ToReturn.Azimuth<90)
            ToReturn.Azimuth+=90;
        else
            ToReturn.Azimuth-=270;
        ToReturn.Elevation=90-ToReturn.Elevation;
 
        return ToReturn;
    }
}
 
string Angles2String(angles Angles)
{
    string ToReturn;
 
    //Elevation
    switch (Angles.Elevation)
    {
        case   0:   ToReturn+='M'; break;
        case  90:   ToReturn+='T'; break;
        case -90:   ToReturn+='X'; break; //No standard for that
        default :   ToReturn+=(Angles.Elevation>0?'U':'B');
                    //if (Angles.phi!=35 && Angles.phi!=-15)
                        ToReturn+=ToAngle3Digits(Angles.Elevation);
    }
 
    ToReturn+='_';
 
    //Azimuth
    if (Angles.Azimuth<0)
        ToReturn+='L';
    else if (Angles.Azimuth>0 && Angles.Azimuth!=180)
        ToReturn+='R';
    ToReturn+=ToAngle3Digits(Angles.Azimuth<0?-Angles.Azimuth:Angles.Azimuth);
 
    return ToReturn;
}
 
string Angles2KnownChannelName(angles& Angles)
{
    //Exception handling
    int Azimuth;
    if (Angles.Azimuth==-180)
        Azimuth=180;
    else
        Azimuth=Angles.Azimuth;
    int Elevation;
    if (Angles.Elevation>=35 && Angles.Elevation<=45)
        Elevation=35;
    else
        Elevation=Angles.Elevation;
 
    Aac_OutputChannel KnownChannel=AnglesToChannelName(angles(Azimuth, Elevation));
    if (KnownChannel!=CH_MAX)
        return Aac_ChannelLayout_GetString(&KnownChannel, 1);
    else
        return Angles2String(Angles);
}
 
//---------------------------------------------------------------------------
extern int32u AC3_bed_channel_assignment_mask_2_nonstd(int16u bed_channel_assignment_mask);
extern Ztring AC3_nonstd_bed_channel_assignment_mask_ChannelLayout(int32u nonstd_bed_channel_assignment_mask);
size_t BedChannelConfiguration_ChannelCount(int32u nonstd_bed_channel_assignment_mask)
{
    Ztring BedChannelConfiguration=AC3_nonstd_bed_channel_assignment_mask_ChannelLayout(nonstd_bed_channel_assignment_mask);
    size_t BedChannelCount=0;
    if (!BedChannelConfiguration.empty())
        for (size_t i=0; i<BedChannelConfiguration.size();)
        {
            BedChannelCount++;
            i=BedChannelConfiguration.find(__T(' '), i+1);
        }
    return BedChannelCount;
}
 
//***************************************************************************
// Constructor/Destructor
//***************************************************************************
 
//---------------------------------------------------------------------------
File_DolbyE::File_DolbyE()
:File__Analyze()
{
    //Configuration
    #if MEDIAINFO_EVENTS
        ParserIDs[0]=MediaInfo_Parser_DolbyE;
    #endif //MEDIAINFO_EVENTS
 
    //Configuration
    MustSynchronize=true;
    Buffer_TotalBytes_FirstSynched_Max=32*1024;
 
    //In
    GuardBand_Before=0;
 
    //Out
    GuardBand_After=0;
 
    //ED2
    Guardband_EMDF_PresentAndSize=0;
    num_desc_packets_m1=(int32u)-1;
 
    //Temp
    SMPTE_time_code_StartTimecode=(int64u)-1;
    FrameInfo.DTS=0;
}
 
//***************************************************************************
// Streams management
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_DolbyE::Streams_Fill()
{
    Fill(Stream_General, 0, General_Format, "Dolby E");
 
    if (!Presets.empty())
    {
        Streams_Fill_ED2();
        return;
    }
 
    int8u DolbyE_Audio_Pos=0;
    for (size_t i=0; i<8; i++)
        if (channel_subsegment_sizes[i].size()>1)
            DolbyE_Audio_Pos=(int8u)-1;
    for (int8u program=0; program<DolbyE_Programs[program_config]; program++)
    {
        Stream_Prepare(Stream_Audio);
        Fill(Stream_Audio, StreamPos_Last, Audio_Format, "Dolby E");
        if (DolbyE_Programs[program_config]>1)
            Fill(Stream_Audio, StreamPos_Last, Audio_ID, Count_Get(Stream_Audio));
        Fill(Stream_Audio, StreamPos_Last, Audio_Channel_s_, DolbyE_Channels_PerProgram(program_config, program));
        Fill(Stream_Audio, StreamPos_Last, Audio_ChannelPositions, DolbyE_ChannelPositions_PerProgram(program_config, program));
        Fill(Stream_Audio, StreamPos_Last, Audio_ChannelPositions_String2, DolbyE_ChannelPositions2_PerProgram(program_config, program));
        Fill(Stream_Audio, StreamPos_Last, Audio_ChannelLayout, DolbyE_ChannelLayout_PerProgram(program_config, program));
        int32u Program_Size=0;
        if (DolbyE_Audio_Pos!=(int8u)-1)
            for (int8u Pos=0; Pos<DolbyE_Channels_PerProgram(program_config, program); Pos++)
                Program_Size+=channel_subsegment_size[DolbyE_Audio_Pos+Pos];
        if (!Mpegv_frame_rate_type[frame_rate_code])
            Program_Size*=2; //Low bit rate, 2 channel component per block
        Program_Size*=bit_depth;
        Fill(Stream_Audio, StreamPos_Last, Audio_BitRate, Program_Size*Mpegv_frame_rate[frame_rate_code], 0);
        if (DolbyE_Audio_Pos!=(int8u)-1)
            DolbyE_Audio_Pos+=DolbyE_Channels_PerProgram(program_config, program);
        Streams_Fill_PerProgram();
 
        if (program<description_text_Values.size())
        {
            Fill(Stream_Audio, StreamPos_Last, Audio_Title, description_text_Values[program].Previous);
            Fill(Stream_Audio, StreamPos_Last, "Title_FromStream", description_text_Values[program].Previous);
            Fill_SetOptions(Stream_Audio, StreamPos_Last, "Title_FromStream", "N NT");
        }
    }
}
 
//---------------------------------------------------------------------------
void File_DolbyE::Streams_Fill_PerProgram()
{
    Fill(Stream_Audio, StreamPos_Last, Audio_SamplingRate, 48000);
    Fill(Stream_Audio, StreamPos_Last, Audio_BitDepth, bit_depth);
 
    if (SMPTE_time_code_StartTimecode!=(int64u)-1)
    {
        Fill(StreamKind_Last, StreamPos_Last, Audio_Delay, SMPTE_time_code_StartTimecode);
        Fill(StreamKind_Last, StreamPos_Last, Audio_Delay_Source, "Stream");
    }
 
    Fill(Stream_Audio, StreamPos_Last, Audio_FrameRate, Mpegv_frame_rate[frame_rate_code]);
    if (FrameInfo.PTS!=(int64u)-1 && bit_depth)
    {
        float BitRate=(float)(96000*bit_depth);
 
        if (GuardBand_Before_Initial)
        {
            float GuardBand_Before_Initial_Duration=GuardBand_Before_Initial*8/BitRate;
            Fill(Stream_Audio, StreamPos_Last, "GuardBand_Before", GuardBand_Before_Initial_Duration, 9);
            Fill(Stream_Audio, StreamPos_Last, "GuardBand_Before/String", Ztring::ToZtring(GuardBand_Before_Initial_Duration*1000000, 0)+Ztring().From_UTF8(" \xC2xB5s")); //0xC2 0xB5 = micro sign
            Fill_SetOptions(Stream_Audio, StreamPos_Last, "GuardBand_Before", "N NT");
            Fill_SetOptions(Stream_Audio, StreamPos_Last, "GuardBand_Before/String", "N NT");
 
            float GuardBand_After_Initial_Duration=GuardBand_After_Initial*8/BitRate;
            Fill(Stream_Audio, StreamPos_Last, "GuardBand_After", GuardBand_After_Initial_Duration, 9);
            Fill(Stream_Audio, StreamPos_Last, "GuardBand_After/String", Ztring::ToZtring(GuardBand_After_Initial_Duration*1000000, 0)+Ztring().From_UTF8(" \xC2xB5s")); //0xC2 0xB5 = micro sign
            Fill_SetOptions(Stream_Audio, StreamPos_Last, "GuardBand_After", "N NT");
            Fill_SetOptions(Stream_Audio, StreamPos_Last, "GuardBand_After/String", "N NT");
        }
    }
 
    if (FrameSizes.size()==1)
    {
        if (StreamPos_Last)
            Fill(Stream_Audio, StreamPos_Last, Audio_BitRate_Encoded, 0, 0, true);
        else
        {
            Fill(Stream_General, 0, General_OverallBitRate, FrameSizes.begin()->first*8*Mpegv_frame_rate[frame_rate_code], 0);
            Fill(Stream_Audio, 0, Audio_BitRate_Encoded, FrameSizes.begin()->first*8*Mpegv_frame_rate[frame_rate_code], 0);
        }
    }
}
 
//---------------------------------------------------------------------------
void File_DolbyE::Streams_Fill_ED2()
{
    Stream_Prepare(Stream_Audio);
    Fill(Stream_Audio, StreamPos_Last, Audio_Format, "Dolby ED2");
    if (Guardband_EMDF_PresentAndSize)
        Fill(Stream_Audio, StreamPos_Last, Audio_BitRate, Guardband_EMDF_PresentAndSize*8*Mpegv_frame_rate[frame_rate_code], 0);
    size_t ChannelCount=DynObjects.size();
    for (size_t p=0; p<BedInstances.size(); p++)
        if (p<nonstd_bed_channel_assignment_masks.size())
            ChannelCount+=BedChannelConfiguration_ChannelCount(nonstd_bed_channel_assignment_masks[p]);
    if (ChannelCount)
        Fill(Stream_Audio, StreamPos_Last, Audio_Channel_s_, ChannelCount);
    Streams_Fill_PerProgram();
 
    if (!Presets.empty())
    {
        Fill(Stream_Audio, 0, "NumberOfPresentations", Presets.size());
        Fill_SetOptions(Stream_Audio, 0, "NumberOfPresentations", "N NIY");
    }
    if (!BedInstances.empty())
    {
        Fill(Stream_Audio, 0, "NumberOfBeds", BedInstances.size());
        Fill_SetOptions(Stream_Audio, 0, "NumberOfBeds", "N NIY");
    }
    if (!DynObjects.empty())
    {
        Fill(Stream_Audio, 0, "NumberOfObjects", DynObjects.size());
        Fill_SetOptions(Stream_Audio, 0, "NumberOfObjects", "N NIY");
    }
 
    for (size_t p=0; p<Presets.size(); p++)
    {
        const preset& Presentation_Current=Presets[p];
 
        string Title;
        if (p<Presets_More.size() && !Presets_More[p].description.empty())
            Title=Presets_More[p].description;
        
        string Summary=Title;
        if (Summary.empty())
            Summary="Yes";
 
        string P="Presentation"+Ztring::ToZtring(p).To_UTF8();
        Fill(Stream_Audio, 0, P.c_str(), Summary);
        if (!Title.empty())
        {
            Fill(Stream_Audio, 0, (P+" Title").c_str(), Title);
            Fill_SetOptions(Stream_Audio, 0, (P+" Title").c_str(), "N NTY");
        }
        Fill(Stream_Audio, 0, (P+" DefaultTargetDeviceConfig").c_str(), default_target_device_config_Value(Presentation_Current.default_target_device_config));
        Fill_SetOptions(Stream_Audio, 0, (P+" DefaultTargetDeviceConfig").c_str(), "Y NTY");
 
        if (!Presentation_Current.target_device_configs.empty())
        {
            Fill(Stream_Audio, 0, (P+" NumberOfTargets").c_str(), Presentation_Current.target_device_configs.size());
            Fill_SetOptions(Stream_Audio, 0, (P+" NumberOfTargets").c_str(), "N NIY");
        }
        for (size_t t=0; t<Presentation_Current.target_device_configs.size(); t++)
        {
            const preset::target_device_config& Target_Current=Presentation_Current.target_device_configs[t];
            
            string Summary2=default_target_device_config_Value(Target_Current.target_devices_mask);
            if (Summary2.empty())
                Summary2="Yes";
 
            string T=P+" Target"+Ztring::ToZtring(t).To_UTF8();
            Fill(Stream_Audio, 0, T.c_str(), Summary2);
 
            Fill(Stream_Audio, 0, (T+" TargetDeviceConfig").c_str(), default_target_device_config_Value(Target_Current.target_devices_mask));
            Fill_SetOptions(Stream_Audio, 0, (T+" TargetDeviceConfig").c_str(), "N NTY");
 
            ZtringList GroupPos[2], GroupNum[2];
            for (size_t i=0; i<Target_Current.md_indexes.size(); i++)
            {
                if (Target_Current.md_indexes[i]!=(int32u)-1)
                {
                    size_t I1, I2;
                    if (i<DynObjects.size())
                    {
                        I1=i;
                        I2=Target_Current.md_indexes[i];
                        if (I2 && I2>DynObjects[I1].Alts.size()) // 0 = OAMD, else Alt - 1
                            I2=0; // There is a problem
                    }
                    else
                    {
                        I1=i-DynObjects.size();
                        I2=Target_Current.md_indexes[i];
                        if (I2 && I2>BedInstances[I1].Alts.size()) // 0 = OAMD, else Alt - 1
                            I2=0; // There is a problem
                    }
 
                    int j=i<DynObjects.size()?1:0;
                    GroupPos[j].push_back(Ztring::ToZtring(I1));
                    GroupNum[j].push_back(Ztring::ToZtring(I1+1));
                    if (I2 )
                    {
                        GroupPos[j][GroupPos[j].size()-1]+=__T('-');
                        GroupNum[j][GroupNum[j].size()-1]+=__T('-');
                        GroupPos[j][GroupPos[j].size()-1]+=Ztring::ToZtring(I2-1);
                        GroupNum[j][GroupNum[j].size()-1]+=Ztring::ToZtring(I2);
                    }
                }
            }
 
            for (size_t i=0; i<2; i++)
            {
                GroupPos[i].Separator_Set(0, __T(" + "));
                Fill(Stream_Audio, 0, (T+(!i?" LinkedTo_Bed_Pos":" LinkedTo_Object_Pos")).c_str(), GroupPos[i].Read());
                Fill_SetOptions(Stream_Audio, 0, (T+(!i?" LinkedTo_Bed_Pos":" LinkedTo_Object_Pos")).c_str(), "N NIY");
                GroupNum[i].Separator_Set(0, __T(" + "));
                Fill(Stream_Audio, 0, (T+(!i?" LinkedTo_Bed_Pos/String":" LinkedTo_Object_Pos/String")).c_str(), GroupNum[i].Read());
                Fill_SetOptions(Stream_Audio, 0, (T+(!i?" LinkedTo_Bed_Pos/String":" LinkedTo_Object_Pos/String")).c_str(), "Y NIN");
            }
        }
    }
 
    // Computing the count of objects in bed instances
    size_t Bed_Object_Count=0;
 
    // Beds
    for (size_t p=0; p<BedInstances.size(); p++)
    {
        const bed_instance& BedInstance=BedInstances[p];
 
        string Summary;
        if (Summary.empty())
            Summary=AC3_nonstd_bed_channel_assignment_mask_ChannelLayout(nonstd_bed_channel_assignment_masks[p]).To_UTF8();
        if (Summary.empty())
            Summary="Yes";
 
        string P=Ztring(__T("Bed")+Ztring::ToZtring(p)).To_UTF8();
        Fill(Stream_Audio, 0, P.c_str(), Summary);
 
        if (p<nonstd_bed_channel_assignment_masks.size())
        {
            Fill(Stream_Audio, StreamPos_Last, (P+" Channel(s)").c_str(), BedChannelConfiguration_ChannelCount(nonstd_bed_channel_assignment_masks[p]));
            Fill_SetOptions(Stream_Audio, 0, (P+" Channel(s)").c_str(), "Y NTY");
            Fill(Stream_Audio, 0, (P+" ChannelLayout").c_str(), AC3_nonstd_bed_channel_assignment_mask_ChannelLayout(nonstd_bed_channel_assignment_masks[p]));
            Fill_SetOptions(Stream_Audio, 0, (P+" ChannelLayout").c_str(), "Y NTY");
 
            // From OAMD
            if (Bed_Object_Count<ObjectElements.size() && !ObjectElements[Bed_Object_Count].Alts.empty())
            {
                string A=P;
 
                // Computing the count of objects in bed instances
                size_t ThisBed_Object_Count=BedChannelConfiguration_ChannelCount(nonstd_bed_channel_assignment_masks[p]);
 
                if (ThisBed_Object_Count)
                {
                    // Check if all same
                    int8s obj_gain_db_Ref=ObjectElements[Bed_Object_Count].Alts[0].obj_gain_db;
                    for (size_t o=1; o<ThisBed_Object_Count; o++)
                    {
                        int8s obj_gain_db=ObjectElements[Bed_Object_Count+o].Alts[0].obj_gain_db;
                        if (obj_gain_db!=obj_gain_db_Ref)
                        {
                            obj_gain_db_Ref=INT8_MAX;
                            break;
                        }
                    }
                    if (obj_gain_db_Ref!=INT8_MAX)
                    {
                        // Same
                        if (obj_gain_db_Ref==INT8_MIN)
                            Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), string("-infinite"), " dB");
                        else
                            Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), obj_gain_db_Ref, " dB");
                    }
                    else
                    {
                        // Not same
                        for (size_t o=0; o<ThisBed_Object_Count; o++)
                        {
                            const dyn_object::dyn_object_alt& DynObject_Current=ObjectElements[Bed_Object_Count+o].Alts[0];
                            if (DynObject_Current.obj_gain_db!=INT8_MAX)
                            {
                                if (DynObject_Current.obj_gain_db==INT8_MIN)
                                    Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), string("-infinite"), " dB");
                                else
                                    Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), DynObject_Current.obj_gain_db, " dB");
                            }
                        }
                    }
 
                }
                Bed_Object_Count+=ThisBed_Object_Count;
            }
        }
 
        if (!BedInstance.Alts.empty())
        {
            Fill(Stream_Audio, 0, (P+" NumberOfAlts").c_str(), BedInstance.Alts.size());
            Fill_SetOptions(Stream_Audio, 0, (P+" NumberOfAlts").c_str(), "N NIY");
        }
        for (size_t a=0; a<BedInstance.Alts.size(); a++)
        {
            const bed_instance::bed_alt& BedInstance_Current=BedInstance.Alts[a];
 
            string Summary2;
            if (Summary2.empty())
                Summary2="Yes";
 
            string A=P+Ztring(__T(" Alt")+Ztring::ToZtring(a)).To_UTF8();
            Fill(Stream_Audio, 0, A.c_str(), Summary2);
            if (BedInstance_Current.bed_gain_db!=INT8_MAX)
            {
                Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), BedInstance_Current.bed_gain_db, " dB");
            }
        }
 
        const substream_mapping& S=substream_mappings[DynObjects.size()+p];
        {
            Fill(Stream_Audio, 0, (P+" SubtstreamIdChannel").c_str(), Ztring::ToZtring(S.substream_id)+__T('-')+Ztring::ToZtring(S.channel_index));
            Fill_SetOptions(Stream_Audio, 0, (P+" SubtstreamIdChannel").c_str(), "Y NTY");
        }
    }
 
    // Dynamic objects
    for (size_t p=0; p<DynObjects.size(); p++)
    {
        const dyn_object& DynObject=DynObjects[p];
        string P=Ztring(__T("Object")+Ztring::ToZtring(p)).To_UTF8();
 
        // From OAMD
        Ztring Position_Cartesian;
        string Position_Polar;
        string ChannelLayout;
        if (Bed_Object_Count<ObjectElements.size() && p<ObjectElements.size()-Bed_Object_Count && !ObjectElements[Bed_Object_Count+p].Alts.empty())
        {
            string A=P;
            const dyn_object::dyn_object_alt& DynObject_Current=ObjectElements[Bed_Object_Count+p].Alts[0];
            if (DynObject_Current.pos3d_x_bits!=(int8u)-1)
            {
                int x_32k=mgi_bitstream_val_to_Q15(DynObject_Current.pos3d_x_bits, 6);
                int y_32k=mgi_bitstream_val_to_Q15(DynObject_Current.pos3d_y_bits, 6);
                int z_32k=mgi_bitstream_pos_z_to_Q15(DynObject_Current.pos3d_z_sig, DynObject_Current.pos3d_z_bits);
                Position_Cartesian=__T("x=")+Ztring::ToZtring((((float)x_32k)*2-32768)/32768, 1)+__T(" y=")+Ztring::ToZtring((((float)y_32k)*2-32768)/32768, 1)+__T(" z=")+Ztring::ToZtring(((float)z_32k)/32768, 1);
                angles Angles=mgi_bitstream_pos_ToAngles(x_32k, y_32k, z_32k);
                Position_Polar=Angles2String(Angles);
                ChannelLayout=Angles2KnownChannelName(Angles);
                if (ChannelLayout==Position_Polar)
                    ChannelLayout.clear();
            }
        }
 
        string Summary;
        if (Summary.empty())
            Summary=sound_category_Values[DynObject.sound_category];
        /*
        if (Summary.empty())
            Summary=ChannelLayout;
        if (Summary.empty())
            Summary=Position_Polar;
        */
        if (Summary.empty())
            Summary="Yes";
 
        Fill(Stream_Audio, 0, P.c_str(), Summary);
 
        if (sound_category_Values[DynObject.sound_category][0])
        {
            Fill(Stream_Audio, 0, (P+" SoundCategory").c_str(), sound_category_Values[DynObject.sound_category]);
            Fill_SetOptions(Stream_Audio, 0, (P+" SoundCategory").c_str(), "Y NTY");
        }
 
        // From OAMD
        if (Bed_Object_Count<ObjectElements.size() && p<ObjectElements.size()-Bed_Object_Count && !ObjectElements[Bed_Object_Count+p].Alts.empty())
        {
            string A=P;
            const dyn_object::dyn_object_alt& DynObject_Current=ObjectElements[Bed_Object_Count+p].Alts[0];
            /*
            if (!ChannelLayout.empty())
            {
                Fill(Stream_Audio, 0, (A+" ChannelLayout").c_str(), ChannelLayout);
                Fill_SetOptions(Stream_Audio, 0, (A+" ChannelLayout").c_str(), "Y NTY");
            }
            */
            if (!Position_Polar.empty())
            {
                Fill(Stream_Audio, 0, (A+" Position_Polar").c_str(), Position_Polar);
                Fill_SetOptions(Stream_Audio, 0, (A+" Position_Polar").c_str(), "Y NTY");
            }
            if (!Position_Cartesian.empty())
            {
                Fill(Stream_Audio, 0, (A+" Position_Cartesian").c_str(), Position_Cartesian);
                Fill_SetOptions(Stream_Audio, 0, (A+" Position_Cartesian").c_str(), "Y NTY");
            }
            if (DynObject_Current.obj_gain_db!=INT8_MAX)
            {
                if (DynObject_Current.obj_gain_db==INT8_MIN)
                    Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), string("-infinite"), " dB");
                else
                    Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), DynObject_Current.obj_gain_db, " dB");
            }
        }
 
        if (!DynObject.Alts.empty())
        {
            Fill(Stream_Audio, 0, (P+" NumberOfAlts").c_str(), DynObject.Alts.size());
            Fill_SetOptions(Stream_Audio, 0, (P+" NumberOfAlts").c_str(), "N NIY");
        }
        for (size_t a=0; a<DynObject.Alts.size(); a++)
        {
            const dyn_object::dyn_object_alt& DynObject_Current=DynObject.Alts[a];
            Ztring Position_Cartesian;
            string Position_Polar;
            string ChannelLayout;
            if (DynObject_Current.pos3d_x_bits!=(int8u)-1)
            {
                int x_32k=mgi_bitstream_val_to_Q15(DynObject_Current.pos3d_x_bits, 6);
                int y_32k=mgi_bitstream_val_to_Q15(DynObject_Current.pos3d_y_bits, 6);
                int z_32k=mgi_bitstream_pos_z_to_Q15(DynObject_Current.pos3d_z_sig, DynObject_Current.pos3d_z_bits);
                Position_Cartesian=__T("x=")+Ztring::ToZtring((((float)x_32k)*2-32768)/32768, 1)+__T(" y=")+Ztring::ToZtring((((float)y_32k)*2-32768)/32768, 1)+__T(" z=")+Ztring::ToZtring(((float)z_32k)/32768, 1);
                angles Angles=mgi_bitstream_pos_ToAngles(x_32k, y_32k, z_32k);
                Position_Polar=Angles2String(Angles);
                ChannelLayout=Angles2KnownChannelName(Angles);
                if (ChannelLayout==Position_Polar)
                    ChannelLayout.clear();
            }
            string Summary2;
            if (Summary2.empty())
                Summary2=sound_category_Values[DynObject.sound_category];
            /*
            if (Summary2.empty())
                Summary2=ChannelLayout;
            if (Summary2.empty())
                Summary2=Position_Polar;
            */
            if (Summary2.empty())
                Summary2="Yes";
 
            string A=P+Ztring(__T(" Alt")+Ztring::ToZtring(a)).To_UTF8();
            Fill(Stream_Audio, 0, A.c_str(), Summary2);
            /*
            if (!ChannelLayout.empty())
            {
                Fill(Stream_Audio, 0, (A+" ChannelLayout").c_str(), ChannelLayout);
                Fill_SetOptions(Stream_Audio, 0, (A+" ChannelLayout").c_str(), "Y NTY");
            }
            */
            if (!Position_Polar.empty())
            {
                Fill(Stream_Audio, 0, (A+" Position_Polar").c_str(), Position_Polar);
                Fill_SetOptions(Stream_Audio, 0, (A+" Position_Polar").c_str(), "Y NTY");
            }
            if (!Position_Cartesian.empty())
            {
                Fill(Stream_Audio, 0, (A+" Position_Cartesian").c_str(), Position_Cartesian);
                Fill_SetOptions(Stream_Audio, 0, (A+" Position_Cartesian").c_str(), "Y NTY");
            }
            if (DynObject_Current.obj_gain_db!=INT8_MAX)
            {
                if (DynObject_Current.obj_gain_db==INT8_MIN)
                    Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), string("-infinite"), " dB");
                else
                    Fill_Measure(Stream_Audio, 0, (A+" Gain").c_str(), DynObject_Current.obj_gain_db, " dB");
            }
            if (DynObject_Current.hp_render_mode!=(int8u)-1)
            {
                Fill(Stream_Audio, 0, (A+" RenderMode").c_str(), hp_render_mode_Values[DynObject_Current.hp_render_mode]);
                Fill_SetOptions(Stream_Audio, 0, (P + " RenderMode").c_str(), "Y NTY");
            }
        }
 
        const substream_mapping& S=substream_mappings[p];
        {
            Fill(Stream_Audio, 0, (P+" SubtstreamIdChannel").c_str(), Ztring::ToZtring(S.substream_id)+__T('-')+Ztring::ToZtring(S.channel_index));
            Fill_SetOptions(Stream_Audio, 0, (P+" SubtstreamIdChannel").c_str(), "Y NTY");
        }
    }
}
 
//---------------------------------------------------------------------------
void File_DolbyE::Streams_Finish()
{
    if (FrameInfo.PTS!=(int64u)-1 && FrameInfo.PTS>PTS_Begin)
    {
        int64s Duration=float64_int64s(((float64)(FrameInfo.PTS-PTS_Begin))/1000000);
        int64s FrameCount;
        if (Mpegv_frame_rate[frame_rate_code])
            FrameCount=float64_int64s(((float64)(FrameInfo.PTS-PTS_Begin))/1000000000*Mpegv_frame_rate[frame_rate_code]);
        else
            FrameCount=0;
 
        for (size_t Pos=0; Pos<Count_Get(Stream_Audio); Pos++)
        {
            Fill(Stream_Audio, Pos, Audio_Duration, Duration);
            if (FrameCount)
                Fill(Stream_Audio, Pos, Audio_FrameCount, FrameCount);
        }
    }
}
 
//***************************************************************************
// Buffer - Synchro
//***************************************************************************
 
//---------------------------------------------------------------------------
bool File_DolbyE::Synchronize()
{
    //Synchronizing
    while (Buffer_Offset+3<=Buffer_Size)
    {
        if ((CC2(Buffer+Buffer_Offset_Temp)&0xFFFE)==0x078E) //16-bit
        {
            bit_depth=16;
            key_present=(CC2(Buffer+Buffer_Offset)&0x0001)?true:false;
            break; //while()
        }
        if ((CC3(Buffer+Buffer_Offset)&0xFFFFE0)==0x0788E0) //20-bit
        {
            bit_depth=20;
            key_present=(CC3(Buffer+Buffer_Offset)&0x000010)?true:false;
            break; //while()
        }
        if ((CC3(Buffer+Buffer_Offset)&0xFFFFFE)==0x07888E) //24-bit
        {
            bit_depth=24;
            key_present=(CC3(Buffer+Buffer_Offset)&0x000001)?true:false;
            break; //while()
        }
        Buffer_Offset++;
    }
 
    //Parsing last bytes if needed
    if (Buffer_Offset+3>Buffer_Size)
        return false;
 
    //Synched
    return true;
}
 
//---------------------------------------------------------------------------
bool File_DolbyE::Synched_Test()
{
    //Must have enough buffer for having header
    if (Buffer_Offset+3>Buffer_Size)
        return false;
 
    //Quick test of synchro
    switch (bit_depth)
    {
        case 16 : if ((CC2(Buffer+Buffer_Offset)&0xFFFE  )!=0x078E  ) {Synched=false; return true;} break;
        case 20 : if ((CC3(Buffer+Buffer_Offset)&0xFFFFE0)!=0x0788E0) {Synched=false; return true;} break;
        case 24 : if ((CC3(Buffer+Buffer_Offset)&0xFFFFFE)!=0x07888E) {Synched=false; return true;} break;
        default : ;
    }
 
    //We continue
    return true;
}
 
//***************************************************************************
// Buffer - Global
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_DolbyE::Read_Buffer_Unsynched()
{
    description_text_Values.clear();
    num_desc_packets_m1=(int32u)-1;
    description_packet_data.clear();
}
 
//***************************************************************************
// Buffer - Per element
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_DolbyE::Header_Parse()
{
    //Filling
    if (IsSub)
        Header_Fill_Size(Buffer_Size-Buffer_Offset);
    else
    {
        //Looking for synchro
        //Synchronizing
        Buffer_Offset_Temp=Buffer_Offset+3;
        if (bit_depth==16)
            while (Buffer_Offset_Temp+2<=Buffer_Size)
            {
                if ((CC2(Buffer+Buffer_Offset_Temp)&0xFFFE)==0x078E) //16-bit
                    break; //while()
                Buffer_Offset_Temp++;
            }
        if (bit_depth==20)
            while (Buffer_Offset_Temp+3<=Buffer_Size)
            {
                if ((CC3(Buffer+Buffer_Offset_Temp)&0xFFFFE0)==0x0788E0) //20-bit
                    break; //while()
                Buffer_Offset_Temp++;
            }
        if (bit_depth==24)
            while (Buffer_Offset_Temp+3<=Buffer_Size)
            {
                if ((CC3(Buffer+Buffer_Offset_Temp)&0xFFFFFE)==0x07888E) //24-bit
                    break; //while()
                Buffer_Offset_Temp++;
            }
 
        if (Buffer_Offset_Temp+(bit_depth>16?3:2)>Buffer_Size)
        {
            if (File_Offset+Buffer_Size==File_Size)
                Buffer_Offset_Temp=Buffer_Size;
            else
            {
                Element_WaitForMoreData();
                return;
            }
        }
 
        Header_Fill_Size(Buffer_Offset_Temp-Buffer_Offset);
    }
    Header_Fill_Code(0, "Dolby_E_frame");
}
 
//---------------------------------------------------------------------------
void File_DolbyE::Data_Parse()
{
    FrameSizes[Element_Size]++;
 
    //In case of scrambling
    const int8u*    Save_Buffer=NULL;
    size_t          Save_Buffer_Offset=0;
    int64u          Save_File_Offset=0;
    if (key_present)
    {
        //We must change the buffer,
        Save_Buffer=Buffer;
        Save_Buffer_Offset=Buffer_Offset;
        Save_File_Offset=File_Offset;
        File_Offset+=Buffer_Offset;
        Buffer_Offset=0;
        Descrambled_Buffer=new int8u[(size_t)Element_Size];
        std::memcpy(Descrambled_Buffer, Save_Buffer+Save_Buffer_Offset, (size_t)Element_Size);
        Buffer=Descrambled_Buffer;
    }
 
    //Parsing
    BS_Begin();
    sync_segment();
    metadata_segment();
    audio_segment();
    metadata_extension_segment();
    audio_extension_segment();
    meter_segment();
    BS_End();
 
    //Check if there is content in padding
    int16u Padding;
    if (Element_Size-Element_Offset>=2)
    {
        Peek_B2(Padding);
        if (Padding==0x5838)
            guard_band();
    }
 
    if (Element_Offset<Element_Size)
        Skip_XX(Element_Size-Element_Offset,                    "Unknown");
 
    //In case of scrambling
    if (key_present)
    {
        delete[] Buffer; Buffer=Save_Buffer;
        Buffer_Offset=Save_Buffer_Offset;
        File_Offset=Save_File_Offset;
    }
 
    FILLING_BEGIN();
        {
            //Guard band
            if (Mpegv_frame_rate[frame_rate_code])
            {
            int64u BytesPerSecond=96000*bit_depth/8;
            float64 BytesPerFrame=BytesPerSecond/Mpegv_frame_rate[frame_rate_code];
            int64u BytesUpToLastFrame;
            int64u BytesUpToNextFrame;
            for (;;)
            {
                BytesUpToLastFrame=(int64u)(BytesPerFrame*Frame_Count);
                BytesUpToLastFrame/=bit_depth/4;
                BytesUpToLastFrame*=bit_depth/4;
                BytesUpToNextFrame=(int64u)(BytesPerFrame*(Frame_Count+1));
                BytesUpToNextFrame/=bit_depth/4;
                BytesUpToNextFrame*=bit_depth/4;
 
                if (BytesUpToLastFrame+GuardBand_Before<BytesUpToNextFrame)
                    break;
 
                // In case previous frame was PCM
                Frame_Count++;
                GuardBand_Before-=BytesUpToNextFrame-BytesUpToLastFrame;
            }
            GuardBand_After=BytesUpToNextFrame-BytesUpToLastFrame;
            int64u ToRemove=GuardBand_Before+(bit_depth>>1)+Element_Size; // Guardband + AES3 header + Dolby E frame
            if (ToRemove<(int64u)GuardBand_After)
                GuardBand_After-=ToRemove;
            else
                GuardBand_After=0;
            GuardBand_After/=bit_depth/4;
            GuardBand_After*=bit_depth/4;
 
            Element_Info1(GuardBand_Before);
            float64 GuardBand_Before_Duration=((float64)GuardBand_Before)/BytesPerSecond;
            Ztring GuardBand_Before_String=__T("GuardBand_Begin ")+Ztring::ToZtring(GuardBand_Before)+__T(" (")+Ztring::ToZtring(GuardBand_Before_Duration*1000000, 0)+Ztring().From_UTF8(" \0xC20xB5s"); //0xC20xB5 = micro sign
            Element_Info1(GuardBand_Before_String);
            }
        }
 
        if (!Status[IsAccepted])
        {
            Accept("Dolby E");
            PTS_Begin=FrameInfo.PTS;
 
            //Guard band
            GuardBand_Before_Initial=GuardBand_Before;
            GuardBand_After_Initial=GuardBand_After;
        }
        Frame_Count++;
        if (Frame_Count_NotParsedIncluded!=(int64u)-1)
            Frame_Count_NotParsedIncluded++;
        if (Mpegv_frame_rate[frame_rate_code])
            FrameInfo.DUR=float64_int64s(1000000000/Mpegv_frame_rate[frame_rate_code]);
        else
            FrameInfo.DUR=(int64u)-1;
        if (FrameInfo.DTS!=(int64u)-1)
            FrameInfo.DTS+=FrameInfo.DUR;
        if (FrameInfo.PTS!=(int64u)-1)
            FrameInfo.PTS+=FrameInfo.DUR;
        if (!Status[IsFilled] && ((description_packet_data.empty() && description_text_Values.empty()) || Frame_Count>=32+1+1+32+1)) // max 32 chars (discarded) + ETX (discarded) + STX + max 32 chars + ETX
            Fill("Dolby E");
    FILLING_END();
    if (Frame_Count==0 && Buffer_TotalBytes>Buffer_TotalBytes_FirstSynched_Max)
        Reject("Dolby E");
}
 
//---------------------------------------------------------------------------
void File_DolbyE::sync_segment()
{
    //Parsing
    Element_Begin1("sync_segment");
    Skip_S3(bit_depth,                                      "sync_word");
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::metadata_segment()
{
    //Parsing
    Element_Begin1("metadata_segment");
    if (key_present)
    {
        //We must change the buffer
        switch (bit_depth)
        {
            case 16 :
                        {
                        int16u metadata_key;
                        Get_S2 (16, metadata_key, "metadata_key");
                        int16u metadata_segment_size=((BigEndian2int16u(Buffer+Buffer_Offset+(size_t)Element_Size-Data_BS_Remain()/8)^metadata_key)>>2)&0x3FF;
 
                        if (Data_BS_Remain()<((size_t)metadata_segment_size+1)*(size_t)bit_depth) //+1 for CRC
                            return; //There is a problem
 
                        int8u* Temp=Descrambled_Buffer+(size_t)Element_Size-Data_BS_Remain()/8;
                        for (int16u Pos=0; Pos<metadata_segment_size+1; Pos++)
                            int16u2BigEndian(Temp+Pos*2, BigEndian2int16u(Temp+Pos*2)^metadata_key);
                        }
                        break;
            case 20 :
                        {
                        int32u metadata_key;
                        Get_S3 (20, metadata_key, "metadata_key");
                        int16u metadata_segment_size=((BigEndian2int16u(Buffer+Buffer_Offset+(size_t)Element_Size-Data_BS_Remain()/8)^(metadata_key>>4))>>2)&0x3FF;
 
                        if (Data_BS_Remain()<((size_t)metadata_segment_size+1)*(size_t)bit_depth) //+1 for CRC
                            return; //There is a problem
 
                        Descramble_20bit(metadata_key, metadata_segment_size);
                        }
                        break;
            case 24 :
                        {
                        int32u metadata_key;
                        Get_S3 (24, metadata_key, "metadata_key");
                        int32u metadata_segment_size=((BigEndian2int16u(Buffer+Buffer_Offset+(size_t)Element_Size-Data_BS_Remain()/8)^metadata_key)>>2)&0x3FF;
 
                        if (Data_BS_Remain()<((size_t)metadata_segment_size+1)*bit_depth) //+1 for CRC
                            return; //There is a problem
 
                        int8u* Temp=Descrambled_Buffer+(size_t)Element_Size-Data_BS_Remain()/8;
                        for (int16u Pos=0; Pos<metadata_segment_size+1; Pos++)
                            int24u2BigEndian(Temp+Pos*2, BigEndian2int24u(Temp+Pos*2)^metadata_key);
                        }
                        break;
            default :   ;
        }
    }
    size_t  metadata_segment_BitCountAfter=Data_BS_Remain();
    int16u  metadata_segment_size;
    Skip_S1( 4,                                                 "metadata_revision_id");
    Get_S2 (10, metadata_segment_size,                          "metadata_segment_size");
    metadata_segment_BitCountAfter-=metadata_segment_size*bit_depth;
    Get_S1 ( 6, program_config,                                 "program_config"); Param_Info1(DolbyE_ChannelPositions[program_config]);
    Get_S1 ( 4, frame_rate_code,                                "frame_rate_code"); Param_Info3(Mpegv_frame_rate[frame_rate_code], " fps", 3);
    Info_S1( 4, original_frame_rate_code,                       "original_frame_rate_code"); Param_Info3(Mpegv_frame_rate[original_frame_rate_code], " fps", 3);
    Skip_S2(16,                                                 "frame_count");
    Element_Begin1("SMPTE_time_code");
    int8u Frames_Units, Frames_Tens, Seconds_Units, Seconds_Tens, Minutes_Units, Minutes_Tens, Hours_Units, Hours_Tens;
    bool  DropFrame;
 
    Skip_S1(4,                                                  "BG8");
    Skip_S1(4,                                                  "BG7");
 
    Skip_SB(                                                    "BGF2 / Field Phase");
    Skip_SB(                                                    "BGF1");
    Get_S1 (2, Hours_Tens,                                      "Hours (Tens)");
    Get_S1 (4, Hours_Units,                                     "Hours (Units)");
 
    Skip_S1(4,                                                  "BG6");
    Skip_S1(4,                                                  "BG5");
 
    Skip_SB(                                                    "BGF0 / BGF2");
    Get_S1 (3, Minutes_Tens,                                    "Minutes (Tens)");
    Get_S1 (4, Minutes_Units,                                   "Minutes (Units)");
 
    Skip_S1(4,                                                  "BG4");
    Skip_S1(4,                                                  "BG3");
 
    Skip_SB(                                                    "FP - Field Phase / BGF0");
    Get_S1 (3, Seconds_Tens,                                    "Seconds (Tens)");
    Get_S1 (4, Seconds_Units,                                   "Seconds (Units)");
 
    Skip_S1(4,                                                  "BG2");
    Skip_S1(4,                                                  "BG1");
 
    Skip_SB(                                                    "CF - Color fame");
    Get_SB (   DropFrame,                                       "DP - Drop frame");
    Get_S1 (2, Frames_Tens,                                     "Frames (Tens)");
    Get_S1 (4, Frames_Units,                                    "Frames (Units)");
 
    if (Hours_Tens<3)
    {
        int64u TimeCode=(int64u)(Hours_Tens     *10*60*60*1000
                               + Hours_Units       *60*60*1000
                               + Minutes_Tens      *10*60*1000
                               + Minutes_Units        *60*1000
                               + Seconds_Tens         *10*1000
                               + Seconds_Units           *1000
                               + (Mpegv_frame_rate[frame_rate_code]?float64_int32s((Frames_Tens*10+Frames_Units)*1000/Mpegv_frame_rate[frame_rate_code]):0));
 
        Element_Info1(Ztring().Duration_From_Milliseconds(TimeCode));
 
        //TimeCode
        if (SMPTE_time_code_StartTimecode==(int64u)-1)
            SMPTE_time_code_StartTimecode=TimeCode;
    }
    Element_End0();
    bool evolution_data_exists;
    Get_SB (    evolution_data_exists,                          "evolution_data_exists");
    Skip_S1( 7,                                                 "metadata_reserved_bits");
    for (int8u Channel=0; Channel<DolbyE_Channels[program_config]; Channel++)
    {
        Get_S2 (10, channel_subsegment_size[Channel],           "channel_subsegment_size");
        channel_subsegment_sizes[Channel][channel_subsegment_size[Channel]]++;
    }
    if (!Mpegv_frame_rate_type[frame_rate_code])
        Get_S1 ( 8, metadata_extension_segment_size,            "metadata_extension_segment_size");
    else
        metadata_extension_segment_size=0;
    Get_S1 ( 8, meter_segment_size,                             "meter_segment_size");
    for (int8u Program=0; Program<DolbyE_Programs[program_config]; Program++)
    {
        Element_Begin1("per program");
        int8u description_text;
        Get_S1 ( 8, description_text,                           "description_text"); Element_Info1(description_text);
        Info_S1( 2, bandwidth_id,                               "bandwidth_id"); Element_Info1(bandwidth_id);
        if (description_text && Program>=description_text_Values.size())
            description_text_Values.resize(Program+1);
        switch (description_text)
        {
            case 0x00: // No text
                    if (Program<description_text_Values.size())
                    {
                        description_text_Values[Program].Previous.clear();
                        description_text_Values[Program].Current.clear();
                    }
                    break;
            case 0x02: // STX
                    description_text_Values[Program].Current.clear();
                    description_text_Values[Program].Current.push_back(description_text);
                    break;
            case 0x03: // ETX
                    if (!description_text_Values[Program].Current.empty() && description_text_Values[Program].Current[0]==0x02)
                        description_text_Values[Program].Previous= description_text_Values[Program].Current.substr(1);
                    else
                        description_text_Values[Program].Previous.clear();
                    description_text_Values[Program].Current.clear();
                    break;
            default: if (description_text>=0x20 && description_text<=0x7E)
                        description_text_Values[Program].Current.push_back(description_text);
        }
        Element_End0();
    }
    for (int8u Channel=0; Channel<DolbyE_Channels[program_config]; Channel++)
    {
        Element_Begin1("per channel");
        Info_S1( 4, revision_id,                                "revision_id"); Element_Info1(revision_id);
        Info_SB(    bitpool_type,                               "bitpool_type");
        Info_S2(10, begin_gain,                                 "begin_gain"); Element_Info1(begin_gain);
        Info_S2(10, end_gain,                                   "end_gain"); Element_Info1(end_gain);
        Element_End0();
    }
    for(;;)
    {
        Element_Begin1("metadata_subsegment");
        int16u  metadata_subsegment_length;
        int8u   metadata_subsegment_id;
        Get_S1 ( 4, metadata_subsegment_id,                     "metadata_subsegment_id");
        if (metadata_subsegment_id==0)
        {
            Element_End0();
            break;
        }
        Get_S2 (12, metadata_subsegment_length,                 "metadata_subsegment_length");
        size_t End=Data_BS_Remain()-metadata_subsegment_length;
        switch (metadata_subsegment_id)
        {
            case 1 : ac3_metadata_subsegment(true); break;
            case 2 : ac3_metadata_subsegment(false); break;
            default: Skip_BS(metadata_subsegment_length,        "metadata_subsegment (unknown)");
        }
        if (Data_BS_Remain()>End)
            Skip_BS(Data_BS_Remain()-End,                       "unknown");
        Element_End0();
    }
    if (evolution_data_exists)
    {
        for (;;)
        {
            const size_t evolution_data_segment_id_Name_Size=2;
            const char* const evolution_data_segment_id_Name[evolution_data_segment_id_Name_Size]=
            {
                "End",
                "intelligent_loudness_evolution_data_segment",
            };
 
            Element_Begin1("evolution_data_segment");
            int16u evolution_data_segment_length;
            int8u evolution_data_segment_id;
            Get_S1 ( 4, evolution_data_segment_id,              "evolution_data_segment_id"); Param_Info1C(evolution_data_segment_id<evolution_data_segment_id_Name_Size, evolution_data_segment_id_Name[evolution_data_segment_id]);
            if (!evolution_data_segment_id)
            {
                Element_End0();
                break;
            }
            Get_S2 (12, evolution_data_segment_length,          "evolution_data_segment_length");
            size_t End=Data_BS_Remain()-evolution_data_segment_length;
            switch (evolution_data_segment_id)
            {
                case  1: intelligent_loudness_evolution_data_segment(); break;
                default: Skip_BS(evolution_data_segment_length, "evolution_data_segment (unknown)");
            }
            if (Data_BS_Remain()>End)
                Skip_BS(Data_BS_Remain()-End,                   "unknown");
            Element_End0();
        }
    }
    if (Data_BS_Remain()>metadata_segment_BitCountAfter)
        Skip_BS(Data_BS_Remain()-metadata_segment_BitCountAfter,"reserved_metadata_bits");
    Skip_S3(bit_depth,                                          "metadata_crc");
 
    {
        //CRC test
        size_t Pos_End=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
        size_t Pos_Begin=Pos_End-(metadata_segment_size+1)*bit_depth; //+1 for CRC
        int8u BitSkip_Begin=Pos_Begin%8;
        Pos_Begin/=8;
        int8u BitSkip_End=0; // Pos_End%8; Looks like that the last bits must not be in the CRC computing
        Pos_End/=8;
        if (BitSkip_End)
            Pos_End++;
 
        int16u CRC=CRC_16_Compute(Buffer+Pos_Begin, Pos_End-Pos_Begin, BitSkip_Begin, BitSkip_End);
        if (CRC)
        {
            // CRC is wrong
            Param_Info1("metadata_crc NOK");
        }
    }
 
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::guard_band()
{
    int8u* NewBuffer=NULL;
    size_t Buffer_Offset_Save;
    size_t Buffer_Size_Save;
    int64u Element_Offset_Save;
    int64u Element_Size_Save;
 
    Element_Begin1("guard_band (with data)");
    int16u element_length;
    int8u element_id;
    bool escape_code_valid;
    Skip_B2(                                                    "sync_word");
    BS_Begin();
    Skip_S1( 3,                                                 "reserved");
    Get_SB (    escape_code_valid,                              "escape_code_valid");
    if (escape_code_valid)
    {
        int16u escape_code;
        Get_S2 (12, escape_code,                                 "escape_code");
        BS_End();
        for (int64u i=Element_Offset; i+1<Element_Size; i++)
        {
            if ( Buffer[Buffer_Offset+i  ]     ==(escape_code>>4) && (Buffer[Buffer_Offset+i+1]>>4)==(escape_code&0x0F))
            {
                //0xABCD --> 0x078D
                if (!NewBuffer)
                {
                    NewBuffer=new int8u[Element_Size-Element_Offset];
                    memcpy(NewBuffer, Buffer+Buffer_Offset+Element_Offset, Element_Size-Element_Offset);
                }
                NewBuffer[i  -Element_Offset]=0x07;
                NewBuffer[i+1-Element_Offset]=(NewBuffer[i+1-Element_Offset]&0x0F)|0x80;
            }
            if ((Buffer[Buffer_Offset+i  ]&0xF)==(escape_code>>8) &&  Buffer[Buffer_Offset+i+1]    ==(escape_code&0xFF))
            {
                //0xABCD --> 0xA078
                if (!NewBuffer)
                {
                    NewBuffer=new int8u[Element_Size-Element_Offset];
                    memcpy(NewBuffer, Buffer+Buffer_Offset+Element_Offset, Element_Size-Element_Offset);
                }
                NewBuffer[i  -Element_Offset]=(NewBuffer[i  -Element_Offset]&0xF0);
                NewBuffer[i+1-Element_Offset]=0x78;
            }
        }
        if (NewBuffer)
        {
            Buffer=NewBuffer;
            Buffer_Offset_Save=Buffer_Offset;
            Buffer_Size_Save=Buffer_Offset;
            Element_Offset_Save=Element_Offset;
            Element_Size_Save=Element_Size;
            File_Offset+=Buffer_Offset+Element_Offset;
            Buffer_Offset=0;
            Buffer_Size=Element_Size-Element_Offset;
            Element_Offset=0;
            Element_Size=Buffer_Size;
        }
    }
    else
    {
        Skip_S2(12,                                             "escape_code");
        BS_End();
    }
    Get_B1 (    element_id,                                     "element_id");
    Get_B2 (    element_length,                                 "element_length");
    int64u After=Element_Offset+element_length;
    switch (element_id)
    {
        case 0xBB: evo_frame(); break;
        default: Skip_XX(element_length,                        "Unknown");
    }
    if (Element_Offset<After)
        Skip_XX(After-Element_Offset,                           "Unknown");
    else if (Element_Offset>After)
    {
        Param_Info1("Problem");
        Element_Offset=After;
    }
    Skip_B2(                                                    "crc");
    {
        //CRC test
        size_t Pos_End=Buffer_Offset+Element_Offset;
        size_t Pos_Begin=Pos_End-(element_length+2); //+2 for CRC
 
        int16u CRC=CRC_16_Compute(Buffer+Pos_Begin, Pos_End-Pos_Begin, 0, 0);
        if (CRC)
        {
            // CRC is wrong
            Param_Info1("crc NOK");
        }
        Element_End0();
        int64u RemainingBytes=Element_Size-Element_Offset;
        if (RemainingBytes && RemainingBytes<bit_depth/4)
        {
            bool HasContent=false;
            size_t Offset=Buffer_Offset+(size_t)Element_Offset;
            size_t Size=Buffer_Offset+(size_t)Element_Size;
            for (; Offset<Size; Offset++)
                if (Buffer[Offset])
                    HasContent=true;
            if (!HasContent)
                Skip_XX(Element_Size-Element_Offset,            "Padding");
        }
    }
 
    if (NewBuffer)
    {
        delete[] Buffer;
        Buffer_Offset=Buffer_Offset_Save;
        Buffer_Size=Buffer_Offset_Save;
        Element_Offset=Element_Offset_Save;
        Element_Size=Element_Size_Save;
        File_Offset-=Buffer_Offset+Element_Offset;
    }
}
 
//---------------------------------------------------------------------------
void File_DolbyE::intelligent_loudness_evolution_data_segment()
{
    Element_Begin1("intelligent_loudness_evolution_data_segment");
    for (int8u program=0; program<DolbyE_Programs[program_config]; program++)
    {
        Element_Begin1("per program");
        Skip_S1(4,                                          "loudness_reg_type");
        Skip_SB(                                            "dialogue_corrected");
        Skip_S1(1,                                          "loudness_corr_type");
        Element_End0();
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
extern const char* Ac3_emdf_payload_id[16];
void File_DolbyE::evo_frame()
{
    if (!Guardband_EMDF_PresentAndSize)
        Guardband_EMDF_PresentAndSize=Element_Size;
 
    Element_Begin1("evo_frame");
    BS_Begin();
    int8u evo_version, key_id;
    Get_S1 (2, evo_version,                                     "evo_version");
    if (evo_version==3)
    {
        int32u evo_version32;
        Get_V4 (2, evo_version32,                               "evo_version");
        evo_version32+=3;
        evo_version=(int8u)evo_version32;
    }
    if (evo_version)
    {
        Skip_BS(Data_BS_Remain(),                               "(Unparsed evo_frame data)");
        Element_End0(); 
        return;
    }
    Get_S1 (3, key_id,                                          "key_id");
    if (key_id==7)
        Skip_V4(3,                                              "key_id");
 
    int32u payload_id = 0;
        
    for(;;)
    {
        Element_Begin1("evo_payload");
        Get_S4 (5, payload_id,                                  "payload_id");
        if (payload_id==0x1F)
        {
            int32u add;
            Get_V4 (5, add,                                     "payload_id");
            payload_id += add;
        }
 
        if (payload_id<16)
            Element_Info1(Ac3_emdf_payload_id[payload_id]);
        if (!payload_id)
        {
            Element_End0();
            break;
        }
 
        evo_payload_config();
 
        int32u payload_size = 0;
        Get_V4 (8, payload_size,                                "payload_size");
        size_t payload_End=(Data_BS_Remain()>payload_size*8)?(Data_BS_Remain()-payload_size*8):0;
 
        Element_Begin1("payload");
            switch (payload_id)
            {
                case 11: object_audio_metadata_payload(); break;
                case 13: mgi_payload(); break;
                default: Skip_BS(payload_size*8,                "(Unknown)");
            }
            size_t Remaining=Data_BS_Remain()-payload_End;
            if (Remaining && Remaining<8)
            {
                int8u padding;
                Peek_S1(Remaining, padding);
                if (!padding)
                    Skip_S1(Remaining,                          "padding");
            }
            if (Data_BS_Remain()>payload_End)
            {
                Skip_BS(Data_BS_Remain()-payload_End,           "(Unparsed payload bytes)");
            }
            else if (Data_BS_Remain()<payload_End)
            {
                //There is a problem, too many bits were consumed by the parser. //TODO: prevent the parser to consume more bits than count of bits in this element
                if (Data_BS_Remain()>=payload_End)
                    Skip_BS(Data_BS_Remain()-payload_End,       "(Problem during emdf_payload parsing)");
                else
                    Skip_BS(Data_BS_Remain(),                   "(Problem during payload parsing, going to end directly)");
                Element_End0();
                Element_End0();
                break;
            }
        Element_End0();
        Element_End0();
    }
 
    evo_protection();
    BS_End();
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::evo_payload_config()
{
    Element_Begin1("payload_config");
    bool timestamp_present;
    TEST_SB_GET (timestamp_present,                             "timestamp_present");
        Skip_V4(11,                                             "timestamp");
    TEST_SB_END();
    TEST_SB_SKIP(                                               "duration_present");
        Skip_V4(11,                                             "duration");
    TEST_SB_END();
    TEST_SB_SKIP(                                               "group_id_present");
        Skip_V4(2,                                              "group_id");
    TEST_SB_END();
    TEST_SB_SKIP(                                               "codec_specific_id_present");
        Skip_S1(8,                                              "codec_specific_id");
    TEST_SB_END();
 
    bool dont_transcode;
    Get_SB(dont_transcode,                                      "dont_transcode");
    if (!dont_transcode)
    {
        bool now_or_never = false;
        if (!timestamp_present)
        {
            Get_SB (now_or_never,                               "now_or_never");
            if (now_or_never)
            {
                Skip_SB(                                        "create_duplicate");
                Skip_SB(                                        "remove_duplicate");
 
            }
        }
 
        if (timestamp_present || now_or_never)
        {
            Skip_S1(5,                                          "priority");
            Skip_S1(2,                                          "tight_coupling");
        }
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::evo_protection()
{
    Element_Begin1("protection");
    int8u len_primary, len_second;
    Get_S1(2, len_primary,                                      "protection_length_primary");
    Get_S1(2, len_second,                                       "protection_length_secondary");
 
    switch (len_primary)
    {
        case 0: len_primary = 0; break;
        case 1: len_primary = 8; break;
        case 2: len_primary = 32; break;
        case 3: len_primary = 128; break;
        default:; //Cannot append, read only 2 bits
    };
    switch (len_second)
    {
        case 0: len_second = 0; break;
        case 1: len_second = 8; break;
        case 2: len_second = 32; break;
        case 3: len_second = 128; break;
        default:; //Cannot append, read only 2 bits
    };
    Skip_BS(len_primary,                                        "protection_bits_primary");
    if (len_second)
        Skip_BS(len_primary,                                    "protection_bits_secondary");
 
    Element_End0();
}
 
 
//---------------------------------------------------------------------------
void File_DolbyE::object_audio_metadata_payload()
{
    nonstd_bed_channel_assignment_masks.clear();
    ObjectElements.clear();
 
    Element_Begin1("object_audio_metadata_payload");
    int8u oa_md_version_bits;
    Get_S1 (2, oa_md_version_bits,                              "oa_md_version_bits");
    if (oa_md_version_bits == 0x3)
    {
        int8u oa_md_version_bits_ext;
        Get_S1 (3, oa_md_version_bits_ext,                      "oa_md_version_bits_ext");
        oa_md_version_bits += oa_md_version_bits_ext;
    }
 
    int8u object_count_bits;
    Get_S1 (5, object_count_bits,                               "object_count_bits");
    if (object_count_bits==0x1F)
    {
        int8u object_count_bits_ext;
        Get_S1 (7, object_count_bits_ext,                       "object_count_bits_ext");
        object_count_bits=0x1F+object_count_bits_ext;
    }
    object_count=object_count_bits+1;
    Param_Info2(object_count, " objects");
 
    program_assignment();
 
    bool b_alternate_object_data_present;
    Get_SB(b_alternate_object_data_present,                     "b_alternate_object_data_present");
    int8u oa_element_count_bits;
    Get_S1(4, oa_element_count_bits,                            "oa_element_count_bits");
    if (oa_element_count_bits==0xF)
    {
        Get_S1(5, oa_element_count_bits,                        "oa_element_count_bits_ext");
        oa_element_count_bits+=0xF;
    }
    for (int8u i=0; i<oa_element_count_bits; i++)
        oa_element_md(b_alternate_object_data_present);
 
    Element_End0();
}
 
void File_DolbyE::program_assignment()
{
    Element_Begin1("program_assignment");
    bool b_dyn_object_only_program = false;
    Get_SB (b_dyn_object_only_program,                          "b_dyn_object_only_program");
    if (b_dyn_object_only_program)
    {
        bool b_lfe_present;
        Get_SB (b_lfe_present,                                  "b_lfe_present");
        if (b_lfe_present)
        {
            nonstd_bed_channel_assignment_masks.push_back(1<<3);
            b_object_in_bed_or_isf.push_back(true);
        }
    }
    else
    {
        int8u content_description_mask;
        Get_S1 (4, content_description_mask,                    "content_description_mask");
        if (content_description_mask & 0x1)
        {
            bool b_bed_object_chan_distribute, b_multiple_bed_instances_present;
 
            Get_SB (b_bed_object_chan_distribute,               "b_bed_object_chan_distribute");
            Get_SB (b_multiple_bed_instances_present,           "b_multiple_bed_instances_present");
            int32u num_bed_instances = 1;
            if (b_multiple_bed_instances_present)
            {
                int8u num_bed_instances_bits = 0;
                Get_S1 (3, num_bed_instances_bits,              "num_bed_instances_bits");
                num_bed_instances = num_bed_instances_bits + 2;
            }
 
            for (int32u bed = 0; bed < num_bed_instances; ++bed)
            {
                Element_Begin1("Bed");
                bool b_lfe_only = true;
                Get_SB (b_lfe_only,                             "b_lfe_only");
                if (!b_lfe_only)
                {
                    bool b_standard_chan_assign;
                    Get_SB (b_standard_chan_assign,             "b_standard_chan_assign");
                    int32u nonstd_bed_channel_assignment_mask;
                    if (b_standard_chan_assign)
                    {
                        int16u bed_channel_assignment_mask;
                        Get_S2 (10, bed_channel_assignment_mask, "bed_channel_assignment_mask");
                        nonstd_bed_channel_assignment_mask=AC3_bed_channel_assignment_mask_2_nonstd(bed_channel_assignment_mask);
                    }
                    else
                    {
                        int32u nonstd_bed_channel_assignment_mask;
                        Get_S3 (17, nonstd_bed_channel_assignment_mask, "nonstd_bed_channel_assignment_mask");
                    }
                    Param_Info1(AC3_nonstd_bed_channel_assignment_mask_ChannelLayout(nonstd_bed_channel_assignment_mask));
                    nonstd_bed_channel_assignment_masks.push_back(nonstd_bed_channel_assignment_mask);
                    size_t BedChannelCount=BedChannelConfiguration_ChannelCount(nonstd_bed_channel_assignment_mask);
                    b_object_in_bed_or_isf.resize(b_object_in_bed_or_isf.size()+BedChannelCount, true);
                }
                else
                    b_object_in_bed_or_isf.push_back(true);
                Element_End0();
            }
        }
 
        if (content_description_mask & 0x2)
        {
            int8u intermediate_spatial_format_idx;
            Get_S1 (3, intermediate_spatial_format_idx,        "intermediate_spatial_format_idx");
            b_object_in_bed_or_isf.resize(b_object_in_bed_or_isf.size()+intermediate_spatial_format_object_count[intermediate_spatial_format_idx], true);
        }
 
        if (content_description_mask & 0x4)
        {
            int8u num_dynamic_objects_bits;
            Get_S1(5, num_dynamic_objects_bits,                 "num_dynamic_objects_bits");
            if (num_dynamic_objects_bits==0x1F)
            {
                int8u num_dynamic_objects_bits_ext = 0;
                Get_S1 (7, num_dynamic_objects_bits_ext,        "num_dynamic_objects_bits_ext");
                num_dynamic_objects_bits=0x1F+num_dynamic_objects_bits_ext;
            }
            num_dynamic_objects_bits++;
            Param_Info2(object_count-num_dynamic_objects_bits, " static objects");
            Param_Info2(num_dynamic_objects_bits, " dynamic objects");
            b_object_in_bed_or_isf.resize(b_object_in_bed_or_isf.size()+num_dynamic_objects_bits, false);
        }
 
        if (content_description_mask & 0x8)
        {
            int8u reserved_data_size_bits;
            Get_S1 (4, reserved_data_size_bits,                 "reserved_data_size_bits");
            int8u padding = 8 - (reserved_data_size_bits % 8);
            Skip_S1(reserved_data_size_bits,                    "reserved_data()");
            Skip_S1(padding,                                    "padding");
        }
    }
 
    Element_End0();
}
 
void File_DolbyE::oa_element_md(bool b_alternate_object_data_present)
{
    Element_Begin1("oa_element_md");
    int8u oa_element_id_idx;
    int32u oa_element_size_bits;
    Get_S1 (4, oa_element_id_idx,                               "oa_element_id_idx");
    Get_V4 (4, 4, oa_element_size_bits,                         "oa_element_size_bits");
    oa_element_size_bits++;
    oa_element_size_bits*=8;
    int32u b_alternate_object_data_present_Reduced=b_alternate_object_data_present*4+1;
    if (oa_element_size_bits<b_alternate_object_data_present_Reduced || oa_element_size_bits>Data_BS_Remain())
    {
        Skip_BS(oa_element_size_bits,                           "?");
        Element_End0();
        return;
    }
    oa_element_size_bits-=b_alternate_object_data_present_Reduced;
    if (b_alternate_object_data_present)
        Skip_S1(4,                                              "alternate_object_data_id_idx");
    Skip_SB(                                                    "b_discard_unknown_element");
    size_t End=Data_BS_Remain()-oa_element_size_bits;
    switch (oa_element_id_idx)
    {
        case  1: object_element(); break;
        default: Skip_BS(oa_element_size_bits,                  "oa_element");
    }
    if (Data_BS_Remain()>End)
        Skip_BS(Data_BS_Remain()-End,                           "padding");
    Element_End0();
}
 
void File_DolbyE::object_element()
{
    Element_Begin1("object_element");
    int8u num_obj_info_blocks_bits;
    md_update_info(num_obj_info_blocks_bits);
    bool b_reserved_data_not_present;
    Get_SB (b_reserved_data_not_present,                        "b_reserved_data_not_present");
    if (!b_reserved_data_not_present)
        Skip_S1(5,                                              "reserved");
    for (int8u i=0; i<object_count; i++)
        object_data(i, num_obj_info_blocks_bits);
    Element_End0();
}
 
void File_DolbyE::md_update_info(int8u& num_obj_info_blocks_bits)
{
    Element_Begin1("md_update_info");
    int8u sample_offset_code;
    Get_S1 (2, sample_offset_code,                              "sample_offset_code");
    switch (sample_offset_code)
    {
        case 1 : Skip_S1(2,                                     "sample_offset_idx"); break;
        case 2 : Skip_S1(5,                                     "sample_offset_bits"); break;
        default:;
    }
    Get_S1 (3, num_obj_info_blocks_bits,                        "num_obj_info_blocks_bits");
    for (int8u blk=0; blk<=num_obj_info_blocks_bits; blk++)
        block_update_info();
    Element_End0();
}
 
void File_DolbyE::block_update_info()
{
    Element_Begin1("block_update_info");
    int8u block_offset_factor_bits, ramp_duration_code;
    Get_S1 (6, block_offset_factor_bits,                        "block_offset_factor_bits");
    Get_S1 (2, ramp_duration_code,                              "ramp_duration_code");
    switch (ramp_duration_code)
    {
        case 3 : 
                {
                    bool b_use_ramp_duration_idx;
                    Get_SB (b_use_ramp_duration_idx,            "b_use_ramp_duration_idx");
                    if (b_use_ramp_duration_idx)
                        Skip_S1( 4,                             "ramp_duration_idx");
                    else
                        Skip_S1(11,                             "ramp_duration_bits");
                }
                break;
        default:;
    }
    Element_End0();
}
 
void File_DolbyE::object_data(int8u obj_idx, int8u num_obj_info_blocks_bits)
{
    ObjectElements.resize(ObjectElements.size()+1);
    ObjectElements[ObjectElements.size()-1].Alts.resize(num_obj_info_blocks_bits+1);
 
    Element_Begin1("object_data");
    for (int8u blk=0; blk<=num_obj_info_blocks_bits; blk++)
        object_info_block(obj_idx, blk);
    Element_End0();
}
 
void File_DolbyE::object_info_block(int8u obj_idx, int8u blk)
{
    Element_Begin1("object_info_block");
    int8u object_basic_info_status_idx, object_render_info_status_idx;
    bool b_object_not_active;
    Get_SB (b_object_not_active,                                "b_object_not_active");
    if (b_object_not_active)
        object_basic_info_status_idx=0;
    else if (!blk)
        object_basic_info_status_idx=1;
    else
        Get_S1 (2, object_basic_info_status_idx,                "object_basic_info_status_idx");
    if (object_basic_info_status_idx&1) // 1 or 3
        object_basic_info(object_basic_info_status_idx>>1, blk); // Use bit 1
    else
    {
        dyn_object& D=ObjectElements[ObjectElements.size()-1];
        dyn_object::dyn_object_alt& A=D.Alts[blk];
        A.obj_gain_db=INT8_MAX;
    }
    if (b_object_not_active || (obj_idx<b_object_in_bed_or_isf.size() && b_object_in_bed_or_isf[obj_idx]))
        object_render_info_status_idx=0;
    else if (!blk)
        object_render_info_status_idx=1;
    else
        Get_S1 (2, object_render_info_status_idx,               "object_render_info_status_idx");
    if (object_render_info_status_idx&1) // 1 or 3
        object_render_info(object_render_info_status_idx>>1, blk); // Use bit 1
    else
    {
        dyn_object& D=ObjectElements[ObjectElements.size()-1];
        dyn_object::dyn_object_alt& A=D.Alts[blk];
        A.pos3d_x_bits=(int8u)-1;
    }
    bool b_additional_table_data_exists;
    Get_SB (b_additional_table_data_exists,                     "b_additional_table_data_exists");
    if (b_additional_table_data_exists)
    {
        int8u additional_table_data_size_bits;
        Get_S1(4, additional_table_data_size_bits,              "additional_table_data_size_bits");
        additional_table_data_size_bits++;
        additional_table_data_size_bits*=8;
        Skip_BS(additional_table_data_size_bits,                "additional_table_data");
    }
    Element_End0();
}
 
void File_DolbyE::object_basic_info(int8u object_basic_info_array, int8u blk)
{
    Element_Begin1("object_basic_info");
    if (!object_basic_info_array) // object_basic_info_array is "reuse" info at this point
        object_basic_info_array=3; // 2x 1
    else
        Get_S1 (2, object_basic_info_array,                     "object_basic_info[]");
    dyn_object& D=ObjectElements[ObjectElements.size()-1];
    dyn_object::dyn_object_alt& A=D.Alts[blk];
    if (object_basic_info_array>>1) // bit 1
    {
        int8u object_gain_idx;
        Get_S1 (2, object_gain_idx,                             "object_gain_idx");
        switch (object_gain_idx)
        {
            case 0 :
                    A.obj_gain_db=0;
                    break;
            case 1 :
                    A.obj_gain_db=INT8_MIN;
                    break;
            case 2 :
                    {
                    int8u object_gain_bits;
                    Get_S1 (6, object_gain_bits,                "object_gain_bits");
                    A.obj_gain_db=(object_gain_bits<15?15:14)-object_gain_bits;
                    }
                    break;
            default:
                    if (ObjectElements.size()>=2)
                        A.obj_gain_db=ObjectElements[ObjectElements.size()-2].Alts[blk].obj_gain_db;
                    else
                        A.obj_gain_db=0;
        }
    }
    else
        A.obj_gain_db=INT8_MAX;
    if (object_basic_info_array&1) // bit 0
    {
        bool b_default_object_priority;
        Get_SB (   b_default_object_priority,                   "b_default_object_priority");
        if (!b_default_object_priority)
            Skip_S1(5,                                          "b_default_object_priority");
    }
    Element_End0();
}
 
void File_DolbyE::object_render_info(int8u object_render_info_array, int8u blk)
{
    Element_Begin1("object_render_info");
    if (!object_render_info_array) // object_render_info_array is "reuse" info at this point
        object_render_info_array=0xF; // 4x 1
    else
        Get_S1 (4, object_render_info_array,                    "object_render_info[]");
    dyn_object& D=ObjectElements[ObjectElements.size()-1];
    dyn_object::dyn_object_alt& A=D.Alts[blk];
    if (object_render_info_array&1) // bit 0
    {
        bool b_differential_position_specified;
        if (!blk)
            b_differential_position_specified=0;
        else
            Get_SB(b_differential_position_specified,           "b_differential_position_specified");
        if (b_differential_position_specified)
        {
            Skip_S1(3,                                          "diff_pos3D_X_bits");
            Skip_S1(3,                                          "diff_pos3D_Y_bits");
            Skip_S1(3,                                          "diff_pos3D_Z_bits");
            A.pos3d_x_bits=(int8u)-1; // Not supported
        }
        else
        {
            bool b_object_distance_specified;
            Get_S1 (6, A.pos3d_x_bits,                          "pos3d_x_bits"); Param_Info3(mgi_bitstream_val_to_Q15(A.pos3d_x_bits, 6)/32768.0*100, "%", 0);
            Get_S1 (6, A.pos3d_y_bits,                          "pos3d_y_bits"); Param_Info3(mgi_bitstream_val_to_Q15(A.pos3d_y_bits, 6)/32768.0*100, "%", 0);
            Get_SB (   A.pos3d_z_sig,                           "pos3d_z_sig");
            Get_S1 (4, A.pos3d_z_bits,                          "pos3d_z_bits"); Param_Info3(mgi_bitstream_pos_z_to_Q15(A.pos3d_z_sig, A.pos3d_z_bits)/32768.0*100, "%", 0);
            Get_SB (   b_object_distance_specified,             "b_object_distance_specified");
            if (b_object_distance_specified)
            {
                bool b_object_at_infinity;
                Get_SB (   b_object_at_infinity,                "b_object_at_infinity");
                if (!b_object_at_infinity)
                    Skip_S1(4,                                  "distance_factor_idx");
            }
        }
    }
    else
        A.pos3d_x_bits=(int8u)-1;
    A.hp_render_mode=(int8u)-1;
    if ((object_render_info_array>>1)&1) // bit 1
    {
        Skip_S1(3,                                              "zone_constraints_idx");
        Skip_SB(                                                "b_enable_elevation");
    }
    if ((object_render_info_array>>2)&1) // bit 2
    {
        int8u object_size_idx;
        Get_S1 (2, object_size_idx,                             "object_size_idx");
        switch (object_size_idx)
        {
            case  1:
                    {
                    Skip_S1(5,                                  "object_size_bits");
                    }
                    break;
            case  2:
                    {
                    Skip_S1(5,                                  "object_width_bits");
                    Skip_S1(5,                                  "object_depth_bits");
                    Skip_S1(5,                                  "object_height_bits");
                    }
                    break;
            default:;
        }
    }
    if (object_render_info_array>>3) // bit 3
    {
        bool b_object_use_screen_ref;
        Get_SB (   b_object_use_screen_ref,                     "b_object_use_screen_ref");
        if (b_object_use_screen_ref)
        {
            Skip_S1(3,                                          "screen_factor_bits");
            Skip_S1(2,                                          "depth_factor_idx");
        }
        Skip_SB(                                                "b_object_snap");
    }
    Element_End0();
}
 
int8u bits_needed(size_t value)
{
    if (!value)
        return 0;
 
    int8u res = 0;
    while (value) {
        res += 1;
        value >>= 1;
    }
    return res;
}
 
void File_DolbyE::mgi_payload()
{
    DynObjects.clear();
    BedInstances.clear();
    Presets.clear();
    substream_mappings.clear();
 
    Element_Begin1("mgi_payload");
    size_t BS_Start=Data_BS_Remain();
    int8u mgi_version, num_presets_m1, num_dyn_objects, num_bed_instances;
    bool program_id_available, is_substream_mapping_present, mini_mixgraph_extension_present, mini_mixgraph_description_present;
    Get_S1 (2, mgi_version,                                     "mgi_version");
    if (mgi_version ==3)
    {
        int32u mgi_version32;
        Get_V4 (2, mgi_version32,                               "mgi_version");
        mgi_version32+=3;
        mgi_version=(int8u)mgi_version32;
    }
    if (mgi_version!=5)
    {
        Skip_BS(1,                                              "(Unparsed mgi_payload data)"); //TODO: exact count of bits
        Element_End0();
        return;
    }
    Get_SB (program_id_available,                               "program_id_available");
    Get_SB (is_substream_mapping_present,                       "is_substream_mapping_present");
    Get_SB (mini_mixgraph_extension_present,                    "mini_mixgraph_extension_present");
    Get_SB (mini_mixgraph_description_present,                  "mini_mixgraph_description_present");
    if (program_id_available)
    {
        Skip_S1( 3,                                             "program_uuid_segment_number");
        Skip_S2(16,                                             "program_uuid_segment");
    }
    {
        size_t ToPadd=(BS_Start-Data_BS_Remain())%8;
        if (ToPadd<8)
            Skip_BS(8-ToPadd,                                   "byte_align");
    }
    Skip_S2(16,                                                 "short_program_id");
    Get_S1 (4, num_presets_m1,                                  "num_presets_m1");
    Get_S1 (7, num_dyn_objects,                                 "num_dyn_objects");
    Get_S1 (4, num_bed_instances,                               "num_bed_instances");
 
    DynObjects.resize(num_dyn_objects);
    for (int8u i=0; i<num_dyn_objects; i++)
    {
        dyn_object& D=DynObjects[i];
        Element_Begin1("dyn_object");
        int32u num_dyn_obj_alt_md;
        Get_V4 (2, num_dyn_obj_alt_md,                          "num_dyn_obj_alt_md");
        Get_S1 (2, D.sound_category,                            "sound_category"); Param_Info1(sound_category_Values[D.sound_category]);
        D.Alts.resize(num_dyn_obj_alt_md);
        for (int32u j=0; j<num_dyn_obj_alt_md; j++)
        {
            dyn_object::dyn_object_alt& A=D.Alts[j];
            Element_Begin1("dyn_obj_alt_md");
            int32u object_info_mask, object_info_size_bytes;
            Get_V4 (3, object_info_mask,                        "object_info_mask");
            //Get_V4 (3, object_info_size_bytes,                  "object_info_size_bytes"); //TODO
            Get_S4 (4, object_info_size_bytes,                  "object_info_size_bytes, variable_bits ignored");
            object_info_size_bytes=4;
            size_t Begin=Data_BS_Remain();
            if (object_info_size_bytes)
            {
                if (object_info_mask & 0x1)
                {
                    int8u dyn_obj_gain_db_bits;
                    Get_S1(6, dyn_obj_gain_db_bits,             "dyn_obj_gain_db_bits");
                    if (dyn_obj_gain_db_bits==63)
                        A.obj_gain_db=INT8_MIN;
                    else
                        A.obj_gain_db=15-dyn_obj_gain_db_bits;
                }
                else
                    A.obj_gain_db=INT8_MAX;
                if (object_info_mask & 0x2)
                {
                    Get_S1 (6, A.pos3d_x_bits,                  "pos3d_x_bits"); Param_Info3(mgi_bitstream_val_to_Q15(A.pos3d_x_bits, 6)/32768.0*100, "%", 0);
                    Get_S1 (6, A.pos3d_y_bits,                  "pos3d_y_bits"); Param_Info3(mgi_bitstream_val_to_Q15(A.pos3d_y_bits, 6)/32768.0*100, "%", 0);
                    Get_SB (   A.pos3d_z_sig,                   "pos3d_z_sig");
                    Get_S1 (4, A.pos3d_z_bits,                  "pos3d_z_bits"); Param_Info3(mgi_bitstream_pos_z_to_Q15(A.pos3d_z_sig, A.pos3d_z_bits)/32768.0*100, "%", 0);
                    Element_Level--;
                    Element_Info1(Angles2String(mgi_bitstream_pos_ToAngles(mgi_bitstream_val_to_Q15(A.pos3d_x_bits, 6), mgi_bitstream_val_to_Q15(A.pos3d_y_bits, 6), mgi_bitstream_pos_z_to_Q15(A.pos3d_z_sig, A.pos3d_z_bits))));
                    Element_Level++;
                }
                else
                    A.pos3d_x_bits=(int8u)-1;
                if (object_info_mask & 0x4)
                {
                    Skip_SB(                                    "hp_md_state");
                    Get_S1 (2, A.hp_render_mode,                "hp_render_mode"); Param_Info1(hp_render_mode_Values[A.hp_render_mode]);
                    Skip_SB(                                    "hp_headtrack_state");
                }
                else
                    A.hp_render_mode=(int8u)-1;
                size_t Parsed=Begin-Data_BS_Remain();
                int8u Padding=8-(Parsed%8);
                Skip_BS(Padding,                                "padding");
            }
            Element_End0();
        }
        Element_End0();
    }
 
    BedInstances.resize(num_bed_instances);
    for (int8u i=0; i<num_bed_instances; i++)
    {
        bed_instance& B=BedInstances[i];
        Element_Begin1("bed_instance");
        int32u num_bed_alt_md;
        Get_V4 (2, num_bed_alt_md,                              "num_bed_alt_md");
        B.Alts.resize(num_bed_alt_md);
        for (int8u j=0; j<num_bed_alt_md; j++)
        {
            bed_instance::bed_alt& A=B.Alts[j];
            Element_Begin1("bed_alt_md");
            int32u bed_info_mask, bed_info_size_bytes;
            Get_V4 (2, bed_info_mask,                           "bed_info_mask");
            Get_V4 (2, bed_info_size_bytes,                     "bed_info_size_bytes");
            size_t End=Data_BS_Remain()-bed_info_size_bytes*8;
            if (bed_info_mask&1)
            {
                int8u bed_gain_db_bits;
                Get_S1(6, bed_gain_db_bits,                     "bed_gain_db_bits");
                A.bed_gain_db=15-bed_gain_db_bits;
            }
            else
                A.bed_gain_db=INT8_MAX;
            if (Data_BS_Remain()>End)
                Skip_BS(Data_BS_Remain()-End,                   "padding");
            Element_End0();
        }
        Element_End0();
    }
 
    Presets.resize(num_presets_m1 + 1);
    for (int8u i=0; i<=num_presets_m1; i++)
    {
        preset& P=Presets[i];
        Element_Begin1("preset");
        int32u num_target_device_configs_m1;
        Get_V4 (2, num_target_device_configs_m1,                "num_target_device_configs_m1");
        Get_V4 (2, P.default_target_device_config,              "default_target_device_config"); Param_Info1(default_target_device_config_Value(P.default_target_device_config));
        P.target_device_configs.resize(num_target_device_configs_m1+1);
        for (int32u j=0; j<=num_target_device_configs_m1; j++)
        {
            preset::target_device_config& T=P.target_device_configs[j];
            Element_Begin1("target_device_config");
            Get_V4 (3, T.target_devices_mask,                   "target_devices_mask"); Param_Info1(default_target_device_config_Value(T.target_devices_mask));
            for (int32u k=0; k<num_dyn_objects+num_bed_instances; k++)
            {
                Element_Begin1(k<num_dyn_objects?"object":"bed");
                bool active;
                Get_SB (active,                                 k<num_dyn_objects?"object_active":"bed_active");
                if (active)
                {
                    int32u md_index;
                    size_t Size=k<num_dyn_objects?DynObjects[k].Alts.size():BedInstances[k-num_dyn_objects].Alts.size();
                    Get_S4 (bits_needed(Size+1), md_index, k<num_dyn_objects?"obj_md_index":"bed_md_index");
                    T.md_indexes.push_back(md_index);
                }
                else
                    T.md_indexes.push_back((int32u)-1);
                Element_End0();
            }
            Element_End0();
        }
        TEST_SB_SKIP(                                           "preset_extension");
            int32u extension_size;
            Get_V4 (4, extension_size,                          "extension_size");
            Skip_BS(extension_size,                             "extension");
        TEST_SB_END();
        Element_End0();
    }
 
    substream_mappings.resize(num_dyn_objects+num_bed_instances);
    if (is_substream_mapping_present)
    {
        Element_Begin1("substream_mapping");
        for (int8u i=0; i<substream_mappings.size(); i++)
        {
            substream_mapping& S=substream_mappings[i];
            Element_Begin1(i<num_dyn_objects?"dyn_object":"bed_instance");
            bool standard_index;
            Get_S1(4, S.substream_id,                           "substream_id");
            Get_SB(   standard_index,                           "standard_index");
            if (standard_index)
                Get_S4 (3, S.channel_index,                     "channel_index");
            else
            {
                Get_S4(5, S.channel_index,                      "channel_index");
                if (S.channel_index==0x1F)
                {
                    Get_V4(3, S.channel_index,                  "channel_index");
                    S.channel_index+=0x1F;
                }
            }
 
            Element_End0();
        }
        Element_End0();
    }
    int8u byte_align_Content=0;
    {
        size_t ToPadd=(BS_Start-Data_BS_Remain())%8;
        if (ToPadd)
            Get_S1(8-ToPadd, byte_align_Content,                "byte_align");
    }
    if (mini_mixgraph_extension_present)
    {
        Element_Begin1("mini_mixgraph_extension");
        TEST_SB_SKIP(                                           "mgi_extension");
            int32u extension_size;
            Get_V4 (4, extension_size,                          "extension_size");
            Skip_BS(extension_size,                             "extension");
        TEST_SB_END();
        Element_End0();
    }
    if (mini_mixgraph_description_present)
    {
        Element_Begin1("mini_mixgraph_description");
        int32u desc_packet_idx, desc_pkt_size_bits;
        if (!byte_align_Content)
        {
            Get_V4 (5, desc_packet_idx,                         "desc_packet_idx");
        }
        else
        {
            Skip_S1(6,                                          "0x3F?");
            desc_packet_idx = 30; //Not in spec. Bug in the stream here?
        }
        if (!desc_packet_idx)
        {
            Get_V4 (5, num_desc_packets_m1,                     "num_desc_packets_m1");
        }
        if (desc_packet_idx==num_desc_packets_m1)
        {
            Get_V4 (7, desc_pkt_size_bits,                      "desc_pkt_size_bits");
        }
        else
        {
            Get_V4 (4, desc_pkt_size_bits,                      "desc_pkt_size_bytes");
            desc_pkt_size_bits<<=3; //bits to bytes
        }
        while (desc_pkt_size_bits)
        {
            int8u bits8=desc_pkt_size_bits>8?8:desc_pkt_size_bits;
            int8u data;
            Get_S1(bits8, data,                                 "description_packet_data");
            if (num_desc_packets_m1!=(int32u)-1)
                description_packet_data.push_back(data<<(8-bits8));
            desc_pkt_size_bits-=bits8;
        }
        if (desc_packet_idx==num_desc_packets_m1 && !description_packet_data.empty())
        {
            Presets_More.clear();
            BitStream_Fast* BS_Save=BS;
            BS=new BitStream_Fast(&*description_packet_data.begin(), description_packet_data.size()); //In bytes, we can not provide a precise count of bits
            Element_Begin1("packet_description");
            int8u preset_description_id_bits;
            Get_S1(3, preset_description_id_bits,           "preset_description_id_bits");
            for (int8u i=0; i <=num_presets_m1; i++)
            {
                Skip_BS(preset_description_id_bits,         "preset_description_id[i]");
            }
            TEST_SB_SKIP("preset_description_text_present");
                Presets_More.resize(num_presets_m1+1);
                for (int8u i=0; i<=num_presets_m1; i++)
                {
                    preset_more& P=Presets_More[i];
                    int8u desc_text_len;
                    Get_S1(5, desc_text_len,                "desc_text_len");
                    Element_Begin1("preset_description");
                    for (int8u j=0; j<desc_text_len; j++)
                    {
                        int8u preset_description_char;
                        Get_S1(8, preset_description_char,  "preset_description_char");
                        P.description+=(char)preset_description_char;
                    }
                    if (Ztring().From_UTF8(P.description).empty())
                        P.description.clear(); // Problem while parsing
                    Element_Info1(P.description);
                    Element_End0();
                }
            TEST_SB_END();
            Element_End0();
            delete BS; BS=BS_Save;
            description_packet_data.clear();
            mini_mixgraph_description_present=false; //Indicates that the stream is ready for data filling
        }
        Element_End0();
        {
            size_t ToPadd=(BS_Start-Data_BS_Remain())%8;
            if (ToPadd<8)
                Skip_BS(8-ToPadd,                               "byte_align");
        }
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::Get_V4(int8u Bits, int8u MaxLoops, int32u& Info, const char* Name)
{
    Info=0;
    #if MEDIAINFO_TRACE
        if (Trace_Activated)
        {
            int8u Count=0;
            for (;;)
            {
                Info+=BS->Get4(Bits);
                Count+=1+Bits;
                if (!BS->GetB() || !(--MaxLoops))
                    break;
                Info<<=Bits;
                Info+=(1<<Bits);
            }
 
            Param(Name, Info, Count);
            Param_Info(__T("(")+Ztring::ToZtring(Count)+__T(" bits)"));
        }
        else
    #endif //MEDIAINFO_TRACE
        {
            for (;;)
            {
                Info+=BS->Get4(Bits);
                if (!BS->GetB() || !(--MaxLoops))
                    break;
                Info<<=Bits;
                Info+=(1<<Bits);
            }
        }
}
 
//---------------------------------------------------------------------------
void File_DolbyE::Get_V4(int8u Bits, int32u& Info, const char* Name)
{
    Info=0;
    #if MEDIAINFO_TRACE
        if (Trace_Activated)
        {
            int8u Count=0;
            for (;;)
            {
                Info+=BS->Get4(Bits);
                Count+=1+Bits;
                if (!BS->GetB())
                    break;
                Info<<=Bits;
                Info+=(1<<Bits);
            }
 
            Param(Name, Info, Count);
            Param_Info(__T("(")+Ztring::ToZtring(Count)+__T(" bits)"));
        }
        else
    #endif //MEDIAINFO_TRACE
        {
            for (;;)
            {
                Info+=BS->Get4(Bits);
                if (!BS->GetB())
                    break;
                Info<<=Bits;
                Info+=(1<<Bits);
            }
        }
}
 
//---------------------------------------------------------------------------
void File_DolbyE::Skip_V4(int8u  Bits, const char* Name)
{
    #if MEDIAINFO_TRACE
        if (Trace_Activated)
        {
            int32u Info=0;
            int8u Count=0;
            for (;;)
            {
                Info+=BS->Get4(Bits);
                Count+=1+Bits;
                if (!BS->GetB())
                    break;
                Info<<=Bits;
                Info+=(1<<Bits);
            }
 
            Param(Name, Info, Count);
            Param_Info(__T("(")+Ztring::ToZtring(Count)+__T(" bits)"));
        }
        else
    #endif //MEDIAINFO_TRACE
        {
            for (;;)
            {
                BS->Skip(Bits);
                if (!BS->GetB())
                    break;
            }
        }
}
 
//---------------------------------------------------------------------------
void File_DolbyE::audio_segment()
{
    //Parsing
    Element_Begin1("audio_segment");
    #if MEDIAINFO_TRACE
        //CRC test
        size_t Pos_Begin=0;
    #endif //MEDIAINFO_TRACE
    for (int8u Channel=0; Channel<DolbyE_Channels[program_config]; Channel++)
    {
        if ((Channel%(DolbyE_Channels[program_config]/2))==0 && key_present)
        {
            int16u audio_subsegment_size=0;
            for (int8u ChannelForSize=0; ChannelForSize<DolbyE_Channels[program_config]/2; ChannelForSize++)
                audio_subsegment_size+=channel_subsegment_size[((Channel<DolbyE_Channels[program_config]/2)?0:(DolbyE_Channels[program_config]/2))+ChannelForSize];
 
            if (Data_BS_Remain()<(audio_subsegment_size+1)*(size_t)bit_depth)
                return; //There is a problem
 
            //We must change the buffer
            switch (bit_depth)
            {
                case 16 :
                            {
                            int16u audio_subsegment_key;
                            Get_S2 (16, audio_subsegment_key, (Channel+1==DolbyE_Channels[program_config])?"audio_subsegment1_key":"audio_subsegment0_key");
 
                            int8u* Temp=Descrambled_Buffer+(size_t)Element_Size-Data_BS_Remain()/8;
                            for (int16u Pos=0; Pos<audio_subsegment_size+1; Pos++)
                                int16u2BigEndian(Temp+Pos*2, BigEndian2int16u(Temp+Pos*2)^audio_subsegment_key);
                            }
                            break;
                case 20 :
                            {
                            int32u audio_subsegment_key;
                            Get_S3 (20, audio_subsegment_key, (Channel+1==DolbyE_Channels[program_config])?"audio_subsegment1_key":"audio_subsegment0_key");
 
                            Descramble_20bit(audio_subsegment_key, audio_subsegment_size);
                            }
                            break;
                default :   ;
            }
        }
 
        #if MEDIAINFO_TRACE
            //CRC test
            if ((Channel%(DolbyE_Channels[program_config]/2))==0)
                Pos_Begin=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
        #endif //MEDIAINFO_TRACE
 
        Element_Begin1(__T("Channel ")+Ztring::ToZtring(Channel));
        Element_Info1(Ztring::ToZtring(channel_subsegment_size[Channel])+__T(" words"));
        Skip_BS(channel_subsegment_size[Channel]*bit_depth,     "channel_subsegment");
        Element_End0();
        if ((Channel%(DolbyE_Channels[program_config]/2))==DolbyE_Channels[program_config]/2-1)
        {
            Skip_S3(bit_depth,                                  (Channel+1==DolbyE_Channels[program_config])?"audio_subsegment1_crc":"audio_subsegment0_crc");
 
            #if MEDIAINFO_TRACE
                //CRC test
                size_t Pos_End=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
                int8u BitSkip_Begin=Pos_Begin%8;
                Pos_Begin/=8;
                int8u BitSkip_End=0; // Pos_End%8; Looks like that the last bits must not be in the CRC computing
                Pos_End/=8;
                if (BitSkip_End)
                    Pos_End++;
 
                int16u CRC=CRC_16_Compute(Buffer+Pos_Begin, Pos_End-Pos_Begin, BitSkip_Begin, BitSkip_End);
                if (CRC)
                {
                    //CRC is wrong
                    Param_Info1("NOK");
                }
            #endif //MEDIAINFO_TRACE
        }
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::metadata_extension_segment()
{
    //Parsing
    Element_Begin1("metadata_extension_segment");
    if (key_present)
    {
        if (Data_BS_Remain()<((size_t)metadata_extension_segment_size+1)*(size_t)bit_depth) //+1 for CRC
            return; //There is a problem
 
        //We must change the buffer
        switch (bit_depth)
        {
            case 16 :
                        {
                        int16u metadata_extension_segment_key;
                        Get_S2 (16, metadata_extension_segment_key, "metadata_extension_segment_key");
 
                        int8u* Temp=Descrambled_Buffer+(size_t)Element_Size-Data_BS_Remain()/8;
                        for (int16u Pos=0; Pos<metadata_extension_segment_size+1; Pos++)
                            int16u2BigEndian(Temp+Pos*2, BigEndian2int16u(Temp+Pos*2)^metadata_extension_segment_key);
                        }
                        break;
            case 20 :
                        {
                        int32u metadata_extension_segment_key;
                        Get_S3 (20, metadata_extension_segment_key, "metadata_extension_segment_key");
 
                        Descramble_20bit(metadata_extension_segment_key, metadata_extension_segment_size);
                        }
                        break;
            default :   ;
        }
    }
 
    #if MEDIAINFO_TRACE
        //CRC test
        size_t Pos_Begin=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
    #endif //MEDIAINFO_TRACE
 
    size_t  metadata_extension_segment_BitCountAfter=Data_BS_Remain();
    metadata_extension_segment_BitCountAfter-=metadata_extension_segment_size*bit_depth;
    if (metadata_extension_segment_size)
    {
        for(;;)
        {
            Element_Begin1("metadata_extension_subsegment");
            int16u  metadata_extension_subsegment_length;
            int8u   metadata_extension_subsegment_id;
            Get_S1 ( 4, metadata_extension_subsegment_id,       "metadata_extension_subsegment_id");
            if (metadata_extension_subsegment_id==0)
            {
                Element_End0();
                break;
            }
            Get_S2 (12, metadata_extension_subsegment_length,   "metadata_extension_subsegment_length");
            switch (metadata_extension_subsegment_id)
            {
                default: Skip_BS(metadata_extension_subsegment_length,"metadata_extension_subsegment (unknown)");
            }
            Element_End0();
        }
        Param_Info1(metadata_extension_segment_BitCountAfter);
        Param_Info1(Data_BS_Remain());
        Param_Info1(Data_BS_Remain()-metadata_extension_segment_BitCountAfter);
        if (Data_BS_Remain()>metadata_extension_segment_BitCountAfter)
            Skip_BS(Data_BS_Remain()-metadata_extension_segment_BitCountAfter,"reserved_metadata_extension_bits");
    }
    Skip_S3(bit_depth,                                          "metadata_extension_crc");
 
    #if MEDIAINFO_TRACE
        //CRC test
        size_t Pos_End=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
        int8u BitSkip_Begin=Pos_Begin%8;
        Pos_Begin/=8;
        int8u BitSkip_End=0; // Pos_End%8; Looks like that the last bits must not be in the CRC computing
        Pos_End/=8;
        if (BitSkip_End)
            Pos_End++;
 
        int16u CRC=CRC_16_Compute(Buffer+Pos_Begin, Pos_End-Pos_Begin, BitSkip_Begin, BitSkip_End);
        if (CRC)
        {
            //CRC is wrong
            Param_Info1("NOK");
        }
    #endif //MEDIAINFO_TRACE
 
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::audio_extension_segment()
{
    //Parsing
    Element_Begin1("audio_extension_segment");
    #if MEDIAINFO_TRACE
        //CRC test
        size_t Pos_Begin=0;
    #endif //MEDIAINFO_TRACE
    for (int8u Channel=0; Channel<DolbyE_Channels[program_config]; Channel++)
    {
        if ((Channel%(DolbyE_Channels[program_config]/2))==0 && key_present)
        {
            int16u audio_extension_subsegment_size=0;
            for (int8u ChannelForSize=0; ChannelForSize<DolbyE_Channels[program_config]/2; ChannelForSize++)
                audio_extension_subsegment_size+=channel_subsegment_size[((Channel<DolbyE_Channels[program_config]/2)?0:(DolbyE_Channels[program_config]/2))+ChannelForSize];
 
            if (Data_BS_Remain()<((size_t)audio_extension_subsegment_size+1)*(size_t)bit_depth)
                return; //There is a problem
 
            //We must change the buffer
            switch (bit_depth)
            {
                case 16 :
                            {
                            int16u audio_extension_subsegment_key;
                            Get_S2 (16, audio_extension_subsegment_key, (Channel+1==DolbyE_Channels[program_config])?"audio_extension_subsegment1_key":"audio_extension_subsegment0_key");
 
                            int8u* Temp=Descrambled_Buffer+(size_t)Element_Size-Data_BS_Remain()/8;
                            for (int16u Pos=0; Pos<audio_extension_subsegment_size+1; Pos++)
                                int16u2BigEndian(Temp+Pos*2, BigEndian2int16u(Temp+Pos*2)^audio_extension_subsegment_key);
                            }
                            break;
                case 20 :
                            {
                            int32u audio_extension_subsegment_key;
                            Get_S3 (20, audio_extension_subsegment_key, (Channel+1==DolbyE_Channels[program_config])?"audio_extension_subsegment1_key":"audio_extension_subsegment0_key");
 
                            Descramble_20bit(audio_extension_subsegment_key, audio_extension_subsegment_size);
                            }
                            break;
                default :   ;
            }
        }
 
        #if MEDIAINFO_TRACE
            //CRC test
            if ((Channel%(DolbyE_Channels[program_config]/2))==0)
                Pos_Begin=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
        #endif //MEDIAINFO_TRACE
 
        Element_Begin1(__T("Channel ")+Ztring::ToZtring(Channel));
        Element_Info1(Ztring::ToZtring(channel_subsegment_size[Channel])+__T(" words"));
        Skip_BS(channel_subsegment_size[Channel]*bit_depth,     "channel_subsegment");
        Element_End0();
        if ((Channel%(DolbyE_Channels[program_config]/2))==DolbyE_Channels[program_config]/2-1)
        {
            Skip_S3(bit_depth,                                  (Channel+1==DolbyE_Channels[program_config])?"audio_extension_subsegment1_crc":"audio_extension_subsegment0_crc");
 
            #if MEDIAINFO_TRACE
                //CRC test
                size_t Pos_End=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
                int8u BitSkip_Begin=Pos_Begin%8;
                Pos_Begin/=8;
                int8u BitSkip_End=0; // Pos_End%8; Looks like that the last bits must not be in the CRC computing
                Pos_End/=8;
                if (BitSkip_End)
                    Pos_End++;
 
                int16u CRC=CRC_16_Compute(Buffer+Pos_Begin, Pos_End-Pos_Begin, BitSkip_Begin, BitSkip_End);
                if (CRC)
                {
                    //CRC is wrong
                    Param_Info1("NOK");
                }
            #endif //MEDIAINFO_TRACE
        }
    }
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::meter_segment()
{
    //Parsing
    Element_Begin1("meter_segment");
    if (key_present)
    {
        if (Data_BS_Remain()<((size_t)meter_segment_size+1)*(size_t)bit_depth) //+1 for CRC
            return; //There is a problem
 
        //We must change the buffer
        switch (bit_depth)
        {
            case 16 :
                        {
                        int16u meter_segment_key;
                        Get_S2 (16, meter_segment_key, "meter_segment_key");
 
                        int8u* Temp=Descrambled_Buffer+(size_t)Element_Size-Data_BS_Remain()/8;
                        for (int16u Pos=0; Pos<meter_segment_size+1; Pos++)
                            int16u2BigEndian(Temp+Pos*2, BigEndian2int16u(Temp+Pos*2)^meter_segment_key);
                        }
                        break;
            case 20 :
                        {
                        int32u meter_segment_key;
                        Get_S3 (20, meter_segment_key, "meter_segment_key");
 
                        Descramble_20bit(meter_segment_key, meter_segment_size);
                        }
                        break;
            default :   ;
        }
    }
    size_t  meter_segment_BitCountAfter=Data_BS_Remain();
    meter_segment_BitCountAfter-=meter_segment_size*bit_depth;
    for (int8u Channel=0; Channel<DolbyE_Channels[program_config]; Channel++)
        Skip_S2(10,                                             "peak_meter");
    for (int8u Channel=0; Channel<DolbyE_Channels[program_config]; Channel++)
        Skip_S2(10,                                             "rms_meter");
    if (Data_BS_Remain()>meter_segment_BitCountAfter)
        Skip_BS(Data_BS_Remain()>meter_segment_BitCountAfter,   "reserved_meter_bits");
    Skip_S3(bit_depth,                                          "meter_crc");
 
    #if MEDIAINFO_TRACE
        //CRC test
        size_t Pos_End=Buffer_Offset*8+(size_t)Element_Size*8-Data_BS_Remain();
        size_t Pos_Begin=Pos_End-(meter_segment_size+1)*bit_depth; //+1 for CRC
        int8u BitSkip_Begin=Pos_Begin%8;
        Pos_Begin/=8;
        int8u BitSkip_End=0; // Pos_End%8; Looks like that the last bits must not be in the CRC computing
        Pos_End/=8;
        if (BitSkip_End)
            Pos_End++;
 
        int16u CRC=CRC_16_Compute(Buffer+Pos_Begin, Pos_End-Pos_Begin, BitSkip_Begin, BitSkip_End);
        if (CRC)
        {
            //CRC is wrong
            Param_Info1("NOK");
        }
    #endif //MEDIAINFO_TRACE
 
    Element_End0();
}
 
//---------------------------------------------------------------------------
void File_DolbyE::ac3_metadata_subsegment(bool xbsi)
{
    for (int8u program=0; program<DolbyE_Programs[program_config]; program++)
    {
        Element_Begin1("per program");
        Skip_S1(5,                                          "ac3_datarate");
        Skip_S1(3,                                          "ac3_bsmod");
        Skip_S1(3,                                          "ac3_acmod");
        Skip_S1(2,                                          "ac3_cmixlev");
        Skip_S1(2,                                          "ac3_surmixlev");
        Skip_S1(2,                                          "ac3_dsurmod");
        Skip_S1(1,                                          "ac3_lfeon");
        Skip_S1(5,                                          "ac3_dialnorm");
        Skip_S1(1,                                          "ac3_langcode");
        Skip_S1(8,                                          "ac3_langcod");
        Skip_S1(1,                                          "ac3_audprodie");
        Skip_S1(5,                                          "ac3_mixlevel");
        Skip_S1(2,                                          "ac3_roomtyp");
        Skip_S1(1,                                          "ac3_copyrightb");
        Skip_S1(1,                                          "ac3_origbs");
        if (xbsi)
        {
            Skip_S1(1,                                      "ac3_xbsi1e");
            Skip_S1(2,                                      "ac3_dmixmod");
            Skip_S1(3,                                      "ac3_ltrtcmixlev");
            Skip_S1(3,                                      "ac3_ltrtsurmixlev");
            Skip_S1(3,                                      "ac3_lorocmixlev"); 
            Skip_S1(3,                                      "ac3_lorosurmixlev");
            Skip_S1(1,                                      "ac3_xbsi2e");
            Skip_S1(2,                                      "ac3_dsurexmod");
            Skip_S1(2,                                      "ac3_dheadphonmod");
            Skip_S1(1,                                      "ac3_adconvtyp");
            Skip_S1(8,                                      "ac3_xbsi2");
            Skip_S1(1,                                      "ac3_encinfo");
        }
        else
        {
            Skip_S1(1,                                      "ac3_timecode1e");
            Skip_S2(14,                                     "ac3_timecode1");
            Skip_S1(1,                                      "ac3_timecode2e");
            Skip_S2(14,                                     "ac3_timecode2");
        }
        Skip_S1(1,                                          "ac3_hpfon");
        Skip_S1(1,                                          "ac3_bwlpfon");
        Skip_S1(1,                                          "ac3_lfelpfon");
        Skip_S1(1,                                          "ac3_sur90on");
        Skip_S1(1,                                          "ac3_suratton");
        Skip_S1(1,                                          "ac3_rfpremphon");
        Skip_S1(1,                                          "ac3_compre");
        Skip_S1(8,                                          "ac3_compr1");
        Skip_S1(1,                                          "ac3_dynrnge");
        Skip_S1(8,                                          "ac3_dynrng1");
        Skip_S1(8,                                          "ac3_dynrng2");
        Skip_S1(8,                                          "ac3_dynrng3");
        Skip_S1(8,                                          "ac3_dynrng4");
        Element_End0();
    }
    for (int8u program=0; program<DolbyE_Programs[program_config]; program++)
    {
        Element_Begin1("per program");
        bool ac3_addbsie;
        Get_SB (   ac3_addbsie,                             "ac3_addbsie");
        if (ac3_addbsie)
        {
            int8u ac3_addbsil;
            Get_S1 (6, ac3_addbsil,                         "ac3_addbsil");
            for (int8u Pos=0; Pos<ac3_addbsil+1; Pos++)
                Skip_S1(8,                                  "ac3_addbsi[x]");
        }
        Element_End0();
    }
}
 
//***************************************************************************
// Helpers
//***************************************************************************
 
//---------------------------------------------------------------------------
void File_DolbyE::Descramble_20bit (int32u key, int16u size)
{
    int8u* Temp=Descrambled_Buffer+(size_t)Element_Size-Data_BS_Remain()/8;
    int64u keys=(((int64u)key)<<20)|key;
    bool Half;
    if (Data_BS_Remain()%8)
    {
        Temp--;
        int24u2BigEndian(Temp, BigEndian2int24u(Temp)^(key));
        Half=true;
    }
    else
        Half=false;
    for (int16u Pos=0; Pos<size-(Half?1:0); Pos+=2)
        int40u2BigEndian(Temp+(Half?3:0)+Pos*5/2, BigEndian2int40u(Temp+(Half?3:0)+Pos*5/2)^keys);
    if ((size-((size && Half)?1:0))%2==0)
        int24u2BigEndian(Temp+(Half?3:0)+(size-((size && Half)?1:0))*5/2, BigEndian2int24u(Temp+(Half?3:0)+(size-((size && Half)?1:0))*5/2)^(key<<4));
}
 
//***************************************************************************
// C++
//***************************************************************************
 
} //NameSpace
 
#endif //MEDIAINFO_DOLBYE_YES

V1028 Possible overflow. Consider casting operands, not the result.

V547 Expression 'ToPadd < 8' is always true.

V547 Expression 'ToPadd < 8' is always true.

V638 A terminal null is present inside a string. The '\0xC2' characters were encountered. Probably meant: '\xC2'.

V1020 The function exited without calling the 'Element_End' function. Check lines: 1727, 1714.

V1020 The function exited without calling the 'Element_End' function. Check lines: 1741, 1714.

V1020 The function exited without calling the 'Element_End' function. Check lines: 1753, 1714.

V1020 The function exited without calling the 'BS_End' function. Check lines: 2115, 2101.

V1020 The function exited without calling the 'Element_End' function. Check lines: 3115, 3101.

V1020 The function exited without calling the 'Element_End' function. Check lines: 3186, 3182.

V1020 The function exited without calling the 'Element_End' function. Check lines: 3287, 3273.

V1020 The function exited without calling the 'Element_End' function. Check lines: 3358, 3354.

V1048 The 'len_primary' variable was assigned the same value.

V1048 The 'len_second' variable was assigned the same value.

V547 Expression 'Summary.empty()' is always true.

V547 Expression 'Summary2.empty()' is always true.

V547 Expression 'Summary.empty()' is always true.

V547 Expression 'Summary2.empty()' is always true.

V547 Expression 'BitSkip_End' is always false.

V547 Expression 'object_info_size_bytes' is always true.

V614 Potentially uninitialized variable 'nonstd_bed_channel_assignment_mask' used. Consider checking the first actual argument of the 'push_back' function.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: Azimuth, Elevation.

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

V1051 Consider checking for misprints. It's possible that the 'metadata_extension_segment_BitCountAfter' should be checked here.

V525 The code contains the collection of similar blocks. Check items '1', '6', '8', '8' in lines 172, 173, 174, 175.

V525 The code contains the collection of similar blocks. Check items 'CC2', 'CC3', 'CC3' in lines 1506, 1507, 1508.

V525 The code contains the collection of similar blocks. Check items '2', '3', '3' in lines 1781, 1788, 1795.

V525 The code contains the collection of similar blocks. Check items '1', '2', '3', '3', '3', '3', '1', '2', '2', '1', '8', '1' in lines 3439, 3440, 3441, 3442, 3443, 3444, 3445, 3446, 3447, 3448, 3449, 3450.

V550 An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(Mpegv_frame_rate[frame_rate_code]) > Epsilon.

V550 An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(Mpegv_frame_rate[frame_rate_code]) > Epsilon.

V550 An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(Mpegv_frame_rate[frame_rate_code]) > Epsilon.

V550 An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(Mpegv_frame_rate[frame_rate_code]) > Epsilon.

V624 The constant 3.14159265359 is being utilized. The resulting value could be inaccurate. Consider using the M_PI constant from .

V624 The constant 3.14159265359 is being utilized. The resulting value could be inaccurate. Consider using the M_PI constant from .

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: object_count, channel_subsegment_size, program_config, metadata_extension_segment_size, meter_segment_size, frame_rate_code, ...

V808 'A' object of 'basic_string' type was created but was not utilized.

V832 It's better to use '= default;' syntax instead of empty constructor body.

V808 'evolution_data_segment_id_Name' array was declared but was not utilized.

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

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

V807 Decreased performance. Consider creating a reference to avoid using the 'description_text_Values[Program].Current' expression repeatedly.