mirror of
https://github.com/LMMS/lmms.git
synced 2026-05-17 11:18:30 -04:00
Improve performance and quality of Sample Track display, and add RMS graph. (#5927)
* Buff Sample Track display performance and visuals, and add RMS display Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
This commit is contained in:
@@ -998,6 +998,13 @@ f_cnt_t SampleBuffer::getPingPongIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t en
|
||||
}
|
||||
|
||||
|
||||
/* @brief Draws a sample buffer on the QRect given in the range [fromFrame, toFrame)
|
||||
* @param QPainter p: Painter object for the painting operations
|
||||
* @param QRect dr: QRect where the buffer will be drawn in
|
||||
* @param QRect clip: QRect used for clipping
|
||||
* @param f_cnt_t fromFrame: First frame of the range
|
||||
* @param f_cnt_t toFrame: Last frame of the range non-inclusive
|
||||
*/
|
||||
void SampleBuffer::visualize(
|
||||
QPainter & p,
|
||||
const QRect & dr,
|
||||
@@ -1009,6 +1016,7 @@ void SampleBuffer::visualize(
|
||||
if (m_frames == 0) { return; }
|
||||
|
||||
const bool focusOnRange = toFrame <= m_frames && 0 <= fromFrame && fromFrame < toFrame;
|
||||
//TODO: If the clip QRect is not being used we should remove it
|
||||
//p.setClipRect(clip);
|
||||
const int w = dr.width();
|
||||
const int h = dr.height();
|
||||
@@ -1017,30 +1025,83 @@ void SampleBuffer::visualize(
|
||||
const float ySpace = h * 0.5f;
|
||||
const int nbFrames = focusOnRange ? toFrame - fromFrame : m_frames;
|
||||
|
||||
const int fpp = qBound<int>(1, nbFrames / w, 20);
|
||||
QPointF * l = new QPointF[nbFrames / fpp + 1];
|
||||
QPointF * r = new QPointF[nbFrames / fpp + 1];
|
||||
int n = 0;
|
||||
const double fpp = std::max(1., static_cast<double>(nbFrames) / w);
|
||||
// There are 2 possibilities: Either nbFrames is bigger than
|
||||
// the width, so we will have width points, or nbFrames is
|
||||
// smaller than the width (fpp = 1) and we will have nbFrames
|
||||
// points
|
||||
const int totalPoints = nbFrames > w
|
||||
? w
|
||||
: nbFrames;
|
||||
std::vector<QPointF> fEdgeMax(totalPoints);
|
||||
std::vector<QPointF> fEdgeMin(totalPoints);
|
||||
std::vector<QPointF> fRmsMax(totalPoints);
|
||||
std::vector<QPointF> fRmsMin(totalPoints);
|
||||
int curPixel = 0;
|
||||
const int xb = dr.x();
|
||||
const int first = focusOnRange ? fromFrame : 0;
|
||||
const int last = focusOnRange ? toFrame - 1 : m_frames - 1;
|
||||
// When the number of frames isn't perfectly divisible by the
|
||||
// width, the remaining frames don't fit the last pixel and are
|
||||
// past the visible area. lastVisibleFrame is the index number of
|
||||
// the last visible frame.
|
||||
const int visibleFrames = (fpp * w);
|
||||
const int lastVisibleFrame = focusOnRange
|
||||
? fromFrame + visibleFrames - 1
|
||||
: visibleFrames - 1;
|
||||
|
||||
for (int frame = first; frame <= last; frame += fpp)
|
||||
for (double frame = first; frame <= last && frame <= lastVisibleFrame; frame += fpp)
|
||||
{
|
||||
auto x = xb + ((frame - first) * double(w) / nbFrames);
|
||||
float maxData = -1;
|
||||
float minData = 1;
|
||||
|
||||
float rmsData[2] = {0, 0};
|
||||
|
||||
// Find maximum and minimum samples within range
|
||||
for (int i = 0; i < fpp && frame + i <= last; ++i)
|
||||
{
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
auto curData = m_data[static_cast<int>(frame) + i][j];
|
||||
|
||||
if (curData > maxData) { maxData = curData; }
|
||||
if (curData < minData) { minData = curData; }
|
||||
|
||||
rmsData[j] += curData * curData;
|
||||
}
|
||||
}
|
||||
|
||||
const float trueRmsData = (rmsData[0] + rmsData[1]) / 2 / fpp;
|
||||
const float sqrtRmsData = sqrt(trueRmsData);
|
||||
const float maxRmsData = qBound(minData, sqrtRmsData, maxData);
|
||||
const float minRmsData = qBound(minData, -sqrtRmsData, maxData);
|
||||
|
||||
// If nbFrames >= w, we can use curPixel to calculate X
|
||||
// but if nbFrames < w, we need to calculate it proportionally
|
||||
// to the total number of points
|
||||
auto x = nbFrames >= w
|
||||
? xb + curPixel
|
||||
: xb + ((static_cast<double>(curPixel) / nbFrames) * w);
|
||||
// Partial Y calculation
|
||||
auto py = ySpace * m_amplification;
|
||||
l[n] = QPointF(x, (yb - (m_data[frame][0] * py)));
|
||||
r[n] = QPointF(x, (yb - (m_data[frame][1] * py)));
|
||||
++n;
|
||||
fEdgeMax[curPixel] = QPointF(x, (yb - (maxData * py)));
|
||||
fEdgeMin[curPixel] = QPointF(x, (yb - (minData * py)));
|
||||
fRmsMax[curPixel] = QPointF(x, (yb - (maxRmsData * py)));
|
||||
fRmsMin[curPixel] = QPointF(x, (yb - (minRmsData * py)));
|
||||
++curPixel;
|
||||
}
|
||||
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.drawPolyline(l, nbFrames / fpp);
|
||||
p.drawPolyline(r, nbFrames / fpp);
|
||||
for (int i = 0; i < totalPoints; ++i)
|
||||
{
|
||||
p.drawLine(fEdgeMax[i], fEdgeMin[i]);
|
||||
}
|
||||
|
||||
delete[] l;
|
||||
delete[] r;
|
||||
p.setPen(p.pen().color().lighter(123));
|
||||
|
||||
for (int i = 0; i < totalPoints; ++i)
|
||||
{
|
||||
p.drawLine(fRmsMax[i], fRmsMin[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user