Add a clipping indicator next to the audio levels (d5c64109) · Commits · Multimedia / Kdenlive · GitLab
Admin message
Join us at
Akademy
to celebrate KDE's 30th anniversary!
Travel support requests
are open till May 31st.
Register now
Commit
d5c64109
authored
Aug 22, 2025
by
balooii balooii
Browse files
parent
8feef446
Loading
Loading
Loading
Loading
Changes
Pipelines
Loading
Original line number
Diff line number
Diff line
@@ -29,6 +29,10 @@ constexpr int MAXIMUM_SECONDARY_AXIS_LENGTH = 7; // maximum height/width for aud
constexpr
int
NO_AUDIO_PRIMARY_AXIS_POSITION
constexpr
double
MIN_DISPLAY_DB
100.0
// Don't display levels below this threshold
// Clipping indicator constants
constexpr
int
CLIPPING_INDICATOR_SPACING
// 4px spacing between levels and clipping indicator
constexpr
int
CLIPPING_INDICATOR_SIZE
// Size of the clipping indicator rectangle
constexpr
qreal
HIDPI_OFFSET_ADJUSTMENT
0.5
constexpr
qreal
HIDPI_LENGTH_ADJUSTMENT
1.0
@@ -219,7 +223,8 @@ void AudioLevelRenderer::drawChannelBorders(QPainter &painter, const RenderData
else
qreal
totalWidth
data
audioChannels
data
secondaryAxisLength
data
audioChannels
CHANNEL_BORDER_WIDTH
qreal
totalHeight
data
primaryAxisLength
CHANNEL_BORDER_WIDTH
QRectF
drawingRect
PainterUtils
::
adjustedForPen
QRectF
effectiveBorderOffset
totalWidth
totalHeight
),
pen
widthF
());
qreal
verticalOffset
data
showClippingIndicator
CLIPPING_INDICATOR_SIZE
CLIPPING_INDICATOR_SPACING
QRectF
drawingRect
PainterUtils
::
adjustedForPen
QRectF
effectiveBorderOffset
verticalOffset
totalWidth
totalHeight
),
pen
widthF
());
if
fillBackground
&&
channelBackgroundColor
isValid
())
painter
fillRect
drawingRect
channelBackgroundColor
);
@@ -227,7 +232,9 @@ void AudioLevelRenderer::drawChannelBorders(QPainter &painter, const RenderData
qreal
channelWidth
data
secondaryAxisLength
for
int
data
audioChannels
++
qreal
effectiveBorderOffset
channelWidth
CHANNEL_BORDER_WIDTH
);
painter
drawLine
PainterUtils
::
adjustedVerticalLine
CHANNEL_BORDER_WIDTH
data
primaryAxisLength
pen
widthF
()));
qreal
verticalOffset
data
showClippingIndicator
CLIPPING_INDICATOR_SIZE
CLIPPING_INDICATOR_SPACING
painter
drawLine
PainterUtils
::
adjustedVerticalLine
verticalOffset
CHANNEL_BORDER_WIDTH
verticalOffset
data
primaryAxisLength
pen
widthF
()));
painter
restore
();
@@ -306,7 +313,8 @@ void AudioLevelRenderer::drawDbScale(QPainter &painter, const RenderData &data)
data
layoutState
shouldDrawLabels
()
||
data
layoutState
isInHoverLabelMode
())
&&
data
layoutState
getWidgetSize
().
height
()
>=
spaceForTwoLabels
for
int
dbLabelCount
++
int
value
dbscale
at
);
dBToPrimaryOffset
value
data
maxDb
data
primaryAxisLength
data
orientation
);
qreal
verticalOffset
data
showClippingIndicator
CLIPPING_INDICATOR_SIZE
CLIPPING_INDICATOR_SPACING
dBToPrimaryOffset
value
data
maxDb
data
primaryAxisLength
data
orientation
verticalOffset
// Draw tick mark
painter
drawLine
PainterUtils
::
adjustedHorizontalLine
TICK_MARK_LENGTH
pen
widthF
()));
@@ -457,13 +465,14 @@ void AudioLevelRenderer::drawChannelPeakIndicator(QPainter &painter, const Rende
peakY
peakHeight
painter
fillRect
QRectF
secondaryOffset
peakY
peakHeight
secondaryLength
peakHeight
),
peakColor
);
qreal
verticalOffset
data
showClippingIndicator
CLIPPING_INDICATOR_SIZE
CLIPPING_INDICATOR_SPACING
painter
fillRect
QRectF
secondaryOffset
verticalOffset
peakY
peakHeight
secondaryLength
peakHeight
),
peakColor
);
void
AudioLevelRenderer
::
drawChannelLevelsSolid
QPainter
painter
const
RenderData
data
const
auto
levelColors
AudioLevelStyleProvider
::
instance
().
getLevelsFillColors
data
palette
);
auto
levelColors
AudioLevelStyleProvider
::
instance
().
getLevelsFillColors
();
for
int
data
audioChannels
++
double
value
data
valueDecibels
at
);
@@ -504,13 +513,14 @@ void AudioLevelRenderer::drawChannelLevelsSolid(QPainter &painter, const RenderD
painter
fillRect
QRectF
segStart
secondaryOffset
segEnd
segStart
secondaryLength
),
levelColors
red
);
else
// Vertical orientation
qreal
verticalOffset
data
showClippingIndicator
CLIPPING_INDICATOR_SIZE
CLIPPING_INDICATOR_SPACING
if
drawGreen
segStart
CHANNEL_BORDER_WIDTH
data
primaryAxisLength
segStart
verticalOffset
CHANNEL_BORDER_WIDTH
data
primaryAxisLength
segEnd
qMax
dBToPrimaryOffset
AudioLevelStyleProvider
::
LevelColors
::
greenThreshold
data
maxDb
data
primaryAxisLength
data
orientation
valuePrimaryAxisPosition
);
if
AudioLevelConfig
::
instance
().
drawBlockLines
())
segEnd
snapDown
segEnd
);
painter
fillRect
QRectF
secondaryOffset
segEnd
secondaryLength
segStart
segEnd
),
levelColors
green
);
painter
fillRect
QRectF
secondaryOffset
segEnd
verticalOffset
secondaryLength
segStart
segEnd
),
levelColors
green
);
if
drawYellow
segStart
segEnd
@@ -518,14 +528,14 @@ void AudioLevelRenderer::drawChannelLevelsSolid(QPainter &painter, const RenderD
qMax
dBToPrimaryOffset
AudioLevelStyleProvider
::
LevelColors
::
yellowThreshold
data
maxDb
data
primaryAxisLength
data
orientation
valuePrimaryAxisPosition
);
if
AudioLevelConfig
::
instance
().
drawBlockLines
())
segEnd
snapDown
segEnd
);
painter
fillRect
QRectF
secondaryOffset
segEnd
secondaryLength
segStart
segEnd
),
levelColors
yellow
);
painter
fillRect
QRectF
secondaryOffset
segEnd
verticalOffset
secondaryLength
segStart
segEnd
),
levelColors
yellow
);
if
drawRed
segStart
segEnd
segEnd
valuePrimaryAxisPosition
if
AudioLevelConfig
::
instance
().
drawBlockLines
())
segEnd
snapDown
segEnd
);
segEnd
+=
CHANNEL_BORDER_WIDTH
painter
fillRect
QRectF
secondaryOffset
segEnd
secondaryLength
segStart
segEnd
),
levelColors
red
);
painter
fillRect
QRectF
secondaryOffset
segEnd
verticalOffset
secondaryLength
segStart
segEnd
),
levelColors
red
);
@@ -547,7 +557,7 @@ void AudioLevelRenderer::drawChannelLevelsGradient(QPainter &painter, const Rend
QColor
bgColor
AudioLevelStyleProvider
::
instance
().
getChannelBackgroundColor
data
palette
);
painter
setOpacity
1.0
);
if
data
orientation
==
Qt
::
Horizontal
QLinearGradient
gradient
AudioLevelStyleProvider
::
instance
().
getLevelsFillGradient
data
palette
data
orientation
data
maxDb
);
QLinearGradient
gradient
AudioLevelStyleProvider
::
instance
().
getLevelsFillGradient
data
orientation
data
maxDb
);
gradient
setStart
CHANNEL_BORDER_WIDTH
);
gradient
setFinalStop
data
primaryAxisLength
);
painter
fillRect
QRectF
CHANNEL_BORDER_WIDTH
secondaryOffset
data
primaryAxisLength
CHANNEL_BORDER_WIDTH
secondaryLength
),
gradient
);
@@ -555,12 +565,14 @@ void AudioLevelRenderer::drawChannelLevelsGradient(QPainter &painter, const Rend
painter
fillRect
QRectF
valuePrimaryOffset
secondaryOffset
data
primaryAxisLength
valuePrimaryOffset
secondaryLength
),
bgColor
);
else
QLinearGradient
gradient
AudioLevelStyleProvider
::
instance
().
getLevelsFillGradient
data
palette
data
orientation
data
maxDb
);
gradient
setStart
CHANNEL_BORDER_WIDTH
data
primaryAxisLength
);
gradient
setFinalStop
CHANNEL_BORDER_WIDTH
);
painter
fillRect
QRectF
secondaryOffset
CHANNEL_BORDER_WIDTH
secondaryLength
data
primaryAxisLength
),
gradient
);
QLinearGradient
gradient
AudioLevelStyleProvider
::
instance
().
getLevelsFillGradient
data
orientation
data
maxDb
);
qreal
verticalOffset
data
showClippingIndicator
CLIPPING_INDICATOR_SIZE
CLIPPING_INDICATOR_SPACING
gradient
setStart
verticalOffset
CHANNEL_BORDER_WIDTH
data
primaryAxisLength
);
gradient
setFinalStop
verticalOffset
CHANNEL_BORDER_WIDTH
);
painter
fillRect
QRectF
secondaryOffset
verticalOffset
CHANNEL_BORDER_WIDTH
secondaryLength
data
primaryAxisLength
),
gradient
);
if
valuePrimaryOffset
CHANNEL_BORDER_WIDTH
painter
fillRect
QRectF
secondaryOffset
CHANNEL_BORDER_WIDTH
secondaryLength
valuePrimaryOffset
CHANNEL_BORDER_WIDTH
),
bgColor
);
painter
fillRect
QRectF
secondaryOffset
verticalOffset
CHANNEL_BORDER_WIDTH
secondaryLength
valuePrimaryOffset
CHANNEL_BORDER_WIDTH
),
bgColor
);
@@ -577,3 +589,48 @@ void AudioLevelRenderer::drawChannelLevels(QPainter &painter, const RenderData &
drawBlockLines
painter
data
);
void
AudioLevelRenderer
::
drawClippingIndicators
QPainter
painter
const
RenderData
data
if
data
showClippingIndicator
||
data
clippingStates
size
()
!=
data
audioChannels
return
QColor
borderColor
AudioLevelStyleProvider
::
instance
().
getBorderColor
data
palette
data
isEnabled
);
QColor
clippingColor
AudioLevelStyleProvider
::
instance
().
getClippingColor
();
QColor
neutralColor
AudioLevelStyleProvider
::
instance
().
getChannelBackgroundColor
data
palette
);
for
int
data
audioChannels
++
bool
isClipping
data
clippingStates
];
QColor
fillColor
isClipping
clippingColor
neutralColor
// To fix glitches in HiDPI with fractional scaling lets slightly enlarge the drawing area as we could otherwise end up with a gap of 1px between the
// levels fill and the border. We'll have to redraw the borders to ensure they are not covered by the levels.
qreal
secondaryOffset
channelToSecondaryOffset
data
secondaryAxisLength
data
layoutState
getBorderOffset
(),
data
orientation
HIDPI_OFFSET_ADJUSTMENT
qreal
secondaryLength
data
secondaryAxisLength
HIDPI_LENGTH_ADJUSTMENT
if
data
orientation
==
Qt
::
Horizontal
// Draw clipping indicator to the right of the levels
qreal
indicatorX
data
primaryAxisLength
CLIPPING_INDICATOR_SPACING
qreal
indicatorY
secondaryOffset
qreal
indicatorWidth
CLIPPING_INDICATOR_SIZE
qreal
indicatorHeight
secondaryLength
painter
fillRect
QRectF
indicatorX
indicatorY
indicatorWidth
indicatorHeight
),
fillColor
);
painter
setPen
QPen
borderColor
CHANNEL_BORDER_WIDTH
));
painter
drawRect
QRectF
indicatorX
indicatorY
indicatorWidth
indicatorHeight
));
else
// Draw clipping indicator above the levels
qreal
indicatorX
secondaryOffset
qreal
indicatorY
qreal
indicatorWidth
secondaryLength
qreal
indicatorHeight
CLIPPING_INDICATOR_SIZE
painter
fillRect
QRectF
indicatorX
indicatorY
indicatorWidth
indicatorHeight
),
fillColor
);
painter
setPen
QPen
borderColor
CHANNEL_BORDER_WIDTH
));
painter
drawRect
QRectF
indicatorX
indicatorY
indicatorWidth
indicatorHeight
));
Original line number
Diff line number
Diff line
@@ -90,6 +90,10 @@ public:
// Layout state - replaces multiple individual parameters
AudioLevelLayoutState
layoutState
// Clipping indicator support
bool
showClippingIndicator
false
QVector
bool
clippingStates
RenderData
const
AudioLevelLayoutState
::
Config
layoutConfig
layoutState
layoutConfig
@@ -103,6 +107,7 @@ public:
void
drawBackground
QPainter
painter
const
RenderData
data
);
void
drawChannelLevels
QPainter
painter
const
RenderData
data
);
void
drawChannelBordersToPixmap
QPixmap
pixmap
const
RenderData
data
);
void
drawClippingIndicators
QPainter
painter
const
RenderData
data
);
// Helper methods for coordinate conversion
static
int
dBToPrimaryOffset
double
dB
int
maxDb
int
primaryLength
Qt
::
Orientation
orientation
);
Original line number
Diff line number
Diff line
@@ -15,7 +15,7 @@ AudioLevelStyleProvider &AudioLevelStyleProvider::instance()
return
instance
AudioLevelStyleProvider
::
LevelColors
AudioLevelStyleProvider
::
getLevelsFillColors
const
QPalette
palette
const
AudioLevelStyleProvider
::
LevelColors
AudioLevelStyleProvider
::
getLevelsFillColors
()
const
LevelColors
colors
colors
darkGreen
QColor
135
60
);
@@ -60,10 +60,15 @@ QColor AudioLevelStyleProvider::getBorderColor(const QPalette &palette, bool isE
QColor
AudioLevelStyleProvider
::
getClippingColor
()
const
return
QColor
225
39
41
);
// Same as the red from LevelColors
QColor
AudioLevelStyleProvider
::
getPeakColor
const
QPalette
palette
double
peakValue
const
if
AudioLevelConfig
::
instance
().
peakIndicatorStyle
()
==
AudioLevel
::
PeakIndicatorStyle
::
Colorful
LevelColors
levelColors
getLevelsFillColors
palette
);
LevelColors
levelColors
getLevelsFillColors
();
if
peakValue
LevelColors
::
yellowThreshold
return
levelColors
red
else
if
peakValue
LevelColors
::
greenThreshold
@@ -91,9 +96,9 @@ QColor AudioLevelStyleProvider::getChannelBackgroundColor(const QPalette &palett
QLinearGradient
AudioLevelStyleProvider
::
getLevelsFillGradient
const
QPalette
palette
Qt
::
Orientation
orientation
double
maxDb
const
QLinearGradient
AudioLevelStyleProvider
::
getLevelsFillGradient
Qt
::
Orientation
orientation
double
maxDb
const
auto
levelColors
getLevelsFillColors
palette
);
auto
levelColors
getLevelsFillColors
();
QLinearGradient
gradient
if
orientation
==
Qt
::
Horizontal
Original line number
Diff line number
Diff line
@@ -32,11 +32,12 @@ public:
static
AudioLevelStyleProvider
instance
();
LevelColors
getLevelsFillColors
const
QPalette
palette
const
LevelColors
getLevelsFillColors
()
const
QColor
getBorderColor
const
QPalette
palette
bool
isEnabled
const
QColor
getClippingColor
()
const
QColor
getPeakColor
const
QPalette
palette
double
peakValue
const
QColor
getChannelBackgroundColor
const
QPalette
palette
const
QLinearGradient
getLevelsFillGradient
const
QPalette
palette
Qt
::
Orientation
orientation
double
maxDb
const
QLinearGradient
getLevelsFillGradient
Qt
::
Orientation
orientation
double
maxDb
const
private
AudioLevelStyleProvider
()
default
// Private constructor for singleton
Original line number
Diff line number
Diff line
@@ -23,6 +23,7 @@ constexpr int MINIMUM_SECONDARY_AXIS_LENGTH = 3; // minimum height/width for a
constexpr
int
MAXIMUM_SECONDARY_AXIS_LENGTH
// maximum height/width for audio level channels
constexpr
int
MARGIN_BETWEEN_LABEL_AND_LEVELS
// px between decibels scale labels and audio levels
constexpr
int
TICK_MARK_LENGTH
// px for tick mark
constexpr
int
SPACE_FOR_CLIPPING_INDICATOR
// Size of the clipping indicator + space between it and the levels (6px + 4px = 10px, see AudioLevelRenderer)
constexpr
double
MIN_DISPLAY_DB
100.0
// Minimum displayable audio level, used to hide decayed peaks and no audio audio levels
constexpr
double
CLIPPING_THRESHOLD_DB
0.0
// Maximum displayable audio level
constexpr
int
NO_AUDIO_PRIMARY_AXIS_POSITION
@@ -32,9 +33,10 @@ constexpr int NO_AUDIO_PRIMARY_AXIS_POSITION = -1;
* @param parent
* @param orientation Qt::Vertical or Qt::Horizontal
* @param tickLabelsMode TickLabelsMode for drawing tick marks and labels
* @param showClippingIndicator Whether to show clipping indicators
* @param backgroundColor Optional color for channel background, defaults to Window color
*/
AudioLevelWidget
::
AudioLevelWidget
QWidget
parent
Qt
::
Orientation
orientation
AudioLevel
::
TickLabelsMode
tickLabelsMode
AudioLevelWidget
::
AudioLevelWidget
QWidget
parent
Qt
::
Orientation
orientation
AudioLevel
::
TickLabelsMode
tickLabelsMode
bool
showClippingIndicator
QWidget
parent
audioChannels
pCore
->
audioChannels
())
m_displayToolTip
false
@@ -54,6 +56,7 @@ AudioLevelWidget::AudioLevelWidget(QWidget *parent, Qt::Orientation orientation,
m_cachedSecondaryAxisLength
m_axisDimensionsNeedUpdate
true
m_renderer
new
AudioLevelRenderer
this
))
m_showClippingIndicator
showClippingIndicator
QFont
ft
QFontDatabase
::
systemFont
QFontDatabase
::
SmallestReadableFont
));
ft
setPointSizeF
ft
pointSize
()
0.6
);
@@ -130,6 +133,10 @@ void AudioLevelWidget::paintEvent(QPaintEvent * /*pe*/)
AudioLevelRenderer
::
RenderData
renderData
createRenderData
();
m_renderer
->
drawChannelLevels
renderData
);
if
m_showClippingIndicator
m_renderer
->
drawClippingIndicators
renderData
);
// Draw cached channel borders on top
drawPixmap
m_bordersCache
);
@@ -295,6 +302,17 @@ void AudioLevelWidget::setAudioValues(const QVector
m_axisDimensionsNeedUpdate
true
updateLayoutAndSizing
();
// Initialize clipping states and frame counters if needed
if
m_showClippingIndicator
&&
channelCountChanged
||
m_clippingStates
size
()
!=
audioChannels
))
m_clippingStates
resize
audioChannels
);
m_clippingFrameCounters
resize
audioChannels
);
for
int
audioChannels
++
m_clippingStates
false
m_clippingFrameCounters
if
peaksInitialized
m_peakDecibels
m_valueDecibels
@@ -317,6 +335,11 @@ void AudioLevelWidget::setAudioValues(const QVector
if
m_showClippingIndicator
updateClippingStates
();
updatePrimaryAxisPositions
();
update
();
@@ -328,6 +351,14 @@ void AudioLevelWidget::reset()
m_valueDecibels
MIN_DISPLAY_DB
// Reset clipping indicators
if
m_showClippingIndicator
for
int
audioChannels
++
m_clippingStates
false
m_clippingFrameCounters
// Reset peaks
for
int
m_peakDecibels
size
();
++
m_peakDecibels
MIN_DISPLAY_DB
@@ -337,6 +368,35 @@ void AudioLevelWidget::reset()
update
();
void
AudioLevelWidget
::
updateClippingStates
()
if
m_showClippingIndicator
||
m_clippingStates
size
()
!=
audioChannels
return
// Calculate frame count for 4-second decay based on current project frame rate
const
double
currentFps
pCore
->
getCurrentFps
();
const
int
clippingDecayFrames
static_cast
int
currentFps
4.0
);
// Clipping decay logic: clipping indicators persist for a fixed time period
// to make brief clipping events visible even if they occur between updates
for
int
audioChannels
++
bool
isClipping
m_valueDecibels
at
>=
CLIPPING_THRESHOLD_DB
if
isClipping
// Start or restart the frame counter when clipping is detected
m_clippingStates
true
m_clippingFrameCounters
// Reset frame counter
else
if
m_clippingStates
])
// Increment frame counter and check if decay period has expired
m_clippingFrameCounters
++
if
m_clippingFrameCounters
>=
clippingDecayFrames
m_clippingStates
false
void
AudioLevelWidget
::
updateToolTip
()
if
isEnabled
())
@@ -471,7 +531,19 @@ void AudioLevelWidget::updateAxisLengths()
return
m_cachedPrimaryAxisLength
AudioLevelRenderer
::
calculatePrimaryAxisLength
size
(),
m_orientation
AudioLevelConfig
::
instance
().
drawBlockLines
());
// Calculate available size for audio levels, accounting for clipping indicators
QSize
availableSize
size
();
if
m_showClippingIndicator
if
m_orientation
==
Qt
::
Horizontal
// Reserve space on the right for clipping indicators
availableSize
setWidth
availableSize
width
()
SPACE_FOR_CLIPPING_INDICATOR
);
else
// Reserve space at the top for clipping indicators
availableSize
setHeight
availableSize
height
()
SPACE_FOR_CLIPPING_INDICATOR
);
m_cachedPrimaryAxisLength
AudioLevelRenderer
::
calculatePrimaryAxisLength
availableSize
m_orientation
AudioLevelConfig
::
instance
().
drawBlockLines
());
// Use layout state to calculate secondary axis length
AudioLevelLayoutState
layoutState
createLayoutConfig
());
@@ -507,5 +579,8 @@ AudioLevelRenderer::RenderData AudioLevelWidget::createRenderData() const
renderData
fontMetrics
fontMetrics
();
renderData
primaryAxisLength
m_cachedPrimaryAxisLength
renderData
secondaryAxisLength
m_cachedSecondaryAxisLength
renderData
showClippingIndicator
m_showClippingIndicator
renderData
clippingStates
m_clippingStates
return
renderData
No newline at end of file
Loading
US