首页程序设计LinuxC程序C/C++修改wav文件采样率实现

C/C++修改wav文件采样率实现

时间2024-03-07 17:00:58发布caterwang分类LinuxC程序浏览998

    将一个采样率为44100的wav文件,改为11025的采样率。

    为了实现以上目标,我们需要储备一些关于wav及PCM数据的相关知识,以下是笔者学习参考的网站地址:


        PCM音频数据处理---降低采样率        PCM数据格式        C++标准库实现WAV文件读写 


     以下我基于项目需要写的代码,实现将采样率为44100的wav文件,改为11025的采样率,并重新生成同名文件。系统环境是centos7 x64。

    1.wavEdit.h

#ifndef _WAVEDIT_H_
#define _WAVEDIT_H_

typedef unsigned char               uint8_t;
typedef signed char                 int8_t;
typedef unsigned short int          uint16_t;
typedef signed short int            int16_t;
typedef unsigned int                uint32_t;
typedef signed int                  int32_t;

#pragma pack(push, 1)
typedef struct  
{
	int8_t 		chunk_id[4]	= { 0 };		// 文件标识,通常为"RIFF"
	uint32_t 	chunk_size 	= 0;			// 文件数据大小,该值 + 8 就是整个WAV文件的大小
	int8_t 		format[4]	= { 0 };		// 文件格式,“WAVE”
	int8_t 		fmt_chunk_id[4]	= { 0 };		// 子块标识,“fmt”
	uint32_t 	fmt_chunk_size 	= 0;			// 子块大小,其数值不确定,取决于编码格式。可以是16、18、20、40 等
	uint16_t 	audio_fomat  	= 0;			// 音频格式,1为PCM格式
	uint16_t 	num_channels 	= 0;			// 声道数,1:单声道,2:双声道(立体声)
	uint32_t 	sample_rate    	= 0;			// 采样率,常用的采样频率有 11025,22050和44100KHZ
	uint32_t 	byte_rate      	= 0;			// 每秒的字节数,值=声道数x采样率x每样本的数据位数/8。播放软件利用这个值可以估计缓冲区大小。
	uint16_t 	block_align  	= 0;			// 块对齐 采样帧大小。该数值为:声道数x位数/8。播放软件需要一次处理多个该值大小的字节数据,用该数值调整缓冲区
	uint16_t 	bits_per_sample = 0;			// 采样深度,即采样位数。常见是4、8、12、16、24、32
	int8_t 		data_chunk_id[4]	= { 0 };	// 子块标识,“data”
	uint32_t	data_chunk_size   	= 0;		// 子块大小
	int32_t		num_frame = 0;					// 文件总帧数
	int32_t		start_pos = 0;					// 音频数据的开始位置
}wavHead;

#pragma pack(pop)

void wavFormate(char* filename,wavHead &wh);

#endif


    2.wavEdit.cpp

#include <stdio.h>
#include <stdlib.h>
#include "wavEdit.h"

char* read_File_rb(const char * filename,int &datalen)
{
	int size=16;
	int count = 1;

	char * outdat = NULL;
    
	//取文件大小
	struct stat statbuf;
	int ret = stat(filename,&statbuf);	//调用stat函数

    if(ret != 0)					//获取失败。
	{
		return outdat;
	}

    datalen = statbuf.st_size;		//返回文件大小。	
	
	FILE* fp = fopen(filename, "rb");
    if (NULL != fp) 
	{
		outdat = (char *)malloc(datalen+size);
		memset(outdat,0,datalen+size);
		
		fread((void*)outdat,(size_t)datalen,1,fp);
		
        fclose(fp);
        return outdat;
    } 
	else 
	{
        return outdat;
    }

}

void wavHeadFormat(wavHead &wh,int nSampleRate,int ndata_chunk_size)
{
	wh.sample_rate 		= nSampleRate;
	wh.byte_rate   		= wh.num_channels*wh.sample_rate*wh.bits_per_sample/8;
	wh.data_chunk_size 	= ndata_chunk_size;								//子块大小,PCM数据大小
	wh.chunk_size      	= ndata_chunk_size+wh.start_pos-8;				// 文件数据大小,该值 + 8 就是整个WAV文件的大小
}

