#include #include #include #include #include #include #include #define TSSND_NATIVE_BITSPERSAMPLE 16 #define TSSND_NATIVE_CHANNELS 2 #define TSSND_NATIVE_SAMPLERATE 22050 #define TSSND_NATIVE_BLOCKALIGN ((TSSND_NATIVE_BITSPERSAMPLE * \ TSSND_NATIVE_CHANNELS) / 8) #define TSSND_NATIVE_AVGBYTESPERSEC (TSSND_NATIVE_BLOCKALIGN * \ TSSND_NATIVE_SAMPLERATE) #define TSSND_SAMPLESPERBLOCK 8192 // // Defines // #undef ASSERT #ifdef DBG #define TRC _DebugMessage #define ASSERT(_x_) if (!(_x_)) \ { TRC(FATAL, "ASSERT failed, line %d, file %s\n", \ __LINE__, __FILE__); DebugBreak(); } #else // !DBG #define TRC #define ASSERT #endif // !DBG #define TSMALLOC( _x_ ) malloc( _x_ ) #define TSFREE( _x_ ) free( _x_ ) #ifndef G723MAGICWORD1 #define G723MAGICWORD1 0xf7329ace #endif #ifndef G723MAGICWORD2 #define G723MAGICWORD2 0xacdeaea2 #endif #ifndef VOXWARE_KEY #define VOXWARE_KEY "35243410-F7340C0668-CD78867B74DAD857-AC71429AD8CAFCB5-E4E1A99E7FFD-371" #endif #ifndef WMAUDIO_KEY #define WMAUDIO_KEY "F6DC9830-BC79-11d2-A9D0-006097926036" #endif #ifndef WMAUDIO_DEC_KEY #define WMAUDIO_DEC_KEY "1A0F78F0-EC8A-11d2-BBBE-006008320064" #endif #define WAVE_FORMAT_WMAUDIO2 0x161 const CHAR *ALV = "TSSNDD::ALV - "; const CHAR *INF = "TSSNDD::INF - "; const CHAR *WRN = "TSSNDD::WRN - "; const CHAR *ERR = "TSSNDD::ERR - "; const CHAR *FATAL = "TSSNDD::FATAL - "; typedef struct _VCSNDFORMATLIST { struct _VCSNDFORMATLIST *pNext; HACMDRIVERID hacmDriverId; WAVEFORMATEX Format; // additional data for the format } VCSNDFORMATLIST, *PVCSNDFORMATLIST; #ifdef _WIN32 #include #else #ifndef RC_INVOKED #pragma pack(1) #endif #endif typedef struct wmaudio2waveformat_tag { WAVEFORMATEX wfx; DWORD dwSamplesPerBlock; // only counting "new" samples "= half of what will be used due to overlapping WORD wEncodeOptions; DWORD dwSuperBlockAlign; // the big size... should be multiples of wfx.nBlockAlign. } WMAUDIO2WAVEFORMAT; typedef struct msg723waveformat_tag { WAVEFORMATEX wfx; WORD wConfigWord; DWORD dwCodeword1; DWORD dwCodeword2; } MSG723WAVEFORMAT; typedef struct intelg723waveformat_tag { WAVEFORMATEX wfx; WORD wConfigWord; DWORD dwCodeword1; DWORD dwCodeword2; } INTELG723WAVEFORMAT; typedef struct tagVOXACM_WAVEFORMATEX { WAVEFORMATEX wfx; DWORD dwCodecId; DWORD dwMode; char szKey[72]; } VOXACM_WAVEFORMATEX, *PVOXACM_WAVEFORMATEX, FAR *LPVOXACM_WAVEFORMATEX; #ifdef _WIN32 #include #else #ifndef RC_INVOKED #pragma pack() #endif #endif ///////////////////////////////////////////////////////////////////// // // Tracing // ///////////////////////////////////////////////////////////////////// VOID _cdecl _DebugMessage( LPCSTR szLevel, LPCSTR szFormat, ... ) { CHAR szBuffer[256]; va_list arglist; if (szLevel == ALV) return; va_start (arglist, szFormat); _vsnprintf (szBuffer, sizeof(szBuffer), szFormat, arglist); va_end (arglist); // printf( "%s:%s", szLevel, szBuffer ); OutputDebugStringA(szLevel); OutputDebugStringA(szBuffer); } /* * Function: * _VCSmdFindSuggestedConverter * * Description: * Searches for intermidiate converter * */ BOOL _VCSndFindSuggestedConverter( HACMDRIVERID hadid, LPWAVEFORMATEX pDestFormat, LPWAVEFORMATEX pInterrimFmt ) { BOOL rv = FALSE; MMRESULT mmres; HACMDRIVER hacmDriver = NULL; HACMSTREAM hacmStream = NULL; ASSERT( NULL != pDestFormat ); ASSERT( NULL != hadid ); ASSERT( NULL != pInterrimFmt ); // // first, open the destination acm driver // mmres = acmDriverOpen(&hacmDriver, hadid, 0); if ( MMSYSERR_NOERROR != mmres ) { TRC(ERR, "_VCSndFindSuggestedConverter: can't " "open the acm driver: %d\n", mmres); goto exitpt; } // // first probe with the native format // if it passes, we don't need intermidiate // format converter // pInterrimFmt->wFormatTag = WAVE_FORMAT_PCM; pInterrimFmt->nChannels = TSSND_NATIVE_CHANNELS; pInterrimFmt->nSamplesPerSec = TSSND_NATIVE_SAMPLERATE; pInterrimFmt->nAvgBytesPerSec = TSSND_NATIVE_AVGBYTESPERSEC; pInterrimFmt->nBlockAlign = TSSND_NATIVE_BLOCKALIGN; pInterrimFmt->wBitsPerSample = TSSND_NATIVE_BITSPERSAMPLE; pInterrimFmt->cbSize = 0; mmres = acmStreamOpen( &hacmStream, hacmDriver, pInterrimFmt, pDestFormat, NULL, // filter 0, // callback 0, // dwinstance ACM_STREAMOPENF_NONREALTIME ); if ( MMSYSERR_NOERROR == mmres ) { // // format is supported // rv = TRUE; goto exitpt; } else { TRC(ALV, "_VCSndFindSuggestedConverter: format is not supported\n"); } // // find a suggested intermidiate PCM format // mmres = acmFormatSuggest( hacmDriver, pDestFormat, pInterrimFmt, sizeof( *pInterrimFmt ), ACM_FORMATSUGGESTF_WFORMATTAG ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ALV, "_VCSndFindSuggestedConverter: can't find " "interrim format: %d\n", mmres); goto exitpt; } if ( 16 != pInterrimFmt->wBitsPerSample || ( 1 != pInterrimFmt->nChannels && 2 != pInterrimFmt->nChannels) || ( 8000 != pInterrimFmt->nSamplesPerSec && 11025 != pInterrimFmt->nSamplesPerSec && 12000 != pInterrimFmt->nSamplesPerSec && 16000 != pInterrimFmt->nSamplesPerSec && 22050 != pInterrimFmt->nSamplesPerSec) ) { TRC(ALV, "_VCSndFindSuggestedConverter: not supported " "interrim format. Details:\n"); TRC(ALV, "Channels - %d\n", pInterrimFmt->nChannels); TRC(ALV, "SamplesPerSec - %d\n", pInterrimFmt->nSamplesPerSec); TRC(ALV, "AvgBytesPerSec - %d\n", pInterrimFmt->nAvgBytesPerSec); TRC(ALV, "BlockAlign - %d\n", pInterrimFmt->nBlockAlign); TRC(ALV, "BitsPerSample - %d\n", pInterrimFmt->wBitsPerSample); goto exitpt; } if ( 1 == pInterrimFmt->nChannels ) { switch ( pInterrimFmt->nSamplesPerSec ) { case 8000: case 11025: case 12000: case 16000: case 22050: break; default: ASSERT( 0 ); } } else { switch ( pInterrimFmt->nSamplesPerSec ) { case 8000: case 11025: case 12000: case 16000: case 22050: break; default: ASSERT( 0 ); } } // // probe with this format // mmres = acmStreamOpen( &hacmStream, hacmDriver, pInterrimFmt, pDestFormat, NULL, // filter 0, // callback 0, // dwinstance ACM_STREAMOPENF_NONREALTIME ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ALV, "_VCSndFindSuggestedConverter: probing the suggested " "format failed: %d\n", mmres); goto exitpt; } TRC(ALV, "_VCSndFindSuggestedConverter: found intermidiate PCM format\n"); TRC(ALV, "Channels - %d\n", pInterrimFmt->nChannels); TRC(ALV, "SamplesPerSec - %d\n", pInterrimFmt->nSamplesPerSec); TRC(ALV, "AvgBytesPerSec - %d\n", pInterrimFmt->nAvgBytesPerSec); TRC(ALV, "BlockAlign - %d\n", pInterrimFmt->nBlockAlign); TRC(ALV, "BitsPerSample - %d\n", pInterrimFmt->wBitsPerSample); rv = TRUE; exitpt: if ( NULL != hacmStream ) acmStreamClose( hacmStream, 0 ); if ( NULL != hacmDriver ) acmDriverClose( hacmDriver, 0 ); return rv; } /* * Function: * _VCSndOrderFormatList * * Description: * Order all formats in descendant order * */ VOID _VCSndOrderFormatList( PVCSNDFORMATLIST *ppFormatList, DWORD *pdwNum ) { PVCSNDFORMATLIST pFormatList; PVCSNDFORMATLIST pLessThan; PVCSNDFORMATLIST pPrev; PVCSNDFORMATLIST pNext; PVCSNDFORMATLIST pIter; PVCSNDFORMATLIST pIter2; DWORD dwNum = 0; ASSERT ( NULL != ppFormatList ); pFormatList = *ppFormatList; pLessThan = NULL; // // fill both lists // pIter = pFormatList; while ( NULL != pIter ) { pNext = pIter->pNext; pIter->pNext = NULL; // // descending order // pIter2 = pLessThan; pPrev = NULL; while ( NULL != pIter2 && pIter2->Format.nAvgBytesPerSec > pIter->Format.nAvgBytesPerSec ) { pPrev = pIter2; pIter2 = pIter2->pNext; } pIter->pNext = pIter2; if ( NULL == pPrev ) pLessThan = pIter; else pPrev->pNext = pIter; pIter = pNext; dwNum ++; } *ppFormatList = pLessThan; if ( NULL != pdwNum ) *pdwNum = dwNum; } // // puts code licensing codes into the header // BOOL _VCSndFixHeader( PWAVEFORMATEX pFmt, PWAVEFORMATEX *ppNewFmt ) { BOOL rv = FALSE; *ppNewFmt = NULL; switch (pFmt->wFormatTag) { case WAVE_FORMAT_MSG723: ASSERT(pFmt->cbSize == 10); ((MSG723WAVEFORMAT *) pFmt)->dwCodeword1 = G723MAGICWORD1; ((MSG723WAVEFORMAT *) pFmt)->dwCodeword2 = G723MAGICWORD2; rv = TRUE; break; case WAVE_FORMAT_MSRT24: // // assume call control will take care of the other // params ? // ASSERT(pFmt->cbSize == 80); strncpy(((VOXACM_WAVEFORMATEX *) pFmt)->szKey, VOXWARE_KEY, 80); rv = TRUE; break; case WAVE_FORMAT_WMAUDIO2: if ( ((WMAUDIO2WAVEFORMAT *)pFmt)->dwSamplesPerBlock > TSSND_SAMPLESPERBLOCK ) { // // block is too big, too high latency // break; } ASSERT( pFmt->cbSize == sizeof( WMAUDIO2WAVEFORMAT ) - sizeof( WAVEFORMATEX )); *ppNewFmt = TSMALLOC( sizeof( WMAUDIO2WAVEFORMAT ) + sizeof( WMAUDIO_KEY )); if ( NULL == *ppNewFmt ) { break; } memcpy( *ppNewFmt, pFmt, sizeof( WMAUDIO2WAVEFORMAT )); strncpy((CHAR *)(((WMAUDIO2WAVEFORMAT *) *ppNewFmt) + 1), WMAUDIO_KEY, sizeof( WMAUDIO_KEY )); (*ppNewFmt)->cbSize += sizeof( WMAUDIO_KEY ); rv = TRUE; break; default: rv = TRUE; } return rv; } /* * Function: * acmFormatEnumCallback * * Description: * All formats enumerator * */ BOOL CALLBACK acmFormatEnumCallback( HACMDRIVERID hadid, LPACMFORMATDETAILS pAcmFormatDetails, DWORD_PTR dwInstance, DWORD fdwSupport ) { PVCSNDFORMATLIST *ppFormatList; PWAVEFORMATEX pEntry, pFixedEntry = NULL; ASSERT(0 != dwInstance); ASSERT(NULL != pAcmFormatDetails); ASSERT(NULL != pAcmFormatDetails->pwfx); if ( 0 == dwInstance || NULL == pAcmFormatDetails || NULL == pAcmFormatDetails->pwfx ) { TRC( ERR, "acmFormatEnumCallback: Invalid parameters\n" ); goto exitpt; } ppFormatList = (PVCSNDFORMATLIST *)dwInstance; if (( 0 != ( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC ) || 0 != ( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CONVERTER )) && pAcmFormatDetails->pwfx->nAvgBytesPerSec < TSSND_NATIVE_AVGBYTESPERSEC ) { // // this codec should be good, save it in the list // keep the list sorted in descended order // PVCSNDFORMATLIST pIter; PVCSNDFORMATLIST pPrev; PVCSNDFORMATLIST pNewEntry; WAVEFORMATEX WaveFormat; // dummy parameter DWORD itemsize; if ( WAVE_FORMAT_PCM == pAcmFormatDetails->pwfx->wFormatTag || !_VCSndFixHeader(pAcmFormatDetails->pwfx, &pFixedEntry ) ) { TRC(ALV, "acmFormatEnumCallback: unsupported format, " "don't use it\n"); goto exitpt; } pEntry = ( NULL == pFixedEntry )?pAcmFormatDetails->pwfx:pFixedEntry; if (!_VCSndFindSuggestedConverter( hadid, pEntry, &WaveFormat )) { TRC(ALV, "acmFormatEnumCallback: unsupported format, " "don't use it\n"); goto exitpt; } TRC(ALV, "acmFormatEnumCallback: codec found %S (%d b/s)\n", pAcmFormatDetails->szFormat, pEntry->nAvgBytesPerSec); itemsize = sizeof( *pNewEntry ) + pEntry->cbSize; pNewEntry = (PVCSNDFORMATLIST) TSMALLOC( itemsize ); if (NULL == pNewEntry) { TRC(ERR, "acmFormatEnumCallback: can't allocate %d bytes\n", itemsize); goto exitpt; } memcpy( &pNewEntry->Format, pEntry, sizeof (pNewEntry->Format) + pEntry->cbSize ); pNewEntry->hacmDriverId = hadid; pNewEntry->pNext = *ppFormatList; *ppFormatList = pNewEntry; } exitpt: if ( NULL != pFixedEntry ) { TSFREE( pFixedEntry ); } return TRUE; } // // returns true if this codec is shipped with windows // because we are testing only the these // BOOL AllowThisCodec( HACMDRIVERID hadid ) { ACMDRIVERDETAILS Details; BOOL rv = FALSE; static DWORD AllowedCodecs[][2] = { MM_INTEL, 503, MM_MICROSOFT, MM_MSFT_ACM_IMAADPCM, MM_FRAUNHOFER_IIS, 12, MM_MICROSOFT, 90, MM_MICROSOFT, MM_MSFT_ACM_MSADPCM, MM_MICROSOFT, 39, MM_MICROSOFT, MM_MSFT_ACM_G711, MM_MICROSOFT, 82, MM_MICROSOFT, MM_MSFT_ACM_GSM610, MM_SIPROLAB, 1, MM_DSP_GROUP, 1, MM_MICROSOFT, MM_MSFT_ACM_PCM }; RtlZeroMemory( &Details, sizeof( Details )); Details.cbStruct = sizeof( Details ); if ( MMSYSERR_NOERROR == acmDriverDetails( hadid, &Details, 0 )) { // // Is this one known // DWORD count; for ( count = 0; count < sizeof( AllowedCodecs ) / (2 * sizeof( DWORD )); count ++ ) { if ( Details.wMid == AllowedCodecs[count][0] && Details.wPid == AllowedCodecs[count][1] ) { rv = TRUE; goto exitpt; } } } exitpt: if ( rv ) TRC( ALV, "ACMDRV: +++++++++++++++++++++ CODEC ALLOWED +++++++++++++++++++++++\n" ); else TRC( ALV, "ACMDRV: ------------------- CODEC DISALLOWED ----------------------\n" ); TRC( ALV, "ACMDRV: Mid: %d\n", Details.wMid ); TRC( ALV, "ACMDRV: Pid: %d\n", Details.wPid ); TRC( ALV, "ACMDRV: ShortName: %S\n", Details.szShortName ); TRC( ALV, "ACMDRV: LongName: %S\n", Details.szLongName ); TRC( ALV, "ACMDRV: Copyright: %S\n", Details.szLicensing ); TRC( ALV, "ACMDRV: Features: %S\n", Details.szFeatures ); return rv; } /* * Function: * acmDriverEnumCallback * * Description: * All drivers enumerator * */ BOOL CALLBACK acmDriverEnumCallback( HACMDRIVERID hadid, DWORD_PTR dwInstance, DWORD fdwSupport ) { PVCSNDFORMATLIST *ppFormatList; MMRESULT mmres; ASSERT(dwInstance); ppFormatList = (PVCSNDFORMATLIST *)dwInstance; if ( (0 != ( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC ) || 0 != ( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CONVERTER )) && AllowThisCodec(hadid) ) { // // a codec found // HACMDRIVER had; mmres = acmDriverOpen(&had, hadid, 0); if (MMSYSERR_NOERROR == mmres) { PWAVEFORMATEX pWaveFormat; ACMFORMATDETAILS AcmFormatDetails; DWORD dwMaxFormatSize; // // first find the max size for the format // mmres = acmMetrics( (HACMOBJ)had, ACM_METRIC_MAX_SIZE_FORMAT, (LPVOID)&dwMaxFormatSize); if (MMSYSERR_NOERROR != mmres || dwMaxFormatSize < sizeof( *pWaveFormat )) dwMaxFormatSize = sizeof( *pWaveFormat ); // // Allocate the format structure // __try { pWaveFormat = (PWAVEFORMATEX) _alloca ( dwMaxFormatSize ); } __except ( EXCEPTION_EXECUTE_HANDLER ) { pWaveFormat = NULL; } if ( NULL == pWaveFormat ) { TRC(ERR, "acmDriverEnumCallback: alloca failed for %d bytes\n", dwMaxFormatSize); goto close_acm_driver; } // // clear the extra format data // memset( pWaveFormat + 1, 0, dwMaxFormatSize - sizeof( *pWaveFormat )); // // create the format to convert from // pWaveFormat->wFormatTag = WAVE_FORMAT_PCM; pWaveFormat->nChannels = TSSND_NATIVE_CHANNELS; pWaveFormat->nSamplesPerSec = TSSND_NATIVE_SAMPLERATE; pWaveFormat->nAvgBytesPerSec = TSSND_NATIVE_AVGBYTESPERSEC; pWaveFormat->nBlockAlign = TSSND_NATIVE_BLOCKALIGN; pWaveFormat->wBitsPerSample = TSSND_NATIVE_BITSPERSAMPLE; pWaveFormat->cbSize = 0; AcmFormatDetails.cbStruct = sizeof( AcmFormatDetails ); AcmFormatDetails.dwFormatIndex= 0; AcmFormatDetails.dwFormatTag = WAVE_FORMAT_PCM; AcmFormatDetails.fdwSupport = 0; AcmFormatDetails.pwfx = pWaveFormat; AcmFormatDetails.cbwfx = dwMaxFormatSize; // // enum all formats supported by this driver // mmres = acmFormatEnum( had, &AcmFormatDetails, acmFormatEnumCallback, (DWORD_PTR)ppFormatList, 0 //ACM_FORMATENUMF_CONVERT ); if (MMSYSERR_NOERROR != mmres) { TRC(ERR, "acmDriverEnumCallback: acmFormatEnum failed %d\n", mmres); } close_acm_driver: acmDriverClose(had, 0); } else TRC(ALV, "acmDriverEnumCallback: acmDriverOpen failed: %d\n", mmres); } // // continue to the next driver // return TRUE; } /* * Function: * VCSndEnumAllCodecFormats * * Description: * Creates a list of all codecs/formats * */ BOOL VCSndEnumAllCodecFormats( PVCSNDFORMATLIST *ppFormatList, DWORD *pdwNumberOfFormats ) { BOOL rv = FALSE; PVCSNDFORMATLIST pIter; PVCSNDFORMATLIST pPrev; PVCSNDFORMATLIST pNext; MMRESULT mmres; DWORD dwNum = 0; ASSERT( ppFormatList ); ASSERT( pdwNumberOfFormats ); *ppFormatList = NULL; mmres = acmDriverEnum( acmDriverEnumCallback, (DWORD_PTR)ppFormatList, 0 ); if (NULL == *ppFormatList) { TRC(WRN, "VCSndEnumAllCodecFormats: acmDriverEnum failed: %d\n", mmres); goto exitpt; } _VCSndOrderFormatList( ppFormatList, &dwNum ); pIter = *ppFormatList; // // number of formats is passed as UINT16, delete all after those // if ( dwNum > 0xffff ) { DWORD dwLimit = 0xfffe; while ( 0 != dwLimit ) { pIter = pIter->pNext; dwLimit --; } pNext = pIter->pNext; pIter->pNext = NULL; pIter = pNext; while( NULL != pIter ) { pNext = pIter->pNext; TSFREE( pNext ); pIter = pNext; } dwNum = 0xffff; } rv = TRUE; exitpt: if (!rv) { // // in case of error free the allocated list of formats // pIter = *ppFormatList; while( NULL != pIter ) { PVCSNDFORMATLIST pNext = pIter->pNext; TSFREE( pIter ); pIter = pNext; } *ppFormatList = NULL; } *pdwNumberOfFormats = dwNum; return rv; } int _cdecl wmain( void ) { PVCSNDFORMATLIST pFormatList = NULL; DWORD dwNumberOfFormats = 0; printf( "// use dumpcod.c to generate this table\n" ); printf( "//\n" ); printf( "// FormatTag | Channels | SamplesPerSec | AvgBytesPerSec | BlockAlign | BitsPerSamepl | ExtraInfo\n" ); printf( "// ================================================================================================\n" ); printf( "//\n" ); printf( "BYTE KnownFormats[] = {\n" ); VCSndEnumAllCodecFormats( &pFormatList, &dwNumberOfFormats ); for ( ;pFormatList != NULL; pFormatList = pFormatList->pNext ) { PWAVEFORMATEX pSndFmt = &(pFormatList->Format); UINT i; printf( "// %.3d, %.2d, %.5d, %.5d, %.3d, %.2d\n", pSndFmt->wFormatTag, pSndFmt->nChannels, pSndFmt->nSamplesPerSec, pSndFmt->nAvgBytesPerSec, pSndFmt->nBlockAlign, pSndFmt->wBitsPerSample); for ( i = 0; i < sizeof( WAVEFORMATEX ); i ++ ) { printf( "0x%02x", ((PBYTE)pSndFmt)[i]); if ( i + 1 < sizeof( WAVEFORMATEX ) || pSndFmt->cbSize ) { printf( ", " ); } } for ( i = 0; i < pSndFmt->cbSize; i++ ) { printf( "0x%02x", (((PBYTE)pSndFmt) + sizeof( WAVEFORMATEX ))[i]); if ( i + 1 < pSndFmt->cbSize ) { printf( ", " ); } } if ( NULL != pFormatList->pNext ) { printf ( ",\n" ); } else { printf( " };\n" ); } } return 0; }