OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6540|回复: 2

音频采集与FFT频率分析

[复制链接]

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
77
金钱
77
注册时间
2016-8-25
在线时间
26 小时
发表于 2017-7-18 11:00:51 | 显示全部楼层 |阅读模式
5金钱
我正在做音频信号的采集这一块,在原子STM32F407开发板上做的预演,借用的是录音实验:

1、采样频率设置为48KHz
   [mw_shl_code=applescript,true] I2S2_SampleRate_Set(48000); //设置采样率 //16000[/mw_shl_code]

2、FFT
    由于FFT采样点数设置为1024,这里采用的是DMA双缓冲接收,因此DMA接收长度设置为512
[mw_shl_code=applescript,true]u8 bufUseFlag1 = 0, bufUseFlag2 = 0;

//录音 I2S_DMA接收中断服务函数.在中断里面写入数据
void rec_i2s_dma_rx_callback(void)
{
    u16 bw;
    u8 res;
    if(rec_sta==0X80)//录音模式
    {
        if (bufUseFlag1 & bufUseFlag2) {
            return ;
        }

                /* 0:当前目标存储器为存储器 0(使用 DMA_SxM0AR 指针寻址)
                   1:当前目标存储器为存储器 1(使用 DMA_SxM1AR 指针寻址) */
        if(DMA1_Stream3->CR&(1<<19))
        {
            res=f_write(f_rec,i2srecbuf1,I2S_RX_DMA_BUF_SIZE,(UINT*)&bw);//写入文件
            if(res)
            {
                printf("write error:%d\r\n",res);
            }
            bufUseFlag1 = 1;
        }else
        {
            res=f_write(f_rec,i2srecbuf2,I2S_RX_DMA_BUF_SIZE,(UINT*)&bw);//写入文件
            if(res)
            {
                printf("write error:%d\r\n",res);
            }
            bufUseFlag2 = 1;
        }
        wavsize+=I2S_RX_DMA_BUF_SIZE;
    }
}[/mw_shl_code]

