335 lines
8.7 KiB
Rust
335 lines
8.7 KiB
Rust
use std::{
|
|
collections::HashMap,
|
|
path::PathBuf,
|
|
sync::{Arc, RwLock},
|
|
time::Duration,
|
|
};
|
|
|
|
use gstreamer::{prelude::*, ClockTime, MessageType, MessageView};
|
|
use thiserror::Error;
|
|
use tokio::sync::mpsc::{Receiver, Sender};
|
|
|
|
use crate::types::{AudioControlMessage, AudioStatusMessage, TrackSpec};
|
|
|
|
#[derive(Debug, Error, PartialEq)]
|
|
pub enum AudioError {
|
|
#[error("No tracks are available to play")]
|
|
NoTracks,
|
|
|
|
#[error("Cannot perform operation in the current state")]
|
|
InvalidState,
|
|
}
|
|
|
|
pub struct AudioControl {}
|
|
|
|
/*
|
|
impl Default for AudioControl {
|
|
fn default() -> Self {
|
|
Self::new(GStreamerBackend::default())
|
|
}
|
|
}
|
|
*/
|
|
|
|
impl AudioControl {
|
|
pub fn new() -> Self {
|
|
Self {}
|
|
}
|
|
|
|
pub async fn listen(&self, mut control_rx: Receiver<AudioControlMessage>) {
|
|
println!("waiting for control message");
|
|
while let Some(msg) = control_rx.recv().await {
|
|
match msg {
|
|
AudioControlMessage::Play => {
|
|
unimplemented!()
|
|
}
|
|
AudioControlMessage::Pause => {
|
|
unimplemented!()
|
|
}
|
|
AudioControlMessage::EnableTrack(_) => {
|
|
unimplemented!()
|
|
}
|
|
AudioControlMessage::DisableTrack(_) => {
|
|
unimplemented!()
|
|
}
|
|
AudioControlMessage::ReportStatus => {
|
|
unimplemented!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn report(&self, status_tx: Sender<AudioStatusMessage>) {
|
|
loop {
|
|
println!("sending status message");
|
|
status_tx
|
|
.send(AudioStatusMessage::Status(vec![]))
|
|
.await
|
|
.expect("to successfully send a message");
|
|
let _ = tokio::time::sleep(Duration::from_secs(1)).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
pub fn playing(&self) -> bool {
|
|
self.backend.read().unwrap().playing()
|
|
}
|
|
|
|
pub fn tracks(&self) -> Vec<TrackSpec> {
|
|
self.backend.read().unwrap().tracks()
|
|
}
|
|
|
|
pub fn play(&self) -> Result<(), AudioError> {
|
|
self.backend.read().unwrap().play()
|
|
}
|
|
|
|
pub fn stop(&self) -> Result<(), AudioError> {
|
|
self.backend.read().unwrap().stop()
|
|
}
|
|
|
|
pub fn add_track(&self, track: TrackSpec) -> Result<(), AudioError> {
|
|
self.backend.write().unwrap().add_track(track)
|
|
}
|
|
}
|
|
|
|
pub trait AudioControlBackend: Send + Sync {
|
|
fn playing(&self) -> bool;
|
|
|
|
fn tracks(&self) -> Vec<TrackSpec>;
|
|
|
|
fn play(&self) -> Result<(), AudioError>;
|
|
|
|
fn stop(&self) -> Result<(), AudioError>;
|
|
|
|
fn add_track(&mut self, track: TrackSpec) -> Result<(), AudioError>;
|
|
|
|
fn remove_track(&mut self, track: TrackSpec) -> Result<(), AudioError>;
|
|
}
|
|
|
|
pub struct MemoryBackend {
|
|
playing: Arc<RwLock<bool>>,
|
|
tracks: HashMap<PathBuf, TrackSpec>,
|
|
}
|
|
|
|
impl Default for MemoryBackend {
|
|
fn default() -> Self {
|
|
Self {
|
|
playing: Arc::new(RwLock::new(false)),
|
|
tracks: HashMap::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AudioControlBackend for MemoryBackend {
|
|
fn playing(&self) -> bool {
|
|
*self.playing.read().unwrap()
|
|
}
|
|
|
|
fn tracks(&self) -> Vec<TrackSpec> {
|
|
/*
|
|
self.tracks.iter().cloned().collect()
|
|
*/
|
|
vec![]
|
|
}
|
|
|
|
fn play(&self) -> Result<(), AudioError> {
|
|
if self.tracks.is_empty() {
|
|
return Err(AudioError::NoTracks);
|
|
}
|
|
|
|
let mut playing = self.playing.write().unwrap();
|
|
if *playing {
|
|
return Err(AudioError::InvalidState);
|
|
}
|
|
|
|
*playing = true;
|
|
Ok(())
|
|
}
|
|
|
|
fn stop(&self) -> Result<(), AudioError> {
|
|
let mut playing = self.playing.write().unwrap();
|
|
if *playing {
|
|
*playing = false;
|
|
Ok(())
|
|
} else {
|
|
Err(AudioError::InvalidState)
|
|
}
|
|
}
|
|
|
|
fn add_track(&mut self, track: TrackSpec) -> Result<(), AudioError> {
|
|
/*
|
|
self.tracks.insert(track);
|
|
*/
|
|
Ok(())
|
|
}
|
|
|
|
fn remove_track(&mut self, track: TrackSpec) -> Result<(), AudioError> {
|
|
/*
|
|
self.tracks.remove(&track);
|
|
*/
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub struct GStreamerBackend {
|
|
bus: gstreamer::Bus,
|
|
pipeline: gstreamer::Pipeline,
|
|
mixer: gstreamer::Element,
|
|
audio_sink: gstreamer::Element,
|
|
monitor: std::thread::JoinHandle<()>,
|
|
|
|
playing: Arc<RwLock<bool>>,
|
|
}
|
|
|
|
impl Default for GStreamerBackend {
|
|
fn default() -> Self {
|
|
let pipeline = gstreamer::Pipeline::new();
|
|
let bus = pipeline.bus().unwrap();
|
|
|
|
let mixer = gstreamer::ElementFactory::find("audiomixer")
|
|
.unwrap()
|
|
.load()
|
|
.unwrap()
|
|
.create()
|
|
.build()
|
|
.unwrap();
|
|
pipeline.add(&mixer).unwrap();
|
|
|
|
let audio_sink = gstreamer::ElementFactory::find("pulsesink")
|
|
.unwrap()
|
|
.load()
|
|
.unwrap()
|
|
.create()
|
|
.build()
|
|
.unwrap();
|
|
pipeline.add(&audio_sink).unwrap();
|
|
mixer.link(&audio_sink).unwrap();
|
|
|
|
let playing = Arc::new(RwLock::new(false));
|
|
|
|
let monitor = std::thread::spawn({
|
|
let pipeline_object = pipeline.clone().upcast::<gstreamer::Object>();
|
|
let playing = playing.clone();
|
|
let bus = bus.clone();
|
|
move || loop {
|
|
if let Some(msg) = bus.timed_pop_filtered(
|
|
ClockTime::NONE,
|
|
&[
|
|
MessageType::Error,
|
|
MessageType::Eos,
|
|
MessageType::StateChanged,
|
|
],
|
|
) {
|
|
match msg.view() {
|
|
MessageView::StateChanged(st) => {
|
|
if msg.src() == Some(&pipeline_object) {
|
|
*playing.write().unwrap() =
|
|
st.current() == gstreamer::State::Playing;
|
|
}
|
|
}
|
|
MessageView::Error(err) => {
|
|
println!("error: {:?}", err);
|
|
}
|
|
MessageView::Eos(_) => {
|
|
println!("EOS");
|
|
}
|
|
_ => {
|
|
unreachable!();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
Self {
|
|
bus,
|
|
pipeline,
|
|
mixer,
|
|
audio_sink,
|
|
|
|
monitor,
|
|
|
|
playing: Arc::new(RwLock::new(false)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AudioControlBackend for GStreamerBackend {
|
|
fn playing(&self) -> bool {
|
|
*self.playing.read().unwrap()
|
|
}
|
|
|
|
fn tracks(&self) -> Vec<TrackSpec> {
|
|
vec![]
|
|
}
|
|
|
|
fn play(&self) -> Result<(), AudioError> {
|
|
let mut playing = self.playing.write().unwrap();
|
|
if !*playing {
|
|
// self.pipeline.set_state(gstreamer::State::Playing).unwrap();
|
|
*playing = true;
|
|
Ok(())
|
|
} else {
|
|
Err(AudioError::InvalidState)
|
|
}
|
|
}
|
|
|
|
fn stop(&self) -> Result<(), AudioError> {
|
|
let mut playing = self.playing.write().unwrap();
|
|
if *playing {
|
|
// self.pipeline.set_state(gstreamer::State::Paused).unwrap();
|
|
*playing = false;
|
|
Ok(())
|
|
} else {
|
|
Err(AudioError::InvalidState)
|
|
}
|
|
}
|
|
|
|
fn add_track(&mut self, track: TrackSpec) -> Result<(), AudioError> {
|
|
let source = gstreamer::ElementFactory::find("filesrc")
|
|
.unwrap()
|
|
.load()
|
|
.unwrap()
|
|
.create()
|
|
.property("location", track.path.to_str().unwrap())
|
|
.build()
|
|
.unwrap();
|
|
self.pipeline.add(&source).unwrap();
|
|
|
|
let decoder = gstreamer::ElementFactory::find("decodebin")
|
|
.unwrap()
|
|
.load()
|
|
.unwrap()
|
|
.create()
|
|
.build()
|
|
.unwrap();
|
|
self.pipeline.add(&decoder).unwrap();
|
|
source.link(&decoder).unwrap();
|
|
|
|
let volume = gstreamer::ElementFactory::find("volume")
|
|
.unwrap()
|
|
.load()
|
|
.unwrap()
|
|
.create()
|
|
.property("mute", false)
|
|
.property("volume", 0.75)
|
|
.build()
|
|
.unwrap();
|
|
self.pipeline.add(&volume).unwrap();
|
|
volume.link(&self.mixer).unwrap();
|
|
|
|
decoder.connect_pad_added(move |_, pad| {
|
|
let next_pad = volume.static_pad("sink").unwrap();
|
|
pad.link(&next_pad).unwrap();
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn remove_track(&mut self, _path: TrackSpec) -> Result<(), AudioError> {
|
|
unimplemented!()
|
|
/* Need to run EOS through to a probe on the trailing end of the volume element */
|
|
}
|
|
}
|
|
*/
|