Skip to content

Commit d3a4951

Browse files
committed
"fantasy" effect
1 parent d02a632 commit d3a4951

File tree

3 files changed

+253
-4
lines changed

3 files changed

+253
-4
lines changed

src/fantasy.rs

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
use crate::custom_dsp::{Dsp, DspType, Parameter, ParameterType, ProcessResult};
2+
use circular_buffer::CircularBuffer;
3+
use realfft::num_complex::Complex;
4+
use realfft::num_traits::Zero;
5+
use realfft::{ComplexToReal, RealFftPlanner, RealToComplex};
6+
use std::sync::Arc;
7+
8+
const BUFLEN: usize = 4096;
9+
const HBUFLEN: usize = BUFLEN / 2 + 1;
10+
11+
pub struct Fantasy {
12+
delay: CircularBuffer<BUFLEN, f32>,
13+
silence: usize,
14+
15+
persistent_freqs: [f32; HBUFLEN],
16+
17+
scratch: [Complex<f32>; BUFLEN],
18+
out: [Complex<f32>; HBUFLEN],
19+
copy: [f32; BUFLEN],
20+
residual: [f32; BUFLEN / 2],
21+
dft_fwd: Arc<dyn RealToComplex<f32>>,
22+
dft_bwd: Arc<dyn ComplexToReal<f32>>,
23+
24+
detune_window: usize,
25+
detune_factor: f32,
26+
detune_bias: bool,
27+
28+
echo_decay: f32,
29+
echo_support: f32
30+
}
31+
32+
impl Dsp for Fantasy {
33+
fn name() -> &'static str {
34+
"Fantasy"
35+
}
36+
37+
fn version() -> u32 {
38+
1
39+
}
40+
41+
fn ty() -> DspType {
42+
DspType::Effect
43+
}
44+
45+
fn parameters() -> Vec<Parameter<Self>> {
46+
vec![
47+
Parameter {
48+
ty: ParameterType::Float {
49+
min: 0.0,
50+
max: 1.0,
51+
default: 0.7,
52+
setter: |value, dsp| dsp.detune_factor = value,
53+
getter: |dsp| dsp.detune_factor,
54+
},
55+
name: "detune_factor",
56+
unit: "",
57+
desc: "",
58+
},
59+
Parameter {
60+
ty: ParameterType::Int {
61+
min: 0,
62+
max: 20,
63+
default: 8,
64+
max_is_inf: false,
65+
names: None,
66+
setter: |value, dsp| dsp.detune_window = value as usize,
67+
getter: |dsp| dsp.detune_window as i32,
68+
},
69+
name: "detune_window",
70+
unit: " bands",
71+
desc: "",
72+
},
73+
Parameter {
74+
ty: ParameterType::Bool {
75+
names: Some(("Forward", "Backwards")),
76+
default: true,
77+
setter: |value, dsp| dsp.detune_bias = value,
78+
getter: |dsp| dsp.detune_bias,
79+
},
80+
name: "detune_bias",
81+
unit: "",
82+
desc: "",
83+
},
84+
Parameter {
85+
ty: ParameterType::Float {
86+
min: 0.5,
87+
max: 0.99,
88+
default: 0.95,
89+
setter: |value, dsp| dsp.echo_decay = value,
90+
getter: |dsp| dsp.echo_decay,
91+
},
92+
name: "echo_decay",
93+
unit: "",
94+
desc: "",
95+
},
96+
Parameter {
97+
ty: ParameterType::Float {
98+
min: 0.0,
99+
max: 1.0,
100+
default: 0.1,
101+
setter: |value, dsp| dsp.echo_support = value,
102+
getter: |dsp| dsp.echo_support,
103+
},
104+
name: "echo_support",
105+
unit: "",
106+
desc: "",
107+
}
108+
]
109+
}
110+
111+
fn create() -> Self {
112+
let mut planner = RealFftPlanner::new();
113+
Fantasy {
114+
delay: Default::default(),
115+
silence: 0,
116+
117+
persistent_freqs: [0.; HBUFLEN],
118+
119+
scratch: [Complex::zero(); BUFLEN],
120+
out: [Complex::zero(); HBUFLEN],
121+
copy: [0.; BUFLEN],
122+
residual: [0.; BUFLEN / 2],
123+
dft_fwd: planner.plan_fft_forward(BUFLEN),
124+
dft_bwd: planner.plan_fft_inverse(BUFLEN),
125+
126+
detune_factor: 0.7,
127+
detune_window: 8,
128+
detune_bias: true,
129+
130+
echo_decay: 0.95,
131+
echo_support: 0.1
132+
}
133+
}
134+
135+
fn reset(&mut self) {
136+
self.delay.fill(0.);
137+
self.silence = 0;
138+
self.persistent_freqs.fill(0.);
139+
self.residual.fill(0.);
140+
}
141+
142+
fn should_process(&mut self, idle: bool, incoming_length: usize) -> ProcessResult {
143+
if idle {
144+
self.silence += incoming_length;
145+
if self.silence >= BUFLEN && self.persistent_freqs.iter().all(|it| *it <= 0.0001) {
146+
ProcessResult::SkipSilent
147+
} else {
148+
ProcessResult::Continue
149+
}
150+
} else {
151+
self.silence = 0;
152+
ProcessResult::Continue
153+
}
154+
}
155+
156+
fn read(
157+
&mut self,
158+
in_data: &[f32],
159+
out_data: &mut [f32],
160+
in_channels: usize,
161+
out_channels: usize,
162+
) {
163+
out_data.fill(0.);
164+
165+
self.delay.extend(
166+
in_data
167+
.iter()
168+
.step_by(2)
169+
.zip(in_data.iter().skip(1).step_by(2))
170+
.map(|(l, r)| (l + r) / 2.),
171+
);
172+
173+
if self.delay.is_full() {
174+
copy_contiguous(&self.delay, &mut self.copy);
175+
self.dft_fwd
176+
.process_with_scratch(&mut self.copy, &mut self.out, &mut self.scratch)
177+
.unwrap();
178+
179+
self.scratch[..HBUFLEN].copy_from_slice(&self.out);
180+
for i in 8..HBUFLEN-8 {
181+
let orig_norm = self.out[i].norm();
182+
let mut target_norm = orig_norm;
183+
184+
let window: usize = self.detune_window;
185+
if window > 0 {
186+
let start = window.saturating_sub(i);
187+
let end = (2 * window + 1).min(HBUFLEN - 2 - i);
188+
let range = start..=end;
189+
if self.detune_bias {
190+
for j in range { // the world if you could `let it: impl Iterator<...>`
191+
target_norm = (1. - self.detune_factor) * target_norm + self.detune_factor * self.scratch[i + j - window].norm();
192+
}
193+
} else {
194+
for j in range.rev() {
195+
target_norm = (1. - self.detune_factor) * target_norm + self.detune_factor * self.scratch[i + j - window].norm();
196+
}
197+
}
198+
//target_norm = (i-range..=i+range).map(|i| self.scratch[i].norm()).sum::<f32>() / (range * 2 + 1) as f32;
199+
}
200+
201+
target_norm += self.persistent_freqs[i];
202+
203+
if orig_norm < 0.000001 {
204+
//self.out[i] = Complex::from(target_norm);
205+
} else {
206+
self.out[i] *= target_norm / orig_norm;
207+
}
208+
209+
self.persistent_freqs[i] += orig_norm * self.echo_support;
210+
self.persistent_freqs[i] *= self.echo_decay;
211+
}
212+
213+
self.dft_bwd
214+
.process_with_scratch(&mut self.out, &mut self.copy, &mut self.scratch)
215+
.unwrap();
216+
}
217+
218+
let out_len = out_data.len() / out_channels;
219+
for i in 0..BUFLEN {
220+
self.copy[i] /= BUFLEN as f32;
221+
}
222+
223+
// write to outputs
224+
for i in 0..out_len {
225+
let new = self.copy[BUFLEN - (out_len * 2) + i];
226+
let old = self.residual[i];
227+
let it = interp(old, new, i, out_len);
228+
out_data[i * 2] = it;
229+
out_data[i * 2 + 1] = it;
230+
}
231+
232+
// update residual
233+
self.residual[..out_len].copy_from_slice(&self.copy[BUFLEN - out_len..]);
234+
}
235+
}
236+
237+
fn copy_contiguous<const N: usize, T: Copy>(from: &CircularBuffer<N, T>, to: &mut [T]) {
238+
let (p1, p2) = from.as_slices();
239+
to[..p1.len()].copy_from_slice(p1);
240+
to[p1.len()..p1.len() + p2.len()].copy_from_slice(p2);
241+
}
242+
243+
fn interp(x: f32, y: f32, i: usize, l: usize) -> f32 {
244+
let r = i as f32 / (l - 1) as f32;
245+
(1. - r) * x + r * y
246+
}

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::dynamics::LocalDynamics;
55
use crate::exact::ExactOut;
6+
use crate::fantasy::Fantasy;
67
use crate::noise_reduction::NoiseReduction;
78
use crate::windy::WindySynth;
89

