Logo Search packages:      
Sourcecode: lame version File versions  Download package

ACM.cpp

/**
 *
 * Lame ACM wrapper, encode/decode MP3 based RIFF/AVI files in MS Windows
 *
 *  Copyright (c) 2002 Steve Lhomme <steve.lhomme at free.fr>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
 
/*!
      \author Steve Lhomme
      \version \$Id: ACM.cpp,v 1.20.8.1 2008/11/01 20:41:47 robert Exp $
*/

#if !defined(STRICT)
#define STRICT
#endif // STRICT

#include <algorithm>

#include <windows.h>
#include <windowsx.h>
#include <intshcut.h>

#include <mmreg.h>
#include <msacm.h>
#include <msacmdrv.h>

#include <assert.h>

#include <lame.h>

#include "adebug.h"
#include "resource.h"
#include "ACMStream.h"

#ifdef ENABLE_DECODING
#include "DecodeStream.h"
#endif // ENABLE_DECODING

#include "ACM.h"

#ifndef IDC_HAND
#define IDC_HAND            MAKEINTRESOURCE(32649)
#endif // IDC_HAND

char ACM::VersionString[120];

const char ACM_VERSION[] = "0.9.2";

#ifdef WIN32
//
//  32-bit versions
//
#if (WINVER >= 0x0400)
 #define VERSION_ACM_DRIVER  MAKE_ACM_VERSION(4,  0, 0)
#else
#define VERSION_ACM_DRIVER  MAKE_ACM_VERSION(3, 51, 0)
#endif
#define VERSION_MSACM MAKE_ACM_VERSION(3, 50, 0)

#else
//
//  16-bit versions
//
#define VERSION_ACM_DRIVER MAKE_ACM_VERSION(1, 0, 0)
#define VERSION_MSACM MAKE_ACM_VERSION(2, 1, 0)

#endif

#define PERSONAL_FORMAT WAVE_FORMAT_MPEGLAYER3
#define SIZE_FORMAT_STRUCT sizeof(MPEGLAYER3WAVEFORMAT)
//#define SIZE_FORMAT_STRUCT 0

//static const char channel_mode[][13] = {"mono","stereo","joint stereo","dual channel"};
static const char channel_mode[][13] = {"mono","stereo"};
static const unsigned int mpeg1_freq[] = {48000,44100,32000};
static const unsigned int mpeg2_freq[] = {24000,22050,16000,12000,11025,8000};
static const unsigned int mpeg1_bitrate[] = {320, 256, 224, 192, 160, 128, 112, 96, 80, 64, 56, 48, 40, 32};
static const unsigned int mpeg2_bitrate[] = {160, 144, 128, 112,  96,  80,  64, 56, 48, 40, 32, 24, 16,  8};

#define SIZE_CHANNEL_MODE (sizeof(channel_mode)  / (sizeof(char) * 13))
#define SIZE_FREQ_MPEG1 (sizeof(mpeg1_freq)    / sizeof(unsigned int))
#define SIZE_FREQ_MPEG2 (sizeof(mpeg2_freq)    / sizeof(unsigned int))
#define SIZE_BITRATE_MPEG1 (sizeof(mpeg1_bitrate) / sizeof(unsigned int))
#define SIZE_BITRATE_MPEG2 (sizeof(mpeg2_bitrate) / sizeof(unsigned int))

static const int FORMAT_TAG_MAX_NB = 2; // PCM and PERSONAL (mandatory to have at least PCM and your format)
static const int FILTER_TAG_MAX_NB = 0; // this is a codec, not a filter

// number of supported PCM formats
static const int FORMAT_MAX_NB_PCM =
      2 *                                           // number of PCM channel mode (stereo/mono)
            (SIZE_FREQ_MPEG1 + // number of MPEG 1 sampling freq
            SIZE_FREQ_MPEG2); // number of MPEG 2 sampling freq

//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
bool bitrate_item::operator<(const bitrate_item & other_bitrate) const
{
      return (other_bitrate.frequency < frequency ||
                (other_bitrate.frequency == frequency &&
                   (other_bitrate.bitrate < bitrate ||
                    (other_bitrate.bitrate == bitrate &&
                     (other_bitrate.channels < channels)))));
}

//////////////////////////////////////////////////////////////////////
// Configuration Dialog
//////////////////////////////////////////////////////////////////////
/*
static CALLBACK ConfigProc(
  HWND hwndDlg,  // handle to dialog box
UINT uMsg,     // message
WPARAM wParam, // first message parameter
LPARAM lParam  // second message parameter
)
{
      BOOL bResult;

      switch (uMsg) {
            case WM_COMMAND:
                  UINT command;
                  command = GET_WM_COMMAND_ID(wParam, lParam);
            if (IDOK == command)
            {
                EndDialog(hwndDlg, (IDOK == command));
            } else if (IDCANCEL == command)
            {
                EndDialog(hwndDlg, (IDOK == command));
            }
            bResult = FALSE;
                  break;
            default:
                  bResult = FALSE; // will be treated by DefWindowProc
}
      return bResult;
}


inline DWORD ACM::Configure(HWND hParentWindow, LPDRVCONFIGINFO pConfig)
{
      my_debug.OutPut(DEBUG_LEVEL_FUNC_START, "ACM : Configure (Parent Window = 0x%08X)",hParentWindow);

      DialogBoxParam( my_hModule, MAKEINTRESOURCE(IDD_CONFIG), hParentWindow, ::ConfigProc , (LPARAM)this);

      return DRVCNF_OK; // Can also return
                              // DRVCNF_CANCEL
                              // and DRVCNF_RESTART
}
*/
//////////////////////////////////////////////////////////////////////
// About Dialog
//////////////////////////////////////////////////////////////////////

