1. 程式人生 > >c++&c#實現拼接wav和mp3檔案

c++&c#實現拼接wav和mp3檔案

經過將近一天的研究和總結,找到了幾個拼接音訊檔案的方法,在這裡記錄方便以後的使用!

首先,使用c++拼接wav檔案。wav檔案有檔案頭和檔案資料兩部分組成,以前做錄音軟體的時候詳細看過。具體內容在這裡不做過多的贅述。感興趣的同學可以自己去看看詳細的文件。我們拼接的任務是保留一個頭資訊合併內容資訊,把兩個或者多個音訊檔案儲存成為一個獨立的音訊檔案。

 合併功能類CutMusicWav.h

#ifndef __CUTMUSIC_H__
#define __CUTMUSIC_H__

#pragma once

#include <stdio.h>
#include <string.h>

using namespace std;

#define RIFF_SIGN_ID 0x46464952ul 
#define WAVE_SIGN_ID 0x45564157ul 
#define FMT__SIGN_ID 0x20746D66ul 
#define FACT_SIGN_ID 0x74636166ul 
#define DATA_SIGN_ID 0x61746164ul

#ifndef DWORD
typedef unsigned long DWORD;
#endif

#ifndef WORD
typedef unsigned short WORD;
#endif

#ifndef BYTE
typedef unsigned char BYTE;
#endif

struct RIFF_HEADER
{
    DWORD RiffID; // 資源交換檔案標誌 0x46464952 'R' 'I' 'F' 'F'
    DWORD RiffSize;   // 從下個地址開始到檔案尾的總位元組數
    DWORD RiffFormat; // WAV檔案標誌 0x45564157 'W','A','V','E'
};

struct WAVE_FORMAT 
{ 
    WORD  FormatTag;      // 格式種類(值為1時,表示資料為線性PCM編碼) 
    WORD  Channels;       // 通道數,單聲道為1,雙聲道為2 
    DWORD SamplesPerSec;  // 取樣頻率
    DWORD AvgBytesPerSec; // 每秒所需位元組數  
    WORD  BlockAlign;     // 資料塊對齊單位(每個取樣需要的位元組數) 
    WORD  BitsPerSample;  // 每個取樣需要的bit數  
    WORD  otherInfo;      // 附加資訊(可選,通過Size來判斷有無) 
};

struct FMT_BLOCK 
{ 
    DWORD       FmtID;     // 波形格式標誌 0x20746D66 'f','m','t',' ' 
    DWORD       FmtSize;   // 波形格式部分長度(一般為00000010H) 
    WAVE_FORMAT wavFormat; // 波形資料格式 
}; 

struct UNKNOW_BLOCK 
{ 
    DWORD ID;   // 未知塊 
    DWORD Size; // 未知塊長度 
}; 

struct FACT_BLOCK 
{ 
    DWORD FactID;   // 可選部分標識 0x74636166 'f','a','c','t' 
    DWORD FactSize; // 可選部分長度 
    BYTE  Data[1];  // 可選部分資料 
}; 

struct DATA_BLOCK 
{ 
    DWORD DataID;   // 資料標誌符 0x61746164, 'd','a','t','a' 
    DWORD DataSize; // DATA總資料長度位元組 
    BYTE  Data[1];  // 資料 
};

class CCutMusicWav
{
public:
    CCutMusicWav();
    ~CCutMusicWav();

public:
    BYTE * openWaveFile(const char *name); 
    BYTE * getWaveData(BYTE * wav, int * dLen); 
    void   printWaveFormat(BYTE * wav); 
    int    saveWaveFile(const char * name, BYTE * wav); 
    BYTE * catWave(BYTE *& wav1, BYTE *& wav2);
    size_t getTotalLen(BYTE * wav);
};


#endif
實現檔案 CutMusicWav.cpp
#include "stdafx.h"
#include "CutMusicWav.h"


CCutMusicWav::CCutMusicWav()
{

}

CCutMusicWav::~CCutMusicWav()
{

}

