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


CConvertToWav::CConvertToWav()
{
   m_pEcoDecoWriterInterface = NULL;
   ::ZeroMemory(&m_awOutputFileName, sizeof(m_awOutputFileName));
}

CConvertToWav::~CConvertToWav()
{
   SAFE_RELEASE(m_pEcoDecoWriterInterface);
}

void CConvertToWav::Release(void)
{
   
   SAFE_RELEASE(m_pEcoDecoWriterInterface);
}

void CConvertToWav::GetFormat(WCHAR *pwInputFormat, WCHAR *pwOutputFormat)
{
   HRESULT hr;
   WAVEFORMATEX inputFormat, outputFormat;

   hr = GetDirectShowFormat(&inputFormat, &outputFormat);

   if(FAILED(hr))
   {
      ::lstrcpy(pwInputFormat, L"-");
      ::lstrcpy(pwOutputFormat, L"-");
   }
   else
   {
      ::wsprintf(pwInputFormat, L"%dHz %2dbit %dch", inputFormat.nSamplesPerSec, inputFormat.wBitsPerSample, inputFormat.nChannels);
      ::wsprintf(pwOutputFormat, L"%dHz %2dbit %dch", outputFormat.nSamplesPerSec, outputFormat.wBitsPerSample, outputFormat.nChannels);
   }
}

HRESULT CConvertToWav::GetReplayGain(double *pdPeak, double *pdGain)
{
   if(m_pEcoDecoWriterInterface == NULL)
      return E_FAIL;

   m_pEcoDecoWriterInterface->GetReplayGain(pdPeak, pdGain);

   return S_OK;
}

HRESULT CConvertToWav::WriteAlbumGain(WCHAR *pwInputFileName, WCHAR *pwOutputFolder, double dAlbumPeak, double dAlbumGain)
{
   HRESULT hr;
   HANDLE hFile = NULL;
   WCHAR awOutputFileName[MAX_PATH];

   
   hr = GetOutputFileName(awOutputFileName, pwInputFileName, pwOutputFolder, L".wav", false);
   if(FAILED(hr)) return E_FAIL;

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

   return WriteReplayGain(awOutputFileName, GAIN_ALBUM_GAIN, dAlbumPeak, dAlbumGain);
}

HRESULT CConvertToWav::WriteReplayGain(WCHAR *pwFileName, int nGainMode, double dPeak, double dGain)
{  

   
   
   
   

   HANDLE hFile = NULL;
   DWORD dwNumberOfBytesWrite;
   short sData;
   float fData;

   
   hFile = ::CreateFile(pwFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, (DWORD)0, NULL);
   if(hFile == NULL) return E_FAIL;
   if(hFile == (HANDLE)0xffffffff) return E_FAIL;

   if(nGainMode == GAIN_TRACK_GAIN)
   {  

      
      ::SetFilePointer(hFile, 44, NULL, FILE_BEGIN);

      
      fData = (float)dPeak;
      ::WriteFile(hFile, &fData, 4, &dwNumberOfBytesWrite, NULL);
      if(dwNumberOfBytesWrite != 4)
         goto error;

      
      sData = 0;
      ::WriteFile(hFile, &sData, 2, &dwNumberOfBytesWrite, NULL);
      if(dwNumberOfBytesWrite != 2)
         goto error;

      
      if(dGain < 0)
      {
         if(dGain < -51.0)
            dGain = -51.0;

         sData = 0x4a00 | (short)(-dGain * 10.0);
      }
      else
      {
         if(51.0 < dGain)
            dGain = 51.0;

         sData = 0x4800 | (short)(dGain * 10.0);
      }

      ::WriteFile(hFile, &sData, 2, &dwNumberOfBytesWrite, NULL);
      if(dwNumberOfBytesWrite != 2)
         goto error;
   }
   else 
   {
      
      ::SetFilePointer(hFile, 48, NULL, FILE_BEGIN);

      if(dGain < 0)
      {
         if(dGain < -51.0)
            dGain = -51.0;

         sData = 0x2a00 | (short)(-dGain * 10.0);
      }
      else
      {
         if(51.0 < dGain)
            dGain = 51.0;

         sData = 0x2800 | (short)(dGain * 10.0);
      }

      ::WriteFile(hFile, &sData, 2, &dwNumberOfBytesWrite, NULL);
      if(dwNumberOfBytesWrite != 2)
         goto error;
   }

   ::CloseHandle(hFile);
   return S_OK;

error:

   if(hFile != NULL)
      ::CloseHandle(hFile);

   return E_FAIL;
}

int CConvertToWav::ConvertStart(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFolder, CONVERTDATA2 *pConvertData, int nWait)
{  

   HRESULT hr;

   if(pConvertData->bNormalize == false)
   {
      WCHAR awOutputFileName[MAX_PATH];

      
      hr = GetOutputFileName(awOutputFileName, pwInputFileName, pwOutputFolder, L".wav", true);
      if(FAILED(hr)) goto error;

      
      hr = Render(hWnd, pwInputFileName, awOutputFileName, pConvertData, nWait);
      if(FAILED(hr)) goto error;

      
      ::lstrcpy(m_awOutputFileName, awOutputFileName);
   }
   else
   {  
      
      hr = Render(hWnd, pwInputFileName, NULL, pConvertData, nWait);
      if(FAILED(hr))
         goto error;
   }

   return STATE_CONVERTING;

error:

   return STATE_END_ERROR;
}

int CConvertToWav::Converting(int *pnPercent)
{  
   *pnPercent = GetPercent();
   return STATE_CONVERTING;
}

int CConvertToWav::ConvertEnd(CONVERTDATA2 *pConvertData)
{  

   if(pConvertData->nGainMode == GAIN_ALBUM_GAIN)
   {
      
      if(m_pEcoDecoWriterInterface != NULL)
         m_pEcoDecoWriterInterface->GetGainResult(pConvertData->anGainResult);
   }

   if(pConvertData->bNormalize == true || pConvertData->nGainMode == GAIN_TRACK_GAIN || pConvertData->nGainMode == GAIN_ALBUM_GAIN)
   {
      HRESULT hr;
      double dPeak, dGain;

      
      hr = GetReplayGain(&dPeak, &dGain);

      
      dGain = dGain + (pConvertData->dTargetGain - 89.0);

      
      ReleaseDirectShow();

      if(SUCCEEDED(hr))
      {
         if( (pConvertData->nGainMode == GAIN_ALBUM_NORMALIZE && pConvertData->bNormalize == true) ||
             (pConvertData->nGainMode == GAIN_ALBUM_GAIN && pConvertData->nPass == 1) )
         {
            if(pConvertData->dAlbumPeak < dPeak)
               pConvertData->dAlbumPeak = dPeak;

            if(dGain < pConvertData->dAlbumGain) 
               pConvertData->dAlbumGain = dGain;

            WriteReplayGain(m_awOutputFileName, GAIN_TRACK_GAIN, dPeak, dGain);
         }
         else if(pConvertData->bNormalize == true) 
            pConvertData->dNormalize = ::pow(10.0, dGain / 20.0);
         else if(pConvertData->nGainMode == GAIN_TRACK_GAIN) 
            WriteReplayGain(m_awOutputFileName, pConvertData->nGainMode, dPeak, dGain);
      }
   }
   else
   {
      
      ReleaseDirectShow();
   }

   
   ::lstrcpy(m_awOutputFileName, L"");

   if(pConvertData->nGainMode == GAIN_TRACK_NORMALIZE && pConvertData->bNormalize == true)
      return STATE_START_SAMEFILE;

   return STATE_START_NEXTFILE;
}

void CConvertToWav::ConvertExit(void)
{  

   
   ReleaseDirectShow();

   
   if(::PathFileExists(m_awOutputFileName) != FALSE)
      ::DeleteFile(m_awOutputFileName);
}

HRESULT CConvertToWav::RenderGraph(IGraphBuilder *pGraphBuilder, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, IBaseFilter *pConvertWav, IConvertWavInterface *pConvertWavInterface , CONVERTDATA2 *pConvertData)
{
   HRESULT hr;
   IBaseFilter *pWriter = NULL;

   
   if(pConvertData->wf.nSamplesPerSec == 0 && pConvertData->wf.wBitsPerSample == 0 && pConvertData->wf.nChannels == 0)
      hr = RenderByFileExt(pGraphBuilder, pwInputFileName, pConvertWav, true);  
   else
      hr = RenderByFileExt(pGraphBuilder, pwInputFileName, pConvertWav, false); 

   if(FAILED(hr))
   {  
      if(pConvertData->wf.nSamplesPerSec == 0 && pConvertData->wf.wBitsPerSample == 0 && pConvertData->wf.nChannels == 0)
         hr = RenderByAllFilter(pGraphBuilder, pwInputFileName, pConvertWav, true);  
      else
         hr = RenderByAllFilter(pGraphBuilder, pwInputFileName, pConvertWav, false); 
   }

   if(FAILED(hr)) goto error;

   if(pConvertData->wf.nSamplesPerSec != 0 || pConvertData->wf.wBitsPerSample != 0 || pConvertData->wf.nChannels != 0)
   {  

      WAVEFORMATEX outFormat;

      
      pConvertWavInterface->GetInFormat(&outFormat);

      
      if(pConvertData->wf.nSamplesPerSec != 0)
         outFormat.nSamplesPerSec = pConvertData->wf.nSamplesPerSec;

      if(pConvertData->wf.nChannels != 0)
         outFormat.nChannels = pConvertData->wf.nChannels;

      if(pConvertData->wf.wBitsPerSample != 0)
      {
         outFormat.wBitsPerSample = pConvertData->wf.wBitsPerSample;
         outFormat.wFormatTag = pConvertData->wf.wFormatTag;
      }

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

      pConvertWavInterface->SetOutFormat(&outFormat);
   }

   
   if(pConvertData->bNormalize == false)
      pWriter = AddWriteFilter(pGraphBuilder, CLSID_EcoDecoWriter, pwOutputFileName);
   else
      pWriter = AddFilter(pGraphBuilder, CLSID_EcoDecoWriter);

   
   pWriter->QueryInterface(IID_IEcoDecoWriterInterface, (void **)&m_pEcoDecoWriterInterface);
   if(m_pEcoDecoWriterInterface == NULL) goto error;

   
   m_pEcoDecoWriterInterface->SetOutputMode(101);

   
   if(pConvertData->bNormalize == true)
      m_pEcoDecoWriterInterface->CheckVolumeMode(true);

   if(pConvertData->nGainMode == GAIN_TRACK_GAIN)
   {  
      m_pEcoDecoWriterInterface->SetReplaygain(true, false);
   }
   else if(pConvertData->nGainMode == GAIN_ALBUM_GAIN)
   {  
      m_pEcoDecoWriterInterface->SetReplaygain(true, true);
   }

   
   hr = ConnectDirect(pGraphBuilder, pConvertWav, pWriter, NULL);
   if(FAILED(hr)) goto error;

   SAFE_RELEASE(pWriter);
   return S_OK;

error:
   SAFE_RELEASE(pWriter);
   return E_FAIL;
}