static BOOL CALLBACK AboutProc(
  HWND hwndDlg,  // handle to dialog box
UINT uMsg,     // message
WPARAM wParam, // first message parameter
LPARAM lParam  // second message parameter
)
{
      static HBRUSH hBrushStatic = NULL;
//    static LOGFONT lf;  // structure for font information  
//    static HFONT hfnt;
      static HCURSOR hcOverCursor = NULL;
      BOOL bResult;

      switch (uMsg) {
            case WM_INITDIALOG:
                  char tmp[150];
                  wsprintf(tmp,"LAME MP3 codec v%s", ACM::GetVersionString());
                  ::SetWindowText(GetDlgItem( hwndDlg, IDC_STATIC_ABOUT_TITLE), tmp);

/*
                  ::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); 
                  lf.lfUnderline = TRUE;

                  hfnt = ::CreateFontIndirect(&lf);

                  ::SendMessage(::GetDlgItem(hwndDlg,IDC_STATIC_ABOUT_URL), WM_SETFONT, (WPARAM) hfnt, TRUE);
* /
                  hBrushStatic = ::CreateSolidBrush(::GetSysColor (COLOR_BTNFACE));
*/                hcOverCursor = ::LoadCursor(NULL,(LPCTSTR)IDC_HAND); 
                  if (hcOverCursor == NULL)
                        hcOverCursor = ::LoadCursor(NULL,(LPCTSTR)IDC_CROSS); 

                  bResult = TRUE;
                  break;
/*
            case WM_CTLCOLORSTATIC:
                  /// \todo only if there are URLs
                  if ((HWND)lParam == ::GetDlgItem(hwndDlg,IDC_STATIC_ABOUT_URL))
                  {
                        ::SetTextColor((HDC)wParam, ::GetSysColor (COLOR_HIGHLIGHT));
                        ::SetBkColor((HDC)wParam, ::GetSysColor (COLOR_BTNFACE));

                        return (LRESULT) hBrushStatic;
                  }
                  else
                        return (LRESULT) NULL;
*/
            case WM_MOUSEMOVE:
                  {
                        POINT pnt;
                        ::GetCursorPos(&pnt);

                        RECT rect;
                        ::GetWindowRect( ::GetDlgItem(hwndDlg,IDC_STATIC_ABOUT_URL), &rect);

                        if (  ::PtInRect(&rect,pnt)  )
                        {
                              ::SetCursor(hcOverCursor);
                        }


                  }
                  break;

            case WM_LBUTTONUP:
                  {
                        POINT pnt;
                        ::GetCursorPos(&pnt);

                        RECT rect;
                        ::GetWindowRect( ::GetDlgItem(hwndDlg,IDC_STATIC_ABOUT_URL), &rect);

                        TCHAR Url[200];
                        bool bUrl = false;
                        if (::PtInRect(&rect,pnt))
                        {
                              wsprintf(Url,get_lame_url());
                              bUrl = true;
                        }

                        if (bUrl)
                        {
                              LPSTR tmpStr;
                              HRESULT hresult = ::TranslateURL(Url, TRANSLATEURL_FL_GUESS_PROTOCOL|TRANSLATEURL_FL_GUESS_PROTOCOL, &tmpStr);
                              if (hresult == S_OK)
                                    ::ShellExecute(hwndDlg,"open",tmpStr,NULL,"",SW_SHOWMAXIMIZED );
                              else if (hresult == S_FALSE)
                                    ::ShellExecute(hwndDlg,"open",Url,NULL,"",SW_SHOWMAXIMIZED );
                        }

                  }
                  break;

            case WM_COMMAND:
                  UINT command;
                  command = GET_WM_COMMAND_ID(wParam, lParam);
            if (IDOK == command)
            {
                EndDialog(hwndDlg, TRUE);
            }
            bResult = FALSE;
                  break;

            case IDC_STATIC_ABOUT_URL:
                  break;
            default:
                  bResult = FALSE; // will be treated by DefWindowProc
}
      return bResult;
}

inline DWORD ACM::About(HWND hParentWindow)
{
      my_debug.OutPut(DEBUG_LEVEL_FUNC_START, "ACM : About (Parent Window = 0x%08X)",hParentWindow);

      DialogBoxParam( my_hModule, MAKEINTRESOURCE(IDD_ABOUT), hParentWindow, ::AboutProc , (LPARAM)this);

      return DRVCNF_OK; // Can also return
// DRVCNF_CANCEL
// and DRVCNF_RESTART
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

00297 ACM::ACM( HMODULE hModule )
 :my_hModule(hModule),
  my_hIcon(NULL),
  my_debug(ADbg(DEBUG_LEVEL_CREATION)),
  my_EncodingProperties(hModule)
{
      my_EncodingProperties.ParamsRestore();

      /// \todo get the debug level from the registry
      unsigned char DebugFileName[512];

      char tmp[128];
      wsprintf(tmp,"LAMEacm 0x%08X",this);
      my_debug.setPrefix(tmp); /// \todo get it from the registry
      my_debug.setIncludeTime(true);  /// \todo get it from the registry

      // Check in the registry if we have to Output Debug information
      DebugFileName[0] = '\0';

      HKEY OssKey;
      if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWARE\\MUKOLI", 0, KEY_READ , &OssKey ) == ERROR_SUCCESS) {
            DWORD DataType;
            DWORD DebugFileNameSize = 512;
            if (RegQueryValueEx( OssKey, "DebugFile", NULL, &DataType, DebugFileName, &DebugFileNameSize ) == ERROR_SUCCESS) {
                  if (DataType == REG_SZ) {
                        my_debug.setUseFile(true);
                        my_debug.setDebugFile((char *)DebugFileName);
                        my_debug.OutPut("Debug file is %s",(char *)DebugFileName);
                  }
            }
      }
        wsprintf(VersionString,"%s - %s", ACM_VERSION, get_lame_version() );
      BuildBitrateTable();
      
      my_debug.OutPut(DEBUG_LEVEL_FUNC_START, "New ACM Creation (0x%08X)",this);
}

ACM::~ACM()
{
// not used, it's done automatically when closing the driver      if (my_hIcon != NULL)
//          CloseHandle(my_hIcon);

      bitrate_table.clear();

      my_debug.OutPut(DEBUG_LEVEL_FUNC_START, "ACM Deleted (0x%08X)",this);
}

//////////////////////////////////////////////////////////////////////
// Main message handler
//////////////////////////////////////////////////////////////////////

LONG ACM::DriverProcedure(const HDRVR hdrvr, const UINT msg, LONG lParam1, LONG lParam2)
{
    DWORD dwRes = 0L;

//my_debug.OutPut(DEBUG_LEVEL_MSG, "message 0x%08X for ThisACM 0x%08X", msg, this);

switch (msg) {
    case DRV_INSTALL:
            my_debug.OutPut(DEBUG_LEVEL_MSG, "DRV_INSTALL");
            // Sent when the driver is installed.
            dwRes = DRVCNF_OK;  // Can also return 
            break;              // DRVCNF_CANCEL
                                          // and DRV_RESTART

      case DRV_REMOVE:
            // Sent when the driver is removed.
            my_debug.OutPut(DEBUG_LEVEL_MSG, "DRV_REMOVE");
            dwRes = 1L;  // return value ignored
            break;

    case DRV_QUERYCONFIGURE:
            my_debug.OutPut(DEBUG_LEVEL_MSG, "DRV_QUERYCONFIGURE");
            // Sent to determine if the driver can be
            // configured.
            dwRes = 1L;  // Zero indicates configuration
            break;       // NOT supported

      case DRV_CONFIGURE:
            my_debug.OutPut(DEBUG_LEVEL_MSG, "DRV_CONFIGURE");
            // Sent to display the configuration
            // dialog box for the driver.
//          dwRes = Configure( (HWND) lParam1, (LPDRVCONFIGINFO) lParam2 );
            if (my_EncodingProperties.Config(my_hModule, (HWND) lParam1))
            {
                  dwRes = DRVCNF_OK; // Can also return
                              // DRVCNF_CANCEL
                              // and DRVCNF_RESTART
            } else {
                  dwRes = DRVCNF_CANCEL;
            }
            break;

      /**************************************
      // ACM additional messages
      ***************************************/

      case ACMDM_DRIVER_ABOUT:
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_DRIVER_ABOUT");

            dwRes = About( (HWND) lParam1 );

        break;

      case ACMDM_DRIVER_DETAILS: // acmDriverDetails
            // Fill-in general informations about the driver/codec
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_DRIVER_DETAILS");

            dwRes = OnDriverDetails(hdrvr, (LPACMDRIVERDETAILS) lParam1);
        
            break;

      case ACMDM_FORMATTAG_DETAILS: // acmFormatTagDetails
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_FORMATTAG_DETAILS");

            dwRes = OnFormatTagDetails((LPACMFORMATTAGDETAILS) lParam1, lParam2);

        break;

      case ACMDM_FORMAT_DETAILS: // acmFormatDetails
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_FORMAT_DETAILS");

            dwRes = OnFormatDetails((LPACMFORMATDETAILS) lParam1, lParam2);
            
        break;           

    case ACMDM_FORMAT_SUGGEST: // acmFormatSuggest
            // Sent to determine if the driver can be
            // configured.
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_FORMAT_SUGGEST");
            dwRes = OnFormatSuggest((LPACMDRVFORMATSUGGEST) lParam1);
        break; 

      /**************************************
      // ACM stream messages
      ***************************************/

      case ACMDM_STREAM_OPEN:
      // Sent to determine if the driver can be
      // configured.
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_STREAM_OPEN");
            dwRes = OnStreamOpen((LPACMDRVSTREAMINSTANCE) lParam1);
        break; 

      case ACMDM_STREAM_SIZE:
      // returns a recommended size for a source 
      // or destination buffer on an ACM stream
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_STREAM_SIZE");
            dwRes = OnStreamSize((LPACMDRVSTREAMINSTANCE)lParam1, (LPACMDRVSTREAMSIZE)lParam2);
        break; 

      case ACMDM_STREAM_PREPARE:
      // prepares an ACMSTREAMHEADER structure for
      // an ACM stream conversion
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_STREAM_PREPARE");
            dwRes = OnStreamPrepareHeader((LPACMDRVSTREAMINSTANCE)lParam1, (LPACMSTREAMHEADER) lParam2);
        break; 

      case ACMDM_STREAM_UNPREPARE:
      // cleans up the preparation performed by
      // the ACMDM_STREAM_PREPARE message for an ACM stream
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_STREAM_UNPREPARE");
            dwRes = OnStreamUnPrepareHeader((LPACMDRVSTREAMINSTANCE)lParam1, (LPACMSTREAMHEADER) lParam2);
        break; 

      case ACMDM_STREAM_CONVERT:
      // perform a conversion on the specified conversion stream
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_STREAM_CONVERT");
            dwRes = OnStreamConvert((LPACMDRVSTREAMINSTANCE)lParam1, (LPACMDRVSTREAMHEADER) lParam2);
            
        break; 

      case ACMDM_STREAM_CLOSE:
      // closes an ACM conversion stream
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACMDM_STREAM_CLOSE");
            dwRes = OnStreamClose((LPACMDRVSTREAMINSTANCE)lParam1);
        break;

      /**************************************
      // Unknown message
      ***************************************/

      default:
      // Process any other messages.
            my_debug.OutPut(DEBUG_LEVEL_MSG, "ACM::DriverProc unknown message (0x%08X), lParam1 = 0x%08X, lParam2 = 0x%08X", msg, lParam1, lParam2);
            return DefDriverProc ((DWORD)this, hdrvr, msg, lParam1, lParam2);
    }

    return dwRes;
}

