Skip to main content

kithara_audio/worker/
traits.rs

1use kithara_decode::PcmChunk;
2use kithara_stream::Timeline;
3
4use crate::{pipeline::track_fsm, traits::AudioEffect};
5
6mod kithara {
7    pub(crate) use kithara_test_macros::mock;
8}
9
10/// Trait for audio sources processed in a blocking worker thread.
11///
12/// The worker calls `step_track()` once per scheduling round. Each call
13/// performs at most one FSM transition and returns a [`TrackStep`] that
14/// tells the worker what happened.
15#[kithara::mock(api = MockAudioWorkerSource, type Chunk = PcmChunk;)]
16pub trait AudioWorkerSource: Send + 'static {
17    type Chunk: Send + 'static;
18
19    /// Advance the track FSM by one step.
20    ///
21    /// Handles seek preemption, source readiness, decoding, and all
22    /// internal state transitions. Returns:
23    /// - `Produced` — decoded chunk ready for the consumer.
24    /// - `StateChanged` — internal transition; caller should call again.
25    /// - `Blocked` — the source is not ready; the caller should wait for a wake.
26    /// - `Eof` — end of stream (may transition out via seek-after-EOF).
27    /// - `Failed` — terminal failure.
28    fn step_track(&mut self) -> track_fsm::TrackStep<Self::Chunk>;
29
30    /// Access the shared timeline for epoch queries.
31    fn timeline(&self) -> &Timeline;
32}
33
34/// Apply the effect chain to the chunk.
35pub(crate) fn apply_effects(
36    effects: &mut [Box<dyn AudioEffect>],
37    mut chunk: PcmChunk,
38) -> Option<PcmChunk> {
39    for effect in &mut *effects {
40        chunk = effect.process(chunk)?;
41    }
42    Some(chunk)
43}
44
45/// Flush effects chain at end of stream.
46pub(crate) fn flush_effects(effects: &mut [Box<dyn AudioEffect>]) -> Option<PcmChunk> {
47    let mut chunk: Option<PcmChunk> = None;
48    for effect in &mut *effects {
49        chunk = match chunk.take() {
50            Some(input) => effect.process(input),
51            None => effect.flush(),
52        };
53    }
54    chunk
55}
56
57/// Reset effects chain (e.g. after seek).
58pub(crate) fn reset_effects(effects: &mut [Box<dyn AudioEffect>]) {
59    for effect in &mut *effects {
60        effect.reset();
61    }
62}