Skip to main content

lavende_core/sources/
playable_track.rs

1use crate::{
2    audio::{
3        AudioFrame,
4        processor::{AudioProcessor, DecoderCommand},
5    },
6    common::AudioFormat,
7    config::player::PlayerConfig,
8    sources::SourcePlugin,
9};
10use async_trait::async_trait;
11use flume::{Receiver, Sender};
12use std::sync::Arc;
13pub type DecoderOutput = (
14    Receiver<AudioFrame>,
15    Sender<DecoderCommand>,
16    Receiver<String>,
17);
18pub type BoxedTrack = Arc<dyn PlayableTrack>;
19pub type BoxedSource = Box<dyn SourcePlugin>;
20pub struct ResolvedTrack {
21    pub reader: Box<dyn symphonia::core::io::MediaSource>,
22    pub hint: Option<AudioFormat>,
23}
24impl ResolvedTrack {
25    pub fn new(
26        reader: Box<dyn symphonia::core::io::MediaSource>,
27        hint: Option<AudioFormat>,
28    ) -> Self {
29        Self { reader, hint }
30    }
31    pub fn without_hint(reader: Box<dyn symphonia::core::io::MediaSource>) -> Self {
32        Self { reader, hint: None }
33    }
34}
35#[async_trait]
36pub trait PlayableTrack: Send + Sync + 'static {
37    async fn resolve(&self) -> Result<ResolvedTrack, String>;
38    fn supports_seek(&self) -> bool {
39        false
40    }
41    fn start_decoding(self: Arc<Self>, config: PlayerConfig) -> DecoderOutput {
42        let (tx, rx) =
43            flume::bounded::<AudioFrame>((config.buffer_duration_ms / 20).max(200) as usize);
44        let (cmd_tx, cmd_rx) = flume::unbounded::<DecoderCommand>();
45        let (err_tx, err_rx) = flume::bounded::<String>(1);
46        let supports_seek = self.supports_seek();
47        tokio::spawn(async move {
48            let ResolvedTrack { reader, hint } = match self.resolve().await {
49                Ok(r) => r,
50                Err(e) => {
51                    let _ = err_tx.send(e);
52                    return;
53                }
54            };
55            let err_tx_clone = err_tx.clone();
56            tokio::task::spawn_blocking(move || {
57                match AudioProcessor::new(
58                    reader,
59                    hint,
60                    tx,
61                    cmd_rx,
62                    Some(err_tx_clone.clone()),
63                    config,
64                ) {
65                    Ok(mut processor) => {
66                        let result = if supports_seek {
67                            processor.run_with_seek()
68                        } else {
69                            processor.run()
70                        };
71                        if let Err(e) = result {
72                            let _ = err_tx_clone.send(format!(
73                                "Playback failed: {e} (hint={hint:?}, seek={supports_seek})"
74                            ));
75                        }
76                    }
77                    Err(e) => {
78                        let _ = err_tx_clone.send(format!(
79                            "Failed to initialize audio processor: {e} (hint={hint:?})"
80                        ));
81                    }
82                }
83            });
84        });
85        (rx, cmd_tx, err_rx)
86    }
87}