add sample rate support

This commit is contained in:
2026-08-26 17:55:15 +02:00
parent 089f215c42
commit 80820cb050
2 changed files with 45 additions and 15 deletions
+42 -13
View File
@@ -5,14 +5,15 @@ use std::io::BufReader;
use crate::event::{AppEvent, Event, EventHandler}; use crate::event::{AppEvent, Event, EventHandler};
use crate::library::{Library, Song}; use crate::library::{Library, Song};
use audiotags::{MimeType, Tag}; use crossterm::event::MediaKeyCode::{self};
use crossterm::event::MediaKeyCode::{self, PlayPause};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use discord_rich_presence::activity::{Assets, Timestamps}; use discord_rich_presence::activity::{Assets, Timestamps};
use discord_rich_presence::{DiscordIpc, DiscordIpcClient, activity}; use discord_rich_presence::{DiscordIpc, DiscordIpcClient, activity};
use ratatui::DefaultTerminal; use ratatui::DefaultTerminal;
use ratatui::widgets::{List, ListItem, ListState}; use ratatui::widgets::ListState;
use rodio::Decoder; use rodio::{
Decoder, DeviceSinkBuilder, DeviceSinkError, MixerDeviceSink, Player, SampleRate, Source,
};
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
@@ -34,8 +35,10 @@ pub struct App {
pub queue: VecDeque<usize>, pub queue: VecDeque<usize>,
pub sink_handle: rodio::MixerDeviceSink, pub sink_handle: MixerDeviceSink,
pub player: rodio::Player, pub sample_rate: SampleRate,
pub sink_volume: f32,
pub player: Player,
pub artist_state: ListState, pub artist_state: ListState,
pub album_state: ListState, pub album_state: ListState,
@@ -57,9 +60,9 @@ impl App {
/// Constructs a new instance of [`App`]. /// Constructs a new instance of [`App`].
pub fn new() -> Self { pub fn new() -> Self {
let sink_handle = let sink_handle =
rodio::DeviceSinkBuilder::open_default_sink().expect("open default audio stream"); DeviceSinkBuilder::open_default_sink().expect("open default audio stream");
let player = rodio::Player::connect_new(sink_handle.mixer()); let player = Player::connect_new(sink_handle.mixer());
player.set_volume(1.0); // player.set_volume(1.0);
let discord_client = { let discord_client = {
let mut client = DiscordIpcClient::new("1525954755292299426"); let mut client = DiscordIpcClient::new("1525954755292299426");
@@ -76,7 +79,6 @@ impl App {
Self { Self {
running: true, running: true,
library: Library::new(), library: Library::new(),
//songs: get_songs(),
queue: vec![].into(), queue: vec![].into(),
artist_state: ListState::default().with_selected(Some(0)), artist_state: ListState::default().with_selected(Some(0)),
album_state: ListState::default().with_selected(Some(0)), album_state: ListState::default().with_selected(Some(0)),
@@ -89,6 +91,8 @@ impl App {
paused_at: 1, paused_at: 1,
sink_handle, sink_handle,
player, player,
sink_volume: 1.0,
sample_rate: SampleRate::new(44100).unwrap(),
discord_client, discord_client,
} }
} }
@@ -137,7 +141,7 @@ impl App {
if self.control_popup == false { if self.control_popup == false {
self.events.send(AppEvent::Quit) self.events.send(AppEvent::Quit)
} else { } else {
self.control_popup = !self.control_popup; self.control_popup = !self.control_popup;
} }
} }
KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => { KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => {
@@ -252,7 +256,8 @@ impl App {
if vol >= 1.0 { if vol >= 1.0 {
self.player.set_volume(1.0); self.player.set_volume(1.0);
} else { } else {
self.player.set_volume(vol + 0.05); self.sink_volume = vol + 0.05;
self.player.set_volume(self.sink_volume);
} }
} }
@@ -261,7 +266,8 @@ impl App {
if vol <= 0.01 { if vol <= 0.01 {
self.player.set_volume(0.0); self.player.set_volume(0.0);
} else { } else {
self.player.set_volume(vol - 0.05); self.sink_volume = vol - 0.05;
self.player.set_volume(self.sink_volume);
} }
} }
@@ -425,6 +431,13 @@ impl App {
File::open(&self.library.songs[*self.queue.front().expect("Queue is empty")].path) File::open(&self.library.songs[*self.queue.front().expect("Queue is empty")].path)
.unwrap(); .unwrap();
let source = Decoder::new(BufReader::new(file)).unwrap(); let source = Decoder::new(BufReader::new(file)).unwrap();
let rate: SampleRate = source.sample_rate();
if rate != self.sample_rate {
self.sample_rate = rate;
let _ = self.rebuild_sink(self.sample_rate);
}
self.player.append(source); self.player.append(source);
self.current_song = Some(self.queue.front().unwrap().to_owned()); self.current_song = Some(self.queue.front().unwrap().to_owned());
self.update_rpc(self.queue.front().unwrap().to_owned()); self.update_rpc(self.queue.front().unwrap().to_owned());
@@ -444,6 +457,22 @@ impl App {
} }
} }
pub fn rebuild_sink(&mut self, rate: SampleRate) -> Result<(), DeviceSinkError> {
self.sink_handle.log_on_drop(false);
let mut sink = DeviceSinkBuilder::from_default_device()
.unwrap()
.with_sample_rate(rate)
.open_stream()
.unwrap();
sink.log_on_drop(false);
self.sink_handle = sink;
self.player = Player::connect_new(self.sink_handle.mixer());
self.player.set_volume(self.sink_volume);
Ok(())
}
/// Set running to false to quit the application. /// Set running to false to quit the application.
pub fn quit(&mut self) { pub fn quit(&mut self) {
self.running = false; self.running = false;
+3 -2
View File
@@ -50,8 +50,9 @@ impl Widget for &mut App {
let now_playing_block = Block::bordered() let now_playing_block = Block::bordered()
.title(format!( .title(format!(
"Now Playing | Vol: {:.0}", "Now Playing | Vol: {:.0} | Rate: {}Hz",
self.player.volume() * 100.0 self.player.volume() * 100.0,
self.sample_rate
)) ))
.border_type(Rounded); .border_type(Rounded);
let inner = now_playing_block.inner(center_area[1]); let inner = now_playing_block.inner(center_area[1]);