前言:目前基于智能门锁的开发,需要用到语音,但是由于如果用语音IC的话,声音内容就不方便调整更改,所以我们打算用单片机的DAC功能直接读取WAV文件,再用功放IC放大来实现智能语音。
步骤一:用朗读女获取WAV文件
有时候无法勾选WAV文件,可能是由于没有下载发音人语音导致的。
步骤二:wav文件转PCM文件。
WAV:wav是一种无损的音频文件格式,WAV符合 PIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。
PCM:PCM(Pulse Code Modulation—-脉码调制录音)。所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录。PCM信号是由[1]、[0]等符号构成的数字信号,而未经过任何编码和压缩处理。与模拟信号比,它不易受传送系统的杂波及失真的影响。动态范围宽,可得到音质相当好的影响效果。
简单来说pcm是无损wav文件中音频数据的一种编码方式,但wav还可以用其它方式编码 。
WAV文件头部解析:
00-03 52 49 46 46:RIFF的标志
04-07 76 43 01 00 :文件的长度
08-0B 57 41 56 45 :WAVE的标志
0C-0F 66 6D 74 20 :fmt的标志
10-13 12 00 00 00 :编码的格式类别,10H代表PCM形式。
14-15 01 00 :字块总数
16-17 01 00 : 通道数 1为单声道,2为双声道
18-1B 80 3E 00 00 :采样率:每秒采集二进制的位数。
1C-1F 00 7D 00 00 :每秒播放的字节数 = 通道数 x 每秒采集二进制的位数 x 采集数据的位数 / 8.
20-21 02 00 :每个样点的字节数
22-23 01 00 :每个样点的数据位数
24-27 00 00 64 61 :data的标志
步骤三:把处理生成的PCM文件,合成一个bin文件烧录到外部的flash中,然后MCU根据地址去读相应的数据
void play_wave(uint8_t index)
{
uint8_t end = 0;
key_flag = 0;
int16_t *sample_ptr;
uint16_t i = 0;
speaker_init();
HAL_Delay(100);
HAL_TIM_Base_Start_IT(&htim6); //打开定时器
switch(index) { //外部flash的地址的索引
case ZERO:
chunk_count = ZERO_SIZE / 256;
chunk_residual = ZERO_SIZE % 256;
flash_addr = ZERO_ADDR;
break;
case ONE:
chunk_count = ONE_SIZE / 256;
chunk_residual = ONE_SIZE % 256;
flash_addr = ONE_ADDR;
break;
}
flash_read_data(flash_addr, (uint8_t *)dma_buf, 256); //读外部flash的数据
flash_read_data(flash_addr + 256, (uint8_t *)&dma_buf[128], 256);
sample_ptr = (int16_t *)dma_buf;
for(i=0; i<256; i++) {
sample_ptr[i] = (sample_ptr[i] >> volume) + 2048;
}
consume_count = 2;
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)dma_buf, 256, DAC_ALIGN_12B_R); //开始DAC的DMA转换
while(1) { //设置一个标志位,采用乒乓的方式自动去读外部flash的数据
if(wave_dma_status == 1) {
end = fill_dma_buffer_from_flash(&hdac, 256, flash_addr);
wave_dma_status = 0;
} else if(wave_dma_status == 2) {
end = fill_dma_buffer_from_flash(&hdac, 0, flash_addr);
wave_dma_status = 0;
}
if(end) {
HAL_TIM_Base_Stop_IT(&htim6);
break;
}
}
HAL_GPIO_WritePin(Speaker_Pwr_GPIO_Port,Speaker_Pwr_Pin,GPIO_PIN_RESET);
}
uint8_t fill_dma_buffer_from_flash(DAC_HandleTypeDef *thdac, uint16_t offset, uint32_t addr)
{
uint32_t src_ptr = addr;
uint8_t *dst_ptr = (uint8_t *)dma_buf;
uint16_t i = 0;
dst_ptr += offset;
src_ptr += consume_count * 256;
int16_t *sample_ptr = (int16_t *)dst_ptr;
if(consume_count == chunk_count) { //分几种情况读完音频文件
if(chunk_residual != 0) {
flash_read_data(src_ptr, dst_ptr, chunk_residual); //second
for(i=0; i
}
} else {
consume_count = 0; //end
HAL_DAC_Stop_DMA(thdac, DAC_CHANNEL_1);
return 1;
}
} else if(consume_count == (chunk_count + 1)) { //end
consume_count = 0;
HAL_DAC_Stop_DMA(thdac, DAC_CHANNEL_1);
return 1;
} else {
flash_read_data(src_ptr, dst_ptr, 256); //first
for(i=0; i<128; i++) {
sample_ptr[i] = (sample_ptr[i] >> volume) + 2048;
}
}
consume_count++;
return 0;
}
实现wav文件的音频播放。