BYTE * CCutMusicWav::openWaveFile(const char *name)
{
    size_t readByte;
    FILE * fp = fopen(name, "rb"); 

    if(fp==NULL) return NULL; 

    RIFF_HEADER fh; 

    if(fread(&fh, sizeof(fh), 1, fp) != 1) 
    { 
        fclose(fp); 
        printf("Riff Header 檔案長度錯誤\n"); 
        return NULL; 
    } 

    if(fh.RiffFormat != WAVE_SIGN_ID || fh.RiffID != RIFF_SIGN_ID) 
    { 
        fclose(fp); 
        printf("檔案識別符號錯誤 ID:%08X Format:%08X\n", fh.RiffID, fh.RiffFormat); 
        return NULL; 
    } 
    BYTE * r = new BYTE[fh.RiffSize + 10], *pr; 
    if(r==NULL) 
    { 
        fclose(fp); 
        printf("記憶體申請錯誤\n"); 
        return NULL; 
    } 
    readByte = fread(r, 1, fh.RiffSize-4, fp); 

    if(readByte != fh.RiffSize-4) 
    { 
        delete [] r; 
        printf("wave 檔案長度錯誤 %d %d\n", readByte, fh.RiffSize); 
        return NULL; 
    } 

    fclose(fp); 

    FMT_BLOCK *fb = (FMT_BLOCK *)r; 

    if(fb->FmtID != FMT__SIGN_ID) 
    { 
        printf("格式識別符號錯誤或格式大小錯誤ID:%08X\n", fb->FmtID); 
        delete [] r; 
        return NULL; 
    } 

    if(fb->wavFormat.FormatTag != 1) 
    { 
        delete [] r; 
        printf("不支援的格式 Format:%d\n", fb->wavFormat.FormatTag); 
        return NULL; 
    } 
    pr = r + 8 + fb->FmtSize; 
    while(1) 
    { 
        UNKNOW_BLOCK * ub = (UNKNOW_BLOCK *)pr; 

        if(ub->ID == FACT_SIGN_ID) 
        { 
            printf("Fact 標籤 length: %d\n", ub->Size); 
            pr += 8 + ub->Size ; 
        } 
        else break; 
    } 

    DATA_BLOCK * db = (DATA_BLOCK *)pr; 

    if(db->DataID  != DATA_SIGN_ID) 
    { 
        delete [] r; 
        printf("資料錯誤\n"); 
        return NULL; 
    } 
    return r;
}

BYTE * CCutMusicWav::getWaveData(BYTE * wav, int * dLen)
{
    UNKNOW_BLOCK * ub = (UNKNOW_BLOCK *)wav; 

    while(ub->ID != DATA_SIGN_ID) 
    { 
        switch(ub->ID) 
        { 
        case DATA_SIGN_ID: 
            break; 
        case FMT__SIGN_ID: 
        case FACT_SIGN_ID: 
            ub = (UNKNOW_BLOCK *)(((BYTE *)ub) + ub->Size + 8); 
            break; 
        default: 
            printf("錯誤標籤 %08X\n", ub->ID ); 
            return NULL; 
        } 
    } 

    DATA_BLOCK * db = (DATA_BLOCK *)ub; 
    *dLen = db->DataSize; 
    return db->Data;
}

void CCutMusicWav::printWaveFormat(BYTE * wav)
{
    int len; 
    getWaveData(wav, &len); 
    FMT_BLOCK *fb = (FMT_BLOCK *)wav; 
    printf("Wave 格式:PCM\n"); 
    printf("通道數量:%d\n", fb->wavFormat.Channels );
    printf("取樣頻率:%dHZ\n", fb->wavFormat.SamplesPerSec );
    printf("每秒所需位元組數:%d位元組\n", fb->wavFormat.AvgBytesPerSec ); 
    printf("資料塊對齊單位:%d位元組\n", fb->wavFormat.BlockAlign ); 
    printf("每個取樣需要的bit數:%dbit\n", fb->wavFormat.BitsPerSample ); 
    printf("長度:%.2f 秒\n", (double)len / fb->wavFormat.AvgBytesPerSec);
}

int CCutMusicWav::saveWaveFile(const char * name, BYTE * wav)
{
    FILE *fp = fopen(name, "wb"); 
    if(fp == 0) return 0; 
    int len = getTotalLen(wav); 
    RIFF_HEADER rh; 
    rh.RiffFormat = WAVE_SIGN_ID; 
    rh.RiffID     = RIFF_SIGN_ID; 
    rh.RiffSize   = len + 4; 
    fwrite(&rh, sizeof(rh), 1, fp); 
    fwrite(wav, 1, len, fp); 
    fclose(fp); 
    return 1;
}

