多媒体基础知识之PCM数据

1.什么是PCM音频数据

PCM(Pulse Code Modulation)也被称为脉冲编码调制。PCM音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准的数字音频数据。

2.PCM音频数据是如何存储的

如果是单声道的音频文件,采样数据按时间的先后顺序依次存入(有的时候也会采用LRLRLR方式存储,只是另一个声道的数据为0),如果是双声道的话就按照LRLRLR的方式存储,存储的时候还和机器的大小端有关。大端模式如下图所示:

3.PCM音频数据中常用的专业术语

一般我们描述PCM音频数据的参数的时候有如下描述方式

44100HZ 16bit stereo: 每秒钟有 44100 次采样, 采样数据用 16 位(2字节)记录, 双声道(立体声);
22050HZ 8bit  mono: 每秒钟有 22050 次采样, 采样数据用 8 位(1字节)记录, 单声道;

44100Hz指的是采样率,它的意思是每秒取样44100次。采样率越大,存储数字音频所占的空间就越大。

16bit指的是采样精度,意思是原始模拟信号被采样后,每一个采样点在计算机中用16位(两个字节)来表示。采样精度越高越能精细地表示模拟信号的差异。

一般来说PCM数据中的波形幅值越大,代表音量越大。

4.PCM音频数据的处理

4.1.分离PCM音频数据左右声道的数据

因为PCM音频数据是按照LRLRLR的方式来存储左右声道的音频数据的,所以我们可以通过将它们交叉的读出来的方式来分离左右声道的数据

int simplest_pcm16le_split(char *url){
    FILE *fp=fopen(url,"rb+");
    FILE *fp1=fopen("output_l.pcm","wb+");
    FILE *fp2=fopen("output_r.pcm","wb+");
    unsigned char *sample=(unsigned char *)malloc(4);
    while(!feof(fp)){
        fread(sample,1,4,fp);
        //L
        fwrite(sample,1,2,fp1);
        //R
        fwrite(sample+2,1,2,fp2);
    }
    free(sample);
    fclose(fp);
    fclose(fp1);
    fclose(fp2);
    return 0;
}

4.2.降低某个声道的音量

因为对于PCM音频数据而言,它的幅值(即该采样点采样值的大小)代表音量的大小,所以我们可以通过减小某个声道的数据的值来实现降低某个声道的音量

int simplest_pcm16le_halfvolumeleft(char *url){
    FILE *fp=fopen(url,"rb+");
    FILE *fp1=fopen("output_halfleft.pcm","wb+");
    int cnt=0;
    unsigned char *sample=(unsigned char *)malloc(4);
    while(!feof(fp)){
        short *samplenum=NULL;
        fread(sample,1,4,fp);
        samplenum=(short *)sample;
        *samplenum=*samplenum/2;
        //L
        fwrite(sample,1,2,fp1);
        //R
        fwrite(sample+2,1,2,fp1);
        cnt++;
    }
    printf("Sample Cnt:%d\n",cnt);
    free(sample);
    fclose(fp);
    fclose(fp1);
    return 0;
}

4.3.将PCM音频数据转换成WAV格式

WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持。WAVE文件通常只是一个具有单个“WAVE”块的RIFF文件,该块由两个子块(”fmt”子数据块和”data”子数据块),它的格式如下图所示

该格式的实质就是在PCM文件的前面加了一个文件头,每个字段的的含义为

typedef struct{
       char          ChunkID[4];//内容为"RIFF"
       unsigned long ChunkSize;//存储文件的字节数(不包含ChunkID和ChunkSize这8个字节)
       char          Format[4];//内容为"WAVE"
   }WAVE_HEADER;
   typedef struct{
        char          Subchunk1ID[4];//内容为"fmt"
        unsigned long  Subchunk1Size;//存储该子块的字节数(不含前面的Subchunk1ID和Subchunk1Size这8个字节)
        unsigned short AudioFormat;//存储音频文件的编码格式,例如若为PCM则其存储值为1,若为其他非PCM格式的则有一定的压缩。
        unsigned short NumChannels;//通道数,单通道(Mono)值为1,双通道(Stereo)值为2,等等
        unsigned long  SampleRate;//采样率,如8k,44.1k等
        unsigned long  ByteRate;//每秒存储的bit数,其值=SampleRate * NumChannels * BitsPerSample/8
        unsigned short BlockAlign;//块对齐大小,其值=NumChannels * BitsPerSample/8
        unsigned short BitsPerSample;//每个采样点的bit数,一般为8,16,32等。
   }WAVE_FMT;
   typedef struct{
        char          Subchunk2ID[4];//内容为“data”
        unsigned long Subchunk2Size;//内容为接下来的正式的数据部分的字节数,其值=NumSamples * NumChannels * BitsPerSample/8
   }WAVE_DATA;