//////////////////////////////////////////////////////////////////////
// Special message handlers
//////////////////////////////////////////////////////////////////////
/*!
      Retreive the config details of this ACM driver
      The index represent the specified format

      \param a_FormatDetails will be filled with all the corresponding data
*/
00497 inline DWORD ACM::OnFormatDetails(LPACMFORMATDETAILS a_FormatDetails, const LPARAM a_Query)
{
      DWORD Result = ACMERR_NOTPOSSIBLE;

      my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "ACM_FORMATDETAILS a_Query = 0x%08X",a_Query);
      switch (a_Query & ACM_FORMATDETAILSF_QUERYMASK) {

            // Fill-in the informations corresponding to the FormatDetails->dwFormatTagIndex
            case ACM_FORMATDETAILSF_INDEX :
                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "enter ACM_FORMATDETAILSF_INDEX for index 0x%04X:%03d",a_FormatDetails->dwFormatTag,a_FormatDetails->dwFormatIndex);
                  if (a_FormatDetails->dwFormatTag == PERSONAL_FORMAT) {
                        if (a_FormatDetails->dwFormatIndex < GetNumberEncodingFormats()) {
                              LPWAVEFORMATEX WaveExt;
                              WaveExt = a_FormatDetails->pwfx;

                              WaveExt->wFormatTag = PERSONAL_FORMAT;

                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "format in  : channels %d, sample rate %d", WaveExt->nChannels, WaveExt->nSamplesPerSec);
                              GetMP3FormatForIndex(a_FormatDetails->dwFormatIndex, *WaveExt, a_FormatDetails->szFormat);
                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "format out : channels %d, sample rate %d", WaveExt->nChannels, WaveExt->nSamplesPerSec);
                              Result = MMSYSERR_NOERROR;
                        }
                        else
                        {
                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "ACM_FORMATDETAILSF_INDEX unknown index 0x%04X:%03d",a_FormatDetails->dwFormatTag,a_FormatDetails->dwFormatIndex);
                        }
                  }
                  else if (a_FormatDetails->dwFormatTag == WAVE_FORMAT_PCM) {
                        if (a_FormatDetails->dwFormatIndex < FORMAT_MAX_NB_PCM) {
                              LPWAVEFORMATEX WaveExt;
                              WaveExt = a_FormatDetails->pwfx;

                              WaveExt->wFormatTag = WAVE_FORMAT_PCM;

                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "format in  : channels %d, sample rate %d", WaveExt->nChannels, WaveExt->nSamplesPerSec);
                              GetPCMFormatForIndex(a_FormatDetails->dwFormatIndex, *WaveExt, a_FormatDetails->szFormat);
                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "format out : channels %d, sample rate %d", WaveExt->nChannels, WaveExt->nSamplesPerSec);
                              Result = MMSYSERR_NOERROR;
                        }
                        else
                        {
                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "ACM_FORMATDETAILSF_INDEX unknown index 0x%04X:%03d",a_FormatDetails->dwFormatTag,a_FormatDetails->dwFormatIndex);
                        }
                  }
                  else
                  {
                        my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Unknown a_FormatDetails->dwFormatTag = 0x%08X",a_FormatDetails->dwFormatTag);
                  }

            case ACM_FORMATDETAILSF_FORMAT :
                  /// \todo we may output the corresponding strong (only for personal format)
                  LPWAVEFORMATEX WaveExt;
                  WaveExt = a_FormatDetails->pwfx;

                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "enter ACM_FORMATDETAILSF_FORMAT : 0x%04X:%03d, format in : channels %d, sample rate %d",a_FormatDetails->dwFormatTag,a_FormatDetails->dwFormatIndex, WaveExt->nChannels, WaveExt->nSamplesPerSec);

                  Result = MMSYSERR_NOERROR;
                  break;
            
            default:
                  Result = ACMERR_NOTPOSSIBLE;
                  break;
      }

      a_FormatDetails->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;

      return Result;
}

