音频声音文件MP3和PCM
两者均是封装格式,为了分析PCM,先下载一个MP3文件,然后通过ffmpeg将MP3文件转成PCM文件进行分析,使用分析软件为audition音频软件。
转换PCM文件
ffmpeg -i hai.mp3 -f s16le audio1.pcm
转换后可以使用此命令播放看转换是否成功:ffplay -ar 44100 -ac 2 -f s16le -i audio1.pcm
转换后的文件放到audition中,并选择采样率和位宽后,便可以显示音频文件频谱


代码分析PCM文件:
PCM的左右声道是间隔存放的,每个声道占用2个字节,所以取值时需要间隔进行读取并存放成左右两个声道
源码如下:
void pcm_spilit_channel(const char* pcmfile) {
FILE* pcmFp = fopen(pcmfile, "rb+");
FILE* pcmlFp = fopen("E:\\audio_leftC.pcm", "wb+");
FILE* pcmrFp = fopen("E:\\audio_rightC.pcm", "wb+");
unsigned char* samples = (unsigned char*)malloc(4);
while (!feof(pcmFp))
{
fread(samples, 1, 4, pcmFp);
fwrite(samples, 1, 2, pcmlFp);
fwrite(samples + 2, 1, 2, pcmrFp);
}
free(samples);
fclose(pcmrFp);
fclose(pcmlFp);
fclose(pcmFp);
}
分离左右声道后,音频文件同样的设置后通过audition进行查看

WAVE文件格式,包含3个头部结构和一个PCM数据块,具体如下:

使用C++将pcm音频转换为wav音频文件
typedef struct {
char fccID[4];
unsigned long dwSize;
char fccType[4];
}WAV_HEADER;
typedef struct
{
char fccID[4];
unsigned long dwSize;
unsigned short wFromatTag;
unsigned short wChannels;
unsigned long dwSamplesPerSec;
unsigned long dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short uiBitsPerSample;
}WAV_FMT;
typedef struct {
char fccID[4];
unsigned long dwSize;
}WAV_DATA;
void pcm2wav(const char* pcmfile, int channel, int sampleRate, const char* wavfile) {
FILE* pcmFp = fopen(pcmfile, "rb+");
FILE* wavFp = fopen(wavfile, "wb+");
WAV_HEADER wavHeader;
WAV_FMT wavFormat;
WAV_DATA wavData;
unsigned short pcmData;
int bits = 16;
memcpy(wavHeader.fccID, "RIFF", strlen("RRIF")); //设置RIFF头
memcpy(wavHeader.fccType, "WAVE", strlen("WAVE")); //设置WAVE标识
memcpy(wavFormat.fccID, "fmt ", strlen("fmt ")); //设置第二块头“fmt ”
wavFormat.dwSize = 16; //WAV_FORMAT的大小- sizeof(fccID) - sizeof(dwSize)
wavFormat.wFromatTag = 1; //PCM格式文件时设置1
wavFormat.wChannels = channel;
wavFormat.dwSamplesPerSec = sampleRate; //比特率
wavFormat.dwAvgBytesPerSec = sampleRate * sizeof(pcmData); //码率
wavFormat.wBlockAlign = sizeof(pcmData); //每个采样点的字节对齐宽度
wavFormat.uiBitsPerSample = bits; //PCM采样的位数
memcpy(wavData.fccID, "data", strlen("data")); //设置第三块头data
int offset = sizeof(WAV_HEADER) + sizeof(WAV_FMT) + sizeof(WAV_DATA);
int pcmDataCount = 0;
fseek(wavFp, offset, SEEK_CUR);
while (!feof(pcmFp)) {
fread(&pcmData, sizeof(unsigned short), 1, pcmFp);
fwrite(&pcmData, sizeof(unsigned short), 1, wavFp);
pcmDataCount++;
}
pcmDataCount = pcmDataCount << 1;
wavData.dwSize = pcmDataCount; //设置大小为PCM数据的大小
wavHeader.dwSize = pcmDataCount + sizeof(WAV_DATA) + sizeof(WAV_FMT) + sizeof(WAV_HEADER) - 8; //头的大小为所有的大小-sizeof(WAV_HEADER.fccID)-sizeof(WAV_HEADER.dwSize)
rewind(wavFp);
fwrite(&wavHeader, 1, sizeof(wavHeader), wavFp);
fwrite(&wavFormat, 1, sizeof(wavFormat), wavFp);
fwrite(&wavData, 1, sizeof(wavData), wavFp);
fclose(pcmFp);
fclose(wavFp);
return;
}
注意:转换后文件大小几乎相同
转换后文件可以播放,放到audition中可以看到波形图正常即可

由此可以了解PCM和wav文件格式,以及音频在这两种文件中的存放方式以及顺序。
