Compare commits

...
13 Commits
Author SHA1 Message Date
Tarasov Aleksandrandgoogle-labs-jules[bot] 6eff8b2c6d perf(gui): eliminate allocations during cache file read using BufReader (#192)
* perf: eliminate allocations during cache file read using BufReader

Replaced `fs::read_to_string` combined with `serde_json::from_str` with `std::fs::File::open` and `serde_json::from_reader(std::io::BufReader::new(file))`. This avoids loading the entire JSON file into a string allocation before parsing it, improving memory usage.

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

* small reformatting

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-09-09 13:15:05 +03:00
Tarasov Aleksandrandgoogle-labs-jules[bot] dd13eee0df perf(gui): optimize GUI add_dirs allocations and cloning (#191)
*  Optimize `add_dirs` method by removing redundant allocations

Replaced the `iter().unique().cloned().collect()` pipeline with a simple
contains check before pushing to `app_state.dirs`. This prevents entire
vector allocations and item clones per user folder pick. Furthermore,
saving the configuration and cloning `dirs` is now only performed if the
app state actually changed (e.g. if the user picked a new folder).

Cleaned up the unused `itertools::Itertools` import.

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

*  Optimize GUI `add_dirs` allocations and cloning

Replaced the `iter().unique().cloned().collect()` pipeline with a simple
contains check before pushing to `app_state.dirs`. This prevents entire
vector allocations and item clones per user folder pick. Furthermore,
saving the configuration and cloning `dirs` is now only performed if the
app state actually changed (e.g. if the user picked a new folder).

Cleaned up the unused `itertools::Itertools` import. Cargo.lock was
not touched.

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-09-09 11:30:56 +03:00
Tarasov Aleksandrandgoogle-labs-jules[bot] 3d1bc24c21 perf(gui): optimize filesystem scanning by avoiding unnecessary PathBuf allocations (#193)
The directory scanning loop allocated a new `PathBuf` for every file encountered by unconditionally calling `entry.path()` and frequently querying disk metadata via `p.is_dir()`.

This patch refactors the code to first check `entry.file_type()` and use `entry.file_name()` for extension matching. This delays or completely avoids creating a `PathBuf` string allocation and stat syscall for all non-directory and non-supported media files, resulting in an ~80% performance boost when scanning directories dominated by non-audio assets.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-09-09 11:24:29 +03:00
arabianq 53fabcee75 change version to 1.16.1 2026-08-31 19:15:35 +03:00
arabianq 6ebe7ec211 cargo update 2026-08-31 19:15:08 +03:00
Tarasov Aleksandrandgoogle-labs-jules[bot] 17146d2ce6 perf: Optimize system font loading by replacing concat() with chain() (#189)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-08-31 04:36:36 +03:00
arabianq 7ad69243cc update rodio 2026-08-31 00:00:41 +03:00
arabianq 5b78cb17d7 cargo update 2026-08-30 23:58:48 +03:00
dependabot[bot] a1184588a0 chore(deps): bump pipewire from 0.10.0 to 0.10.1 (#188)
* chore(deps): bump pipewire from 0.10.0 to 0.10.1

Bumps pipewire from 0.10.0 to 0.10.1.

---
updated-dependencies:
- dependency-name: pipewire
  dependency-version: 0.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(flatpak): automatically update cargo-sources.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-29 13:39:17 +03:00
arabianq 9b65384cfd cargo update 2026-08-10 16:37:05 +03:00
dependabot[bot] 59c7f25a38 chore(deps): bump clap from 4.6.5 to 4.6.6 in the clap-stack group (#185)
* chore(deps): bump clap from 4.6.5 to 4.6.6 in the clap-stack group

Bumps the clap-stack group with 1 update: [clap](https://github.com/clap-rs/clap).


Updates `clap` from 4.6.5 to 4.6.6
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.5...clap_complete-v4.6.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.6.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: clap-stack
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(flatpak): automatically update cargo-sources.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 16:30:43 +03:00
dependabot[bot] 2b544d2e63 chore(deps): bump the egui-stack group with 5 updates (#184)
* chore(deps): bump the egui-stack group with 5 updates

Bumps the egui-stack group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [egui](https://github.com/emilk/egui) | `0.35.0` | `0.36.0` |
| [eframe](https://github.com/emilk/egui) | `0.35.0` | `0.36.0` |
| [egui_extras](https://github.com/emilk/egui) | `0.35.0` | `0.36.0` |
| [egui_material_icons](https://github.com/lucasmerlin/hello_egui) | `0.7.0` | `0.8.0` |
| [egui_dnd](https://github.com/lucasmerlin/hello_egui) | `0.16.0` | `0.17.0` |


Updates `egui` from 0.35.0 to 0.36.0
- [Release notes](https://github.com/emilk/egui/releases)
- [Changelog](https://github.com/emilk/egui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/emilk/egui/compare/0.35.0...0.36.0)

Updates `eframe` from 0.35.0 to 0.36.0
- [Release notes](https://github.com/emilk/egui/releases)
- [Changelog](https://github.com/emilk/egui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/emilk/egui/compare/0.35.0...0.36.0)

Updates `egui_extras` from 0.35.0 to 0.36.0
- [Release notes](https://github.com/emilk/egui/releases)
- [Changelog](https://github.com/emilk/egui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/emilk/egui/compare/0.35.0...0.36.0)

Updates `egui_material_icons` from 0.7.0 to 0.8.0
- [Release notes](https://github.com/lucasmerlin/hello_egui/releases)
- [Changelog](https://github.com/lucasmerlin/hello_egui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lucasmerlin/hello_egui/compare/egui_material_icons-v0.7.0...egui_material_icons-v0.8.0)

Updates `egui_dnd` from 0.16.0 to 0.17.0
- [Release notes](https://github.com/lucasmerlin/hello_egui/releases)
- [Changelog](https://github.com/lucasmerlin/hello_egui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lucasmerlin/hello_egui/compare/egui_dnd-v0.16.0...egui_dnd-v0.17.0)

---
updated-dependencies:
- dependency-name: egui
  dependency-version: 0.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: egui-stack
- dependency-name: eframe
  dependency-version: 0.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: egui-stack
- dependency-name: egui_extras
  dependency-version: 0.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: egui-stack
- dependency-name: egui_material_icons
  dependency-version: 0.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: egui-stack
- dependency-name: egui_dnd
  dependency-version: 0.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: egui-stack
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(flatpak): automatically update cargo-sources.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 16:30:28 +03:00
dependabot[bot] b939709047 chore(deps): bump clap from 4.6.4 to 4.6.5 in the clap-stack group (#183)
* chore(deps): bump clap from 4.6.4 to 4.6.5 in the clap-stack group

Bumps the clap-stack group with 1 update: [clap](https://github.com/clap-rs/clap).


Updates `clap` from 4.6.4 to 4.6.5
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.4...clap_complete-v4.6.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.6.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: clap-stack
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(flatpak): automatically update cargo-sources.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-05 14:58:22 +03:00
10 changed files with 953 additions and 933 deletions
Generated
+304 -300
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -8,7 +8,7 @@ members = [
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
version = "1.16.0" version = "1.16.1"
edition = "2024" edition = "2024"
authors = ["arabian"] authors = ["arabian"]
homepage = "https://pwsp.arabianq.ru" homepage = "https://pwsp.arabianq.ru"
@@ -52,26 +52,26 @@ rustix = { version = "1.1.4", features = ["process"] }
rust-i18n = "4.0.0" rust-i18n = "4.0.0"
sys-locale = "0.3.2" sys-locale = "0.3.2"
rodio = { git = "https://github.com/RustAudio/rodio.git", rev = "c5f1e94290922fe4ee963ce6f85754ea6ba36243", default-features = false, features = [ rodio = { git = "https://github.com/RustAudio/rodio.git", rev = "880aaba54b00aead53651ed1ccbdb672d1375a95", default-features = false, features = [
"symphonia-all", "symphonia-all",
"symphonia-libopus", "symphonia-libopus",
"playback", "playback",
] } ] }
pipewire = "0.10.0" pipewire = "0.10.0"
egui = { version = "0.35.0", default-features = false, features = [ egui = { version = "0.36.0", default-features = false, features = [
"default_fonts", "default_fonts",
"rayon", "rayon",
] } ] }
eframe = { version = "0.35.0", default-features = false, features = [ eframe = { version = "0.36.0", default-features = false, features = [
"default_fonts", "default_fonts",
"glow", "glow",
"x11", "x11",
"wayland", "wayland",
] } ] }
egui_extras = "0.35.0" egui_extras = "0.36.0"
egui_material_icons = "0.7.0" egui_material_icons = "0.8.0"
egui_dnd = "0.16.0" egui_dnd = "0.17.0"
reqwest = { version = "0.13.4", default-features = false, features = ["native-tls"] } reqwest = { version = "0.13.4", default-features = false, features = ["native-tls"] }
percent-encoding = "2.3.2" percent-encoding = "2.3.2"
+4 -4
View File
@@ -1,6 +1,6 @@
pkgbase = pwsp-bin pkgbase = pwsp-bin
pkgdesc = Lets you play audio files through your microphone (Pre-built binaries) pkgdesc = Lets you play audio files through your microphone (Pre-built binaries)
pkgver = 1.16.0 pkgver = 1.16.1
pkgrel = 1 pkgrel = 1
url = https://github.com/arabianq/pipewire-soundpad url = https://github.com/arabianq/pipewire-soundpad
arch = x86_64 arch = x86_64
@@ -10,11 +10,11 @@ pkgbase = pwsp-bin
depends = alsa-lib depends = alsa-lib
provides = pwsp provides = pwsp
conflicts = pwsp conflicts = pwsp
source = pipewire-soundpad-1.16.0.tar.gz :: https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.16.0.tar.gz source = pipewire-soundpad-1.16.1.tar.gz :: https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.16.1.tar.gz
sha256sums = SKIP sha256sums = SKIP
source_x86_64 = pwsp-1.16.0-x86_64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.16.0/pwsp-v1.16.0-linux-x64.zip source_x86_64 = pwsp-1.16.1-x86_64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.16.1/pwsp-v1.16.1-linux-x64.zip
sha256sums_x86_64 = SKIP sha256sums_x86_64 = SKIP
source_aarch64 = pwsp-1.16.0-aarch64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.16.0/pwsp-v1.16.0-linux-arm64.zip source_aarch64 = pwsp-1.16.1-aarch64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.16.1/pwsp-v1.16.1-linux-arm64.zip
sha256sums_aarch64 = SKIP sha256sums_aarch64 = SKIP
pkgname = pwsp-bin pkgname = pwsp-bin
+1 -1
View File
@@ -1,7 +1,7 @@
# Maintainer: Alexander Tarasov <a.tevg@ya.ru> # Maintainer: Alexander Tarasov <a.tevg@ya.ru>
pkgname=pwsp-bin pkgname=pwsp-bin
_pkgname=pipewire-soundpad _pkgname=pipewire-soundpad
pkgver=1.16.0 pkgver=1.16.1
pkgrel=1 pkgrel=1
pkgdesc="Lets you play audio files through your microphone (Pre-built binaries)" pkgdesc="Lets you play audio files through your microphone (Pre-built binaries)"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
+2 -2
View File
@@ -1,6 +1,6 @@
pkgbase = pwsp pkgbase = pwsp
pkgdesc = Lets you play audio files through your microphone pkgdesc = Lets you play audio files through your microphone
pkgver = 1.16.0 pkgver = 1.16.1
pkgrel = 1 pkgrel = 1
url = https://github.com/arabianq/pipewire-soundpad url = https://github.com/arabianq/pipewire-soundpad
arch = x86_64 arch = x86_64
@@ -12,7 +12,7 @@ pkgbase = pwsp
makedepends = cmake makedepends = cmake
makedepends = pipewire makedepends = pipewire
makedepends = alsa-lib makedepends = alsa-lib
source = https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.16.0.tar.gz source = https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.16.1.tar.gz
sha256sums = SKIP sha256sums = SKIP
pkgname = pwsp pkgname = pwsp
+1 -1
View File
@@ -1,7 +1,7 @@
# Maintainer: Alexander Tarasov <a.tevg@ya.ru> # Maintainer: Alexander Tarasov <a.tevg@ya.ru>
pkgsubn=pwsp pkgsubn=pwsp
pkgname=pwsp pkgname=pwsp
pkgver=1.16.0 pkgver=1.16.1
pkgrel=1 pkgrel=1
pkgdesc="Lets you play audio files through your microphone" pkgdesc="Lets you play audio files through your microphone"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
File diff suppressed because one or more lines are too long
@@ -31,7 +31,7 @@
<name>arabian</name> <name>arabian</name>
</developer> </developer>
<releases> <releases>
<release version="1.16.0" date="2026-07-30" /> <release version="1.16.1" date="2026-08-31" />
</releases> </releases>
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
</component> </component>
+1 -1
View File
@@ -13,7 +13,7 @@
Name: pwsp Name: pwsp
Version: 1.16.0 Version: 1.16.1
Release: %autorelease Release: %autorelease
Summary: Lets you play audio files through your microphone Summary: Lets you play audio files through your microphone
+40 -24
View File
@@ -5,7 +5,6 @@ mod views;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use eframe::{NativeOptions, icon_data::from_png_bytes, run_native}; use eframe::{NativeOptions, icon_data::from_png_bytes, run_native};
use egui::{Context, FontData, FontDefinitions, FontFamily, FontTweak, Vec2, ViewportBuilder}; use egui::{Context, FontData, FontDefinitions, FontFamily, FontTweak, Vec2, ViewportBuilder};
use itertools::Itertools;
use pwsp_lib::{ use pwsp_lib::{
types::{ types::{
audio_player::PlayerState, audio_player::PlayerState,
@@ -24,6 +23,7 @@ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fs, fs,
hash::{DefaultHasher, Hash, Hasher}, hash::{DefaultHasher, Hash, Hasher},
io::BufReader,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, Mutex}, sync::{Arc, Mutex},
thread, thread,
@@ -110,12 +110,17 @@ impl SoundpadGui {
pub fn add_dirs(&mut self) { pub fn add_dirs(&mut self) {
let file_dialog = FileDialog::new(); let file_dialog = FileDialog::new();
if let Some(paths) = file_dialog.pick_folders() { if let Some(paths) = file_dialog.pick_folders() {
let mut changed = false;
for path in paths { for path in paths {
self.app_state.dirs.push(path); if !self.app_state.dirs.contains(&path) {
self.app_state.dirs.push(path);
changed = true;
}
}
if changed {
self.config.dirs = self.app_state.dirs.clone();
self.config.save_to_file().ok();
} }
self.app_state.dirs = self.app_state.dirs.iter().unique().cloned().collect();
self.config.dirs = self.app_state.dirs.clone();
self.config.save_to_file().ok();
} }
} }
@@ -171,11 +176,9 @@ impl SoundpadGui {
// 1. Try to load from disk cache if we don't have it in memory yet // 1. Try to load from disk cache if we don't have it in memory yet
if !is_cached if !is_cached
&& let Ok(data) = fs::read_to_string(&cache_file) && let Ok(file) = fs::File::open(&cache_file)
&& let Ok((all_files, dir_updates)) = serde_json::from_str::<( && let Ok((all_files, dir_updates)) =
Vec<PathBuf>, serde_json::from_reader(BufReader::new(file))
HashMap<PathBuf, Vec<PathBuf>>,
)>(&data)
{ {
finished_scans.lock().unwrap().push(( finished_scans.lock().unwrap().push((
path_clone.clone(), path_clone.clone(),
@@ -196,18 +199,27 @@ impl SoundpadGui {
if let Ok(entries) = fs::read_dir(&dir) { if let Ok(entries) = fs::read_dir(&dir) {
let mut children = Vec::new(); let mut children = Vec::new();
for entry in entries.filter_map(|e| e.ok()) { for entry in entries.filter_map(|e| e.ok()) {
let p = entry.path(); if let Ok(file_type) = entry.file_type() {
if p.is_dir() { if file_type.is_dir() {
dirs_to_visit.push(p.clone()); let p = entry.path();
children.push(p); dirs_to_visit.push(p.clone());
} else if crate::gui::SUPPORTED_EXTENSIONS.contains( children.push(p);
&p.extension() } else {
.unwrap_or_default() let file_name = entry.file_name();
.to_str() let is_supported = Path::new(&file_name)
.unwrap_or_default(), .extension()
) { .map(|e| {
all_files.push(p.clone()); crate::gui::SUPPORTED_EXTENSIONS
children.push(p); .contains(&e.to_str().unwrap_or_default())
})
.unwrap_or(false);
if is_supported {
let p = entry.path();
all_files.push(p.clone());
children.push(p);
}
}
} }
} }
dir_updates.insert(dir, children); dir_updates.insert(dir, children);
@@ -389,9 +401,13 @@ fn load_system_fonts(fonts: &mut FontDefinitions) -> Result<()> {
let (_, ja_sans) = find_for_locale("ja", FontStyle::Sans); let (_, ja_sans) = find_for_locale("ja", FontStyle::Sans);
let (_, ar_sans) = find_for_locale("ar", FontStyle::Sans); let (_, ar_sans) = find_for_locale("ar", FontStyle::Sans);
let system_fonts = [en_sans, en_serif, ja_sans, ar_sans].concat(); let system_fonts = en_sans
.into_iter()
.chain(en_serif)
.chain(ja_sans)
.chain(ar_sans);
for font in system_fonts.iter().rev() { for font in system_fonts.rev() {
let font_bytes = match &font.source { let font_bytes = match &font.source {
FoundFontSource::Path(path) => fs::read(path)?, FoundFontSource::Path(path) => fs::read(path)?,
FoundFontSource::Bytes(bytes) => bytes.to_vec(), FoundFontSource::Bytes(bytes) => bytes.to_vec(),