wav文件1分析
wav文件2分析
注:
00-03 52 49 46 46:RIFF的标志
04-07 24 90 01 00 :文件的长度
08-0B 57 41 56 45 :WAVE的标志
0C-0F 66 6D 74 20 :fmt的标志
10-13 10 00 00 00 :编码的格式类别,10H代表PCM形式。
14-15 01 00 :字块总数
16-17 01 00 : 通道数 1为单声道,2为双声道
18-1B 80 3E 00 00 :采样率:每秒采集二进制的位数。16000Hz
1C-1F 00 7D 00 00 :每秒播放的字节数 = 通道数 x 每秒采集二进制的位数 x 采集数据的位数 / 8. 32000
20-21 02 00 :每个样点的字节数
22-23 01 00 :每个样点的数据位数
24-27 64 61 74 61 :data的标志
28-2b 00 90 01 00 : 文件长度 3200KB
12位dac播放16位音频数据
一般WAV数据开始的第44字节开始就是wav数据,把wav数据取出后直接送到DAC播放。这里的WAV的dac数据是16bit的需要进行转换成12bit(stm32是12bit的DAC,所以整体需要右移4位,损失4bit的精度,保留大头)
//方法1:
buffer[i] = buffer[i]+0x8000;//16bit dac 数据为补码形式的,需要进行处理+0x8000
buffer[i] = buffer[i] >>num; //num 一般为4 右移4位剩下12bit dac数据
//方法二:
(buffer[0]+(buffer[1]-0x80)*256)>>4)//高位用补码形式保存的,转换时需要先减去0x80
//或者
tmpCap = (((u8)(buffer[1] - 0x80) << 4) | (buffer[0] >> 4));
设置time定时器的频率16K的采集率,每秒定时中断1/16000,在中断中填充音频数据到dac接口,即可实现普通dac播放音频文件!
注意:
【1】用二进制补码表示的音频数据(MCU或者计算机数值⼀律⽤补码来表⽰和存储),数据有正负,所以需要转换;
【2】有符号数据的模拟量波形是在0值附近上下波动的,所以需要向上平移0x8000,后再取高12位的数据送到 12位的DAC里面 !
//测试1:
s16 data1 = 1000;
s16 data2 = -1000;
printf("data = %x,%x\n",data1,data2);
printf("----------------------------------------------\n\n");
u8 wav_data1[2] = {0xe8,0x03};//1000
u8 wav_data2[2] = {0x18,0xfc};//-1000
u16 dac_data1 =(((u8)(wav_data1[1]+0x80)<<8)|(wav_data1[0]));
printf("wav_data1 = %d,%x\n",*(s16*)wav_data1,*(s16*)wav_data1);
printf("dac_data1 = %d,%x\n",dac_data1,dac_data1);
u16 tmp_data =(((u8)(wav_data1[1])<<8)|(wav_data1[0])) + 0x8000;
printf("tmp_data = %d,%x\n",tmp_data,tmp_data);
u16 dac_data2 = (((u8)(wav_data2[1]+0x80)<<8)|(wav_data2[0]));
printf("----------------------------------------------\n\n");
printf("wav_data2 = %d,%x\n",*(s16*)wav_data2,*(s16*)wav_data2);
printf("dac_data2 = %d,%x\n",dac_data2,dac_data2);
tmp_data =(((u8)(wav_data2[1])<<8)|(wav_data2[0])) + 0x8000;
printf("tmp_data = %d,%x\n",tmp_data,tmp_data);
//测试2:
s16 data1 = 1000;
s16 data2 = -1000;
printf("data = %x,%x\n",data1,data2);
printf("----------------------------------------------\n\n");
u8 wav_data1[2] = {0xe8,0x03};//1000
u8 wav_data2[2] = {0x18,0xfc};//-1000
u16 dac_data1 =(((u8)(wav_data1[1]-0x80)<<8)|(wav_data1[0]));
printf("wav_data1 = %d,%x\n",*(s16*)wav_data1,*(s16*)wav_data1);
printf("dac_data1 = %d,%x\n",dac_data1,dac_data1);
u16 tmp_data =(((u8)(wav_data1[1])<<8)|(wav_data1[0])) - 0x8000;
printf("tmp_data = %d,%x\n",tmp_data,tmp_data);
u16 dac_data2 = (((u8)(wav_data2[1]-0x80)<<8)|(wav_data2[0]));
printf("----------------------------------------------\n\n");
printf("wav_data2 = %d,%x\n",*(s16*)wav_data2,*(s16*)wav_data2);
printf("dac_data2 = %d,%x\n",dac_data2,dac_data2);
tmp_data =(((u8)(wav_data2[1])<<8)|(wav_data2[0])) - 0x8000;
printf("tmp_data = %d,%x\n",tmp_data,tmp_data);
测试打印(目的是为了验证( buffer[1]+0x8000))以及 (buffer[1]-0x80)效果都是一样;
有符号减去或者加上0x8000计算的结果都是一样(因为两种情况s16/u16有符号数据都溢出了,所以计算结果一样);
有符号数据的模拟量波形是在0值附近上下波动的,所以需要向上平移0x8000:
测试1打印:
data = 3e8,fffffc18
----------------------------------------------
wav_data1 = 1000,3e8
dac_data1 = 33768,83e8
tmp_data = 33768,83e8
----------------------------------------------
wav_data2 = -1000,fffffc18
dac_data2 = 31768,7c18
tmp_data = 31768,7c18
测试2打印:
data = 3e8,fffffc18
----------------------------------------------
wav_data1 = 1000,3e8
dac_data1 = 33768,83e8
tmp_data = 33768,83e8
----------------------------------------------
wav_data2 = -1000,fffffc18
dac_data2 = 31768,7c18
tmp_data = 31768,7c18
补码和原码之间的换算:
原码 转 补码:
正整数的补码是它本身,即 1的原码是0000 0001 ,补码 还是 0000 0001.
负整数的补码是符号位不变,其余位按位取反 ,再加1 ,例如 -1 的原码 1000 0001,补码 1111 1111 .补码 转 原码
正整数的补码即是原码。例,1 的补码是0000 0001 ,原码还是 0000 0001
负整数已知补码求原码,只需要再对补码求一次补码即可,即负整数的补码的补码即是原码。
例 -1 的补码 1111 1111 ,再取一次补码(符号位不变,其余位按位取反,再加1)即是原码 1000 0001.mcu数据以补码形式存储:
声音制作
来源:
https://blog.csdn.net/weixin_42107504/article/details/139420370