Skip to main content

timestretch/
render.rs

1//! Offline rendering is a driver of the same session used by playback.
2use crate::{
3    AppliedParameters, Backend, PreparedSession, ProcessStatus, SessionConfig, SessionCounters,
4    SessionError, SessionResult, SourceAccess, TimingInfo,
5};
6
7#[derive(Clone, Debug)]
8pub struct RenderReport {
9    pub source_frames: u64,
10    pub output_frames: u64,
11    pub source_sample_rate: u32,
12    pub output_sample_rate: u32,
13    pub channels: usize,
14    pub backend: Backend,
15    pub parameters: AppliedParameters,
16    pub timing: TimingInfo,
17    pub counters: SessionCounters,
18    pub memory: crate::OwnedMemory,
19}
20#[derive(Clone, Debug)]
21pub struct RenderedAudio {
22    pub samples: Vec<f32>,
23    pub report: RenderReport,
24}
25
26/// Render interleaved samples without normalization or a second DSP path.
27pub fn render(input: &[f32], mut config: SessionConfig) -> SessionResult<RenderedAudio> {
28    if !(1..=2).contains(&config.channels) || input.len() % config.channels != 0 {
29        return Err(SessionError::InvalidFormat);
30    }
31    if input.iter().any(|v| !v.is_finite()) {
32        return Err(SessionError::NonFiniteInput);
33    }
34    let frames = (input.len() / config.channels) as u64;
35    if config.source_frames.is_some_and(|length| length != frames) {
36        return Err(SessionError::InvalidFormat);
37    }
38    config.source_frames = Some(frames);
39    config.source_access = SourceAccess::Seekable;
40    let mut session = PreparedSession::new(config)?.activate();
41    let channels = session.config().channels;
42    let parameters = session.applied_parameters();
43    let expected =
44        (frames as f64 * parameters.achieved_stretch * session.config().output_sample_rate as f64
45            / session.config().source_sample_rate as f64)
46            .round() as usize;
47    let mut samples = Vec::with_capacity(expected.saturating_mul(channels));
48    let mut block = vec![0.0; 1024 * channels];
49    while !session.finished() {
50        let progress = session.advance(32);
51        match progress.status {
52            ProcessStatus::Fault => {
53                return Err(session.fault().unwrap_or(SessionError::InvalidState));
54            }
55            ProcessStatus::NeedSource => {
56                let request = session.source_request().ok_or(SessionError::InvalidState)?;
57                let start = request.start_frame as usize * channels;
58                let end = (start + request.frames * channels).min(input.len());
59                if start == end {
60                    session.finish_input()?;
61                } else {
62                    session.supply(request, &input[start..end])?;
63                }
64            }
65            _ => {}
66        }
67        let stamp = session.read_output(&mut block)?;
68        samples.extend_from_slice(&block[..stamp.frames * channels]);
69        session.acknowledge(stamp.consumed(stamp.frames)?)?;
70    }
71    Ok(RenderedAudio {
72        report: RenderReport {
73            source_frames: frames,
74            output_frames: (samples.len() / channels) as u64,
75            source_sample_rate: session.config().source_sample_rate,
76            output_sample_rate: session.config().output_sample_rate,
77            channels,
78            backend: session.config().backend,
79            parameters,
80            timing: session.timing(),
81            counters: session.counters(),
82            memory: session.owned_memory(),
83        },
84        samples,
85    })
86}