Skip to content

Conversation

@mxmilkiib
Copy link
Contributor

displays hotcue markers and minute markers on library overview waveform column, similar to rekordbox.

changes:

  • hotcue markers: 10px wide bright vertical lines at each hotcue position
  • minute markers: white vertical ticks at top and bottom every 60 seconds
  • rendering: markers drawn after waveform normalization for maximum visibility
  • colors: uses smart contrast colors, 60% brighter for visibility

implementation:

  • modified waveformOverviewRenderer::render() to accept hotcue data and track duration
  • extended OverviewCache::prepareOverview() to load cues via CueDAO and track metadata
  • added HotcueInfo struct to pass cue position, color, and label to renderer
  • disabled smooth pixmap transform in delegate to preserve marker opacity

fixes #14994

- add hotcue marker rendering in library overview waveform column
- load cue data and track metadata (duration, sample rate) in OverviewCache::prepareOverview()
- draw hotcue markers as semi-transparent vertical lines with cue colors
- calculate hotcue positions proportionally across waveform width based on track duration

implements mixxxdj#14994
@mxmilkiib mxmilkiib force-pushed the feature/hotcues-on-overview-waveform branch from 9323387 to 4f2aace Compare October 20, 2025 05:46
markerPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why we need to set the CompositionMode?
And why you picked 10px (!) for a position marker?

Also note that both the waveforms and the overview draw wider, contrasting "shadow" lines underneath the actual marker lines to increase visibility (colors set here, shadow lines set here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for the intermediate baseColor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried a few ways to get the lines to be as distinct as on the deck overview but struggled

10px made it almost bearable

I'll see what I can do now

and no

Copy link
Member

@ronso0 ronso0 Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried a few ways to get the lines to be as distinct as on the deck overview but struggled

Okay, that's because this paints the full-width waveform and the scaling to desired width happens in OverviewCache (IIRC this has been adopted from CoverArtCache), hence no fixed width here will guarantee that the markers have adequate width in the final image in the library.

Options are a) pass the resized image here (to a new function) to paint the hotcue overlay, b) do the scaling here and paint hotcues on top, or c) ...

Back'n forth of a) is not ideal (heayv QImage), so it's b) unless you/ai figure another way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, please remove those // MARK: .. comments, IMO they're pointless

Comment on lines 186 to 199
QSqlQuery query(database);
query.prepare(QStringLiteral("SELECT " LIBRARY_TABLE ".") +
LIBRARYTABLE_DURATION + QStringLiteral(", " LIBRARY_TABLE ".") +
LIBRARYTABLE_SAMPLERATE +
QStringLiteral(" FROM " LIBRARY_TABLE " WHERE " LIBRARY_TABLE ".id=:id"));
query.bindValue(":id", trackId.toVariant());
if (query.exec() && query.next()) {
trackDurationMillis = query.value(0).toDouble() *
1000.0; // convert seconds to milliseconds
sampleRate = query.value(1).toInt();
if (sampleRate <= 0) {
sampleRate = 44100; // fallback if invalid
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing SQL queries here we may get values from columns in the delegate?
If possible I'd prefer that instead of acting on the db here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ completely removed database queries from prepareOverview()
✅ now passes TrackPointer from delegate
✅ gets cues via pTrack->getCueInfos() and duration via pTrack->getDuration()

2,
imageHeight - lowerMarkerYPos,
minuteColor);
markerPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why set/reset the CompositionMode for each marker?
Consider using PainterScope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ now using PainterScope instead of manual setCompositionMode() calls

// draw hotcue markers
if (!hotcues.isEmpty()) {
for (const auto& hotcue : hotcues) {
if (hotcue.positionMillis <= 0 || hotcue.positionMillis > trackDurationMillis) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why skip hotcues in the preroll?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ now allows negative positions (removed positionMillis <= 0 check)
✅ only skips hotcues beyond track end

Comment on lines 16 to 20
struct HotcueInfo {
double positionMillis;
mixxx::RgbColor::code_t colorCode;
QString label;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a struct if we already have the CueInfo class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ removed custom struct
✅ now uses existing mixxx::CueInfo class directly
✅ filters by CueType::HotCue in renderer

Milkii Brewster added 2 commits October 20, 2025 23:31
- use existing CueInfo instead of custom HotcueInfo struct
- pass track metadata from delegate instead of SQL queries in OverviewCache
- use PainterScope for hotcue rendering
- match deck overview marker style: 1px border + 1px fill line
- allow preroll hotcues (negative positions)
- remove unnecessary intermediate variables
Track does not have a getCueInfos() method - need to iterate
through getCuePoints() and call getCueInfo(sampleRate) on each cue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

library: display hotcues on overview waveform? (like Rekordbox)

2 participants