Adjust audio-player example
This commit is contained in:
parent
983609e2f4
commit
d236d108e8
|
|
@ -180,6 +180,7 @@ dependencies = [
|
||||||
"stream-download",
|
"stream-download",
|
||||||
"symphonia",
|
"symphonia",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rodio = { version = "0.17.1", default-features = false, features = ["symphonia-all"] }
|
rodio = { version = "0.17.1", default-features = false, features = [
|
||||||
|
"symphonia-all",
|
||||||
|
] }
|
||||||
symphonia = { version = "0.5.3", features = ["all"] }
|
symphonia = { version = "0.5.3", features = ["all"] }
|
||||||
stream-download = { git = "https://github.com/aschey/stream-download-rs.git" }
|
stream-download = { git = "https://github.com/aschey/stream-download-rs.git" }
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
|
|
@ -12,3 +14,6 @@ url = "2.4.0"
|
||||||
flume = "0.10.14"
|
flume = "0.10.14"
|
||||||
thiserror = "1.0.40"
|
thiserror = "1.0.40"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
use audio_player::{Player, PlayerMessage};
|
use audio_player::{Player, PlayerMessage};
|
||||||
|
|
||||||
fn main() {
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
let player = Player::default();
|
let player = Player::default();
|
||||||
|
|
||||||
player.play("https://www2.cs.uic.edu/~i101/SoundFiles/CantinaBand60.wav");
|
player
|
||||||
|
.play("https://www2.cs.uic.edu/~i101/SoundFiles/CantinaBand60.wav")
|
||||||
|
.await;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match player.messages.recv() {
|
match player.messages.recv_async().await {
|
||||||
Ok(PlayerMessage::Duration(duration)) => {
|
Ok(PlayerMessage::Duration { duration }) => {
|
||||||
println!("DURATION: {:?}", duration);
|
println!("DURATION: {:?}", duration);
|
||||||
}
|
}
|
||||||
Ok(PlayerMessage::Elapsed(el)) => {
|
Ok(PlayerMessage::Elapsed { duration, elapsed }) => {
|
||||||
println!("ELAPSED: {:?}", el);
|
println!("ELAPSED: {:?}", elapsed);
|
||||||
if el.as_secs() >= 10 {
|
if elapsed.as_secs() >= 10 {
|
||||||
player.stop();
|
player.stop().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(PlayerMessage::Stopped) => {
|
Ok(PlayerMessage::Stopped) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use flume::Sender;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use flume::Sender;
|
||||||
|
use rodio::Source;
|
||||||
use symphonia::{
|
use symphonia::{
|
||||||
core::{
|
core::{
|
||||||
audio::{AudioBufferRef, SampleBuffer, SignalSpec},
|
audio::{AudioBufferRef, SampleBuffer, SignalSpec},
|
||||||
|
|
@ -17,9 +19,7 @@ use symphonia::{
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use rodio::Source;
|
use crate::player_engine::PlayerEngineCommand;
|
||||||
|
|
||||||
use crate::PlayerEngineCommand;
|
|
||||||
|
|
||||||
// Decoder errors are not considered fatal.
|
// Decoder errors are not considered fatal.
|
||||||
// The correct action is to just get a new packet and try again.
|
// The correct action is to just get a new packet and try again.
|
||||||
|
|
|
||||||
|
|
@ -1,170 +1,10 @@
|
||||||
mod decoder;
|
mod decoder;
|
||||||
|
mod player;
|
||||||
mod player_engine;
|
mod player_engine;
|
||||||
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
pub use decoder::MediaInfo;
|
pub use decoder::MediaInfo;
|
||||||
use flume::{Receiver, Sender};
|
pub use player::{Player, PlayerError};
|
||||||
|
|
||||||
pub use player_engine::PlayerMessage;
|
pub use player_engine::PlayerMessage;
|
||||||
use player_engine::{PlayerEngine, PlayerEngineCommand};
|
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// * Emit buffering
|
|
||||||
|
|
||||||
pub enum PlayerError {}
|
|
||||||
|
|
||||||
pub struct Player {
|
|
||||||
pub messages: Receiver<PlayerMessage>,
|
|
||||||
tx_engine: Sender<PlayerEngineCommand>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Player {
|
|
||||||
fn default() -> Self {
|
|
||||||
let (tx_engine, rx_engine) = flume::bounded(10);
|
|
||||||
let (tx_player, messages): (Sender<PlayerMessage>, Receiver<PlayerMessage>) =
|
|
||||||
flume::bounded(10);
|
|
||||||
|
|
||||||
let tx_decoder = tx_engine.clone();
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
let mut player = PlayerEngine::new(tx_decoder, tx_player);
|
|
||||||
loop {
|
|
||||||
match rx_engine.recv() {
|
|
||||||
Ok(PlayerEngineCommand::Play(source_str, tx)) => {
|
|
||||||
tx.send(player.play(&source_str))
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::Pause(tx)) => {
|
|
||||||
tx.send(player.pause())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::Unpause(tx)) => {
|
|
||||||
tx.send(player.unpause())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::Stop(tx)) => {
|
|
||||||
tx.send(player.stop())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::TogglePlay(tx)) => {
|
|
||||||
tx.send(player.toggle_play())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::Restart(tx)) => {
|
|
||||||
tx.send(player.restart())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::GetDuration(tx)) => {
|
|
||||||
tx.send(player.duration())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::GetElapsed(tx)) => {
|
|
||||||
tx.send(player.elapsed())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::GetVolume(tx)) => {
|
|
||||||
tx.send(player.volume())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::GetPaused(tx)) => {
|
|
||||||
tx.send(player.is_paused())
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::SetVolume(volume, tx)) => {
|
|
||||||
tx.send(player.set_volume(volume))
|
|
||||||
.unwrap_or_else(|e| warn!("Send error {}", e));
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::SetElapsed(elapsed)) => {
|
|
||||||
player.handle_elapsed(elapsed);
|
|
||||||
}
|
|
||||||
Ok(PlayerEngineCommand::Eos) => {
|
|
||||||
player.handle_eos();
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Recv error {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
messages,
|
|
||||||
tx_engine,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Player {
|
|
||||||
pub async fn play(&self, source_str: &str) -> Result<MediaInfo> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine
|
|
||||||
.send(PlayerEngineCommand::Play(source_str.to_string(), tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn restart(&self) -> Result<MediaInfo> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::Restart(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn elpased(&self) -> Result<Duration> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::GetElapsed(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn duration(&self) -> Result<Duration> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::GetDuration(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn volume(&self) -> Result<f32> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::GetVolume(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn is_paused(&self) -> Result<bool> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::GetPaused(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set_volume(&self, volume: f32) -> Result<f32> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
let vol = volume.clamp(0.0, 1.1);
|
|
||||||
self.tx_engine
|
|
||||||
.send(PlayerEngineCommand::SetVolume(vol, tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn pause(&self) -> Result<()> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::Pause(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn unpause(&self) -> Result<()> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::Unpause(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn toggle_play(&self) -> Result<bool> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::TogglePlay(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn stop(&self) -> Result<()> {
|
|
||||||
let (tx, rx) = flume::bounded(1);
|
|
||||||
self.tx_engine.send(PlayerEngineCommand::Stop(tx))?;
|
|
||||||
rx.recv_async().await?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use flume::{Receiver, Sender};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
use crate::decoder::MediaInfo;
|
||||||
|
use crate::player_engine::{PlayerEngine, PlayerEngineCommand, PlayerMessage};
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// * Emit buffering
|
||||||
|
|
||||||
|
pub enum PlayerError {}
|
||||||
|
|
||||||
|
pub struct Player {
|
||||||
|
pub messages: Receiver<PlayerMessage>,
|
||||||
|
tx_engine: Sender<PlayerEngineCommand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Player {
|
||||||
|
fn default() -> Self {
|
||||||
|
let (tx_engine, rx_engine) = flume::bounded(10);
|
||||||
|
let (tx_player, messages): (Sender<PlayerMessage>, Receiver<PlayerMessage>) =
|
||||||
|
flume::bounded(10);
|
||||||
|
|
||||||
|
let tx_decoder = tx_engine.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut player = PlayerEngine::new(tx_decoder, tx_player);
|
||||||
|
loop {
|
||||||
|
match rx_engine.recv() {
|
||||||
|
Ok(PlayerEngineCommand::Play(source_str, tx)) => {
|
||||||
|
tx.send(player.play(&source_str))
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::Pause(tx)) => {
|
||||||
|
tx.send(player.pause())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::Unpause(tx)) => {
|
||||||
|
tx.send(player.unpause())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::Stop(tx)) => {
|
||||||
|
tx.send(player.stop())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::TogglePlay(tx)) => {
|
||||||
|
tx.send(player.toggle_play())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::Restart(tx)) => {
|
||||||
|
tx.send(player.restart())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::GetDuration(tx)) => {
|
||||||
|
tx.send(player.duration())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::GetElapsed(tx)) => {
|
||||||
|
tx.send(player.elapsed())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::GetVolume(tx)) => {
|
||||||
|
tx.send(player.volume())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::GetPaused(tx)) => {
|
||||||
|
tx.send(player.is_paused())
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::SetVolume(volume, tx)) => {
|
||||||
|
tx.send(player.set_volume(volume))
|
||||||
|
.unwrap_or_else(|e| warn!("Send error {}", e));
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::SetElapsed(elapsed)) => {
|
||||||
|
player.handle_elapsed(elapsed);
|
||||||
|
}
|
||||||
|
Ok(PlayerEngineCommand::Eos) => {
|
||||||
|
player.handle_eos();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Recv error {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
messages,
|
||||||
|
tx_engine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Player {
|
||||||
|
pub async fn play(&self, source_str: &str) -> Result<MediaInfo> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine
|
||||||
|
.send(PlayerEngineCommand::Play(source_str.to_string(), tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn restart(&self) -> Result<MediaInfo> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::Restart(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn elpased(&self) -> Result<Duration> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::GetElapsed(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn duration(&self) -> Result<Duration> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::GetDuration(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn volume(&self) -> Result<f32> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::GetVolume(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_paused(&self) -> Result<bool> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::GetPaused(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_volume(&self, volume: f32) -> Result<f32> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
let vol = volume.clamp(0.0, 1.1);
|
||||||
|
self.tx_engine
|
||||||
|
.send(PlayerEngineCommand::SetVolume(vol, tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pause(&self) -> Result<()> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::Pause(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unpause(&self) -> Result<()> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::Unpause(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn toggle_play(&self) -> Result<bool> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::TogglePlay(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn stop(&self) -> Result<()> {
|
||||||
|
let (tx, rx) = flume::bounded(1);
|
||||||
|
self.tx_engine.send(PlayerEngineCommand::Stop(tx))?;
|
||||||
|
rx.recv_async().await?
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue