hexodsp/shared_feedback.rs
1// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
2// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
3// See README.md and COPYING for details.
4
5//! Provides an implementation for a shared feedback buffer for the DSP node graph.
6//! It is used for instance by the `FbWr` and `FbRd` nodes to implement their functionality.
7//!
8//! See also [crate::NodeGlobalData] which provides the [SharedFeedback] to the nodes.
9
10use crate::dsp::MAX_BLOCK_SIZE;
11use std::sync::Arc;
12use synfx_dsp::AtomicFloat;
13
14pub const FB_DELAY_LENGTH_MS: f32 = 3.14;
15
16/// The SharedFeedback is a feedback delay buffer for the `FbWr` and `FbRd` nodes.
17///
18/// They have a fixed delay of 3.14ms, which should be equal for all sample rates above 42kHz.
19/// Below that the delay might be longer to accomodate the [crate::dsp::MAX_BLOCK_SIZE].
20///
21/// See also [crate::NodeGlobalData] which provides the [SharedFeedback] to the DSP nodes.
22#[derive(Debug, Clone)]
23pub struct SharedFeedback {
24 buffer: Arc<Vec<AtomicFloat>>,
25 delay_sample_count: usize,
26}
27
28impl SharedFeedback {
29 pub fn new(sample_rate: f32) -> Self {
30 let mut buf = vec![];
31 let delay_sample_count = ((sample_rate * FB_DELAY_LENGTH_MS) / 1000.0) as usize;
32
33 // Ensure we got at least MAX_BLOCK_SIZE though!
34 let delay_sample_count = delay_sample_count.max(MAX_BLOCK_SIZE);
35
36 // Multiply by 3, to make ample space for the FB_DELAY_LENGTH_MS,
37 // probably 2*delay_sample_count would be fine too,
38 // but I'm anxious about off by one bugs :-)
39 buf.resize_with(3 * delay_sample_count, || AtomicFloat::new(0.0));
40
41 Self { buffer: Arc::new(buf), delay_sample_count }
42 }
43}
44
45/// This instance writes into the [SharedFeedback] buffer.
46///
47/// Even though it's safe to have multiple writers of this will not work
48/// or produce any meaningful results. The goal is really, that one `FbWr` DSP node
49/// in the audio thread writes the buffer, and one (or multiple) `FbRd` DSP nodes
50/// read from that [SharedFeedback] buffer via a [SharedFeedbackReader].
51#[derive(Debug, Clone)]
52pub struct SharedFeedbackWriter {
53 buffer: Arc<Vec<AtomicFloat>>,
54 write_ptr: usize,
55 delay_sample_count: usize,
56}
57
58impl SharedFeedbackWriter {
59 pub fn new(sfb: &SharedFeedback) -> Self {
60 let buffer = sfb.buffer.clone();
61 Self {
62 buffer,
63 delay_sample_count: sfb.delay_sample_count,
64 write_ptr: sfb.delay_sample_count,
65 }
66 }
67
68 /// Write the next sample in to the feedback buffer.
69 ///
70 /// Even though it's safe to have multiple writers of this will not work
71 /// or produce any meaningful results. The goal is really, that one `FbWr` DSP node
72 /// on the audio thread writing the buffer per buffer iteration. And then one or more
73 /// `FbRd` DSP node reading from that buffer.
74 pub fn write(&mut self, s: f32) {
75 self.buffer[self.write_ptr].set(s);
76 self.write_ptr = (self.write_ptr + 1) % self.delay_sample_count;
77 }
78}
79
80/// A reader for the [SharedFeedback] buffer, used to implement the `FbRd` DSP node.
81///
82/// Multiple readers are okay, and you can even read from the buffer across the threads.
83/// It is sound to read from another thread. But keep in mind, that this is not a ring buffer
84/// and you will get partially written buffer contents. There is also only a per sample reading
85/// API, that means without the current sample rate you will not know how many samples the 3.14ms
86/// buffer is big.
87#[derive(Debug, Clone)]
88pub struct SharedFeedbackReader {
89 buffer: Arc<Vec<AtomicFloat>>,
90 read_ptr: usize,
91 delay_sample_count: usize,
92}
93
94impl SharedFeedbackReader {
95 pub fn new(sfb: &SharedFeedback) -> Self {
96 Self { buffer: sfb.buffer.clone(), delay_sample_count: sfb.delay_sample_count, read_ptr: 0 }
97 }
98
99 /// Read the next sample from the buffer. Wraps around after some internal buffer
100 /// size (that is consistent with the [SharedFeedback] buffer size). Used by `FbRd` DSP node
101 /// to do it's functionality.
102 pub fn read(&mut self) -> f32 {
103 let ret = self.buffer[self.read_ptr].get();
104 self.read_ptr = (self.read_ptr + 1) % self.delay_sample_count;
105 ret
106 }
107}