C#获取声音信号并通过FFT得到声音频谱

news/2024/7/6 1:48:39 标签: c#, FFT, NAudio

        最近研究了下用C#通过麦克风获取声音信号,再通过快速傅里叶变换,得到声音的频谱。

想应用到实际的led灯上,放歌曲,led灯展示频谱,现在用C#先模拟实现下,如下图。

        上面的表展示的是声音的电压信号,下图是频谱信息

一、获取声音信号 

通过麦克风获取声音用的是NAudio这个库,可以通过NuGet获取

具体使用方式是:

        private NAudio.Wave.WaveIn sourceStream = null;
        private NAudio.Wave.DirectSoundOut waveOut = null;

 获取麦克风列表

List<NAudio.Wave.WaveInCapabilities> sources = new List<NAudio.Wave.WaveInCapabilities>();
for (int i = 0; i < NAudio.Wave.WaveIn.DeviceCount; i++)
{
    sources.Add(NAudio.Wave.WaveIn.GetCapabilities(i));
}
sourceListView.Items.Clear();
foreach (var source in sources)
{
    ListViewItem item = new ListViewItem(source.ProductName);
    item.SubItems.Add(new ListViewItem.ListViewSubItem(item, source.Channels.ToString()));
    sourceListView.Items.Add(item);
}

初始化麦克风 

 if (sourceListView.SelectedItems.Count == 0) return;
 int deviceNumber = sourceListView.SelectedItems[0].Index;
 sourceStream = new NAudio.Wave.WaveIn();
 sourceStream.DeviceNumber = deviceNumber;

 int a=WaveIn.GetCapabilities(0).Channels;
 sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(9600, 16, 1);
 sourceStream.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(sourceStream_DataAvailable);
 sourceStream.BufferMilliseconds = 20;
 sourceStream.StartRecording();

回调函数,lastBuffer就存储的是声音信号 

private void sourceStream_DataAvailable(object sender, WaveInEventArgs e)
{    
    int samplesRecord = e.BytesRecorded / 2;
    if (lastBuffer is null||lastBuffer.Length!=samplesRecord)
        lastBuffer = new double[samplesRecord];
    for (int i = 0; i < samplesRecord; i++)
        lastBuffer[i] = BitConverter.ToInt16(e.Buffer, i * 2);
        
}

二、FFT变换

        FFT变换原理之前文章我用C++写过

C++实现二维快速傅里叶变换(FFT)_c++ fft-CSDN博客

这里我用C#再实现下,复数可以直接using System.Numerics里面的Complex,而不用自己定义了。

FFT源码如下:

int ReverseBin(int a, int n)
{
    int ret = 0;
    for (int i = 0; i < n; i++)
    {
        if ((a & (1 << i)) != 0) ret |= (1 << (n - 1 - i));
    }
    return ret;
}

public void FFT(ref Complex[] a, int log2_N, int opt)
{
    int index;
    int length = 1 << log2_N;
    Complex[] tempA = new Complex[length];
    for (int i = 0; i < length; i++)
    {
        index = ReverseBin(i, log2_N);
        tempA[i] = a[index];
    }

    //生成WN表,减少重复计算
    Complex[] WN = new Complex[length / 2];
    for (int i = 0; i < length / 2; i++)
    {
        WN[i] = new Complex(Math.Cos(2 * Math.PI * i  / length), opt * -1 * Math.Sin(2 * Math.PI*i / length));
    }

    //蝶形运算
    int Index0, Index1;
    Complex temp;
    for (int steplenght = 2; steplenght <= length; steplenght *= 2)
    {
        for (int step = 0; step < length / steplenght; step++)
        {
            for (int i = 0; i < steplenght / 2; i++)
            {
                Index0 = steplenght * step + i;
                Index1 = steplenght * step + i + steplenght / 2;

                temp = tempA[Index1] * WN[length / steplenght * i];
                tempA[Index1] = tempA[Index0] - temp;
                tempA[Index0] = tempA[Index0] + temp;
            }
        }
    }

    for (int i = 0; i < length; i++)
    {
        if (opt == -1)
        {
            a[i] = tempA[i] / length;
        }
        else
        {
            a[i] = tempA[i];
        }
    }

}

三、总结

     这样可以实时得到了一段声音信号,可以加个汉明窗处理下,再FFT下,就得到声音的频谱信息了。后续计划,可以通过这种方式,控制led灯,展示音乐的频谱跳动。


http://www.niftyadmin.cn/n/5045500.html

相关文章

WordPress主题DUX v8.2源码下载

新增产品分类左侧多级分类折叠显示 新增网站默认字体对 MiSans 和 HarmonyOS Sans 的支持 新增顶部左上角显示登录注册的模块开关&#xff0c;且支持原生登录方式 新增手机端导航菜单的关闭按钮 新增文章内容中标题二的强化展示 新增全站禁止复制、右键和选择的操作 新增文章内…

解决react报错“JSX 表达式必须具有一个父元素“

现象如下&#xff1a; 原因&#xff1a; 新插入的dom元素跟已有的dom元素平级了&#xff0c;必须创建一个共有的根元素 解决办法&#xff1a; 使用<> </>标签作为根元素&#xff0c;把所有子元素包裹起来 <> ....原代码 </> 问题解决&#xff01;…

Spring事务2+银行转账拓展

前言 Transactional()内可以写的属性 一、Dao层的更新 public interface LogDao {//#{}符号取的是接口方法中的形参 now()方法直接调用的内置函数Insert("insert into log (info,createDate) values(#{info},now())")void log(String info); }二、LogService层…

【Vue】轻松理解数据代理

hello&#xff0c;我是小索奇&#xff0c;精心制作的Vue教程持续更新哈&#xff0c;想要学习&巩固&避坑就一起学习叭~ Object定义配置方法 代码 引出数据代理&#xff0c;先上代码&#xff0c;后加解释 <!DOCTYPE html> <html><head><meta cha…

OpenGLES:单纹理贴图

一.概述 最近疏于写博客&#xff0c;接下来会陆续更新这段时间OpenGLES的一些开发过程。 前两篇OpenGLES的博客讲解了怎样使用OpenGLES实现相机普通预览和多宫格滤镜 在相机实现过程中&#xff0c;虽然使用到了纹理&#xff0c;但只是在生成一个纹理之后&#xff0c;使用纹理…

如何使用固态硬盘+硬盘盒子+U盘创造移动双系统

本文背景 这学期上了一节鸟水课《大数据实践》&#xff0c;老师要求扩展硬盘盒&#xff0c;以部署大数据工具进行 机器挖掘等大数据领域工作 参考视频链接&#xff1a;无需启动盘&#xff0c;用虚拟机将ubuntu安装到移动硬盘上_哔哩哔哩_bilibili 项目使用设备 1.绿联&#…

【力扣】70. 爬楼梯

题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1 阶 1 阶2 阶 示例 2&…

鲁大师特殊股息割韭菜

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 港股鲁大师9月11日宣布特别分红&#xff0c;6个交易日股价涨了73%&#xff0c;接着大股东趁股价高涨减持12%的股份&#xff0c;然后取消董事会、再接着取消特别分红。 这公司还真是个人才!在港股也…