mpd-qt6
Garnetune
A desktop MPD client written in Crystal using Qt6 shard.
Screenshots

Features
- Playback controls with progress seeking, shuffle/repeat, volume control, and current song metadata.
- Album art support, including a full-size cover preview and optional blurred cover background in the playback header.
- Queue management with multi-select, drag-and-drop reordering, keyboard playback, row removal, and context menu actions.
- Library browser grouped by artist, album, and song, with natural album/year and disc/track sorting.
- Library search by artist, album, title, and file path, plus genre filtering from MPD's database tags.
- Drag artists, albums, songs, or stored playlist tracks into the queue, including insertion at the drop position.
- Saved playlist management: browse MPD playlists, preview songs, save the current queue, rename/delete playlists, append playlists to the queue, or replace the queue with a playlist.
- Configurable MPD connection and optional Last.fm scrobbling.
- Optional LRCLIB lyrics support with synced lyric highlighting, auto-scroll, plain-text fallback, local cache, and copy support.
- MPD output management for enabling or disabling configured audio outputs.
- Optional MPD FIFO spectrum visualizer in the playback header.
- Linux desktop integration through MPRISv2 for media keys, desktop media widgets, metadata, position, volume, shuffle/repeat, and cover art.
- System tray support with close-to-tray behavior, restore/show toggle, and playback actions.
- Persistent UI preferences for layout, expanded mode, menu visibility, blurred cover background, visualizer settings, window size, and splitter sizes.
Requirements
- Crystal >= 1.19.1
- Garnetune must be built with Crystal's multithreaded execution context flags:
-Dpreview_mt -Dexecution_context - Qt6 Widgets development packages
- Arch:
pacman -S qt6-base - Ubuntu:
apt-get install qt6-base-dev - macOS:
brew install qt
- Arch:
- A running MPD server
Installation
git clone https://github.com/mamantoha/mpd-qt6
cd mpd-qt6
shards install
shards build --release -Dpreview_mt -Dexecution_context
./bin/garnetune
For local development, use the same flags:
crystal run src/main.cr -Dpreview_mt -Dexecution_context
Visualizer
Garnetune can show a spectrum visualizer in the playback header. MPD must be configured to write raw PCM audio to a FIFO because MPD does not expose spectrum data directly.
Add a FIFO output to mpd.conf:
audio_output {
type "fifo"
name "visualizer"
path "/tmp/mpd.fifo"
format "44100:16:2"
}
Restart MPD after changing the config. In Garnetune, open Settings -> Visualizer, enable the visualizer, and set the FIFO path to the same value, for example /tmp/mpd.fifo.
Lyrics
Garnetune can fetch lyrics from LRCLIB and show them in the middle panel between the library/playlists browser and the play queue. Synced lyrics are shown as timestamped rows with the current line highlighted during playback. Unsynced lyrics use the same read-only row layout without timestamp synchronization.
Lyrics are cached in the user's cache directory under Garnetune's application cache. The cache stores both found lyrics and "not found" results so the player does not repeatedly query LRCLIB for the same track. Some tracks may not have lyrics available, or may only have plain lyrics without timestamps.
Open Settings -> Lyrics to enable or disable online lyrics and synced lyric auto-scroll.
Dependencies
| Shard | Purpose |
|---|---|
| djberg96/crystal-qt6 | Qt6 bindings for Crystal |
| mamantoha/crystal_mpd | MPD protocol client |
Platform support
Tested on Linux and macOS with Qt6. Windows are untested.
Architecture notes
src/mpd_ui/app.crowns the mainAppclass and acts mostly as the composition root: it loads settings, creates the Qt application/window, wires views/controllers/adapters together, starts MPD, and runs the Qt event loop- Domain/service objects keep non-Qt behavior isolated:
song.crandplayback_state.crwrap MPD song metadata and current playback statecover_art_service.crfetches MPD cover art and handles the disk cover cachevisualizer_service.crreads MPD's raw FIFO audio, tracks FIFO availability/playback state, and exposes normalized levels for the UIlyrics_service.crfetches LRCLIB lyrics in the background, applies fallback lookup attempts, reads/writes the local lyrics cache, and ignores stale song requestslyrics_cache.crstores found and not-found lyrics responses in the user's cache directorylibrary_index.crhandles database filtering, artist/album/song grouping, album sorting by year, and song sorting by disc/trackbackground_task.crcentralizes short worker-thread jobs and Qt-main-thread callbacks
src/mpd_ui/dsp/contains small audio-processing helpers:spectrum_analyzer.crconverts raw PCM frames into logarithmic FFT spectrum bands for the header visualizer
- View classes under
src/mpd_ui/views/own Qt widget construction and rendering:application_menu.crbuilds the main menu/actions and menu shortcutsapp_layout_view.crarranges the player header, browser/lyrics/queue splitter, and compact spacerplayer_header_view.crowns the playback header widgets, visualizer widget, controls, volume popup, cover click handling, and progress tooltipvisualizer_widget.crpaints spectrum bars fromVisualizerServicequeue_view.crowns the queueQTreeView, context menu, shortcuts, selection helpers, drop filter, and row indicatorslibrary_view.crowns the database browser tree, search panel, genre filter, custom item delegate, context menu, drag filter, and selected URI collectionplaylists_view.crowns the saved playlist tree, playlist/song context menus, and playlist-song drag sourcelyrics_view.crowns the lyrics panel, synced lyrics list, plain-text fallback, loading/error states, active-line sync, and copy action
- Custom Qt models under
src/mpd_ui/models/adapt domain data to Qt's model/view API:queue_model.crexposes the current MPD queue as a flatQAbstractItemModelwith drag/drop payloads and row indicator updateslibrary_model.crexposes the artist/album/song database tree without building thousands ofQStandardItemobjectsplaylists_model.crexposes stored playlists and their songs as a tree model with playlist/song roles for context menus and drag/droplyrics_model.crexposes synced lyrics as a list model with timestamp and active-line highlighting roles- These models keep data in Crystal objects and let Qt query rows, parents, roles, tooltips, and MIME data on demand
- Controller classes under
src/mpd_ui/controllers/keep state transitions and queue calculations away from Qt widget setup:player_controller.crreads MPD status/current-song/playlist snapshots and converts them intoPlaybackStatetransitionsqueue_controller.crtracks queue positions/ids and plans multi-row reorders
- App glue modules under
src/mpd_ui/app/connect views/controllers/services to MPD commands and UI state:player.crhandles playback refresh, progress, volume, cover rendering, blurred header background, visualizer playback state, and current-song UI updatesqueue.crwiresQueueView/QueueControllerto MPD queue commands and database-to-queue dropsdatabase.crwiresLibraryView/LibraryIndexto MPD database loading, searching, genre filtering, and add-to-queue behaviorplaylists.crwiresPlaylistsViewto MPD saved playlist commandslyrics.crwires the lyrics panel to current playback state, lazy LRCLIB lookups, settings, and progress-based active-line syncmpris.crconnects Qt/MPD callbacks to the app-specific MPRIS adapterlastfm.crfeeds playback snapshots into the app-specific Last.fm adapteroutputs.crloads MPD audio outputs and applies output enable/disable commands from the UIwindow_events.crhandles main-window close/show/hide/resize policy, including close-to-tray when a tray icon exists and expanded-window size trackingtray.crhandles only system tray integration: tray icon/menu setup, tray activation, tray messages, tray state, and tray tooltip updatesabout_dialog.crandsettings_dialog.crkeep dialogs isolated from the main UI setup
src/mpd_ui/adapters/contains app-specific integration adapters:mpris_adapter.crownsMPRIS::Service, callback registration, playback-state mapping, current MPRIS song/artwork state, and position sync throttlinglastfm_adapter.crowns Last.fm client/scrobbler construction and playback sync
src/ext/mpriscontains a small Crystal MPRIS/DBus implementation kept separate from Qt-specific app codesrc/ext/lastfmcontains the Last.fm API client, request signing, scrobble timing, and retry cachesrc/ext/lrclibcontains the standalone LRCLIB API client and synced lyric parser- One MPD client handles commands and status reads
- A separate callback-enabled MPD listener pushes live updates from the server
EventBridgemarshals callback-thread updates safely onto the Qt main threadSettingswrapsQSettingspersistence for connection details, UI visibility preferences, visualizer configuration, layout size, and splitter state- The UI uses Qt Widgets directly, including
QMainWindow, menus/actions, push buttons, sliders, splitters, tree views, customQAbstractItemModelmodels, custom item delegates, event filters, shortcuts, and graphics effects
License
MIT
mpd-qt6
- 2
- 0
- 0
- 0
- 2
- about 7 hours ago
- April 16, 2026
MIT License
Tue, 16 Jun 2026 15:20:08 GMT