Skip to content

InstaZDLL/WaveFlow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

47 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

WaveFlow logo

WaveFlow

Local music player for desktop β€” built with Tauri 2, React 19 & Rust

Version Tauri 2 React 19 Rust License Platform


WaveFlow is a local music player desktop app with a Spotify-inspired 3-panel UI. It scans your local audio folders, organizes tracks by album/artist/genre, and plays them with a real-time audio engine β€” no streaming, no cloud, your music stays on your machine.

Features

Playback engine

  • Audio playback β€” symphonia decoder + cpal output, supports MP3, FLAC, WAV, OGG Vorbis, AAC, ALAC (M4A)
  • Real-time engine β€” lock-free 3-thread architecture (decoder, ring buffer, cpal callback), zero allocations in the hot path
  • Crossfade DSP β€” real dual-decoder mix with equal-power gains (cos/sin) over the user-set window
  • Audio settings β€” volume normalization (-3 dB), mono downmix, configurable crossfade
  • Resume β€” remembers last track + position across app restarts
  • Queue β€” persistent queue with shuffle (Fisher-Yates), repeat (off/all/one), auto-advance, drag-and-drop reorder

Library

  • Scanning β€” point to any folder, metadata extraction via lofty, embedded artwork extraction
  • Watch folders β€” notify-driven filesystem watcher, debounced rescans so dropped-in files appear automatically; deleted files are flagged unavailable rather than purged so play history survives
  • Track analysis β€” on-demand or auto-after-scan: peak, loudness (dB), ReplayGain, BPM via autocorrelation; tagged musical key (TKEY / INITIALKEY) read at scan time
  • Audio quality surfacing β€” sample rate / bitrate / size / codec / bit depth strip under the player; Hi-Res badges (β‰₯ 24-bit, β‰₯ 44.1 kHz) on covers and rows
  • Track Properties dialog β€” foobar2000-style modal with metadata, audio specs, analysis results, file path, and Show in Explorer
  • POPM ratings β€” 5-star ratings (with half-steps) extracted from tags + editable inline
  • Multi-select β€” ctrl/shift selection across views with floating action bar (Play / Add to queue / Add to playlist / Remove)
  • Multi-artist β€” automatic split of "Artist A, Artist B" into individual, independently-linkable artists
  • Album & artist detail pages β€” clickable cards open dedicated views with tracklist, discography, biography, and stats
  • Cover picker β€” manual Deezer search, local file upload (magic-byte validation), batch fetch for albums missing artwork
  • A-Z navigator β€” letter rail on the artists tab, NFD-normalized for diacritics
  • Lightbox β€” double-click any cover or artist photo to view full-size

Playlists & navigation

  • Playlists β€” create, edit, delete; add tracks from folders/albums/artists in bulk; drag-and-drop reorder (virtualized for large playlists)
  • Likes β€” heart any track, dedicated "Liked tracks" view
  • Recent β€” automatic 50-track recency list driven by play_event
  • Search β€” instant full-text search (FTS5 contentless) across titles, artists, albums with prefix matching
  • Right-click context menu β€” Spotify-style: Play next, Add to queue, Add to playlist (submenu), Like, Go to album, Go to artist (submenu when multi-artist), Properties, Show in explorer

Integrations

  • Metadata enrichment β€” Deezer public API (artist images, album covers, labels) + Last.fm (artist biographies) cached 30 days locally; artwork downloaded into a hash-addressed on-disk cache so it renders offline on re-visits
  • Last.fm scrobbling β€” signed auth.getMobileSession login, retry queue with exponential backoff, live track.updateNowPlaying, automatic re-auth prompt on session expiry
  • Synchronized lyrics β€” LRCLIB lookup with embedded-tag fallback and .lrc file import

