ソース資料の一部


//
// 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 );
}

////////////////////////////////////////////////////////////////////////////////