use rill_core::{
Node, NodeCategory, NodeId, NodeMetadata, NodeState, ParamValue, ParameterId, Port,
ProcessError, ProcessResult, Processor, Transcendental,
};
use rill_core_dsp::filters::{Biquad, FilterParams, FilterType};
use super::{BandType, FilterFactory, GraphicEq, ParametricEq};
#[derive(Debug, Clone, Default)]
pub struct BiquadFactory;
impl FilterFactory<Biquad<f32>> for BiquadFactory {
fn create_filter(
&self,
filter_type: FilterType,
frequency: f32,
q: f32,
gain_db: f32,
) -> Biquad<f32> {
let params = FilterParams {
filter_type,
cutoff: frequency,
q,
gain_db,
};
Biquad::new(params)
}
}
pub struct ParametricEqProcessor<T: Transcendental, const BUF_SIZE: usize> {
id: NodeId,
metadata: NodeMetadata,
inputs: Vec<Port<T, BUF_SIZE>>,
outputs: Vec<Port<T, BUF_SIZE>>,
controls: Vec<Port<T, BUF_SIZE>>,
state: NodeState<T, BUF_SIZE>,
eq: ParametricEq<Biquad<f32>, BiquadFactory>,
pub output_gain: f32,
num_bands: usize,
}
impl<T: Transcendental, const BUF_SIZE: usize> ParametricEqProcessor<T, BUF_SIZE> {
pub fn new(sample_rate: f32, num_bands: usize) -> Self {
let metadata = NodeMetadata::new("ParametricEqProcessor", NodeCategory::Processor);
let mut inputs = Vec::new();
let mut outputs = Vec::new();
inputs.push(Port::input(NodeId(0), 0, "signal_in"));
outputs.push(Port::output(NodeId(0), 0, "signal_out"));
let factory = BiquadFactory;
let mut eq = ParametricEq::new(factory, num_bands, sample_rate);
eq.init(sample_rate);
Self {
id: NodeId(0),
metadata,
inputs,
outputs,
controls: Vec::new(),
state: NodeState::new(sample_rate),
eq,
output_gain: 1.0,
num_bands,
}
}
pub fn set_band(
&mut self,
index: usize,
frequency: f32,
q: f32,
gain_db: f32,
) -> Result<(), rill_core::Error> {
self.eq.set_band(index, frequency, q, gain_db)?;
Ok(())
}
pub fn set_band_type(
&mut self,
index: usize,
band_type: BandType,
) -> Result<(), rill_core::Error> {
self.eq.set_band_type(index, band_type)?;
Ok(())
}
pub fn set_band_enabled(
&mut self,
index: usize,
enabled: bool,
) -> Result<(), rill_core::Error> {
self.eq.set_band_enabled(index, enabled)?;
Ok(())
}
pub fn set_output_gain(&mut self, gain: f32) {
self.output_gain = gain.clamp(0.0, 4.0);
self.eq.set_output_gain(self.output_gain);
}
pub fn num_bands(&self) -> usize {
self.num_bands
}
pub fn eq(&self) -> &ParametricEq<Biquad<f32>, BiquadFactory> {
&self.eq
}
pub fn eq_mut(&mut self) -> &mut ParametricEq<Biquad<f32>, BiquadFactory> {
&mut self.eq
}
}
impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE>
for ParametricEqProcessor<T, BUF_SIZE>
{
fn node_type_id(&self) -> rill_core::NodeTypeId
where
Self: 'static + Sized,
{
rill_core::NodeTypeId::of::<Self>()
}
fn id(&self) -> NodeId {
self.id
}
fn set_id(&mut self, id: NodeId) {
self.id = id;
}
fn metadata(&self) -> NodeMetadata {
self.metadata.clone()
}
fn init(&mut self, sample_rate: f32) {
self.state.sample_rate = sample_rate;
self.eq.init(sample_rate);
}
fn reset(&mut self) {
self.state.sample_pos = 0;
self.state.blocks_processed = 0;
self.eq.reset();
}
fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
let name = id.as_str();
if name == "output_gain" {
return Some(ParamValue::Float(self.output_gain));
}
let parts: Vec<&str> = name.split('_').collect();
if parts.len() >= 3 && parts[0] == "band" {
if let Ok(index) = parts[1].parse::<usize>() {
if index < self.num_bands {
let field = parts[2];
match field {
"freq" => {
return self.eq.get_band_frequency(index).map(ParamValue::Float);
}
"q" => {
return self.eq.get_band_q(index).map(ParamValue::Float);
}
"gain" => {
return self.eq.get_band_gain(index).map(ParamValue::Float);
}
"enabled" => {
return self.eq.get_band_enabled(index).map(ParamValue::Bool);
}
_ => {}
}
}
}
}
None
}
fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
let name = id.as_str();
if name == "output_gain" {
if let Some(v) = value.as_f32() {
self.set_output_gain(v);
Ok(())
} else {
Err(ProcessError::parameter("Expected float value"))
}
} else {
let parts: Vec<&str> = name.split('_').collect();
if parts.len() >= 3 && parts[0] == "band" {
if let Ok(index) = parts[1].parse::<usize>() {
if index >= self.num_bands {
return Err(ProcessError::parameter(format!(
"Band index {} out of range",
index
)));
}
let field = parts[2];
match field {
"freq" => {
if let Some(v) = value.as_f32() {
self.eq
.set_band(
index,
v,
self.eq.get_band_q(index).unwrap_or(1.0),
self.eq.get_band_gain(index).unwrap_or(0.0),
)
.map_err(|e| ProcessError::parameter(e.to_string()))?;
Ok(())
} else {
Err(ProcessError::parameter("Expected float value"))
}
}
"q" => {
if let Some(v) = value.as_f32() {
self.eq
.set_band(
index,
self.eq.get_band_frequency(index).unwrap_or(1000.0),
v,
self.eq.get_band_gain(index).unwrap_or(0.0),
)
.map_err(|e| ProcessError::parameter(e.to_string()))?;
Ok(())
} else {
Err(ProcessError::parameter("Expected float value"))
}
}
"gain" => {
if let Some(v) = value.as_f32() {
self.eq
.set_band(
index,
self.eq.get_band_frequency(index).unwrap_or(1000.0),
self.eq.get_band_q(index).unwrap_or(1.0),
v,
)
.map_err(|e| ProcessError::parameter(e.to_string()))?;
Ok(())
} else {
Err(ProcessError::parameter("Expected float value"))
}
}
"enabled" => {
if let Some(b) = value.as_bool() {
self.eq
.set_band_enabled(index, b)
.map_err(|e| ProcessError::parameter(e.to_string()))?;
Ok(())
} else {
Err(ProcessError::parameter("Expected boolean value"))
}
}
_ => Err(ProcessError::parameter(format!(
"Unknown band field: {}",
field
))),
}
} else {
Err(ProcessError::parameter(format!(
"Invalid band index: {}",
parts[1]
)))
}
} else {
Err(ProcessError::parameter(format!(
"Unknown parameter: {}",
name
)))
}
}
}
fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.inputs.get(index)
}
fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.inputs.get_mut(index)
}
fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.outputs.get(index)
}
fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.outputs.get_mut(index)
}
fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.controls.get(index)
}
fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.controls.get_mut(index)
}
fn num_inputs(&self) -> usize {
self.inputs.len()
}
fn num_outputs(&self) -> usize {
self.outputs.len()
}
fn num_signal_inputs(&self) -> usize {
self.inputs.len()
}
fn num_signal_outputs(&self) -> usize {
self.outputs.len()
}
fn state(&self) -> &NodeState<T, BUF_SIZE> {
&self.state
}
fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
&mut self.state
}
}
impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE>
for ParametricEqProcessor<T, BUF_SIZE>
{
fn process(
&mut self,
_ctx: &rill_core::RenderContext,
_signal_inputs: &[&[T; BUF_SIZE]],
_control_inputs: &[T],
_clock_inputs: &[rill_core::RenderContext],
_feedback_inputs: &[&[T; BUF_SIZE]],
) -> ProcessResult<()> {
let input_buf = *self.inputs[0].read();
let output_buf = self.outputs[0].write();
let mut input_f32 = [0.0f32; BUF_SIZE];
for (dest, &src) in input_f32.iter_mut().zip(input_buf.iter()) {
*dest = src.to_f32();
}
let mut output_f32 = [0.0f32; BUF_SIZE];
self.eq.process_block(&input_f32, &mut output_f32);
for (dest, &src) in output_buf.iter_mut().zip(output_f32.iter()) {
*dest = T::from_f32(src);
}
Ok(())
}
fn latency(&self) -> usize {
0
}
}
pub struct GraphicEqProcessor<T: Transcendental, const BUF_SIZE: usize> {
id: NodeId,
metadata: NodeMetadata,
inputs: Vec<Port<T, BUF_SIZE>>,
outputs: Vec<Port<T, BUF_SIZE>>,
controls: Vec<Port<T, BUF_SIZE>>,
state: NodeState<T, BUF_SIZE>,
eq: GraphicEq<Biquad<f32>>,
pub output_gain: f32,
num_bands: usize,
}
impl<T: Transcendental, const BUF_SIZE: usize> GraphicEqProcessor<T, BUF_SIZE> {
pub fn new_third_octave(sample_rate: f32) -> Self {
let metadata = NodeMetadata::new("GraphicEqProcessor", NodeCategory::Processor);
let mut inputs = Vec::new();
let mut outputs = Vec::new();
inputs.push(Port::input(NodeId(0), 0, "signal_in"));
outputs.push(Port::output(NodeId(0), 0, "signal_out"));
let factory = BiquadFactory;
let mut eq = GraphicEq::new_third_octave(factory, sample_rate);
eq.init(sample_rate);
let num_bands = eq.num_bands();
Self {
id: NodeId(0),
metadata,
inputs,
outputs,
controls: Vec::new(),
state: NodeState::new(sample_rate),
eq,
output_gain: 1.0,
num_bands,
}
}
pub fn with_frequencies(frequencies: Vec<f32>, sample_rate: f32) -> Self {
let metadata = NodeMetadata::new("GraphicEqProcessor", NodeCategory::Processor);
let mut inputs = Vec::new();
let mut outputs = Vec::new();
inputs.push(Port::input(NodeId(0), 0, "signal_in"));
outputs.push(Port::output(NodeId(0), 0, "signal_out"));
let factory = BiquadFactory;
let mut eq = GraphicEq::with_frequencies(factory, frequencies, sample_rate);
eq.init(sample_rate);
let num_bands = eq.num_bands();
Self {
id: NodeId(0),
metadata,
inputs,
outputs,
controls: Vec::new(),
state: NodeState::new(sample_rate),
eq,
output_gain: 1.0,
num_bands,
}
}
pub fn set_band_gain(&mut self, index: usize, gain_db: f32) -> Result<(), rill_core::Error> {
self.eq.set_band_gain(index, gain_db)?;
Ok(())
}
pub fn set_band_enabled(
&mut self,
index: usize,
enabled: bool,
) -> Result<(), rill_core::Error> {
self.eq.set_band_enabled(index, enabled)?;
Ok(())
}
pub fn set_output_gain(&mut self, gain: f32) {
self.output_gain = gain.clamp(0.0, 4.0);
self.eq.set_output_gain(self.output_gain);
}
pub fn num_bands(&self) -> usize {
self.num_bands
}
pub fn eq(&self) -> &GraphicEq<Biquad<f32>> {
&self.eq
}
pub fn eq_mut(&mut self) -> &mut GraphicEq<Biquad<f32>> {
&mut self.eq
}
}
impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE>
for GraphicEqProcessor<T, BUF_SIZE>
{
fn node_type_id(&self) -> rill_core::NodeTypeId
where
Self: 'static + Sized,
{
rill_core::NodeTypeId::of::<Self>()
}
fn id(&self) -> NodeId {
self.id
}
fn set_id(&mut self, id: NodeId) {
self.id = id;
}
fn metadata(&self) -> NodeMetadata {
self.metadata.clone()
}
fn init(&mut self, sample_rate: f32) {
self.state.sample_rate = sample_rate;
self.eq.init(sample_rate);
}
fn reset(&mut self) {
self.state.sample_pos = 0;
self.state.blocks_processed = 0;
self.eq.reset();
}
fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
let name = id.as_str();
if name == "output_gain" {
return Some(ParamValue::Float(self.output_gain));
}
let parts: Vec<&str> = name.split('_').collect();
if parts.len() >= 3 && parts[0] == "band" {
if let Ok(index) = parts[1].parse::<usize>() {
if index < self.num_bands {
let field = parts[2];
match field {
"gain" => {
return self.eq.get_band_gain(index).map(ParamValue::Float);
}
"enabled" => {
return self.eq.get_band_enabled(index).map(ParamValue::Bool);
}
_ => {}
}
}
}
}
None
}
fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
let name = id.as_str();
if name == "output_gain" {
if let Some(v) = value.as_f32() {
self.set_output_gain(v);
Ok(())
} else {
Err(ProcessError::parameter("Expected float value"))
}
} else {
let parts: Vec<&str> = name.split('_').collect();
if parts.len() >= 3 && parts[0] == "band" {
if let Ok(index) = parts[1].parse::<usize>() {
if index >= self.num_bands {
return Err(ProcessError::parameter(format!(
"Band index {} out of range",
index
)));
}
let field = parts[2];
match field {
"gain" => {
if let Some(v) = value.as_f32() {
self.eq
.set_band_gain(index, v)
.map_err(|e| ProcessError::parameter(e.to_string()))?;
Ok(())
} else {
Err(ProcessError::parameter("Expected float value"))
}
}
"enabled" => {
if let Some(b) = value.as_bool() {
self.eq
.set_band_enabled(index, b)
.map_err(|e| ProcessError::parameter(e.to_string()))?;
Ok(())
} else {
Err(ProcessError::parameter("Expected boolean value"))
}
}
_ => Err(ProcessError::parameter(format!(
"Unknown band field: {}",
field
))),
}
} else {
Err(ProcessError::parameter(format!(
"Invalid band index: {}",
parts[1]
)))
}
} else {
Err(ProcessError::parameter(format!(
"Unknown parameter: {}",
name
)))
}
}
}
fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.inputs.get(index)
}
fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.inputs.get_mut(index)
}
fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.outputs.get(index)
}
fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.outputs.get_mut(index)
}
fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.controls.get(index)
}
fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.controls.get_mut(index)
}
fn num_inputs(&self) -> usize {
self.inputs.len()
}
fn num_outputs(&self) -> usize {
self.outputs.len()
}
fn num_signal_inputs(&self) -> usize {
self.inputs.len()
}
fn num_signal_outputs(&self) -> usize {
self.outputs.len()
}
fn state(&self) -> &NodeState<T, BUF_SIZE> {
&self.state
}
fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
&mut self.state
}
}
impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE>
for GraphicEqProcessor<T, BUF_SIZE>
{
fn process(
&mut self,
_ctx: &rill_core::RenderContext,
_signal_inputs: &[&[T; BUF_SIZE]],
_control_inputs: &[T],
_clock_inputs: &[rill_core::RenderContext],
_feedback_inputs: &[&[T; BUF_SIZE]],
) -> ProcessResult<()> {
let input_buf = *self.inputs[0].read();
let output_buf = self.outputs[0].write();
let mut input_f32 = [0.0f32; BUF_SIZE];
for (dest, &src) in input_f32.iter_mut().zip(input_buf.iter()) {
*dest = src.to_f32();
}
let mut output_f32 = [0.0f32; BUF_SIZE];
self.eq.process_block(&input_f32, &mut output_f32);
for (dest, &src) in output_buf.iter_mut().zip(output_f32.iter()) {
*dest = T::from_f32(src);
}
Ok(())
}
fn latency(&self) -> usize {
0
}
}