@@ -17,5 +18,7 @@ mod result;
1718
mod dynamics;
1819
mod noise_reduction;
1920
mod exact;
21+
mod fantasy;
22+
mod vocoder;
2023

21-
expose_dsp_list!(WindySynth, LocalDynamics, NoiseReduction, ExactOut);
24+
expose_dsp_list!(WindySynth, LocalDynamics, NoiseReduction, ExactOut, Fantasy);

src/simulate.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::custom_dsp;
1+
use crate::{custom_dsp, fantasy};
22
use crate::raw_bindings::FMOD_RESULT::FMOD_OK;
33
use crate::raw_bindings::{FMOD_DEBUG_FLAGS, FMOD_DEBUG_MODE, FMOD_Debug_Initialize, FMOD_RESULT};
44
use crate::result::FmResultTrait;
@@ -47,13 +47,13 @@ fn sim_effect() {
4747
.fm_unwrap();
4848
}
4949
let system = System::create().fm_unwrap();
50-
let desc = custom_dsp::into_desc::<noise_reduction::NoiseReduction>();
50+
let desc = custom_dsp::into_desc::<fantasy::Fantasy>();
5151
let dsp = system.create_dsp_from_description(&desc).fm_unwrap();
5252
let sound = system.create_sound("./noisy.mp3").fm_unwrap();
5353
let channel = system.play_sound(sound, None, true).fm_unwrap();
5454
channel.add_dsp(0, &dsp).fm_unwrap();
5555
channel.set_paused(false).fm_unwrap();
56-
for _ in 0..(25 * 60) {
56+
for _ in 0..(80 * 60) {
5757
system.update().fm_unwrap();
5858
sleep(Duration::from_millis(12));
5959
}

0 commit comments

Comments
 (0)