/*  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.
 */

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// A good start : http://www.codeproject.com/audio/MPEGAudioInfo.asp
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------

//***************************************************************************
// Constants (Common)
//***************************************************************************

//---------------------------------------------------------------------------
#if defined(MEDIAINFO_MPEGA_YES) || defined(MEDIAINFO_MPEGTS_YES) || defined(MEDIAINFO_MPEGPS_YES)
//---------------------------------------------------------------------------

#include "ZenLib/Conf.h"
using namespace ZenLib;

namespace MediaInfoLib
{

//---------------------------------------------------------------------------
const char* Mpega_Version[4]=
{
    "MPA2.5",
    "",
    "MPA2",
    "MPA1"
};

//---------------------------------------------------------------------------
const char* Mpega_Layer[4]=
{
    "",
    "L3",
    "L2",
    "L1",
};

//---------------------------------------------------------------------------
const char* Mpega_Format_Profile_Version[4]=
{
    "Version 2.5",
    "",
    "Version 2",
    "Version 1"
};

//---------------------------------------------------------------------------
const char* Mpega_Format_Profile_Layer[4]=
{
    "",
    "Layer 3",
    "Layer 2",
    "Layer 1",
};

//---------------------------------------------------------------------------
} //NameSpace

//---------------------------------------------------------------------------
#endif //...
//---------------------------------------------------------------------------

//***************************************************************************
//
//***************************************************************************

//---------------------------------------------------------------------------
#if defined(MEDIAINFO_MPEGA_YES)
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Audio/File_Mpega.h"
#include "ZenLib/BitStream.h"
#include "ZenLib/Utils.h"
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
using namespace ZenLib;
//---------------------------------------------------------------------------

namespace MediaInfoLib
{

//***************************************************************************
// Constants
//***************************************************************************

//---------------------------------------------------------------------------
static const char* Mpega_Version_String[4]=
{
    "MPEG-2.5 Audio",
    "",
    "MPEG-2 Audio",
    "MPEG-1 Audio",
};

//---------------------------------------------------------------------------
static const char* Mpega_Layer_String[4]=
{
    "",
    " layer 3",
    " layer 2",
    " layer 1",
};

//---------------------------------------------------------------------------
static const int16u Mpega_BitRate[4][4][16]=
{
    {{0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},  //MPEG Audio 2.5 layer X
     {0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160,   0},  //MPEG Audio 2.5 layer 3
     {0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160,   0},  //MPEG Audio 2.5 layer 2
     {0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256,   0}}, //MPEG Audio 2.5 layer 1
    {{0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},  //MPEG Audio X layer X
     {0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},  //MPEG Audio X layer 3
     {0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},  //MPEG Audio X layer 2
     {0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0}}, //MPEG Audio X layer 1
    {{0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},  //MPEG Audio 2 layer X
     {0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160,   0},  //MPEG Audio 2 layer 3
     {0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160,   0},  //MPEG Audio 2 layer 2
     {0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256,   0}}, //MPEG Audio 2 layer 1
    {{0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},  //MPEG Audio 1 layer X
     {0,  32,  40,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320,   0},  //MPEG Audio 1 layer 3
     {0,  32,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384,   0},  //MPEG Audio 1 layer 2
     {0,  32,  64,  96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,   0}}, //MPEG Audio 1 layer 1
};

//---------------------------------------------------------------------------
static const int16u Mpega_SamplingRate[4][4]=
{
    {11025, 12000,  8000, 0}, //MPEG Audio 2.5
    {    0,     0,     0, 0}, //MPEG Audio X
    {22050, 24000, 16000, 0}, //MPEG Audio 2
    {44100, 48000, 32000, 0}, //MPEG Audio 1
};

//---------------------------------------------------------------------------
static const int16u Mpega_Channels[4]=
{
    2,
    2,
    2,
    1,
};

//---------------------------------------------------------------------------
static const char* Mpega_Codec_Profile[4]=
{
    "",
    "Joint stereo",
    "Dual mono",
    "",
};

//---------------------------------------------------------------------------
static const char* Mpega_Codec_Profile_Extension[]=
{
    "",
    "Intensity Stereo",
    "MS Stereo",
    "Intensity Stereo + MS Stereo",
};

//---------------------------------------------------------------------------
static const char* Mpega_Emphasis[]=
{
    "",
    "50/15ms",
    "Reserved",
    "CCITT",
};

//---------------------------------------------------------------------------
static const char* Lame_BitRate_Mode[]=
{
    "",
    "CBR",
    "VBR",
    "VBR",
    "VBR",
    "VBR",
    "VBR",
    "",
    "CBR",
    "VBR",
    "",
    "",
    "",
    "",
    "",
    "",
};

//---------------------------------------------------------------------------
static const char* Lame_Method[]=
{
    "",
    "CBR",
    "ABR",
    "VBR (rh)",
    "VBR (mtrh)",
    "VBR (rh)",
    "VBR",
    "",
    "CBR (2-pass)",
    "ABR (2-pass)",
    "",
    "",
    "",
    "",
    "",
    "",
};

//---------------------------------------------------------------------------
static const int8u Mpega_Coefficient[4][4] = //Samples per Frame / 8
{
    {  0,  72, 144,  12}, //MPEG Audio 2.5
    {  0,   0,   0,   0}, //MPEG Audio X
    {  0,  72, 144,  12}, //MPEG Audio 2
    {  0, 144, 144,  12}, //MPEG Audio 1
};

//---------------------------------------------------------------------------
static const int8u Mpega_SlotSize[4]= //A frame is coposed of slots
{
    0, // Layer X
    1, // Layer3
    1, // Layer2
    4, // Layer1
};

//---------------------------------------------------------------------------
static const int16u Mpega_CRC12_Table[]=
{
  0x000, 0x80f, 0x811, 0x01e, 0x82d, 0x022, 0x03c, 0x833,
  0x855, 0x05a, 0x044, 0x84b, 0x078, 0x877, 0x869, 0x066,
  0x8a5, 0x0aa, 0x0b4, 0x8bb, 0x088, 0x887, 0x899, 0x096,
  0x0f0, 0x8ff, 0x8e1, 0x0ee, 0x8dd, 0x0d2, 0x0cc, 0x8c3,
  0x945, 0x14a, 0x154, 0x95b, 0x168, 0x967, 0x979, 0x176,
  0x110, 0x91f, 0x901, 0x10e, 0x93d, 0x132, 0x12c, 0x923,
  0x1e0, 0x9ef, 0x9f1, 0x1fe, 0x9cd, 0x1c2, 0x1dc, 0x9d3,
  0x9b5, 0x1ba, 0x1a4, 0x9ab, 0x198, 0x997, 0x989, 0x186,
  0xa85, 0x28a, 0x294, 0xa9b, 0x2a8, 0xaa7, 0xab9, 0x2b6,
  0x2d0, 0xadf, 0xac1, 0x2ce, 0xafd, 0x2f2, 0x2ec, 0xae3,
  0x220, 0xa2f, 0xa31, 0x23e, 0xa0d, 0x202, 0x21c, 0xa13,
  0xa75, 0x27a, 0x264, 0xa6b, 0x258, 0xa57, 0xa49, 0x246,
  0x3c0, 0xbcf, 0xbd1, 0x3de, 0xbed, 0x3e2, 0x3fc, 0xbf3,
  0xb95, 0x39a, 0x384, 0xb8b, 0x3b8, 0xbb7, 0xba9, 0x3a6,
  0xb65, 0x36a, 0x374, 0xb7b, 0x348, 0xb47, 0xb59, 0x356,
  0x330, 0xb3f, 0xb21, 0x32e, 0xb1d, 0x312, 0x30c, 0xb03,
  0xd05, 0x50a, 0x514, 0xd1b, 0x528, 0xd27, 0xd39, 0x536,
  0x550, 0xd5f, 0xd41, 0x54e, 0xd7d, 0x572, 0x56c, 0xd63,
  0x5a0, 0xdaf, 0xdb1, 0x5be, 0xd8d, 0x582, 0x59c, 0xd93,
  0xdf5, 0x5fa, 0x5e4, 0xdeb, 0x5d8, 0xdd7, 0xdc9, 0x5c6,
  0x440, 0xc4f, 0xc51, 0x45e, 0xc6d, 0x462, 0x47c, 0xc73,
  0xc15, 0x41a, 0x404, 0xc0b, 0x438, 0xc37, 0xc29, 0x426,
  0xce5, 0x4ea, 0x4f4, 0xcfb, 0x4c8, 0xcc7, 0xcd9, 0x4d6,
  0x4b0, 0xcbf, 0xca1, 0x4ae, 0xc9d, 0x492, 0x48c, 0xc83,
  0x780, 0xf8f, 0xf91, 0x79e, 0xfad, 0x7a2, 0x7bc, 0xfb3,
  0xfd5, 0x7da, 0x7c4, 0xfcb, 0x7f8, 0xff7, 0xfe9, 0x7e6,
  0xf25, 0x72a, 0x734, 0xf3b, 0x708, 0xf07, 0xf19, 0x716,
  0x770, 0xf7f, 0xf61, 0x76e, 0xf5d, 0x752, 0x74c, 0xf43,
  0xec5, 0x6ca, 0x6d4, 0xedb, 0x6e8, 0xee7, 0xef9, 0x6f6,
  0x690, 0xe9f, 0xe81, 0x68e, 0xebd, 0x6b2, 0x6ac, 0xea3,
  0x660, 0xe6f, 0xe71, 0x67e, 0xe4d, 0x642, 0x65c, 0xe53,
  0xe35, 0x63a, 0x624, 0xe2b, 0x618, 0xe17, 0xe09, 0x606
};

//***************************************************************************
// Constructor/Destructor
//***************************************************************************

//---------------------------------------------------------------------------
File_Mpega::File_Mpega()
:File__Analyze(), File__Tags_Helper()
{
    //File__Tags_Helper
    Base=this;

    //Configuration
    #if MEDIAINFO_TRACE
        Trace_Layers_Update(8); //Stream
    #endif //MEDIAINFO_TRACE
    MustSynchronize=true;
    Buffer_TotalBytes_FirstSynched_Max=64*1024;
    PTS_DTS_Needed=true;
    StreamSource=IsStream;
    Frame_Count_NotParsedIncluded=0;

    //In
    Frame_Count_Valid=0;
    FrameIsAlwaysComplete=false;
    CalculateDelay=false;

    //Temp - BitStream info
    Surround_Frames=0;
    Block_Count[0]=0;
    Block_Count[1]=0;
    Block_Count[2]=0;
    Channels_Count[0]=0;
    Channels_Count[1]=0;
    Channels_Count[2]=0;
    Channels_Count[3]=0;
    Extension_Count[0]=0;
    Extension_Count[1]=0;
    Extension_Count[2]=0;
    Extension_Count[3]=0;
    Emphasis_Count[0]=0;
    Emphasis_Count[1]=0;
    Emphasis_Count[2]=0;
    Emphasis_Count[3]=0;
    Scfsi=0;
    Scalefac=0;
    Reservoir=0;
    LastSync_Offset=(int64u)-1;
    VBR_FileSize=0;
    VBR_Frames=0;
    Reservoir_Max=0;
    Xing_Scale=0;
    BitRate=0;
    MpegPsPattern_Count=0;
    VBR_Frames_IsCbr=false;
}

//***************************************************************************
// Streams management
//***************************************************************************

//---------------------------------------------------------------------------
void File_Mpega::Streams_Fill()
{
    File__Tags_Helper::Stream_Prepare(Stream_Audio);
    Fill(Stream_Audio, 0, Audio_Format, "MPEG Audio");
    if (!Frame_Count)
        return;

    //VBR detection without header
    if (VBR_Frames==0)
    {
        //How much kinds of bitrates?
        if (BitRate_Count.size()>1)
            BitRate_Mode=__T("VBR");
    }

    Fill(Stream_Audio, 0, Audio_Format_Version, Mpega_Format_Profile_Version[ID]);
    Fill(Stream_Audio, 0, Audio_Format_Profile, Mpega_Format_Profile_Layer[layer]);
    if (mode && mode<4)
    {
        Fill(Stream_Audio, 0, Audio_Format_Settings, Mpega_Codec_Profile[mode]);
        Fill(Stream_Audio, 0, Audio_Format_Settings_Mode, Mpega_Codec_Profile[mode]);
    }
    if (mode_extension && mode_extension<4)
    {
        Fill(Stream_Audio, 0, Audio_Format_Settings, Mpega_Codec_Profile_Extension[mode_extension]);
        Fill(Stream_Audio, 0, Audio_Format_Settings_ModeExtension, Mpega_Codec_Profile_Extension[mode_extension]);
    }
    if (emphasis && emphasis<4)
    {
        Fill(Stream_Audio, 0, Audio_Format_Settings, Mpega_Emphasis[emphasis]);
        Fill(Stream_Audio, 0, Audio_Format_Settings_Emphasis, Mpega_Emphasis[emphasis]);
    }
    Fill(Stream_Audio, 0, Audio_Codec, Ztring(Mpega_Version[ID])+Ztring(Mpega_Layer[layer]));
    Fill(Stream_Audio, 0, Audio_Codec_String, Ztring(Mpega_Version_String[ID])+Ztring(Mpega_Layer_String[layer]), true);
    Fill(Stream_Audio, 0, Audio_SamplingRate, Mpega_SamplingRate[ID][sampling_frequency]);
    if (mode<4)
    {
        Fill(Stream_Audio, 0, Audio_Channel_s_, Mpega_Channels[mode]);
        Fill(Stream_Audio, 0, Audio_Codec_Profile, Mpega_Codec_Profile[mode]);
    }

    //Bitrate, if CBR
    if (VBR_Frames==0 && BitRate_Mode!=__T("VBR"))
    {
        BitRate_Mode=__T("CBR");
        BitRate=Mpega_BitRate[ID][layer][bitrate_index]*1000;
        Fill(Stream_General, 0, General_OverallBitRate, BitRate);
        Fill(Stream_Audio, 0, Audio_BitRate, BitRate);
        if (CalculateDelay && Buffer_TotalBytes_FirstSynched>10 && BitRate>0)
        {
            Fill(Stream_Audio, 0, Audio_Delay, Buffer_TotalBytes_FirstSynched*8*1000/BitRate, 0);
            Fill(Stream_Audio, 0, Audio_Delay_Source, "Stream");
        }
    }

    //Bitrate mode
    Fill(Stream_Audio, 0, Audio_BitRate_Mode, BitRate_Mode);
    Fill(Stream_Audio, 0, Audio_BitRate_Minimum, BitRate_Minimum);
    Fill(Stream_Audio, 0, Audio_BitRate_Nominal, BitRate_Nominal);

    #if MEDIAINFO_ADVANCED
        if (!IsSub && !VBR_Frames && !VBR_FileSize && BitRate_Mode==__T("VBR") && ID<4 && sampling_frequency<4 && Retrieve_Const(Stream_Audio, 0, Audio_BitRate).empty() && Config->File_RiskyBitRateEstimation_Get())
        {
            size_t Divider;
            if (ID==3 && layer==3) //MPEG 1 layer 1
                 Divider=384/8;
            else if ((ID==2 || ID==0) && layer==3) ///MPEG 2 or 2.5 layer 1
                 Divider=192/8;
            else if ((ID==2 || ID==0) && layer==1) //MPEG 2 or 2.5 layer 3
                Divider=576/8;
            else
                Divider=1152/8;
            BitRate=(int32u)((File_Offset+Buffer_Offset+Element_Size)*Mpega_SamplingRate[ID][sampling_frequency]/Frame_Count/Divider);
            Fill(Stream_Audio, 0, Audio_BitRate, BitRate);
        }
    #endif //MEDIAINFO_ADVANCED

    //Tags
    File__Tags_Helper::Streams_Fill();
}

//---------------------------------------------------------------------------
void File_Mpega::Streams_Finish()
{
    //Reservoir
    //Fill("Reservoir_Avg", Reservoir/Frame_Count);
    //Fill("Reservoir_Max", Reservoir_Max);
    //size_t Granules=(Mpeg==3?2:1);
    //size_t Ch=Mpega_Channels[Channels];
    //Fill("Scalefactors", Ztring::ToZtring(Scalefac*100/(Granules*Ch*Frame_Count))+__T('%'));

    if (LastSync_Offset != (int64u)-1 && File_Offset + Buffer_Size > LastSync_Offset)
    {
        Buffer_Offset = Buffer_Size;
        SynchLost("MPEG-Audio", File_Offset + Buffer_Size - LastSync_Offset, !LastSync_Offset_AreNotZero);
    }

    //VBR_FileSize calculation
    if (!IsSub && (File_Size!=(int64u)-1 || LastSync_Offset!=(int64u)-1) && VBR_FileSize==0)
    {
        //We calculate VBR_FileSize from the last synch or File_Size
        if (LastSync_Offset!=(int64u)-1)
        {
            VBR_FileSize=LastSync_Offset;
            VBR_FileSize-=File_BeginTagSize;
        }
        else
        {
            VBR_FileSize=File_Size;
            VBR_FileSize-=File_BeginTagSize;
            VBR_FileSize-=File_EndTagSize;
        }
    }

    //Bitrate calculation if VBR
    int64u FrameCount=0;
    if (VBR_Frames>0)
    {
        FrameCount=VBR_Frames;
        float32 FrameLength=((float32)(VBR_FileSize?VBR_FileSize:File_Size-File_EndTagSize-File_BeginTagSize))/VBR_Frames;
        size_t Divider;
        if (ID==3 && layer==3) //MPEG 1 layer 1
             Divider=384/8;
        else if ((ID==2 || ID==0) && layer==3) ///MPEG 2 or 2.5 layer 1
             Divider=192/8;
        else if ((ID==2 || ID==0) && layer==1) //MPEG 2 or 2.5 layer 3
            Divider=576/8;
        else
            Divider=1152/8;
        if (ID<4 && sampling_frequency<4)
            BitRate=float32_int32s(FrameLength*Mpega_SamplingRate[ID][sampling_frequency]/Divider);
        BitRate_Mode=(VBR_Frames_IsCbr?__T("CBR"):__T("VBR"));
    }
    //if (BitRate_Count.size()>1)
    //{
    //    Ztring BitRate_VBR;
    //    if (!BitRate_VBR.empty())
    //        BitRate_VBR+=__T(' ');
    //    BitRate_VBR+=Ztring::ToZtring(8);
    //    BitRate_VBR+=__T(':');
    //    BitRate_VBR+=Ztring::ToZtring(BitRate_Count[8]);
    //    Fill("BitRate_VBR", Ztring::ToZtring(BitRate_Count[8]));
    //}
    if (VBR_FileSize)
    {
        if (BitRate)
        {
            Fill(Stream_General, 0, General_Duration, VBR_FileSize*8*1000/BitRate, 10, true);
            Fill(Stream_General, 0, General_OverallBitRate, BitRate, 10, true);
            Fill(Stream_Audio, 0, Audio_BitRate, BitRate, 10, true);
            if (CalculateDelay && Buffer_TotalBytes_FirstSynched>10 && BitRate>0)
            {
                Fill(Stream_Audio, 0, Audio_Delay, Buffer_TotalBytes_FirstSynched*8*1000/BitRate, 0, true);
                Fill(Stream_Audio, 0, Audio_Delay_Source, "Stream", Unlimited, true, true);
            }
        }
        Fill(Stream_Audio, 0, Audio_StreamSize, VBR_FileSize);
    }
    Fill(Stream_Audio, 0, Audio_BitRate_Mode, BitRate_Mode, true);

    //Encoding library
    if (Encoded_Library.empty())
        Encoded_Library_Guess();
    if (!Encoded_Library.empty())
    {
        Ztring Encoded_LibraryZ;
        Encoded_LibraryZ.From_UTF8(Encoded_Library.c_str());
        if (Encoded_LibraryZ.empty())
            Encoded_LibraryZ.From_ISO_8859_1(Encoded_Library.c_str());
        Fill(Stream_General, 0, General_Encoded_Library, Encoded_LibraryZ, true);
        Fill(Stream_Audio, 0, Audio_Encoded_Library, Encoded_LibraryZ, true);
        Fill(Stream_Audio, 0, Audio_Encoded_Library_Settings, Encoded_Library_Settings, true);
    }

    //Surround
    if (Surround_Frames>=Frame_Count*0.9)
    {
        //Fill(Stream_Audio, 0, Audio_Channel_s_, 6);
    }

    if (FrameInfo.PTS!=(int64u)-1 && FrameInfo.PTS>PTS_Begin)
    {
        auto Duration = ((float64)(FrameInfo.PTS - PTS_Begin)) / 1000000;
        const auto& ExpectedDurationS = Retrieve_Const(Stream_Audio, 0, Audio_Duration);
        if (!ExpectedDurationS.empty()) {
            DurationIssue(float64_int64s(Duration), ExpectedDurationS.To_int64u(), false, "MPEG-Audio");
        }
        Fill(Stream_Audio, 0, Audio_Duration, Duration, 0, true);
        if (Retrieve(Stream_Audio, 0, Audio_BitRate_Mode)==__T("CBR") && ID<4 && sampling_frequency<4)
        {
            int16u Samples;
            if (ID==3 && layer==3) //MPEG 1 layer 1
                 Samples=384;
            else if ((ID==2 || ID==0) && layer==1) //MPEG 2 or 2.5 layer 3
                Samples=576;
            else
                Samples=1152;

            float64 Frame_Duration=((float64)1)/Mpega_SamplingRate[ID][sampling_frequency]*Samples;
            FrameCount=float64_int64s(((float64)(FrameInfo.PTS-PTS_Begin))/1000000000/Frame_Duration);
        }
    }

    if (FrameCount==0 && VBR_FileSize && Retrieve(Stream_Audio, 0, Audio_BitRate_Mode)==__T("CBR") && ID<4 && layer<4 && sampling_frequency<4 && bitrate_index<16 && Mpega_SamplingRate[ID][sampling_frequency])
    {
        float64 Size=((float64)Mpega_Coefficient[ID][layer]*Mpega_BitRate[ID][layer][bitrate_index]*1000/Mpega_SamplingRate[ID][sampling_frequency])*Mpega_SlotSize[layer];
        if (Size)
            FrameCount=float64_int64s(VBR_FileSize/Size);
    }

    if (FrameCount)
    {
        int16u Samples;
        if (ID==3 && layer==3) //MPEG 1 layer 1
             Samples=384;
        else if ((ID==2 || ID==0) && layer==1) //MPEG 2 or 2.5 layer 3
            Samples=576;
        else
            Samples=1152;
        Fill(Stream_Audio, 0, Audio_FrameCount, FrameCount, 10, true);
        Fill(Stream_Audio, 0, Audio_SamplingCount, FrameCount*Samples, 10, true);
        float64 audio_fps = (float64)((float64)Mpega_SamplingRate[ID][sampling_frequency] / (float64)Samples);
        Fill(Stream_Audio, 0, Audio_FrameRate, audio_fps, 3, true);
    }

    File__Tags_Helper::Streams_Finish();
}

//***************************************************************************
// Buffer - File header
//***************************************************************************

//---------------------------------------------------------------------------
bool File_Mpega::FileHeader_Begin()
{
    //Buffer size
    if (Buffer_Size<8)
        return File_Size<8; //Must wait for more data

    //Detecting WAV/SWF/FLV/ELF/DPG/WM/MZ/DLG files
    int32u Magic4=CC4(Buffer);
    int32u Magic3=Magic4>>8;
    int16u Magic2=Magic4>>16;
    if (Magic4==0x52494646 || Magic3==0x465753 || Magic3==0x464C56 || Magic4==0x7F454C46 || Magic4==0x44504730 || Magic4==0x3026B275 || Magic2==0x4D5A || Magic4==0x000001BA || Magic4==0x000001B3 || Magic4==0x00000100 || CC8(Buffer+Buffer_Offset)==0x444C472056312E30LL)
    {
        File__Tags_Helper::Reject("MPEG Audio");
        return false;
    }

    //Seems OK
    if (!Frame_Count_Valid)
        Frame_Count_Valid=Config->ParseSpeed>=0.5?128:(Config->ParseSpeed>=0.3?32:(IsSub?1:4));
    return true;
}

//***************************************************************************
// Buffer - Synchro
//***************************************************************************

//---------------------------------------------------------------------------
bool File_Mpega::Synchronize()
{
    //Tags
    bool Tag_Found_Begin;
    if (!File__Tags_Helper::Synchronize(Tag_Found_Begin))
        return false;
    if (Tag_Found_Begin)
        return true;

    //Padding
    while (Buffer_Offset<Buffer_Size && Buffer[Buffer_Offset]==0x00)
        Buffer_Offset++;

    //Synchro
    if (Buffer_Offset+3>Buffer_Size)
        return false;
    if (!Status[IsAccepted]
     && Buffer[Buffer_Offset+0]==0x46 //"FLV"
     && Buffer[Buffer_Offset+1]==0x4C
     && Buffer[Buffer_Offset+2]==0x56)
    {
        File__Tags_Helper::Reject(); //Parser accepts too easily FLV files with MPEG Audio (synchronization lost due to FLV header is  not enough), rejecting if FLV magic value is found before the first synchro 
        return false;
    }

    //Synchronizing
    auto Buffer_Offset_BeforeZero = Buffer_Offset;
    LastSync_Offset_AreNotZero = true; // In case there is a return during check
    while (Buffer_Offset+4<=Buffer_Size)
    {
        while (Buffer_Offset+4<=Buffer_Size)
        {
            if (Buffer[Buffer_Offset  ]==0xFF
             && (Buffer[Buffer_Offset+1]&0xE0)==0xE0
             && (Buffer[Buffer_Offset+2]&0xF0)!=0xF0
             && (Buffer[Buffer_Offset+2]&0x0C)!=0x0C)
                break; //while()

            //Tags
            bool Tag_Found_Synchro;
            if (!File__Tags_Helper::Synchronize(Tag_Found_Synchro))
                return false;
            if (Tag_Found_Synchro)
                return true;

            //Better detect MPEG-PS
            if (Frame_Count==0
             && Buffer[Buffer_Offset  ]==0x00
             && Buffer[Buffer_Offset+1]==0x00
             && Buffer[Buffer_Offset+2]==0x01
             && Buffer[Buffer_Offset+3]==0xBA)
            {
                MpegPsPattern_Count++;
                if (MpegPsPattern_Count>=2)
                {
                    File__Tags_Helper::Reject("MPEG Audio");
                    return false;
                }
            }

            Buffer_Offset++;
        }

        if (Buffer_Offset+4<=Buffer_Size)//Testing if size is coherant
        {
            //Retrieving some info
            int8u ID0                =(CC1(Buffer+Buffer_Offset+1)>>3)&0x03;
            int8u layer0             =(CC1(Buffer+Buffer_Offset+1)>>1)&0x03;
            int8u bitrate_index0     =(CC1(Buffer+Buffer_Offset+2)>>4)&0x0F;
            int8u sampling_frequency0=(CC1(Buffer+Buffer_Offset+2)>>2)&0x03;
            int8u padding_bit0       =(CC1(Buffer+Buffer_Offset+2)>>1)&0x01;
            //Coherancy
            if (Mpega_SamplingRate[ID0][sampling_frequency0]==0 || Mpega_Coefficient[ID0][layer0]==0 || Mpega_BitRate[ID0][layer0][bitrate_index0]==0 || Mpega_SlotSize[layer0]==0)
                Buffer_Offset++; //False start
            else
            {
                //Testing next start, to be sure
                size_t Size0=(Mpega_Coefficient[ID0][layer0]*Mpega_BitRate[ID0][layer0][bitrate_index0]*1000/Mpega_SamplingRate[ID0][sampling_frequency0]+(padding_bit0?1:0))*Mpega_SlotSize[layer0];
                if (IsSub && Buffer_Offset+Size0==Buffer_Size)
                    break;
                if (File_Offset+Buffer_Offset+Size0!=File_Size-File_EndTagSize)
                {
                    //Padding
                    while (Buffer_Offset+Size0+4<=Buffer_Size && Buffer[Buffer_Offset+Size0]==0x00)
                        Size0++;

                    if (Buffer_Offset+Size0+4>Buffer_Size)
                        return false; //Need more data

                    //Tags
                    bool Tag_Found0;
                    if (!File__Tags_Helper::Synchronize(Tag_Found0, Size0))
                        return false;
                    if (Tag_Found0)
                        return true;
                    if (File_Offset+Buffer_Offset+Size0==File_Size-File_EndTagSize)
                        break;

                    //Testing
                    if ((CC2(Buffer+Buffer_Offset+Size0)&0xFFE0)!=0xFFE0 || (CC1(Buffer+Buffer_Offset+Size0+2)&0xF0)==0xF0 || (CC1(Buffer+Buffer_Offset+Size0+2)&0x0C)==0x0C)
                    {
                        //Testing VBRI in a malformed frame
                        bool VbriFound=false;
                        for (size_t Pos=Buffer_Offset+4; Pos+4<Buffer_Offset+Size0; Pos++)
                        {
                           int32u ToTest=CC4(Buffer+Pos);
                            if (ToTest==0x496E666F  // "Info"
                             || ToTest==0x56425249  // "VBRI"
                             || ToTest==0x58696E67) // "Xing"
                            {
                                VbriFound=true;
                                break;
                            }
                            if (Buffer[Pos])
                                break; //Only NULL bytes are authorized before VBRI header
                        }
                        if (VbriFound)
                            break;
                        Buffer_Offset++;
                    }
                    else
                    {
                        //Retrieving some info
                        int8u ID1                =(CC1(Buffer+Buffer_Offset+Size0+1)>>3)&0x03;
                        int8u layer1             =(CC1(Buffer+Buffer_Offset+Size0+1)>>1)&0x03;
                        int8u bitrate_index1     =(CC1(Buffer+Buffer_Offset+Size0+2)>>4)&0x0F;
                        int8u sampling_frequency1=(CC1(Buffer+Buffer_Offset+Size0+2)>>2)&0x03;
                        int8u padding_bit1       =(CC1(Buffer+Buffer_Offset+Size0+2)>>1)&0x01;
                        //Coherancy
                        if (Mpega_SamplingRate[ID1][sampling_frequency1]==0 || Mpega_Coefficient[ID1][layer1]==0 || Mpega_BitRate[ID1][layer1][bitrate_index1]==0 || Mpega_SlotSize[layer1]==0)
                            Buffer_Offset++; //False start
                        else
                        {
                            //Testing next start, to be sure
                            size_t Size1=(Mpega_Coefficient[ID1][layer1]*Mpega_BitRate[ID1][layer1][bitrate_index1]*1000/Mpega_SamplingRate[ID1][sampling_frequency1]+(padding_bit1?1:0))*Mpega_SlotSize[layer1];
                            if (IsSub && Buffer_Offset+Size0+Size1==Buffer_Size)
                                break;
                            if (File_Offset+Buffer_Offset+Size0+Size1!=File_Size-File_EndTagSize)
                            {
                                //Padding
                                while (Buffer_Offset+Size0+Size1+4<=Buffer_Size && Buffer[Buffer_Offset+Size0+Size1]==0x00)
                                    Size0++;

                                if (Buffer_Offset+Size0+Size1+4>Buffer_Size)
                                    return false; //Need more data

                                //Tags
                                bool Tag_Found1;
                                if (!File__Tags_Helper::Synchronize(Tag_Found1, Size0+Size1))
                                    return false;
                                if (Tag_Found1)
                                    return true;
                                if (File_Offset+Buffer_Offset+Size0+Size1==File_Size-File_EndTagSize)
                                    break;

                                //Testing
                                if ((CC2(Buffer+Buffer_Offset+Size0+Size1)&0xFFE0)!=0xFFE0 || (CC1(Buffer+Buffer_Offset+Size0+Size1+2)&0xF0)==0xF0 || (CC1(Buffer+Buffer_Offset+Size0+Size1+2)&0x0C)==0x0C)
                                    Buffer_Offset++;
                                else
                                {
                                    //Retrieving some info
                                    int8u ID2                =(CC1(Buffer+Buffer_Offset+Size0+Size1+1)>>3)&0x03;
                                    int8u layer2             =(CC1(Buffer+Buffer_Offset+Size0+Size1+1)>>1)&0x03;
                                    int8u bitrate_index2     =(CC1(Buffer+Buffer_Offset+Size0+Size1+2)>>4)&0x0F;
                                    int8u sampling_frequency2=(CC1(Buffer+Buffer_Offset+Size0+Size1+2)>>2)&0x03;
                                    int8u padding_bit2       =(CC1(Buffer+Buffer_Offset+Size0+Size1+2)>>1)&0x01;
                                    //Coherancy
                                    if (Mpega_SamplingRate[ID2][sampling_frequency2]==0 || Mpega_Coefficient[ID2][layer2]==0 || Mpega_BitRate[ID2][layer2][bitrate_index2]==0 || Mpega_SlotSize[layer2]==0)
                                        Buffer_Offset++; //False start
                                    else
                                    {
                                        //Testing next start, to be sure
                                        size_t Size2=(Mpega_Coefficient[ID2][layer2]*Mpega_BitRate[ID2][layer2][bitrate_index2]*1000/Mpega_SamplingRate[ID2][sampling_frequency2]+(padding_bit2?1:0))*Mpega_SlotSize[layer2];
                                        if (IsSub && Buffer_Offset+Size0+Size1+Size2==Buffer_Size)
                                            break;
                                        if (File_Offset+Buffer_Offset+Size0+Size1+Size2!=File_Size-File_EndTagSize)
                                        {
                                            //Padding
                                            while (Buffer_Offset+Size0+Size1+Size2+4<=Buffer_Size && Buffer[Buffer_Offset+Size0+Size1+Size2]==0x00)
                                                Size0++;

                                            if (Buffer_Offset+Size0+Size1+Size2+4>Buffer_Size)
                                            {
                                                if (IsSub || File_Offset+Buffer_Offset+Size0+Size1+Size2<File_Size)
                                                    break;
                                                return false; //Need more data
                                            }

                                            //Tags
                                            bool Tag_Found2;
                                            if (!File__Tags_Helper::Synchronize(Tag_Found2, Size0+Size1+Size2))
                                                return false;
                                            if (Tag_Found2)
                                                return true;
                                            if (File_Offset+Buffer_Offset+Size0+Size1+Size2==File_Size-File_EndTagSize)
                                                break;

                                            //Testing
                                            if ((CC2(Buffer+Buffer_Offset+Size0+Size1+Size2)&0xFFE0)!=0xFFE0 || (CC1(Buffer+Buffer_Offset+Size0+Size1+Size2+2)&0xF0)==0xF0 || (CC1(Buffer+Buffer_Offset+Size0+Size1+Size2+2)&0x0C)==0x0C)
                                                Buffer_Offset++;
                                            else
                                                break; //while()
                                        }
                                        else
                                            break; //while()
                                    }
                                }
                            }
                            else
                                break; //while()
                        }
                    }
                }
                else
                    break; //while()
            }
        }
    }

    //Parsing last bytes if needed
    if (Buffer_Offset+4>Buffer_Size)
    {
        if (Buffer_Offset+3==Buffer_Size && (CC2(Buffer+Buffer_Offset)&0xFFE0)!=0xFFE0)
            Buffer_Offset++;
        if (Buffer_Offset+2==Buffer_Size && (CC2(Buffer+Buffer_Offset)&0xFFE0)!=0xFFE0)
            Buffer_Offset++;
        if (Buffer_Offset+1==Buffer_Size && CC1(Buffer+Buffer_Offset)!=0x00)
            Buffer_Offset++;
        return false;
    }

    if (Frame_Count == 0 && !IsSub) {
        FrameInfo.PTS = 0;
    }

    //Synched is OK
    if (LastSync_Offset != (int64u)-1 && File_Offset + Buffer_Offset > LastSync_Offset) {
        LastSync_Offset_AreNotZero = Buffer_Offset_BeforeZero != Buffer_Offset;
        SynchLost("MPEG-Audio", File_Offset + Buffer_Offset - LastSync_Offset, !LastSync_Offset_AreNotZero);
    }
    LastSync_Offset = File_Offset + Buffer_Offset;
    LastSync_Offset_AreNotZero = false;
    return true;
}

//---------------------------------------------------------------------------
bool File_Mpega::Synched_Test()
{
    //Tags
    if (!File__Tags_Helper::Synched_Test() && File_Offset+Buffer_Size<File_Size)
        return false;

    LastSync_Offset=File_Offset+Buffer_Offset+Element_Offset;

    //Must have enough buffer for having header
    if (Buffer_Offset+3>Buffer_Size)
        return false;

    //Quick test of synchro
    if (Buffer[Buffer_Offset  ]!=0xFF
     || (Buffer[Buffer_Offset+1]&0xE0)!=0xE0
     || (Buffer[Buffer_Offset+2]&0xF0)==0xF0
     || (Buffer[Buffer_Offset+2]&0x0C)==0x0C)
    {
        Synched=false;
        return true;
    }

    //Retrieving some info
    int8u ID0                =(CC1(Buffer+Buffer_Offset+1)>>3)&0x03;
    int8u layer0             =(CC1(Buffer+Buffer_Offset+1)>>1)&0x03;
    int8u bitrate_index0     =(CC1(Buffer+Buffer_Offset+2)>>4)&0x0F;
    int8u sampling_frequency0=(CC1(Buffer+Buffer_Offset+2)>>2)&0x03;
    if (Mpega_SamplingRate[ID0][sampling_frequency0]==0 || Mpega_Coefficient[ID0][layer0]==0 || Mpega_BitRate[ID0][layer0][bitrate_index0]==0 || Mpega_SlotSize[layer0]==0)
    {
        Synched=false;
        return true;
    }

    //We continue
    return true;
}

//***************************************************************************
// Buffer - Demux
//***************************************************************************

//---------------------------------------------------------------------------
#if MEDIAINFO_DEMUX
bool File_Mpega::Demux_UnpacketizeContainer_Test()
{
    //Retrieving some info
    int8u ID0                =(CC1(Buffer+Buffer_Offset+1)>>3)&0x03;
    int8u layer0             =(CC1(Buffer+Buffer_Offset+1)>>1)&0x03;
    int8u bitrate_index0     =(CC1(Buffer+Buffer_Offset+2)>>4)&0x0F;
    int8u sampling_frequency0=(CC1(Buffer+Buffer_Offset+2)>>2)&0x03;
    int8u padding_bit0       =(CC1(Buffer+Buffer_Offset+2)>>1)&0x01;

    if (Mpega_SamplingRate[ID][sampling_frequency]==0 || Mpega_Coefficient[ID][layer]==0 || Mpega_BitRate[ID][layer][bitrate_index]==0 || Mpega_SlotSize[layer]==0)
        return true; //Synhro issue

    #if MEDIAINFO_ADVANCED
        if (Frame_Count && File_Demux_Unpacketize_StreamLayoutChange_Skip)
        {
            int8u mode0              =CC1(Buffer+Buffer_Offset+3)>>6;
            if (sampling_frequency0!=sampling_frequency_Frame0 || Mpega_Channels[mode0]!=Mpega_Channels[mode_Frame0])
            {
                return true;
            }
        }
    #endif //MEDIAINFO_ADVANCED

    Demux_Offset=Buffer_Offset+(Mpega_Coefficient[ID0][layer0]*Mpega_BitRate[ID0][layer0][bitrate_index0]*1000/Mpega_SamplingRate[ID0][sampling_frequency0]+(padding_bit0?1:0))*Mpega_SlotSize[layer0];

    if (Demux_Offset>Buffer_Size)
        return false;

    Demux_UnpacketizeContainer_Demux();

    return true;
}
#endif //MEDIAINFO_DEMUX

//***************************************************************************
// Buffer - Globzl
//***************************************************************************

//---------------------------------------------------------------------------
void File_Mpega::Read_Buffer_Unsynched()
{
    FrameInfo.PTS = (int64u)-1;
    LastSync_Offset = (int64u)-1;
    LastSync_Offset_AreNotZero = false;
}

//---------------------------------------------------------------------------
void File_Mpega::Read_Buffer_Continue()
{
    if (IsSub)
    {
        LastSync_Offset = (int64u)-1;
        LastSync_Offset_AreNotZero = false;
    }
}

//***************************************************************************
// Buffer - Per element
//***************************************************************************

//---------------------------------------------------------------------------
void File_Mpega::Header_Parse()
{
    //Parsing
    BS_Begin();
    Skip_S2(11,                                                 "syncword");
    Get_S1 (2, ID,                                              "ID"); Param_Info1(Mpega_Version[ID]);
    Get_S1 (2, layer,                                           "layer"); Param_Info1(Mpega_Layer[layer]);
    Get_SB (   protection_bit,                                  "protection_bit");
    Get_S1 (4, bitrate_index,                                   "bitrate_index"); Param_Info2(Mpega_BitRate[ID][layer][bitrate_index], " Kbps");
    Get_S1 (2, sampling_frequency,                              "sampling_frequency"); Param_Info2(Mpega_SamplingRate[ID][sampling_frequency], " Hz");
    Get_SB (   padding_bit,                                     "padding_bit");
    Skip_SB(                                                    "private_bit");
    Get_S1 (2, mode,                                            "mode"); Param_Info2(Mpega_Channels[mode], " channels"); Param_Info1(Mpega_Codec_Profile[mode]);
    Get_S1 (2, mode_extension,                                  "mode_extension"); Param_Info1(Mpega_Codec_Profile_Extension[mode_extension]);
    Get_SB (   copyright,                                       "copyright");
    Get_SB (   original_home,                                   "original_home");
    Get_S1 (2, emphasis,                                        "emphasis"); Param_Info1(Mpega_Emphasis[emphasis]);
    BS_End();

    //Coherancy
    if (Mpega_SamplingRate[ID][sampling_frequency]==0 || Mpega_Coefficient[ID][layer]==0 || Mpega_BitRate[ID][layer][bitrate_index]==0 || Mpega_SlotSize[layer]==0)
    {
        Element_Offset=1;
        Header_Fill_Size(1);
        Header_Fill_Code(0, "False start");
        Synched=false;
        return;
    }

    //Filling
    int64u Size = ((int64u)Mpega_Coefficient[ID][layer] * (int64u)Mpega_BitRate[ID][layer][bitrate_index] * 1000 / (int64u)Mpega_SamplingRate[ID][sampling_frequency] + (padding_bit ? 1 : 0)) * (int64u)Mpega_SlotSize[layer];

    //Special case: tags is inside the last frame
    if (File_Offset+Buffer_Offset+Size>=File_Size-File_EndTagSize)
        Size=File_Size-File_EndTagSize-(File_Offset+Buffer_Offset);

    Header_Fill_Size(Size);
    Header_Fill_Code(0, "frame");

    //Filling error detection
    sampling_frequency_Count[sampling_frequency]++;
    mode_Count[mode]++;

    FILLING_BEGIN();
        #if MEDIAINFO_DEMUX
            #if MEDIAINFO_ADVANCED
                if (!Frame_Count)
                {
                    File_Demux_Unpacketize_StreamLayoutChange_Skip=Config->File_Demux_Unpacketize_StreamLayoutChange_Skip_Get();
                    if (File_Demux_Unpacketize_StreamLayoutChange_Skip)
                    {
                        sampling_frequency_Frame0=sampling_frequency;
                        mode_Frame0=mode;
                    }
                }
            #endif //MEDIAINFO_ADVANCED
        #endif //MEDIAINFO_DEMUX
    FILLING_END();
}

//***************************************************************************
// Elements
//***************************************************************************

//---------------------------------------------------------------------------
void File_Mpega::Data_Parse()
{
    //If false start
    if (Element_Size==0)
    {
        Element_DoNotShow();
        return;
    }

    LastSync_Offset = File_Offset + Buffer_Offset + Element_Size;
    LastSync_Offset_AreNotZero = false;

    //VBR and library headers
    if (Frame_Count<3) //No need to do it too much
    {
        if (!Header_Xing())
            Header_VBRI();
    }

    //Counting
    if (File_Offset+Buffer_Offset+Element_Size==File_Size-File_EndTagSize)
        Frame_Count_Valid=Frame_Count; //Finish MPEG Audio frames in case of there are less than Frame_Count_Valid frames
    if (Frame_Count==0 && Frame_Count_NotParsedIncluded==0)
        PTS_Begin=FrameInfo.PTS;
    {
        int16u Samples;
        if (ID==3 && layer==3) //MPEG 1 layer 1
             Samples=384;
        else if ((ID==2 || ID==0) && layer==1) //MPEG 2 or 2.5 layer 3
            Samples=576;
        else
            Samples=1152;

        Frequency_b=Mpega_SamplingRate[ID][sampling_frequency];
        TS_Add(Samples);
    }

    //LAME
    if (Encoded_Library.empty() && (Frame_Count<Frame_Count_Valid || File_Offset+Buffer_Offset+Element_Size==File_Size-File_EndTagSize)) //Can be elsewhere... At the start, or end frame
        Header_Encoders();

    //Filling
    BitRate_Count[Mpega_BitRate[ID][layer][bitrate_index]]++;
    Channels_Count[mode]++;
    Extension_Count[mode_extension]++;
    Emphasis_Count[emphasis]++;

    //Partial frame
    auto FrameSize = ((int64u)Mpega_Coefficient[ID][layer] * (int64u)Mpega_BitRate[ID][layer][bitrate_index] * 1000 / (int64u)Mpega_SamplingRate[ID][sampling_frequency] + (padding_bit ? 1 : 0))* (int64u)Mpega_SlotSize[layer];
    auto RealFrameSize = Header_Size + Element_Size;
    if (padding_bit && FrameSize - RealFrameSize == 1)
    {
        Fill_Conformance(BuildConformanceName(ParserName, "MPEG-Audio", "PaddingBit").c_str(), string("Padding bit is wrong") + (!IsSub && File_Offset + Buffer_Offset + Element_Size == File_Size ? " at end of the file" : "") + " (actual 1, expected 0)", {}, Conformance_Warning);
    }
    else if (RealFrameSize < FrameSize)
    {
        IsTruncated(File_Offset+Buffer_Offset+FrameSize-Header_Size, true, "MPEG-Audio");
        Element_Info1("Partial frame");
        Skip_XX(Element_Size,                                   "Data");
        return;
    }

    if (Status[IsFilled])
    {
        Skip_XX(Element_Size,                                   "Data");
        return;
    }

    //error_check
    if (!protection_bit)
    {
        Element_Begin1("error_check");
        Skip_B2(                                                "crc_check");
        Element_End0();
    }

    //audio_data
    Element_Begin1("audio_data");
    switch (layer)
    {
        case 1 : //Layer 3
                audio_data_Layer3();
                break;
        default: Skip_XX(Element_Size-Element_Offset,           "(data)");
    }
    Element_End0();

    //MP3 Surround detection
    for (int64u Element_Offset_S=Element_Offset; Element_Offset_S+4<Element_Size; Element_Offset_S++)
    {
        if ( Buffer[(size_t)(Buffer_Offset+Element_Offset_S  )]      ==0xCF
         && (Buffer[(size_t)(Buffer_Offset+Element_Offset_S+1)]&0xF0)==0x30) //12 bits, 0xCF3x
        {
            int8u Surround_Size=((Buffer[(size_t)(Buffer_Offset+Element_Offset_S+1)]&0x0F)<<4)
                              | ((Buffer[(size_t)(Buffer_Offset+Element_Offset_S+2)]&0xF0)>>4);
            int16u CRC12       =((Buffer[(size_t)(Buffer_Offset+Element_Offset_S+2)]&0x0F)<<8)
                              |   Buffer[(size_t)(Buffer_Offset+Element_Offset_S+3)];
            if (Element_Offset_S+Surround_Size-4>Element_Size)
                break;

            //CRC
            int16u CRC12_Calculated=0x0FFF;
            int8u* Data=(int8u*)Buffer+(size_t)(Buffer_Offset+Element_Offset_S+4);
            if (Element_Offset_S+Surround_Size+4>=Element_Size)
                break;
            for (int8u Surround_Pos=0; Surround_Pos<Surround_Size-4; Surround_Pos++)
                CRC12_Calculated=0x0FFF & (((CRC12_Calculated<<8)&0xff00)^Mpega_CRC12_Table[((CRC12_Calculated>>4) ^ *Data++) & 0xff]);
            if (CRC12_Calculated!=CRC12)
                break;

            //Parsing
            Skip_XX(Element_Offset_S-Element_Offset,            "data");
            BS_Begin();
            Element_Begin1("Surround");
            Skip_S2(12,                                         "Sync");
            Skip_S1( 8,                                         "Size");
            Skip_S2(12,                                         "CRC12");
            BS_End();
            Skip_XX(Surround_Size-4,                            "data");
            Element_End0();

            //Filling
            Surround_Frames++;
            break;
        }
    }

    if (Element_Offset<Element_Size)
        Skip_XX(Element_Size-Element_Offset,                    "next data");

    FILLING_BEGIN();
        //Filling
        if (IsSub && BitRate_Count.size()>1 && !Encoded_Library.empty())
            Frame_Count_Valid=Frame_Count;
        if (!Status[IsAccepted])
            File__Analyze::Accept("MPEG Audio");
        if (!Status[IsFilled] && Frame_Count>=Frame_Count_Valid)
        {
            Fill("MPEG Audio");

            //Jumping
            if (!IsSub && Config->ParseSpeed<1.0 && File_Offset+Buffer_Offset<File_Size/2)
            {
                File__Tags_Helper::GoToFromEnd(16*1024, "MPEG-A");
                LastSync_Offset=(int64u)-1;
                if (File_GoTo!=(int64u)-1)
                    Open_Buffer_Unsynch();
            }
        }

        //Detect Id3v1 tags inside a frame
        if (!IsSub && File_Offset+Buffer_Offset+(size_t)Element_Size>File_Size-File_EndTagSize)
        {
            Open_Buffer_Unsynch();
            File__Analyze::Data_GoTo(File_Size-File_EndTagSize, "Tags inside a frame, parsing the tags");
        }
    FILLING_END();

    Merge_Conformance();
}

//---------------------------------------------------------------------------
void File_Mpega::audio_data_Layer3()
{
    if (mode>=4)
        return;
    const bool mono=(mode==3);
    const bool mpeg1=(ID==3);
    int16u main_data_end;
    BS_Begin();
    Get_S2 (mpeg1?9:8, main_data_end,                               "main_data_end");
    if ((int32u)main_data_end>Reservoir_Max)
        Reservoir_Max=main_data_end;
    Reservoir+=main_data_end;
    if (mpeg1) //MPEG-1
    {
            Skip_S1(mono?5:3,                                          "private_bits");
    }
    else
    {
            Skip_S1(mono?1:2,                                          "private_bits");
    }
    if (mpeg1) //MPEG-1
    {
        Element_Begin1("scfsi");
        for(int8u ch=0; ch<Mpega_Channels[mode]; ch++)
            for(int8u scfsi_band=0; scfsi_band<4; scfsi_band++)
            {
                bool scfsi;
                Get_SB (   scfsi,                               "scfsi");
                if (scfsi)
                    Scfsi++;
            }
        Element_End0();
    }
    for(int8u gr=0; gr<(ID==3?2:1); gr++)
    {
        Element_Begin1("granule");
        for(int8u ch=0; ch<Mpega_Channels[mode]; ch++)
        {
            Element_Begin1("channel");
            Skip_S2(12,                                         "part2_3_length");
            Skip_S2(9,                                          "big_values");
            Skip_S1(8,                                          "global_gain");
            if (mpeg1) //MPEG-1
                Skip_S1(4,                                      "scalefac_compress");
            else
                Skip_S2(9,                                      "scalefac_compress");
            bool blocksplit_flag;
            Get_SB (   blocksplit_flag,                         "blocksplit_flag");
            if (blocksplit_flag==1)
            {
                int8u block_type;
                bool  mixed_block_flag;
                Get_S1 (2, block_type,                          "block_type");
                Get_SB (   mixed_block_flag,                    "mixed_block_flag");
                for (int8u region=0; region<2; region++)
                    Skip_S1(5,                                  "table_select");
                for (int8u window=0; window<3; window++)
                    Skip_S1(3,                                  "subblock_gain");
                if (block_type == 2)
                {
                    if (mixed_block_flag==1)
                    {
                        Param_Info1("Mixed");
                        Block_Count[2]++; //Mixed
                    }
                    else
                    {
                        Param_Info1("Short");
                        Block_Count[1]++; //Short
                    }
                }
                else
                {
                    Param_Info1("Long");
                    Block_Count[0]++; //Long
                }
            }
            else
            {
                for (int8u region=0; region<3; region++)
                    Skip_S1(5,                                  "table_select");
                Skip_S1(4,                                      "region0_count");
                Skip_S1(3,                                      "region1_count");
                Param_Info1("Long");
                Block_Count[0]++; //Long
            }
            if (mpeg1) //MPEG-1
                Skip_SB(                                        "preflag");
            bool scalefac;
            Get_SB (   scalefac,                                "scalefac_scale");
            if (scalefac)
                Scalefac++;
            Skip_SB(                                            "count1table_select");
            Element_End0();
        } //channels
        Element_End0();
    } //granules
    BS_End();
    //Skip_XX(Element_Size-main_data_end-Element_Offset,          "main_data");
}

//---------------------------------------------------------------------------
void File_Mpega::Data_Parse_Fill()
{
}

//---------------------------------------------------------------------------
bool File_Mpega::Header_Xing()
{
    int32u Xing_Header_Offset;
    if (ID==3) //MPEG-1
        if (mode==3) //Mono
            Xing_Header_Offset=21-4;
        else
            Xing_Header_Offset=36-4;
    else //MPEG-2 or 2.5
        if (mode==3) //Mono
            Xing_Header_Offset=13-4;
        else
            Xing_Header_Offset=21-4;
    if (Buffer_Offset+Xing_Header_Offset+128<Buffer_Size)
    {
        const int8u* Xing_Header=Buffer+Buffer_Offset+Xing_Header_Offset;
        if (CC4(Xing_Header)==CC4("Xing") || CC4(Xing_Header)==CC4("Info"))
        {
            //This is a "tag"
            Element_Info1("Tag (Xing)");

            //Parsing
            Element_Begin1("Xing");
            Element_Begin1("Xing header");
            Skip_XX(Xing_Header_Offset,                         "Junk");
            int32u Flags;
            bool FrameCount, FileSize, TOC, Scale, Lame;
            Skip_C4(                                            "Xing");
            Get_B4 (Flags,                                      "Flags");
                Get_Flags(Flags, 0, FrameCount,                 "FrameCount");
                Get_Flags(Flags, 1, FileSize,                   "FileSize");
                Get_Flags(Flags, 2, TOC,                        "TOC");
                Get_Flags(Flags, 3, Scale,                      "Scale");
                Get_Flags(Flags, 4, Lame,                       "Lame");
            int32u Xing_Header_Size=8
                                   +(FrameCount?  4:0)    //FrameCount
                                   +(FileSize?    4:0)    //FileSize
                                   +(TOC?       100:0)    //TOC
                                   +(Scale?       4:0)    //Scale
                                   +(Lame?      348:0);   //Lame
            Element_End0();
            //Element size
            if (Xing_Header_Size>Element_Size-Xing_Header_Offset)
                return false; //Error tag size

            //Parsing
            if (FrameCount)
            {
                Get_B4 (VBR_Frames,                             "FrameCount"); //FrameCount exclude this frame
                VBR_Frames_IsCbr=(CC4(Xing_Header)==CC4("Info"));
            }
            if (FileSize)
            {
                int32u VBR_FileSize_Temp;
                Get_B4 (VBR_FileSize_Temp,                      "FileSize");
                if (VBR_FileSize_Temp>4+Element_Size)
                   VBR_FileSize=VBR_FileSize_Temp-4-Element_Size; //FileSize include the Xing element
            }
            if (TOC)
                Skip_XX(100,                                    "TOC");
            if (Scale)
                Get_B4 (Xing_Scale,                             "Scale");
            string Lib;
            Element_End0();
            Peek_String(4, Lib);
            if (Lame || Lib=="LAME" || Lib=="GOGO" || Lib=="L3.9")
                Header_Encoders_Lame();

            //Clearing Error detection
            sampling_frequency_Count.clear();
            mode_Count.clear();

            return true;
        }
    }
    return false;
}

//---------------------------------------------------------------------------
bool File_Mpega::Header_VBRI()
{
    const size_t Fraunhofer_Header_Offset=36-4;
    if (Buffer_Offset+Fraunhofer_Header_Offset+32<Buffer_Size)
    {
        const int8u* Fraunhofer_Header=Buffer+Buffer_Offset+Fraunhofer_Header_Offset;
        if (CC4(Fraunhofer_Header)==CC4("VBRI") && CC2(Fraunhofer_Header+4)==0x0001) //VBRI v1 only
        {
            //This is a "tag"

            Element_Info1("Tag (VBRI)");

            //Parsing
            int32u VBR_FileSize_Temp;
            int16u TableSize, TableScale, EntryBytes;
            Skip_XX(Fraunhofer_Header_Offset,                   "Junk");
            Element_Begin1("VBRI");
            Skip_C4(                                            "Sync");
            Skip_B2(                                            "Version");
            Skip_B2(                                            "Delay");
            Skip_B2(                                            "Quality");
            Get_B4 (VBR_FileSize_Temp,                          "StreamBytes");
            Get_B4 (VBR_Frames,                                 "StreamFrames"); //Multiplied by SamplesPerFrame (1152 for >=32KHz, else 576) --> Duration in samples
            Get_B2 (TableSize,                                  "TableSize");
            Get_B2 (TableScale,                                 "TableScale");
            Get_B2 (EntryBytes,                                 "EntryBytes");
            Skip_B2(                                            "EntryFrames"); //Count of frames per entry
            Element_Begin1("Table");
                for (int16u Pos=0; Pos<TableSize; Pos++)
                {
                    switch (EntryBytes)
                    {
                        case 1 : {Info_B1(Entry,                "Entry"); Param_Info2 (Entry*TableScale, " bytes");} break;
                        case 2 : {Info_B2(Entry,                "Entry"); Param_Info2 (Entry*TableScale, " bytes");} break;
                        case 4 : {Info_B4(Entry,                "Entry"); Param_Info2 (Entry*TableScale, " bytes");} break;
                        default: Skip_XX(EntryBytes,            "Entry");
                    }
                }
            Element_End0();
            Element_End0();
            VBR_FileSize=VBR_FileSize_Temp;

            //Clearing Error detection
            sampling_frequency_Count.clear();
            mode_Count.clear();

            return true;
        }
    }
    return false;
}

//---------------------------------------------------------------------------
bool File_Mpega::Header_Encoders()
{
    std::string BufferS((const char*)(Buffer+Buffer_Offset), (size_t)Element_Size);
    size_t Buffer_Pos;

    //Lame
    Buffer_Pos=BufferS.find("LAME");
    if (Buffer_Pos!=std::string::npos && Buffer_Pos<=Element_Size-8)
    {
        Element_Info1("With tag (Lame)");
        Element_Offset=Buffer_Pos;
        if (Element_Offset+20<=Element_Size)
            Get_String (20, Encoded_Library,                    "Encoded_Library");
        else
            Get_String ( 8, Encoded_Library,                    "Encoded_Library");
        Encoded_Library.erase(Encoded_Library.find_last_not_of("AU\xAA")+1);
        Element_Offset=0; //Reseting it
        return true;
    }

    //RCA
    Buffer_Pos=BufferS.find("RCA mp3PRO Encoder");
    if (Buffer_Pos!=std::string::npos && Buffer_Pos<Element_Size-23)
    {
        Element_Info1("With tag (RCA)");
        Encoded_Library="RCA ";
        Encoded_Library+=string((const char*)(Buffer+Buffer_Offset+18), 5);
        return true;
    }

    //Thomson
    Buffer_Pos=BufferS.find("THOMSON mp3PRO Encoder");
    if (Buffer_Pos!=std::string::npos && Buffer_Pos<Element_Size-29)
    {
        Element_Info1("With tag (Thomson)");
        Encoded_Library="Thomson ";
        Encoded_Library+=string((const char*)(Buffer+Buffer_Offset+22), 6);
        return true;
    }

    //Gogo (old)
    Buffer_Pos=BufferS.find("MPGE");
    if (Buffer_Pos!=std::string::npos)
    {
        Element_Info1("With tag (Gogo)");
        Encoded_Library="Gogo <3.0";
        return true;
    }

    //Gogo (new)
    Buffer_Pos=BufferS.find("GOGO");
    if (Buffer_Pos!=std::string::npos)
    {
        Element_Info1("With tag (Gogo)");
        Encoded_Library="Gogo >=3.0";
        return true;
    }

    return false;
}

void File_Mpega::Header_Encoders_Lame()
{
    bool HasInfoTag=false;
    if (Element_Offset+9<=Element_Size)
    {
        const int8u* Tag=Buffer+Buffer_Offset+(size_t)Element_Offset;
        int32u Name=BigEndian2int32u(Tag);
        if (Name==0x4C414D45   // "LAME"
         && Tag[5]=='.')
        {
            //Needs addtional tests, as v3.89 and less have no Lame Info tag, but v3.100 exists too
            if ( Tag[4]> '3'                                                                                    // v4 or more
             || (Tag[4]=='3' && Tag[6]=='9')                                                                    // v3.9yz-v3.9yz
             ||  Tag[4]=='3' && Tag[8]>='0' && Tag[8]<='9')                                                     // v3.xy0-v3.xy9
                HasInfoTag=true;
        }
        if (Name==0x4C414D45 && Tag[4]=='H') // "LAMEH", Helix MP3 encoder
            HasInfoTag=true;
        if (Name==0x4C332E39   // "L3.9"
         && Tag[4]=='9')
            HasInfoTag=true; //Form old code, to be confirmed: Ugly version string in Lame 3.99.1 "L3.99r1\0".
    }
    if (HasInfoTag)
    {
        int8u Flags, lowpass, EncodingFlags, BitRate, StereoMode;
        Param_Info1(Ztring(__T("V "))+Ztring::ToZtring((100-Xing_Scale)/10));
        Param_Info1(Ztring(__T("q "))+Ztring::ToZtring((100-Xing_Scale)%10));
        Get_String (9, Encoded_Library,                         "Encoded_Library");
        Get_B1 (Flags,                                          "Flags");
        if ((Flags&0xF0)<=0x20) //Rev. 0 or 1, http://gabriel.mp3-tech.org/mp3infotag.html and Rev. 2 was seen.
        {
            Param_Info1(Lame_Method[Flags&0x0F]);
            BitRate_Mode=Lame_BitRate_Mode[Flags&0x0F];
            if ((Flags&0x0F)==1 || (Flags&0x0F)==8) //2 possible values for CBR
                VBR_Frames=0;
        }
        Get_B1 (lowpass,                                        "Lowpass filter value"); Param_Info2(lowpass*100, " Hz");
        Skip_B4(                                                "Peak signal amplitude");
        Skip_B2(                                                "Radio Replay Gain");
        Skip_B2(                                                "Audiophile Replay Gain");
        Get_B1 (EncodingFlags,                                  "Encoding Flags"); Param_Info1(Ztring(__T("ATH Type="))+Ztring::ToZtring(Flags&0x0F));
            Skip_Flags(EncodingFlags, 4,                        "nspsytune");
            Skip_Flags(EncodingFlags, 5,                        "nssafejoint");
            Skip_Flags(EncodingFlags, 6,                        "nogap (after)");
            Skip_Flags(EncodingFlags, 7,                        "nogap (before)");
        Get_B1 (BitRate,                                        "BitRate");
        Skip_B3(                                                "Encoder delays");
        BS_Begin();
        Skip_S1(2,                                              "Source sample frequency");
        Skip_SB(                                                "unwise settings used");
        Get_S1 (3, StereoMode,                                  "Stereo mode");
        Skip_S1(2,                                              "noise shapings");
        BS_End();
        Skip_B1(                                                "MP3 Gain");
        Skip_B2(                                                "Preset and surround info");
        Skip_B4(                                                "MusicLength");
        Skip_B2(                                                "MusicCRC");
        Skip_B2(                                                "CRC-16 of Info Tag");

        FILLING_BEGIN();
            Encoded_Library_Settings+=__T("-m ");
            switch(StereoMode)
            {
                case 0 : Encoded_Library_Settings+=__T("m"); break;
                case 1 : Encoded_Library_Settings+=__T("s"); break;
                case 2 : Encoded_Library_Settings+=__T("d"); break;
                case 3 : Encoded_Library_Settings+=__T("j"); break;
                case 4 : Encoded_Library_Settings+=__T("f"); break;
                case 5 : Encoded_Library_Settings+=__T("a"); break;
                case 6 : Encoded_Library_Settings+=__T("i"); break;
                default: ;
            }
            if (Xing_Scale<=100) //Xing_Scale is used for LAME quality
            {
                Encoded_Library_Settings+=__T( " -V ")+Ztring::ToZtring((100-Xing_Scale)/10);
                Encoded_Library_Settings+=__T( " -q ")+Ztring::ToZtring((100-Xing_Scale)%10);
            }
            if (lowpass)
                Encoded_Library_Settings+=(Encoded_Library_Settings.empty()?__T("-lowpass "):__T(" -lowpass "))+((lowpass%10)?Ztring::ToZtring(((float)lowpass)/10, 1):Ztring::ToZtring(lowpass/10));
            switch (Flags&0x0F)
            {
                case  2 :
                case  9 : //ABR
                            Encoded_Library_Settings+=__T(" --abr"); break;
                case  3 : //VBR (old/rh)
                            Encoded_Library_Settings+=__T(" --vbr-old"); break;
                case  4 : //VBR (new/mtrh)
                            Encoded_Library_Settings+=__T(" --vbr-new"); break;
                case  5 : //VBR (?/mt)
                            Encoded_Library_Settings+=__T(" --vbr-mt"); break;
                default : ;
            }
            if (BitRate!=0x00 && BitRate!=0xFF)
            {
                switch (Flags&0x0F)
                {
                    case  1 :
                    case  8 : //CBR
                        Encoded_Library_Settings+=__T(" -b ")+Ztring::ToZtring(BitRate);
                        break;
                    case  2 :
                    case  9 : //ABR
                        BitRate_Nominal.From_Number(BitRate*1000);
                        Encoded_Library_Settings+=__T(" ")+Ztring::ToZtring(BitRate);
                        break;
                    case  3 : //VBR (old/rh)
                    case  4 : //VBR (new/mtrh)
                    case  5 : //VBR (?/mt)
                        BitRate_Minimum.From_Number(BitRate*1000);
                        Encoded_Library_Settings+=__T(" -b ")+Ztring::ToZtring(BitRate);
                        break;
                    default : ;
                }
            }
        FILLING_END();
    }
    else
        Get_String (20, Encoded_Library,                        "Encoded_Library");
}

void File_Mpega::Encoded_Library_Guess()
{
    //TODO: Not yet enough precise

    /*
    if (Block_Count[1]==0) //No short blocks
    {
        if (mode==2) //Dual Mono
        {
            if (Scfsi>0) //scfsi used
                {}
            else //no scfsi
            {
                if (Scalefac>0) //scalefacors used
                    {}
                else //scalefacors not used
                    Encoded_Library="Shine";
            }
        }
        else //Other than dual mono
        {
            if (Extension_Count[1]>0 || Extension_Count[3]>0) //Intensity Stereo
                Encoded_Library="Xing (very old)";
            else //No Intensity Stereo
            {
                if (Scfsi>0) //Scfsi used
                    Encoded_Library="Xing (new)";
                else //Scsfi not used
                {
                    if (Channels_Count[2]>0) //Joint Stereo
                    {
                        if (Channels_Count[0]>0) //also includes no Joint Stereo frames
                        {
                            if (padding_bit) //Padding
                            {
                                if (original_home)
                                    Encoded_Library="FhG (l3enc)";
                                else
                                    Encoded_Library="FhG (fastenc or mp3enc)";
                            }
                            else //No padding
                                Encoded_Library="FhG (ACM or producer pro)";
                        }
                        else //No stereo frames: joint stereo was forced
                        {
                            if (padding_bit && !original_home && !copyright)
                                Encoded_Library="QDesign (fast mode)";
                        }
                    }
                    else
                    {
                        if (Channels_Count[0]>0 && Scalefac==0 && !original_home) //Stereo
                            Encoded_Library="Plugger";
                        else
                            Encoded_Library="Xing (old)";
                    }
                }
            }
        }
    }
    else //Short blocks
    {
        if (Scfsi)  //scfsi used
        {
            if (Scalefac>0) //Scalefactor used
                Encoded_Library="Gogo (after 3.0)"; //Could be lame, but with a label, detected elsewhere before
            else
                Encoded_Library="Lame (old) or m3e";
        }
        else //Scfsi not used
        {
            if (Scalefac>0) //Scalefactor used
            {
                if (padding_bit)
                {
                    if (original_home)
                    {
                        //10 last bytes
                        //int sum = get_final_sum(data);
                        //if (sum==0)
                        //    return guess = __T("FhG (fastenc, low quality mode)");
                        //else if (sum==10 * 0xFF)
                        //    return guess = __T("FhG (l3enc)");
                        //else if (sum==5 * 0x20)
                        //    return guess = __T("FhG (fastenc, medium or high quality mode)");
                        //else
                        //    return guess = __T("FhG (l3enc or fastenc)");
                    }
                    else
                    {
                        if (Channels_Count[1]>0 && Extension_Count[1]>0)        //Joint Stereo and some Intensity Stereo
                            Encoded_Library="Thomson mp3PRO Encoder";
                        else
                            Encoded_Library="FhG (fastenc or mp3enc)";
                    }
                }
                else //No padding
                {
                    if (BitRate_Mode.find(__T("VBR"))==0) //VBR
                        Encoded_Library="FhG (fastenc)";
                    else
                        Encoded_Library="FhG (ACM or producer pro)";
                }
            }
            else //scalefactors not used
            {
                if (Channels_Count[1]>0) //Joint Stereo
                {
                    if (padding_bit && !original_home && !copyright)
                        Encoded_Library="QDesign";
                }
                else //Joint Stereo not used
                {
                    if (BitRate_Mode.find(__T("VBR"))==0) //VBR
                        Encoded_Library="Lame (old)";
                    else //CBR
                    {

                        if (mode==2) //Dual Mono
                        {
                            if (padding_bit)
                                Encoded_Library="Blade";
                            else
                                Encoded_Library="dist10 encoder or other encoder";
                        }
                        else //Stereo or Mono
                        {
                            //if (data.av_reservoir < 40 && !data.vbr) //ISO based encoders are unable to properly use bit reservoir... average reservoir usage is about 10
                            //{
                            //    if (data.padding)
                            //        return guess = __T("Blade");
                            //    else
                            //        return guess = __T("dist10 encoder or other encoder");
                            //}
                            //else
                            //    return guess = __T("Gogo (before 3.0)");
                        }
                    }
                }
            }
        }
    }
    */
}

//***************************************************************************
// C++
//***************************************************************************

} //NameSpace

#endif //MEDIAINFO_MPEGA_YES