/*!
      Retreive the details of each known format by this ACM driver
      The index represent the specified format (0 = MP3 / 1 = PCM)

      \param a_FormatTagDetails will be filled with all the corresponding data
*/
00572 inline DWORD ACM::OnFormatTagDetails(LPACMFORMATTAGDETAILS a_FormatTagDetails, const LPARAM a_Query)
{
      DWORD Result;
      DWORD the_format = WAVE_FORMAT_UNKNOWN; // the format to give details

      if (a_FormatTagDetails->cbStruct >= sizeof(*a_FormatTagDetails)) {

            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "ACMDM_FORMATTAG_DETAILS, a_Query = 0x%08X",a_Query);
            switch(a_Query & ACM_FORMATTAGDETAILSF_QUERYMASK) {

                  case ACM_FORMATTAGDETAILSF_INDEX:
                  // Fill-in the informations corresponding to the a_FormatDetails->dwFormatTagIndex
                        my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "get ACM_FORMATTAGDETAILSF_INDEX for index %03d",a_FormatTagDetails->dwFormatTagIndex);

                        if (a_FormatTagDetails->dwFormatTagIndex < FORMAT_TAG_MAX_NB) {
                              switch (a_FormatTagDetails->dwFormatTagIndex)
                              {
                              case 0:
                                    the_format = PERSONAL_FORMAT;
                                    break;
                              default :
                                    the_format = WAVE_FORMAT_PCM;
                                    break;
                              }
                        }
                        else
                        {
                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "ACM_FORMATTAGDETAILSF_INDEX for unsupported index %03d",a_FormatTagDetails->dwFormatTagIndex);
                              Result = ACMERR_NOTPOSSIBLE;
                        }
                        break;

                  case ACM_FORMATTAGDETAILSF_FORMATTAG:
                  // Fill-in the informations corresponding to the a_FormatDetails->dwFormatTagIndex and hdrvr given
                        switch (a_FormatTagDetails->dwFormatTag)
                        {
                        case WAVE_FORMAT_PCM:
                              the_format = WAVE_FORMAT_PCM;
                              break;
                        case PERSONAL_FORMAT:
                              the_format = PERSONAL_FORMAT;
                              break;
                        default:
                    return (ACMERR_NOTPOSSIBLE);
                        }
                        my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "get ACM_FORMATTAGDETAILSF_FORMATTAG for index 0x%02X, cStandardFormats = %d",a_FormatTagDetails->dwFormatTagIndex,a_FormatTagDetails->cStandardFormats);
                        break;
                  case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
                        my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "ACM_FORMATTAGDETAILSF_LARGESTSIZE not used");
                        Result = 0L;
                        break;
                  default:
                        my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnFormatTagDetails Unknown Format tag query");
                        Result = MMSYSERR_NOTSUPPORTED;
                        break;
            }

            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnFormatTagDetails the_format = 0x%08X",the_format);
            switch(the_format)
            {
                  case WAVE_FORMAT_PCM:
                        a_FormatTagDetails->dwFormatTag      = WAVE_FORMAT_PCM;
                        a_FormatTagDetails->dwFormatTagIndex = 0;
                        a_FormatTagDetails->cbFormatSize     = sizeof(PCMWAVEFORMAT);
                        /// \note 0 may mean we don't know how to decode
                        a_FormatTagDetails->fdwSupport       = ACMDRIVERDETAILS_SUPPORTF_CODEC;
                        a_FormatTagDetails->cStandardFormats = FORMAT_MAX_NB_PCM;
                        // should be filled by Windows                        a_FormatTagDetails->szFormatTag[0] = '\0';
                        Result = MMSYSERR_NOERROR;
                        break;
                  case PERSONAL_FORMAT:
                        a_FormatTagDetails->dwFormatTag      = PERSONAL_FORMAT;
                        a_FormatTagDetails->dwFormatTagIndex = 1;
                        a_FormatTagDetails->cbFormatSize     = SIZE_FORMAT_STRUCT;
                        a_FormatTagDetails->fdwSupport       = ACMDRIVERDETAILS_SUPPORTF_CODEC;
                        a_FormatTagDetails->cStandardFormats = GetNumberEncodingFormats();
                        lstrcpyW( a_FormatTagDetails->szFormatTag, L"Lame MP3" );
                        Result = MMSYSERR_NOERROR;
                        break;
                  default:
                        my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnFormatTagDetails Unknown format 0x%08X",the_format);
                        return (ACMERR_NOTPOSSIBLE);
            }
            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnFormatTagDetails %d possibilities for format 0x%08X",a_FormatTagDetails->cStandardFormats,the_format);
      }
      else
      {
            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "a_FormatTagDetails->cbStruct < sizeof(*a_FormatDetails)");
            Result = ACMERR_NOTPOSSIBLE;
      }

      return Result;
}

/*!
      Retreive the global details of this ACM driver

      \param a_DriverDetail will be filled with all the corresponding data
*/
00671 inline DWORD ACM::OnDriverDetails(const HDRVR hdrvr, LPACMDRIVERDETAILS a_DriverDetail)
{
      if (my_hIcon == NULL)
            my_hIcon = LoadIcon(GetDriverModuleHandle(hdrvr), MAKEINTRESOURCE(IDI_ICON));
      a_DriverDetail->hicon       = my_hIcon;

      a_DriverDetail->fccType     = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
      a_DriverDetail->fccComp     = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;

      /// \note this is an explicit hack of the FhG values
      /// \note later it could be a new value when the decoding is done
      a_DriverDetail->wMid        = MM_FRAUNHOFER_IIS;
      a_DriverDetail->wPid        = MM_FHGIIS_MPEGLAYER3;

      a_DriverDetail->vdwACM      = VERSION_MSACM;
      a_DriverDetail->vdwDriver   = VERSION_ACM_DRIVER;
      a_DriverDetail->fdwSupport  = ACMDRIVERDETAILS_SUPPORTF_CODEC;
      a_DriverDetail->cFormatTags = FORMAT_TAG_MAX_NB; // 2 : MP3 and PCM
//    a_DriverDetail->cFormatTags = 1; // 2 : MP3 and PCM
      a_DriverDetail->cFilterTags = FILTER_TAG_MAX_NB;

      lstrcpyW( a_DriverDetail->szShortName, L"LAME MP3" );
      char tmpStr[128];
      wsprintf(tmpStr, "LAME MP3 Codec v%s", GetVersionString());
      int u = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, tmpStr, -1, a_DriverDetail->szLongName, 0);
      MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, tmpStr, -1, a_DriverDetail->szLongName, u);
      lstrcpyW( a_DriverDetail->szCopyright, L"2002 Steve Lhomme" );
      lstrcpyW( a_DriverDetail->szLicensing, L"LGPL (see gnu.org)" );
      /// \todo update this part when the code changes
      lstrcpyW( a_DriverDetail->szFeatures , L"only CBR implementation" );

    return MMSYSERR_NOERROR;  // Can also return DRVCNF_CANCEL
}

/*!
      Suggest an output format for the specified input format

      \param a_FormatSuggest will be filled with all the corresponding data
*/
00710 inline DWORD ACM::OnFormatSuggest(LPACMDRVFORMATSUGGEST a_FormatSuggest)
{
      DWORD Result = MMSYSERR_NOTSUPPORTED;
    DWORD fdwSuggest = (ACM_FORMATSUGGESTF_TYPEMASK & a_FormatSuggest->fdwSuggest);

my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest %s%s%s%s (0x%08X)",
                         (fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS) ? "channels, ":"",
                         (fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC) ? "samples/sec, ":"",
                         (fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE) ? "bits/sample, ":"",
                         (fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG) ? "format, ":"",
                         fdwSuggest);

my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest for source format = 0x%04X, channels = %d, Samples/s = %d, AvgB/s = %d, BlockAlign = %d, b/sample = %d",
                         a_FormatSuggest->pwfxSrc->wFormatTag,
                         a_FormatSuggest->pwfxSrc->nChannels,
                         a_FormatSuggest->pwfxSrc->nSamplesPerSec,
                         a_FormatSuggest->pwfxSrc->nAvgBytesPerSec,
                         a_FormatSuggest->pwfxSrc->nBlockAlign,
                         a_FormatSuggest->pwfxSrc->wBitsPerSample);