BYTE * CCutMusicWav::catWave(BYTE *& wav1, BYTE *& wav2)
{
    FMT_BLOCK * fb1 = (FMT_BLOCK *)wav2; 

    const FMT_BLOCK * fb2 = (const FMT_BLOCK *)wav2; 

    if( fb1->wavFormat.AvgBytesPerSec == fb2->wavFormat.AvgBytesPerSec && 
        fb1->wavFormat.BitsPerSample  == fb2->wavFormat.BitsPerSample  && 
        fb1->wavFormat.BlockAlign     == fb2->wavFormat.BlockAlign     && 
        fb1->wavFormat.Channels       == fb2->wavFormat.Channels       && 
        fb1->wavFormat.FormatTag      == fb2->wavFormat.FormatTag      && 
        fb1->wavFormat.SamplesPerSec  == fb2->wavFormat.SamplesPerSec) 
    { 
        int len1 = getTotalLen(wav1), len2; 
        BYTE * Data2 = getWaveData(wav2, &len2); 
        BYTE * nD = new BYTE[len1 + len2 + 10]; 
        if(nD == NULL) return NULL; 
        memcpy(nD, wav1, len1); 
        delete [] wav1; 
        wav1 = nD; 
        BYTE * Data1 = getWaveData(wav1, &len1); 
        DATA_BLOCK * db1 = (DATA_BLOCK *)(Data1 - 8); 
        db1->DataSize += len2; 
        memcpy(Data1 + len1, Data2, len2); 
        return wav1; 
    } 

    return NULL;
}

size_t CCutMusicWav::getTotalLen(BYTE * wav)
{
    size_t r = 0; 

    UNKNOW_BLOCK * ub = (UNKNOW_BLOCK *)wav; 

    while(1) 
    { 
        switch(ub->ID) 
        { 
        case DATA_SIGN_ID: 
            r += ub->Size + 8; 
            return r; 
        case FMT__SIGN_ID: 
        case FACT_SIGN_ID: 
            r += ub->Size + 8; 
            ub = (UNKNOW_BLOCK *)(((BYTE *)ub) + ub->Size + 8); 
            break; 
        default: 
            printf("錯誤標籤 %08X\n", ub->ID ); 
            return NULL; 
        } 
    } 

    return -1;
}
 呼叫實現:
    CCutMusicWav cCutMusicWav;

    BYTE* data1 = cCutMusicWav.openWaveFile("1.wav");
    cCutMusicWav.printWaveFormat(data1);

    BYTE* data2 = cCutMusicWav.openWaveFile("2.wav");
    cCutMusicWav.printWaveFormat(data2);

    data1 = cCutMusicWav.catWave(data1,data2);
    cCutMusicWav.printWaveFormat(data1);

    cCutMusicWav.saveWaveFile("new.wav",data1);
 我們就可以拼接wav檔案了!

下面使用c++拼接mp3檔案。

同樣CutMusicMp3.h

#ifndef __CUTMUSICMP3_H__
#define __CUTMUSICMP3_H__

#pragma once

#include <RpcNdr.h>

using namespace std;

class CCutMusicMp3
{
public:
    CCutMusicMp3(void);
    ~CCutMusicMp3(void);

public:

    //定義變數
    CString NewMp3Path;//合併後新MP3儲存路徑;
    int NewMp3Lengh;//合併後新MP3檔案長度;
    byte p_ID3v1[128];//儲存MP3檔案 ID3v1資料;
    byte *p_ID3v2;//儲存MP3檔案 ID3v2資料;
    int mp3_length;//讀取多個mp3檔案的長度;
    byte * MData;//儲存讀取的mp3資料檔案——單檔案
    byte *NewData;//儲存合成後mp3檔案資料——多檔案
    byte *NewData_temp;//暫時儲存合成後mp3檔案資料——多檔案
    int NewDataLength;//儲存合成後mp3長度;
    //ID3v2標識部分
    byte Header[3]; /*必須為"ID3"否則認為標籤不存在*/
    byte Ver; /*版本號ID3V2.3 就記錄3*/
    byte Revision; /*副版本號此版本記錄為0*/
    byte Flag; /*存放標誌的位元組,這個版本只定義了三位,稍後詳細解說*/
    byte Size[4]; /*標籤大小,包括標籤頭的10 個位元組和所有的標籤幀的大小*/
    int n_head;
    int n_end;
    int MpegType;
    int LayerType;
    int mp1[3][14];
    int mp2[3][14];
    int sf[3][3];
    int sfn[3][3];

