168 lines
5.0 KiB
Rust
168 lines
5.0 KiB
Rust
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
use anyhow::Result;
|
|
use flume::{Receiver, Sender};
|
|
use tracing::error;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use crate::player_engine::{MediaInfo, PlayerEngine, PlayerEngineCommand, PlayerMessage};
|
|
use crate::spectrum_tap::SpectrumTap;
|
|
|
|
pub enum PlayerError {}
|
|
|
|
pub struct Player {
|
|
pub messages: Receiver<PlayerMessage>,
|
|
tx_engine: Sender<PlayerEngineCommand>,
|
|
/// The spectrum tap, shared with the engine thread. The server's FFT
|
|
/// task reads it (architecture/spectrum.md).
|
|
spectrum: Arc<SpectrumTap>,
|
|
}
|
|
|
|
impl Default for Player {
|
|
fn default() -> Self {
|
|
let (tx_engine, rx_engine) = flume::bounded(16);
|
|
let (tx_player, messages): (Sender<PlayerMessage>, Receiver<PlayerMessage>) =
|
|
flume::bounded(16);
|
|
|
|
let tx_callbacks = tx_engine.clone();
|
|
// Capture the runtime handle here: the engine thread itself is not a
|
|
// tokio context but needs one to create http streams.
|
|
let runtime = tokio::runtime::Handle::try_current().ok();
|
|
|
|
// Created here and shared into the engine thread so callers can
|
|
// read it without reaching across the thread boundary.
|
|
let spectrum = SpectrumTap::new();
|
|
let engine_tap = spectrum.clone();
|
|
thread::spawn(move || {
|
|
let engine = match PlayerEngine::init(tx_callbacks, tx_player, runtime, engine_tap) {
|
|
Err(e) => {
|
|
error!("Could not initialize player: {}", e);
|
|
return;
|
|
}
|
|
Ok(engine) => engine,
|
|
};
|
|
engine.run(rx_engine);
|
|
});
|
|
|
|
Self {
|
|
messages,
|
|
tx_engine,
|
|
spectrum,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Player {
|
|
/// The spectrum tap the played audio is mirrored into.
|
|
pub fn spectrum_tap(&self) -> Arc<SpectrumTap> {
|
|
self.spectrum.clone()
|
|
}
|
|
|
|
pub async fn play(&self, source_str: &str) -> Result<MediaInfo> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::Play(source_str.to_string(), tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn restart(&self) -> Result<MediaInfo> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::Restart(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn elapsed(&self) -> Result<Duration> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::GetElapsed(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn duration(&self) -> Result<Duration> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::GetDuration(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn seek_to(&self, time: Duration) -> Result<Duration> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::SeekTo(time, tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn volume(&self) -> Result<f32> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::GetVolume(tx))
|
|
.await?;
|
|
Ok(rx.recv_async().await?)
|
|
}
|
|
|
|
pub async fn is_paused(&self) -> Result<bool> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::GetPaused(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn set_volume(&self, volume: f32) -> Result<f32> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::SetVolume(volume, tx))
|
|
.await?;
|
|
Ok(rx.recv_async().await?)
|
|
}
|
|
|
|
/// Toggles mute; resolves to the new muted state.
|
|
pub async fn toggle_mute(&self) -> Result<bool> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::ToggleMute(tx))
|
|
.await?;
|
|
Ok(rx.recv_async().await?)
|
|
}
|
|
|
|
pub async fn pause(&self) -> Result<()> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::Pause(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn unpause(&self) -> Result<()> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::Unpause(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn toggle_play(&self) -> Result<bool> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::TogglePlay(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
|
|
pub async fn stop(&self) -> Result<()> {
|
|
let (tx, rx) = flume::bounded(1);
|
|
self.tx_engine
|
|
.send_async(PlayerEngineCommand::Stop(tx))
|
|
.await?;
|
|
rx.recv_async().await?
|
|
}
|
|
}
|