aede_core/playback/
exact_session.rs1use std::error::Error;
4use std::fmt;
5
6use aede_dsp::ProcessStats;
7
8use super::exact_output::{
9 ExactOutputError, ExactOutputFormat, ExactPcmAdapter, MAX_EXACT_BLOCK_FRAMES,
10};
11use super::format::IntegerPcmFormat;
12use super::session::TrackSpan;
13
14#[derive(Debug)]
16pub enum ExactSessionError {
17 Output(ExactOutputError),
19 TrackActive,
21 NoTrack,
23 Finished,
25 FrameCountOverflow,
27}
28
29impl fmt::Display for ExactSessionError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Self::Output(error) => write!(f, "{error}"),
33 Self::TrackActive => f.write_str("seal the active exact playback track first"),
34 Self::NoTrack => f.write_str("no active exact playback track"),
35 Self::Finished => f.write_str("exact playback session has finished"),
36 Self::FrameCountOverflow => f.write_str("exact playback frame count overflowed"),
37 }
38 }
39}
40
41impl Error for ExactSessionError {
42 fn source(&self) -> Option<&(dyn Error + 'static)> {
43 match self {
44 Self::Output(error) => Some(error),
45 _ => None,
46 }
47 }
48}
49
50impl From<ExactOutputError> for ExactSessionError {
51 fn from(error: ExactOutputError) -> Self {
52 Self::Output(error)
53 }
54}
55
56pub struct ExactSessionBlock<'a> {
58 pub samples: &'a [i32],
60 pub encoded: &'a [u8],
62 pub spans: &'a [TrackSpan],
65}
66
67pub struct ExactPcmSession {
82 adapter: ExactPcmAdapter,
83 active: Option<usize>,
84 source_frames: u64,
85 finished: bool,
86 samples: Vec<i32>,
87 bytes: Vec<u8>,
88 spans: Vec<TrackSpan>,
89}
90
91impl ExactPcmSession {
92 pub fn new(
94 input: IntegerPcmFormat,
95 output: ExactOutputFormat,
96 ) -> Result<Self, ExactSessionError> {
97 let adapter = ExactPcmAdapter::new(input, output)?;
98 let capacity = MAX_EXACT_BLOCK_FRAMES * usize::from(input.channels());
99 let mut samples = Vec::new();
100 samples
101 .try_reserve(capacity)
102 .map_err(ExactOutputError::Allocation)?;
103 let mut bytes = Vec::new();
104 bytes
105 .try_reserve(capacity * output.representation().bytes_per_sample())
106 .map_err(ExactOutputError::Allocation)?;
107 let mut spans = Vec::new();
108 spans.try_reserve(1).map_err(ExactOutputError::Allocation)?;
109 Ok(Self {
110 adapter,
111 active: None,
112 source_frames: 0,
113 finished: false,
114 samples,
115 bytes,
116 spans,
117 })
118 }
119
120 pub fn input_format(&self) -> IntegerPcmFormat {
122 self.adapter.source_format()
123 }
124
125 pub fn output_format(&self) -> ExactOutputFormat {
127 self.adapter.output_format()
128 }
129
130 pub fn source_frames(&self) -> u64 {
132 self.source_frames
133 }
134
135 pub fn compatible(&self, input: IntegerPcmFormat, output: ExactOutputFormat) -> bool {
137 !self.finished && input == self.input_format() && output == self.output_format()
138 }
139
140 pub fn begin_track(&mut self, token: usize) -> Result<(), ExactSessionError> {
142 if self.finished {
143 return Err(ExactSessionError::Finished);
144 }
145 if self.active.is_some() {
146 return Err(ExactSessionError::TrackActive);
147 }
148 self.active = Some(token);
149 self.clear_block();
150 Ok(())
151 }
152
153 pub fn push_source(
159 &mut self,
160 samples: &[i32],
161 ) -> Result<ExactSessionBlock<'_>, ExactSessionError> {
162 if self.finished {
163 return Err(ExactSessionError::Finished);
164 }
165 let token = self.active.ok_or(ExactSessionError::NoTrack)?;
166 let channels = usize::from(self.input_format().channels());
167 let frames = u64::try_from(samples.len() / channels)
168 .map_err(|_| ExactSessionError::FrameCountOverflow)?;
169 let source_frames = self
170 .source_frames
171 .checked_add(frames)
172 .ok_or(ExactSessionError::FrameCountOverflow)?;
173 self.adapter.encode_block(samples, &mut self.bytes)?;
174 self.samples.clear();
175 self.samples.extend_from_slice(samples);
176 self.spans.clear();
177 if !samples.is_empty() {
178 let scale = if self.input_format().bits_per_sample() == 16 {
181 1.0 / 32768.0
182 } else {
183 1.0 / 8388608.0
184 };
185 let sample_peak = samples
186 .iter()
187 .map(|&sample| (sample as f32 * scale).abs())
188 .fold(0.0, f32::max);
189 self.spans.push(TrackSpan {
190 token,
191 samples: 0..samples.len(),
192 stats: ProcessStats {
193 sample_peak,
194 overfull_samples: 0,
195 },
196 complete: false,
197 });
198 }
199 self.source_frames = source_frames;
200 Ok(self.block())
201 }
202
203 pub fn end_track(&mut self) -> Result<ExactSessionBlock<'_>, ExactSessionError> {
205 if self.finished {
206 return Err(ExactSessionError::Finished);
207 }
208 let token = self.active.ok_or(ExactSessionError::NoTrack)?;
209 self.clear_block();
210 self.active = None;
211 self.spans.push(TrackSpan {
212 token,
213 samples: 0..0,
214 stats: ProcessStats::default(),
215 complete: true,
216 });
217 Ok(self.block())
218 }
219
220 pub fn finish(&mut self) -> Result<ExactSessionBlock<'_>, ExactSessionError> {
222 if self.active.is_some() {
223 return Err(ExactSessionError::TrackActive);
224 }
225 self.clear_block();
226 self.finished = true;
227 Ok(self.block())
228 }
229
230 fn clear_block(&mut self) {
231 self.samples.clear();
232 self.bytes.clear();
233 self.spans.clear();
234 }
235
236 fn block(&self) -> ExactSessionBlock<'_> {
237 ExactSessionBlock {
238 samples: &self.samples,
239 encoded: &self.bytes,
240 spans: &self.spans,
241 }
242 }
243}
244
245#[cfg(test)]
246#[path = "exact_session_tests.rs"]
247mod tests;