/**************************************************************************
 * Copyright (C) 2008 Cocha                                               *
 * http://sourceforge.jp/projects/ecodecotool/                            *
 *                                                                        *
 *  This Program is free software; you can redistribute it and/or modify  *
 *  it under the terms of the GNU General Public License as published by  *
 *  the Free Software Foundation; either version 2, or (at your option)   *
 *  any later version.                                                    *
 *                                                                        *
 *  This Program is distributed in the hope that it will be useful,       *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
 *  GNU General Public License for more details.                          *
 *                                                                        *
 *  You should have received a copy of the GNU General Public License     *
 *  along with GNU Make; see the file COPYING.  If not, write to          *
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *
 *                                                                        *
 **************************************************************************/

#include "EcoDecoMp3.h"

CDecodeMp3::CDecodeMp3(IUnknown *pUnk, HRESULT *phr)
   : CSimpleTransform(L"MP3 Decoder", pUnk, CLSID_DecodeMp3, phr)
{  

   m_bFirstReceive = true;
   m_rtCurrent = 0;
   m_pMpg123 = NULL;
   m_pOutSample = NULL;
   m_nOutBufferSize = 0;
   m_pOutBuffer = NULL;
   m_nOutActualDataLength = 0;
   ::ZeroMemory(&m_outFormat, sizeof(m_outFormat));
   m_outFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   m_outFormat.cbSize = 0;
   m_outFormat.wBitsPerSample = 32;
}
CDecodeMp3::~CDecodeMp3()  
{  

   SAFE_RELEASE(m_pOutSample);
   SAFE_DELETE(m_pMpg123);

   if(m_pOutBuffer != NULL)
   {
      delete []m_pOutBuffer;
      m_pOutBuffer = NULL;
   }
}  
bool CDecodeMp3::ID3v1TagExists(BYTE *pData, int nDataLength)
{  

   for(int i=0;i<(nDataLength-2);i++)
   {
      if(pData[i] == 'T' && pData[i+1] == 'A' && pData[i+2] == 'G')
         return true;
   }

   return false;
}
int CDecodeMp3::GetFramePos(BYTE *pData, int nStartPos, int nEndPos)
{  

   for(int i=nStartPos;i<(nEndPos-1);i++)
   {
      if(pData[i] == 0xff && (pData[i+1] & 0xf0) == 0xf0)
         return i;
   }

   return -1;
}
bool CDecodeMp3::GetLameScale(BYTE *pLameHeader, int nHeaderLength, double *pdScale)
{  


   int nHeaderScalePos;
   int nFlagBits = (pLameHeader[4] << 24) + (pLameHeader[5] << 16) + (pLameHeader[6] << 8) + pLameHeader[7]; 

   if( (nFlagBits & 0x0008) != 0x0008)
      return false;  

   nHeaderScalePos = 8;       

   if(nFlagBits & 0x0001)
      nHeaderScalePos += 4;   

   if(nFlagBits & 0x0002)
      nHeaderScalePos += 4;   

   if(nFlagBits & 0x0004)
      nHeaderScalePos += 100; 


   if( (nHeaderScalePos + 4) > nHeaderLength)
      return false;  

   double _dScale = (double)((pLameHeader[nHeaderScalePos] << 24) + (pLameHeader[nHeaderScalePos+1] << 16) + (pLameHeader[nHeaderScalePos+2] << 8) + pLameHeader[nHeaderScalePos+3]);
   *pdScale = ::pow(10.0, _dScale / 20.0);

   return true;
}
HRESULT CDecodeMp3::OnStart(void)
{  
   m_bFirstReceive = true;
   m_rtCurrent = 0;

   SAFE_RELEASE(m_pOutSample);
   m_nOutActualDataLength = 0;

   SAFE_DELETE(m_pMpg123);

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
      m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_LINEAR_PCM, m_outFormat.wBitsPerSample);
   else
      m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_IEEE_FLOAT, m_outFormat.wBitsPerSample);

   if(m_pMpg123 == NULL)
      return E_FAIL;

   return S_OK;
}
HRESULT CDecodeMp3::OnSeek(void)
{  

   m_bFirstReceive = true;
   m_rtCurrent = 0;

   SAFE_RELEASE(m_pOutSample);
   m_nOutActualDataLength = 0;

   SAFE_DELETE(m_pMpg123);

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
      m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_LINEAR_PCM, m_outFormat.wBitsPerSample);
   else
      m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_IEEE_FLOAT, m_outFormat.wBitsPerSample);

   if(m_pMpg123 == NULL)
      return E_FAIL;

   return S_OK;
}
HRESULT CDecodeMp3::OnStop(bool bEos)
{  

   if(bEos == true)
   {  

      if(m_pOutSample != NULL && m_nOutActualDataLength > 0)
      {
         REFERENCE_TIME rtStop;
         rtStop = m_rtCurrent + (REFERENCE_TIME)((LONGLONG)m_nOutActualDataLength * ONE_SECOND / m_outFormat.nAvgBytesPerSec);
         m_pOutSample->SetTime(&m_rtCurrent, &rtStop);
         m_rtCurrent = rtStop;

         m_pOutSample->SetSyncPoint(TRUE);
         m_pOutSample->SetDiscontinuity(FALSE);
         m_pOutSample->SetPreroll(FALSE);
         m_pOutSample->SetActualDataLength(m_nOutActualDataLength);
         m_pOutput->Deliver(m_pOutSample);
      }
   }

   SAFE_RELEASE(m_pOutSample);
   m_nOutActualDataLength = 0;

   return S_OK;
}
HRESULT CDecodeMp3::OnReceive(IMediaSample *pInSample)
{
   HRESULT hr;
   int nDone, nOutLength, nResult, nHeaderPos;

   BYTE *pbInBuffer = NULL;

   pInSample->GetPointer(&pbInBuffer);


   nHeaderPos = 0;
   nOutLength = 0;

   if(m_bFirstReceive == true)
   {  

      m_bFirstReceive = false;

      nHeaderPos = GetFramePos(pbInBuffer, 0, pInSample->GetActualDataLength());

      if(nHeaderPos < 0 || pInSample->GetActualDataLength() <= (nHeaderPos+42))
         return S_OK;

      bool bLameHeader = false;

      double dScale;
      bool bExistScale = false;

      if(::memcmp(pbInBuffer+nHeaderPos+21, "Xing", 4) == 0 || ::memcmp(pbInBuffer+nHeaderPos+21, "Info", 4) == 0)
      {
         bLameHeader = true;
         bExistScale = GetLameScale(pbInBuffer+nHeaderPos+21, pInSample->GetActualDataLength()-nHeaderPos-21, &dScale);
      }
      else if(::memcmp(pbInBuffer+nHeaderPos+23, "Xing", 4) == 0 || ::memcmp(pbInBuffer+nHeaderPos+23, "Info", 4) == 0)
      {
         bLameHeader = true;
         bExistScale = GetLameScale(pbInBuffer+nHeaderPos+23, pInSample->GetActualDataLength()-nHeaderPos-23, &dScale);
      }
      else if(::memcmp(pbInBuffer+nHeaderPos+36, "Xing", 4) == 0 || ::memcmp(pbInBuffer+nHeaderPos+36, "Info", 4) == 0)
      {
         bLameHeader = true;
         bExistScale = GetLameScale(pbInBuffer+nHeaderPos+36, pInSample->GetActualDataLength()-nHeaderPos-36, &dScale);
      }
      else if(::memcmp(pbInBuffer+nHeaderPos+38, "Xing", 4) == 0 || ::memcmp(pbInBuffer+nHeaderPos+38, "Info", 4) == 0)
      {
         bLameHeader = true;
         bExistScale = GetLameScale(pbInBuffer+nHeaderPos+38, pInSample->GetActualDataLength()-nHeaderPos-38, &dScale);
      }

      if(bExistScale == true)
      {
      }

      if(bLameHeader == true)
      {  

         nHeaderPos = GetFramePos(pbInBuffer, nHeaderPos+40, pInSample->GetActualDataLength());
         if(nHeaderPos < 0)
            return S_OK;
      }
      
      if(bLameHeader == false)
         nHeaderPos = 0;
   }

   bool bFirstDecode = true;

   while(true)
   {
      if(bFirstDecode == true)
      {
         if(m_outFormat.wBitsPerSample == 16)
            nResult = m_pMpg123->decode((const unsigned char *)(pbInBuffer+nHeaderPos), pInSample->GetActualDataLength()-nHeaderPos, (unsigned char *)(m_pOutBuffer + nOutLength), 8192*2, &nDone);
         else
            nResult = m_pMpg123->decode((const unsigned char *)(pbInBuffer+nHeaderPos), pInSample->GetActualDataLength()-nHeaderPos, (unsigned char *)(m_pOutBuffer + nOutLength), 8192*6, &nDone);
      }
      else
      {
         if(m_outFormat.wBitsPerSample == 16)
            nResult = m_pMpg123->decode(NULL, 0, (unsigned char *)(m_pOutBuffer + nOutLength), 8192*2, &nDone);
         else
            nResult = m_pMpg123->decode(NULL, 0, (unsigned char *)(m_pOutBuffer + nOutLength), 8192*6, &nDone);
      }
      if(nResult == MP3_NEED_MORE)
         break;

      if(nResult == MP3_OK)
      {
         bFirstDecode = false;

         nOutLength += nDone;
      }
      else if(nResult == MP3_ERR)
      {
         if(m_pMpg123 != NULL)
         {
            delete m_pMpg123;
            m_pMpg123 = NULL;
         }

         if(m_pMpg123 == NULL)
         {
            if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
               m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_LINEAR_PCM, m_outFormat.wBitsPerSample);
            else
               m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_IEEE_FLOAT, m_outFormat.wBitsPerSample);
         }

         if(ID3v1TagExists(pbInBuffer, pInSample->GetActualDataLength()) == true)
            break;

         nOutLength = 0;
         nHeaderPos = GetFramePos(pbInBuffer, nHeaderPos+40, pInSample->GetActualDataLength());
         if(nHeaderPos < 0)
            break;

         continue;
      }
      else if(nResult == MP3_WAR)
      {
         break;
      }
   }


   BYTE *pbOutBuffer = NULL;
   int nTmpBufferStartPos = 0;

   while(nOutLength > 0)
   {
      if(m_pOutSample == NULL)
      {
         hr = InitializeOutputSample(pInSample, &m_pOutSample);
         if (FAILED(hr))
         {
            SAFE_RELEASE(m_pOutSample);
            return hr;
         }
      }

      if(pbOutBuffer == NULL)
         m_pOutSample->GetPointer(&pbOutBuffer);

      if( (m_nOutActualDataLength + nOutLength) >= m_nOutBufferSize)
      {  

         ::CopyMemory(&pbOutBuffer[m_nOutActualDataLength], &m_pOutBuffer[nTmpBufferStartPos], m_nOutBufferSize - m_nOutActualDataLength);

         nOutLength -= (m_nOutBufferSize - m_nOutActualDataLength);
         nTmpBufferStartPos += (m_nOutBufferSize - m_nOutActualDataLength);
         m_nOutActualDataLength = 0;

         REFERENCE_TIME rtStop;
         rtStop = m_rtCurrent + (REFERENCE_TIME)((LONGLONG)m_nOutBufferSize * ONE_SECOND / m_outFormat.nAvgBytesPerSec);
         m_pOutSample->SetTime(&m_rtCurrent, &rtStop);
         m_rtCurrent = rtStop;

         m_pOutSample->SetSyncPoint(TRUE);
         m_pOutSample->SetDiscontinuity(FALSE);
         m_pOutSample->SetPreroll(FALSE);
         m_pOutSample->SetActualDataLength(m_nOutBufferSize);
         hr = m_pOutput->Deliver(m_pOutSample);

         SAFE_RELEASE(m_pOutSample);
         pbOutBuffer = NULL;

         continue;
      }

      ::CopyMemory(&pbOutBuffer[m_nOutActualDataLength], &m_pOutBuffer[nTmpBufferStartPos], nOutLength);
      m_nOutActualDataLength += nOutLength;

      break;
   }

   return S_OK;
}
HRESULT CDecodeMp3::GetOutFormat(void *pFormat)
{  
   CheckPointer(pFormat, E_POINTER);
   ::CopyMemory(pFormat, &m_outFormat, sizeof(m_outFormat));
   return S_OK;
}
HRESULT CDecodeMp3::SetOutFormat(void *pFormat)
{  

   CheckPointer(pFormat, E_POINTER);
   WAVEFORMATEX *pwf = (WAVEFORMATEX *)pFormat;

   if(pwf->wFormatTag == WAVE_FORMAT_PCM && pwf->wBitsPerSample == 16)
   {
      ::CopyMemory(&m_outFormat, pFormat, sizeof(m_outFormat));
      return S_OK;
   }
   else if(pwf->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && pwf->wBitsPerSample == 32)
   {
      ::CopyMemory(&m_outFormat, pFormat, sizeof(m_outFormat));
      return S_OK;
   }

   return E_FAIL;
}
HRESULT CDecodeMp3::OnConnectInPin(const CMediaType *pmtIn)
{  

   if(pmtIn->subtype != MEDIASUBTYPE_Mp3 && pmtIn->subtype != MEDIASUBTYPE_MPEG2_AUDIO)
      return S_FALSE;

   if(pmtIn->formattype != FORMAT_WaveFormatEx)
      return S_FALSE;

   WAVEFORMATEX *pwf = (WAVEFORMATEX *)pmtIn->Format();

   m_outFormat.nSamplesPerSec = pwf->nSamplesPerSec;
   m_outFormat.nChannels = pwf->nChannels;
   m_outFormat.nBlockAlign = pwf->nChannels * (m_outFormat.wBitsPerSample / 8);
   m_outFormat.nAvgBytesPerSec = pwf->nSamplesPerSec * m_outFormat.nBlockAlign;

   return S_OK;
}
HRESULT CDecodeMp3::OnConnectOutPin(const CMediaType *pmtIn, int nInBufferSize, CMediaType *pmtOut, int *pnOutBufferSize)
{  

   SAFE_DELETE(m_pMpg123);

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
      m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_LINEAR_PCM, m_outFormat.wBitsPerSample);
   else
      m_pMpg123 = new mpg123(mpg123::DATA_FORMAT_IEEE_FLOAT, m_outFormat.wBitsPerSample);

   if(m_pMpg123 == NULL)
      return E_FAIL;

   pmtOut->SetType(&MEDIATYPE_Audio);
   pmtOut->SetSubtype(&MEDIASUBTYPE_PCM);
   pmtOut->SetTemporalCompression(FALSE);
   pmtOut->SetSampleSize(0);
   pmtOut->SetFormatType(&FORMAT_WaveFormatEx);
   pmtOut->SetFormat((BYTE*)&m_outFormat, sizeof(m_outFormat));

   m_nOutBufferSize = m_outFormat.nAvgBytesPerSec / 10; 
   *pnOutBufferSize = m_nOutBufferSize;

   if(m_pOutBuffer != NULL)
   {
      delete []m_pOutBuffer;
      m_pOutBuffer = NULL;
   }

   if(m_outFormat.wBitsPerSample == 16)
      m_pOutBuffer = new BYTE[8192*2];
   else
      m_pOutBuffer = new BYTE[8192*6];

   return S_OK;
}
STDMETHODIMP CDecodeMp3::OnQueryInterface(REFIID riid, void ** ppv)
{
   if(riid == IID_IDecodeInterface)
      return GetInterface((IDecodeInterface *)this, ppv);

   return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);
}
CUnknown * WINAPI CDecodeMp3::CreateInstance(LPUNKNOWN punk, HRESULT *phr)
{  

   ASSERT(phr);
    
   CDecodeMp3 *pNewObject = new CDecodeMp3(punk, phr);
   if (pNewObject == NULL)
   {
      if(phr != NULL)
         *phr = E_OUTOFMEMORY;
   }

   return dynamic_cast<CUnknown *>(pNewObject);
}
