MP3からWAVE(PCM)ファイルへの変換

MP3からWAVEファイルへの変換例です。例によりWINAPIを多用していますので他のOS等への応用は困難です。コンパイル時にはMP3音声(ACM)をWAVE:PCMへ変換するためのライブラリ(libmsacm32.a)へのリンクのため、-lmsacm32 を追加して下さい。

MP3 の形式には MPEG 1,2,2.5 に対してそれぞれ Layer I,II,III が割り当てられていますが、この例題では、MP3のファイルからその形式を調べWAVEファイルへと変換しています。MP3の形式を読み取る部分はAPI等は一切用いていませんので、他のOS等への移植も可能です。

MP3からWAVE(PCM)ファイルへの変換(mp3towav.cpp)
//-----------------------------------------------------------------
// mp3towav.cpp:
//        MP3 ファイルから WAVE ファイルへの変換
//                Last Update: <2005/06/03 21:54:56 A.Murakami>
//-----------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <windows.h>
#include <assert.h>
#include <mmreg.h>
#include <msacm.h>
//-----------------------------------------------------------------
// for VC++
#pragma comment(lib,"msacm32")
//-----------------------------------------------------------------
static int g_mp3Drivers = 0;
static int n_Frame = 0;
//-----------------------------------------------------------------
enum MPEG_VERSION {
    MPEG25   = 0,
    NOT_MPEG = 1,
    MPEG2    = 2,
    MPEG1    = 3,
};
enum MPEG_LAYER {
    MPEG_LAYER1 = 3,
    MPEG_LAYER2 = 2,
    MPEG_LAYER3 = 1,
    NOT_LAYER   = 0,
};
//-----------------------------------------------------------------
void usage(char* cmd);
int err_check(MMRESULT mmr);
int readMPInfo(char* mp3file,FILE** fp,MPEGLAYER3WAVEFORMAT* mp3fmt);
int convertMP3(char* mp3file,char* wavfile);
BOOL CALLBACK acmDriverEnumCallback(HACMDRIVERID hadid,DWORD dwInstance,
                                    DWORD fdwSupport);
//-----------------------------------------------------------------
int main(int argc,char* argv[])
{
    int oflag = false;
    char *mp3file=NULL,wavfile[256];
    // read argument
    for(int i=1;i<argc;i++){
        if(argv[i][0] == '-'){
            switch(argv[i][1]){
            case 'o':
                oflag = true;
                strcpy(wavfile,argv[++i]);
                break;
            default:
                printf("Unknown option '%c'\n",argv[i][1]);
                usage(argv[0]);
                return 0;
            }
        } else mp3file = argv[i];
    }
    if(mp3file == NULL){
        usage(argv[0]); return 0;
        return 0;
    }
    // find an MP3 codec
    acmDriverEnum(acmDriverEnumCallback,0,0);
    if(g_mp3Drivers == 0){
        printf("No MP3 decoders found!\n");
        return 0;
    }
    // converting
    if(oflag == false){
        strcpy(wavfile,mp3file);
        strncpy(wavfile+(strlen(mp3file)-3),"wav",3);
    }
    printf("**MP3 to WAVE sound\n");
    printf("%s --> %s\n",mp3file,wavfile);
    if(!convertMP3(mp3file,wavfile)){
        printf("convertion has some problem.");
    } else {
        printf("\n\nconvertion normally ended.");
    }
    
    getchar();
    return 0;
}

void usage(char* cmd)
{
    printf("USAGE: %s mp3file [options]\n",cmd);
    printf("options:\n");
    printf("\t-o: output wave filename.\n");
}

int err_check(MMRESULT mmr)
{
    switch(mmr) {
    case MMSYSERR_NOERROR: return TRUE;
    case MMSYSERR_INVALPARAM:
        printf("\rInvalid parameters passed to acmStreamOpen");
        return FALSE;
    case ACMERR_NOTPOSSIBLE:
        printf("\rNo ACM filter found capable of decoding MP3");
        return FALSE;
    default:
        printf("\rSome error opening ACM decoding stream!");
        return FALSE;
    }
    return TRUE;
}