my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggested destination format = 0x%04X, channels = %d, Samples/s = %d, AvgB/s = %d, BlockAlign = %d, b/sample = %d",
                   a_FormatSuggest->pwfxDst->wFormatTag,
                   a_FormatSuggest->pwfxDst->nChannels,
                   a_FormatSuggest->pwfxDst->nSamplesPerSec,
                   a_FormatSuggest->pwfxDst->nAvgBytesPerSec,
                   a_FormatSuggest->pwfxDst->nBlockAlign,
                   a_FormatSuggest->pwfxDst->wBitsPerSample);

      switch (a_FormatSuggest->pwfxSrc->wFormatTag)
      {
        case WAVE_FORMAT_PCM:
                  /// \todo handle here the decoding ?
                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest for PCM source");
            //
                  //  if the destination format tag is restricted, verify that
                  //  it is within our capabilities...
                  //
                  //  this driver is able to decode to PCM
                  //
                  if (ACM_FORMATSUGGESTF_WFORMATTAG & fdwSuggest)
            {
                if (PERSONAL_FORMAT != a_FormatSuggest->pwfxDst->wFormatTag)
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->wFormatTag = PERSONAL_FORMAT;
            }


my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest succeed A");
            //
                  //  if the destination channel count is restricted, verify that
                  //  it is within our capabilities...
                  //
                  //  this driver is not able to change the number of channels
                  //
                  if (ACM_FORMATSUGGESTF_NCHANNELS & fdwSuggest)
            {
                if (a_FormatSuggest->pwfxSrc->nChannels != a_FormatSuggest->pwfxDst->nChannels)
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->nChannels = a_FormatSuggest->pwfxSrc->nChannels;
            }

                  if (a_FormatSuggest->pwfxSrc->nChannels != 1 && a_FormatSuggest->pwfxSrc->nChannels != 2)
                        return MMSYSERR_INVALPARAM;


my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest succeed B");
            //
                  //  if the destination samples per second is restricted, verify
                  //  that it is within our capabilities...
                  //
                  //  this driver is not able to change the sample rate
                  //
                  if (ACM_FORMATSUGGESTF_NSAMPLESPERSEC & fdwSuggest)
            {
                if (a_FormatSuggest->pwfxSrc->nSamplesPerSec != a_FormatSuggest->pwfxDst->nSamplesPerSec)
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->nSamplesPerSec = a_FormatSuggest->pwfxSrc->nSamplesPerSec;
            }


my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest succeed C");
            //
                  //  if the destination bits per sample is restricted, verify
                  //  that it is within our capabilities...
                  //
                  //  We prefer decoding to 16-bit PCM.
                  //
                  if (ACM_FORMATSUGGESTF_WBITSPERSAMPLE & fdwSuggest)
            {
                if ( (16 != a_FormatSuggest->pwfxDst->wBitsPerSample) && (8 != a_FormatSuggest->pwfxDst->wBitsPerSample) )
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->wBitsPerSample = 16;
            }

                  //                a_FormatSuggest->pwfxDst->nBlockAlign = FORMAT_BLOCK_ALIGN;
                  a_FormatSuggest->pwfxDst->nBlockAlign = a_FormatSuggest->pwfxDst->nChannels * a_FormatSuggest->pwfxDst->wBitsPerSample / 8;
                  
                  a_FormatSuggest->pwfxDst->nAvgBytesPerSec = a_FormatSuggest->pwfxDst->nChannels * 64000 / 8;

                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest succeed");
                  Result = MMSYSERR_NOERROR;


                  break;
            case PERSONAL_FORMAT:
                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest for PERSONAL source");
            //
                  //  if the destination format tag is restricted, verify that
                  //  it is within our capabilities...
                  //
                  //  this driver is able to decode to PCM
                  //
                  if (ACM_FORMATSUGGESTF_WFORMATTAG & fdwSuggest)
            {
                if (WAVE_FORMAT_PCM != a_FormatSuggest->pwfxDst->wFormatTag)
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->wFormatTag = WAVE_FORMAT_PCM;
            }


            //
                  //  if the destination channel count is restricted, verify that
                  //  it is within our capabilities...
                  //
                  //  this driver is not able to change the number of channels
                  //
                  if (ACM_FORMATSUGGESTF_NCHANNELS & fdwSuggest)
            {
                if (a_FormatSuggest->pwfxSrc->nChannels != a_FormatSuggest->pwfxDst->nChannels)
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->nChannels = a_FormatSuggest->pwfxSrc->nChannels;
            }


            //
                  //  if the destination samples per second is restricted, verify
                  //  that it is within our capabilities...
                  //
                  //  this driver is not able to change the sample rate
                  //
                  if (ACM_FORMATSUGGESTF_NSAMPLESPERSEC & fdwSuggest)
            {
                if (a_FormatSuggest->pwfxSrc->nSamplesPerSec != a_FormatSuggest->pwfxDst->nSamplesPerSec)
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->nSamplesPerSec = a_FormatSuggest->pwfxSrc->nSamplesPerSec;
            }


            //
                  //  if the destination bits per sample is restricted, verify
                  //  that it is within our capabilities...
                  //
                  //  We prefer decoding to 16-bit PCM.
                  //
                  if (ACM_FORMATSUGGESTF_WBITSPERSAMPLE & fdwSuggest)
            {
                if ( (16 != a_FormatSuggest->pwfxDst->wBitsPerSample) && (8 != a_FormatSuggest->pwfxDst->wBitsPerSample) )
                    return (ACMERR_NOTPOSSIBLE);
            }
            else
                  {
                a_FormatSuggest->pwfxDst->wBitsPerSample = 16;
            }

                  //                a_FormatSuggest->pwfxDst->nBlockAlign = FORMAT_BLOCK_ALIGN;
                  a_FormatSuggest->pwfxDst->nBlockAlign = a_FormatSuggest->pwfxDst->nChannels * a_FormatSuggest->pwfxDst->wBitsPerSample / 8;
                  
                  /// \todo this value must be a correct one !
                  a_FormatSuggest->pwfxDst->nAvgBytesPerSec = a_FormatSuggest->pwfxDst->nSamplesPerSec * a_FormatSuggest->pwfxDst->nChannels * a_FormatSuggest->pwfxDst->wBitsPerSample / 8;

                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggest succeed");
                  Result = MMSYSERR_NOERROR;


                  break;
      }

      my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Suggested destination format = 0x%04X, channels = %d, Samples/s = %d, AvgB/s = %d, BlockAlign = %d, b/sample = %d",
                         a_FormatSuggest->pwfxDst->wFormatTag,
                         a_FormatSuggest->pwfxDst->nChannels,
                         a_FormatSuggest->pwfxDst->nSamplesPerSec,
                         a_FormatSuggest->pwfxDst->nAvgBytesPerSec,
                         a_FormatSuggest->pwfxDst->nBlockAlign,
                         a_FormatSuggest->pwfxDst->wBitsPerSample);

      return Result;
}