    byte mp3_ID3v1_name[30];
    byte mp3_ID3v1_singer[30];
    byte mp3_ID3v1_zhuanji[30];
    byte mp3_ID3v1_year[4];
    byte mp3_ID3v1_type[1];

    // 讀取mp3檔案 ID3v1幀,儲存在*p_ID3v1中,返回ID3v1標籤長度
    int ReadID3v1(byte *pv1,int filesize);

    // 讀取mp3檔案 ID3v2幀,儲存在*p_ID3v2中,引數為讀取mp3檔案字元陣列,返回ID3v2標籤長度,引數2表示是否需要儲存ID3v2
    int ReadID3v2(byte *pv2,int isave);

    // 讀取mp3檔案,引數1為傳入陣列,引數2,為檔案ID3v2長度,引數3為檔案字元數;
    int ReadMp3(byte *p,int nh,int nl,int ne);

    // 讀取多個檔案,引數1為mp3檔案路徑陣列,引數2為陣列元素個數
    void ReadFiles(CString files[],int FilesNum,int FilesSize);

    //---------cbr
    // 讀取mp3檔案位率,對cbr
    int ReadBitRate(byte b,int mpegtype,int layertype);

    // 讀取mp3檔案位率,對cbr
    int ReadSf(byte b,int mpegtype,int layertype);

    // 讀取mp3檔案位率,對cbr
    int ReadPading(byte b);

    // 將生成的mp3檔案寫入
    void WriteNewMp3(byte *s,CString path,int l);

    // 取樣個數;
    int ReadSfN(int mpegtype,int layertype);

};

#endif   // __CUTMUSICMP3_H__
標頭檔案 CutMusicMp3.cpp
#include "stdafx.h"
#include "CutMusicMp3.h"


CCutMusicMp3::CCutMusicMp3(void)
{

}

CCutMusicMp3::~CCutMusicMp3(void)
{

}

//檢測是否含有ID3v1資訊,並返回ID3v1資訊大小,若不為0,則將資訊儲存在p_ID3v1中
int CCutMusicMp3::ReadID3v1(byte *pv1,int filesize)
{
    // 裡面用""就會報錯, 所有用acsII碼來代替
    if((pv1[filesize-128]==0x54&&pv1[filesize-127]==0x41&&pv1[filesize-126]==0x47))
    {
        memset(p_ID3v1,0,128);
        memcpy(p_ID3v1,pv1+filesize-128,128);
        memcpy(mp3_ID3v1_name,p_ID3v1+3,30);
        memcpy(mp3_ID3v1_singer,p_ID3v1+33,30);
        memcpy(mp3_ID3v1_zhuanji,p_ID3v1+63,30);
        memcpy(mp3_ID3v1_year,p_ID3v1+93,4);
        memcpy(mp3_ID3v1_type,p_ID3v1+127,1);
        return 128;
    }
    else
    {
        return 0;
    }
}

//檢測是否含有ID3v2資訊,並返回ID3v2資訊大小,若不為0,則將資訊儲存在p_ID3v2中,isave=0時,儲存資訊,否則不儲存;
//ID3v2資訊大小 :應為原計算結果+10;
int CCutMusicMp3::ReadID3v2(byte *pv2,int isave)
{
    //if((pv2[0]==_T('I'))&&pv2[1]==_T('D')&&(pv2[2]==_T('3')))// 裡面用""就會報錯
    if((pv2[0]==0x49)&&pv2[1]==0x44&&(pv2[2]==0x33))
    {
        for(int i=0;i<3;i++)
        {
            Header[i]=pv2[i];
        }
        Ver=pv2[3];
        Revision=pv2[4];
        Flag=pv2[5];
        for(int i=6;i<10;i++)
        {
            Size[i-6]=pv2[i];
        }

        //計算結果上10(幀頭長度),才是原有ID3v2幀長度,
        int n=(Size[0]&0x7F)*0x200000+(Size[1]&0x7F)*0x4000+(Size[2]&0x7F)*0x80+(Size[3]&0x7F)+10;
        if (isave==0)
        {
            p_ID3v2=new byte[n];
            memset(p_ID3v2,0,n);
            memcpy(p_ID3v2,pv2,n);
            //int mmm=sizeof(p_ID3v2);
        }

        return n;
    }
    else
    {
        return 0;
    }
}

