/**************************************************************************
 * 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 "SourceWv.h"


CSourceWv::CSourceWv(IUnknown *pUnk, HRESULT *phr)
   : CSimplePushSource(L"Source", pUnk, CLSID_SourceWv, phr)
{  

   
   m_pWvContext = NULL;
   m_llTotalDuration = 0;
   ::ZeroMemory(&m_outFormat, sizeof(m_outFormat));
   ::ZeroMemory(&m_awInputFileName, sizeof(m_awInputFileName));


   m_rtCurrent = 0;
   m_rtStop = INFINITE_SECOND;
}

CSourceWv::~CSourceWv()  
{  

   
   if(m_pWvContext != NULL)
   {
      WavpackCloseFile(m_pWvContext);
      m_pWvContext = NULL;
   }
}  

HRESULT CSourceWv::FillBuffer(IMediaSample *pOutSample)
{  

   if(m_rtCurrent >= m_rtStop)
   {  
      return S_FALSE;
   }

   REFERENCE_TIME rtEnd;
   BYTE *pbOutBuffer = NULL;
   int i, j, nSampleSize, nUseArraySize, nOutLength;

   
   pOutSample->GetPointer(&pbOutBuffer);

   
   nSampleSize = WavpackUnpackSamples(m_pWvContext, m_anOutbuffer, MAX_ARRAY_SIZE/m_outFormat.nChannels);

   if(nSampleSize <= 0)
      return S_FALSE;

   nUseArraySize = nSampleSize * m_outFormat.nChannels;

   if(m_outFormat.wBitsPerSample == 16)
   {
      short *ps = (short *)pbOutBuffer;

      for(i=0;i<nUseArraySize;i++)
         ps[i] = m_anOutbuffer[i];
   }
   else if(m_outFormat.wBitsPerSample == 8)
   {
      for(i=0;i<nUseArraySize;i++)
         pbOutBuffer[i] = (BYTE)(m_anOutbuffer[i] - 128);
   }
   else if(m_outFormat.wBitsPerSample == 24)
   {
      for(i=0,j=0; i<nUseArraySize; i++,j+=3)
      {
         pbOutBuffer[j]   = (BYTE)(m_anOutbuffer[i] & 0x000000ff);
         pbOutBuffer[j+1] = (BYTE)((m_anOutbuffer[i] & 0x0000ff00) >> 8);
         pbOutBuffer[j+2] = (BYTE)((m_anOutbuffer[i] & 0x00ff0000) >> 16);
      }
   }
   else 
   {
      
      ::CopyMemory(pbOutBuffer, m_anOutbuffer, nUseArraySize * 4);
   }

   
   nOutLength = nUseArraySize * (m_outFormat.wBitsPerSample / 8);

   
   rtEnd = m_rtCurrent + (LONGLONG)nOutLength * ONE_SECOND / m_outFormat.nAvgBytesPerSec;

   if(rtEnd >= m_rtStop)
   {  

      nOutLength = nOutLength - (int)((rtEnd - m_rtStop) * m_outFormat.nAvgBytesPerSec / ONE_SECOND);
      rtEnd = m_rtStop;

      if(nOutLength < 0)
         nOutLength = 0;
   }

   
   pOutSample->SetTime(&m_rtCurrent, &rtEnd);
   pOutSample->SetActualDataLength(nOutLength);
   pOutSample->SetSyncPoint(FALSE);

   
   m_rtCurrent = rtEnd;

   return S_OK;
}

HRESULT CSourceWv::OnSeek(LONGLONG llNewPosition, LONGLONG llStopPosition)
{  

   if(m_pWvContext == NULL)
      return E_NOTIMPL;

   if(llNewPosition >= 0)
   {  

      
      int nTotalSamples = WavpackGetNumSamples(m_pWvContext);
      int nSeekSamples = (int)((LONGLONG)nTotalSamples * llNewPosition / m_llTotalDuration);
      WavpackSeekSample(m_pWvContext, nSeekSamples);

      
      m_rtCurrent = (REFERENCE_TIME)((LONGLONG)nSeekSamples * ONE_SECOND / (LONGLONG)m_outFormat.nSamplesPerSec);
   }

   if(llStopPosition >= 0)
   {  
      m_rtStop = (REFERENCE_TIME)llStopPosition;
   }

   return S_OK;
}

HRESULT CSourceWv::OnConnectOutPin(CMediaType *pmtOut, int *pnOutBufferSize, REFERENCE_TIME *pTotalMediaTime)
{  

   if(m_pWvContext == NULL)
      return E_NOTIMPL;

   
   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));

   
   *pnOutBufferSize = MAX_ARRAY_SIZE * m_outFormat.nBlockAlign;

   
   *pTotalMediaTime = (REFERENCE_TIME)m_llTotalDuration;

   return S_OK;
}

STDMETHODIMP CSourceWv::OnQueryInterface(REFIID riid, void ** ppv)
{
   if(riid == IID_IFileSourceFilter)
      return GetInterface((IFileSourceFilter *)this, ppv);

   return CSource::NonDelegatingQueryInterface(riid, ppv);
}

CUnknown * WINAPI CSourceWv::CreateInstance(LPUNKNOWN punk, HRESULT *phr)
{  

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

   return dynamic_cast<CUnknown *>(pNewObject);
}

STDMETHODIMP CSourceWv::Load(LPCOLESTR pszFileName, const AM_MEDIA_TYPE *pmt)
{  

   if(::PathFileExists(pszFileName) == FALSE)
      return E_FAIL;

   
   if(m_pWvContext != NULL)
   {
      WavpackCloseFile(m_pWvContext);
      m_pWvContext = NULL;
   }

   
   DWORD dwReadSize;
   char ckID [4];
   HANDLE hFile = NULL;

   hFile = ::CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if(hFile == INVALID_HANDLE_VALUE)
      return E_FAIL;

   ::ReadFile(hFile, ckID, 4, &dwReadSize, NULL);

   ::CloseHandle(hFile);
   hFile = NULL;

   if(dwReadSize != 4)
      return E_FAIL;

   if(ckID[0] != 'w' || ckID[1] != 'v' || ckID[2] != 'p' || ckID[3] != 'k')
      return E_FAIL;

   
   char szFileName[MAX_PATH];
   ::WideCharToMultiByte(CP_ACP, 0, pszFileName, -1, szFileName, MAX_PATH, NULL, NULL);

   
   char error[ERRORLEN];
   m_pWvContext = WavpackOpenFileInput(szFileName, error, OPEN_TAGS, 0);
   if(m_pWvContext == NULL)
        goto error;

   
   m_outFormat.nChannels      = (WORD)WavpackGetNumChannels(m_pWvContext);
   m_outFormat.wBitsPerSample = (WORD)WavpackGetBitsPerSample(m_pWvContext);
   m_outFormat.nSamplesPerSec = (DWORD)WavpackGetSampleRate(m_pWvContext);
   m_outFormat.cbSize = 0;
   m_outFormat.wFormatTag = WAVE_FORMAT_PCM;

   if(m_outFormat.wBitsPerSample == 32)
   {
      if(WavpackGetMode(m_pWvContext) & MODE_FLOAT)
         m_outFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   }

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

   
   m_llTotalDuration = (LONGLONG)((double)WavpackGetNumSamples(m_pWvContext) / (double)m_outFormat.nSamplesPerSec * (double)ONE_SECOND);

   
   ::lstrcpy(m_awInputFileName, pszFileName);

   return S_OK;

error:

   if(m_pWvContext != NULL)
   {
      WavpackCloseFile(m_pWvContext);
      m_pWvContext = NULL;
   }

   return E_FAIL;
}

STDMETHODIMP CSourceWv::GetCurFile(LPOLESTR *ppszFileName, AM_MEDIA_TYPE *pmt)
{  

   CheckPointer(ppszFileName, E_POINTER);

   ::lstrcpy(*ppszFileName, m_awInputFileName);

   if(pmt != NULL)
   {
      ::ZeroMemory(pmt, sizeof(*pmt));
      pmt->majortype = MEDIATYPE_NULL;
      pmt->subtype = MEDIASUBTYPE_NULL;
   }

   return S_OK;
}