/*!
      Create a stream instance for decoding/encoding

      \param a_StreamInstance contain information about the stream desired
*/
00924 inline DWORD ACM::OnStreamOpen(LPACMDRVSTREAMINSTANCE a_StreamInstance)
{
      DWORD Result = ACMERR_NOTPOSSIBLE;

      //
      //  the most important condition to check before doing anything else
      //  is that this ACM driver can actually perform the conversion we are
      //  being opened for. this check should fail as quickly as possible
      //  if the conversion is not possible by this driver.
      //
      //  it is VERY important to fail quickly so the ACM can attempt to
      //  find a driver that is suitable for the conversion. also note that
      //  the ACM may call this driver several times with slightly different
      //  format specifications before giving up.
      //
      //  this driver first verifies that the source and destination formats
      //  are acceptable...
      //
      switch (a_StreamInstance->pwfxSrc->wFormatTag)
      {
        case WAVE_FORMAT_PCM:
                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Open stream for PCM source (%05d samples %d channels %d bits/sample)",a_StreamInstance->pwfxSrc->nSamplesPerSec,a_StreamInstance->pwfxSrc->nChannels,a_StreamInstance->pwfxSrc->wBitsPerSample);
                  if (a_StreamInstance->pwfxDst->wFormatTag == PERSONAL_FORMAT)
                  {
                        unsigned int OutputFrequency;

                        /// \todo Smart mode
                        if (my_EncodingProperties.GetSmartOutputMode())
                              OutputFrequency = ACMStream::GetOutputSampleRate(a_StreamInstance->pwfxSrc->nSamplesPerSec,a_StreamInstance->pwfxDst->nAvgBytesPerSec,a_StreamInstance->pwfxDst->nChannels);
                        else
                              OutputFrequency = a_StreamInstance->pwfxSrc->nSamplesPerSec;

                        my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Open stream for PERSONAL output (%05d samples %d channels %d bits/sample %d kbps)",a_StreamInstance->pwfxDst->nSamplesPerSec,a_StreamInstance->pwfxDst->nChannels,a_StreamInstance->pwfxDst->wBitsPerSample,8 * a_StreamInstance->pwfxDst->nAvgBytesPerSec);

                        /// \todo add the possibility to have channel resampling (mono to stereo / stereo to mono)
                        /// \todo support resampling ?
                        /// \todo only do the test on OutputFrequency in "Smart Output" mode
                        if (a_StreamInstance->pwfxDst->nSamplesPerSec != OutputFrequency ||
//                            a_StreamInstance->pwfxSrc->nSamplesPerSec != a_StreamInstance->pwfxDst->nSamplesPerSec ||
                              a_StreamInstance->pwfxSrc->nChannels != a_StreamInstance->pwfxDst->nChannels ||
                              a_StreamInstance->pwfxSrc->wBitsPerSample != 16)
                        {
                              Result = ACMERR_NOTPOSSIBLE;
                        } else {
                              if ((a_StreamInstance->fdwOpen & ACM_STREAMOPENF_QUERY) == 0)
                              {
                                    ACMStream * the_stream = ACMStream::Create();
                                    a_StreamInstance->dwInstance = (DWORD) the_stream;

                                    if (the_stream != NULL)
                                    {
                                          MPEGLAYER3WAVEFORMAT * casted = (MPEGLAYER3WAVEFORMAT *) a_StreamInstance->pwfxDst;
                                          vbr_mode a_mode = (casted->fdwFlags-2 == 0)?vbr_abr:vbr_off;
                                          if (the_stream->init(a_StreamInstance->pwfxDst->nSamplesPerSec,
                                                                         OutputFrequency,
                                                                         a_StreamInstance->pwfxDst->nChannels,
                                                                         a_StreamInstance->pwfxDst->nAvgBytesPerSec,
                                                                         a_mode))
                                                Result = MMSYSERR_NOERROR;
                                          else
                                                ACMStream::Erase( the_stream );
                                    }
                              }
                              else
                              {
                                    Result = MMSYSERR_NOERROR;
                              }
                        }
                  }
                  break;
            case PERSONAL_FORMAT:
                  my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Open stream for PERSONAL source (%05d samples %d channels %d bits/sample %d kbps)",a_StreamInstance->pwfxSrc->nSamplesPerSec,a_StreamInstance->pwfxSrc->nChannels,a_StreamInstance->pwfxSrc->wBitsPerSample,8 * a_StreamInstance->pwfxSrc->nAvgBytesPerSec);
                  if (a_StreamInstance->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
                  {
#ifdef ENABLE_DECODING
                        if ((a_StreamInstance->fdwOpen & ACM_STREAMOPENF_QUERY) == 0)
                        {
                              /// \todo create the decoding stream
                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Open stream for PCM output (%05d samples %d channels %d bits/sample %d B/s)",a_StreamInstance->pwfxDst->nSamplesPerSec,a_StreamInstance->pwfxDst->nChannels,a_StreamInstance->pwfxDst->wBitsPerSample,a_StreamInstance->pwfxDst->nAvgBytesPerSec);

                              DecodeStream * the_stream = DecodeStream::Create();
                              a_StreamInstance->dwInstance = (DWORD) the_stream;

                              if (the_stream != NULL)
                              {
                                    if (the_stream->init(a_StreamInstance->pwfxDst->nSamplesPerSec,
                                                                   a_StreamInstance->pwfxDst->nChannels,
                                                                   a_StreamInstance->pwfxDst->nAvgBytesPerSec,
                                                                   a_StreamInstance->pwfxSrc->nAvgBytesPerSec))
                                          Result = MMSYSERR_NOERROR;
                                    else
                                          DecodeStream::Erase( the_stream );
                              }
                        }
                        else
                        {
                              /// \todo decoding verification
                              my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Open stream is valid");
                              Result = MMSYSERR_NOERROR;
                        }
#endif // ENABLE_DECODING
                  }
                  break;
      }

      my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Open stream Result = %d",Result);
      return Result;
}

inline DWORD ACM::OnStreamSize(LPACMDRVSTREAMINSTANCE a_StreamInstance, LPACMDRVSTREAMSIZE the_StreamSize)
{
      DWORD Result = ACMERR_NOTPOSSIBLE;

    switch (ACM_STREAMSIZEF_QUERYMASK & the_StreamSize->fdwSize)
    {
      case ACM_STREAMSIZEF_DESTINATION:
            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Get source buffer size for destination size = %d",the_StreamSize->cbDstLength);
            break;
      case ACM_STREAMSIZEF_SOURCE:
            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "Get destination buffer size for source size = %d",the_StreamSize->cbSrcLength);
        if (WAVE_FORMAT_PCM == a_StreamInstance->pwfxSrc->wFormatTag &&
                  PERSONAL_FORMAT == a_StreamInstance->pwfxDst->wFormatTag)
        {
                  ACMStream * the_stream = (ACMStream *) a_StreamInstance->dwInstance;
                  if (the_stream != NULL)
                  {
                        the_StreamSize->cbDstLength = the_stream->GetOutputSizeForInput(the_StreamSize->cbSrcLength);
                        Result = MMSYSERR_NOERROR;
                  }
            }
        else if (PERSONAL_FORMAT == a_StreamInstance->pwfxSrc->wFormatTag &&
                   WAVE_FORMAT_PCM== a_StreamInstance->pwfxDst->wFormatTag)
            {
#ifdef ENABLE_DECODING
                  DecodeStream * the_stream = (DecodeStream *) a_StreamInstance->dwInstance;
                  if (the_stream != NULL)
                  {
                        the_StreamSize->cbDstLength = the_stream->GetOutputSizeForInput(the_StreamSize->cbSrcLength);
                        Result = MMSYSERR_NOERROR;
                  }
#endif // ENABLE_DECODING
            }
            break;
      default:
            Result = MMSYSERR_INVALFLAG;
            break;
      }

      return Result;
}

inline DWORD ACM::OnStreamClose(LPACMDRVSTREAMINSTANCE a_StreamInstance)
{
      DWORD Result = ACMERR_NOTPOSSIBLE;

      my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnStreamClose the stream 0x%X",a_StreamInstance->dwInstance);
    if (WAVE_FORMAT_PCM == a_StreamInstance->pwfxSrc->wFormatTag &&
            PERSONAL_FORMAT == a_StreamInstance->pwfxDst->wFormatTag)
    {
      ACMStream::Erase( (ACMStream *) a_StreamInstance->dwInstance );
      }
    else if (PERSONAL_FORMAT == a_StreamInstance->pwfxSrc->wFormatTag &&
             WAVE_FORMAT_PCM== a_StreamInstance->pwfxDst->wFormatTag)
    {
#ifdef ENABLE_DECODING
            DecodeStream::Erase( (DecodeStream *) a_StreamInstance->dwInstance );
#endif // ENABLE_DECODING
      }

      // nothing to do yet
      Result = MMSYSERR_NOERROR;

      return Result;
}