////讀取mp3檔案FrameData,引數1為傳入陣列,引數2,為檔案ID3v2長度,引數3為檔案字元數,引數4為id3v1長度(0或者128);
//返回讀取mp3檔案資料長度
int CCutMusicMp3::ReadMp3(byte *p,int nh,int nl,int ne)
{
  	//關於如何計算mp3幀長度 
	//位率不變、CRC校驗為0(FB):(144*BitRate)/Sampling_freq+pading
	//byte *p_t=new byte[nl];
	int BitRate=0;
	int Sampling_freq=0;
	int pading=0;
	int Sampling_Num=0;
	int nframe=0;//單個數據幀長度
	int nframes=0;//資料幀總長度
	MpegType=4;
	LayerType=4;
	//int crcNum=0;//判斷是否存在校驗位
	for (int i = nh; i < nl-ne;)
	{
		//資料幀長度計算公式,有無校驗都一樣

		if((p[i]==0xff)&&((p[i+1]>>5)==0x7))//判斷是否是資料頭 不能用char型,不知道為什麼
		{
			/*layertype:0——layer I;1——layer II;2——layer III;
			mpegtype:  0——MPEG1; 1——MPEG2;  2——MPEG2.5;  */
			switch (p[i+1]>>3&0x03)
			{
			case 0x0:
				MpegType=2;
				break;
			case 0x2:
				MpegType=1;
				break;
			case 0x3:
				MpegType=0;
				break;
			}
			switch (p[i+1]>>1&0x03)
			{
			case 0x01:
				LayerType=2;
				break;
			case 0x02:
				LayerType=1;
				break;
			case 0x03:
				LayerType=0;
				break;
			}
			if(LayerType!=4&&MpegType!=4)
			{
				Sampling_Num=ReadSfN(MpegType,LayerType);
				BitRate=ReadBitRate(p[i+2],MpegType,LayerType);
				Sampling_freq=ReadSf(p[i+2],MpegType,LayerType);
				if(Sampling_freq==0)
				{
					MessageBox(NULL,"取樣頻率為0,計算有錯誤","資訊提示",MB_OK);
				}
				else
				{
					pading=ReadPading(p[i+2]);
					nframe=(Sampling_Num*BitRate)/Sampling_freq+pading;
					i=i+nframe;
					nframes+=nframe;
				}
			}
			else
			{
				i+=1;
			}

		}
		else
		{
			i+=1;
			int na=i;
		}
	}
	return nframes;
}

/*---------------------------------
//ReadFiles(CString files[],int filesNum)函式
//讀取files陣列中,儲存的檔案路徑,通過迴圈,
//讀取mp3資料幀,儲存在byte * MData,進而儲存在byte *NewData中
-----------------------------------*/
void CCutMusicMp3::ReadFiles(CString files[],int FilesNum,int FilesSize)
{
    if(FilesNum>1)
    {
        NewDataLength=0;
        NewData_temp=new byte[FilesSize];
        for(int i=0;i<FilesNum;i++)
        {
            CString s=files[i];
            CFile cf;
            cf.Open(s,CFile::modeRead);
            int fsize=cf.GetLength();//獲取檔案大小
            byte *p_temp=new byte[fsize];
            cf.Read(p_temp,fsize);//將mp3檔案資料存入byte陣列中
            cf.Close();
            n_head=0;
            n_end=0;

            //=0;//記錄幀資料長度;
            if(i==0)
            {
                n_head=ReadID3v2(p_temp,0);
                n_end=ReadID3v1(p_temp,fsize);
            }
            else 
            {
                n_head=ReadID3v2(p_temp,1);
                n_end=ReadID3v1(p_temp,fsize);
            }
            int dataL=ReadMp3(p_temp,n_head,fsize,n_end);
            memcpy(NewData_temp+NewDataLength,p_temp+n_head,dataL);
            NewDataLength+=dataL;
            delete[] p_temp;
        }
        int itemp=NewDataLength+n_head+n_end;
        NewData=new byte[itemp];
        memset(NewData,0,itemp);
        memcpy(NewData,p_ID3v2,n_head);
        memcpy(NewData+n_head,NewData_temp,NewDataLength);
        memcpy(NewData+n_head+NewDataLength,p_ID3v1,n_end);
        WriteNewMp3(NewData,NewMp3Path,itemp);
        delete[] NewData_temp;
    }
    else
    {
        //CopyFile(files[0],NewMp3Path,true);
        MessageBox(NULL,"選擇數目不可少於1個的檔案","資訊提示",MB_OK);
    }
}

