1

我正在尝试使用 Qt (c++) 创建一个程序,该程序可以使用 QAudioinput 和 QIODevice 从我的麦克风录制音频。现在,我想可视化我的信号

任何帮助,将不胜感激。谢谢

[Edit1] - 从您的评论中复制(由 Spektre 提供)

  • 我只有一个缓冲区用于两个通道
  • 我使用 Qt ,通道的值在缓冲区上交错
  • 这就是我分离价值观的方式

    for ( int i = 0, j = 0; i < countSamples ; ++j) 
     {
     YVectorRight[j]=Samples[i++];
     YVectorLeft[j] =Samples[i++];
     }
    
  • 在我绘制 YvectorRight 和 YvectorLeft 之后。我看不到如何只触发一个频道

4

1 回答 1

1

呵呵,几年前在课堂上为学生做的。我希望你知道示波器是如何工作的,所以这里只是基础知识:

  1. 时基

    • fsmpl是输入信号采样频率 [Hz]

    尝试使用尽可能大的频率,(44100,48000, ???)以便检测到的最大频率fsmpl/2为您提供时基轴的顶部。下限由您的缓冲区长度给出

  2. 创建将从指定起始地址(缓冲区内)呈现采样缓冲区的函数:

    • Y-scale ...幅度设置
    • Y-offset ... 垂直光束位置
    • X-offset ... 时移或水平位置

    这可以通过修改起始地址或仅通过 X 偏移曲线来完成

  3. 等级

    创建将模拟关卡功能的函数。所以从起始地址搜索缓冲区,如果幅度超过电平则停止。你可以有更多的模式,但这些是你应该实现的基础:

    • 振幅:( < lvl ) -> ( > lvl )
    • 振幅:( > lvl ) -> ( < lvl )

    还有许多其他级别的可能性,例如故障,相对边缘,...

  4. 预习

    您可以将所有这些放在一起,例如:您有start address变量,因此将数据连续采样到某个缓冲区并在计时器调用level时使用start address(并更新它)。然后用 new 调用 drawstart address并添加timebase periodstart address(当然就您的样本而言)

  5. 多通道

    我使用Line IN,所以我有立体声输入(A,B = 左,右),因此我可以添加一些其他内容,例如:

    • 电平源 ( A,B,none)
    • 渲染模式(时基,切比雪夫(李萨如曲线,如果关闭))
    • Chebyshev = xaxis is A, yaxis isB这会创建著名的切比雪夫图像,这些图像对相关的正弦信号很有用。通常形成圆形、椭圆形、扭曲的环......
  6. 杂项

    您可以为模拟电容或输入接地等通道添加滤波器

  7. 图形用户界面

    你需要很多设置我更喜欢模拟旋钮而不是按钮/滚动条/滑块,就像在真正的示波器上一样

    • (半)模拟值:Amplitude,TimeBase,Level,X-offset,Y-offset
    • 离散值:电平模式(/,),电平源(A,B,-),每个通道(直接开启,接地,关闭,容量开启)

这是我的示波器的一些屏幕截图:

示波器 示波器

这是我的生成器的屏幕截图:

发电机

最后在添加了一些 FFT 之后还有频谱分析仪

频谱分析仪

PS。

  • 我从DirectSound开始,但由于有错误/无功能的缓冲区回调,它很糟糕
  • 我现在对我的应用程序中的所有声音都使用WinAPI WaveIn/Out。经过一些怪癖之后,它最适合我的需求并且具有最佳延迟(Directsound 太慢了 10 倍以上)但对于示波器它没有任何优点(我需要低延迟主要用于模拟器)

顺便提一句。我将这三个应用程序作为可链接的 C++ 子窗口类(Borland)

  • 最后与我的 ATMega168 仿真器一起用于我的无传感器 BLDC 驱动程序调试
  • 在这里你可以试试我的示波器、发生器和频谱分析

如果您需要任何帮助,希望对您有所帮助,请评论我

[Edit1] 触发器

您一次触发所有通道,但通常只从一个通道检查触发条件现在实现很简单,例如让触发条件A(左)通道高于水平,因此:

  1. 首先在没有触发器的情况下进行连续播放,你写的是这样的:

    for ( int i = 0, j = 0; i < countSamples ; ++j) 
     {
     YVectorRight[j]=Samples[i++];
     YVectorLeft[j] =Samples[i++];
     }
    // here draw or FFT,draw  buffers YVectorRight,YVectorLeft
    
  2. 添加触发器

    要添加触发条件,您只需找到满足它的样本并开始从中绘制,以便将其更改为类似这样

    // static or global variables
    static int i0=0; // actual start for drawing
    static bool _copy_data=true; // flag that new samples need to be copied
    static int level=35; // trigger level value datatype should be the same as your samples...
    
    int i,j;
    for (;;)
     {
     // copy new samples to buffer if needed
     if (_copy_data)
      for (_copy_data=false,i=0,j=0;i<countSamples;++j) 
      {
      YVectorRight[j]=Samples[i++];
      YVectorLeft[j] =Samples[i++];
      }
     // now search for new start
     for (i=i0+1;i<countSamples>>1;i++) 
      if (YVectorLeft[i-1]<level) // lower then level before i
       if (YVectorLeft[i]>=level) // higher then level after i
        {
        i0=i;
        break;
        }
     if (i0>=(countSamples>>1)-view_samples) { i0=0; _copy_data=true; continue; }
     break;
     }
    // here draw or FFT,draw  buffers YVectorRight,YVectorLeft from i0 position
    
    • view_samples是查看/处理的数据大小(对于一个或多个屏幕)它应该比(countSamples>>1)
    • 此代码可以在边界区域松动一个屏幕以避免您需要实现循环缓冲区(环)但对于初学者来说甚至可以
    • 只需通过一些 if 或 switch 语句对所有触发条件进行编码
于 2014-02-09T10:52:07.003 回答