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


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

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

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

    一、获取声音信号 

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

    具体使用方式是:

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

     获取麦克风列表

    1. List sources = new List();
    2. for (int i = 0; i < NAudio.Wave.WaveIn.DeviceCount; i++)
    3. {
    4. sources.Add(NAudio.Wave.WaveIn.GetCapabilities(i));
    5. }
    6. sourceListView.Items.Clear();
    7. foreach (var source in sources)
    8. {
    9. ListViewItem item = new ListViewItem(source.ProductName);
    10. item.SubItems.Add(new ListViewItem.ListViewSubItem(item, source.Channels.ToString()));
    11. sourceListView.Items.Add(item);
    12. }

    初始化麦克风 

    1. if (sourceListView.SelectedItems.Count == 0) return;
    2. int deviceNumber = sourceListView.SelectedItems[0].Index;
    3. sourceStream = new NAudio.Wave.WaveIn();
    4. sourceStream.DeviceNumber = deviceNumber;
    5. int a=WaveIn.GetCapabilities(0).Channels;
    6. sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(9600, 16, 1);
    7. sourceStream.DataAvailable += new EventHandler(sourceStream_DataAvailable);
    8. sourceStream.BufferMilliseconds = 20;
    9. sourceStream.StartRecording();

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

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

    二、FFT变换

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

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

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

    FFT源码如下:

    1. int ReverseBin(int a, int n)
    2. {
    3. int ret = 0;
    4. for (int i = 0; i < n; i++)
    5. {
    6. if ((a & (1 << i)) != 0) ret |= (1 << (n - 1 - i));
    7. }
    8. return ret;
    9. }
    10. public void FFT(ref Complex[] a, int log2_N, int opt)
    11. {
    12. int index;
    13. int length = 1 << log2_N;
    14. Complex[] tempA = new Complex[length];
    15. for (int i = 0; i < length; i++)
    16. {
    17. index = ReverseBin(i, log2_N);
    18. tempA[i] = a[index];
    19. }
    20. //生成WN表,减少重复计算
    21. Complex[] WN = new Complex[length / 2];
    22. for (int i = 0; i < length / 2; i++)
    23. {
    24. WN[i] = new Complex(Math.Cos(2 * Math.PI * i / length), opt * -1 * Math.Sin(2 * Math.PI*i / length));
    25. }
    26. //蝶形运算
    27. int Index0, Index1;
    28. Complex temp;
    29. for (int steplenght = 2; steplenght <= length; steplenght *= 2)
    30. {
    31. for (int step = 0; step < length / steplenght; step++)
    32. {
    33. for (int i = 0; i < steplenght / 2; i++)
    34. {
    35. Index0 = steplenght * step + i;
    36. Index1 = steplenght * step + i + steplenght / 2;
    37. temp = tempA[Index1] * WN[length / steplenght * i];
    38. tempA[Index1] = tempA[Index0] - temp;
    39. tempA[Index0] = tempA[Index0] + temp;
    40. }
    41. }
    42. }
    43. for (int i = 0; i < length; i++)
    44. {
    45. if (opt == -1)
    46. {
    47. a[i] = tempA[i] / length;
    48. }
    49. else
    50. {
    51. a[i] = tempA[i];
    52. }
    53. }
    54. }

    三、总结

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

  • 相关阅读:
    ubuntu 23.04从源码编译安装rocm运行tensorflow-rocm
    Java 数据结构、集合框架、ArrayList
    面向交通运输的计算机视觉和深度学习2
    Mysql - 分库分表
    【哈士奇赠书活动 - 44期】- 〖从零基础到精通Flutter开发〗
    【每日一题】34. 在排序数组中查找元素的第一个和最后一个位置
    Java之~反射,String类型模板匹配类属性方法
    如何开始学习量子机器学习
    【Linux详解】冯诺依曼架构 | 操作系统设计 | 斯坦福经典项目Pintos
    如何免费创建三级域名?
  • 原文地址:https://blog.csdn.net/Isaac320/article/details/133032945