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等への移植も可能です。
//----------------------------------------------------------------- // 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; } |