signalsmith_stretch/
lib.rs

1//! A rust wrapper for the
2//! [Signalsmith Stretch](https://github.com/Signalsmith-Audio/signalsmith-stretch)
3//! audio stretching and pitch-shifting library.
4
5#[allow(non_camel_case_types)]
6mod sys {
7    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
8}
9
10/// Provides time stretching and/or pitch shifting for audio.
11pub struct Stretch {
12    inner: *mut sys::signalsmith_stretch_t,
13    channel_count: usize,
14}
15
16unsafe impl Send for Stretch {}
17unsafe impl Sync for Stretch {}
18
19impl Stretch {
20    /// Creates a new instance with a FFT window size and interval determined by
21    /// `block_length`.
22    pub fn new(channel_count: u32, block_length: usize, interval: usize) -> Self {
23        let ptr =
24            unsafe { sys::signalsmith_stretch_create(channel_count as _, block_length, interval) };
25
26        Self {
27            inner: ptr,
28            channel_count: channel_count as usize,
29        }
30    }
31
32    /// Creates a new instance with default values determined by the sample rate.
33    pub fn preset_default(channel_count: u32, sample_rate: u32) -> Self {
34        let ptr = unsafe {
35            sys::signalsmith_stretch_create_preset_default(channel_count as i32, sample_rate as f32)
36        };
37
38        Self {
39            inner: ptr,
40            channel_count: channel_count as usize,
41        }
42    }
43
44    /// Creates a new instance with default values determined by the sample rate,
45    /// tweaked to be less computationally expensive.
46    pub fn preset_cheaper(channel_count: u32, sample_rate: u32) -> Self {
47        let ptr = unsafe {
48            sys::signalsmith_stretch_create_preset_cheaper(channel_count as i32, sample_rate as f32)
49        };
50
51        Self {
52            inner: ptr,
53            channel_count: channel_count as usize,
54        }
55    }
56
57    /// Reset the instance to its initial (configured) state.
58    pub fn reset(&mut self) {
59        unsafe { sys::signalsmith_stretch_reset(self.inner) }
60    }
61
62    /// Get the current input latency, in frames. This is the delay between
63    /// samples passed to process and the center of the any pitch-shift or
64    /// stretch effect.
65    pub fn input_latency(&self) -> usize {
66        unsafe { sys::signalsmith_stretch_input_latency(self.inner) }
67    }
68
69    /// Get the current output latency, in frames. This is the delay between
70    /// the center of any pitch/shift or stretch effect and the output generated
71    /// by [Self::process].
72    pub fn output_latency(&self) -> usize {
73        unsafe { sys::signalsmith_stretch_output_latency(self.inner) }
74    }
75
76    /// Set the frequency multiplier and an optional tonality limit.
77    pub fn set_transpose_factor(&mut self, multiplier: f32, tonality_limit: Option<f32>) {
78        unsafe {
79            sys::signalsmith_stretch_set_transpose_factor(
80                self.inner,
81                multiplier,
82                tonality_limit.unwrap_or(0.0),
83            )
84        }
85    }
86
87    /// Set the frequency multiplier (in semitones) and an optional tonality
88    /// limit.
89    pub fn set_transpose_factor_semitones(&mut self, semitones: f32, tonality_limit: Option<f32>) {
90        unsafe {
91            sys::signalsmith_stretch_set_transpose_factor_semitones(
92                self.inner,
93                semitones,
94                tonality_limit.unwrap_or(0.0),
95            )
96        }
97    }
98
99    /// Set formant shift factor, with an option to compensate for pitch.
100    pub fn set_formant_factor(&mut self, multiplier: f32, compensate_pitch: bool) {
101        unsafe {
102            sys::signalsmith_stretch_set_formant_factor(
103                self.inner,
104                multiplier,
105                if compensate_pitch { 1 } else { 0 },
106            )
107        }
108    }
109
110    /// Set formant shift in semitones, with an option to compensate for pitch.
111    pub fn set_formant_factor_semitones(&mut self, semitones: f32, compensate_pitch: bool) {
112        unsafe {
113            sys::signalsmith_stretch_set_formant_factor_semitones(
114                self.inner,
115                semitones,
116                if compensate_pitch { 1 } else { 0 },
117            )
118        }
119    }
120
121    /// Rough guesstimate of the fundamental frequency, used for formant analysis.
122    /// 0 means attempting to detect the pitch.
123    pub fn signalsmith_stretch_set_formant_base(&self, frequency: f32) {
124        unsafe { sys::signalsmith_stretch_set_formant_base(self.inner, frequency) }
125    }
126
127    /// Add "pre-roll" to the output without affecting the stream position.
128    ///
129    /// Input samples must be interleaved, with the correct number of channels.
130    pub fn seek(&mut self, input: impl AsRef<[f32]>, playback_rate: f64) {
131        let input = input.as_ref();
132        let ptr = input.as_ptr();
133
134        debug_assert_eq!(0, input.len() % self.channel_count);
135
136        unsafe {
137            sys::signalsmith_stretch_seek(
138                self.inner,
139                ptr as _,
140                input.len() / self.channel_count,
141                playback_rate,
142            );
143        }
144    }
145
146    /// Add input to the stream, and get output. The length of input and output
147    /// may differ, which will create a time-stretch effect.
148    ///
149    /// Input samples must be interleaved, with the correct number of channels.
150    /// The output will be the same.
151    pub fn process(&mut self, input: impl AsRef<[f32]>, mut output: impl AsMut<[f32]>) {
152        let input = input.as_ref();
153        let output = output.as_mut();
154
155        debug_assert_eq!(0, input.len() % self.channel_count);
156        debug_assert_eq!(0, output.len() % self.channel_count);
157
158        unsafe {
159            sys::signalsmith_stretch_process(
160                self.inner,
161                input.as_ptr() as _,
162                input.len() / self.channel_count,
163                output.as_mut_ptr(),
164                output.len() / self.channel_count,
165            );
166        }
167    }
168
169    /// Process a complete audio buffer all in one go.
170    pub fn exact(&mut self, input: impl AsRef<[f32]>, mut output: impl AsMut<[f32]>) -> bool {
171        let input = input.as_ref();
172        let output = output.as_mut();
173
174        debug_assert_eq!(0, input.len() % self.channel_count);
175        debug_assert_eq!(0, output.len() % self.channel_count);
176
177        unsafe {
178            sys::signalsmith_stretch_exact(
179                self.inner,
180                input.as_ptr() as _,
181                input.len() / self.channel_count,
182                output.as_mut_ptr(),
183                output.len() / self.channel_count,
184            )
185        }
186    }
187
188    /// Flush remaining output from the decoder. Use [Self::output_latency] to
189    /// determine the correct length of the output buffer.
190    pub fn flush(&mut self, mut output: impl AsMut<[f32]>) {
191        let output = output.as_mut();
192        debug_assert_eq!(0, output.len() % self.channel_count);
193
194        unsafe {
195            sys::signalsmith_stretch_flush(
196                self.inner,
197                output.as_mut_ptr(),
198                output.len() / self.channel_count,
199            );
200        }
201    }
202}
203
204impl Drop for Stretch {
205    fn drop(&mut self) {
206        unsafe { sys::signalsmith_stretch_destroy(self.inner) }
207    }
208}