1use std::ffi::{CString, c_int, c_uint};
13use std::ptr;
14
15use crate::decode::{OUT_CHANNELS, OUT_RATE, check};
16use crate::ffi::{self, AVERROR_EAGAIN, AVERROR_EOF, AVFilterContext, AVFilterGraph, AVFrame, Api};
17use crate::sync::{self, TempoTimeline};
18use crate::{Error, Result};
19
20pub struct Tempo {
22 api: &'static Api,
23 graph: *mut AVFilterGraph,
24 src: *mut AVFilterContext,
25 sink: *mut AVFilterContext,
26 frame: *mut AVFrame,
27 rate: f64,
28 timeline: TempoTimeline,
29 frames_in: i64,
31}
32
33impl Tempo {
34 pub fn new(rate: f64) -> Result<Tempo> {
36 let api = ffi::api().map_err(|e| Error(e.to_string()))?;
37 Tempo::with_api(api, rate)
38 }
39
40 fn with_api(api: &'static Api, rate: f64) -> Result<Tempo> {
41 let frame = unsafe { (api.av_frame_alloc)() };
43 let graph = unsafe { (api.avfilter_graph_alloc)() };
45 let mut tempo = Tempo {
47 api,
48 graph,
49 src: ptr::null_mut(),
50 sink: ptr::null_mut(),
51 frame,
52 rate,
53 timeline: TempoTimeline::new(rate, OUT_RATE),
54 frames_in: 0,
55 };
56 if frame.is_null() || graph.is_null() {
57 return Err(Error("could not allocate the tempo filter".into()));
58 }
59 let source = format!("sample_rate={OUT_RATE}:sample_fmt=flt:channel_layout=stereo:time_base=1/{OUT_RATE}");
60 tempo.src = tempo.filter("abuffer", "in", Some(&source))?;
61 tempo.sink = tempo.filter("abuffersink", "out", None)?;
62 let mut last = tempo.src;
63 for (i, speed) in sync::tempo_chain(rate).into_iter().enumerate() {
64 let next = tempo.filter("atempo", &format!("tempo{i}"), Some(&format!("tempo={speed}")))?;
65 tempo.link(last, next)?;
66 last = next;
67 }
68 tempo.link(last, tempo.sink)?;
69 check(api, unsafe { (api.avfilter_graph_config)(tempo.graph, ptr::null_mut()) }, "set up the tempo filter")?;
71 Ok(tempo)
72 }
73
74 fn filter(&self, kind: &str, name: &str, args: Option<&str>) -> Result<*mut AVFilterContext> {
76 let (kind, name) = (cstring(kind)?, cstring(name)?);
77 let args = args.map(cstring).transpose()?;
78 let filter = unsafe { (self.api.avfilter_get_by_name)(kind.as_ptr()) };
80 if filter.is_null() {
81 return Err(Error(format!("this FFmpeg has no {} filter", kind.to_string_lossy())));
82 }
83 let mut ctx = ptr::null_mut();
84 let r = unsafe { (self.api.avfilter_graph_create_filter)(&mut ctx, filter, name.as_ptr(), args.as_ref().map_or(ptr::null(), |a| a.as_ptr()), ptr::null_mut(), self.graph) };
87 check(self.api, r, "add a tempo filter")?;
88 Ok(ctx)
89 }
90
91 fn link(&self, from: *mut AVFilterContext, to: *mut AVFilterContext) -> Result<()> {
92 const FIRST_PAD: c_uint = 0;
93 check(self.api, unsafe { (self.api.avfilter_link)(from, FIRST_PAD, to, FIRST_PAD) }, "link the tempo filter")?;
95 Ok(())
96 }
97
98 pub fn rate(&self) -> f64 {
100 self.rate
101 }
102
103 pub fn push(&mut self, samples: &[f32], pts: Option<f64>) -> Result<()> {
106 let frames = samples.len() / OUT_CHANNELS;
107 if frames == 0 {
108 return Ok(());
109 }
110 self.timeline.input(pts);
111 let api = self.api;
112 let r = unsafe {
117 let f = &mut *self.frame;
118 f.format = ffi::AV_SAMPLE_FMT_FLT;
119 f.sample_rate = OUT_RATE as c_int;
120 f.nb_samples = frames as c_int;
121 f.pts = self.frames_in;
122 (api.av_channel_layout_default)(&mut f.ch_layout, OUT_CHANNELS as c_int);
123 let r = (api.av_frame_get_buffer)(self.frame, 0);
124 if r < 0 {
125 (api.av_frame_unref)(self.frame);
126 r
127 } else {
128 ptr::copy_nonoverlapping(samples.as_ptr(), f.data[0].cast::<f32>(), frames * OUT_CHANNELS);
129 let r = (api.av_buffersrc_add_frame)(self.src, self.frame);
130 (api.av_frame_unref)(self.frame);
131 r
132 }
133 };
134 check(api, r, "send sound to the tempo filter")?;
135 self.frames_in += frames as i64;
136 Ok(())
137 }
138
139 pub fn finish(&mut self) -> Result<()> {
141 check(self.api, unsafe { (self.api.av_buffersrc_add_frame)(self.src, ptr::null_mut()) }, "finish the tempo filter")?;
143 Ok(())
144 }
145
146 pub fn pull(&mut self, out: &mut Vec<f32>) -> Option<f64> {
149 let mut first = None;
150 loop {
151 let r = unsafe { (self.api.av_buffersink_get_frame)(self.sink, self.frame) };
153 if r < 0 {
154 debug_assert!(r == AVERROR_EAGAIN || r == AVERROR_EOF, "{}", ffi::error_text(self.api, r));
155 return first;
156 }
157 let f = unsafe { &*self.frame };
159 let frames = f.nb_samples.max(0) as usize;
160 if f.format == ffi::AV_SAMPLE_FMT_FLT && frames > 0 {
161 let time = self.timeline.next();
162 let got = unsafe { std::slice::from_raw_parts(f.data[0].cast::<f32>(), frames * OUT_CHANNELS) };
165 out.extend_from_slice(got);
166 self.timeline.output(frames);
167 first = first.or(time);
168 }
169 unsafe { (self.api.av_frame_unref)(self.frame) };
171 }
172 }
173}
174
175impl Drop for Tempo {
176 fn drop(&mut self) {
177 unsafe {
179 (self.api.av_frame_free)(&mut self.frame);
180 (self.api.avfilter_graph_free)(&mut self.graph);
181 }
182 }
183}
184
185fn cstring(s: &str) -> Result<CString> {
186 CString::new(s).map_err(|e| Error(e.to_string()))
187}
188
189pub struct Stretch {
194 api: &'static Api,
195 tempo: Option<Tempo>,
196 refused: Option<f64>,
197}
198
199impl Stretch {
200 pub fn new(api: &'static Api) -> Stretch {
201 Stretch { api, tempo: None, refused: None }
202 }
203
204 pub fn at(&mut self, rate: f64) -> Option<&mut Tempo> {
206 if !sync::needs_tempo(rate) {
207 return None;
208 }
209 if self.tempo.as_ref().is_some_and(|t| t.rate() != rate) {
210 self.tempo = None;
211 }
212 if self.tempo.is_none() && self.refused != Some(rate) {
213 match Tempo::with_api(self.api, rate) {
214 Ok(t) => self.tempo = Some(t),
215 Err(_) => self.refused = Some(rate),
216 }
217 }
218 self.tempo.as_mut()
219 }
220
221 pub fn reset(&mut self) {
223 self.tempo = None;
224 }
225}