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文件合并的时候会导致音频文件时间轴发生错误,打点信息不再可靠。需要注意

 多个文件合成,懒癌患者循环遍历即可,勤快的同学可以修改代码的文件读取部分,做到批处理。











  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值