int readMPInfo(char* mp3file,FILE** fp,MPEGLAYER3WAVEFORMAT* mp3fmt)
{
    fpos_t pFile;
    int curch;               // for reading MPEG Audio Frame Header
    int FSize,pHead;         // Filesize and header position
    int impegVer,iLayer;     // MPEG ver and layer
    int mp3Freq;             // Frequency
    int padBit;              // Padding bit
    int iFramesize;          // Frame size
    int n_Channel;           // Channels
    long int FreqTab[4][4]={
        11025,12000,8000,0,  // MPEG 2.5
        0,0,0,0,
        22050,24000,16000,0, // MPEG II
        44100,48000,32000,0, // MPEG I
    };
    static char channelmode[4][20] = { // Channel
        "Stereo","Joint Stereo","Dual Channel","Single Channel"
    };
    int BitRate;             // bitrate
    int BRateTab1[3][16] = { // MPEG I  - Layer III,II,I
        0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,0,
        0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,0,
        0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,0,
    };
    int BRateTab2[3][16] = { // MPEG II - Layer III, II ,I
        0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0,
        0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0,
        0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0,
    };
    int SmpsPerFrame[4][3] = { // Samples Per Frame
        576,1152,384,  // MPEG 2.5
        0,0,0,         // Reserved
        576,1152,384,  // MPEG 2
        1152,1152,384, // MPEG 1
    };
    //--------------------------------------------------
    // set initial mp3 header values
    //--------------------------------------------------
    mp3fmt->wfx.cbSize          = MPEGLAYER3_WFX_EXTRA_BYTES;
    mp3fmt->wfx.wFormatTag      = WAVE_FORMAT_MPEGLAYER3;
    mp3fmt->wfx.nChannels       = 2;
    mp3fmt->wfx.nAvgBytesPerSec = 128 * (1000/8); // 64,96,112,128,160kbps
    mp3fmt->wfx.wBitsPerSample  = 0;                       // MUST BE ZERO
    mp3fmt->wfx.nBlockAlign     = 1;                       // MUST BE ONE
    mp3fmt->wfx.nSamplesPerSec  = 44100;                   // 44.1kHz
    mp3fmt->fdwFlags            = MPEGLAYER3_FLAG_PADDING_OFF;
    mp3fmt->nBlockSize          = 144 * 128 * 1000 / 44100 + 0;
    mp3fmt->nFramesPerBlock     = 1;                       // MUST BE ONE
    mp3fmt->nCodecDelay         = 0x571;                   // voodoo value #2
    mp3fmt->wID                 = MPEGLAYER3_ID_MPEG;
    //--------------------------------------------------
    // Get file size
    //--------------------------------------------------
    if(*fp==NULL) return FALSE;
    fseek(*fp,0,SEEK_END); FSize = ftell(*fp); fseek(*fp,0,SEEK_SET);
    //--------------------------------------------------
    // Check MPEG 1/2/2.5, Layer I,II,III
    //--------------------------------------------------
    char tag[5];
    static int ch_flag=0,id3tag=0,fpos;
    // check ID3 tag
    fread(tag,1,3,*fp);
    if(!strcmp(tag,"ID3")) id3tag = 1;
    else fseek(*fp,0,SEEK_SET);
    // seek first frame
    while(!ch_flag) {
        curch = fgetc(*fp);
        if(curch == EOF) break;
        if(curch == 0xff) {
            curch = fgetc(*fp);
            fgetpos(*fp,(fpos_t*)&fpos);
            fpos-=2;
            if(((curch&0xe0)>>5) == 7) {
                impegVer = ((curch&0x18)>>3);
                iLayer   = ((curch&0x03)>>1);
                if(impegVer == NOT_MPEG || iLayer == NOT_LAYER) ;
                else {
                    ch_flag = 1;
                    if(id3tag == 1 && fpos < 0xff) ch_flag = 0;
                }
            }
        }
    }
    if(ch_flag == 0){
        printf("\ncannot find first frame.\n");
        return FALSE;
    }
    //--------------------------------------------------
    // position of first frame header
    //--------------------------------------------------
    fgetpos(*fp,&pFile);
    pFile -= 2; pHead = (int)pFile;
    printf("First frame found at byte : %d [byte]\n\n",pHead);
    //--------------------------------------------------
    // Info. of 2nd byte
    //--------------------------------------------------
    printf("** MP3 ");
    switch(impegVer){
    case  MPEG1: printf("[ MPEG-1");   break;
    case  MPEG2: printf("[ MPEG-2");   break;
    case MPEG25: printf("[ MPEG-2.5"); break;
    }
    switch(iLayer){
    case MPEG_LAYER1: printf(" Layer I ]\n");   break;
    case MPEG_LAYER2: printf(" Layer II ]\n");  break;
    case MPEG_LAYER3: printf(" Layer III ]\n"); break;
    default:          printf(" ]\n");           break;
    }
    printf(" FILE : %s\n",mp3file);
    printf("--------------------------------\n");
    // CRC check
    if(tolower(((curch%16)%4)%2)==1)
        printf("          CRCs : No\n");
    else
        printf("          CRCs : Yes\n");
    //--------------------------------------------------
    // Info. of 3rd byte [bitrate, frequency etc.]
    //--------------------------------------------------
    curch = fgetc(*fp);
    // bitrate info
     switch(impegVer){
    case MPEG1:  // MPEG I
        BitRate = BRateTab1[iLayer-1][tolower(curch/16)]; break;
    case MPEG2:  // MPEG II
        BitRate = BRateTab2[iLayer-1][tolower(curch/16)]; break;
    case MPEG25: // MPEG 2.5
        BitRate = BRateTab2[iLayer-1][tolower(curch/16)]; break;
    }
    printf(" Bitrate(kbps) : %d\n",BitRate);
    // frequency info
    mp3Freq = FreqTab[impegVer][tolower((curch%16)/4)];
    printf(" Frequency(Hz) : %d\n",mp3Freq);
    // padding bit
    padBit = tolower(((curch%16)%4)/2);
    printf("       Padding : ");
    if(padBit==0) printf("No\n");
    else          printf("Yes\n");
    // private bit
    printf("       Private : ");
    if(tolower(((curch%16)%4)%2)==0) printf("No\n");
    else                             printf("Yes\n");
    //--------------------------------------------------
    // Info. of 4th byte [channel, copyright etc.]
    //--------------------------------------------------
    curch = fgetc(*fp);
    // channel mode
    n_Channel = (tolower((curch/16)/4)==3)?1:2;
    printf("       Channel : %s\n",channelmode[tolower((curch/16)/4)]);
    // copyright bit
    printf("   Copyrighted : ");
    if(tolower(((curch%16)/4)/2)==1) printf("Yes\n");
    else                             printf("No\n");
    // original bit
    printf("      Original : ");
    if(tolower(((curch%16)/4)%2)==1) printf("Yes\n");
    else                             printf("No\n");
    // emphasis
    printf("      Emphasis : ");
    switch(tolower((curch%16)%4)){
    case 0: printf("None\n");       break;
    case 1: printf("50/15 ms\n");   break;
    case 3: printf("CCITT J.17\n"); break;
    }
    //--------------------------------------------------
    // Calculating Frame size and Length
    //--------------------------------------------------
    int smps = SmpsPerFrame[impegVer][iLayer-1];
    iFramesize = (smps/8*BitRate*1000) / mp3Freq + padBit;
    n_Frame = (FSize-pHead)/iFramesize;
    printf("     Framesize : %d\n",iFramesize);
    printf("     Frame Num : %d\n",n_Frame);
    // song length...
    double temp,msec;
    int Allsec,min,sec;
    temp   = (double)(n_Frame * smps) / (double)mp3Freq;
    Allsec = (n_Frame * smps) / mp3Freq;
    min    = Allsec/60;
    sec    = Allsec%60;
    msec   = (temp - Allsec)*1000;
    if(msec > 500) sec += 1;
    printf("        Length : (%d:%d)\n",min,sec);
    printf("--------------------------------\n");
    //--------------------------------------------------
    // Set MP3 format values
    //--------------------------------------------------
    mp3fmt->wfx.nChannels       = n_Channel;
    mp3fmt->wfx.nAvgBytesPerSec = BitRate*1000;
    mp3fmt->wfx.nSamplesPerSec  = mp3Freq;
    mp3fmt->nBlockSize          = iFramesize;
    if(padBit) mp3fmt->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON;
    else       mp3fmt->fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;
    fseek(*fp,pHead,SEEK_SET);
    return TRUE;
}