inline DWORD ACM::OnStreamPrepareHeader(LPACMDRVSTREAMINSTANCE a_StreamInstance, LPACMSTREAMHEADER a_StreamHeader)
{
      DWORD Result = ACMERR_NOTPOSSIBLE;

      my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "  prepare : Src : %d (0x%08X) / %d - Dst : %d (0x%08X) / %d"
                                                                        , a_StreamHeader->cbSrcLength
                                                                        , a_StreamHeader->pbSrc
                                                                        , a_StreamHeader->cbSrcLengthUsed
                                                                        , a_StreamHeader->cbDstLength
                                                                        , a_StreamHeader->pbDst
                                                                        , a_StreamHeader->cbDstLengthUsed
                                                                    );

      if (WAVE_FORMAT_PCM == a_StreamInstance->pwfxSrc->wFormatTag &&
            PERSONAL_FORMAT == a_StreamInstance->pwfxDst->wFormatTag)
      {
            ACMStream * the_stream = (ACMStream *)a_StreamInstance->dwInstance;
            
            if (the_stream->open(my_EncodingProperties))
                  Result = MMSYSERR_NOERROR;
      }
      else if (PERSONAL_FORMAT == a_StreamInstance->pwfxSrc->wFormatTag &&
                 WAVE_FORMAT_PCM == a_StreamInstance->pwfxDst->wFormatTag)
      {
#ifdef ENABLE_DECODING
            DecodeStream * the_stream = (DecodeStream *)a_StreamInstance->dwInstance;
            
            if (the_stream->open())
                  Result = MMSYSERR_NOERROR;
#endif // ENABLE_DECODING
      }

      return Result;
}

inline DWORD ACM::OnStreamUnPrepareHeader(LPACMDRVSTREAMINSTANCE a_StreamInstance, LPACMSTREAMHEADER a_StreamHeader)
{
      DWORD Result = ACMERR_NOTPOSSIBLE;

      my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "unprepare : Src : %d / %d - Dst : %d / %d"
                                                                  , a_StreamHeader->cbSrcLength
                                                                  , a_StreamHeader->cbSrcLengthUsed
                                                                  , a_StreamHeader->cbDstLength
                                                                  , a_StreamHeader->cbDstLengthUsed
                                                                  );
    if (WAVE_FORMAT_PCM == a_StreamInstance->pwfxSrc->wFormatTag &&
            PERSONAL_FORMAT == a_StreamInstance->pwfxDst->wFormatTag)
    {
      ACMStream * the_stream = (ACMStream *)a_StreamInstance->dwInstance;
      DWORD OutputSize = a_StreamHeader->cbDstLength;
      
      if (the_stream->close(a_StreamHeader->pbDst, &OutputSize) && (OutputSize <= a_StreamHeader->cbDstLength))
      {
            a_StreamHeader->cbDstLengthUsed = OutputSize;
                  Result = MMSYSERR_NOERROR;
            }
      }
    else if (PERSONAL_FORMAT == a_StreamInstance->pwfxSrc->wFormatTag &&
             WAVE_FORMAT_PCM== a_StreamInstance->pwfxDst->wFormatTag)
    {
#ifdef ENABLE_DECODING
            DecodeStream * the_stream = (DecodeStream *)a_StreamInstance->dwInstance;
            DWORD OutputSize = a_StreamHeader->cbDstLength;
            
            if (the_stream->close(a_StreamHeader->pbDst, &OutputSize) && (OutputSize <= a_StreamHeader->cbDstLength))
            {
                  a_StreamHeader->cbDstLengthUsed = OutputSize;
                  Result = MMSYSERR_NOERROR;
      }
#endif // ENABLE_DECODING
      }

      return Result;
}

inline DWORD ACM::OnStreamConvert(LPACMDRVSTREAMINSTANCE a_StreamInstance, LPACMDRVSTREAMHEADER a_StreamHeader)
{
      DWORD Result = ACMERR_NOTPOSSIBLE;

      if (WAVE_FORMAT_PCM == a_StreamInstance->pwfxSrc->wFormatTag &&
            PERSONAL_FORMAT == a_StreamInstance->pwfxDst->wFormatTag)
      {
            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnStreamConvert SRC = PCM (encode)");

            ACMStream * the_stream = (ACMStream *) a_StreamInstance->dwInstance;
            if (the_stream != NULL)
            {
                  if (the_stream->ConvertBuffer( a_StreamHeader ))
                        Result = MMSYSERR_NOERROR;
            }
      }
      else if (PERSONAL_FORMAT == a_StreamInstance->pwfxSrc->wFormatTag &&
                 WAVE_FORMAT_PCM == a_StreamInstance->pwfxDst->wFormatTag)
      {
            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnStreamConvert SRC = MP3 (decode)");

#ifdef ENABLE_DECODING
            DecodeStream * the_stream = (DecodeStream *) a_StreamInstance->dwInstance;
            if (the_stream != NULL)
            {
                  if (the_stream->ConvertBuffer( a_StreamHeader ))
                        Result = MMSYSERR_NOERROR;
            }
#endif // ENABLE_DECODING
      }
      else
            my_debug.OutPut(DEBUG_LEVEL_FUNC_CODE, "OnStreamConvert unsupported conversion");

      return Result;
}


01211 void ACM::GetMP3FormatForIndex(const DWORD the_Index, WAVEFORMATEX & the_Format, unsigned short the_String[ACMFORMATDETAILS_FORMAT_CHARS]) const
{
      int Block_size;
    char temp[ACMFORMATDETAILS_FORMAT_CHARS];


      if (the_Index < bitrate_table.size())
      {
      //    the_Format.wBitsPerSample = 16;
            the_Format.wBitsPerSample = 0;
      
            /// \todo handle more channel modes (mono, stereo, joint-stereo, dual-channel)
      //    the_Format.nChannels = SIZE_CHANNEL_MODE - int(the_Index % SIZE_CHANNEL_MODE);
      
            the_Format.nBlockAlign = 1;

            the_Format.nSamplesPerSec = bitrate_table[the_Index].frequency;
            the_Format.nAvgBytesPerSec = bitrate_table[the_Index].bitrate * 1000 / 8;
            if (bitrate_table[the_Index].frequency >= mpeg1_freq[SIZE_FREQ_MPEG1-1])
                  Block_size = 1152;
            else
                  Block_size = 576;
      
            the_Format.nChannels = bitrate_table[the_Index].channels;

            the_Format.cbSize = sizeof(MPEGLAYER3WAVEFORMAT) - sizeof(WAVEFORMATEX);
            MPEGLAYER3WAVEFORMAT * tmpFormat = (MPEGLAYER3WAVEFORMAT *) &the_Format;
            tmpFormat->wID             = 1;
            // this is the only way I found to know if we do CBR or ABR
            tmpFormat->fdwFlags        = 2 + ((bitrate_table[the_Index].mode == vbr_abr)?0:2);
            tmpFormat->nBlockSize      = WORD(Block_size * the_Format.nAvgBytesPerSec / the_Format.nSamplesPerSec);
            tmpFormat->nFramesPerBlock = 1;
            tmpFormat->nCodecDelay     = 0; // 0x0571 on FHG
      
         /// \todo : generate the string with the appropriate stereo mode
         if (bitrate_table[the_Index].mode == vbr_abr)
             wsprintfA( temp, "%d Hz, %d kbps ABR, %s", the_Format.nSamplesPerSec, the_Format.nAvgBytesPerSec * 8 / 1000, (the_Format.nChannels == 1)?"Mono":"Stereo");
         else
             wsprintfA( temp, "%d Hz, %d kbps CBR, %s", the_Format.nSamplesPerSec, the_Format.nAvgBytesPerSec * 8 / 1000, (the_Format.nChannels == 1)?"Mono":"Stereo");

         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, temp, -1, the_String, ACMFORMATDETAILS_FORMAT_CHARS);
     }
 }