比如下面的例子

这里是一个WAVE文件的开头72字节,字节显示为十六进制数字: 
52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00 
22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00 
24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d 
字段解析:

代码实现为

int simplest_pcm16le_to_wave(const char *pcmpath,int channels,int sample_rate,const char *wavepath)
{
    typedef struct WAVE_HEADER{
        char         fccID[4];
        unsigned   long    dwSize;
        char         fccType[4];
    }WAVE_HEADER;
    typedef struct WAVE_FMT{
        char         fccID[4];
        unsigned   long       dwSize;
        unsigned   short     wFormatTag;
        unsigned   short     wChannels;
        unsigned   long       dwSamplesPerSec;
        unsigned   long       dwAvgBytesPerSec;
        unsigned   short     wBlockAlign;
        unsigned   short     uiBitsPerSample;
    }WAVE_FMT;
    typedef struct WAVE_DATA{
        char       fccID[4];
        unsigned long dwSize;
    }WAVE_DATA;
    if(channels==0||sample_rate==0){
    channels = 2;
    sample_rate = 44100;
    }
    int bits = 16;
    WAVE_HEADER   pcmHEADER;
    WAVE_FMT   pcmFMT;
    WAVE_DATA   pcmDATA;  

    unsigned   short   m_pcmData;
    FILE   *fp,*fpout;
    fp=fopen(pcmpath, "rb");
    if(fp == NULL) {
        printf("open pcm file error\n");
        return -1;
    }
    fpout=fopen(wavepath,   "wb+");
    if(fpout == NULL) {
        printf("create wav file error\n");
        return -1;
    }
    //WAVE_HEADER
    memcpy(pcmHEADER.fccID,"RIFF",strlen("RIFF"));
    memcpy(pcmHEADER.fccType,"WAVE",strlen("WAVE"));
    fseek(fpout,sizeof(WAVE_HEADER),1);
    //WAVE_FMT
    pcmFMT.dwSamplesPerSec=sample_rate;
    pcmFMT.dwAvgBytesPerSec=pcmFMT.dwSamplesPerSec*sizeof(m_pcmData);
    pcmFMT.uiBitsPerSample=bits;
    memcpy(pcmFMT.fccID,"fmt ",strlen("fmt "));
    pcmFMT.dwSize=16;
    pcmFMT.wBlockAlign=2;
    pcmFMT.wChannels=channels;
    pcmFMT.wFormatTag=1;  

    fwrite(&pcmFMT,sizeof(WAVE_FMT),1,fpout);
    //WAVE_DATA;
    memcpy(pcmDATA.fccID,"data",strlen("data"));
    pcmDATA.dwSize=0;
    fseek(fpout,sizeof(WAVE_DATA),SEEK_CUR);
    fread(&m_pcmData,sizeof(unsigned short),1,fp);
    while(!feof(fp)){
        pcmDATA.dwSize+=2;
        fwrite(&m_pcmData,sizeof(unsigned short),1,fpout);
        fread(&m_pcmData,sizeof(unsigned short),1,fp);
    }
    pcmHEADER.dwSize=44+pcmDATA.dwSize;
    rewind(fpout);
    fwrite(&pcmHEADER,sizeof(WAVE_HEADER),1,fpout);
    fseek(fpout,sizeof(WAVE_FMT),SEEK_CUR);
    fwrite(&pcmDATA,sizeof(WAVE_DATA),1,fpout);

    fclose(fp);
    fclose(fpout);
    return 0;
}

参考文章:

http://blog.csdn.net/leixiaohua1020/article/details/50534316

http://blog.csdn.net/u010011236/article/details/53026127

时间: 2024-10-26 04:19:40

多媒体基础知识之PCM数据的相关文章

多媒体基础知识之YUV数据

