/** * * 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.18 2005/03/13 14:43:21 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 <version.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[20]; const char ACM_VERSION[] = "0.9.0"; #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,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 ////////////////////////////////////////////////////////////////////// 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); } } } #if LAME_ALPHA_VERSION > 0 wsprintf(VersionString,"%s - %d.%d (alpha %d)", ACM_VERSION, LAME_MAJOR_VERSION, LAME_MINOR_VERSION,LAME_PATCH_VERSION); #elif LAME_BETA_VERSION > 0 wsprintf(VersionString,"%s - %d.%d (beta %d)", ACM_VERSION, LAME_MAJOR_VERSION, LAME_MINOR_VERSION, LAME_PATCH_VERSION); #else wsprintf(VersionString,"%s - %d.%d (stable)", ACM_VERSION, LAME_MAJOR_VERSION, LAME_MINOR_VERSION); #endif 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 */ 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 */ 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 */ 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 */ 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 */ 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; } 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 = 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 = new bitrate_item; if (bitrate_table_tmp == NULL) return; 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 = new bitrate_item; if (bitrate_table_tmp == NULL) return; 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 = new bitrate_item; if (bitrate_table_tmp == NULL) return; 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 = new bitrate_item; if (bitrate_table_tmp == NULL) return; 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"); }