1use rill_core::{
2 buffer::{ResourceRegistry, TapeWriter},
3 math::vector::scalar::ScalarVector4,
4 math::vector::traits::Vector as VecTrait,
5 math::Transcendental,
6 traits::{Node, NodeCategory, NodeMetadata, NodeState, Processor},
7 NodeId, ParamValue, ParameterId, Port, ProcessError, ProcessResult, RenderContext,
8};
9
10#[allow(unsafe_code)]
13unsafe impl<T: Transcendental, const B: usize> Send for WriteHead<T, B> {}
14#[allow(unsafe_code)]
15unsafe impl<T: Transcendental, const B: usize> Sync for WriteHead<T, B> {}
16
17pub struct WriteHead<T: Transcendental, const BUF_SIZE: usize> {
33 id: NodeId,
34 metadata: NodeMetadata,
35 inputs: Vec<Port<T, BUF_SIZE>>,
36 outputs: Vec<Port<T, BUF_SIZE>>,
37 state: NodeState<T, BUF_SIZE>,
38
39 tape: Option<TapeWriter<T>>,
40 resource_name: String,
41 delay_time: f32,
42 feedback: f32,
43 sample_rate: f32,
44}
45
46impl<T: Transcendental, const BUF_SIZE: usize> WriteHead<T, BUF_SIZE> {
47 pub fn new(sample_rate: f32) -> Self {
52 Self::with_resource(sample_rate, "tape_0")
53 }
54
55 pub fn with_resource(sample_rate: f32, resource_name: &str) -> Self {
57 let mut metadata = NodeMetadata::new("WriteHead", NodeCategory::Processor);
58 metadata.parameters = vec![
59 rill_core::ParamMetadata::new(
60 "delay_time",
61 rill_core::ParamType::Float,
62 ParamValue::Float(0.5),
63 )
64 .with_range(0.01, 2.0, 0.01),
65 rill_core::ParamMetadata::new(
66 "feedback",
67 rill_core::ParamType::Float,
68 ParamValue::Float(0.3),
69 )
70 .with_range(0.0, 0.99, 0.01),
71 ];
72
73 let mut inputs = Vec::new();
74 let mut outputs = Vec::new();
75 inputs.push(Port::input(NodeId(0), 0, "signal_in"));
76 inputs.push(Port::input(NodeId(0), 1, "feedback_in"));
77 outputs.push(Port::output(NodeId(0), 0, "main_out"));
78
79 Self {
80 id: NodeId(0),
81 metadata,
82 inputs,
83 outputs,
84 state: NodeState::new(sample_rate),
85 tape: None,
86 resource_name: resource_name.to_string(),
87 delay_time: 0.5,
88 feedback: 0.3,
89 sample_rate,
90 }
91 }
92}
93
94impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE> for WriteHead<T, BUF_SIZE> {
97 fn process(
98 &mut self,
99 _ctx: &RenderContext,
100 _signal_inputs: &[&[T; BUF_SIZE]],
101 _control_inputs: &[T],
102 _clock_inputs: &[RenderContext],
103 _feedback_inputs: &[&[T; BUF_SIZE]],
104 ) -> ProcessResult<()> {
105 let Some(tape) = self.tape.as_mut() else {
106 debug_assert!(false, "WriteHead: tape not set");
107 return Ok(());
108 };
109
110 let input_buf = self.inputs[0].read();
111 let fb_gain = T::from_f32(self.feedback);
112 let zero_buf = [T::ZERO; BUF_SIZE];
113 let fb_buf = self.inputs[1].feedback().unwrap_or(&zero_buf);
120 let chunks = BUF_SIZE / 4;
121 let fg = ScalarVector4::splat(fb_gain);
122
123 for chunk in 0..chunks {
124 let o = chunk * 4;
125 let i_v = ScalarVector4::load(&input_buf[o..o + 4]);
126 let f_v = ScalarVector4::load(&fb_buf[o..o + 4]);
127 let w = i_v.add(&f_v.mul(&fg));
128
129 for k in 0..4 {
130 tape.write(w.extract(k));
131 }
132 }
133
134 for i in chunks * 4..BUF_SIZE {
136 tape.write(input_buf[i] + fb_buf[i] * fb_gain);
137 }
138
139 self.outputs[0].write_from(input_buf);
140 self.state.advance();
141 Ok(())
142 }
143
144 fn latency(&self) -> usize {
145 0
146 }
147}
148
149impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE> for WriteHead<T, BUF_SIZE> {
152 fn node_type_id(&self) -> rill_core::NodeTypeId
153 where
154 Self: 'static + Sized,
155 {
156 rill_core::NodeTypeId::of::<Self>()
157 }
158 fn id(&self) -> NodeId {
159 self.id
160 }
161 fn set_id(&mut self, id: NodeId) {
162 self.id = id;
163 }
164 fn metadata(&self) -> NodeMetadata {
165 self.metadata.clone()
166 }
167 fn init(&mut self, sample_rate: f32) {
168 self.sample_rate = sample_rate;
169 self.state.sample_rate = sample_rate;
170 }
171 fn reset(&mut self) {
172 self.state.sample_pos = 0;
173 self.state.blocks_processed = 0;
174 }
175
176 fn resolve_resources(&mut self, resources: &mut ResourceRegistry<T>) {
177 if self.tape.is_some() {
178 return;
179 }
180 self.tape = resources.writer(&self.resource_name);
181 }
182
183 fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
184 match id.as_str() {
185 "delay_time" => Some(ParamValue::Float(self.delay_time)),
186 "feedback" => Some(ParamValue::Float(self.feedback)),
187 _ => None,
188 }
189 }
190
191 fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
192 let name = id.as_str();
193 if let Some(v) = value.as_f32() {
194 match name {
195 "delay_time" => {
196 self.delay_time = v.clamp(0.01, 2.0);
197 Ok(())
198 }
199 "feedback" => {
200 self.feedback = v.clamp(0.0, 0.99);
201 Ok(())
202 }
203 _ => Err(ProcessError::parameter(format!(
204 "Unknown parameter: {}",
205 name
206 ))),
207 }
208 } else {
209 Err(ProcessError::parameter("Expected float value"))
210 }
211 }
212
213 fn input_port(&self, i: usize) -> Option<&Port<T, BUF_SIZE>> {
214 self.inputs.get(i)
215 }
216 fn input_port_mut(&mut self, i: usize) -> Option<&mut Port<T, BUF_SIZE>> {
217 self.inputs.get_mut(i)
218 }
219 fn output_port(&self, i: usize) -> Option<&Port<T, BUF_SIZE>> {
220 self.outputs.get(i)
221 }
222 fn output_port_mut(&mut self, i: usize) -> Option<&mut Port<T, BUF_SIZE>> {
223 self.outputs.get_mut(i)
224 }
225 fn control_port(&self, _: usize) -> Option<&Port<T, BUF_SIZE>> {
226 None
227 }
228 fn control_port_mut(&mut self, _: usize) -> Option<&mut Port<T, BUF_SIZE>> {
229 None
230 }
231 fn num_signal_inputs(&self) -> usize {
232 2
233 }
234 fn num_signal_outputs(&self) -> usize {
235 1
236 }
237 fn num_feedback_ports(&self) -> usize {
238 0
239 }
240 fn state(&self) -> &NodeState<T, BUF_SIZE> {
241 &self.state
242 }
243 fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
244 &mut self.state
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn test_write_head_creation() {
254 let wh = WriteHead::<f32, 64>::new(44100.0);
255 assert!((wh.delay_time - 0.5).abs() < 1e-6);
256 assert!((wh.feedback - 0.3).abs() < 1e-6);
257 assert_eq!(wh.inputs.len(), 2);
258 assert_eq!(wh.outputs.len(), 1);
259 }
260
261 #[test]
262 fn test_write_head_params() {
263 let mut wh = WriteHead::<f32, 64>::new(44100.0);
264 wh.set_parameter(
265 &ParameterId::new("feedback").unwrap(),
266 ParamValue::Float(0.5),
267 )
268 .unwrap();
269 assert!((wh.feedback - 0.5).abs() < 1e-6);
270 }
271}