1.什么是YUV格式 YUV,是一种颜色编码方法.Y表示明亮度(Luminance.Luma),也就是灰度值.U和V则是色度.浓度(Chrominance.Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色.与我们熟知的RGB类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题.并且,YUV不像RGB那样要求三个独立的视频信

第二篇 python基础知识总结:数据、运算符

引子 我们跟任何人交流,说的每一句都是都一些文字组成,包含名词.动词.语句.标点符号等,组成我们说普通话构成的基本要素.同理我们学习python语言也要明白这些基本要素,也就是我们常说的基本语法,这是我们必须掌握的基础知识.下面我就根据我们常用的知识总结一下.对此内容不过多的讲解!我们需要的是多练!坚持练!反复练!着重强调! 一 变量与常量 任何一门计算机语言中,变量与常量都是首先要了解的最基础知识.变量就是变化的量,常量就是一个不变的量,一个常数.python语言中的变量不指定存储类型,这点与

六、多媒体基础知识

多媒体:对多种媒体的融合,能够同时采集.处理编辑.存储.展示2个及以上不同类型信息媒体的技术.这些信息媒体包括:文字.声音.图形.图像.动画.活动影像…… 1.多媒体的基本概念 2.声音 3.图形和图像 4.动画和视频 5.多媒体网络 6.多媒体计算机系统

数据通信的基础知识

数据通信的基础知识 典型的数据通信模型: 相关术语 通信的目的是传送消息. 数据(data)--运送消息的实体: 信号(signal)--数据的电气或电磁的表现: "数字信号"--代表消息的参数的取值是离散的: "模拟信号"--代表消息的参数的取值是连续的: 码元(code)--在使用时间域的波形标识数字信号时,则代表不同离散数值的基本波形就形成了码元: 在数字通信中常常使用时间间隔相同的符号来表示一个二进制数字,这样的时间间隔内的信号称为二进制码元.而这个间隔被称

Android基础知识(6)—数据持久化之数据存储

阅读前,请浏览此处上方目录. Android基础知识(6)-数据持久化之数据存储 本章内容为个人笔记,参考书籍有:<疯狂的android>第3版.<第一行代码> 首先,我们要知道什么是数据持久化. 数据持久化就是指那些内存中的瞬时数据保存到存储设备中,保证即使手机在关机的情况下,这些数据不会丢失.保存在内存中的数据是处于瞬时状态,保存在存储设备中的数据是处于持久状态.持久化技术则是提供了一种机制可以让数据在瞬时状态和持久状态之间进行转换. Android系统主要提供了三种方式用于简

SQL server基础知识(表操作、数据约束、多表链接查询)

SQL server基础知识 一.基础知识 (1).存储结构:数据库->表->数据 (2).管理数据库 增加:create database 数据库名称 删除:drop database 数据库名称 查询:select name from master..sysdatabases 修改:alter database 数据库名称(在 sql server 中修改与数据库关联的文件和文件组.在数据库中添加或删除文件和文件组.更改数据库或其文件和文件组的属性) (3).管理表 选择数据库:use 数

Wireshark数据抓包教程之Wireshark的基础知识

Wireshark数据抓包教程之Wireshark的基础知识 Wireshark的基础知识 在这个网络信息时代里,计算机安全始终是一个让人揪心的问题,网络安全则有过之而无不及.Wireshark作为国际知名的网络数据抓包和分析工具,可以广泛地应用各种领域,尤其是网络安全领域.借助Wireshark,网络安全工程师可以快速的从数据抓包中找出各种潜在的安全问题.本章将详细讲解Wireshark的简单使用. Wireshark简介 Wireshark(前称Ethereal)是一个网络封包分析软件.网络

iOS开发基础知识之数据下载

1数据下载 简介: (1)在本文中笔者将给大家带来一些网络基础知识的介绍,NSURLConnection从网络上下载数据的方式,以及如何利用利用同步,异步下载显示图片和数据. 1网络基础知识 什么是网络应用? 1 网络应用软件是用户利用软件开发平台,按照各自需要开发的各种各样的网上业务应用系统.常见的开发平台有各种数据库管理系统.办公自动化管理系统以及浏览器.网页制作网站管理等软件. 2客户端与服务端 网络应用不同于本地应用, 网络应用的数据是从网络上下载下来的, 所以需要在网络上运行一个程序为

学习大数据要什么基础知识?学到什么程度才能从事大数据行业?

因为大数据前景好,薪资高,很多人想通过参加学习大数据,然后进入大数据行业发展.但是因为大数据的门槛较高,对于学习人员有一定的要求,那么学习大数据需要什么基础知识呢? 首先我们了解下对于大数据学习者本身的学历水平的要求 目前大多数的机构,对于大数据学习者要求必须是大专学历以上,而且大专学历还要求是计算机相关专业的,如果是本科及本科以上的,则对专业要求适当的放宽.同时大数据分为两大方向:大数据开发和大数据分析. 这两大方向的对于基础知识的要求不同,大数据分析偏向于分析,对于编程要求不高,相较而言对于