/*------------------------------------------
* filename 文件名称路径(含路径)
* wh: 用来保存文件头的结构体实例
--------------------------------------------*/
void wavFormate(char* filename,wavHead &wh) 
{
	//2.文件头分析
	const int HEAD_LENGTH = 256 * 1024;//256kb
	char buf[HEAD_LENGTH];

	//memcpy(buf,fileBuf,HEAD_LENGTH);
	
	FILE *stream = fopen(filename, "rb");
	fread(buf, 1, HEAD_LENGTH, stream);	
	fclose (stream);
	//记录文件读取位置
	int pos = 0;


	//寻找“RIFF”标记
	while (pos < HEAD_LENGTH) {
		if ((buf[pos] == 'R') && (buf[pos + 1] == 'I') && (buf[pos + 2] == 'F') && (buf[pos + 3] == 'F')) {
			wh.chunk_id[0] = 'R';
			wh.chunk_id[1] = 'I';
			wh.chunk_id[2] = 'F';
			wh.chunk_id[3] = 'F';
			pos += 4;
			break;
		}
		++pos;
	}

	//读取Header部分
	wh.chunk_size = *(int *)&buf[pos];
	pos += 4;
	wh.format[0] = buf[pos];
	wh.format[1] = buf[pos + 1];
	wh.format[2] = buf[pos + 2];
	wh.format[3] = buf[pos + 3];
	pos += 4;


	//寻找“fmt”标记
	while (pos < HEAD_LENGTH) {
		if ((buf[pos] == 'f') && (buf[pos + 1] == 'm') && (buf[pos + 2] == 't')) {
			wh.fmt_chunk_id[0] = 'f';
			wh.fmt_chunk_id[1] = 'm';
			wh.fmt_chunk_id[2] = 't';
			wh.fmt_chunk_id[3] = ' ';	//这个可别漏了哦
			pos += 4;
			break;
		}
		++pos;
	}

	//读取Format Chunk部分
	wh.fmt_chunk_size = *(int *)&buf[pos];
	pos += 4;
	wh.audio_fomat = *(short *)&buf[pos];
	pos += 2;
	wh.num_channels = *(short *)&buf[pos];
	pos += 2;
	wh.sample_rate = *(int *)&buf[pos];
	pos += 4;
	wh.byte_rate = *(int *)&buf[pos];
	pos += 4;
	wh.block_align = *(short *)&buf[pos];
	pos += 2;
	wh.bits_per_sample = *(short *)&buf[pos];
	pos += 2;


	//寻找“data”标记
	while (pos < HEAD_LENGTH) {
		if ((buf[pos] == 'd') && (buf[pos + 1] == 'a') && (buf[pos + 2] == 't') && (buf[pos + 3] == 'a'))
		{
			wh.data_chunk_id[0] = 'd';
			wh.data_chunk_id[1] = 'a';
			wh.data_chunk_id[2] = 't';
			wh.data_chunk_id[3] = 'a';
			pos += 4;
			break;
		}
		++pos;
	}

	//读取Data Chunk的非data部分
	wh.data_chunk_size = *(int *)&buf[pos];
	pos += 4;

	//记录真正音频数据的开始位置
	wh.start_pos = pos;

	//计算文件总帧数
	wh.num_frame = wh.data_chunk_size / (wh.num_channels*(wh.bits_per_sample / 8));
	printf("[0]sample_rate(%d),byte_rate(%d),data_chunk_size(%d),chunk_size(%d),fmt_chunk_size(%d),start_pos(%d) @ %s.",\
		wh.sample_rate,		\
		wh.byte_rate,		\
		wh.data_chunk_size,	\
		wh.chunk_size,wh.fmt_chunk_size,wh.start_pos,\
		filename);
	//3.数据转写-
	//https://www.cnblogs.com/wangguchangqing/p/5970516.html
	if(wh.num_channels==2 && wh.sample_rate>11025 && wh.bits_per_sample==16)
	{	
		int rate = wh.sample_rate/11025;
		if(rate>1)
		{
			//读取文件原始数据
			int fileLen;
			char *fileBuf = read_File_rb(filename,fileLen); 
		
			FILE* fp = fopen(filename, "wb+");	
		    if (NULL != fp) 
			{
				//写文件头
				fwrite(wh.chunk_id, sizeof(wavHead)-8, 1, fp);
				int16_t temp;
				int32_t tempSumL 	= 0;
				int32_t tempSumR 	= 0;
				int flag          	= 0;
				int data_chunk_size = 0;	//修改后PCM数据长度,单位字节
				
				uint16_t *pdata = (uint16_t *)(fileBuf+wh.start_pos);		//转成16位数据
				int max 	    = (wh.data_chunk_size/2)-rate*2;				
				for(int i=0;i<=max;i+=(rate*2))
				{
					flag	 = 0;
					tempSumL = 0;
					tempSumR = 0;
					
					while(flag<rate)
					{
						if(flag%2==0)
						{
							temp = pdata[i+flag];
							tempSumL += temp;
						}
						else
						{
							temp = pdata[i+flag+1];
							tempSumR += temp;
						}
						flag++;
					}

					temp = (uint16_t)(tempSumL/rate);
					fwrite(&temp, 2, 1, fp); 
					
					temp = (uint16_t)(tempSumR/rate);
					fwrite(&temp, 2, 1, fp); 
					
					data_chunk_size+=4;					
				}	
				fseek(fp,0,SEEK_SET);	//定位到文件起始位置
				//重写文件头
				wavHeadFormat(wh,11025,data_chunk_size);
				fwrite(wh.chunk_id, sizeof(wavHead)-8, 1, fp);					
		                fclose(fp);
				printf("[1]sample_rate(%d),byte_rate(%d),data_chunk_size(%d),chunk_size(%d),fmt_chunk_size(%d),start_pos(%d) @ %s.",\
					wh.sample_rate,		\
					wh.byte_rate,		\
					wh.data_chunk_size,	\
					wh.chunk_size,wh.fmt_chunk_size,wh.start_pos,\
					filename);	
		    }
			//内存释放
			free(fileBuf);
			fileBuf=NULL;			
		}
	}
}

3.测试引用

#include "wavEdit.h"
int main(int argc, char* argv[])
{
    char m_audioFile[256]="xx.wav";
    wavHead wh;
    // 执行完志后,m_audioFile 所指向的文件已经完成更新。
    wavFormate(m_audioFile, wh);
    
    return 0;
}

以上是完整的实现过程。

凯特网版权声明:以上内容允许转载,但请注明出处,谢谢!

展开全文READ MORE
wav文件采样率C++音频文件处理
运放电路之电压比较器 基于运放的水位检测电路

游客 回复需填写必要信息