UI & UX

  • System tray β€” quick playback controls + close-to-tray
  • Now Playing / Lyrics panels β€” Spotify-style right-edge panels (large artwork, clickable artists, artist biography, synchronized lyrics)
  • Statistics view β€” KPIs, listening-by-day / listening-by-hour charts, top tracks / artists / albums
  • Thumbnails β€” SIMD-accelerated 1x / 2x covers via fast_image_resize
  • Virtual scroll β€” handles 6000+ tracks without UI freeze (@tanstack/react-virtual)
  • Single-click play β€” optional toggle, sort memory persisted per context
  • Dark mode β€” animated radial transition via View Transitions API
  • i18n β€” French, English, Spanish, German; auto-detected, switchable in settings
  • Accessibility β€” keyboard navigation, ARIA roles, focus rings, prefers-reduced-motion
  • Profiles β€” isolated per-profile database (libraries, playlists, settings, play history); shared metadata cache across profiles

Tech Stack

Layer Technologies
Desktop shell Tauri 2.10 (tray icon, opener, dialog plugins)
Frontend React 19, TypeScript, Vite 8, Tailwind CSS 4, Lucide icons, @dnd-kit (drag-and-drop), @tanstack/react-virtual (virtualization)
Backend Rust, SQLite (sqlx), FTS5 contentless full-text search
Audio symphonia 0.5 (decode), cpal 0.15 (output), rubato 0.15 (resample), rtrb 0.3 (SPSC ring)
Metadata extraction lofty 0.22 (tags, embedded art, POPM, INITIALKEY)
Imaging image 0.25 + fast_image_resize 6 (SIMD thumbnails)
Filesystem watcher notify 8 (debounced rescans of watched folders)
External APIs Deezer public API (no auth) + Last.fm (read + signed methods via md-5 + reqwest 0.12 with rustls) + LRCLIB (synchronized lyrics)
Package manager Bun

Getting Started

# Install dependencies
bun install

# Run the desktop app in development mode
bun run tauri dev

# Build for production
bun run tauri build

Development Commands

bun run dev          # Vite dev server only (no Tauri shell)
bun run typecheck    # TypeScript check
bun run lint         # ESLint
bun run lint:fix     # ESLint with auto-fix
bun run format       # Prettier

Project Structure

