• PCM编码格式


    目录

    一、PCM音频编码格式

    1. 相关英文单词

    2. PCM数据结构

    2.1 单声道数据

    2.2 多声道数据

    2.3 小结

    二、WAV音频文件格式


    一、PCM音频编码格式

    1. 相关英文单词

    Amplitude:幅度

    PAM:Pulse Amplitude Modulation 脉冲幅度调制(数字信号过程采样)

    Stereo:立体声(双声道),Mono:单声道

    PCM:Pulse Code Modulation

    Sampling:采样        Quantization:量化           Code:编码

    Nyquist Sampling theorem:奈奎斯特采样定理

    Analog Signal:模拟信号   Digital Signal:数字信号

    Sample rate:采样率(每秒钟的样本点数)sampleing bit depth:采样位深度

    Channel:通道数/声道数

    (1)PCM是最原始的音频编码格式,并不是文件格式,WAV/MP3等才是文件格式。

    (2)采样率:每秒钟的采样次数/每秒钟的样本点数,即一秒钟能采集到多少个离散的数值点,比如:16KHz=16000Hz,意思就是16000个采样点/s,采样率是保证音质的一个重要参数。

    (3)采样位深度是每一个采样点的值的bit数,很容易理解,bit越多,能表示的数范围就越广,更加能表示模拟信号,可以直观理解,如果采样率和位深都很大,采样的离散序列就可以近似等于模拟信号。

    8bit:256个值,从0~255

    16bit:65536个值,从0~65535

    24bit:………………………………

    32bit:………………………………….

    2. PCM数据结构

    (1)关于音频数据流

    编码之后的PCM数据流是一串0和1的组合:0011000010101100……

    所以,就要根据一些相关信息来解析数据。假如采样位深是8bit,且是有符号的数据。那么数据解析如下:

    0011 0000    1010 1100……

    第一个字节,除掉最高位符号位后,值就是+48

    第二个字节,除掉最高位符号位后,值就是-44

    在范围-128~127之内。

    一般来说,常用的是16bit的位深,在C语言中用short类型(两字节)的数据来表示即可。

    2.1 单声道数据

    实例:读取一个32KHz,单声道,16bit的PCM音频文件数据到内存,并再次写入新文件。

    1. int main(int argc, char** argv)
    2. {
    3. int i, j, count;
    4. FILE *fpin = NULL;
    5. FILE *fpout = NULL;
    6. fpin = fopen("input.pcm", "rb"); //单声道
    7. fpout = fopen("output.pcm", "wb");
    8. fseek(fpin, 0, SEEK_END);
    9. long inputdata_length = ftell(fpin);
    10. inputdata_length /= 2;
    11. printf("input_file_len:% ld\n", inputdata_length);
    12. short *stream_in = (short *)malloc(inputdata_length * sizeof(short));
    13. //读出数据流(0/1数据流--->字节数据)
    14. rewind(fpin);
    15. count = fread(stream_in, sizeof(short), inputdata_length, fpin);
    16. printf("count=%d\n", count);
    17. //将数据写入文件
    18. fwrite(stream_in, sizeof(short), inputdata_length, fpout);
    19. fclose(fpin);
    20. fclose(fpout);
    21. free(stream_in);
    22. printf("process finished.\r\n");
    23. return 0;
    24. }

    注:

    (1)音频数据一般都有符号,可以直接打印出每一个采样点的音频数据:printf(“stream_in[%d]=%hd\n”, 100, stream_in[100]);

    (2) 16bit的深度,所以缓存空间的数据类型定义成short,刚好合适

    (3) 代码运行结束,生成的文件大小没变,可使用cooledit pro打开对比查看波形图

    2.2 多声道数据

    (1)数据排布

    对于多声道的数据流,声道数据会交叉排列。当然,这跟底层驱动程序相关,比如一些ADC采集模块,stereo数据排布就是这样的。

    第1个byte(左声道)/第2个byte(右)/第3个byte(左)/第4个byte(左)……

    (2)关于字节序

    数据是大端存储结构还是小端存储结构,一般使用的是小端存储结构(Little Endian)。如下图:

     实例:分离双声道PCM文件数据(LE/44100Hz/16bi/立体声)

    1. int main(int argc, char** argv)
    2. {
    3. int i, j, count;
    4. FILE *fpin = NULL;
    5. FILE *fp_l = NULL;
    6. FILE *fp_r = NULL;
    7. fpin = fopen("44100_2ch_stereo.pcm", "rb");
    8. fp_l = fopen("left.pcm", "wb");
    9. fp_r = fopen("right.pcm", "wb");
    10. fseek(fpin, 0, SEEK_END);
    11. int inputdata_length = ftell(fpin);
    12. inputdata_length = inputdata_length/2; //16bit
    13. printf("input_file_len:% d\n", inputdata_length);
    14. short *stream_in = (short *)malloc(inputdata_length * sizeof(short));
    15. short *stream_l = (short *)malloc((inputdata_length/2) * sizeof(short));
    16. short *stream_r = (short *)malloc((inputdata_length/2) * sizeof(short));
    17. //读出数据流(数据流--->字节数据)
    18. rewind(fpin);
    19. count = fread(stream_in, sizeof(short), inputdata_length, fpin);
    20. printf("count=%d\n", count);
    21. //分离左右声道数据
    22. int loop=inputdata_length/2;
    23. short *dat_ptr=stream_in;
    24. for(int i=0; i<loop; i=i+2) {
    25. stream_l[i]=*dat_ptr;
    26. stream_l[i+1]=*(dat_ptr+1);
    27. dat_ptr=dat_ptr+2;
    28. stream_r[i]=*dat_ptr;
    29. stream_r[i+1]=*(dat_ptr+1);
    30. dat_ptr=dat_ptr+2;
    31. }
    32. //将左右声道数据写入文件
    33. fwrite(stream_l, sizeof(short), inputdata_length/2, fp_l);
    34. fwrite(stream_r, sizeof(short), inputdata_length/2, fp_r);
    35. fclose(fpin);
    36. fclose(fp_l);
    37. fclose(fp_r);
    38. free(stream_in);
    39. free(stream_l);
    40. free(stream_r);
    41. printf("process finished.\r\n");
    42. return 0;
    43. }

    文件大小和数据量减小一半,左右声道均选择44100Hz的采样率进行播放,声音正常。

     

    2.3 小结

    (1)s16le

    s-----signed,就是有符号数据,根据采样位深度,可以知道数值范围,所以在音频处理的时候,需要注意数据是否溢出

    16----采样位深16bit

     le----小端存储

    (2)PC的声卡一般是16bit或者24bit

    (3)PCM不是音频文件格式,因此一般的音乐播放软件无法打开,需要保存为wav或者其他格式(加个文件头)才行。

    二、WAV音频文件格式

    关于wav音频文件格式不放在这里解析,算法处理过程中(实际上算法一般都是针对PCM原始编码格式进行),需要注意下面几点即可:

    1. 构成:文件头+数据部分。
    2. 文件头占44个字节,后面就是数据部分,读取音频数据时,要注意偏移。
    3. 音频部分的编码可以是PCM,也可以是其他格式。

    实例:读取wav音频文件数据,伪代码如下

    /* 01. 读取音频数据 */

    fp_in = fopen("clean.wav", "rb");

    fseek(fp_in, 0, SEEK_END);

    long clean_len = ftell(fp_in);

    clean_len = clean_len -44; //减掉文件头

    clean_len /= 2;

    printf("clean voice len:% ld\n", clean_len);

    short *clean_buf = (short *)malloc(clean_len * sizeof(short)); //申请音频数据存储空间

    rewind(fp_in);

    //先偏移,跳过文件头再读数据

    fseek(fp_in, 44, SEEK_SET);

    count = fread(clean_buf, sizeof(short), clean_len, fp_in);

    /* 02. 分帧,加窗,傅里叶变换到频域,计算 */

    /* 03. 还原到时域 */

    /* 04. 写入新文件(注意文件头部信息的写入) */

  • 相关阅读:
    2022/7/2做题总结
    go语言 中 new能初始化哪些类型?
    机器学习编译器的前世今生
    python考研志愿填报模拟系统vue
    手把手教你打造美观实用的家居微信小程序
    国产10米分辨率的卫星介绍、下载和处理教程
    构造函数、类成员、析构函数调用顺序
    vue同时校验多个表单
    搞定面试官 - 你可以介绍一下在 MySQL 中,哪些情况下索引会失效嘛?
    【面试题】智力题
  • 原文地址:https://blog.csdn.net/qq_40088639/article/details/126721834