int CCutMusicMp3::ReadBitRate(byte b,int mpegtype,int layertype)
{
  	/*		v1 l1       v1 l2       v1 l3       v2 l1       v2 l2      v2 l3
	0000	free		free		free        free        free       free
	0001	32          32          32          32(32)      32(8)      8(8)
	0010	64          48          40          64(48)      48(16)     16(16)
	0011	96          56          48          96(56)      56(24)     24(24)
	0100	128	        64	        56	        64      	32
	0101	160	        80	        64	        80	        40
	0110	192	        96	        80	        96	        48
	0111	224       	112	        96	        112      	56
	1000	256	        128      	112     	128      	64
	1001	288	        160	        128	        144	        80
	1010	320     	192	        160      	160	        96
	1011	352      	224      	192	        176	        112
	1100	384      	256	        224      	384(192)    256(128)    128(128)
	1101	416      	320	        256     	224	        144
	1110	448	        384	        320      	256	        160

	*/
	//行座標為:layer( 0 1 2 )
	//---------------1--2--3--4---5---6---7---8---9---10--11--12--13--14
	int mp2[3][14]={{32,64,96,128,160,192,224,256,288,320,352,384,416,448},
	{32,48,56,64, 80, 96, 112,128,160,192,224,256,320,384},
	{32,40,48,56, 64, 80, 96, 112,128,160,192,224,256,320}};
	int mp1[3][14]={{32,48,56,64, 80, 96, 112,128,144,160,176,192,224,256},
	{8, 16,24,32, 40, 48, 56, 64, 80, 96, 112,128,144,160},
	{8, 16,24,32, 40, 48, 56, 64, 80, 96, 112,128,144,160}};
	int n=0;
	switch (b>>4)
	{
	case 0x0:
		break;
	case 0x1:
		if(mpegtype==1)
		{
			n=mp1[layertype][0];
		}
		else
		{
			n=mp2[layertype][0];
		}
		break;
	case 0x2:
		if(mpegtype==1)
		{
			n=mp1[layertype][1];
		}
		else
		{
			n=mp2[layertype][1];
		}
		break;
	case 0x3:
		if(mpegtype==1)
		{
			n=mp1[layertype][2];
		}
		else
		{
			n=mp2[layertype][2];
		}
		break;
	case 0x4:
		if(mpegtype==1)
		{
			n=mp1[layertype][3];
		}
		else
		{
			n=mp2[layertype][3];
		}
		break;
	case 0x5:
		if(mpegtype==1)
		{
			n=mp1[layertype][4];
		}
		else
		{
			n=mp2[layertype][4];
		}
		break;
	case 0x6:
		if(mpegtype==1)
		{
			n=mp1[layertype][5];
		}
		else
		{
			n=mp2[layertype][5];
		}
		break;
	case 0x7:
		if(mpegtype==1)
		{
			n=mp1[layertype][6];
		}
		else
		{
			n=mp2[layertype][6];
		}
		break;
	case 0x8:
		if(mpegtype==1)
		{
			n=mp1[layertype][7];
		}
		else
		{
			n=mp2[layertype][7];
		}
		break;
	case 0x9:
		if(mpegtype==1)
		{
			n=mp1[layertype][8];
		}
		else
		{
			n=mp2[layertype][8];
		}
		break;
	case 0xa:
		if(mpegtype==1)
		{
			n=mp1[layertype][9];
		}
		else
		{
			n=mp2[layertype][9];
		}
		break;
	case 0xb:
		if(mpegtype==1)
		{
			n=mp1[layertype][10];
		}
		else
		{
			n=mp2[layertype][10];
		}
		break;
	case 0xc:
		if(mpegtype==1)
		{
			n=mp1[layertype][11];
		}
		else
		{
			n=mp2[layertype][11];
		}
		break;
	case 0xd:
		if(mpegtype==1)
		{
			n=mp1[layertype][12];
		}
		else
		{
			n=mp2[layertype][12];
		}
		break;
	case 0xe:
		if(mpegtype==1)
		{
			n=mp1[layertype][13];
		}
		else
		{
			n=mp2[layertype][13];
		}
		break;
	case 0xf:
		break;
	}
	n=n*1000;
	return n;
}

