论wav文件格式和wav读写(c代码)

老调重谈的话题。

基本上标准的44个字节的头,对照这个表格就可以了

两个记录大小的地方是 相差36 2084 = 2048 +36

但也有较少的超过44个字节头的,比如这个:

这篇文章说的很清楚

https://sites.google.com/site/musicgapi/technical-documents/wav-file-format

还有其他的chunkID

多出来的,非44个自己的头,就是这些ID搞的鬼。我们搞信号处理的,不是很在乎其他的额外信息,那么直接对这些数据跳过去即可

他们都遵循着 4个字节的 chunkID,4个字节的 Chunk DATAsize,我们fread读到这里,直接过去即可

    if(!((header->data_tag[0]=='d')
        &&(header->data_tag[1]=='a')
        &&(header->data_tag[2]=='t')
        &&(header->data_tag[3]=='a')))
    {
        printf("Chunck ID: %s detected\n",header->data_tag);

        printf("Non-Data Chunk detected\n");
        fseek(fp,-4,SEEK_CUR);

        do{
            fread(&ChunkLen,1,4,fp);//读出来 长度
            fseek(fp,ChunkLen,SEEK_CUR);//直接跳过拉倒

            fread(&c1,1,1,fp);
            fread(&c2,1,1,fp);
            fread(&c3,1,1,fp);
            fread(&c4,1,1,fp);

            printf("Chunck ID: %c%c%c%c detected\n",c1,c2,c3,c4);
            
        }
        while(!((c1=='d')&&(c2=='a')&&(c3=='t')&&(c4=='a')));        

        header->data_tag[0] = 'd';
        header->data_tag[1] = 'a';
        header->data_tag[2] = 't';
        header->data_tag[3] = 'a';

        fread(&header->data_length,sizeof(header->data_length),1,fp);
    }

对应Matlab有一个wavread2专门处理这个

https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/46271/versions/1/previews/wavread2.m/index.html

处理一下上述文件

附加信息被记录到info里面

//wave 写
typedef struct WaveHeader
{
    char  riff_id[4];                       //"RIFF"
    int   riff_datasize;                   // RIFF chunk data size,exclude riff_id[4] and riff_datasize,total - 8

    char  riff_type[4];                     // "WAVE"

    char  fmt_id[4];                        // "fmt "
    int   fmt_datasize;                     // fmt chunk data size,16 for pcm
    short fmt_compression_code;             // 1 for PCM
    short fmt_channels;                     // 1(mono) or 2(stereo)
    int   fmt_sample_rate;                  // samples per second
    int   fmt_avg_bytes_per_sec;            // sample_rate * channels * bit_per_sample / 8
    short fmt_block_align;                  // number bytes per sample, bit_per_sample * channels / 8
    short fmt_bit_per_sample;               // bits of each sample(8,16,32).

    char  data_id[4];                       // "data"
    int   data_datasize;                    // data chunk size,pcm_size - 44
}WaveHeader_t;

//快速写wav文件,单声道16khz


char header[]= {82,73,70,70, //"RIFF"
                0x37,0x00,0x00,0x00,//FileSize-8
                87,65,86,69,//"WAVE"
                102,109,116,32,//"fmt "
                16,0,0,0,//数据段16bit
                1,0,//PCM格式
                1,0,//单声道
                0x80,0x3e,0x00,0x00,//采样率16kHz
                0x00,0x7d,0x00,0x00,//音频码率=采样率*通道数*bit数/8                    
                2,0,//采样一次占2个字节
                16,0,//每一个通道16bit数据
                100,97,116,97,//"data"
                0x01,0x00,0x00,0x00//数据长度    6s
               };
             

FILE *fp1,*fp2; // File pointers
int i;

printf("Exp. 1.2 --- file IO\n");

fp1 = fopen("1.wav", "rb"); // Open input file
fp2 = fopen("2.wav","wb");

if (fp1 == NULL) // Check if the input file exists
{
printf("Failed to open input file \n");
exit(0);
}

fseek(fp1, 44, 0);

fwrite(header,sizeof(header),1,fp2);


i=0;

while (fread(ch, sizeof(Uint8), SIZE, fp1) == SIZE) // Read in SIZE of input data bytes
{
fread(ch, sizeof(Uint8), SIZE, fp1);
fwrite(ch, sizeof(Uint8), SIZE, fp2); // Write SIZE of data bytes to output file
i += SIZE;
printf("%ld bytes processed\n", i); // Show the number of data is processed
}