FFT处理
[mw_shl_code=applescript,true]//WAV录音
void wav_recorder(void)
{
    u8 res;
    u8 key;
    u8 rval=0;
    __WaveHeader *wavhead=0;
    DIR recdir;                     //目录
    u8 *pname=0;
    u8 timecnt=0;                   //计时器
    u32 recsec=0;                   //录音时间

    arm_cfft_radix4_instance_f32 scfft;

    arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);



    while(f_opendir(&recdir,"0:/RECORDER"))//打开录音文件夹
    {
        printf("\r\n没有0:/RECORDER,创建\r\n");
        f_mkdir("0:/RECORDER");             //创建该目录
    }
    i2srecbuf1=mymalloc(SRAMIN,I2S_RX_DMA_BUF_SIZE);//I2S录音内存1申请
    i2srecbuf2=mymalloc(SRAMIN,I2S_RX_DMA_BUF_SIZE);//I2S录音内存2申请
    f_rec=(FIL *)mymalloc(SRAMIN,sizeof(FIL));      //开辟FIL字节的内存区域
    wavhead=(__WaveHeader*)mymalloc(SRAMIN,sizeof(__WaveHeader));//开辟__WaveHeader字节的内存区域
    pname=mymalloc(SRAMIN,30);                      //申请30个字节内存,类似"0:RECORDER/REC00001.wav"
    if(!i2srecbuf1||!i2srecbuf2||!f_rec||!wavhead||!pname) {
        rval=1;
        printf("\r\n录音环境创建失败\r\n");
    }

    if(rval==0)
    {
        recoder_enter_rec_mode();   //进入录音模式,此时耳机可以听到咪头采集到的音频
        pname[0]=0;                 //pname没有任何文件名
        while(rval==0)
        {
            u32 i;

            if (bufUseFlag1 & bufUseFlag2) {
                for(i=0;i<FFT_LENGTH;i++) { //生成信号序列
                    if (i < I2S_RX_DMA_BUF_SIZE)
                        fft_inputbuf[2*i] = (float32_t)i2srecbuf1;   //生成输入信号实部
                    else
                        fft_inputbuf[2*i] = (float32_t)i2srecbuf2[i-I2S_RX_DMA_BUF_SIZE];

                    fft_inputbuf[2*i+1]=0;                              //虚部全部为0
                }


                arm_cfft_radix4_f32(&scfft, fft_inputbuf);  //FFT计算(基4)
                arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH);  //把运算结果复数求模得幅值
                for(i=0; i<FFT_LENGTH; i++) {
                    printf("fft_outputbuf[%d]:%f\r\n",i,fft_outputbuf);
                }

                bufUseFlag1 = 0;
                bufUseFlag2 = 0;
            }


            key=KEY_Scan(0);
            switch(key)
            {
                case KEY2_PRES: //STOP&SAVE
                    if(rec_sta&0X80)//有录音
                    {
                        rec_sta=0;  //关闭录音
                        wavhead->riff.ChunkSize=wavsize+36;     //整个文件的大小-8;
                        wavhead->data.ChunkSize=wavsize;        //数据大小
                        f_lseek(f_rec,0);                       //偏移到文件头.
                        f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw);//写入头数据
                        f_close(f_rec);
                        wavsize=0;

                        printf("\r\n录音结束,并保存音频文件\r\n");
                    }
                    rec_sta=0;
                    recsec=0;
                    LED1=1;                         //关闭DS1
                    break;
                case KEY0_PRES: //REC/PAUSE
                    if(rec_sta&0X01)//原来是暂停,继续录音
                    {
                        rec_sta&=0XFE;//取消暂停
                    }else if(rec_sta&0X80)//已经在录音了,暂停
                    {
                        rec_sta|=0X01;  //暂停
                    }else               //还没开始录音
                    {
                        printf("\r\n还没开始录音\r\n");
                        recsec=0;
                        recoder_new_pathname(pname);            //得到新的名字
                        recoder_wav_init(wavhead);              //初始化wav数据
                        res=f_open(f_rec,(const TCHAR*)pname, FA_CREATE_ALWAYS | FA_WRITE);
                        if(res)         //文件创建失败
                        {
                            rec_sta=0;  //创建文件失败,不能录音
                            rval=0XFE;  //提示是否存在SD卡
                            printf("\r\nSD卡内无法打开/创建文件\r\n");
                        }else
                        {
                            res=f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw);//写入头数据
                            recoder_msg_show(0,0);
                            rec_sta|=0X80;  //开始录音
                        }
                    }
                    if(rec_sta&0X01)LED1=0; //提示正在暂停
                    else LED1=1;
                    break;
                case WKUP_PRES: //播放最近一段录音
                    if(rec_sta!=0X80)//没有在录音
                    {
                        if(pname[0])//如果触摸按键被按下,且pname不为空
                        {
                            printf("\r\n播放录音\r\n");
                            recoder_enter_play_mode();  //进入播放模式
                            audio_play_song(pname);     //播放pname
                            recoder_enter_rec_mode();   //重新进入录音模式
                        }
                    }
                    break;
            }

            delay_ms(5);

            timecnt++;
            if((timecnt%20)==0)LED0=!LED0;//DS0闪烁
            if(recsec!=(wavsize/wavhead->fmt.ByteRate)) //录音时间显示
            {
                LED0=!LED0;//DS0闪烁
                recsec=wavsize/wavhead->fmt.ByteRate;   //录音时间
                recoder_msg_show(recsec,wavhead->fmt.SampleRate*wavhead->fmt.NumOfChannels*wavhead->fmt.BitsPerSample);//显示码率
            }
        }
    }
    myfree(SRAMIN,i2srecbuf1);  //释放内存
    myfree(SRAMIN,i2srecbuf2);  //释放内存
    myfree(SRAMIN,f_rec);       //释放内存
    myfree(SRAMIN,wavhead);     //释放内存
    myfree(SRAMIN,pname);       //释放内存
}[/mw_shl_code]

3、疑问
(1)我这样的处理思路对吗?没有学过信号处理,希望大神看到帮忙分析下;
(2)我的本意是提取采集到的音频信号频率,根据音乐的不同频率处理不同的事情,但是我看到网上是这么说的:
    采样频率为48000Hz,采样点数为1024,频率被分成了1024份,没分为48000/1024=46.875, 我现在不是很明白,如果每个点的频率都是这么计算,那样我怎么确定那个才是声音的频率呢?
(3)由于原子的音频驱动是通过DMA发送两个魔术字0x00来采集音频的,那样的话:即使没有接入耳机口,也会采集到音频信号,我按照方式二采集,同样会有音频数据,怎么才能代码中区分出接入耳机和没接入耳机的情况呢?

希望大家多多指导!

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
77
金钱
77
注册时间
2016-8-25
在线时间
26 小时
 楼主| 发表于 2017-7-18 16:59:54 | 显示全部楼层
@原子哥 我这边 调用 arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH); 后数据出来了  如果作频谱显示fft_outputbuf的数据是不是就是这个频谱值? 但是我发现DAM双缓冲接收的两个buf的数据作为输入源 在没有音频信号接入的情况下 执行录音按键 与有音频信号计入的情况下 执行音频录音按键 两个 打印的数据结果 差别不是很大 未接入的比接入的平均值小了1000   能不能帮忙分析下 感谢!
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
77
金钱
77
注册时间
2016-8-25
在线时间
26 小时
 楼主| 发表于 2017-7-18 17:18:58 | 显示全部楼层
@正点原子 原子哥 帮忙分析下 感激
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-12-19 01:18

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表