wav文件的采样位数从16位转换到8位的C语言实现

前言

今天来对自己找到的一份网上的有关将wav文件的采样位数从16位转化成8位的程序进行解析,通过分析后加入到自己的程序中,从而完成一个自己毕设软件中的一个功能。

程序解析及源码

函数学习

首先对程序中遇到的新函数进行学习,便于后面对于程序的理解。

malloc与free函数

由于malloc函数与free函数一般都是连用的,故而放在一起进行说明。
作用:malloc函数用于向操作系统申请一片内存,free函数对应的是释放这片申请的内存,如果申请后不释放会造成内存泄漏的错误,故而free函数是必要的。

头文件: #include <stdlib.h>
函数原型:
void *malloc(size_t size);
void free(void *ptr);
参数:
malloc:size是指需要分配的字节数。
free:ptr-- 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
返回值:
malloc:分配成功,则返回一个指向分配空间的指针。失败返回NULL指针。
free:void类型,不返回任何值。

memcpy函数

作用:C和C++使用的内存拷贝函数。从源source所指的内存地址的起始位置开始拷贝n个字节到目标destin所指的内存地址的起始位置中。
头文件: #include <string.h>
函数原型
void *memcpy(void destin, void source, unsigned n);
参数
destin: 指向用于存储复制内容的目标数组,类型强制转换为 void
指针。
source:指向要复制的数据源,类型强制转换为 void
指针。
n: 要被复制的字节数。
返回值
该函数返回一个指向目标存储区destin的指针。

feof函数

作用:检测流上的文件结束符。文件结束:返回非0值,文件未结束,返回0值。
头文件: #include <stdio.h>
函数原型
int feof(FILE *stream);
参数
stream:FILE结构的指针
返回值
文件结束:返回非0值,文件未结束,返回0值。

PS:feof判断文件结束是通过读取函数fread/fscanf等返回错误来识别的,故而判断文件是否结束应该是在读取函数之后进行判断。比如,在while循环读取一个文件时,如果是在读取函数之前进行判断,则如果文件最后一行是空白行,可能会造成内存错误。

源码解析

下面附上我注释过的源码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* wav音频头部格式 */
typedef struct _wave_pcm_hdr
{
	char            riff[4];                // = "RIFF"
	int				size_8;                 // = FileSize - 8
	char            wave[4];                // = "WAVE"
	char            fmt[4];                 // = "fmt "
	int				fmt_size;		// = 下一个结构体的大小 : 16

	short       	format_tag;             // = PCM : 1
	short       	channels;               // = 通道数 : 1
	int				samples_per_sec;        // = 采样率 : 8000 | 6000 | 11025 | 16000
	int				avg_bytes_per_sec;      // = 每秒字节数 : samples_per_sec * bits_per_sample / 8
	short       	block_align;            // = 每采样点字节数 : wBitsPerSample / 8
	short       	bits_per_sample;        // = 量化比特数: 8 | 16

	char            data[4];                // = "data";
	int				data_size;              // = 纯数据长度 : FileSize - 44
} wave_pcm_hdr;

int wave_16bit_to_8bit(char *source_file,char *dest_file)
{
	short *data_16 = NULL ;  //short型的指针
	char data_8 = 0 ; //8位的数据
	unsigned char mdata_8 = 0 ; //8位写入数据
	unsigned char* dstemp = (unsigned char*)malloc(4);  //向操作系统申请4字节的内存空间,返回一个unsigned char的指针
	FILE *pInput = NULL , * pOutput = NULL ;  //操作文件的指针

	wave_pcm_hdr wave_header_16 = {0}; //定义并初始化16位采样的wav文件的文件头
	wave_pcm_hdr wave_header_8 = {0};  //同上,8位
	
	//打开文件
	if( strcmp(source_file,dest_file) == 0 ) 
	{
		printf("源文件名与目标文件名同名了;\n");
		return -2 ;
	}

	pInput = fopen(source_file,"rb");
	pOutput = fopen(dest_file,"wb");
	if(pInput == NULL || pOutput == NULL)
	{
		printf("打开源文件或打开目标文件失败;\n");
		return -1 ;
	}

	fread(&wave_header_16,1,44,pInput); //读取原本16位采样的wav文件头
	printf("bits_per_sample:%d;\n",wave_header_16.bits_per_sample);//采样位数16
	
	memcpy(&wave_header_8,&wave_header_16,sizeof(wave_pcm_hdr)); //将16位的wav文件头数据拷贝到8位里面去
	wave_header_8.size_8 = (wave_header_16.size_8+8)/2 + 14;  //根本不用变。去掉此行也可以
	wave_header_8.avg_bytes_per_sec = 8000; //每秒字节数8000
	wave_header_8.block_align = 1 ; //采样帧大小1字节
	wave_header_8.bits_per_sample = 8 ;//改为8 ,上面的值也需要随之改变
	wave_header_8.data_size = wave_header_16.data_size/2 ; //data区数据变为16位的一半

	fwrite(&wave_header_8,1,sizeof(wave_pcm_hdr),pOutput); //将文件头写入目标文件

	while(!feof(pInput)) //循环检测是否到了文件尾
	{
		fread(dstemp,1,4,pInput); // 每次从源文件读取4字节data数据到dstemp,前面fread已经读取了44字节头部,因此直接到了data区

		//每次将两字节数据转化为1字节数据存到输出文件中
		data_16 = (short*)dstemp; //将dstemp强转为short指针再
		data_8 = (*data_16) >> 8; //将2字节数据右移8位,就是清除1字节数据,让原本16位单声道的高8位清除,再赋值给8位的data
		mdata_8 = data_8 + 128 ;  //要将data_8+128才赋值给mdata_8是???,应该不加也可以
		printf("%d\n",mdata_8);
		fwrite(&mdata_8,1,1,pOutput);

		data_16 = (short*)(dstemp + 2);
		data_8 = (*data_16) >> 8;
		mdata_8 = data_8 + 128 ;
		fwrite(&mdata_8,1,1,pOutput);
	}

	fclose(pOutput);
	fclose(pInput);
	return 0 ;
}


int main(int argc , char **argv)
{
	wave_16bit_to_8bit("tts_sample.wav","tts_sample.wav");
	return 0 ;
}

简单的说,上面的代码简单的通过将16位wav文件采样数据右移8位,清除了高8位的数据,再将低8位数据赋值给了8位wav文件的采样数据,完成了wav文件16位到8位的变化。其中源码作者有将每个得到的8位data数据加了128,我觉得不加也可以,这要在后面的测试中发现问题了。今天的文章先写到这里,下篇文章,尝试将此功能添加到我的毕设软件中。

发布了19 篇原创文章 · 获赞 2 · 访问量 837

猜你喜欢

转载自blog.csdn.net/aa98865646/article/details/104971407