mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 05:43:53 +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")]
|
||||
use musicdb_lib::data::cache_manager::CacheManager;
|
||||
#[cfg(feature = "playback")]
|
||||
use musicdb_lib::player::{rodio::PlayerBackendRodio, Player};
|
||||
use musicdb_lib::player::{playback_rs::PlayerBackendPlaybackRs, Player};
|
||||
use musicdb_lib::{
|
||||
data::{
|
||||
database::{ClientIo, Database},
|
||||
@ -152,7 +152,7 @@ fn main() {
|
||||
cm.set_cache_songs_count(20);
|
||||
cache_manager = Some(cm);
|
||||
Some(Player::new_client(
|
||||
PlayerBackendRodio::new_without_command_sending().unwrap(),
|
||||
PlayerBackendPlaybackRs::new_without_command_sending().unwrap(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
|
@ -6,11 +6,14 @@ edition = "2021"
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
colorize = "0.1.0"
|
||||
playback-rs = { version = "0.4.3", optional = true }
|
||||
rand = "0.8.5"
|
||||
rc-u8-reader = "2.0.16"
|
||||
rodio = { version = "0.18.0", optional = true }
|
||||
sysinfo = "0.30.12"
|
||||
|
||||
[features]
|
||||
# default = ["playback"]
|
||||
playback = ["dep:rodio"]
|
||||
default = ["playback"]
|
||||
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;
|
||||
|
||||
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 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")]
|
||||
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.
|
||||
// these commands are sent to the database asap.
|
||||
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")]
|
||||
let mut player = if play_audio {
|
||||
Some(Player::new(
|
||||
PlayerBackendRodio::new(command_sender.clone()).unwrap(),
|
||||
))
|
||||
Some(Player::new(backend))
|
||||
} else {
|
||||
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…
Reference in New Issue
Block a user