Your lossless library and your streaming apps, played untouched to your DAC.
A native Linux music player that is also a UPnP / OpenHome network receiver.
Features · Screenshots · Install · Stream from your phone · Bit-perfect · FAQ · Roadmap
Most Linux players either sound right or look right. ricercar tries to do both:
- It never touches your samples. Every track goes to an exclusive
hw:ALSA device at its native rate and bit depth: no mixer, no resampling, no volume maths. A format the DAC can't take is refused, never quietly converted. - It shows the signal path instead of just claiming bit-perfect. The chain (source → processing → output format → DAC) sits under the seek bar at all times; one click opens every hop and flags anything that changes the samples.
- It is a real desktop app. Album grid, artist pages, synced lyrics, queue, playlists, radio and search, native and fast, with no webview and no Electron.
- It plays what your phone streams. BubbleUPnP, Symfonium, Linn Kazoo or mConnect push Qobuz, Tidal or your NAS to ricercar the same way they would push to a hi-fi network streamer.
ricercar is inspired by QBZ but is a clean-room project with no QBZ code. Like QBZ, it contains no private or unofficial streaming-service API. Catalogues reach it through standard UPnP / OpenHome, with your own subscription in your own app.
|
|
|
|
Screenshots use a synthetic demo library (generated tones and artwork).
They are rendered with RICERCAR_SNAPSHOT, see Development.
Every release ships ready-made packages for x86_64 and aarch64:
| Your system | Package | Install |
|---|---|---|
| Debian, Ubuntu, Mint, Pop!_OS… | .deb |
sudo apt install ./ricercar_*.deb |
| Fedora, openSUSE, RHEL / Alma / Rocky | .rpm |
sudo dnf install ./ricercar-*.rpm |
| Arch, Manjaro, EndeavourOS, CachyOS, Omarchy | .pkg.tar.zst |
sudo pacman -U ricercar-*.pkg.tar.zst |
| Anything else | .AppImage |
chmod +x ricercar-*.AppImage && ./ricercar-*.AppImage |
Arch / Manjaro / Omarchy
sudo pacman -S --needed rust alsa-lib fontconfig libxkbcommon
git clone https://github.com/pata27/ricercar && cd ricercar
cargo build --releaseOr build the package: cd dist/arch && makepkg -si.
Debian / Ubuntu
sudo apt install cargo libasound2-dev libfontconfig1-dev libxkbcommon-dev pkg-config
git clone https://github.com/pata27/ricercar && cd ricercar
cargo build --releaseFedora
sudo dnf install cargo alsa-lib-devel fontconfig-devel libxkbcommon-devel
git clone https://github.com/pata27/ricercar && cd ricercar
cargo build --releaseRust 1.85 or newer is required. The binaries end up in target/release/:
| binary | what it is |
|---|---|
ricercar |
the desktop app (--headless for no window) |
ricercar-daemon |
headless player + UPnP + MPRIS, for a dedicated box |
ricercar-cli |
remote control over MPRIS |
Desktop integration: copy dist/ricercar.desktop to
~/.local/share/applications/ and dist/ricercar.svg to
~/.local/share/icons/hicolor/scalable/apps/.
./target/release/ricercar. Your XDG music folder (usually~/Music) is indexed automatically; add others in Settings → Library.- Pick your DAC in Settings → Audio output.
hw:devices are marked BIT-PERFECT;default/pipewireare marked SHARED (they go through the system mixer). - Press play. The green dot on the chain line under the seek bar means bit-perfect.
./target/release/ricercar --print-devices # list outputsricercar shows up on your network as a renderer, just like a hardware streamer. Your phone app handles sign-in and browsing, and ricercar fetches the audio itself.
flowchart LR
phone["📱 Phone app<br/>BubbleUPnP · Symfonium · Kazoo"]
service[("Streaming service<br/>or NAS")]
ric["🖥️ ricercar<br/>decode · no resampling"]
dac["🔊 DAC<br/>hw: exclusive"]
phone -- "UPnP / OpenHome<br/>commands only" --> ric
phone -. "your subscription" .- service
service -- "FLAC over HTTP" --> ric
ric -- "bit-perfect PCM" --> dac
- Start ricercar on the PC wired to your DAC (desktop app or daemon).
- In your phone app, choose ricercar as the renderer. With OpenHome (BubbleUPnP, Kazoo…) the queue lives on the PC and your phone can sleep.
- Play anything the app can reach. ricercar's player bar, MPRIS and the signal-path view show exactly what reaches the DAC.
See docs/CONTROLS.md for the control-point compatibility matrix and what the protocol tests cover.
For an always-on audio box:
systemctl --user enable --now ricercar # uses dist/ricercar.serviceWhen the output is a hw: device, the volume is at 100 %, ReplayGain is off
and nothing is muted, samples reach the kernel untouched:
- the device is opened at the track's native rate and channel count, and a mismatch is refused, not resampled;
- the container holds every bit (16-bit audio may travel zero-padded in S32 if that is all the DAC accepts, which is still bit-perfect);
- a rate change between tracks drains and reopens the device; within one rate, gapless is sample-exact.
The signal-path view marks each hop green (untouched) or amber (altered: software volume, ReplayGain, a shared device…). The pipeline tests compare decoded output byte for byte against ffmpeg references for every container.
| Keys | Action | Keys | Action | |
|---|---|---|---|---|
| Space | Play / pause | Ctrl F | Search | |
| Ctrl → / ← | Next / previous | Ctrl L | Now playing & lyrics | |
| → / ← | Seek ± 10 s | Ctrl Q | Queue | |
| Ctrl ↑ / ↓ | Volume | Esc | Close panel |
Everything is editable in Settings. It is stored in
~/.config/ricercar/config.toml, and command-line flags override it for
one run.
[audio]
device = "hw:1,0" # see --print-devices
replaygain = "off" # off | track | album | auto
preamp_db = 0.0
restore_session = true
[library]
roots = ["/home/me/Music", "/mnt/nas/flac"]
watch = true
[network]
name = "Living room" # name shown in phone apps
renderer = true # UPnP AV + OpenHome renderer
media_server = true # share the library over UPnP
[ui]
theme = "dark" # dark | light
accent = "#d4a35a"
adaptive_colors = true # tint the UI from the album cover
tray = true
close_to_tray = false
[scrobble]
listenbrainz_token = ""
lastfm_api_key = "" # your own Last.fm API account
[online]
lyrics = true # lrclib.net when no local lyrics exist
cover_art = true # MusicBrainz / Cover Art Archive for missing covers
radio = trueCommand line
ricercar [FILE|URI]... [--headless] [--config FILE] [--device NAME] [--name NAME]
[--db PATH] [--library DIR]... [--no-mpris] [--no-upnp] [--no-session]
ricercar --print-devices
ricercar-cli status | play | pause | toggle | stop | next | prev
ricercar-cli open FILE|URI seek ±SECONDS volume [0..1]
ricercar-cli shuffle [on|off] repeat [none|track|playlist] metadata
Only one instance owns the DAC: ricercar song.flac while ricercar is
running hands the file to the running instance.
Where things are stored
| path | content |
|---|---|
~/.config/ricercar/config.toml |
settings |
~/.local/share/ricercar/library.db |
library index, stats, playlists |
~/.local/share/ricercar/session.json |
queue & position |
~/.cache/ricercar/ |
cover thumbnails, lyrics, stream spool |
ricercar works fully offline. Its optional online features contact only:
| service | used for | toggle |
|---|---|---|
| lrclib.net | synced lyrics | Settings → Online extras |
| MusicBrainz / Cover Art Archive | missing album covers | Settings → Online extras |
| Radio Browser | the Radio page | opening the page |
| ListenBrainz / Last.fm | scrobbling | only with your own credentials |
No telemetry, no account, and never a streaming-service API.
flowchart TB
ui["ricercar-ui<br/><sub>Slint desktop app</sub>"]
daemon["ricercar-daemon<br/><sub>startup · scrobbler · headless</sub>"]
cli["ricercar-cli"]
core["ricercar-core<br/><sub>library · queue controller · covers · config</sub>"]
audio["ricercar-audio<br/><sub>symphonia decode · ALSA sink · engine</sub>"]
upnp["ricercar-upnp<br/><sub>SSDP · AVTransport · OpenHome · ContentDirectory</sub>"]
mpris["ricercar-mpris"]
online["ricercar-online<br/><sub>ListenBrainz · Last.fm · lrclib · radio · cover art</sub>"]
ui --> daemon
daemon --> core & upnp & mpris & online
upnp --> core
mpris --> core
core --> audio
cli -. D-Bus .-> mpris
| crate | role |
|---|---|
ricercar-audio |
decode + sink pipeline, engine thread, HTTP spooling |
ricercar-core |
SQLite/FTS5 library, tags, covers, queue controller, config |
ricercar-upnp |
SSDP / HTTP / SOAP / GENA, AVTransport, OpenHome, MediaServer |
ricercar-mpris |
org.mpris.MediaPlayer2 on the session bus |
ricercar-online |
scrobbling, lyrics, radio directory, cover art |
ricercar-daemon |
shared startup, scrobbler, headless binary |
ricercar-ui |
Slint desktop app (ricercar binary) |
ricercar-cli |
MPRIS remote control |
cargo test --workspace # 180+ tests
cargo clippy --workspace --all-targets -- -D warnings
dbus-run-session -- cargo test -p ricercar-mpris- Your speakers are safe: tests only use
nullandfile:sinks; no audio is sent to hardware duringcargo test. - Screenshots without a display:
RICERCAR_SNAPSHOT=out/ ricercar --device null --no-upnp --no-mpris --library <dir>renders the main views with Slint's software renderer and writes PNGs. - Translations: French strings live in
crates/ricercar-ui/tools/fr.json; runtools/gen_po.pyafter changing UI text (CI checks that the catalogue is up to date). - Design system: tokens and rules are documented in DESIGN.md.
Is this a Qobuz / Tidal client?
No. ricercar never talks to a streaming service's API. Your phone app, used with your own subscription, sends ricercar a plain stream URL over UPnP or OpenHome, the way it would to a network streamer. That keeps the project, and your account, on safe ground.
Why is my track "altered" and not bit-perfect?
Open the signal-path view (click the chain line under the seek bar). Common
causes: volume below 100 %, ReplayGain enabled, mute, or a shared output
(default, pipewire, pulse) instead of a hw: device.
My DAC refuses a track.
With a hw: device, ricercar refuses a rate or bit depth that the DAC can't
play natively rather than resampling it. Pick the pipewire / default
output if you prefer conversion over refusal.
Does it work with PipeWire?
Yes. Selecting a hw: device takes the card exclusively while ricercar
plays and releases it on stop. Choose pipewire if you want to share the
card with other apps (not bit-perfect).
Can I run it on a Raspberry Pi without a screen?
Yes. Use ricercar-daemon (or ricercar --headless) with the systemd user
unit in dist/, and control it from your phone over UPnP / OpenHome.
- Bit-perfect ALSA engine, gapless, native-rate switching
- UPnP AV + OpenHome renderer, UPnP MediaServer
- Desktop app: library, lyrics, queue, playlists, radio, EN/FR
- Scrobbling, MPRIS, tray, headless daemon
- Reports from real control points (help wanted)
- Device capabilities panel (rates and formats your DAC accepts)
- Optional parametric EQ / convolution (clearly marked non bit-perfect)
- DSD over PCM (DoP), CUE sheets
- Composer / work views for classical music
- Flatpak and AppImage
Issues and pull requests are welcome. The most useful contribution right now is a compatibility report: your phone app, its version, your DAC, and what worked or broke (see docs/CONTROLS.md). Please follow CONTRIBUTING.md, and note that the project will not host, list or promote any code that bypasses a streaming service's official API.
Built on symphonia, Slint, lofty, rusqlite and zbus. Typeface Inter (SIL OFL), icons Lucide (ISC). Data from MusicBrainz, Cover Art Archive, LRCLIB and Radio Browser. Thanks to the QBZ project for showing what a Linux hi-fi player can be.
MIT © ricercar contributors. See AUTHORS.
ricercar is an independent open-source project. It is not affiliated with, endorsed by, or connected to the record label Ricercar (Outhere), Qobuz, or any other streaming service. "Ricercar" is used in its old musical sense: a contrapuntal study, literally "to search".









