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")]
|
#[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…
Reference in New Issue
Block a user