mirror of
				https://github.com/Dummi26/musicdb.git
				synced 2025-10-30 03:25:26 +01:00 
			
		
		
		
	change player backend from rodio to playback-rs
because rodio struggled to load some (possibly broken, but still playable) audio files. this is not perfect and sometimes suffers from small lags, and it uses song_finished_polling instead of being event-driven.
This commit is contained in:
		
							parent
							
								
									3a0d28b9b4
								
							
						
					
					
						commit
						aa657382fa
					
				
							
								
								
									
										2
									
								
								musicdb-client/rust-toolchain.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								musicdb-client/rust-toolchain.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | [toolchain] | ||||||
|  | channel = "nightly" | ||||||
| @ -12,7 +12,7 @@ use gui::GuiEvent; | |||||||
| #[cfg(feature = "playback")] | #[cfg(feature = "playback")] | ||||||
| use musicdb_lib::data::cache_manager::CacheManager; | use musicdb_lib::data::cache_manager::CacheManager; | ||||||
| #[cfg(feature = "playback")] | #[cfg(feature = "playback")] | ||||||
| use musicdb_lib::player::{rodio::PlayerBackendRodio, Player}; | use musicdb_lib::player::{playback_rs::PlayerBackendPlaybackRs, Player}; | ||||||
| use musicdb_lib::{ | use musicdb_lib::{ | ||||||
|     data::{ |     data::{ | ||||||
|         database::{ClientIo, Database}, |         database::{ClientIo, Database}, | ||||||
| @ -152,7 +152,7 @@ fn main() { | |||||||
|                 cm.set_cache_songs_count(20); |                 cm.set_cache_songs_count(20); | ||||||
|                 cache_manager = Some(cm); |                 cache_manager = Some(cm); | ||||||
|                 Some(Player::new_client( |                 Some(Player::new_client( | ||||||
|                     PlayerBackendRodio::new_without_command_sending().unwrap(), |                     PlayerBackendPlaybackRs::new_without_command_sending().unwrap(), | ||||||
|                 )) |                 )) | ||||||
|             } else { |             } else { | ||||||
|                 None |                 None | ||||||
|  | |||||||
| @ -6,11 +6,14 @@ edition = "2021" | |||||||
| [dependencies] | [dependencies] | ||||||
| base64 = "0.22.1" | base64 = "0.22.1" | ||||||
| colorize = "0.1.0" | colorize = "0.1.0" | ||||||
|  | playback-rs = { version = "0.4.3", optional = true } | ||||||
| rand = "0.8.5" | rand = "0.8.5" | ||||||
| rc-u8-reader = "2.0.16" | rc-u8-reader = "2.0.16" | ||||||
| rodio = { version = "0.18.0", optional = true } | rodio = { version = "0.18.0", optional = true } | ||||||
| sysinfo = "0.30.12" | sysinfo = "0.30.12" | ||||||
| 
 | 
 | ||||||
| [features] | [features] | ||||||
| # default = ["playback"] | default = ["playback"] | ||||||
| playback = ["dep:rodio"] | playback = ["playback-via-playback-rs"] | ||||||
|  | playback-via-playback-rs = ["dep:playback-rs"] | ||||||
|  | playback-via-rodio = ["dep:rodio"] | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								musicdb-lib/rust-toolchain.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								musicdb-lib/rust-toolchain.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | [toolchain] | ||||||
|  | channel = "nightly" | ||||||
| @ -1,3 +1,6 @@ | |||||||
|  | #[cfg(feature = "playback-via-playback-rs")] | ||||||
|  | pub mod playback_rs; | ||||||
|  | #[cfg(feature = "playback-via-rodio")] | ||||||
| pub mod rodio; | pub mod rodio; | ||||||
| 
 | 
 | ||||||
| use std::{collections::HashMap, ffi::OsStr, sync::Arc}; | use std::{collections::HashMap, ffi::OsStr, sync::Arc}; | ||||||
|  | |||||||
							
								
								
									
										162
									
								
								musicdb-lib/src/player/playback_rs.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								musicdb-lib/src/player/playback_rs.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,162 @@ | |||||||
|  | use std::{ffi::OsStr, io::Cursor, path::Path, sync::Arc, time::Duration}; | ||||||
|  | 
 | ||||||
|  | use playback_rs::Hint; | ||||||
|  | 
 | ||||||
|  | use crate::{data::SongId, server::Command}; | ||||||
|  | 
 | ||||||
|  | use super::PlayerBackend; | ||||||
|  | 
 | ||||||
|  | pub struct PlayerBackendPlaybackRs<T> { | ||||||
|  |     player: playback_rs::Player, | ||||||
|  |     current: Option<(SongId, Option<playback_rs::Song>, T)>, | ||||||
|  |     next: Option<(SongId, Option<playback_rs::Song>, T)>, | ||||||
|  |     command_sender: Option<std::sync::mpsc::Sender<Command>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> PlayerBackendPlaybackRs<T> { | ||||||
|  |     pub fn new( | ||||||
|  |         command_sender: std::sync::mpsc::Sender<Command>, | ||||||
|  |     ) -> Result<Self, Box<dyn std::error::Error>> { | ||||||
|  |         Self::new_with_optional_command_sending(Some(command_sender)) | ||||||
|  |     } | ||||||
|  |     pub fn new_without_command_sending() -> Result<Self, Box<dyn std::error::Error>> { | ||||||
|  |         Self::new_with_optional_command_sending(None) | ||||||
|  |     } | ||||||
|  |     pub fn new_with_optional_command_sending( | ||||||
|  |         command_sender: Option<std::sync::mpsc::Sender<Command>>, | ||||||
|  |     ) -> Result<Self, Box<dyn std::error::Error>> { | ||||||
|  |         Ok(Self { | ||||||
|  |             player: playback_rs::Player::new(None)?, | ||||||
|  |             current: None, | ||||||
|  |             next: None, | ||||||
|  |             command_sender, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> PlayerBackend<T> for PlayerBackendPlaybackRs<T> { | ||||||
|  |     fn load_next_song( | ||||||
|  |         &mut self, | ||||||
|  |         id: SongId, | ||||||
|  |         filename: &OsStr, | ||||||
|  |         bytes: Arc<Vec<u8>>, | ||||||
|  |         _load_duration: bool, | ||||||
|  |         custom_data: T, | ||||||
|  |     ) { | ||||||
|  |         let mut hint = Hint::new(); | ||||||
|  |         if let Some(ext) = Path::new(filename).extension().and_then(OsStr::to_str) { | ||||||
|  |             hint.with_extension(ext); | ||||||
|  |         } | ||||||
|  |         let reader = Box::new(Cursor::new(ArcVec(bytes))); | ||||||
|  |         let loaded_song = match playback_rs::Song::new(reader, &hint, None) { | ||||||
|  |             Ok(v) => Some(v), | ||||||
|  |             Err(e) => { | ||||||
|  |                 if let Some(s) = &self.command_sender { | ||||||
|  |                     s.send(Command::ErrorInfo( | ||||||
|  |                         format!("Couldn't decode song #{id}!"), | ||||||
|  |                         format!("Error: {e}"), | ||||||
|  |                     )) | ||||||
|  |                     .unwrap(); | ||||||
|  |                 } | ||||||
|  |                 None | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         if let Some(song) = &loaded_song { | ||||||
|  |             if self.player.has_current_song() { | ||||||
|  |                 if let Err(e) = self.player.play_song_next(song, None) { | ||||||
|  |                     if let Some(s) = &self.command_sender { | ||||||
|  |                         s.send(Command::ErrorInfo( | ||||||
|  |                             format!("Couldn't preload song #{id}!"), | ||||||
|  |                             format!("Error: {e}"), | ||||||
|  |                         )) | ||||||
|  |                         .unwrap(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         self.next = Some((id, loaded_song, custom_data)); | ||||||
|  |     } | ||||||
|  |     fn pause(&mut self) { | ||||||
|  |         self.player.set_playing(false); | ||||||
|  |     } | ||||||
|  |     fn stop(&mut self) { | ||||||
|  |         self.pause(); | ||||||
|  |         self.player.seek(Duration::ZERO); | ||||||
|  |     } | ||||||
|  |     fn resume(&mut self) { | ||||||
|  |         self.player.set_playing(true); | ||||||
|  |     } | ||||||
|  |     fn next(&mut self, play: bool, _load_duration: bool) { | ||||||
|  |         self.pause(); | ||||||
|  |         self.player.skip(); | ||||||
|  |         self.current = self.next.take(); | ||||||
|  |         if self.player.has_current_song() { | ||||||
|  |             self.player.set_playing(play); | ||||||
|  |         } else { | ||||||
|  |             if let Some((id, song, _)) = &self.current { | ||||||
|  |                 if let Some(song) = song { | ||||||
|  |                     if let Err(e) = self.player.play_song_now(song, None) { | ||||||
|  |                         if let Some(s) = &self.command_sender { | ||||||
|  |                             s.send(Command::ErrorInfo( | ||||||
|  |                                 format!("Couldn't play song #{id}!"), | ||||||
|  |                                 format!("Error: {e}"), | ||||||
|  |                             )) | ||||||
|  |                             .unwrap(); | ||||||
|  |                             s.send(Command::NextSong).unwrap(); | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         self.player.set_playing(play); | ||||||
|  |                     } | ||||||
|  |                 } else if let Some(s) = &self.command_sender { | ||||||
|  |                     s.send(Command::NextSong).unwrap(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     fn clear(&mut self) { | ||||||
|  |         // remove next song
 | ||||||
|  |         let _ = self.player.force_remove_next_song(); | ||||||
|  |         // remove current song
 | ||||||
|  |         let _ = self.player.force_remove_next_song(); | ||||||
|  |         self.current = None; | ||||||
|  |         self.next = None; | ||||||
|  |     } | ||||||
|  |     fn playing(&self) -> bool { | ||||||
|  |         self.player.is_playing() | ||||||
|  |     } | ||||||
|  |     fn current_song(&self) -> Option<(SongId, bool, &T)> { | ||||||
|  |         self.current.as_ref().map(|v| (v.0, true, &v.2)) | ||||||
|  |     } | ||||||
|  |     fn next_song(&self) -> Option<(SongId, bool, &T)> { | ||||||
|  |         self.next.as_ref().map(|v| (v.0, true, &v.2)) | ||||||
|  |     } | ||||||
|  |     fn gen_data_mut(&mut self) -> (Option<&mut T>, Option<&mut T>) { | ||||||
|  |         ( | ||||||
|  |             self.current.as_mut().map(|v| &mut v.2), | ||||||
|  |             self.next.as_mut().map(|v| &mut v.2), | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     fn song_finished_polling(&self) -> bool { | ||||||
|  |         true | ||||||
|  |     } | ||||||
|  |     fn song_finished(&self) -> bool { | ||||||
|  |         self.current.is_some() && !self.player.has_current_song() | ||||||
|  |     } | ||||||
|  |     fn current_song_duration(&self) -> Option<u64> { | ||||||
|  |         self.player | ||||||
|  |             .get_playback_position() | ||||||
|  |             .map(|v| v.1.as_millis() as _) | ||||||
|  |     } | ||||||
|  |     fn current_song_playback_position(&self) -> Option<u64> { | ||||||
|  |         self.player | ||||||
|  |             .get_playback_position() | ||||||
|  |             .map(|v| v.0.as_millis() as _) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct ArcVec(pub Arc<Vec<u8>>); | ||||||
|  | impl AsRef<[u8]> for ArcVec { | ||||||
|  |     fn as_ref(&self) -> &[u8] { | ||||||
|  |         self.0.as_ref().as_ref() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -137,18 +137,25 @@ pub fn run_server_caching_thread_opt( | |||||||
|     use std::time::Instant; |     use std::time::Instant; | ||||||
| 
 | 
 | ||||||
|     use crate::data::cache_manager::CacheManager; |     use crate::data::cache_manager::CacheManager; | ||||||
|  |     #[cfg(feature = "playback-via-playback-rs")] | ||||||
|  |     use crate::player::playback_rs::PlayerBackendPlaybackRs; | ||||||
|  |     #[cfg(feature = "playback-via-rodio")] | ||||||
|  |     use crate::player::rodio::PlayerBackendRodio; | ||||||
|     #[cfg(feature = "playback")] |     #[cfg(feature = "playback")] | ||||||
|     use crate::player::{rodio::PlayerBackendRodio, PlayerBackend}; |     use crate::player::PlayerBackend; | ||||||
| 
 | 
 | ||||||
|     // commands sent to this will be handeled later in this function in an infinite loop.
 |     // commands sent to this will be handeled later in this function in an infinite loop.
 | ||||||
|     // these commands are sent to the database asap.
 |     // these commands are sent to the database asap.
 | ||||||
|     let (command_sender, command_receiver) = mpsc::channel(); |     let (command_sender, command_receiver) = mpsc::channel(); | ||||||
| 
 | 
 | ||||||
|  |     #[cfg(feature = "playback-via-playback-rs")] | ||||||
|  |     let backend = PlayerBackendPlaybackRs::new(command_sender.clone()).unwrap(); | ||||||
|  |     #[cfg(feature = "playback-via-rodio")] | ||||||
|  |     let backend = PlayerBackendRodio::new(command_sender.clone()).unwrap(); | ||||||
|  | 
 | ||||||
|     #[cfg(feature = "playback")] |     #[cfg(feature = "playback")] | ||||||
|     let mut player = if play_audio { |     let mut player = if play_audio { | ||||||
|         Some(Player::new( |         Some(Player::new(backend)) | ||||||
|             PlayerBackendRodio::new(command_sender.clone()).unwrap(), |  | ||||||
|         )) |  | ||||||
|     } else { |     } else { | ||||||
|         None |         None | ||||||
|     }; |     }; | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								musicdb-server/rust-toolchain.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								musicdb-server/rust-toolchain.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | [toolchain] | ||||||
|  | channel = "nightly" | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Mark
						Mark