经过将近一天的研究和总结,找到了几个拼接音频文件的方法,在这里记录方便以后的使用!
代码参考:点击打开链接
首先,使用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文件合并的时候会导致音频文件时间轴发生错误,打点信息不再可靠。需要注意
多个文件合成,懒癌患者循环遍历即可,勤快的同学可以修改代码的文件读取部分,做到批处理。