简介

通过FATFS管理SD卡,读取SD卡中的wav文件,根据wav文件的头分析出该音频文件的各项参数(采样率,位深,声道数等)配置外置DAC芯片;将wav数据通过I2S双缓冲区发送给外置DAC芯片播放;同时将PCM格式数据加hanning窗后给到FFT变换到频域,最后将频域信号以对数形式显示在LCD上

1 特点与不足

特点:
1、支持44100、48000Hz采样率,16、24bits位深的音频文件
2、FATFS和I2S均采用DMA传输,给FFT提供了充足的CPU资源
3、加入了hanning窗减少频谱泄露
4、频谱显示采用对数方式,视觉效果更好
不足:
1、FFT采用1024点运算,两频点间隔43Hz,部分频段细节有少许缺失

2 分块介绍

在这里插入图片描述
2.1 ADC音量调节
cubemx生成代码,固定时间间隔采集ADC数值,若和上次采集有差别则设置DAC芯片

2.2 音乐播放
1、为了保证音乐播放的连续性,I2S需使用双缓冲区传输方式,通过在传输完成中断设置标志位选择需往哪个缓冲区中填入数据

if (wavwitchbuf)
{   fillnum = wav_buffill(g_audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE, wavctrl.bps); 
else
{   fillnum = wav_buffill(g_audiodev.saibuf1, WAV_SAI_TX_DMA_BUFSIZE, wavctrl.bps);

2、wav文件解析详解网上有很多资料,这里补充个关于data数据块需注意的点
1)data数据块是以有符号型存储的,注意分辨
2)data数据块存储方式是小端模式
3)16bits几个数据点就占2bytes,24bits占3bytes,依此类推
在这里插入图片描述2.3 音频处理
1、PCM格式转float32格式
raw_data_buf以uint8的形式存储data数据,因为上面提到的小端模式,这段代码会先将raw_data_buf[2 * i + 1]转换成uint16型高位为0后左移8位再与raw_data_buf[2 * i]拼接后转成有符号型value;
然后value转成有符号浮点型后归一化到[-1,1]

int16_t value = (uint16_t)raw_data_buf[2 * i] | 
				((uint16_t)raw_data_buf[2 * i + 1] << 8);
output_buf[2 * i] = (float32_t)value / 32767.0f;
output_buf[2 * i + 1] = 0.0f;	

而24位数据因为DAC芯片需要需对数据做些处理

地址 0x01 0x02 0x03 0x04
数据 数据低位 数据中位 数据高位 无效位(非0)
int32_t value =  ((uint32_t)raw_data_buf[4 * i + 0] << 8 ) |
                 ((uint32_t)raw_data_buf[4 * i + 1] << 16) | 
                 ((uint32_t)raw_data_buf[4 * i + 2] << 24);
output_buf[2 * i] = (float32_t)value / 2147483647.0f;
output_buf[2 * i + 1] = 0.0f;

2、hanning窗
没什么特别要注意的,窗函数与数据相乘即可

void arm_hanning_f32(float32_t *pbuf, uint32_t blockSize)
{
    float32_t k = 2.0f / ((float32_t)blockSize);
    float32_t w;

    for (uint32_t i = 0; i < blockSize; i++)
    {
        w = PI * i * k;
        w = 0.5f * (1.0f - cosf(w));
        pbuf[i] *= w;
    }
}

3、对数显示
线性频率取log以2为底就可以得到对数频率了

static uint16_t display_fft_log(uint16_t sx, uint16_t sy, uint16_t linear_freq, uint16_t width)
{
    uint16_t log_current_freq = log2f((float32_t)linear_freq) * 50;
    uint16_t log_next_freq = log2f((float32_t)(linear_freq + width)) * 50;
    uint8_t i = 0;
    for(; i < log_next_freq - log_current_freq; i++)
    {
        if(sy > 200){sy = 200;}
        lcd_fill(sx+i, 300-sy, sx+i+1, 300, RED);
    }
    return sx+i;
}

因为是柱状图的形式,而且音乐1kHz左右频谱的跃动比较有观赏价值,所以我设置在了3kHz前显示尽可能多的细节,3kHz后细节减少,最后显示就会如图所示
在这里插入图片描述

3 资源共享

STM32代码,学习用,留个底
https://gitee.com/creator-len/study_music
成果展示

[STM32学习]简易音乐播放器+FFT频谱显示成功展示

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