Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 用C+绘制音频波形+/Qt_C++_Qt_Plot_Real Time - Fatal编程技术网

C++ 用C+绘制音频波形+/Qt

C++ 用C+绘制音频波形+/Qt,c++,qt,plot,real-time,C++,Qt,Plot,Real Time,我有一个大学作业,用C++/Qt显示音频文件的波形。我们应该能够修改用于显示它的比例(以每个屏幕像素的音频样本表示) 到目前为止,我能够: 打开音频文件 阅读样本 以给定的比例绘制样本 为了以给定的比例绘制样本,我尝试了两种策略。假设N是刻度的值: 对于从0到窗口宽度的i,在屏幕像素i处绘制第i*n个音频样本。这是非常快速且时间恒定的,因为我们总是访问相同数量的音频数据点。 但是,它不能正确表示波形,因为我们仅使用1个点的值来表示N个点。 对于从0到N*width的i,在屏幕位置i/(N*

我有一个大学作业,用C++/Qt显示音频文件的波形。我们应该能够修改用于显示它的比例(以每个屏幕像素的音频样本表示)

到目前为止,我能够:

  • 打开音频文件
  • 阅读样本
  • 以给定的比例绘制样本
为了以给定的比例绘制样本,我尝试了两种策略。假设N是刻度的值:

  • 对于从0到窗口宽度的i,在屏幕像素i处绘制第i*n个音频样本。这是非常快速且时间恒定的,因为我们总是访问相同数量的音频数据点。
    但是,它不能正确表示波形,因为我们仅使用1个点的值来表示N个点。

  • 对于从0到N*width的i,在屏幕位置i/(N*width)绘制第i个音频样本,让Qt找出如何在物理屏幕像素上正确表示该音频样本。
    绘制出非常漂亮的波形,但访问数据需要花费大量时间。例如,如果我想每像素显示500个样本,窗口宽度为100px,我必须访问50000个点,然后Qt将这些点绘制为100个物理点(像素)。

那么,我如何才能得到一个正确的音频数据图,可以快速计算?我应该为每个物理像素计算N个样本的平均值吗?我应该做一些曲线拟合吗?

换句话说,当Qt/Matplotlib/Matlab/etc将数千个数据点绘制到数量非常有限的物理像素上时,会涉及什么样的操作?

仅仅因为我知道如何操作,并且我已经询问了stackoverflow的类似问题,我将参考。稍后我将提供代码

绘制波形是一个真正的问题。我花了半年多的时间试图解决这个问题! 总而言之:

根据报告:

波形视图使用两种蓝色,一种较暗,一种较亮

  • 波形的深蓝色部分显示像素所代表区域的最高峰值。在默认缩放级别,Audacity将 显示该像素宽度内的多个样本,因此该像素表示 组中声音最大的样本的值
  • 波形的浅蓝色部分显示同一组样本的平均RMS(均方根)值。这是一个艰难的过程 这个区域的声音有多大的指南,但没有办法 单独提取或使用波形的RMS部分
因此,您只需尝试从数据块中获取重要信息。如果你反复这样做,你将有多个阶段,可用于绘图

我将在这里提供一些代码,请耐心等待,它正在开发中:

template<typename T>
class CacheHandler {
public:

    std::vector<T> data;
    vector2d<T> min, max, rms;

    CacheHandler(std::vector<T>& data) throw(std::exception);

    void addData(std::vector<T>& samples);

    /*
    irreversible removes data.
    Fails if end index is greater than data length
    */
    void removeData(int endIndex);

    void removeData(int startIndex, int endIndex);
};
模板
类缓存处理程序{
公众:
std::矢量数据;
矢量2D最小值、最大值、均方根值;
CacheHandler(std::vector&data)抛出(std::exception);
void addData(标准::向量和样本);
/*
不可逆删除数据。
如果结束索引大于数据长度,则失败
*/
无效删除数据(内部索引);
void removeData(intstartindex,intendindex);
};
使用此选项:

template<typename T>
inline WaveformPane::CacheHandler<T>::CacheHandler(std::vector<T>& data, int sampleSizeInBits) throw(std::exception)
{
    this->data = data;
    this->sampleSizeInBits = sampleSizeInBits;
    int N = log(data.size()) / log(2);
    rms.resize(N); min.resize(N); max.resize(N);
    rms[0] = calcRMSSegments(data, 2);
    min[0] = getMinPitchSegments(data, 2);
    max[0] = getMaxPitchSegments(data, 2);
    for (int i = 1; i < N; i++) {
        rms[i] = calcRMSSegments(rms[i - 1], 2);
        min[i] = getMinPitchSegments(min[i - 1], 2);
        max[i] = getMaxPitchSegments(max[i - 1], 2);
    }
}
模板
内联waveormpane::CacheHandler::CacheHandler(std::vector&data,int sampleSizeInBits)抛出(std::exception)
{
这->数据=数据;
此->sampleSizeInBits=sampleSizeInBits;
int N=log(data.size())/log(2);
rms.resize(N);min.resize(N);max.resize(N);
rms[0]=计算单元(数据,2);
min[0]=getMinPitchSegments(数据,2);
max[0]=getMaxPitchSegments(数据,2);
对于(int i=1;i
我的建议如下:

给定音频文件中的
totalNumSamples
音频样本,以及显示小部件中宽度为
widgetWidth
的像素,您可以计算每个像素代表的样本:

// Given an x value (in pixels), returns the appropriate corresponding
// offset into the audio-samples array that represents the
// first sample that should be included in that pixel.
int GetFirstSampleIndexForPixel(int x, int widgetWidth, int totalNumSamples)
{
   return (totalNumSamples*x)/widgetWidth;
}

virtual void paintEvent(QPaintEvent * e)
{
   QPainter p(this);
   for (int x=0; x<widgetWidth; x++)
   {
      const int firstSampleIndexForPixel = GetFirstSampleIndexForPixel(x, widgetWidth, totalNumSamples);
      const int lastSampleIndexForPixel = GetFirstSampleIndexForPixel(x+1, widgetWidth, totalNumSamples)-1;
      const int largestSampleValueForPixel = GetMaximumSampleValueInRange(firstSampleIndexForPixel, lastSampleIndexForPixel);
      const int smallestSampleValueForPixel = GetMinimumSampleValueInRange(firstSampleIndexForPixel, lastSampleIndexForPixel);

      // draw a vertical line spanning all sample values that are contained in this pixel
      p.drawLine(x, GetYValueForSampleValue(largestSampleValueForPixel), x, GetYValueForSampleValue(smallestSampleValueForPixel));
   }
}

有了它,您只需为
sampleIndexAleftedgeofWidget
sampleIndexAfterRightEdgeOfWidget
输入不同的值,即可进行缩放/平移,这两个值一起指示要显示的文件的子范围。

子采样是将音频信号的频率大幅降低的正常方法。(由于别名的存在,少量使用会更加困难)。平均出一个窗口或滑动窗口会改变数据。这与我问的一个问题非常相似。您可能会发现这里的讨论很有趣:
int GetFirstSampleIndexForPixel(int x, int widgetWidth, int sampleIndexAtLeftEdgeOfWidget, int sampleIndexAfterRightEdgeOfWidget)
{
   int numSamplesToDisplay = sampleIndexAfterRightEdgeOfWidget-sampleIndexAtLeftEdgeOfWidget;
   return sampleIndexAtLeftEdgeOfWidget+((numSamplesToDisplay*x)/widgetWidth);
}