基于C的PCM音频数据的读取、处理与写入(一)

PCM音频数据,是模拟音频信号经过数模转换后直接形成的二进制序列,是一种罕见的音频文件格式,因此在对pcm文件进行读写的时候,要选用以二进制的形式打开。
此次读写的PCM文件,单声道,采样率是16KHz,分辨率是16位,也就是2个字节的长度。
在程序编写之前,要考虑一个问题,pcm文件内的数据为short型,我们调整音量倍数可能是float型,因此两者相乘后值为float型,若强行进行取整运算,则会对数据产生较大影响,即引入了较大噪声,因此我们通过以下流程进行实现:short_in→float_in→float_in*float_vol=float_out→short_out。
在short<—>float的转换中,我们利用了归一化的思想,通过几组数据,验证得出他们之间的替换关系,如下:

*short型数据,在人为查看时,并不能直观判定该数据大小,即并不知晓其占满量程的百分比,因此通常归一化为float数据,
    那如何进行shortfloat之间的转换那,可以用以下代码来验证,最后得出两个常量值,当short为负数时,除以32768,且要乘以-1,
    当short为正数时,除以32767,就可将short转换为float,当需要存储到文件的时候,再乘以相应的数值,转换为short后进行存储。*/
    
    const short MAX_VOL_S16_N = 32768;   //16Bit满量程转换
    const short MAX_VOL_S16_P = 32767;
    
  //  以下代码进行了short-float之间的转换
/*
    short test_a = 32767;
    float test_b = 0.5;
    short test_c = -32768;
    float test_d = -0.5;

    short tmp_s;
    short tmp_s2;
    float tmp_f;
    float tmp_f2;
    
      
    tmp_s=(short)(test_b*MAX_VOL_S16_P);
    tmp_s2=(short)(test_d*MAX_VOL_S16_N);

    tmp_f=(float)test_a/MAX_VOL_S16_P;
    tmp_f2=(float)test_c/MAX_VOL_S16_N;
 
    printf("%d\n",tmp_s); //运行结果为16383,符合预期
    printf("%f\n",tmp_f);//运行结果为1,符合预期
    printf("%d\n",tmp_s2);//运行结果为16384,不符合预期
    printf("%d\n",(short)(-1*test_d*MAX_VOL_S16_N));  //运行结果为-16383,符合预期
    printf("%f\n",tmp_f2);//运行结果为1,不符合预期
    printf("%f\n",(float)-1*test_c/MAX_VOL_S16_N); //运行结果为-1,符合预期
    */
    /*综上,4个可实现的变换中,有两个需要将待变换内容扩起来,可能和运算符的优先级有关,后续再探究。
    当待转换值为负值时,均需要乘以-1*/

得到上述关系后,下一步将进行pcm文件读取,并通过更改volume_adjust实现音量的调节,调节后的数据写入到另一PCM中,通过ocenaudio,可查看其幅值,以作验证。

//PCM音频文件,采样率16KHZ,分辨率16位,读取文件并调节音量即幅值后另存到另一PCM文件中。

#include
#include



int main()
{   
const short MAX_VOL_S16_N = 32768;   //16Bit满量程转换
const short MAX_VOL_S16_P = 32767;
//打开文件,读取,操作,并存入另一文件
    short in_data;
    short out_data;
    float in_data_f;
    float out_data_f;
    float volume_adjust=0;

    FILE *fp_in = fopen("file.pcm","rb");  //r是打开文本文件,rb是打开二进制文件
    FILE *fp_out=fopen("out.pcm","wb+");  //pcm文件是模拟音频信号经过数模转换后直接形成的二进制序列。这是一种十分罕见的音频文件格式。

    printf("请输入音量调节系数:");
    scanf("%f",&volume_adjust);
   
    while(!feof(fp_in))   //当文件不是文件尾时均执行后续代码
    {
         fread(&in_data,2,1,fp_in);
         
         if(in_data>=0)
         {
             in_data_f=(float)in_data/MAX_VOL_S16_P;
         }
         else
         {
         
             in_data_f=(float)-1*in_data/MAX_VOL_S16_N;
         }
        // printf("%f\n",in_data_f);
         
         out_data_f= in_data_f*volume_adjust; 
      
        if(out_data_f>=0)
            {
                out_data=(short)(out_data_f*MAX_VOL_S16_P);
             }
        else
            {
                out_data=(short)(-1*out_data_f*MAX_VOL_S16_N);
            }
     if(out_data>32767)
        {
            out_data = 32767;
        }
      else if(out_data<-32768)
        {
            out_data = -32768;
        }
      else
      {
          out_data = out_data;
      }
        //printf("%d",out_data);

        fwrite(&out_data,2,1,fp_out);
         printf("%d\t%f\t%d\t%f\t%f\n",in_data,in_data_f,out_data,out_data_f,volume_adjust);

     }
    
   
    fclose(fp_in);
    fclose(fp_out);
   

    return 0;

}


以上可基本实现要求的功能,有几个问题还需继续探究:
1、后续使用fopen_s替代fopen
2、由short-float转换式中出现的是否添加括号引申出的运算符的优先级的问题
3、传入指针参数时的保护
4、结构体的用法
5、左移右移操作
以上几个问题,将会结合下一篇代码进行分析。

你可能感兴趣的:(VS2008(C++),c++,c语言,算法)