ezk_audio_nodes/convert/
mod.rs

1use self::channels::ChannelMixer;
2use self::format::convert_sample_format;
3use self::rate::RateConverter;
4use ezk::{ConfigRange, NextEventIsCancelSafe, Result, Source, SourceEvent};
5use ezk_audio::{RawAudio, RawAudioConfig, RawAudioConfigRange};
6
7mod channels;
8mod format;
9mod rate;
10
11/// Converts any [`RawAudio`] to match downstream's requirements
12pub struct AudioConvert<S> {
13    source: S,
14
15    stream: Option<Stream>,
16}
17
18impl<S: Source<MediaType = RawAudio> + NextEventIsCancelSafe> NextEventIsCancelSafe
19    for AudioConvert<S>
20{
21}
22
23struct Stream {
24    config: RawAudioConfig,
25    channel_mixer: Option<ChannelMixer>,
26    rate_converter: Option<RateConverter>,
27}
28
29impl<S: Source<MediaType = RawAudio>> AudioConvert<S> {
30    pub fn new(source: S) -> Self {
31        Self {
32            source,
33            stream: None,
34        }
35    }
36}
37
38impl<S: Source<MediaType = RawAudio>> Source for AudioConvert<S> {
39    type MediaType = RawAudio;
40
41    async fn capabilities(&mut self) -> Result<Vec<RawAudioConfigRange>> {
42        let mut caps = self.source.capabilities().await?;
43        caps.push(RawAudioConfigRange::any());
44        Ok(caps)
45    }
46
47    async fn negotiate_config(
48        &mut self,
49        mut available: Vec<RawAudioConfigRange>,
50    ) -> Result<RawAudioConfig> {
51        // Keep a copy of the original offer, to find out later if the negotiated config is valid with downstream or not
52        let mut original = available.clone();
53
54        available.push(RawAudioConfigRange::any());
55
56        let negotiated_config = self.source.negotiate_config(available).await?;
57
58        // Find out if converting is required or the config can just passed through
59        if original
60            .iter()
61            .any(|original| original.contains(&negotiated_config))
62        {
63            // Easy path, no converting required
64            self.stream = Some(Stream {
65                config: negotiated_config.clone(),
66                channel_mixer: None,
67                rate_converter: None,
68            });
69
70            return Ok(negotiated_config);
71        }
72
73        // Hard path, set up converter
74        // TODO: find a config requiring the least amount of conversion
75        let best_config = original.remove(0);
76
77        let best_config = RawAudioConfig {
78            sample_rate: best_config.sample_rate.first_value(),
79            channels: best_config.channels.first_value(),
80            format: best_config.format.first_value(),
81        };
82
83        let channel_mixer = if negotiated_config.channels != best_config.channels {
84            Some(ChannelMixer::new(
85                negotiated_config.channels.clone(),
86                best_config.channels.clone(),
87            ))
88        } else {
89            None
90        };
91
92        let rate_converter = if negotiated_config.sample_rate != best_config.sample_rate {
93            Some(RateConverter::new(
94                best_config.format,
95                negotiated_config.sample_rate,
96                best_config.sample_rate,
97                best_config.channels.clone(),
98            ))
99        } else {
100            None
101        };
102
103        self.stream = Some(Stream {
104            config: best_config.clone(),
105            channel_mixer,
106            rate_converter,
107        });
108
109        Ok(best_config)
110    }
111
112    async fn next_event(&mut self) -> Result<SourceEvent<Self::MediaType>> {
113        let Some(stream) = &mut self.stream else {
114            return Ok(SourceEvent::RenegotiationNeeded);
115        };
116
117        loop {
118            match self.source.next_event().await? {
119                SourceEvent::Frame(mut frame) => {
120                    if let Some(channel_mixer) = &mut stream.channel_mixer {
121                        frame = channel_mixer.convert(frame);
122                    }
123
124                    if let Some(rate_converter) = stream.rate_converter.as_mut() {
125                        // RateConverter also converts the sample type
126                        if let Some(f) = rate_converter.convert(frame) {
127                            frame = f;
128                        } else {
129                            // Frame was consumed into the rate-converter's internal queue
130                            continue;
131                        }
132                    } else {
133                        frame = convert_sample_format(frame, stream.config.format);
134                    }
135
136                    return Ok(SourceEvent::Frame(frame));
137                }
138                SourceEvent::EndOfData => return Ok(SourceEvent::EndOfData),
139                SourceEvent::RenegotiationNeeded => return Ok(SourceEvent::RenegotiationNeeded),
140            }
141        }
142    }
143}