waveflow/
β”œβ”€β”€ src/                              # React frontend
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ common/                   # Reusable UI (Artwork, ArtistLink, ContextMenu, TrackContextMenu, TrackPropertiesModal, HiResBadge, Lightbox, CoverPickerModal, StarRating, SelectionActionBar, modals, EmptyState…)
β”‚   β”‚   β”œβ”€β”€ layout/                   # Sidebar, TopBar, AppLayout, QueuePanel, NowPlayingPanel, LyricsPanel, DeviceMenu
β”‚   β”‚   β”œβ”€β”€ player/                   # PlayerBar, PlaybackControls, VolumeControl, ProgressBar, AudioQualityFooter
β”‚   β”‚   └── views/                    # Home, Library, Playlist, AlbumDetail, ArtistDetail, Liked, Recent, Settings, Statistics…
β”‚   β”œβ”€β”€ contexts/                     # ThemeContext, PlayerContext, LibraryContext, PlaylistContext, ProfileContext
β”‚   β”œβ”€β”€ hooks/                        # useTheme, usePlayer, useLibrary, usePlaylist, useProfile, useTrackContextMenu, useMultiSelect, useSortMemory
β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   β”œβ”€β”€ tauri/                    # Typed invoke() wrappers (track, browse, player, playlist, detail, integration, analysis, lyrics, stats, profile, dialog, deezer, library, artwork)
β”‚   β”‚   β”œβ”€β”€ hiRes.ts                  # `isHiRes` helper (β‰₯ 24-bit, β‰₯ 44.1 kHz threshold)
β”‚   β”‚   β”œβ”€β”€ imageCache.ts             # In-memory LRU for resolved artwork URLs
β”‚   β”‚   β”œβ”€β”€ playlistVisuals.ts        # Shared color/icon constants for playlists
β”‚   β”‚   └── PlaylistIcon.tsx          # Icon dispatcher component
β”‚   β”œβ”€β”€ i18n/locales/                 # fr.json, en.json, es.json, de.json
β”‚   β”œβ”€β”€ types/                        # ViewId, LibraryTab, NavItemProps, etc.
β”‚   β”œβ”€β”€ App.tsx                       # Provider tree
β”‚   └── main.tsx                      # Entry point
β”œβ”€β”€ src-tauri/                        # Rust backend
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ audio/                    # Audio engine (engine, decoder, output, resampler, state, analytics, crossfade)
β”‚   β”‚   β”œβ”€β”€ commands/                 # Tauri commands (library, playlist, track, browse, player, scan, profile, deezer, integration, lyrics, stats, analysis, maintenance, app_info)
β”‚   β”‚   β”œβ”€β”€ db/                       # Database open/migrate helpers (app.db + per-profile data.db)
β”‚   β”‚   β”œβ”€β”€ analysis.rs               # Per-track audio analysis (peak, loudness dB, ReplayGain, BPM)
β”‚   β”‚   β”œβ”€β”€ deezer.rs                 # Deezer public API client (search/get artist & album)
β”‚   β”‚   β”œβ”€β”€ lastfm.rs                 # Last.fm API client (artist.getInfo + signed mobile-session / scrobble / now-playing)
β”‚   β”‚   β”œβ”€β”€ lrclib.rs                 # LRCLIB API client (synchronized lyrics)
β”‚   β”‚   β”œβ”€β”€ metadata_artwork.rs       # Shared on-disk cache for remote artwork (blake3-hashed)
β”‚   β”‚   β”œβ”€β”€ queue.rs                  # Persistent queue operations (fill, advance, shuffle, reorder, restore)
β”‚   β”‚   β”œβ”€β”€ scrobbler.rs              # Last.fm scrobble worker (queue drain, retry/backoff, re-auth prompt)
β”‚   β”‚   β”œβ”€β”€ thumbnails.rs             # SIMD-accelerated 1x/2x cover thumbnails
β”‚   β”‚   β”œβ”€β”€ watcher.rs                # Filesystem watcher manager (per-folder notify watchers, debounced rescans)
β”‚   β”‚   β”œβ”€β”€ state.rs                  # AppState (profile pool, paths, global app_db)
β”‚   β”‚   β”œβ”€β”€ paths.rs                  # Filesystem layout
β”‚   β”‚   β”œβ”€β”€ error.rs                  # AppError + AppResult
β”‚   β”‚   └── lib.rs                    # Tauri setup, command registration, system tray, shutdown hook
β”‚   β”œβ”€β”€ migrations/
β”‚   β”‚   β”œβ”€β”€ app/                      # Global app.db schema (profile list, app_setting, shared metadata cache: metadata_artist, metadata_album, lyrics)
β”‚   β”‚   └── profile/                  # Per-profile SQLite schema (FTS5 contentless, triggers, indexes, scrobble_queue, track_analysis, audio quality columns)
β”‚   β”œβ”€β”€ Cargo.toml
β”‚   └── tauri.conf.json
└── package.json

Audio Architecture

β”Œβ”€ Tauri commands (tokio)     β”Œβ”€ Decoder thread (std)        β”Œβ”€ cpal callback (real-time)
β”‚  player_play, pause, seek   β”‚  symphonia FormatReader +     β”‚  pop f32 from SPSC ring
β”‚  β†’ crossbeam::Sender ──────►│  Decoder + rubato Resampler   β”‚  Γ— volume Γ— normalization
β”‚                              β”‚  push f32 β†’ rtrb::Producer ──►│  mono downmix (if enabled)
β”‚                              β”‚  emit position/state events   β”‚  β†’ device native format
└──────────────────────────────┴───────────────────────────────┴──────────────────────────

Rules: the cpal callback never allocates, never locks, never logs. It only touches rtrb::Consumer and Atomic* fields in SharedPlayback.

i18n

Currently shipping French, English, Spanish and German β€” auto-detected at first launch from the OS locale, switchable from Settings.

Strings are externalized in src/i18n/locales/. To add a language:

  1. Create src/i18n/locales/xx.json (same structure as fr.json β€” keep all keys translated, the loader doesn't fall back per-key)
  2. Import it in src/i18n/index.ts and add to SUPPORTED_LANGUAGES
  3. It will appear in the Settings language selector automatically

License

GPL-3.0 β€” see LICENSE

About

🌊 WaveFlow is a modern local music player with a clean, Spotify-like interface. Manage and listen to your own audio library directly from your browser. Built with React & Tailwind CSS.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages