1use std::f64::consts::TAU;
20use std::sync::{Arc, Mutex};
21
22use serde::{Deserialize, Serialize};
23
24use super::{
25 EventKind, HostIdentity, PreRoll, PresetState, RenderError, RenderJob, RenderRecord, Rendered,
26 Renderer, note_events,
27};
28use crate::audio::Audio;
29use crate::cast;
30use crate::synth::Synth;
31use crate::units::Seconds;
32
33const LEVEL_JITTER: f64 = 0.005;
36const RELEASE_DB: f64 = 60.0;
38
39#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
41pub struct FakePatch {
42 pub harmonics: Vec<f64>,
44 pub attack: Seconds,
46 pub release: Seconds,
48}
49
50impl FakePatch {
51 #[must_use]
53 pub fn init() -> Self {
54 Self {
55 harmonics: vec![1.0, 0.5, 0.33, 0.25, 0.2],
56 attack: Seconds::from_const(0.005),
57 release: Seconds::from_const(0.2),
58 }
59 }
60
61 pub fn state(&self, synth: Synth) -> Result<PresetState, serde_json::Error> {
68 let root = serde_json::json!({ "preset_name": "fake", "settings": self });
69 Ok(PresetState::new(synth, serde_json::to_vec(&root)?))
70 }
71
72 fn of_state(state: &[u8]) -> Result<Self, RenderError> {
74 let not_fake = |cause: String| RenderError::State(format!("not a fake patch ({cause})"));
75 let root: serde_json::Value =
76 serde_json::from_slice(state).map_err(|source| not_fake(source.to_string()))?;
77 serde_json::from_value(root["settings"].clone())
78 .map_err(|source| not_fake(source.to_string()))
79 }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum Fault {
85 Crash,
87 Hang,
90 Silence,
92 KeepInit,
95 Rename,
98}
99
100#[derive(Clone, Copy, Debug, PartialEq)]
102pub enum FakeStep {
103 Load,
105 PreRoll(PreRoll),
107 Clip(Seconds),
109}
110
111pub type FakeLog = Arc<Mutex<Vec<FakeStep>>>;
113
114#[derive(Clone, Debug)]
116pub struct FakeRenderer {
117 synth: Synth,
118 seed: u64,
119 renders: u64,
120 faults: Vec<(u64, Fault)>,
121 loaded: Option<Vec<u8>>,
122 fixed: Option<FakePatch>,
123 log: Option<FakeLog>,
124 last_state_load: Option<Seconds>,
125}
126
127impl FakeRenderer {
128 #[must_use]
131 pub fn new(synth: Synth, seed: u64) -> Self {
132 Self {
133 synth,
134 seed,
135 renders: 0,
136 faults: Vec::new(),
137 loaded: None,
138 fixed: None,
139 log: None,
140 last_state_load: None,
141 }
142 }
143
144 #[must_use]
147 pub fn with_patch(mut self, patch: FakePatch) -> Self {
148 self.fixed = Some(patch);
149 self
150 }
151
152 #[must_use]
154 pub fn with_log(mut self, log: FakeLog) -> Self {
155 self.log = Some(log);
156 self
157 }
158
159 #[must_use]
161 pub fn with_fault(mut self, at: u64, fault: Fault) -> Self {
162 self.faults.push((at, fault));
163 self
164 }
165
166 #[must_use]
168 pub fn renders(&self) -> u64 {
169 self.renders
170 }
171}
172
173impl Renderer for FakeRenderer {
174 fn identity(&self) -> HostIdentity {
175 HostIdentity {
176 synth: self.synth,
177 build: "fake".to_owned(),
178 host: "fake".to_owned(),
179 arch: "fake".to_owned(),
180 }
181 }
182
183 fn render(&mut self, job: &RenderJob) -> Result<Rendered, RenderError> {
184 self.play_job(job, true)
185 }
186
187 fn render_init(&mut self, job: &RenderJob) -> Result<Rendered, RenderError> {
188 if self.loaded.is_some() {
189 return Err(RenderError::Host {
190 cause: "the fake already holds a loaded state".to_owned(),
191 fix: "Render the init patch first".to_owned(),
192 });
193 }
194 self.play_job(job, false)
195 }
196
197 fn last_state_load(&self) -> Option<Seconds> {
198 self.last_state_load
199 }
200
201 fn save_state(&mut self) -> Result<Vec<u8>, RenderError> {
202 match &self.loaded {
203 Some(state) => Ok(state.clone()),
204 None => Ok(init_state(self.synth)?.bytes().to_vec()),
205 }
206 }
207}
208
209fn init_state(synth: Synth) -> Result<PresetState, RenderError> {
210 FakePatch::init()
211 .state(synth)
212 .map_err(|source| RenderError::State(source.to_string()))
213}
214
215impl FakeRenderer {
216 fn play_job(&mut self, job: &RenderJob, load: bool) -> Result<Rendered, RenderError> {
217 let index = self.renders;
218 self.renders += 1;
219 let fault = self
220 .faults
221 .iter()
222 .find(|(at, _)| *at == index)
223 .map(|(_, fault)| *fault);
224 match fault {
225 Some(Fault::Crash) => return Err(RenderError::Crashed("fake crash".to_owned())),
226 Some(Fault::Hang) => {
227 return Err(RenderError::TimedOut {
228 after: Seconds::from_const(30.0),
229 output: "fake hang".to_owned(),
230 });
231 }
232 _ => {}
233 }
234 if job.state().synth() != self.synth {
235 return Err(RenderError::WrongSynth {
236 state: job.state().synth(),
237 renderer: self.synth,
238 });
239 }
240 self.last_state_load = None;
241 let loading = std::time::Instant::now();
242 let patch = if load && fault != Some(Fault::KeepInit) {
243 let patch = match &self.fixed {
244 Some(patch) => patch.clone(),
245 None => FakePatch::of_state(job.state().bytes())?,
246 };
247 let mut saved = job.state().bytes().to_vec();
248 if fault == Some(Fault::Rename) {
249 let mut root: serde_json::Value = serde_json::from_slice(&saved)
250 .map_err(|source| RenderError::State(source.to_string()))?;
251 root["preset_name"] = "renamed".into();
252 saved = serde_json::to_vec(&root)
253 .map_err(|source| RenderError::State(source.to_string()))?;
254 }
255 self.loaded = Some(saved);
256 patch
257 } else {
258 if load {
259 self.loaded = Some(init_state(self.synth)?.bytes().to_vec());
260 }
261 FakePatch::init()
262 };
263 if load {
264 self.last_state_load = Seconds::new(loading.elapsed().as_secs_f64()).ok();
265 }
266 if let Some(log) = &self.log {
267 let mut log = log
268 .lock()
269 .unwrap_or_else(std::sync::PoisonError::into_inner);
270 if load {
271 log.push(FakeStep::Load);
272 }
273 log.extend(
274 job.recipe()
275 .pre_roll()
276 .iter()
277 .copied()
278 .map(FakeStep::PreRoll),
279 );
280 log.push(FakeStep::Clip(job.last_note_off()));
281 }
282 let mut rng = SplitMix::for_render(self.seed, index);
283 let (mono, end) = play(&patch, job, &mut rng, fault == Some(Fault::Silence))?;
284 let audio = Audio::new(job.recipe().sample_rate(), vec![mono.clone(), mono])?;
285 Ok(Rendered {
286 audio,
287 record: RenderRecord::new(self.identity(), job, end),
288 })
289 }
290}
291
292struct Voice {
294 key: u8,
295 hz: f64,
296 gain: f64,
297 phases: Vec<f64>,
298 on: usize,
299 off: Option<usize>,
300}
301
302fn play(
304 patch: &FakePatch,
305 job: &RenderJob,
306 rng: &mut SplitMix,
307 silent: bool,
308) -> Result<(Vec<f64>, super::ClipEnd), RenderError> {
309 let recipe = job.recipe();
310 let rate = f64::from(recipe.sample_rate().get());
311 let mut voices: Vec<Voice> = Vec::new();
312 let events = note_events(job.notes(), recipe.sample_rate());
313 let mut next = 0;
314 let mut watch = recipe.tail_watch(job.notes());
315 let attack = patch.attack.get() * rate;
316 let release = patch.release.get() * rate;
317 let mut out = Vec::new();
318 loop {
319 let frame = out.len();
320 while let Some(event) = events.get(next).filter(|e| e.frame <= frame) {
321 match event.kind {
322 EventKind::On => voices.push(Voice {
323 key: event.key,
324 hz: 440.0 * 2_f64.powf((f64::from(event.key) - 69.0) / 12.0),
325 gain: f64::from(event.velocity.get()) / 127.0
326 * (1.0 + LEVEL_JITTER * (2.0 * rng.unit() - 1.0)),
327 phases: patch.harmonics.iter().map(|_| TAU * rng.unit()).collect(),
328 on: frame,
329 off: None,
330 }),
331 EventKind::Off => {
332 if let Some(voice) = voices
333 .iter_mut()
334 .find(|v| v.off.is_none() && v.key == event.key)
335 {
336 voice.off = Some(frame);
337 }
338 }
339 }
340 next += 1;
341 }
342 let t = cast::usize_to_f64(frame) / rate;
343 let sample: f64 = if silent {
344 0.0
345 } else {
346 voices
347 .iter()
348 .map(|voice| {
349 let age = cast::usize_to_f64(frame - voice.on);
350 let rise = if attack > 0.0 {
351 (age / attack).min(1.0)
352 } else {
353 1.0
354 };
355 let fall = voice.off.map_or(1.0, |off| {
356 let since = cast::usize_to_f64(frame - off);
357 10_f64.powf(-RELEASE_DB / 20.0 * since / release.max(1.0))
358 });
359 let tone: f64 = patch
360 .harmonics
361 .iter()
362 .zip(&voice.phases)
363 .enumerate()
364 .map(|(k, (amp, phase))| {
365 amp * (TAU * voice.hz * cast::usize_to_f64(k + 1) * t + phase).sin()
366 })
367 .sum();
368 voice.gain * rise * fall * tone
369 })
370 .sum()
371 };
372 out.push(sample);
373 if let Some(end) = watch.push(&[sample])? {
374 return Ok((out, end));
375 }
376 }
377}
378
379struct SplitMix(u64);
381
382impl SplitMix {
383 fn for_render(seed: u64, index: u64) -> Self {
387 let first = Self(seed).next();
388 Self(first ^ Self(index).next().rotate_left(32))
389 }
390
391 fn next(&mut self) -> u64 {
393 self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
394 let mut z = self.0;
395 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
396 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
397 z ^ (z >> 31)
398 }
399
400 fn unit(&mut self) -> f64 {
402 cast::unit_interval(self.next())
403 }
404}