use core::marker::PhantomData;
use hashbrown::HashMap;
use rtrb::RingBuffer;
use crate::graph::AudioGraph;
use crate::node::{AudioNode, NodeId};
use crate::nodes::{ResamplingSource, RtrbSink};
#[cfg(feature = "cpal_sink")]
use crate::device::CpalDevice;
pub struct Handle<M: Send + 'static> {
pub(crate) node_id: NodeId,
#[allow(dead_code)]
pub(crate) graph_id: usize,
pub(crate) sender: rtrb::Producer<M>,
pub(crate) _marker: PhantomData<M>,
}
impl<M: Send + 'static> Handle<M> {
pub fn send(&mut self, msg: M) -> Result<(), M> {
self.sender.push(msg).map_err(|rtrb::PushError::Full(m)| m)
}
}
struct SubGraph {
graph: AudioGraph,
#[allow(dead_code)]
sample_rate: u32,
sink_node: NodeId,
resampler_node: NodeId,
blocks_processed: u64,
}
pub struct Klingt {
main_graph: AudioGraph,
sample_rate: u32,
channels: usize,
sub_graphs: HashMap<u32, SubGraph>,
sink_node: Option<NodeId>,
main_blocks_processed: u64,
}
impl Klingt {
pub fn new(sample_rate: u32) -> Self {
Self {
main_graph: AudioGraph::new(sample_rate),
sample_rate,
channels: 2,
sub_graphs: HashMap::new(),
sink_node: None,
main_blocks_processed: 0,
}
}
#[cfg(feature = "cpal_sink")]
pub fn default_output() -> Option<Self> {
let device = CpalDevice::default_output()?;
let sample_rate = device.sample_rate();
let channels = device.channels() as usize;
let mut klingt = Self {
main_graph: AudioGraph::new(sample_rate),
sample_rate,
channels,
sub_graphs: HashMap::new(),
sink_node: None,
main_blocks_processed: 0,
};
let sink = device.create_sink();
let handle = klingt.main_graph.add(sink);
klingt.sink_node = Some(handle.id());
klingt.main_graph.set_terminal(&handle);
Some(klingt)
}
pub fn with_channels(mut self, channels: usize) -> Self {
self.channels = channels;
self
}
pub fn with_output<S: AudioNode<Message = ()>>(mut self, sink: S) -> Self {
let handle = self.main_graph.add(sink);
self.sink_node = Some(handle.id());
self.main_graph.set_terminal(&handle);
self
}
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}
pub fn add<N: AudioNode>(&mut self, node: N) -> Handle<N::Message> {
let node_rate = node.native_sample_rate();
if let Some(rate) = node_rate {
if rate != self.sample_rate {
return self.add_to_subgraph(node, rate);
}
}
let handle = self.main_graph.add(node);
let node_id = handle.id();
Handle {
node_id,
graph_id: 0,
sender: handle.sender,
_marker: PhantomData,
}
}
fn add_to_subgraph<N: AudioNode>(&mut self, node: N, rate: u32) -> Handle<N::Message> {
let channels = node.num_outputs().max(self.channels);
if !self.sub_graphs.contains_key(&rate) {
self.create_subgraph(rate, channels);
}
let sub = self.sub_graphs.get_mut(&rate).unwrap();
let handle = sub.graph.add(node);
let node_id = handle.id();
Handle {
node_id,
graph_id: rate as usize,
sender: handle.sender,
_marker: PhantomData,
}
}
fn create_subgraph(&mut self, rate: u32, channels: usize) {
let buffer_size = ((rate as f32 * 0.1) as usize * channels).next_power_of_two().max(8192);
let (producer, consumer) = RingBuffer::<f32>::new(buffer_size);
let mut sub_graph = AudioGraph::new(rate);
let sink = RtrbSink::new(producer, channels);
let sink_handle = sub_graph.add(sink);
let sink_node = sink_handle.id();
sub_graph.set_terminal(&sink_handle);
let resampler = ResamplingSource::new(consumer, channels, rate);
let resampler_handle = self.main_graph.add(resampler);
let resampler_node = resampler_handle.id();
self.sub_graphs.insert(rate, SubGraph {
graph: sub_graph,
sample_rate: rate,
sink_node,
resampler_node,
blocks_processed: 0,
});
}
pub fn connect<M1, M2>(&mut self, from: &Handle<M1>, to: &Handle<M2>)
where
M1: Send + 'static,
M2: Send + 'static,
{
let from_graph_id = from.graph_id;
let to_graph_id = to.graph_id;
let from_h = Self::make_handle::<M1>(from.node_id);
let to_h = Self::make_handle::<M2>(to.node_id);
match (from_graph_id, to_graph_id) {
(0, 0) => {
self.main_graph.connect(&from_h, &to_h);
}
(r1, r2) if r1 == r2 && r1 != 0 => {
let rate = r1 as u32;
let sub = self.sub_graphs.get_mut(&rate).unwrap();
sub.graph.connect(&from_h, &to_h);
}
(rate_usize, 0) if rate_usize != 0 => {
let rate = rate_usize as u32;
let sub = self.sub_graphs.get_mut(&rate).unwrap();
let sink_handle = Self::make_handle::<()>(sub.sink_node);
sub.graph.connect(&from_h, &sink_handle);
let resampler_handle = Self::make_handle::<()>(sub.resampler_node);
self.main_graph.connect(&resampler_handle, &to_h);
}
_ => {
panic!("Cannot connect nodes across different sub-graphs directly (from graph {} to graph {})", from_graph_id, to_graph_id);
}
}
}
pub fn output<M: Send + 'static>(&mut self, handle: &Handle<M>) {
let sink_id = self.sink_node.expect("No output sink configured. Use default_output() or with_output().");
if handle.graph_id == 0 {
let from_h = Self::make_handle::<M>(handle.node_id);
let to_h = Self::make_handle::<()>(sink_id);
self.main_graph.connect(&from_h, &to_h);
} else {
let rate = handle.graph_id as u32;
let sub = self.sub_graphs.get_mut(&rate)
.expect("Sub-graph not found for handle's graph_id");
let from_h = Self::make_handle::<M>(handle.node_id);
let sink_handle = Self::make_handle::<()>(sub.sink_node);
sub.graph.connect(&from_h, &sink_handle);
let resampler_handle = Self::make_handle::<()>(sub.resampler_node);
let to_h = Self::make_handle::<()>(sink_id);
self.main_graph.connect(&resampler_handle, &to_h);
}
}
pub fn process(&mut self) {
let main_rate = self.sample_rate as f64;
let main_blocks = self.main_blocks_processed + 1;
for (rate, sub) in self.sub_graphs.iter_mut() {
let rate_ratio = *rate as f64 / main_rate;
let blocks_needed = ((main_blocks as f64) * rate_ratio).ceil() as u64 + 4;
while sub.blocks_processed < blocks_needed {
sub.graph.process();
sub.blocks_processed += 1;
}
}
self.main_graph.process();
self.main_blocks_processed += 1;
}
fn make_handle<M: Send + 'static>(node_id: NodeId) -> crate::graph::NodeHandle<M> {
crate::graph::NodeHandle {
id: node_id,
sender: rtrb::RingBuffer::new(1).0, _marker: PhantomData,
}
}
}