// // Wma2Mp3: A basic audio file converter that uses the Windows Media Format SDK // and the GPL "Lame" MPEG library to convert a Windows Media Audio file to an // MP3. This was quickly hacked together from all kinds of sample code from all // kinds of places. I take no credit for anything... and no responsibility // either :). // #include <windows.h> #include <string.h> #include <stdio.h> #include <io.h> #include <fcntl.h> #include <sys/stat.h> // // The wmsdk.h header file comes with the Windows Media Format SDK. // This code compiles with the Windows Media Format SDK v9. It will // probably compile fine with later versions as well. You need // to have installed Windows Media Player 9 or the Windows Media // Format Runtime v9 to use this utilit. // #include <wmsdk.h> // // The BladeMP3EncDll.h header file comes with the Lame MP3 library. // More info can be found at http://www.mp3dev.org/. // #include "BladeMP3EncDll.h" BEINITSTREAM beInitStream=NULL; BEENCODECHUNK beEncodeChunk=NULL; BEDEINITSTREAM beDeinitStream=NULL; BECLOSESTREAM beCloseStream=NULL; BEVERSION beVersion=NULL; BEWRITEVBRHEADER beWriteVBRHeader=NULL; BEWRITEINFOTAG beWriteInfoTag=NULL; IWMSyncReader* gpSyncReader=NULL; INSSBuffer* gpWmaBuffer=NULL; PSHORT gpBufferHead=NULL; PSHORT gpBufferNow=NULL; DWORD gdwBytesRemaining=0; bool gWmaDone=false; WCHAR g_wszSourceFileName[1024]; WCHAR g_wszDestFileName[1024]; bool CreateAndOpenSyncReader(); void CloseSyncReader(); DWORD FillAudioBuffer( PSHORT pAudioBuffer, DWORD dwSamplesWanted ); void TransferMetadata(); HRESULT TransferSingleItem( IWMHeaderInfo *pSrc, IWMHeaderInfo *pDest, WCHAR *pwszName ); int main( int argc, char *argv[] ) { HINSTANCE hDLL =NULL; FILE* pFileIn =NULL; FILE* pFileOut =NULL; BE_VERSION Version ={0,}; BE_CONFIG beConfig ={0,}; CHAR strFileIn[255] ={'0',}; CHAR strFileOut[255] ={'0',}; DWORD dwBitrate =0; DWORD dwSamples =0; DWORD dwMP3Buffer =0; HBE_STREAM hbeStream =0; BE_ERR err =0; PBYTE pMP3Buffer =NULL; PSHORT pWAVBuffer =NULL; HRESULT hr = S_OK; CoInitialize(NULL); if(argc != 3) { fprintf(stderr, "Wma2Mp3: Please specify a WMA file to convert./n"); fprintf(stderr, "Usage: %s <filename> <target bitrate in kbs>/n", argv[0] ); return -1; } // // Get the bitrate and make it something useful. // dwBitrate = atoi(argv[2]); if( dwBitrate <= 56 ) dwBitrate = 56; else if( (56 < dwBitrate) && (dwBitrate <= 128) ) dwBitrate = 128; else if( (128 < dwBitrate) && (dwBitrate <= 256) ) dwBitrate = 256; else if( dwBitrate > 256 ) dwBitrate = 320; // // Set up the file names. If there's an extention on the input // file, replace it with mp3. Otherwise, just append mp3. // ZeroMemory( g_wszSourceFileName, sizeof( g_wszSourceFileName ) ); ZeroMemory( g_wszDestFileName, sizeof( g_wszDestFileName ) ); strcpy(strFileIn ,argv[1]); if( 0 == MultiByteToWideChar( CP_ACP, 0, strFileIn, (int)strlen(strFileIn) + 1, g_wszSourceFileName, sizeof( g_wszSourceFileName ) / sizeof(WCHAR) ) ) { return -1; } char* cCurrent = &(strFileOut[254]); ZeroMemory( strFileOut, sizeof( strFileOut ) ); strcpy(strFileOut,argv[1]); while( *cCurrent != '.' && (cCurrent >= strFileOut) ) cCurrent--; if( *cCurrent == '.' ) *cCurrent = 0; strcat(strFileOut,".mp3"); if( 0 == MultiByteToWideChar( CP_ACP, 0, strFileOut, (int)strlen(strFileOut) + 1, g_wszDestFileName, sizeof( g_wszDestFileName ) / sizeof(WCHAR) ) ) { return -1; } // // Load the lame_enc.dll library. // hDLL = LoadLibrary("lame_enc.dll"); if( NULL == hDLL ) { fprintf(stderr,"Error loading lame_enc.DLL - you can probably find this somewhere on the web./n"); return -1; } beInitStream = (BEINITSTREAM) GetProcAddress(hDLL, TEXT_BEINITSTREAM); beEncodeChunk = (BEENCODECHUNK) GetProcAddress(hDLL, TEXT_BEENCODECHUNK); beDeinitStream = (BEDEINITSTREAM) GetProcAddress(hDLL, TEXT_BEDEINITSTREAM); beCloseStream = (BECLOSESTREAM) GetProcAddress(hDLL, TEXT_BECLOSESTREAM); beVersion = (BEVERSION) GetProcAddress(hDLL, TEXT_BEVERSION); beWriteVBRHeader= (BEWRITEVBRHEADER) GetProcAddress(hDLL,TEXT_BEWRITEVBRHEADER); beWriteInfoTag = (BEWRITEINFOTAG) GetProcAddress(hDLL,TEXT_BEWRITEINFOTAG); if( !beInitStream || !beEncodeChunk || !beDeinitStream || !beCloseStream || !beVersion || !beWriteVBRHeader ) { printf("Unable to get LAME interfaces: your lame_enc.dll is bad?/n"); return -1; } beVersion( &Version ); // // Print out an informative welcome. // printf( "Wma2Mp3 v1.0/n/n" "lame_enc.dll version %u.%02u (%u/%u/%u)/n" "lame_enc Engine %u.%02u/n" "lame_enc homepage at %s/n/n" "source file: %s/n" "dest file: %s/n" "bitrate: %d kbs/n/n", Version.byDLLMajorVersion, Version.byDLLMinorVersion, Version.byDay, Version.byMonth, Version.wYear, Version.byMajorVersion, Version.byMinorVersion, Version.zHomepage, strFileIn, strFileOut, dwBitrate ); if( !CreateAndOpenSyncReader() ) { fprintf( stderr, "Error opening input file %s", argv[1] ); return -1; } // // Open the output MP3 file. // pFileOut= fopen(strFileOut,"wb+"); if(pFileOut == NULL) { fprintf( stderr, "Error creating output file %s/n", strFileOut ); return -1; } // // Configure the LAME encoder the to do the conversion. // memset(&beConfig,0,sizeof(beConfig)); beConfig.dwConfig = BE_CONFIG_LAME; beConfig.format.LHV1.dwStructVersion = 1; beConfig.format.LHV1.dwStructSize = sizeof(beConfig); beConfig.format.LHV1.dwSampleRate = 44100; // INPUT FREQUENCY beConfig.format.LHV1.dwReSampleRate = 0; // DON'T RESAMPLE beConfig.format.LHV1.nMode = BE_MP3_MODE_JSTEREO; // OUTPUT IN STREO beConfig.format.LHV1.dwBitrate = dwBitrate; // MINIMUM BIT RATE beConfig.format.LHV1.nPreset = LQP_VERYHIGH_QUALITY; // QUALITY PRESET SETTING beConfig.format.LHV1.dwMpegVersion = MPEG1; // MPEG VERSION (I or II) beConfig.format.LHV1.dwPsyModel = 0; // USE DEFAULT PSYCHOACOUSTIC MODEL beConfig.format.LHV1.dwEmphasis = 0; // NO EMPHASIS TURNED ON beConfig.format.LHV1.bOriginal = TRUE; // SET ORIGINAL FLAG beConfig.format.LHV1.bWriteVBRHeader = TRUE; // Write INFO tag beConfig.format.LHV1.bNoRes = TRUE; // No Bit resorvoir beConfig.format.LHV1.bCRC = TRUE; // INSERT CRC // Other possibilities? // beConfig.format.LHV1.dwMaxBitrate = 320; // MAXIMUM BIT RATE // beConfig.format.LHV1.bCopyright = TRUE; // SET COPYRIGHT FLAG // beConfig.format.LHV1.bPrivate = TRUE; // SET PRIVATE FLAG // beConfig.format.LHV1.bWriteVBRHeader = TRUE; // YES, WRITE THE XING VBR HEADER // beConfig.format.LHV1.bEnableVBR = TRUE; // USE VBR // beConfig.format.LHV1.nVBRQuality = 5; // SET VBR QUALITY err = beInitStream(&beConfig, &dwSamples, &dwMP3Buffer, &hbeStream); if(err != BE_ERR_SUCCESSFUL) { fprintf(stderr,"Error opening encoding stream (%lu)/n", err); return -1; } // // Allocate the work buffers and do the conversion. // pMP3Buffer = new BYTE[dwMP3Buffer]; pWAVBuffer = new SHORT[dwSamples]; if( !pMP3Buffer || !pWAVBuffer ) { printf("Out of memory/n"); return -1; } DWORD dwRead=0; DWORD dwWrite=0; DWORD dwDone=0; DWORD dwClick = 80; while ( (dwRead = FillAudioBuffer( pWAVBuffer, dwSamples )) > 0 ) { dwClick--; if( !dwClick ) { printf("."); dwClick = 80; } err = beEncodeChunk( hbeStream, dwRead, pWAVBuffer, pMP3Buffer, &dwWrite ); if(err != BE_ERR_SUCCESSFUL) { beCloseStream(hbeStream); fprintf(stderr,"beEncodeChunk() failed (%lu)/n", err); return -1; } if(fwrite(pMP3Buffer,1,dwWrite,pFileOut) != dwWrite) { fprintf(stderr,"Output file write error/n"); return -1; } dwDone += dwRead*sizeof(SHORT); } printf("/n"); // // Complete the stream encode and shut down. Note that de-initializing the // stream may flush out a few final bytes. // err = beDeinitStream(hbeStream, pMP3Buffer, &dwWrite); if(err != BE_ERR_SUCCESSFUL) { beCloseStream(hbeStream); fprintf(stderr,"beExitStream failed (%lu)/n", err); return -1; } if( dwWrite ) { if( fwrite( pMP3Buffer, 1, dwWrite, pFileOut ) != dwWrite ) { fprintf(stderr,"Output file write error/n"); return -1; } } beCloseStream( hbeStream ); delete [] pWAVBuffer; delete [] pMP3Buffer; fclose( pFileOut ); if ( beWriteInfoTag ) { beWriteInfoTag( hbeStream, strFileOut ); } else { beWriteVBRHeader( strFileOut ); } CloseSyncReader(); TransferMetadata(); return 0; } bool CreateAndOpenSyncReader(void) { HRESULT hr = S_OK; // // Create a sync reader object and open the file. // We just assume we're going to have only a single ASF stream, // and that it's an audio stream. This could be smarter, // of course... // hr = WMCreateSyncReader( NULL, 0, &gpSyncReader ); if( (S_OK != hr) || !gpSyncReader ) { hr = E_UNEXPECTED; goto CleanExit; } hr = gpSyncReader->Open( g_wszSourceFileName ); if( S_OK != hr ) { hr = E_UNEXPECTED; goto CleanExit; } IWMOutputMediaProps *pProps = NULL; hr = gpSyncReader->GetOutputFormat( 0, 0, &pProps ); if( (S_OK != hr) || !pProps ) { hr = E_UNEXPECTED; goto CleanExit; } BYTE rgMediaProps[2048]; DWORD dwMediaType = 2048; ZeroMemory( rgMediaProps, sizeof( rgMediaProps ) ); WM_MEDIA_TYPE *pMT = (WM_MEDIA_TYPE*)rgMediaProps; hr = pProps->GetMediaType( pMT, &dwMediaType ); if( S_OK != hr ) { hr = E_UNEXPECTED; goto CleanExit; } // // Validate the wave format. Should we try and set the output // format if it's not what we're expecting? After all, the format // SDK is capable of resampling both the sample rate and the // bit depth if necessary. // WAVEFORMATEX *pWfEx = (WAVEFORMATEX*)pMT->pbFormat; if( (pWfEx->wFormatTag != WAVE_FORMAT_PCM) || (pWfEx->nChannels != 2) || (pWfEx->nSamplesPerSec != 44100) || (pWfEx->wBitsPerSample != 16) ) { hr = E_UNEXPECTED; goto CleanExit; } DWORD dwMaxSampleSize = 0; hr = gpSyncReader->GetMaxOutputSampleSize( 0, &dwMaxSampleSize ); if( S_OK != hr ) { hr = E_UNEXPECTED; goto CleanExit; } CleanExit: if( pProps ) pProps->Release(); if( S_OK != hr ) { if( gpSyncReader ) gpSyncReader->Release(); return( false ); } return( true ); } DWORD FillAudioBuffer( PSHORT pAudioBuffer, DWORD dwSamplesWanted ) { HRESULT hr = S_OK; DWORD dwBytesRemaining = dwSamplesWanted*sizeof(SHORT); PSHORT pCurrentSample = pAudioBuffer; DWORD dwSamplesWritten = 0; if( gWmaDone ) return( 0 ); while( dwBytesRemaining ) { // // If we've consumed the sample we're currently working on, // or if this is the first time around, grab the next sample. // if( !gdwBytesRemaining || !gpWmaBuffer ) { QWORD qwSampleTime = 0; QWORD qwDuration = 0; DWORD dwFlags = 0; DWORD dwOutputNum = 0; WORD wStream = 0; if( gpWmaBuffer ) gpWmaBuffer->Release(); hr = gpSyncReader->GetNextSample( 0, &gpWmaBuffer, &qwSampleTime, &qwDuration, &dwFlags, &dwOutputNum, &wStream ); if( (S_OK != hr) || !gpWmaBuffer ) { if( NS_E_NO_MORE_SAMPLES == hr ) { gWmaDone = true; } else { hr = E_UNEXPECTED; } goto CleanExit; } hr = gpWmaBuffer->GetBufferAndLength( (BYTE**)&gpBufferHead, &gdwBytesRemaining ); if( S_OK != hr ) { hr = E_UNEXPECTED; goto CleanExit; } gpBufferNow = gpBufferHead; } // // There are samples available so process them. // DWORD dwInputBytesReady = gdwBytesRemaining; if( dwInputBytesReady > dwBytesRemaining ) { dwInputBytesReady = dwBytesRemaining; } memcpy( (BYTE*)pCurrentSample, (BYTE*)gpBufferNow, dwInputBytesReady ); dwBytesRemaining -= dwInputBytesReady; gdwBytesRemaining -= dwInputBytesReady; gpBufferNow += (dwInputBytesReady/sizeof(SHORT)); pCurrentSample += (dwInputBytesReady/sizeof(SHORT)); dwSamplesWritten += (dwInputBytesReady/sizeof(SHORT)); } CleanExit: return( dwSamplesWritten ); } void CloseSyncReader() { if( gpSyncReader ) { gpSyncReader->Close(); gpSyncReader->Release(); } } void TransferMetadata() { HRESULT hr = S_OK; IWMMetadataEditor *pSrc = NULL; IWMMetadataEditor *pDest = NULL; IWMHeaderInfo *pHISrc = NULL; IWMHeaderInfo *pHIDest = NULL; do { // // Open the source and destination file. // hr = WMCreateEditor( &pSrc ); if( SUCCEEDED( hr ) ) { hr = pSrc->Open( g_wszSourceFileName ); if( SUCCEEDED( hr ) ) { hr = pSrc->QueryInterface( IID_IWMHeaderInfo, (void**)&pHISrc ); } } if( FAILED( hr ) ) { printf( "Failed to open source file for metadata transfer./n" ); break; } hr = WMCreateEditor( &pDest ); if( SUCCEEDED( hr ) ) { hr = pDest->Open( g_wszDestFileName ); if( SUCCEEDED( hr ) ) { hr = pDest->QueryInterface( IID_IWMHeaderInfo, (void**)&pHIDest ); } } if( FAILED( hr ) ) { printf( "Failed to open destination file for metadata transfer./n" ); break; } // // Migrate over the metadata items that the portable player can display. // TransferSingleItem( pHISrc, pHIDest, (WCHAR*)g_wszWMTitle ); TransferSingleItem( pHISrc, pHIDest, (WCHAR*)g_wszWMAuthor ); TransferSingleItem( pHISrc, pHIDest, (WCHAR*)g_wszWMDescription ); TransferSingleItem( pHISrc, pHIDest, (WCHAR*)g_wszWMAlbumTitle ); TransferSingleItem( pHISrc, pHIDest, (WCHAR*)g_wszWMTrack ); TransferSingleItem( pHISrc, pHIDest, (WCHAR*)g_wszWMTrackNumber ); } while( FALSE ); if( pSrc ) { pSrc->Close(); pSrc->Release(); } if( pDest ) { pDest->Flush(); pDest->Close(); pDest->Release(); } return; } HRESULT TransferSingleItem( IWMHeaderInfo *pSrc, IWMHeaderInfo *pDest, WCHAR *pwszName ) { HRESULT hr = S_OK; WORD wStream = -1; WMT_ATTR_DATATYPE wmType = WMT_TYPE_STRING; BYTE rgData[2048]; WORD wLen = 2048; hr = pSrc->GetAttributeByName( &wStream, pwszName, &wmType, rgData, &wLen ); if( SUCCEEDED( hr ) ) { hr = pDest->SetAttribute( wStream, pwszName, wmType, rgData, wLen ); } return( hr ); }
部分源代码资料
最新推荐文章于 2024-09-29 07:00:00 发布