waveHeader[40] = (Uint8)(i&0xff); // Update the size parameter into WAV header
waveHeader[41] = (Uint8)(i>>8)&0xff;
waveHeader[42] = (Uint8)(i>>16)&0xff;
waveHeader[43] = (Uint8)(i>>24)&0xff;

i = i +36;
waveHeader[4] = (Uint8)(i&0xff);
waveHeader[5] = (Uint8)(i>>8)&0xff;
waveHeader[6] = (Uint8)(i>>16)&0xff;
waveHeader[7] = (Uint8)(i>>24)&0xff;

rewind(fp2); // Adjust output file point to beginning
fwrite(waveHeader, sizeof(Uint8), 44, fp2); // Write 44 bytes of WAV header to output file

fclose(fp1); // Close input file
fclose(fp2); // Close output file

printf("\nExp --- completed\n");

//--------------------------- 简化写法

    char header[] = { 82,73,70,70, //"RIFF"
                0x37,0x00,0x00,0x00,//FileSize-8
                87,65,86,69,//"WAVE"
                102,109,116,32,//"fmt "
                16,0,0,0,//数据段16bit
                1,0,//PCM格式
                1,0,//单声道
                0x80,0x3e,0x00,0x00,//采样率16kHz
                0x00,0x7d,0x00,0x00,//音频码率=采样率*通道数*bit数/8                    
                2,0,//采样一次占2个字节
                16,0,//每一个通道16bit数据
                100,97,116,97,//"data"
                0x01,0x00,0x00,0x00//数据长度    6s
    };

//打开

    FILE* fp = fopen(wavfile,"wb");
    if (!fp) return;

//写头

fwrite(header, sizeof(header), 1, fp);

//写数据,len 是长度(字节数)

fwrite(data,sizeof(data),1,fp);........

//重新写入数据长度字节数,

    int len = size;
    fseek(fp, 40, SEEK_SET);
    fwrite(&len, sizeof(int), 1, fp); 
    len = size + 36;
    fseek(fp, 4, SEEK_SET);
    fwrite(&len,sizeof(int),1,fp);

//关闭文件

fclose(fp);

/---------------------------------------------------------------------------/

简单读wav文件的代码,尤其对付 不是标准44字节头的wav

    if( filename == NULL) return false;
    
    FILE* fp_wave = fopen(filename,"rb");
    if(fp_wave == NULL) return false;
    
    fseek(fp_wave,22L,0);//移动到声道数位置
    short temp1;
    fread(&temp1,1,sizeof(short),fp_wave);
    if(!(temp1 == REC_CHANNEL_NUM)) return false;
    
    fseek(fp_wave,24L,0);//移动到采样率位置
    long temp2;
    fread(&temp2,1,sizeof(long),fp_wave);
    if(temp2 != SAMPLE_RATE) return false;

    rewind(fp_wave);

    //专门死磕wave的头不是标准44个字节的情况
    while(!feof(fp_wave))//seek到data后面的真实数据
    {
        short temp;
        fread(&temp,1,sizeof(short),fp_wave);
        if(temp==24932)//'data'的‘d’‘a’
        {
            fread(&temp,1,sizeof(short),fp_wave);// 把‘t’’a‘也读了
            fread(&temp,1,sizeof(short),fp_wave);//把后面的data-len也读了
            fread(&temp,1,sizeof(short),fp_wave);
            break;
        }
    }
    //它其实可以直接用下面的这句代替,当然必须是标准头
    //fseek(fp_wave,44L,0);//标准44字节头
    
    short TmpXChframe[FIFO_LEN*REC_CHANNEL_NUM];
    int n_frame =0;

    while(!feof(fp_wave))//读入真实的pcm数据
    {
        //每次读入 FIFO_LEN 个数据,1 帧 chanN 声道的数据
        //读取指定长度的数据
        fread(TmpXChframe,REC_CHANNEL_NUM * FIFO_LEN,sizeof(short),fp_wave);

        //处理

        n_frame++;

   }

fclose(fp_wav);

猜你喜欢

转载自blog.csdn.net/book_bbyuan/article/details/110188074