//取樣頻率  
int CCutMusicMp3::ReadSf(byte b,int mpegtype,int layertype)
{
	/*layertype:0——layer I;1——layer II;2——layer III;
	mpegtype:  0——MPEG1; 1——MPEG2;  2——MPEG2.5;  */
	//              00     01    32
	int sf[3][3]={{44100,48000,32000},
	{22050,24000,16000},
	{11025,12000,8000}};//取樣頻率
	int n=0;
	switch ((b&0x0f)>>2)
	{
	case 0x00:
		n=sf[mpegtype][0];
		break;
	case 0x01:
		n=sf[mpegtype][1];
		break;
	case 0x02:
		n=sf[mpegtype][2];
		break;
	}
	return n;
}

int CCutMusicMp3::ReadPading(byte b)
{
    int n=0;
    switch ((b&0x02)>>1)
    {
    case 0:
        n=0;
        break;
    case 1:
        n=1;
        break;
    }
    return n;
}

void CCutMusicMp3::WriteNewMp3(byte *s,CString path,int l)
{
    CFile wfile;
    wfile.Open(path,CFile::modeCreate|CFile::modeWrite);
    wfile.Write(s,l);
    wfile.Close();
}

int CCutMusicMp3::ReadSfN(int mpegtype,int layertype)
{
  	/*            MPEG1   MPEG2   MPEG2.5	
	layer I        384      384     384
	layer II       1152     1152    1152
	layer III      1152     576     576
	*/
	int sfn[3][3]={{384,1152,1152},{384,1152,576},{384,1152,576}};//取樣個數;
	int n=sfn[mpegtype][layertype];
	n=n/8;
	return n;
}
呼叫測試程式碼:
CString mp3_filename[100];
int files_size=0;//標識選取MP3檔案總大小;
int fileNum=0;//標識選擇檔案數量

struct ID3v1
{
    byte mp3_name[30];
    byte mp3_singer[30];
    byte mp3_zhuanji[30];
    byte mp3_year[4];
    byte mp3_type[1];
}ID3v1_temp;

    CCutMusicMp3 cCutMusicMp3;

    CString strPath = _T("G:\\自習\\CutMusicMp3Test\\CutMusicMp3Test\\bin\\new.mp3");
    cCutMusicMp3.NewMp3Path = strPath;

    mp3_filename[0] = "G:\\自習\\CutMusicMp3Test\\CutMusicMp3Test\\bin\\1.mp3";
    mp3_filename[1] = "G:\\自習\\CutMusicMp3Test\\CutMusicMp3Test\\bin\\2.mp3";

    CFile cfile;
    if (cfile.Open(mp3_filename[0], CFile::modeRead))
    {
        files_size+= cfile.GetLength();
    }
    cfile.Close();
    if (cfile.Open(mp3_filename[1], CFile::modeRead))
    {                                
        files_size+= cfile.GetLength();
    }
    cfile.Close();
    cCutMusicMp3.mp3_length=files_size;

    // 總的檔案個數
    int fileNum = 2;

    cCutMusicMp3.ReadFiles(mp3_filename,fileNum,cCutMusicMp3.mp3_length);

    CString edit_name = _T("");
    CString edit_singer = _T("");
    CString edit_zhuanji = _T("");
    CString edit_year = _T("");

    char *a=(char *)cCutMusicMp3.mp3_ID3v1_name;
    CString s=_T("");
    s.Format("%c",a);
    edit_name=a;
    a=(char *)cCutMusicMp3.mp3_ID3v1_singer;
    s.Format("%c",a);
    edit_singer=a;
    a=(char *)cCutMusicMp3.mp3_ID3v1_year;
    s.Format("%c",a);
    edit_year=a;

    CString ptemp=cCutMusicMp3.NewMp3Path;
    byte b[128];
    memset(b,0,128);
    b[0]=0x54;
    b[1]=0x41;
    b[2]=0x47;
    memcpy(b+3,edit_name,edit_name.GetLength());
    memcpy(b+33,edit_singer,edit_singer.GetLength());
    memcpy(b+63,edit_zhuanji,edit_zhuanji.GetLength());
    memcpy(b+93,edit_year,edit_year.GetLength());

    if(cCutMusicMp3.n_end==0)//合併後mp3沒有ID3v1資訊
    {
        CFile cf;
        cf.Open(ptemp,CFile::modeReadWrite);
        int l=cf.GetLength();
        char *pm=new char[l+128];
        cf.Read(pm,l);
        memcpy(pm+l,b,128);
        cf.Write(pm,l+128);//是“覆蓋”還是“追加”?
        delete[] pm;
        cf.Close();
    }
    else//合併後mp3有ID3v1資訊
    {
        CFile cf;
        cf.Open(ptemp,CFile::modeReadWrite);
        int l=cf.GetLength();
        char *pm=new char[l];
        cf.Read(pm,l-128);
        memcpy(pm+l-128,&b,128);
        //cf.Write(p,l);//是“追加”
        cf.SeekToBegin();
        cf.Write(pm,l);
        cf.Close();
        delete[] pm;
    }