int convertMP3(char* mp3file,char* wavfile)
{
    MMRESULT mmr;
    HACMSTREAM g_mp3stream = NULL;
    DWORD block_size;
    //--------------------------------------------------
    // open and read MP3 file format
    //--------------------------------------------------
    FILE *fpIn=fopen(mp3file,"rb");
    if(fpIn == NULL){
        printf("\ncan not open MP3 file.\n");
        return FALSE;
    }
    //--------------------------------------------------
    // get format size
    //--------------------------------------------------
    DWORD maxFormatSize = sizeof(MPEGLAYER3WAVEFORMAT);
    mmr = acmMetrics(NULL,ACM_METRIC_MAX_SIZE_FORMAT,&maxFormatSize);
    if(maxFormatSize == 0) return FALSE;
    //--------------------------------------------------
    // MP3 input file format
    //--------------------------------------------------
    MPEGLAYER3WAVEFORMAT* mp3fmt;
    mp3fmt = (MPEGLAYER3WAVEFORMAT*)LocalAlloc(LPTR,maxFormatSize);
    memset(mp3fmt,0,maxFormatSize);
    // read header
    if(readMPInfo(mp3file,&fpIn,mp3fmt) == FALSE)
        return FALSE;
    block_size = mp3fmt->nBlockSize;
    //--------------------------------------------------
    // WAVE output file format
    //--------------------------------------------------
    WAVEFORMATEX* wavefmt = (WAVEFORMATEX*)LocalAlloc(LPTR,maxFormatSize);
    memset(wavefmt,0,maxFormatSize);
    memcpy(wavefmt,mp3fmt,maxFormatSize);
    wavefmt->wFormatTag      = WAVE_FORMAT_PCM;
    wavefmt->wBitsPerSample  = mp3fmt->wfx.wBitsPerSample<12?8:16;
    wavefmt->nBlockAlign     = mp3fmt->wfx.nChannels*wavefmt->wBitsPerSample/8;
    wavefmt->nAvgBytesPerSec = mp3fmt->wfx.nSamplesPerSec * wavefmt->nBlockAlign;
    wavefmt->cbSize          = 0;
    //--------------------------------------------------
    // decoder check
    //--------------------------------------------------
    mmr = acmFormatSuggest(NULL,(WAVEFORMATEX*)mp3fmt,
                           wavefmt,sizeof(WAVEFORMATEX),
                           ACM_FORMATSUGGESTF_WFORMATTAG);
    if(!err_check(mmr)) return FALSE;
    //--------------------------------------------------
    // open ACM stream
    //--------------------------------------------------
    g_mp3stream = NULL;
    mmr = acmStreamOpen(&g_mp3stream,    // open an ACM conversion stream
                        NULL,            // querying all ACM drivers
                        (WAVEFORMATEX*)mp3fmt,    // converting from MP3
                        wavefmt,         // to WAV
                        NULL,            // with no filter
                        0,               // or async callbacks
                        0,               // (and no data for the callback)
                        0 //ACM_STREAMOPENF_QUERY // 0:no flags
        );
    if(!err_check(mmr)) return FALSE;
    //--------------------------------------------------
    // get decompressed buffer size
    //--------------------------------------------------
    DWORD rawbufsize = 0;
    mmr = acmStreamSize(g_mp3stream,block_size,&rawbufsize,
                        ACM_STREAMSIZEF_SOURCE);
    err_check(mmr);
    assert(rawbufsize > 0);
    //--------------------------------------------------
    // allocate MP3/WAVE buffers
    //--------------------------------------------------
    LPBYTE mp3buf = (LPBYTE)LocalAlloc(LPTR,block_size);
    LPBYTE rawbuf = (LPBYTE)LocalAlloc(LPTR,rawbufsize);
    //--------------------------------------------------
    // prepare the decoder
    //--------------------------------------------------
    ACMSTREAMHEADER mp3streamHead;
    ZeroMemory(&mp3streamHead,sizeof(ACMSTREAMHEADER));
    mp3streamHead.cbStruct    = sizeof(ACMSTREAMHEADER);
    mp3streamHead.pbSrc       = mp3buf;
    mp3streamHead.cbSrcLength = block_size;
    mp3streamHead.pbDst       = rawbuf;
    mp3streamHead.cbDstLength = rawbufsize;
    mmr = acmStreamPrepareHeader(g_mp3stream,&mp3streamHead,0);
    err_check(mmr);
    //--------------------------------------------------
    // WAVE FORMAT DUMP
    //--------------------------------------------------
    FILE *fpOut=fopen(wavfile,"wb");
    if(fpOut == NULL){
        printf("\ncan not output output PCM!\n");
        fclose(fpIn);
        return FALSE;
    }
    //--------------------------------------------------
    // write wave header
    //--------------------------------------------------
    DWORD cbsize,wave_fsize,dwTmp;
    cbsize = sizeof(WAVEFORMATEX)-sizeof(WORD);
    wave_fsize = rawbufsize + sizeof(char)*12+sizeof(DWORD)*2+cbsize;
    dwTmp = mmioFOURCC('R','I','F','F');
    fwrite(&dwTmp,1,sizeof(DWORD),fpOut);
    dwTmp = 0; fwrite(&dwTmp,1,sizeof(DWORD),fpOut);
    dwTmp = mmioFOURCC('W','A','V','E');
    fwrite(&dwTmp,1,sizeof(DWORD),fpOut);
    dwTmp = mmioFOURCC('f','m','t',' ');
    fwrite(&dwTmp,1,sizeof(DWORD),fpOut);
    fwrite(&cbsize,1,sizeof(DWORD),fpOut);
    fwrite((void*)wavefmt,1,cbsize,fpOut);
    dwTmp = mmioFOURCC('d','a','t','a');
    fwrite(&dwTmp,1,sizeof(DWORD),fpOut);
    dwTmp = 0; fwrite(&dwTmp,1,sizeof(DWORD),fpOut);
    //--------------------------------------------------
    // convert ACM to PCM, and write PCM
    //--------------------------------------------------
    printf("\n");
    DWORD dwSize=0,cnt=0,count,eflag = true;
    while(eflag) {
        printf("\rconverting [%5d/%5d] frame",cnt++,n_Frame);
        // suck in some MP3 data
        count = fread(mp3buf,1,block_size,fpIn);
        if(count != block_size) eflag = false;
        // convert the data
        mmr = acmStreamConvert(g_mp3stream,&mp3streamHead,
                               ACM_STREAMCONVERTF_BLOCKALIGN);
        if(err_check(mmr)){
            // convert completly finished
            if((int)mp3streamHead.cbSrcLengthUsed == block_size) {
                // write the decoded PCM to disk
                count = fwrite(rawbuf,1,mp3streamHead.cbDstLengthUsed,fpOut);
                dwSize += count;
            }
            // there are excess data
            else {
                LONG len = block_size - mp3streamHead.cbSrcLengthUsed;
                fseek(fpIn,-(LONG)len,SEEK_CUR);
            }
        }
    };
    //--------------------------------------------------
    // write the wave data size
    //--------------------------------------------------
    fseek(fpOut,-(LONG)(dwSize+sizeof(DWORD)),SEEK_END);
    fwrite(&dwSize,sizeof(DWORD),1,fpOut);
    fseek(fpOut,sizeof(DWORD),SEEK_SET);
    dwSize+=sizeof(char)*12+sizeof(DWORD)*2+cbsize;
    fwrite(&dwSize,sizeof(DWORD),1,fpOut);
    //--------------------------------------------------
     // clean up
    //--------------------------------------------------
    fclose(fpIn);
    fclose(fpOut);
    assert(acmStreamUnprepareHeader(g_mp3stream,&mp3streamHead,0) == 0);
    LocalFree(mp3fmt);
    LocalFree(wavefmt);
    LocalFree(rawbuf);
    LocalFree(mp3buf);
    assert(acmStreamClose(g_mp3stream,0) == 0);
    return TRUE;
}

