1mod effects;
8mod kit;
9mod osc;
10mod seq;
11#[cfg(test)]
12mod tests;
13
14pub(crate) use effects::{FilterKind, biquad_coeffs, drive_antideriv, drive_curve};
15pub(crate) use osc::{osc, poly_blep};
16pub(crate) use seq::seq_to_signal;
17
18use crate::dsl::{Adsr, Node, Playback, Shape, SoundDoc, Value};
19use crate::dsp::{Rng, node_path, node_seed, peak_limit};
20use effects::{biquad, chorus, compress, drive_adaa, flanger, modal_bank, phaser, reverb};
21use osc::{
22 dust_signal, fm_signal, impact_signal, noise_signal, osc_signal, saw_signal, square_signal,
23 super_signal, tri_signal,
24};
25use std::f32::consts::TAU;
26
27type Signal = Vec<f32>;
29
30pub struct RenderProduct {
36 pub mono: Signal,
39 pub stereo: Option<(Signal, Signal)>,
41 pub layers: Vec<LayerStats>,
44}
45
46mod output;
47mod tracks;
48
49pub use output::{loop_seam_db, make_loop_buffer, stereoize};
50use output::{normalize_output, normalize_output_v4};
51pub use tracks::{LayerStats, TracksRender, render_tracks};
52
53pub fn render_product(doc: &SoundDoc) -> RenderProduct {
57 if let Some(tr) = render_tracks(doc) {
58 let mono = tr
60 .left
61 .iter()
62 .zip(&tr.right)
63 .map(|(a, b)| 0.5 * (a + b))
64 .collect();
65 return RenderProduct {
66 mono,
67 stereo: Some((tr.left, tr.right)),
68 layers: tr.layers,
69 };
70 }
71 RenderProduct {
72 mono: render_plain(doc),
73 stereo: None,
74 layers: Vec::new(),
75 }
76}
77
78pub fn render(doc: &SoundDoc) -> Signal {
80 render_product(doc).mono
81}
82
83#[cfg(test)]
87pub(crate) fn render_graph(doc: &SoundDoc) -> Signal {
88 let sr = doc.sample_rate;
89 let n = ((doc.duration.clamp(0.0, 600.0) * sr as f32).ceil() as usize).max(1);
92 let mut rng = Rng::new(doc.seed);
93 render_node(&doc.root, n, sr, &mut rng, doc.effective_engine(), doc.seed)
94}
95
96fn render_plain(doc: &SoundDoc) -> Signal {
98 let sr = doc.sample_rate;
99 let n = ((doc.duration.clamp(0.0, 600.0) * sr as f32).ceil() as usize).max(1);
102 let mut rng = Rng::new(doc.seed);
103 let engine = doc.effective_engine();
104 let mut out = render_node(&doc.root, n, sr, &mut rng, engine, doc.seed);
105 if let Playback::Loop {
107 start_secs,
108 end_secs,
109 crossfade_secs,
110 } = doc.playback
111 {
112 out = make_loop_buffer(&out, sr, start_secs, end_secs, crossfade_secs);
113 }
114 match &doc.normalize {
115 Some(nz) if engine >= 4 => normalize_output_v4(&mut [&mut out], nz, sr),
117 Some(nz) => normalize_output(&mut out, nz),
118 None => peak_limit(&mut [&mut out]),
120 }
121 out
122}
123
124fn eval_value(v: &Value, n: usize, sr: u32) -> Vec<f32> {
130 let mut val = crate::streaming::value::Val::build(v, sr, n);
131 (0..n).map(|t| val.eval(t)).collect()
132}
133
134pub(crate) fn rand_seed(seed: u64, from: f32, to: f32, rate: f32) -> u64 {
138 let mut h = seed ^ crate::dsp::GOLDEN_GAMMA;
139 for bits in [from.to_bits(), to.to_bits(), rate.to_bits()] {
140 h = (h ^ bits as u64).wrapping_mul(crate::dsp::FNV_PRIME);
141 }
142 h
143}
144
145fn render_node(node: &Node, n: usize, sr: u32, rng: &mut Rng, engine: u32, path: u64) -> Signal {
150 match node {
151 Node::Square { freq, duty } => square_signal(freq, duty, n, sr),
152 Node::Triangle { freq } => tri_signal(freq, n, sr),
153 Node::Sawtooth { freq } => saw_signal(freq, n, sr),
154 Node::Super {
155 wave,
156 freq,
157 voices,
158 detune_cents,
159 } => super_signal(*wave, freq, *voices, *detune_cents, n, sr),
160 Node::Sine { freq } => osc_signal(freq, n, sr, |p| osc(Shape::Sine, p)),
161 Node::Noise { color } => {
162 if engine >= 2 {
166 let mut local = Rng::new(node_seed(path));
167 noise_signal(*color, n, &mut local)
168 } else {
169 noise_signal(*color, n, rng)
170 }
171 }
172 Node::Fm { freq, ratio, index } => fm_signal(freq, *ratio, index, n, sr),
173 Node::Seq { .. } => {
177 if engine >= 2 {
178 let mut local = Rng::new(node_seed(path));
179 seq_to_signal(node, n, sr, &mut local, engine)
180 } else {
181 seq_to_signal(node, n, sr, rng, engine)
182 }
183 }
184 Node::Impact { hardness, velocity } => impact_signal(*hardness, *velocity, n, sr),
185 Node::Dust { density, decay } => {
186 if engine >= 2 {
187 let mut local = Rng::new(node_seed(path));
188 dust_signal(*density, *decay, n, sr, &mut local)
189 } else {
190 dust_signal(*density, *decay, n, sr, rng)
191 }
192 }
193 Node::Env { adsr: env } => adsr(env, n, sr),
194 Node::Tracks { tracks, .. } => {
196 let mut acc = vec![0.0f32; n];
197 for (i, t) in tracks.iter().enumerate() {
198 let sig = render_node(&t.node, n, sr, rng, engine, node_path(path, i));
199 for (o, v) in acc.iter_mut().zip(sig) {
200 *o += v * t.gain;
201 }
202 }
203 acc
204 }
205 Node::Mix { inputs } => {
206 let mut acc = vec![0.0f32; n];
207 for (i, input) in inputs.iter().enumerate() {
208 let s = render_node(input, n, sr, rng, engine, node_path(path, i));
209 for (o, v) in acc.iter_mut().zip(s) {
210 *o += v;
211 }
212 }
213 acc
214 }
215 Node::Mul { inputs } => {
216 let mut acc = vec![1.0f32; n];
217 for (i, input) in inputs.iter().enumerate() {
218 let s = render_node(input, n, sr, rng, engine, node_path(path, i));
219 for (o, v) in acc.iter_mut().zip(s) {
220 *o *= v;
221 }
222 }
223 acc
224 }
225 Node::Chain { stages } => {
226 let mut buf: Option<Signal> = None;
227 for (i, stage) in stages.iter().enumerate() {
228 let cp = node_path(path, i);
229 buf = Some(match (&buf, stage.is_processor()) {
230 (Some(input), true) => apply_processor(stage, input, sr, rng, engine, cp),
232 (_, _) => render_node(stage, n, sr, rng, engine, cp),
234 });
235 }
236 buf.unwrap_or_else(|| vec![0.0; n])
237 }
238 _ if node.is_processor() => vec![0.0; n],
240 _ => unreachable!("unhandled source node in render_node"),
243 }
244}
245
246fn apply_processor(
251 node: &Node,
252 input: &[f32],
253 sr: u32,
254 rng: &mut Rng,
255 engine: u32,
256 path: u64,
257) -> Signal {
258 match node {
259 Node::Duck {
260 trigger,
261 amount,
262 attack,
263 release,
264 } => {
265 let trig = render_node(trigger, input.len(), sr, rng, engine, node_path(path, 0));
268 let srf = sr as f32;
269 let at = (-1.0 / (attack.max(1e-4) * srf)).exp();
270 let rt = (-1.0 / (release.max(1e-4) * srf)).exp();
271 let mut env = 0.0f32;
272 input
273 .iter()
274 .zip(trig)
275 .map(|(&x, t)| {
276 let rect = t.abs().min(1.0);
277 let coeff = if rect > env { at } else { rt };
278 env = rect + coeff * (env - rect);
279 x * (1.0 - amount * env)
280 })
281 .collect()
282 }
283 Node::Lowpass { cutoff, q } => biquad(input, cutoff, *q, sr, FilterKind::Low),
284 Node::Highpass { cutoff, q } => biquad(input, cutoff, *q, sr, FilterKind::High),
285 Node::Bandpass { cutoff, q } => biquad(input, cutoff, *q, sr, FilterKind::Band),
286 Node::Notch { cutoff, q } => biquad(input, cutoff, *q, sr, FilterKind::Notch),
287 Node::Peak { cutoff, q, gain_db } => {
288 biquad(input, cutoff, *q, sr, FilterKind::Peak(*gain_db))
289 }
290 Node::Lowshelf { cutoff, gain_db } => {
291 biquad(input, cutoff, 0.707, sr, FilterKind::LowShelf(*gain_db))
292 }
293 Node::Highshelf { cutoff, gain_db } => {
294 biquad(input, cutoff, 0.707, sr, FilterKind::HighShelf(*gain_db))
295 }
296 Node::Gain { amount } => {
297 let g = eval_value(amount, input.len(), sr);
298 input.iter().zip(g).map(|(x, k)| x * k).collect()
299 }
300 Node::Bitcrush { bits } => {
301 let levels = (1u32 << (*bits as u32).min(31)) as f32;
304 let half = levels / 2.0;
305 input
306 .iter()
307 .map(|x| (x.clamp(-1.0, 1.0) * half).round() / half)
308 .collect()
309 }
310 Node::Downsample { factor } => {
311 let f = (*factor).max(1) as usize;
312 let mut out = Vec::with_capacity(input.len());
313 let mut held = 0.0;
314 for (i, &x) in input.iter().enumerate() {
315 if i % f == 0 {
316 held = x;
317 }
318 out.push(held);
319 }
320 out
321 }
322 Node::Delay { secs, feedback } => {
323 let dn = crate::dsp::delay_line_len(*secs, sr);
326 let mut buf = vec![0.0f32; dn];
327 let mut w = 0usize;
328 let mut out = Vec::with_capacity(input.len());
329 for &x in input {
330 let delayed = buf[w];
331 let y = x + feedback * delayed;
332 buf[w] = y;
333 w = (w + 1) % dn;
334 out.push(y);
335 }
336 out
337 }
338 Node::Reverb { room, mix } => reverb(input, *room, *mix, sr, 0),
339 Node::Modal { modes, mix } => modal_bank(input, modes, *mix, sr),
340 Node::Drive { amount, shape, aa } => {
341 let a = eval_value(amount, input.len(), sr);
342 let use_adaa = engine >= 1 && aa.unwrap_or(true);
346 if use_adaa {
347 drive_adaa(input, &a, *shape)
348 } else {
349 input
350 .iter()
351 .zip(a)
352 .map(|(x, amt)| drive_curve(amt.max(0.0) * x, *shape))
353 .collect()
354 }
355 }
356 Node::RingMod { freq } => {
357 let f = eval_value(freq, input.len(), sr);
358 let srf = sr as f32;
359 let mut phase = 0.0f32;
360 let mut out = Vec::with_capacity(input.len());
361 for (i, &x) in input.iter().enumerate() {
362 out.push(x * (TAU * phase).sin());
363 phase += f[i].max(0.0) / srf;
364 phase -= phase.floor();
365 }
366 out
367 }
368 Node::Chorus { rate, depth, mix } => chorus(input, *rate, *depth, *mix, sr),
369 Node::Flanger {
370 rate,
371 depth,
372 feedback,
373 mix,
374 } => flanger(input, *rate, *depth, *feedback, *mix, sr),
375 Node::Phaser {
376 rate,
377 depth,
378 feedback,
379 mix,
380 } => phaser(input, *rate, *depth, *feedback, *mix, sr),
381 Node::Compress {
382 threshold,
383 ratio,
384 attack,
385 release,
386 makeup,
387 } => compress(input, *threshold, *ratio, *attack, *release, *makeup, sr),
388 _ => unreachable!("unhandled processor node in apply_processor"),
392 }
393}
394
395fn adsr(env: &Adsr, n: usize, sr: u32) -> Signal {
397 let Adsr { a, d, s, r, punch } = *env;
398 let srf = sr as f32;
399 let rel_start = (n as f32 / srf - r).max(0.0);
400 (0..n)
401 .map(|i| crate::dsp::adsr_env(i as f32 / srf, a, d, s, r, punch, rel_start))
402 .collect()
403}