但是這個MP3檔案由於大家的格式不規範,這樣處理有缺陷,經過反覆考量,發現c#實現起來比較簡單。附上c#實現程式碼
using System.Collections.Generic;
using System.IO;
using NAudio.Wave;
namespace WindowsTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private void button1_Click(object sender, EventArgs e)
        {
            List<string> ls = new List<string> {
                @"G:\自習\CutMusicMp3Test\CutMusicMp3Test\bin\1.mp3",
                @"G:\自習\CutMusicMp3Test\CutMusicMp3Test\bin\2.mp3",
                @"G:\自習\CutMusicMp3Test\CutMusicMp3Test\bin\new.mp3",
            };
            Get_mp3(ls);
        }

        static void Get_mp3(List<string> l)
        {
            List<string> ls = l;
            int n = ls.Count;
            byte[] OUT = File_2_Byte(ls[0]);
            byte[] OUT1 = File_2_Byte(ls[1]);
            byte[] OUT2 = Combine_2_Byte(OUT, OUT1);

            using (StreamWriter sw = new StreamWriter(ls[2], false, Encoding.GetEncoding(1252)))
            {
                sw.Write(Encoding.GetEncoding(1252).GetString(OUT2));
            }
    }

        static byte[] File_2_Byte(string url)
        {
            long l = new FileInfo(url).Length;
            byte[] b = new byte[l];
            new FileStream(url, FileMode.Open).Read(b, 0, (int)l);
            return b;
        }

        static byte[] Combine_2_Byte(byte[] a, byte[] b)
        {
            long la = a.Length;
            long lb = b.Length;
            long lc = la + lb;
            byte[] c = new byte[lc];
            for (int i = 0; i < la; i++)
            {
                c[i] = a[i];
            }
            for (int i = 0; i < lb; i++)
            {
                c[i + la] = b[i];
            }
            return c;
        }
}
}
c#還有一種實現方式,利用第三方庫NAudio

首先在Referenfes中新增NAudio.dll的庫依賴

在程式開頭新增:

using NAudio.Wave;
就可以在程式中使用了。
        private void button2_Click(object sender, EventArgs e)
        {
            string[] str1 = new string[2];
            str1[0] = "G:\\自習\\CutMusicMp3Test\\CutMusicMp3Test\\bin\\1.mp3";
            str1[1] = "G:\\自習\\CutMusicMp3Test\\CutMusicMp3Test\\bin\\2.mp3";

            FileStream SourceStream = File.Open("G:\\自習\\CutMusicMp3Test\\CutMusicMp3Test\\bin\\new.mp3", FileMode.OpenOrCreate);
            Combine(str1, SourceStream);
        }

        public static void Combine(string[] inputFiles, Stream output)
        {
            foreach (string file in inputFiles)
            {
                Mp3FileReader reader = new Mp3FileReader(file);
                if ((output.Position == 0) && (reader.Id3v2Tag != null))
                {
                    output.Write(reader.Id3v2Tag.RawData, 0, reader.Id3v2Tag.RawData.Length);
                }
                Mp3Frame frame;
                while ((frame = reader.ReadNextFrame()) != null)
                {
                    output.Write(frame.RawData, 0, frame.RawData.Length);
                }
            }

            output.Close();
        }
由於我們要合成檔案,所有使用Stream的子類FileStream來進行資料流的處理工作,詳細情況可以去了解Stream,FIleStream,和MemoryStream的具體內容。

 由於MP3檔案又一個失真率的概念,所以不同品質的MP3檔案合併的時候會導致音訊檔案時間軸發生錯誤,打點資訊不再可靠。需要注意

 多個檔案合成,懶癌患者迴圈遍歷即可,勤快的同學可以修改程式碼的檔案讀取部分,做到批處理。