BOOL CALLBACK acmDriverEnumCallback(HACMDRIVERID hadid,DWORD dwInstance,
                                    DWORD fdwSupport)
{
    if(fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC) {
        MMRESULT mmr;
        ACMDRIVERDETAILS details;
        HACMDRIVER driver;
        UINT i;
        details.cbStruct = sizeof(ACMDRIVERDETAILS);
        mmr = acmDriverDetails(hadid,&details,0);
        mmr = acmDriverOpen(&driver,hadid,0);
        for(i=0;i<details.cFormatTags;i++) {
            ACMFORMATTAGDETAILS fmtDetails;
            ZeroMemory(&fmtDetails,sizeof(fmtDetails));
            fmtDetails.cbStruct = sizeof(ACMFORMATTAGDETAILS);
            fmtDetails.dwFormatTagIndex = i;
            mmr = acmFormatTagDetails(driver,&fmtDetails,
                                      ACM_FORMATTAGDETAILSF_INDEX);
            if(fmtDetails.dwFormatTag == WAVE_FORMAT_MPEGLAYER3){
                printf("Found an MP3-capable ACM codec: %s\n",
                       details.szLongName);
                g_mp3Drivers++;
            }
        }
        mmr = acmDriverClose(driver,0);
    }
    return true;
}
inserted by FC2 system