lavende_core/sources/
playable_track.rs1use 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}