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


HRESULT CConvertBits::SetFormat(WAVEFORMATEX *pInFormat, WAVEFORMATEX *pOutFormat)
{
   
   ::CopyMemory(&m_inFormat, pInFormat, sizeof(m_inFormat));
   ::CopyMemory(&m_outFormat, pOutFormat, sizeof(m_outFormat));

   
   m_dRatio = (pow(2.0, m_outFormat.wBitsPerSample-1) - 1.0 )/ (pow(2.0, m_inFormat.wBitsPerSample-1) - 1.0);

  return S_OK;
}

HRESULT CConvertBits::Convert(BYTE *pbInBuffer, int nInLength, BYTE *pbOutBuffer, int *pnOutLength)
{
   HRESULT hr;

   if(m_inFormat.wFormatTag == WAVE_FORMAT_PCM)
   {
      if(m_inFormat.wBitsPerSample == 16)
         hr = Convert16(pbInBuffer, nInLength, pbOutBuffer, pnOutLength);
      else if(m_inFormat.wBitsPerSample == 8)
         hr = Convert8(pbInBuffer, nInLength, pbOutBuffer, pnOutLength);
      else if(m_inFormat.wBitsPerSample == 24)
         hr = Convert24(pbInBuffer, nInLength, pbOutBuffer, pnOutLength);
      else if(m_inFormat.wBitsPerSample == 32)
         hr = Convert32i(pbInBuffer, nInLength, pbOutBuffer, pnOutLength);
      else
         return E_FAIL;
   }
   else if(m_inFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
   {
      if(m_inFormat.wBitsPerSample == 32)
         hr = Convert32f((float *)pbInBuffer, nInLength / 4, pbOutBuffer, pnOutLength);
      else if(m_inFormat.wBitsPerSample == 64)
         hr = Convert64((double *)pbInBuffer, nInLength / 8, pbOutBuffer, pnOutLength);
      else
         return E_FAIL;
   }
   else
      return E_FAIL;

   return hr;
}

HRESULT CConvertBits::Convert8(BYTE *pInData, int nInLength, BYTE *pOutData, int *pnOutLength)
{  

   int i, j, nData;

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
   {
      if(m_outFormat.wBitsPerSample == 16)
      {  

         for(i=0,j=0; i<nInLength; i++,j+=2)
         {
            if(pInData[i] & 0x80)
            {
               nData = int( (pInData[i] ^ 0x80) * m_dRatio + 0.5);

               pOutData[j] = nData;
               pOutData[j+1] = nData >> 8;
            }
            else
            {
               pOutData[j] = 0;
               pOutData[j+1] = pInData[i] ^ 0x80;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 24)
      {  

         for(i=0,j=0; i<nInLength; i++,j+=3)
         {
            if(pInData[i] & 0x80)
            {
               nData = int( (pInData[i] ^ 0x80) * m_dRatio + 0.5);

               pOutData[j] = nData;
               pOutData[j+1] = nData >> 8;
               pOutData[j+2] = nData >> 16;
            }
            else
            {
               pOutData[j] = 0;
               pOutData[j+1] = 0;
               pOutData[j+2] = pInData[i] ^ 0x80;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 32)
      {  

         for(i=0,j=0; i<nInLength; i++,j+=4)
         {
            if(pInData[i] & 0x80)
            {
               nData = int( (pInData[i] & 0x7f) * m_dRatio + 0.5);
               ::CopyMemory(&pOutData[j], &nData, 4);
            }
            else
            {
               pOutData[j] = 0;
               pOutData[j+1] = 0;
               pOutData[j+2] = 0;
               pOutData[j+3] = pInData[i] ^ 0x80;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
   }
   else if(m_outFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
   {
      if(m_outFormat.wBitsPerSample == 32)
      {  

         float fData;

         for(i=0,j=0; i<nInLength; i++,j+=4)
         {
            if(pInData[i] & 0x80)
               fData = (pInData[i] - 128.f) / 127.f;
            else
               fData = (pInData[i] - 128.f) / 128.f;

            ::CopyMemory(&pOutData[j], &fData, 4);
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 64)
      {  

         double dData;

         for(i=0,j=0; i<nInLength; i++,j+=8)
         {
            if(pInData[i] & 0x80)
               dData = (pInData[i] - 128.0) / 127.0;
            else
               dData = (pInData[i] - 128.0) / 128.0;

            ::CopyMemory(&pOutData[j], &dData, 8);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }

   return E_FAIL;
}

HRESULT CConvertBits::Convert16(BYTE *pInData, int nInLength, BYTE *pOutData, int *pnOutLength)
{  

   int i, j, nData;

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
   {
      if(m_outFormat.wBitsPerSample == 8)
      {  

         for(i=0,j=0; i<nInLength; i+=2,j++)
         {
            if(pInData[i+1] & 0x80)
               pOutData[j] = pInData[i+1] ^ 0x80;
            else
            {
               nData = int( ( (pInData[i+1] << 8) | pInData[i] ) * m_dRatio + 127.5);

               pOutData[j] = nData;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 24)
      {  

         for(i=0,j=0; i<nInLength; i+=2,j+=3)
         {
            if(pInData[i+1] & 0x80)
            {
               pOutData[j] = 0;
               pOutData[j+1] = pInData[i];
               pOutData[j+2] = pInData[i+1];
            }
            else
            {
               nData = int( ((pInData[i+1] << 8) | pInData[i]) * m_dRatio + 0.5);

               pOutData[j] = nData;
               pOutData[j+1] = nData >> 8;
               pOutData[j+2] = nData >> 16;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 32)
      {  

         for(i=0,j=0; i<nInLength; i+=2,j+=4)
         {
            if(pInData[i+1] & 0x80)
            {
               pOutData[j] = 0;
               pOutData[j+1] = 0;
               pOutData[j+2] = pInData[i];
               pOutData[j+3] = pInData[i+1];
            }
            else
            {
               nData = int( ((pInData[i+1] << 8) | pInData[i]) * m_dRatio + 0.5);
               ::CopyMemory(&pOutData[j], &nData, 4);
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
   }
   else if(m_outFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
   {
      if(m_outFormat.wBitsPerSample == 32)
      {  

         float fData;

         for(i=0,j=0; i<nInLength; i+=2,j+=4)
         {
            if(pInData[i+1] & 0x80)
              fData = (short((pInData[i+1] << 8) | pInData[i])) / 32768.f;
            else
              fData = ((pInData[i+1] << 8) | pInData[i]) / 32767.f;

            ::CopyMemory(&pOutData[j], &fData, 4);
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 64)
      {  

         double dData;

         for(i=0,j=0; i<nInLength; i+=2,j+=8)
         {
            if(pInData[i+1] & 0x80)
              dData = (short((pInData[i+1] << 8) | pInData[i])) / 32768.0;
            else
              dData = ((pInData[i+1] << 8) | pInData[i]) / 32767.0;

            ::CopyMemory(&pOutData[j], &dData, 8);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }

   return E_FAIL;
}

HRESULT CConvertBits::Convert24(BYTE *pInData, int nInLength, BYTE *pOutData, int *pnOutLength)
{  

   int i, j, nData;

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
   {
      if(m_outFormat.wBitsPerSample == 8)
      {  

         for(i=0,j=0; i<nInLength; i+=3,j++)
         {
            if(pInData[i+2] & 0x80)
               pOutData[j] = pInData[i+2] ^ 0x80;
            else
            {
               nData = int( ( (pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i] ) * m_dRatio + 127.5);

               pOutData[j] = nData;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 16)
      {  

         for(i=0,j=0; i<nInLength; i+=3,j+=2)
         {
            if(pInData[i+2] & 0x80)
            {
               pOutData[j] = pInData[i+1];
               pOutData[j+1] = pInData[i+2];
            }
            else
            {
               nData = int( ((pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) * m_dRatio + 0.5);

               pOutData[j] = nData;
               pOutData[j+1] = nData >> 8;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 32)
      {  

         for(i=0,j=0; i<nInLength; i+=3,j+=4)
         {
            if(pInData[i+2] & 0x80)
            {
               pOutData[j] = 0;
               pOutData[j+1] = pInData[i];
               pOutData[j+2] = pInData[i+1];
               pOutData[j+3] = pInData[i+2];
            }
            else
            {
               nData = int( ((pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) * m_dRatio + 0.5);
               ::CopyMemory(&pOutData[j], &nData, 4);
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
   }
   else if(m_outFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
   {
      if(m_outFormat.wBitsPerSample == 32)
      {  

         float fData;

         for(i=0,j=0; i<nInLength; i+=3,j+=4)
         {
            if(pInData[i+2] & 0x80)
              fData = ( ((pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) - 0x01000000) / 8388608.f;
            else
              fData = ((pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) / 8388607.f;

            ::CopyMemory(&pOutData[j], &fData, 4);
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 64)
      {  

         double dData;

         for(i=0,j=0; i<nInLength; i+=3,j+=8)
         {
            if(pInData[i+2] & 0x80)
              dData = ( ((pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) - 0x01000000) / 8388608.0;
            else
              dData = ((pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) / 8388607.0;

            ::CopyMemory(&pOutData[j], &dData, 8);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }

   return E_FAIL;
}

HRESULT CConvertBits::Convert32i(BYTE *pInData, int nInLength, BYTE *pOutData, int *pnOutLength)
{  

   int i, j, nData;

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
   {
      if(m_outFormat.wBitsPerSample == 8)
      {  

         for(i=0,j=0; i<nInLength; i+=4,j++)
         {
            if(pInData[i+3] & 0x80)
               pOutData[j] = pInData[i+3] ^ 0x80;
            else
            {
               nData = int( ( (pInData[i+3] << 24) | (pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i] ) * m_dRatio + 127.5);

               pOutData[j] = nData;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 16)
      {  

         for(i=0,j=0; i<nInLength; i+=4,j+=2)
         {
            if(pInData[i+3] & 0x80)
            {
               pOutData[j] = pInData[i+2];
               pOutData[j+1] = pInData[i+3];
            }
            else
            {
               nData = int( ((pInData[i+3] << 24) | (pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) * m_dRatio + 0.5);

               pOutData[j] = nData;
               pOutData[j+1] = nData >> 8;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 24)
      {  

         for(i=0,j=0; i<nInLength; i+=4,j+=3)
         {
            if(pInData[i+3] & 0x80)
            {
               pOutData[j] = pInData[i+1];
               pOutData[j+1] = pInData[i+2];
               pOutData[j+2] = pInData[i+3];
            }
            else
            {

               nData = int( ((pInData[i+3] << 24) | (pInData[i+2] << 16) | (pInData[i+1] << 8) | pInData[i]) * m_dRatio + 0.5);

               pOutData[j] = nData;
               pOutData[j+1] = nData >> 8;
               pOutData[j+2] = nData >> 16;
            }
         }

         *pnOutLength = j;
         return S_OK;
      }
   }
   else if(m_outFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
   {
      if(m_outFormat.wBitsPerSample == 32)
      {  

         float fData;

         for(i=0,j=0; i<nInLength; i+=4,j+=4)
         {
            ::CopyMemory(&nData, &pInData[i], 4);

            if(nData < 0)
              fData = nData / 2147483648.f;
            else
              fData = nData / 2147483647.f;

            ::CopyMemory(&pOutData[j], &fData, 4);
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 64)
      {  

         double dData;

         for(i=0,j=0; i<nInLength; i+=4,j+=8)
         {
            ::CopyMemory(&nData, &pInData[i], 4);

            if(nData < 0)
              dData = nData / 2147483648.0;
            else
              dData = nData / 2147483647.0;

            ::CopyMemory(&pOutData[j], &dData, 8);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }

   return E_FAIL;
}

HRESULT CConvertBits::Convert32f(float *pInData, int nInLength, BYTE *pOutData, int *pnOutLength)
{  

   int i, j, nData;

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
   {
      if(m_outFormat.wBitsPerSample == 8)
      {  

         for(i=0,j=0; i<nInLength; i++,j++)
         {
            if(pInData[i] < 0.f)
               nData = BYTE(pInData[i] * 128.f + 127.5f);
            else
               nData = BYTE(pInData[i] * 127.f + 128.5f);

            if(nData < 0)
               nData = 0;

            if(nData > 255)
               nData = 255;

            pOutData[j] = nData;
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 16)
      {  

         for(i=0,j=0; i<nInLength; i++,j+=2)
         {
            if(pInData[i] < 0.f)
               nData = int(pInData[i] * 32768.f - 0.5f);
            else
               nData = int(pInData[i] * 32767.f + 0.5f);

            if(nData < -32768)
               nData = -32768;

            if(nData > 32767)
               nData = 32767;

            pOutData[j] = nData;
            pOutData[j+1] = nData >> 8;
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 24)
      {  

         int nData;

         for(i=0,j=0; i<nInLength; i++,j+=3)
         {
            if(pInData[i] < 0.f)
               nData = int(pInData[i] * 8388608.f - 0.5f);
            else
               nData = int(pInData[i] * 8388607.f + 0.5f);

            if(nData < -8388608)
               nData = -8388608;

            if(nData > 8388607)
               nData = 8388607;

            pOutData[j] = nData;
            pOutData[j+1] = nData >> 8;
            pOutData[j+2] = nData >> 16;
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 32)
      {  

         float fData;

         for(i=0,j=0; i<nInLength; i++,j+=4)
         {
            if(pInData[i] < 0.f)
               fData = pInData[i] * 2147483648.f - 0.5f;
            else
               fData = pInData[i] * 2147483647.f + 0.5f;

            if(fData < -2147483648.f)
               fData = -2147483648.f;

            if(fData > 2147483647.f)
               fData = 2147483647.f;

            nData = (int)fData;
            ::CopyMemory(&pOutData[j], &nData, 4);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }
   else if(m_outFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
   {
      if(m_outFormat.wBitsPerSample == 64)
      {  

         double dData;

         for(i=0,j=0; i<nInLength; i++,j+=8)
         {
            dData = (double)pInData[i];

            ::CopyMemory(&pOutData[j], &dData, 8);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }

   return E_FAIL;
}

HRESULT CConvertBits::Convert64(double *pInData, int nInLength, BYTE *pOutData, int *pnOutLength)
{  

   int i, j, nData;

   if(m_outFormat.wFormatTag == WAVE_FORMAT_PCM)
   {
      if(m_outFormat.wBitsPerSample == 8)
      {  

         for(i=0,j=0; i<nInLength; i++,j++)
         {
            if(pInData[i] < 0.0)
               nData = BYTE(pInData[i] * 128.0 + 127.5);
            else
               nData = BYTE(pInData[i] * 127.0 + 128.5);

            if(nData < 0)
               nData = 0;

            if(nData > 255)
               nData = 255;

            pOutData[j] = nData;
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 16)
      {  

         for(i=0,j=0; i<nInLength; i++,j+=2)
         {
            if(pInData[i] < 0.0)
               nData = int(pInData[i] * 32768.0 - 0.5);
            else
               nData = int(pInData[i] * 32767.0 + 0.5);

            if(nData < -32768)
               nData = -32768;

            if(nData > 32767)
               nData = 32767;

            pOutData[j] = nData;
            pOutData[j+1] = nData >> 8;
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 24)
      {  

         int nData;

         for(i=0,j=0; i<nInLength; i++,j+=3)
         {
            if(pInData[i] < 0.0)
               nData = int(pInData[i] * 8388608.0 - 0.5);
            else
               nData = int(pInData[i] * 8388607.0 + 0.5);

            if(nData < -8388608)
               nData = -8388608;

            if(nData > 8388607)
               nData = 8388607;

            pOutData[j] = nData;
            pOutData[j+1] = nData >> 8;
            pOutData[j+2] = nData >> 16;
         }

         *pnOutLength = j;
         return S_OK;
      }
      else if(m_outFormat.wBitsPerSample == 32)
      {  

         double dData;

         for(i=0,j=0; i<nInLength; i++,j+=4)
         {
            if(pInData[i] < 0.0)
               dData = pInData[i] * 2147483648.0 - 0.5;
            else
               dData = pInData[i] * 2147483647.0 + 0.5;

            if(dData < -2147483648.0)
               dData = -2147483648.0;

            if(dData > 2147483647.0)
               dData = 2147483647.0;

            nData = (int)dData;
            ::CopyMemory(&pOutData[j], &nData, 4);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }
   else if(m_outFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
   {
      if(m_outFormat.wBitsPerSample == 32)
      {  

         float fData;

         for(i=0,j=0; i<nInLength; i++,j+=4)
         {
            fData = (float)pInData[i];

            ::CopyMemory(&pOutData[j], &fData, 4);
         }

         *pnOutLength = j;
         return S_OK;
      }
   }

   return E_FAIL;
}

