1use std::sync::{Arc, Mutex};
7
8use rill_core::{
9 math::Transcendental,
10 time::{ClockTick, RenderContext},
11 traits::{
12 Node, NodeCategory, NodeMetadata, NodeState, ParamValue, ParameterId, Port, PortId,
13 ProcessError, ProcessResult, Sink,
14 },
15 NodeId,
16};
17
18pub struct RecordingSink<T: Transcendental, const B: usize> {
32 id: NodeId,
33 metadata: NodeMetadata,
34 inputs: Vec<Port<T, B>>,
35 state: NodeState<T, B>,
36 recorded: Arc<Mutex<Vec<f32>>>,
37}
38
39impl<T: Transcendental, const B: usize> RecordingSink<T, B> {
40 pub fn new(recorded: Arc<Mutex<Vec<f32>>>, channels: usize) -> Self {
42 let ch = channels.clamp(1, 2);
43 let inputs: Vec<_> = if ch == 1 {
44 vec![Port::input(NodeId(0), 0, "mono")]
45 } else {
46 vec![
47 Port::input(NodeId(0), 0, "left"),
48 Port::input(NodeId(0), 1, "right"),
49 ]
50 };
51 Self {
52 id: NodeId(0),
53 metadata: NodeMetadata::new("RecordingSink", NodeCategory::Sink),
54 inputs,
55 state: NodeState::new(44100.0),
56 recorded,
57 }
58 }
59
60 #[cfg(feature = "wav")]
62 pub fn write_wav(
63 path: &str,
64 sample_rate: u32,
65 channels: u16,
66 samples: &[f32],
67 ) -> Result<(), String> {
68 let spec = hound::WavSpec {
69 channels,
70 sample_rate,
71 bits_per_sample: 16,
72 sample_format: hound::SampleFormat::Int,
73 };
74 let mut writer = hound::WavWriter::create(path, spec).map_err(|e| e.to_string())?;
75 for &s in samples {
76 let v = (s.clamp(-1.0, 1.0) * 32767.0) as i16;
77 writer.write_sample(v).map_err(|e| e.to_string())?;
78 }
79 writer.finalize().map_err(|e| e.to_string())
80 }
81}
82
83impl<T: Transcendental, const B: usize> Node<T, B> for RecordingSink<T, B> {
84 fn node_type_id(&self) -> rill_core::NodeTypeId
85 where
86 Self: 'static + Sized,
87 {
88 rill_core::NodeTypeId::of::<Self>()
89 }
90 fn id(&self) -> NodeId {
91 self.id
92 }
93 fn set_id(&mut self, id: NodeId) {
94 self.id = id;
95 for (i, p) in self.inputs.iter_mut().enumerate() {
96 p.id = PortId::signal_in(id, i as u16);
97 }
98 }
99 fn metadata(&self) -> NodeMetadata {
100 self.metadata.clone()
101 }
102 fn init(&mut self, sample_rate: f32) {
103 self.state = NodeState::new(sample_rate);
104 }
105 fn reset(&mut self) {
106 self.state.sample_pos = 0;
107 }
108 fn get_parameter(&self, _id: &ParameterId) -> Option<ParamValue> {
109 None
110 }
111 fn set_parameter(&mut self, _id: &ParameterId, _value: ParamValue) -> ProcessResult<()> {
112 Err(ProcessError::parameter("RecordingSink has no parameters"))
113 }
114 fn input_port(&self, index: usize) -> Option<&Port<T, B>> {
115 self.inputs.get(index)
116 }
117 fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, B>> {
118 self.inputs.get_mut(index)
119 }
120 fn output_port(&self, _index: usize) -> Option<&Port<T, B>> {
121 None
122 }
123 fn output_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, B>> {
124 None
125 }
126 fn control_port(&self, _index: usize) -> Option<&Port<T, B>> {
127 None
128 }
129 fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, B>> {
130 None
131 }
132 fn num_signal_inputs(&self) -> usize {
133 self.inputs.len()
134 }
135 fn num_signal_outputs(&self) -> usize {
136 0
137 }
138 fn state(&self) -> &NodeState<T, B> {
139 &self.state
140 }
141 fn state_mut(&mut self) -> &mut NodeState<T, B> {
142 &mut self.state
143 }
144}
145
146impl<T: Transcendental, const B: usize> Sink<T, B> for RecordingSink<T, B> {
147 fn consume(
148 &mut self,
149 _ctx: &RenderContext,
150 _signal_inputs: &[&[T; B]],
151 _control_inputs: &[T],
152 _clock_inputs: &[RenderContext],
153 _feedback_inputs: &[&[T; B]],
154 _tick: &ClockTick,
155 ) -> ProcessResult<()> {
156 if self.inputs.is_empty() {
157 return Ok(());
158 }
159 let nch = self.inputs.len();
160 let ch0 = self.inputs[0].read();
161 let ch1 = if nch > 1 {
162 Some(self.inputs[1].read())
163 } else {
164 None
165 };
166 let mut dst = self.recorded.lock().unwrap();
167 for i in 0..B {
168 dst.push(ch0[i].to_f32());
169 if let Some(c1) = ch1 {
170 dst.push(c1[i].to_f32());
171 }
172 }
173 self.state.advance();
174 Ok(())
175 }
176}