1use core::num::NonZeroU32;
2use core::ops::Range;
3
4use crate::{
5 diff::{Diff, EventQueue, Patch, PatchError, PathBuilder},
6 dsp::fade::FadeCurve,
7 event::ParamData,
8 param::smoother::{DEFAULT_GAIN_SPAN, SmoothedParam, SmootherConfig},
9};
10
11#[repr(transparent)]
17#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
18#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct Mix(f32);
21
22impl Mix {
23 pub const FULLY_FIRST: Self = Self(0.0);
25 pub const FULLY_SECOND: Self = Self(1.0);
27
28 pub const FULLY_DRY: Self = Self(0.0);
30 pub const FULLY_WET: Self = Self(1.0);
32
33 pub const CENTER: Self = Self(0.5);
35
36 pub const fn new(mix: f32) -> Self {
43 Self(mix.clamp(0.0, 1.0))
44 }
45
46 pub const fn from_percent(percent: f32) -> Self {
53 Self::new(percent / 100.0)
54 }
55
56 pub const fn get(&self) -> f32 {
57 self.0
58 }
59
60 pub const fn to_percent(self) -> f32 {
61 self.0 * 100.0
62 }
63
64 pub fn compute_gains(&self, fade_curve: FadeCurve) -> (f32, f32) {
66 fade_curve.compute_gains_0_to_1(self.0)
67 }
68}
69
70impl From<f32> for Mix {
71 fn from(value: f32) -> Self {
72 Self::new(value)
73 }
74}
75
76impl From<f64> for Mix {
77 fn from(value: f64) -> Self {
78 Self::new(value as f32)
79 }
80}
81
82impl From<Mix> for f32 {
83 fn from(value: Mix) -> Self {
84 value.get()
85 }
86}
87
88impl From<Mix> for f64 {
89 fn from(value: Mix) -> Self {
90 value.get() as f64
91 }
92}
93
94impl Diff for Mix {
95 fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
96 if self != baseline {
97 event_queue.push_param(ParamData::F32(self.0), path);
98 }
99 }
100}
101
102impl Patch for Mix {
103 type Patch = Self;
104
105 fn patch(data: &ParamData, _: &[u32]) -> Result<Self::Patch, PatchError> {
106 match data {
107 ParamData::F32(v) => Ok(Self::new(*v)),
108 _ => Err(PatchError::InvalidData),
109 }
110 }
111
112 fn apply(&mut self, value: Self::Patch) {
113 *self = value;
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq)]
119pub struct MixDSP {
120 gain_0: SmoothedParam,
121 gain_1: SmoothedParam,
122}
123
124impl MixDSP {
125 pub fn new(
126 mix: Mix,
127 fade_curve: FadeCurve,
128 config: SmootherConfig,
129 sample_rate: NonZeroU32,
130 ) -> Self {
131 let (gain_0, gain_1) = mix.compute_gains(fade_curve);
132
133 Self {
134 gain_0: SmoothedParam::new(gain_0, DEFAULT_GAIN_SPAN, config, sample_rate),
135 gain_1: SmoothedParam::new(gain_1, DEFAULT_GAIN_SPAN, config, sample_rate),
136 }
137 }
138
139 pub fn set_mix(&mut self, mix: Mix, fade_curve: FadeCurve) {
140 let (gain_0, gain_1) = mix.compute_gains(fade_curve);
141
142 self.gain_0.set_value(gain_0);
143 self.gain_1.set_value(gain_1);
144 }
145
146 pub fn reset_to_target(&mut self) {
148 self.gain_0.reset_to_target();
149 self.gain_1.reset_to_target();
150 }
151
152 pub fn update_sample_rate(&mut self, sample_rate: NonZeroU32) {
153 self.gain_0.update_sample_rate(sample_rate);
154 self.gain_1.update_sample_rate(sample_rate);
155 }
156
157 pub fn is_smoothing(&self) -> bool {
158 self.gain_0.is_smoothing() || self.gain_1.is_smoothing()
159 }
160
161 pub fn has_settled(&self) -> bool {
162 self.gain_0.has_settled() && self.gain_1.has_settled()
163 }
164
165 pub fn mix_dry_into_wet_mono(&mut self, dry: &[f32], wet: &mut [f32], frames: usize) {
166 self.mix_first_into_second_mono(dry, wet, frames);
167 }
168
169 pub fn first_gain_target(&self) -> f32 {
170 self.gain_0.target_value()
171 }
172
173 pub fn second_gain_target(&self) -> f32 {
174 self.gain_1.target_value()
175 }
176
177 pub fn mix_dry_into_wet_stereo(
178 &mut self,
179 dry_l: &[f32],
180 dry_r: &[f32],
181 wet_l: &mut [f32],
182 wet_r: &mut [f32],
183 frames: usize,
184 ) {
185 self.mix_first_into_second_stereo(dry_l, dry_r, wet_l, wet_r, frames);
186 }
187
188 pub fn mix_dry_into_wet<VF: AsRef<[f32]>, VS: AsMut<[f32]>>(
189 &mut self,
190 dry: &[VF],
191 wet: &mut [VS],
192 dry_range: Range<usize>,
193 wet_range: Range<usize>,
194 scratch_buffer_0: &mut [f32],
195 scratch_buffer_1: &mut [f32],
196 ) {
197 self.mix_first_into_second(
198 dry,
199 wet,
200 dry_range,
201 wet_range,
202 scratch_buffer_0,
203 scratch_buffer_1,
204 );
205 }
206
207 pub fn mix_first_into_second_mono(&mut self, first: &[f32], second: &mut [f32], frames: usize) {
208 let first = &first[..frames];
209 let second = &mut second[..frames];
210
211 if self.is_smoothing() {
212 for (first_s, second_s) in first.iter().zip(second.iter_mut()) {
213 let gain_first = self.gain_0.next_smoothed();
214 let gain_second = self.gain_1.next_smoothed();
215
216 *second_s = first_s * gain_first + *second_s * gain_second;
217 }
218
219 self.gain_0.settle();
220 self.gain_1.settle();
221 } else if self.gain_1.target_value() <= 0.00001 && self.gain_0.target_value() >= 0.99999 {
222 second.copy_from_slice(first);
224 } else if self.gain_0.target_value() <= 0.00001 && self.gain_1.target_value() >= 0.99999 {
225 } else {
227 for (first_s, second_s) in first.iter().zip(second.iter_mut()) {
228 *second_s =
229 first_s * self.gain_0.target_value() + *second_s * self.gain_1.target_value();
230 }
231 }
232 }
233
234 pub fn mix_first_into_second_stereo(
235 &mut self,
236 first_l: &[f32],
237 first_r: &[f32],
238 second_l: &mut [f32],
239 second_r: &mut [f32],
240 frames: usize,
241 ) {
242 let first_l = &first_l[..frames];
243 let first_r = &first_r[..frames];
244 let second_l = &mut second_l[..frames];
245 let second_r = &mut second_r[..frames];
246
247 if self.is_smoothing() {
248 for i in 0..frames {
249 let gain_0 = self.gain_0.next_smoothed();
250 let gain_1 = self.gain_1.next_smoothed();
251
252 second_l[i] = first_l[i] * gain_0 + second_l[i] * gain_1;
253 second_r[i] = first_r[i] * gain_0 + second_r[i] * gain_1;
254 }
255
256 self.gain_0.settle();
257 self.gain_1.settle();
258 } else if self.gain_1.target_value() <= 0.00001 && self.gain_0.target_value() >= 0.99999 {
259 second_l.copy_from_slice(first_l);
261 second_r.copy_from_slice(first_r);
262 } else if self.gain_0.target_value() <= 0.00001 && self.gain_1.target_value() >= 0.99999 {
263 } else {
265 for i in 0..frames {
266 second_l[i] = first_l[i] * self.gain_0.target_value()
267 + second_l[i] * self.gain_1.target_value();
268 second_r[i] = first_r[i] * self.gain_0.target_value()
269 + second_r[i] * self.gain_1.target_value();
270 }
271 }
272 }
273
274 pub fn mix_first_into_second<VF: AsRef<[f32]>, VS: AsMut<[f32]>>(
275 &mut self,
276 first: &[VF],
277 second: &mut [VS],
278 first_range: Range<usize>,
279 second_range: Range<usize>,
280 scratch_buffer_0: &mut [f32],
281 scratch_buffer_1: &mut [f32],
282 ) {
283 let frames =
284 (first_range.end - first_range.start).min(second_range.end - second_range.start);
285
286 if second.len() == 1 {
287 self.mix_first_into_second_mono(
288 &first[0].as_ref()[first_range.clone()],
289 &mut second[0].as_mut()[second_range.clone()],
290 frames,
291 );
292 } else if second.len() == 2 {
293 let (second_l, second_r) = second.split_first_mut().unwrap();
294 self.mix_first_into_second_stereo(
295 &first[0].as_ref()[first_range.clone()],
296 &first[1].as_ref()[first_range.clone()],
297 &mut second_l.as_mut()[second_range.clone()],
298 &mut second_r[0].as_mut()[second_range.clone()],
299 frames,
300 );
301 } else if self.is_smoothing() {
302 self.gain_0
303 .process_into_buffer(&mut scratch_buffer_0[..frames]);
304 self.gain_1
305 .process_into_buffer(&mut scratch_buffer_1[..frames]);
306
307 for (first_ch, second_ch) in first[..second.len()].iter().zip(second.iter_mut()) {
308 for (((&first_s, &g0), &g1), second_s) in first_ch.as_ref()
309 [first_range.start..first_range.start + frames]
310 .iter()
311 .zip(scratch_buffer_0[..frames].iter())
312 .zip(scratch_buffer_1[..frames].iter())
313 .zip(
314 second_ch.as_mut()[second_range.start..second_range.start + frames]
315 .iter_mut(),
316 )
317 {
318 *second_s = first_s * g0 + *second_s * g1;
319 }
320 }
321
322 self.gain_0.settle();
323 self.gain_1.settle();
324 } else if self.gain_1.target_value() <= 0.00001 && self.gain_0.target_value() >= 0.99999 {
325 for (first_ch, second_ch) in first[..second.len()].iter().zip(second.iter_mut()) {
327 second_ch.as_mut()[second_range.start..second_range.start + frames]
328 .copy_from_slice(
329 &first_ch.as_ref()[first_range.start..first_range.start + frames],
330 );
331 }
332 } else if self.gain_0.target_value() <= 0.00001 && self.gain_1.target_value() >= 0.99999 {
333 } else {
335 for (first_ch, second_ch) in first[..second.len()].iter().zip(second.iter_mut()) {
336 for (&first_s, second_s) in first_ch.as_ref()
337 [first_range.start..first_range.start + frames]
338 .iter()
339 .zip(
340 second_ch.as_mut()[second_range.start..second_range.start + frames]
341 .iter_mut(),
342 )
343 {
344 *second_s = first_s * self.gain_0.target_value()
345 + *second_s * self.gain_1.target_value();
346 }
347 }
348 }
349 }
350}