【音视频系列3】音频MP3文件和PCM文件,分析PCM音频文件,及转换为WAV文件

音频声音文件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个字节,所以取值时需要间隔进行读取并存放成左右两个声道

  源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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音频文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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文件格式,以及音频在这两种文件中的存放方式以及顺序。

posted @   风吹大风车  阅读(1750)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示