use super::channel::{ChannelConfig, ChannelState};
use super::send::{SendConfig, SendType};
use rill_core::traits::{
Node, NodeCategory, NodeId, NodeMetadata, NodeState, NodeTypeId, ParamMetadata, ParamRange,
ParamType, ParamValue, ParameterId, Port,
};
use rill_core::RenderContext;
use rill_core::{ProcessError, ProcessResult};
use std::collections::HashMap;
pub struct MixerNode<const BUF_SIZE: usize> {
pub master_volume: f32,
pub smoothing: f32,
pub channels: Vec<ChannelState>,
pub channel_names: HashMap<String, usize>,
pub buses: Vec<Vec<f32>>,
pub sends: Vec<Vec<SendConfig>>,
pub current_master_volume: f32,
pub buffer_size: usize,
pub sample_rate: f32,
pub control_values: Vec<f32>,
pub param_ids: HashMap<String, ParameterId>,
pub after_param_change_closure: fn(&mut Self, &str, f32),
pub id: NodeId,
pub input_ports: Vec<Port<f32, BUF_SIZE>>,
pub output_ports: Vec<Port<f32, BUF_SIZE>>,
pub control_ports: Vec<Port<f32, BUF_SIZE>>,
pub state: NodeState<f32, BUF_SIZE>,
}
impl<const BUF_SIZE: usize> MixerNode<BUF_SIZE> {
pub fn new(num_channels: usize, num_buses: usize) -> Self {
let mut channels = Vec::with_capacity(num_channels);
let mut channel_names = HashMap::new();
let mut sends = Vec::with_capacity(num_channels);
for i in 0..num_channels {
let config = ChannelConfig {
name: format!("Channel {}", i + 1),
..Default::default()
};
channel_names.insert(config.name.clone(), i);
channels.push(ChannelState::new(config));
sends.push(Vec::new()); }
let mut input_ports = Vec::with_capacity(num_channels);
for i in 0..num_channels {
input_ports.push(Port::input(
NodeId::new(0),
i as u16,
&format!("ch{}_in", i + 1),
));
}
let mut output_ports = Vec::with_capacity(2 + num_buses);
output_ports.push(Port::output(NodeId::new(0), 0, "master_left"));
output_ports.push(Port::output(NodeId::new(0), 1, "master_right"));
for bus_idx in 0..num_buses {
output_ports.push(Port::output(
NodeId::new(0),
(2 + bus_idx) as u16,
&format!("bus{}_out", bus_idx + 1),
));
}
Self {
master_volume: 1.0,
smoothing: 0.1,
channels,
channel_names,
buses: vec![Vec::new(); num_buses],
sends,
current_master_volume: 1.0,
buffer_size: 0,
sample_rate: 44100.0,
control_values: Vec::new(),
param_ids: HashMap::new(),
after_param_change_closure: |_, _, _| {},
id: NodeId::new(0),
input_ports,
output_ports,
control_ports: Vec::new(),
state: NodeState::new(44100.0),
}
}
pub fn num_inputs(&self) -> usize {
self.num_signal_inputs()
}
pub fn num_outputs(&self) -> usize {
self.num_signal_outputs()
}
pub fn get_param(&self, name: &str) -> Option<ParamValue> {
let id = ParameterId::new(name).ok()?;
self.get_parameter(&id)
}
pub fn set_param(&mut self, name: &str, value: ParamValue) -> ProcessResult<()> {
let id = ParameterId::new(name)
.map_err(|e| rill_core::ProcessError::Parameter(e.to_string()))?;
self.set_parameter(&id, value)
}
pub fn add_channel(&mut self, config: ChannelConfig) -> usize {
let index = self.channels.len();
self.channel_names.insert(config.name.clone(), index);
self.channels.push(ChannelState::new(config));
self.sends.push(Vec::new());
self.input_ports.push(Port::input(
NodeId::new(0),
index as u16,
&format!("ch{}_in", index + 1),
));
index
}
pub fn remove_channel(&mut self, index: usize) -> Result<(), ProcessError> {
if index >= self.channels.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
let name = self.channels[index].config().name.clone();
self.channel_names.remove(&name);
self.channels.remove(index);
self.sends.remove(index);
self.input_ports.remove(index);
Ok(())
}
pub fn add_send(&mut self, channel_index: usize, send: SendConfig) -> Result<(), ProcessError> {
if channel_index >= self.sends.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
if send.bus_index >= self.buses.len() {
return Err(ProcessError::Parameter("Bus index out of range".into()));
}
self.sends[channel_index].push(send);
Ok(())
}
pub fn clear_sends(&mut self, channel_index: usize) -> Result<(), ProcessError> {
if channel_index >= self.sends.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
self.sends[channel_index].clear();
Ok(())
}
pub fn set_channel_volume(
&mut self,
channel_index: usize,
volume: f32,
) -> Result<(), ProcessError> {
if channel_index >= self.channels.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
let mut config = self.channels[channel_index].config().clone();
config.volume = volume.clamp(0.0, 1.0);
self.channels[channel_index].set_config(config);
Ok(())
}
pub fn set_channel_pan(&mut self, channel_index: usize, pan: f32) -> Result<(), ProcessError> {
if channel_index >= self.channels.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
let mut config = self.channels[channel_index].config().clone();
config.pan = pan.clamp(-1.0, 1.0);
self.channels[channel_index].set_config(config);
Ok(())
}
pub fn set_channel_mute(
&mut self,
channel_index: usize,
mute: bool,
) -> Result<(), ProcessError> {
if channel_index >= self.channels.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
let mut config = self.channels[channel_index].config().clone();
config.muted = mute;
self.channels[channel_index].set_config(config);
Ok(())
}
pub fn set_master_volume(&mut self, volume: f32) {
self.master_volume = volume.clamp(0.0, 2.0);
}
pub fn set_smoothing(&mut self, factor: f32) {
self.smoothing = factor.clamp(0.0, 1.0);
for channel in &mut self.channels {
channel.set_smoothing(factor);
}
}
}
impl<const BUF_SIZE: usize> rill_core::traits::Node<f32, BUF_SIZE> for MixerNode<BUF_SIZE> {
fn metadata(&self) -> NodeMetadata {
let mut params = vec![ParamMetadata {
name: "master_volume".to_string(),
description: String::new(),
typ: ParamType::Float,
default: ParamValue::Float(1.0),
range: ParamRange {
min: Some(0.0),
max: Some(2.0),
step: Some(0.01),
},
unit: Some("gain".to_string()),
choices: None,
}];
for i in 0..self.channels.len() {
let ch_num = i + 1;
params.push(ParamMetadata {
name: format!("ch_{}_volume", ch_num),
description: String::new(),
typ: ParamType::Float,
default: ParamValue::Float(1.0),
range: ParamRange {
min: Some(0.0),
max: Some(1.0),
step: Some(0.01),
},
unit: Some("gain".to_string()),
choices: None,
});
params.push(ParamMetadata {
name: format!("ch_{}_pan", ch_num),
description: String::new(),
typ: ParamType::Float,
default: ParamValue::Float(0.0),
range: ParamRange {
min: Some(-1.0),
max: Some(1.0),
step: Some(0.01),
},
unit: Some("pan".to_string()),
choices: None,
});
params.push(ParamMetadata {
name: format!("ch_{}_mute", ch_num),
description: String::new(),
typ: ParamType::Bool,
default: ParamValue::Bool(false),
range: ParamRange {
min: None,
max: None,
step: None,
},
unit: None,
choices: None,
});
}
NodeMetadata {
name: "Mixer".to_string(),
type_name: Some("rill/mixer".to_string()),
category: NodeCategory::Utility,
description: format!(
"Mixer with {} channels and {} buses",
self.channels.len(),
self.buses.len()
),
author: "Rill Mixer".to_string(),
version: "0.2.0".to_string(),
signal_inputs: self.channels.len(),
signal_outputs: 2 + self.buses.len(),
control_inputs: 0,
control_outputs: 0,
clock_inputs: 0,
clock_outputs: 0,
feedback_ports: 0,
parameters: params,
}
}
fn node_type_id(&self) -> NodeTypeId
where
Self: 'static + Sized,
{
NodeTypeId::of::<Self>()
}
fn init(&mut self, sample_rate: f32) {
self.sample_rate = sample_rate;
self.state.sample_rate = sample_rate;
}
fn reset(&mut self) {
self.current_master_volume = self.master_volume;
self.state.reset();
for channel in &mut self.channels {
channel.set_smoothing(self.smoothing);
}
}
fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
let name = id.as_str();
if name == "master_volume" {
return Some(ParamValue::Float(self.master_volume));
}
if name.starts_with("ch_") {
let parts: Vec<&str> = name.split('_').collect();
if parts.len() >= 3 {
if let Ok(idx) = parts[1].parse::<usize>() {
if idx > 0 && idx <= self.channels.len() {
let channel = &self.channels[idx - 1];
match parts[2] {
"volume" => return Some(ParamValue::Float(channel.config().volume)),
"pan" => return Some(ParamValue::Float(channel.config().pan)),
"mute" => return Some(ParamValue::Bool(channel.config().muted)),
_ => {}
}
}
}
}
}
if name == "smoothing" {
return Some(ParamValue::Float(self.smoothing));
}
None
}
fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
let name = id.as_str();
if name == "master_volume" {
if let ParamValue::Float(v) = value {
self.set_master_volume(v);
return Ok(());
}
}
if name == "smoothing" {
if let ParamValue::Float(v) = value {
self.set_smoothing(v);
return Ok(());
}
}
if name.starts_with("ch_") {
let parts: Vec<&str> = name.split('_').collect();
if parts.len() >= 3 {
if let Ok(idx) = parts[1].parse::<usize>() {
if idx > 0 && idx <= self.channels.len() {
match parts[2] {
"volume" => {
if let ParamValue::Float(v) = value {
return self.set_channel_volume(idx - 1, v).map_err(|e| {
rill_core::ProcessError::Parameter(e.to_string())
});
}
}
"pan" => {
if let ParamValue::Float(v) = value {
return self.set_channel_pan(idx - 1, v).map_err(|e| {
rill_core::ProcessError::Parameter(e.to_string())
});
}
}
"mute" => {
if let ParamValue::Bool(v) = value {
return self.set_channel_mute(idx - 1, v).map_err(|e| {
rill_core::ProcessError::Parameter(e.to_string())
});
}
}
_ => {}
}
}
}
}
}
Err(rill_core::ProcessError::Parameter(format!(
"Unknown parameter: {}",
name
)))
}
fn id(&self) -> NodeId {
self.id
}
fn set_id(&mut self, id: NodeId) {
self.id = id;
}
fn input_port(&self, index: usize) -> Option<&Port<f32, BUF_SIZE>> {
self.input_ports.get(index)
}
fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
self.input_ports.get_mut(index)
}
fn output_port(&self, index: usize) -> Option<&Port<f32, BUF_SIZE>> {
self.output_ports.get(index)
}
fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
self.output_ports.get_mut(index)
}
fn control_port(&self, index: usize) -> Option<&Port<f32, BUF_SIZE>> {
self.control_ports.get(index)
}
fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<f32, BUF_SIZE>> {
self.control_ports.get_mut(index)
}
fn state(&self) -> &NodeState<f32, BUF_SIZE> {
&self.state
}
fn state_mut(&mut self) -> &mut NodeState<f32, BUF_SIZE> {
&mut self.state
}
fn num_signal_inputs(&self) -> usize {
self.channels.len()
}
fn num_signal_outputs(&self) -> usize {
2 + self.buses.len()
}
fn num_control_inputs(&self) -> usize {
0
}
fn num_control_outputs(&self) -> usize {
0
}
fn num_clock_inputs(&self) -> usize {
0
}
fn num_clock_outputs(&self) -> usize {
0
}
fn num_feedback_ports(&self) -> usize {
0
}
}
impl<const BUF_SIZE: usize> rill_core::traits::Router<f32, BUF_SIZE> for MixerNode<BUF_SIZE> {
fn route(&mut self, ctx: &RenderContext, _inputs: &[&[f32; BUF_SIZE]]) -> ProcessResult<()> {
let _num_buses = self.buses.len();
let buffer_size = BUF_SIZE;
self.state.sample_pos = ctx.sample_pos;
self.state.blocks_processed = ctx.sample_pos / buffer_size as u64;
for bus in &mut self.buses {
if bus.len() != buffer_size {
bus.resize(buffer_size, 0.0);
} else {
bus.fill(0.0);
}
}
let mut master_left = [0.0f32; BUF_SIZE];
let mut master_right = [0.0f32; BUF_SIZE];
for (ch_idx, channel) in self.channels.iter_mut().enumerate() {
if ch_idx >= self.input_ports.len() {
continue;
}
let input_buf = self.input_ports[ch_idx].read();
let channel_volume = channel.config().volume;
for (i, ((&sample, left), right)) in input_buf
.iter()
.zip(master_left.iter_mut())
.zip(master_right.iter_mut())
.enumerate()
{
let (left_out, right_out) = channel.process_mono(sample);
*left += left_out;
*right += right_out;
for send in &self.sends[ch_idx] {
if send.bus_index < self.buses.len() {
let bus = &mut self.buses[send.bus_index];
let send_signal = match send.send_type {
SendType::PreFader => sample,
SendType::PostFader => sample * channel_volume,
};
bus[i] += send_signal * send.level;
}
}
}
}
self.current_master_volume +=
(self.master_volume - self.current_master_volume) * self.smoothing;
let master_gain = self.current_master_volume;
if self.output_ports.len() >= 2 {
let (first, rest) = self.output_ports.split_at_mut(1);
let out_l = first[0].write();
let out_r = rest[0].write();
for ((master_l, master_r), (out_l, out_r)) in master_left
.iter()
.zip(master_right.iter())
.zip(out_l.iter_mut().zip(out_r.iter_mut()))
{
*out_l = master_l * master_gain;
*out_r = master_r * master_gain;
}
}
for (bus_idx, bus) in self.buses.iter().enumerate() {
let out_idx = 2 + bus_idx;
if out_idx < self.output_ports.len() {
let out_buf = self.output_ports[out_idx].write();
out_buf.copy_from_slice(&bus[..buffer_size]);
}
}
Ok(())
}
fn num_route_inputs(&self) -> usize {
self.channels.len()
}
fn num_route_outputs(&self) -> usize {
2 + self.buses.len()
}
fn set_connection(&mut self, from: usize, to: usize, gain: f32) -> ProcessResult<()> {
if from >= self.channels.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
if to == 0 || to == 1 {
self.set_channel_volume(from, gain.clamp(0.0, 1.0))
} else if to >= 2 && to < 2 + self.buses.len() {
let bus_idx = to - 2;
if let Some(existing) = self.sends[from].iter_mut().find(|s| s.bus_index == bus_idx) {
existing.level = gain.clamp(0.0, 1.0);
Ok(())
} else {
self.add_send(
from,
SendConfig {
bus_index: bus_idx,
level: gain.clamp(0.0, 1.0),
send_type: SendType::PostFader,
},
)
}
} else {
Err(ProcessError::Parameter("Output index out of range".into()))
}
}
fn remove_connection(&mut self, from: usize, to: usize) -> ProcessResult<()> {
if from >= self.channels.len() {
return Err(ProcessError::Parameter("Channel index out of range".into()));
}
if to == 0 || to == 1 {
self.set_channel_mute(from, true)
} else if to >= 2 && to < 2 + self.buses.len() {
let bus_idx = to - 2;
self.sends[from].retain(|s| s.bus_index != bus_idx);
Ok(())
} else {
Err(ProcessError::Parameter("Output index out of range".into()))
}
}
fn routing_matrix(&self) -> Vec<Vec<(usize, f32)>> {
let n_out = self.num_route_outputs();
let mut matrix = vec![Vec::new(); n_out];
for (ch_idx, ch) in self.channels.iter().enumerate() {
if !ch.config().muted {
matrix[0].push((ch_idx, ch.config().volume));
matrix[1].push((ch_idx, ch.config().volume));
}
}
for (ch_idx, ch_sends) in self.sends.iter().enumerate() {
for send in ch_sends {
let out_idx = 2 + send.bus_index;
if out_idx < n_out {
matrix[out_idx].push((ch_idx, send.level));
}
}
}
matrix
}
}