diff --git a/packaging/arch/PKGBUILD b/packaging/arch/PKGBUILD index 479f312b3..726b9c928 100644 --- a/packaging/arch/PKGBUILD +++ b/packaging/arch/PKGBUILD @@ -23,6 +23,11 @@ makedepends=( pkgconf clang ) +optdepends=( + 'alsa-utils: Required for bit-perfect device detection and better ALSA device names' + 'pipewire-alsa: PipeWire audio support' + 'pipewire-pulse: PulseAudio compatibility' +) source=("https://github.com/vicrodh/qbz/archive/refs/tags/v${pkgver}.tar.gz") sha256sums=('SKIP') diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD index baa08d1b0..b6c52cd54 100644 --- a/packaging/aur/PKGBUILD +++ b/packaging/aur/PKGBUILD @@ -8,7 +8,7 @@ url="https://github.com/vicrodh/qbz" license=('MIT') depends=('webkit2gtk-4.1' 'gtk3' 'alsa-lib' 'libappindicator-gtk3' 'libxkbcommon' 'openssl') optdepends=( - 'alsa-utils: Better ALSA device names and descriptions' + 'alsa-utils: Required for bit-perfect device detection and better ALSA device names' 'pipewire-alsa: PipeWire audio support' 'pipewire-pulse: PulseAudio compatibility' ) diff --git a/src-tauri/src/commands/audio_backends.rs b/src-tauri/src/commands/audio_backends.rs index b6bb502e5..e985ca684 100644 --- a/src-tauri/src/commands/audio_backends.rs +++ b/src-tauri/src/commands/audio_backends.rs @@ -100,3 +100,69 @@ pub fn get_alsa_plugins() -> Result, String> { Ok(plugins) } + +/// Check if alsa-utils is installed (aplay command available) +#[tauri::command] +pub fn check_alsa_utils_installed() -> bool { + use std::process::Command; + + Command::new("which") + .arg("aplay") + .output() + .map(|o| o.status.success()) + .unwrap_or(false) +} + +/// Linux distribution info for install commands +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LinuxDistroInfo { + pub distro_id: String, + pub distro_name: String, + pub install_command: String, +} + +/// Detect Linux distribution and return appropriate install command for alsa-utils +#[tauri::command] +pub fn get_linux_distro() -> LinuxDistroInfo { + use std::fs; + + // Try to read /etc/os-release + let os_release = fs::read_to_string("/etc/os-release").unwrap_or_default(); + + let mut distro_id = String::new(); + let mut distro_name = String::new(); + + for line in os_release.lines() { + if let Some(id) = line.strip_prefix("ID=") { + distro_id = id.trim_matches('"').to_lowercase(); + } + if let Some(name) = line.strip_prefix("NAME=") { + distro_name = name.trim_matches('"').to_string(); + } + } + + // Determine install command based on distro + let install_command = match distro_id.as_str() { + "arch" | "manjaro" | "endeavouros" | "garuda" | "artix" => + "sudo pacman -S alsa-utils".to_string(), + "debian" | "ubuntu" | "linuxmint" | "pop" | "elementary" | "zorin" | "kali" => + "sudo apt install alsa-utils".to_string(), + "fedora" | "rhel" | "centos" | "rocky" | "alma" => + "sudo dnf install alsa-utils".to_string(), + "opensuse" | "opensuse-leap" | "opensuse-tumbleweed" => + "sudo zypper install alsa-utils".to_string(), + "void" => + "sudo xbps-install alsa-utils".to_string(), + "gentoo" => + "sudo emerge media-sound/alsa-utils".to_string(), + "nixos" => + "nix-env -iA nixpkgs.alsa-utils".to_string(), + _ => "# Install alsa-utils using your package manager".to_string(), + }; + + LinuxDistroInfo { + distro_id, + distro_name, + install_command, + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 677b550b6..279f7ee8f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -762,6 +762,8 @@ pub fn run() { commands::get_available_backends, commands::get_devices_for_backend, commands::get_alsa_plugins, + commands::check_alsa_utils_installed, + commands::get_linux_distro, // Download settings commands config::download_settings::get_download_settings, config::download_settings::set_download_root, diff --git a/src/lib/components/AlsaUtilsHelpModal.svelte b/src/lib/components/AlsaUtilsHelpModal.svelte new file mode 100644 index 000000000..fa15221f9 --- /dev/null +++ b/src/lib/components/AlsaUtilsHelpModal.svelte @@ -0,0 +1,270 @@ + + + + {#snippet children()} +
+ {#if isLoading} +
Checking system...
+ {:else} +
+
+ {#if isInstalled} + + alsa-utils is installed + {:else} + + alsa-utils is not installed + {/if} +
+
+ +
+

+ The alsa-utils package provides hardware enumeration tools + that QBZ uses to detect bit-perfect capable devices (hw:X,Y). +

+

+ Without it, only generic ALSA devices are shown, and you won't see the + BP badge for true bit-perfect outputs. +

+
+ + {#if !isInstalled && distroInfo} +
+
+ + Install command{distroInfo.distro_name ? ` for ${distroInfo.distro_name}` : ''} +
+
+ {distroInfo.install_command} + +
+

+ After installing, restart QBZ to detect bit-perfect devices. +

+
+ {:else if isInstalled} +
+

Your system is properly configured for bit-perfect device detection.

+

If you don't see your device listed as bit-perfect, ensure it's connected and recognized by your system.

+
+ {/if} + {/if} +
+ {/snippet} +
+ + diff --git a/src/lib/components/views/SettingsView.svelte b/src/lib/components/views/SettingsView.svelte index c0cbc4325..42ffc89e7 100644 --- a/src/lib/components/views/SettingsView.svelte +++ b/src/lib/components/views/SettingsView.svelte @@ -4,10 +4,11 @@ import ViewTransition from '../ViewTransition.svelte'; import { getCurrentWebview } from '@tauri-apps/api/webview'; import { writeText as copyToClipboard } from '@tauri-apps/plugin-clipboard-manager'; - import { ArrowLeft, ChevronRight, ChevronDown, ChevronUp, Loader2, Sun, Moon, SunMoon } from 'lucide-svelte'; + import { ArrowLeft, ChevronRight, ChevronDown, ChevronUp, Loader2, Sun, Moon, SunMoon, HelpCircle } from 'lucide-svelte'; import Toggle from '../Toggle.svelte'; import Dropdown from '../Dropdown.svelte'; import DeviceDropdown from '../DeviceDropdown.svelte'; + import AlsaUtilsHelpModal from '../AlsaUtilsHelpModal.svelte'; import VolumeSlider from '../VolumeSlider.svelte'; import UpdateCheckResultModal from '../updates/UpdateCheckResultModal.svelte'; import WhatsNewModal from '../updates/WhatsNewModal.svelte'; @@ -141,6 +142,9 @@ let showMigrationModal = $state(false); let legacyTracksCount = $state(0); + // ALSA Utils help modal + let showAlsaUtilsHelpModal = $state(false); + // Offline mode state let offlineStatus = $state(getOfflineStatus()); let offlineSettings = $state(getOfflineSettings()); @@ -1931,14 +1935,23 @@ {#if isLoadingDevices} Loading devices... {:else if selectedBackend === 'ALSA Direct'} - + {:else if selectedBackend === 'PipeWire'} font-style: italic; } + .dropdown-with-help { + display: flex; + align-items: center; + gap: 8px; + } + + .help-icon-btn { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-muted); + cursor: pointer; + transition: all 150ms ease; + flex-shrink: 0; + } + + .help-icon-btn:hover { + background: var(--bg-hover); + color: var(--accent); + } + .loading-overlay { position: fixed; top: 0; @@ -3668,3 +3707,8 @@ flatpak override --user --filesystem=/home/USUARIO/Música com.blitzfc.qbz onClose={closeMigrationModal} totalTracks={legacyTracksCount} /> + + showAlsaUtilsHelpModal = false} +/>