void ACM::GetPCMFormatForIndex(const DWORD the_Index, WAVEFORMATEX & the_Format, unsigned short the_String[ACMFORMATDETAILS_FORMAT_CHARS]) const
{
      the_Format.nChannels = SIZE_CHANNEL_MODE - int(the_Index % SIZE_CHANNEL_MODE);
      the_Format.wBitsPerSample = 16;
      the_Format.nBlockAlign = the_Format.nChannels * the_Format.wBitsPerSample / 8;


      DWORD a_Channel_Independent = the_Index / SIZE_CHANNEL_MODE;

      // first MPEG1 frequencies
      if (a_Channel_Independent < SIZE_FREQ_MPEG1)
      {
            the_Format.nSamplesPerSec = mpeg1_freq[a_Channel_Independent];
      }
      else
      {
            a_Channel_Independent -= SIZE_FREQ_MPEG1;
            the_Format.nSamplesPerSec = mpeg2_freq[a_Channel_Independent];
      }

      the_Format.nAvgBytesPerSec = the_Format.nSamplesPerSec * the_Format.nChannels * the_Format.wBitsPerSample / 8;
}

DWORD ACM::GetNumberEncodingFormats() const
{
      return bitrate_table.size();
}

bool ACM::IsSmartOutput(const int frequency, const int bitrate, const int channels) const
{
      double compression_ratio = double(frequency * 2 * channels) / double(bitrate * 100);

//my_debug.OutPut(DEBUG_LEVEL_FUNC_DEBUG, "compression_ratio %f, freq %d, bitrate %d, channels %d", compression_ratio, frequency, bitrate, channels);

      if(my_EncodingProperties.GetSmartOutputMode())
            return (compression_ratio <= my_EncodingProperties.GetSmartRatio());
      else return true;
}

void ACM::BuildBitrateTable()
{
      my_debug.OutPut("entering BuildBitrateTable");

      // fill the table
      unsigned int channel,bitrate,freq;
      
      bitrate_table.clear();

      // CBR bitrates
      for (channel = 0;channel < SIZE_CHANNEL_MODE;channel++)
      {
            // MPEG I
            for (freq = 0;freq < SIZE_FREQ_MPEG1;freq++)
            {
                  for (bitrate = 0;bitrate < SIZE_BITRATE_MPEG1;bitrate++)
                  {

                        if (!my_EncodingProperties.GetSmartOutputMode() || IsSmartOutput(mpeg1_freq[freq], mpeg1_bitrate[bitrate], channel+1))
                        {
                              bitrate_item bitrate_table_tmp;
                              
                              bitrate_table_tmp.frequency = mpeg1_freq[freq];
                              bitrate_table_tmp.bitrate = mpeg1_bitrate[bitrate];
                              bitrate_table_tmp.channels = channel+1;
                              bitrate_table_tmp.mode = vbr_off;
                              bitrate_table.push_back(bitrate_table_tmp);
                        }
                  }
            }
            // MPEG II / II.5
            for (freq = 0;freq < SIZE_FREQ_MPEG2;freq++)
            {
                  for (bitrate = 0;bitrate < SIZE_BITRATE_MPEG2;bitrate++)
                  {
                        if (!my_EncodingProperties.GetSmartOutputMode() || IsSmartOutput(mpeg2_freq[freq], mpeg2_bitrate[bitrate], channel+1))
                        {
                              bitrate_item bitrate_table_tmp;

                                        bitrate_table_tmp.frequency = mpeg2_freq[freq];
                              bitrate_table_tmp.bitrate = mpeg2_bitrate[bitrate];
                              bitrate_table_tmp.channels = channel+1;
                              bitrate_table_tmp.mode = vbr_abr;
                              bitrate_table.push_back(bitrate_table_tmp);
                        }
                  }
            }
      }

      if (my_EncodingProperties.GetAbrOutputMode())
      // ABR bitrates
      {
            for (channel = 0;channel < SIZE_CHANNEL_MODE;channel++)
            {
                  // MPEG I
                  for (freq = 0;freq < SIZE_FREQ_MPEG1;freq++)
                  {
                        for (bitrate = my_EncodingProperties.GetAbrBitrateMax();
                                 bitrate >= my_EncodingProperties.GetAbrBitrateMin(); 
                             bitrate -= my_EncodingProperties.GetAbrBitrateStep())
                        {
                              if (bitrate >= mpeg1_bitrate[SIZE_BITRATE_MPEG1-1] && (!my_EncodingProperties.GetSmartOutputMode() || IsSmartOutput(mpeg1_freq[freq], bitrate, channel+1)))
                              {
                                    bitrate_item bitrate_table_tmp;
                                    
                                    bitrate_table_tmp.frequency = mpeg1_freq[freq];
                                    bitrate_table_tmp.bitrate = bitrate;
                                    bitrate_table_tmp.channels = channel+1;
                                    bitrate_table_tmp.mode = vbr_abr;
                                    bitrate_table.push_back(bitrate_table_tmp);
                              }
                        }
                  }
                  // MPEG II / II.5
                  for (freq = 0;freq < SIZE_FREQ_MPEG2;freq++)
                  {
                        for (bitrate = my_EncodingProperties.GetAbrBitrateMax();
                                 bitrate >= my_EncodingProperties.GetAbrBitrateMin(); 
                             bitrate -= my_EncodingProperties.GetAbrBitrateStep())
                        {
                              if (bitrate >= mpeg2_bitrate[SIZE_BITRATE_MPEG2-1] && (!my_EncodingProperties.GetSmartOutputMode() || IsSmartOutput(mpeg2_freq[freq], bitrate, channel+1)))
                              {
                                    bitrate_item bitrate_table_tmp;
                                    
                                    bitrate_table_tmp.frequency = mpeg2_freq[freq];
                                    bitrate_table_tmp.bitrate = bitrate;
                                    bitrate_table_tmp.channels = channel+1;
                                    bitrate_table_tmp.mode = vbr_abr;
                                    bitrate_table.push_back(bitrate_table_tmp);
                              }
                        }
                  }
            }
      }

      // sorting by frequency/bitrate/channel
      std::sort(bitrate_table.begin(), bitrate_table.end());

/*    {
            // display test
            int i=0;
            for (i=0; i<bitrate_table.size();i++)
            {
                  my_debug.OutPut("bitrate_table[%d].frequency = %d",i,bitrate_table[i].frequency);
                  my_debug.OutPut("bitrate_table[%d].bitrate = %d",i,bitrate_table[i].bitrate);
                  my_debug.OutPut("bitrate_table[%d].channel = %d",i,bitrate_table[i].channels);
                  my_debug.OutPut("bitrate_table[%d].ABR = %s\n",i,(bitrate_table[i].mode == vbr_abr)?"ABR":"CBR");
            }
      }*/

      my_debug.OutPut("leaving BuildBitrateTable");
}

Generated by  Doxygen 1.6.0   Back to index