diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 6c1dd861926..f64d3df6077 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -675,8 +675,8 @@ PatternView { /* sample track pattern */ SampleTCOView { - background-color: rgb( 74, 253, 133 ); - color: rgb( 187, 227, 236 ); + background-color: rgba(42,51,59,255); + color: #FF8F05; } /* automation pattern */ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 6c291a2de69..92a15dd2a42 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -721,8 +721,8 @@ PatternView { /* sample track pattern */ SampleTCOView { - background-color: #DE7C05; - color: #FFE8CD; + background-color: rgba(42,51,59,255); + color: #FF8F05; } /* automation pattern */ diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 370e89bc732..e99b2d0a81c 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -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(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(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 fEdgeMax(totalPoints); + std::vector fEdgeMin(totalPoints); + std::vector fRmsMax(totalPoints); + std::vector 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(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(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]); + } } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index fd48217f633..987d8e09f99 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -574,9 +574,13 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) QPainter p( &m_paintPixmap ); - QLinearGradient lingrad( 0, 0, 0, height() ); - QColor c = getColorForDisplay( painter.background().color() ); bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); + bool selected = isSelected(); + + QLinearGradient lingrad(0, 0, 0, height()); + QColor c = painter.background().color(); + if (muted) { c = c.darker(150); } + if (selected) { c = c.darker(150); } lingrad.setColorAt( 1, c.darker( 300 ) ); lingrad.setColorAt( 0, c ); @@ -593,7 +597,24 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) p.fillRect( rect(), c ); } - p.setPen( !muted ? painter.pen().brush().color() : mutedColor() ); + auto tcoColor = m_tco->hasColor() + ? (m_tco->usesCustomClipColor() + ? m_tco->color() + : m_tco->getTrack()->color()) + : painter.pen().brush().color(); + + p.setPen(tcoColor); + + if (muted) + { + QColor penColor = p.pen().brush().color(); + penColor.setHsv(penColor.hsvHue(), penColor.hsvSaturation() / 4, penColor.value()); + p.setPen(penColor.darker(250)); + } + if (selected) + { + p.setPen(p.pen().brush().color().darker(150)); + } const int spacing = TCO_BORDER_WIDTH + 1; const float ppb = fixedTCOs() ?