feat: add buffer size option

This commit is contained in:
talwat 2025-03-17 16:32:06 +01:00
parent 968c1ee670
commit 34577efe8f
6 changed files with 28 additions and 17 deletions

2
Cargo.lock generated
View File

@ -1453,7 +1453,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "lowfi" name = "lowfi"
version = "1.6.3-dev" version = "1.6.4-dev"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"arc-swap", "arc-swap",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lowfi" name = "lowfi"
version = "1.6.3-dev" version = "1.6.4-dev"
edition = "2021" edition = "2021"
description = "An extremely simple lofi player." description = "An extremely simple lofi player."
license = "MIT" license = "MIT"

View File

@ -12,7 +12,7 @@ mod tracks;
mod scrape; mod scrape;
/// An extremely simple lofi player. /// An extremely simple lofi player.
#[derive(Parser)] #[derive(Parser, Clone)]
#[command(about, version)] #[command(about, version)]
#[allow( #[allow(
clippy::struct_excessive_bools, clippy::struct_excessive_bools,
@ -45,7 +45,11 @@ struct Args {
/// Use a custom track list /// Use a custom track list
#[clap(long, short, alias = "list", short_alias = 'l')] #[clap(long, short, alias = "list", short_alias = 'l')]
tracklist: Option<String>, track_list: Option<String>,
/// Song buffer size.
#[clap(long, short = 's', alias = "buffer", default_value_t = 5)]
buffer_size: usize,
/// The command that was ran. /// The command that was ran.
/// This is [None] if no command was specified. /// This is [None] if no command was specified.
@ -54,7 +58,7 @@ struct Args {
} }
/// Defines all of the extra commands lowfi can run. /// Defines all of the extra commands lowfi can run.
#[derive(Subcommand)] #[derive(Subcommand, Clone)]
enum Commands { enum Commands {
/// Scrapes the lofi girl website file server for files. /// Scrapes the lofi girl website file server for files.
Scrape { Scrape {

View File

@ -102,13 +102,13 @@ pub async fn play(args: Args) -> eyre::Result<()> {
// Initialize the UI, as well as the internal communication channel. // Initialize the UI, as well as the internal communication channel.
let (tx, rx) = mpsc::channel(8); let (tx, rx) = mpsc::channel(8);
let ui = task::spawn(ui::start(Arc::clone(&player), tx.clone(), args)); let ui = task::spawn(ui::start(Arc::clone(&player), tx.clone(), args.clone()));
// Sends the player an "init" signal telling it to start playing a song straight away. // Sends the player an "init" signal telling it to start playing a song straight away.
tx.send(Messages::Init).await?; tx.send(Messages::Init).await?;
// Actually starts the player. // Actually starts the player.
Player::play(Arc::clone(&player), tx.clone(), rx).await?; Player::play(Arc::clone(&player), tx.clone(), rx, args.buffer_size).await?;
// Save the volume.txt file for the next session. // Save the volume.txt file for the next session.
PersistentVolume::save(player.sink.volume()).await?; PersistentVolume::save(player.sink.volume()).await?;

View File

@ -71,9 +71,6 @@ pub enum Messages {
/// The time to wait in between errors. /// The time to wait in between errors.
const TIMEOUT: Duration = Duration::from_secs(3); const TIMEOUT: Duration = Duration::from_secs(3);
/// The amount of songs to buffer up.
const BUFFER_SIZE: usize = 5;
/// Main struct responsible for queuing up & playing tracks. /// Main struct responsible for queuing up & playing tracks.
// TODO: Consider refactoring [Player] from being stored in an [Arc], into containing many smaller [Arc]s. // TODO: Consider refactoring [Player] from being stored in an [Arc], into containing many smaller [Arc]s.
// TODO: In other words, this would change the type from `Arc<Player>` to just `Player`. // TODO: In other words, this would change the type from `Arc<Player>` to just `Player`.
@ -178,7 +175,7 @@ impl Player {
let volume = PersistentVolume::load().await?; let volume = PersistentVolume::load().await?;
// Load the track list. // Load the track list.
let list = List::load(args.tracklist.as_ref()).await?; let list = List::load(args.track_list.as_ref()).await?;
// We should only shut up alsa forcefully on Linux if we really have to. // We should only shut up alsa forcefully on Linux if we really have to.
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -207,7 +204,7 @@ impl Player {
.build()?; .build()?;
let player = Self { let player = Self {
tracks: RwLock::new(VecDeque::with_capacity(5)), tracks: RwLock::new(VecDeque::with_capacity(args.buffer_size)),
current: ArcSwapOption::new(None), current: ArcSwapOption::new(None),
client, client,
sink, sink,
@ -294,10 +291,12 @@ impl Player {
/// skip tracks or pause. /// skip tracks or pause.
/// ///
/// This will also initialize a [Downloader] as well as an MPRIS server if enabled. /// This will also initialize a [Downloader] as well as an MPRIS server if enabled.
/// The [Downloader]s internal buffer size is determined by `buf_size`.
pub async fn play( pub async fn play(
player: Arc<Self>, player: Arc<Self>,
tx: Sender<Messages>, tx: Sender<Messages>,
mut rx: Receiver<Messages>, mut rx: Receiver<Messages>,
buf_size: usize,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
// Initialize the mpris player. // Initialize the mpris player.
// //
@ -313,7 +312,7 @@ impl Player {
})?; })?;
// `itx` is used to notify the `Downloader` when it needs to download new tracks. // `itx` is used to notify the `Downloader` when it needs to download new tracks.
let downloader = Downloader::new(Arc::clone(&player)); let downloader = Downloader::new(Arc::clone(&player), buf_size);
let (itx, downloader) = downloader.start(); let (itx, downloader) = downloader.start();
// Start buffering tracks immediately. // Start buffering tracks immediately.

View File

@ -8,7 +8,7 @@ use tokio::{
time::sleep, time::sleep,
}; };
use super::{Player, BUFFER_SIZE, TIMEOUT}; use super::{Player, TIMEOUT};
/// This struct is responsible for downloading tracks in the background. /// This struct is responsible for downloading tracks in the background.
/// ///
@ -24,6 +24,9 @@ pub struct Downloader {
/// A copy of the internal sender, which can be useful for keeping /// A copy of the internal sender, which can be useful for keeping
/// track of it. /// track of it.
tx: Sender<()>, tx: Sender<()>,
/// The size of the internal download buffer.
buf_size: usize,
} }
impl Downloader { impl Downloader {
@ -37,9 +40,14 @@ impl Downloader {
/// ///
/// This also sends a [`Sender`] which can be used to notify /// This also sends a [`Sender`] which can be used to notify
/// when the downloader needs to begin downloading more tracks. /// when the downloader needs to begin downloading more tracks.
pub fn new(player: Arc<Player>) -> Self { pub fn new(player: Arc<Player>, buf_size: usize) -> Self {
let (tx, rx) = mpsc::channel(8); let (tx, rx) = mpsc::channel(8);
Self { player, rx, tx } Self {
player,
rx,
tx,
buf_size,
}
} }
/// Actually starts & consumes the [Downloader]. /// Actually starts & consumes the [Downloader].
@ -50,7 +58,7 @@ impl Downloader {
// Loop through each update notification. // Loop through each update notification.
while self.rx.recv().await == Some(()) { while self.rx.recv().await == Some(()) {
// For each update notification, we'll push tracks until the buffer is completely full. // For each update notification, we'll push tracks until the buffer is completely full.
while self.player.tracks.read().await.len() < BUFFER_SIZE { while self.player.tracks.read().await.len() < self.buf_size {
let data = self.player.list.random(&self.player.client).await; let data = self.player.list.random(&self.player.client).await;
match data { match data {
Ok(track) => self.player.tracks.write().await.push_back(track), Ok(track) => self.player.tracks.write().await.push_back(track),