Skip to main content

rill_router/mixer/
node.rs

1//! Mixer node implementation
2
3use super::channel::{ChannelConfig, ChannelState};
4use super::send::{SendConfig, SendType};
5use rill_core::traits::{
6    Node, NodeCategory, NodeId, NodeMetadata, NodeState, NodeTypeId, ParamMetadata, ParamRange,
7    ParamType, ParamValue, ParameterId, Port,
8};
9use rill_core::RenderContext;
10use rill_core::{ProcessError, ProcessResult};
11use std::collections::HashMap;
12
13/// Mixer node with multiple channels and aux sends
14pub struct MixerNode<const BUF_SIZE: usize> {
15    /// Master volume (0.0 - 2.0)
16    pub master_volume: f32,
17    /// Smoothing factor (0.0 - 1.0)
18    pub smoothing: f32,
19    /// Channels
20    pub channels: Vec<ChannelState>,
21    /// Channel names for parameter lookup
22    pub channel_names: HashMap<String, usize>,
23    /// Aux buses (each bus accumulates signals from sends)
24    pub buses: Vec<Vec<f32>>,
25    /// Send configurations per channel
26    pub sends: Vec<Vec<SendConfig>>,
27    /// Current master volume with smoothing
28    pub current_master_volume: f32,
29    /// Buffer size for buses (updated each block)
30    pub buffer_size: usize,
31    /// Sample rate
32    pub sample_rate: f32,
33    /// Control input values (updated from graph)
34    pub control_values: Vec<f32>,
35    /// Parameter IDs for automation
36    pub param_ids: HashMap<String, ParameterId>,
37    /// Optional hook called after a parameter changes
38    pub after_param_change_closure: fn(&mut Self, &str, f32),
39    /// Node ID
40    pub id: NodeId,
41    /// Audio input ports
42    pub input_ports: Vec<Port<f32, BUF_SIZE>>,
43    /// Audio output ports
44    pub output_ports: Vec<Port<f32, BUF_SIZE>>,
45    /// Control ports
46    pub control_ports: Vec<Port<f32, BUF_SIZE>>,
47    /// Node state
48    pub state: NodeState<f32, BUF_SIZE>,
49}
50
51impl<const BUF_SIZE: usize> MixerNode<BUF_SIZE> {
52    /// Create a new mixer with specified number of channels and buses
53    pub fn new(num_channels: usize, num_buses: usize) -> Self {
54        let mut channels = Vec::with_capacity(num_channels);
55        let mut channel_names = HashMap::new();
56        let mut sends = Vec::with_capacity(num_channels);
57
58        for i in 0..num_channels {
59            let config = ChannelConfig {
60                name: format!("Channel {}", i + 1),
61                ..Default::default()
62            };
63            channel_names.insert(config.name.clone(), i);
64            channels.push(ChannelState::new(config));
65            sends.push(Vec::new()); // no sends initially
66        }
67
68        let mut input_ports = Vec::with_capacity(num_channels);
69        for i in 0..num_channels {
70            input_ports.push(Port::input(
71                NodeId::new(0),
72                i as u16,
73                &format!("ch{}_in", i + 1),
74            ));
75        }
76
77        let mut output_ports = Vec::with_capacity(2 + num_buses);
78        output_ports.push(Port::output(NodeId::new(0), 0, "master_left"));
79        output_ports.push(Port::output(NodeId::new(0), 1, "master_right"));
80        for bus_idx in 0..num_buses {
81            output_ports.push(Port::output(
82                NodeId::new(0),
83                (2 + bus_idx) as u16,
84                &format!("bus{}_out", bus_idx + 1),
85            ));
86        }
87
88        Self {
89            master_volume: 1.0,
90            smoothing: 0.1,
91            channels,
92            channel_names,
93            buses: vec![Vec::new(); num_buses],
94            sends,
95            current_master_volume: 1.0,
96            buffer_size: 0,
97            sample_rate: 44100.0,
98            control_values: Vec::new(),
99            param_ids: HashMap::new(),
100            after_param_change_closure: |_, _, _| {},
101            id: NodeId::new(0),
102            input_ports,
103            output_ports,
104            control_ports: Vec::new(),
105            state: NodeState::new(44100.0),
106        }
107    }
108
109    /// Number of audio inputs (channels)
110    pub fn num_inputs(&self) -> usize {
111        self.num_signal_inputs()
112    }
113
114    /// Number of audio outputs (master L/R + buses)
115    pub fn num_outputs(&self) -> usize {
116        self.num_signal_outputs()
117    }
118
119    /// Get parameter value by name (convenience wrapper)
120    pub fn get_param(&self, name: &str) -> Option<ParamValue> {
121        let id = ParameterId::new(name).ok()?;
122        self.get_parameter(&id)
123    }
124
125    /// Set parameter value by name (convenience wrapper)
126    pub fn set_param(&mut self, name: &str, value: ParamValue) -> ProcessResult<()> {
127        let id = ParameterId::new(name)
128            .map_err(|e| rill_core::ProcessError::Parameter(e.to_string()))?;
129        self.set_parameter(&id, value)
130    }
131
132    /// Add a channel
133    pub fn add_channel(&mut self, config: ChannelConfig) -> usize {
134        let index = self.channels.len();
135        self.channel_names.insert(config.name.clone(), index);
136        self.channels.push(ChannelState::new(config));
137        self.sends.push(Vec::new());
138        self.input_ports.push(Port::input(
139            NodeId::new(0),
140            index as u16,
141            &format!("ch{}_in", index + 1),
142        ));
143        index
144    }
145
146    /// Remove a channel by index
147    pub fn remove_channel(&mut self, index: usize) -> Result<(), ProcessError> {
148        if index >= self.channels.len() {
149            return Err(ProcessError::Parameter("Channel index out of range".into()));
150        }
151        let name = self.channels[index].config().name.clone();
152        self.channel_names.remove(&name);
153        self.channels.remove(index);
154        self.sends.remove(index);
155        self.input_ports.remove(index);
156        Ok(())
157    }
158
159    /// Add a send from a channel to a bus
160    pub fn add_send(&mut self, channel_index: usize, send: SendConfig) -> Result<(), ProcessError> {
161        if channel_index >= self.sends.len() {
162            return Err(ProcessError::Parameter("Channel index out of range".into()));
163        }
164        if send.bus_index >= self.buses.len() {
165            return Err(ProcessError::Parameter("Bus index out of range".into()));
166        }
167        self.sends[channel_index].push(send);
168        Ok(())
169    }
170
171    /// Clear sends for a channel
172    pub fn clear_sends(&mut self, channel_index: usize) -> Result<(), ProcessError> {
173        if channel_index >= self.sends.len() {
174            return Err(ProcessError::Parameter("Channel index out of range".into()));
175        }
176        self.sends[channel_index].clear();
177        Ok(())
178    }
179
180    /// Set channel volume
181    pub fn set_channel_volume(
182        &mut self,
183        channel_index: usize,
184        volume: f32,
185    ) -> Result<(), ProcessError> {
186        if channel_index >= self.channels.len() {
187            return Err(ProcessError::Parameter("Channel index out of range".into()));
188        }
189        let mut config = self.channels[channel_index].config().clone();
190        config.volume = volume.clamp(0.0, 1.0);
191        self.channels[channel_index].set_config(config);
192        Ok(())
193    }
194
195    /// Set channel pan
196    pub fn set_channel_pan(&mut self, channel_index: usize, pan: f32) -> Result<(), ProcessError> {
197        if channel_index >= self.channels.len() {
198            return Err(ProcessError::Parameter("Channel index out of range".into()));
199        }
200        let mut config = self.channels[channel_index].config().clone();
201        config.pan = pan.clamp(-1.0, 1.0);
202        self.channels[channel_index].set_config(config);
203        Ok(())
204    }
205
206    /// Set channel mute
207    pub fn set_channel_mute(
208        &mut self,
209        channel_index: usize,
210        mute: bool,
211    ) -> Result<(), ProcessError> {
212        if channel_index >= self.channels.len() {
213            return Err(ProcessError::Parameter("Channel index out of range".into()));
214        }
215        let mut config = self.channels[channel_index].config().clone();
216        config.muted = mute;
217        self.channels[channel_index].set_config(config);
218        Ok(())
219    }
220
221    /// Set master volume
222    pub fn set_master_volume(&mut self, volume: f32) {
223        self.master_volume = volume.clamp(0.0, 2.0);
224    }
225
226    /// Set smoothing factor
227    pub fn set_smoothing(&mut self, factor: f32) {
228        self.smoothing = factor.clamp(0.0, 1.0);
229        for channel in &mut self.channels {
230            channel.set_smoothing(factor);
231        }
232    }
233}
234
235impl<const BUF_SIZE: usize> rill_core::traits::Node<f32, BUF_SIZE> for MixerNode<BUF_SIZE> {
236    fn metadata(&self) -> NodeMetadata {
237        let mut params = vec![ParamMetadata {
238            name: "master_volume".to_string(),
239            description: String::new(),
240            typ: ParamType::Float,
241            default: ParamValue::Float(1.0),
242            range: ParamRange {
243                min: Some(0.0),
244                max: Some(2.0),
245                step: Some(0.01),
246            },
247            unit: Some("gain".to_string()),
248            choices: None,
249        }];
250
251        // Add per-channel parameters
252        for i in 0..self.channels.len() {
253            let ch_num = i + 1;
254            params.push(ParamMetadata {
255                name: format!("ch_{}_volume", ch_num),
256                description: String::new(),
257                typ: ParamType::Float,
258                default: ParamValue::Float(1.0),
259                range: ParamRange {
260                    min: Some(0.0),
261                    max: Some(1.0),
262                    step: Some(0.01),
263                },
264                unit: Some("gain".to_string()),
265                choices: None,
266            });
267            params.push(ParamMetadata {
268                name: format!("ch_{}_pan", ch_num),
269                description: String::new(),
270                typ: ParamType::Float,
271                default: ParamValue::Float(0.0),
272                range: ParamRange {
273                    min: Some(-1.0),
274                    max: Some(1.0),
275                    step: Some(0.01),
276                },
277                unit: Some("pan".to_string()),
278                choices: None,
279            });
280            params.push(ParamMetadata {
281                name: format!("ch_{}_mute", ch_num),
282                description: String::new(),
283                typ: ParamType::Bool,
284                default: ParamValue::Bool(false),
285                range: ParamRange {
286                    min: None,
287                    max: None,
288                    step: None,
289                },
290                unit: None,
291                choices: None,
292            });
293        }
294
295        NodeMetadata {
296            name: "Mixer".to_string(),
297            type_name: Some("rill/mixer".to_string()),
298            category: NodeCategory::Utility,
299            description: format!(
300                "Mixer with {} channels and {} buses",
301                self.channels.len(),
302                self.buses.len()
303            ),
304            author: "Rill Mixer".to_string(),
305            version: "0.2.0".to_string(),
306            signal_inputs: self.channels.len(),
307            signal_outputs: 2 + self.buses.len(),
308            control_inputs: 0,
309            control_outputs: 0,
310            clock_inputs: 0,
311            clock_outputs: 0,
312            feedback_ports: 0,
313            parameters: params,
314        }
315    }
316
317    fn node_type_id(&self) -> NodeTypeId
318    where
319        Self: 'static + Sized,
320    {
321        NodeTypeId::of::<Self>()
322    }
323
324    fn init(&mut self, sample_rate: f32) {
325        self.sample_rate = sample_rate;
326        self.state.sample_rate = sample_rate;
327    }
328
329    fn reset(&mut self) {
330        self.current_master_volume = self.master_volume;
331        self.state.reset();
332        for channel in &mut self.channels {
333            channel.set_smoothing(self.smoothing);
334        }
335    }
336
337    fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
338        let name = id.as_str();
339        if name == "master_volume" {
340            return Some(ParamValue::Float(self.master_volume));
341        }
342        if name.starts_with("ch_") {
343            let parts: Vec<&str> = name.split('_').collect();
344            if parts.len() >= 3 {
345                if let Ok(idx) = parts[1].parse::<usize>() {
346                    if idx > 0 && idx <= self.channels.len() {
347                        let channel = &self.channels[idx - 1];
348                        match parts[2] {
349                            "volume" => return Some(ParamValue::Float(channel.config().volume)),
350                            "pan" => return Some(ParamValue::Float(channel.config().pan)),
351                            "mute" => return Some(ParamValue::Bool(channel.config().muted)),
352                            _ => {}
353                        }
354                    }
355                }
356            }
357        }
358        if name == "smoothing" {
359            return Some(ParamValue::Float(self.smoothing));
360        }
361        None
362    }
363
364    fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
365        let name = id.as_str();
366        if name == "master_volume" {
367            if let ParamValue::Float(v) = value {
368                self.set_master_volume(v);
369                return Ok(());
370            }
371        }
372        if name == "smoothing" {
373            if let ParamValue::Float(v) = value {
374                self.set_smoothing(v);
375                return Ok(());
376            }
377        }
378        if name.starts_with("ch_") {
379            let parts: Vec<&str> = name.split('_').collect();
380            if parts.len() >= 3 {
381                if let Ok(idx) = parts[1].parse::<usize>() {
382                    if idx > 0 && idx <= self.channels.len() {
383                        match parts[2] {
384                            "volume" => {
385                                if let ParamValue::Float(v) = value {
386                                    return self.set_channel_volume(idx - 1, v).map_err(|e| {
387                                        rill_core::ProcessError::Parameter(e.to_string())
388                                    });
389                                }
390                            }
391                            "pan" => {
392                                if let ParamValue::Float(v) = value {
393                                    return self.set_channel_pan(idx - 1, v).map_err(|e| {
394                                        rill_core::ProcessError::Parameter(e.to_string())
395                                    });
396                                }
397                            }
398                            "mute" => {
399                                if let ParamValue::Bool(v) = value {
400                                    return self.set_channel_mute(idx - 1, v).map_err(|e| {
401                                        rill_core::ProcessError::Parameter(e.to_string())
402                                    });
403                                }
404                            }
405                            _ => {}
406                        }
407                    }
408                }
409            }
410        }
411        Err(rill_core::ProcessError::Parameter(format!(
412            "Unknown parameter: {}",
413            name
414        )))
415    }
416
417    fn id(&self) -> NodeId {
418        self.id
419    }
420
421    fn set_id(&mut self, id: NodeId) {
422        self.id = id;
423    }
424
425    fn input_port(&self, index: usize) -> Option<&Port<f32, BUF_SIZE>> {
426        self.input_ports.get(index)
427    }
428
429    fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
430        self.input_ports.get_mut(index)
431    }
432
433    fn output_port(&self, index: usize) -> Option<&Port<f32, BUF_SIZE>> {
434        self.output_ports.get(index)
435    }
436
437    fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
438        self.output_ports.get_mut(index)
439    }
440
441    fn control_port(&self, index: usize) -> Option<&Port<f32, BUF_SIZE>> {
442        self.control_ports.get(index)
443    }
444
445    fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
446        self.control_ports.get_mut(index)
447    }
448
449    fn state(&self) -> &NodeState<f32, BUF_SIZE> {
450        &self.state
451    }
452
453    fn state_mut(&mut self) -> &mut NodeState<f32, BUF_SIZE> {
454        &mut self.state
455    }
456
457    fn num_signal_inputs(&self) -> usize {
458        self.channels.len()
459    }
460
461    fn num_signal_outputs(&self) -> usize {
462        2 + self.buses.len()
463    }
464
465    fn num_control_inputs(&self) -> usize {
466        0
467    }
468
469    fn num_control_outputs(&self) -> usize {
470        0
471    }
472
473    fn num_clock_inputs(&self) -> usize {
474        0
475    }
476
477    fn num_clock_outputs(&self) -> usize {
478        0
479    }
480
481    fn num_feedback_ports(&self) -> usize {
482        0
483    }
484}
485
486// ── Router trait — N→M configurable routing ────────────────
487impl<const BUF_SIZE: usize> rill_core::traits::Router<f32, BUF_SIZE> for MixerNode<BUF_SIZE> {
488    fn route(&mut self, ctx: &RenderContext, _inputs: &[&[f32; BUF_SIZE]]) -> ProcessResult<()> {
489        let _num_buses = self.buses.len();
490        let buffer_size = BUF_SIZE;
491
492        // Update state with ctx
493        self.state.sample_pos = ctx.sample_pos;
494        self.state.blocks_processed = ctx.sample_pos / buffer_size as u64;
495
496        // Ensure bus buffers are sized correctly and zeroed
497        for bus in &mut self.buses {
498            if bus.len() != buffer_size {
499                bus.resize(buffer_size, 0.0);
500            } else {
501                bus.fill(0.0);
502            }
503        }
504
505        // Prepare temporary output accumulators for master (stack-allocated)
506        let mut master_left = [0.0f32; BUF_SIZE];
507        let mut master_right = [0.0f32; BUF_SIZE];
508
509        // Process each channel
510        for (ch_idx, channel) in self.channels.iter_mut().enumerate() {
511            if ch_idx >= self.input_ports.len() {
512                continue;
513            }
514            let input_buf = self.input_ports[ch_idx].read();
515
516            let channel_volume = channel.config().volume;
517
518            // Process per sample
519            for (i, ((&sample, left), right)) in input_buf
520                .iter()
521                .zip(master_left.iter_mut())
522                .zip(master_right.iter_mut())
523                .enumerate()
524            {
525                let (left_out, right_out) = channel.process_mono(sample);
526
527                *left += left_out;
528                *right += right_out;
529
530                for send in &self.sends[ch_idx] {
531                    if send.bus_index < self.buses.len() {
532                        let bus = &mut self.buses[send.bus_index];
533
534                        let send_signal = match send.send_type {
535                            SendType::PreFader => sample,
536                            SendType::PostFader => sample * channel_volume,
537                        };
538
539                        bus[i] += send_signal * send.level;
540                    }
541                }
542            }
543        }
544
545        // Apply master volume with smoothing
546        self.current_master_volume +=
547            (self.master_volume - self.current_master_volume) * self.smoothing;
548        let master_gain = self.current_master_volume;
549
550        // Output master
551        if self.output_ports.len() >= 2 {
552            let (first, rest) = self.output_ports.split_at_mut(1);
553            let out_l = first[0].write();
554            let out_r = rest[0].write();
555            for ((master_l, master_r), (out_l, out_r)) in master_left
556                .iter()
557                .zip(master_right.iter())
558                .zip(out_l.iter_mut().zip(out_r.iter_mut()))
559            {
560                *out_l = master_l * master_gain;
561                *out_r = master_r * master_gain;
562            }
563        }
564
565        // Output buses (starting from output index 2)
566        for (bus_idx, bus) in self.buses.iter().enumerate() {
567            let out_idx = 2 + bus_idx;
568            if out_idx < self.output_ports.len() {
569                let out_buf = self.output_ports[out_idx].write();
570                out_buf.copy_from_slice(&bus[..buffer_size]);
571            }
572        }
573
574        Ok(())
575    }
576
577    fn num_route_inputs(&self) -> usize {
578        self.channels.len()
579    }
580
581    fn num_route_outputs(&self) -> usize {
582        2 + self.buses.len()
583    }
584
585    fn set_connection(&mut self, from: usize, to: usize, gain: f32) -> ProcessResult<()> {
586        // For the mixer, "connection" means routing channel `from` to output `to`.
587        // Channel volume controls the gain to master L/R.
588        // Bus sends are managed via add_send().
589        if from >= self.channels.len() {
590            return Err(ProcessError::Parameter("Channel index out of range".into()));
591        }
592        if to == 0 || to == 1 {
593            // Master L/R: set channel volume (pan is unchanged)
594            self.set_channel_volume(from, gain.clamp(0.0, 1.0))
595        } else if to >= 2 && to < 2 + self.buses.len() {
596            // Aux bus: add/update a send
597            let bus_idx = to - 2;
598            // Check if a send to this bus already exists
599            if let Some(existing) = self.sends[from].iter_mut().find(|s| s.bus_index == bus_idx) {
600                existing.level = gain.clamp(0.0, 1.0);
601                Ok(())
602            } else {
603                self.add_send(
604                    from,
605                    SendConfig {
606                        bus_index: bus_idx,
607                        level: gain.clamp(0.0, 1.0),
608                        send_type: SendType::PostFader,
609                    },
610                )
611            }
612        } else {
613            Err(ProcessError::Parameter("Output index out of range".into()))
614        }
615    }
616
617    fn remove_connection(&mut self, from: usize, to: usize) -> ProcessResult<()> {
618        if from >= self.channels.len() {
619            return Err(ProcessError::Parameter("Channel index out of range".into()));
620        }
621        if to == 0 || to == 1 {
622            // Master L/R: mute the channel
623            self.set_channel_mute(from, true)
624        } else if to >= 2 && to < 2 + self.buses.len() {
625            // Remove the send to this bus
626            let bus_idx = to - 2;
627            self.sends[from].retain(|s| s.bus_index != bus_idx);
628            Ok(())
629        } else {
630            Err(ProcessError::Parameter("Output index out of range".into()))
631        }
632    }
633
634    fn routing_matrix(&self) -> Vec<Vec<(usize, f32)>> {
635        let n_out = self.num_route_outputs();
636        let mut matrix = vec![Vec::new(); n_out];
637
638        // Master L (0): sum of all channels with their volumes
639        // Master R (1): same
640        for (ch_idx, ch) in self.channels.iter().enumerate() {
641            if !ch.config().muted {
642                matrix[0].push((ch_idx, ch.config().volume));
643                matrix[1].push((ch_idx, ch.config().volume));
644            }
645        }
646
647        // Buses: send connections
648        for (ch_idx, ch_sends) in self.sends.iter().enumerate() {
649            for send in ch_sends {
650                let out_idx = 2 + send.bus_index;
651                if out_idx < n_out {
652                    matrix[out_idx].push((ch_idx, send.level));
653                }
654            }
655        }
656
657        matrix
658    }
659}