use core::any::TypeId;
use core::error::Error;
use core::fmt;
use core::marker::PhantomData;
use core::ops::Range;
use core::time::Duration;
use core::{any::Any, fmt::Debug, hash::Hash, num::NonZeroU32};
#[cfg(feature = "std")]
use std::collections::hash_map::{Entry, HashMap};
#[cfg(not(feature = "std"))]
use bevy_platform::collections::hash_map::{Entry, HashMap};
#[cfg(not(feature = "std"))]
use bevy_platform::prelude::{Box, Vec};
use crate::dsp::buffer::ConstSequentialBuffer;
use crate::dsp::volume::is_buffer_silent;
use crate::log::RealtimeLogger;
use crate::mask::{ConnectedMask, ConstantMask, MaskType, SilenceMask};
use crate::{
StreamInfo,
channel_config::{ChannelConfig, ChannelCount},
clock::{DurationSamples, InstantSamples, InstantSeconds},
dsp::declick::DeclickValues,
event::{NodeEvent, NodeEventType, ProcEvents},
};
#[cfg(feature = "scheduled_events")]
use crate::clock::EventInstant;
#[cfg(feature = "musical_transport")]
use crate::clock::{InstantMusical, MusicalTransport};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]
pub struct NodeID(pub thunderdome::Index);
impl NodeID {
pub const DANGLING: Self = Self(thunderdome::Index::DANGLING);
}
impl Default for NodeID {
fn default() -> Self {
Self::DANGLING
}
}
#[derive(Debug)]
pub struct NodeError(pub Box<dyn Error>);
impl NodeError {
pub const fn from_boxed(error: Box<dyn Error>) -> Self {
Self(error)
}
}
impl<E> From<E> for NodeError
where
E: Error + 'static,
{
fn from(err: E) -> Self {
NodeError(Box::new(err))
}
}
impl fmt::Display for NodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Node Error: {}", self.0)
}
}
impl From<NodeError> for Box<dyn Error> {
fn from(value: NodeError) -> Self {
value.0
}
}
#[derive(Debug)]
pub struct AudioNodeInfo {
debug_name: &'static str,
channel_config: ChannelConfig,
call_update_method: bool,
custom_state: Option<Box<dyn Any>>,
latency_frames: u32,
in_place_buffers: bool,
}
impl AudioNodeInfo {
pub const fn new() -> Self {
Self {
debug_name: "unnamed",
channel_config: ChannelConfig {
num_inputs: ChannelCount::ZERO,
num_outputs: ChannelCount::ZERO,
},
call_update_method: false,
custom_state: None,
latency_frames: 0,
in_place_buffers: false,
}
}
pub const fn debug_name(mut self, debug_name: &'static str) -> Self {
self.debug_name = debug_name;
self
}
pub const fn channel_config(mut self, channel_config: ChannelConfig) -> Self {
self.channel_config = channel_config;
self
}
pub const fn is_pre_process(mut self) -> Self {
self.channel_config = ChannelConfig {
num_inputs: ChannelCount::ZERO,
num_outputs: ChannelCount::ZERO,
};
self
}
pub const fn call_update_method(mut self, call_update_method: bool) -> Self {
self.call_update_method = call_update_method;
self
}
pub fn custom_state<T: 'static>(mut self, custom_state: T) -> Self {
self.custom_state = Some(Box::new(custom_state));
self
}
pub const fn latency_frames(mut self, latency_frames: u32) -> Self {
self.latency_frames = latency_frames;
self
}
pub const fn in_place_buffers(mut self, in_place_buffers: bool) -> Self {
self.in_place_buffers = in_place_buffers;
self
}
}
impl Default for AudioNodeInfo {
fn default() -> Self {
Self::new()
}
}
impl From<AudioNodeInfo> for AudioNodeInfoInner {
fn from(value: AudioNodeInfo) -> Self {
AudioNodeInfoInner {
debug_name: value.debug_name,
channel_config: value.channel_config,
call_update_method: value.call_update_method,
custom_state: value.custom_state,
latency_frames: value.latency_frames,
in_place_buffers: value.in_place_buffers,
}
}
}
#[derive(Debug)]
pub struct AudioNodeInfoInner {
pub debug_name: &'static str,
pub channel_config: ChannelConfig,
pub call_update_method: bool,
pub custom_state: Option<Box<dyn Any>>,
pub latency_frames: u32,
pub in_place_buffers: bool,
}
pub trait AudioNode {
type Configuration: Default;
fn info(&self, configuration: &Self::Configuration) -> Result<AudioNodeInfo, NodeError>;
fn construct_processor(
&self,
configuration: &Self::Configuration,
cx: ConstructProcessorContext,
) -> Result<impl AudioNodeProcessor, NodeError>;
fn update(&mut self, configuration: &Self::Configuration, cx: UpdateContext) {
let _ = configuration;
let _ = cx;
}
}
pub struct ConstructProcessorContext<'a> {
pub node_id: NodeID,
pub stream_info: &'a StreamInfo,
custom_state: &'a mut Option<Box<dyn Any>>,
}
impl<'a> ConstructProcessorContext<'a> {
pub fn new(
node_id: NodeID,
stream_info: &'a StreamInfo,
custom_state: &'a mut Option<Box<dyn Any>>,
) -> Self {
Self {
node_id,
stream_info,
custom_state,
}
}
pub fn custom_state<T: 'static>(&self) -> Option<&T> {
self.custom_state
.as_ref()
.and_then(|s| s.downcast_ref::<T>())
}
pub fn custom_state_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.custom_state
.as_mut()
.and_then(|s| s.downcast_mut::<T>())
}
}
pub struct UpdateContext<'a> {
pub node_id: NodeID,
pub stream_info: Option<&'a StreamInfo>,
custom_state: &'a mut Option<Box<dyn Any>>,
event_queue: &'a mut Vec<NodeEvent>,
}
impl<'a> UpdateContext<'a> {
pub fn new(
node_id: NodeID,
stream_info: Option<&'a StreamInfo>,
custom_state: &'a mut Option<Box<dyn Any>>,
event_queue: &'a mut Vec<NodeEvent>,
) -> Self {
Self {
node_id,
stream_info,
custom_state,
event_queue,
}
}
pub fn queue_event(&mut self, event: NodeEventType) {
self.event_queue.push(NodeEvent {
node_id: self.node_id,
#[cfg(feature = "scheduled_events")]
time: None,
event,
});
}
#[cfg(feature = "scheduled_events")]
pub fn schedule_event(&mut self, event: NodeEventType, time: EventInstant) {
self.event_queue.push(NodeEvent {
node_id: self.node_id,
time: Some(time),
event,
});
}
pub fn custom_state<T: 'static>(&self) -> Option<&T> {
self.custom_state
.as_ref()
.and_then(|s| s.downcast_ref::<T>())
}
pub fn custom_state_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.custom_state
.as_mut()
.and_then(|s| s.downcast_mut::<T>())
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EmptyConfig;
pub trait DynAudioNode {
fn info(&self) -> Result<AudioNodeInfo, NodeError>;
fn construct_processor(
&self,
cx: ConstructProcessorContext,
) -> Result<Box<dyn AudioNodeProcessor>, NodeError>;
fn update(&mut self, cx: UpdateContext) {
let _ = cx;
}
}
pub struct Constructor<T, C> {
constructor: T,
configuration: C,
}
impl<T: AudioNode> Constructor<T, T::Configuration> {
pub fn new(constructor: T, configuration: Option<T::Configuration>) -> Self {
Self {
constructor,
configuration: configuration.unwrap_or_default(),
}
}
}
impl<T: AudioNode> DynAudioNode for Constructor<T, T::Configuration> {
fn info(&self) -> Result<AudioNodeInfo, NodeError> {
self.constructor.info(&self.configuration)
}
fn construct_processor(
&self,
cx: ConstructProcessorContext,
) -> Result<Box<dyn AudioNodeProcessor>, NodeError> {
Ok(Box::new(
self.constructor
.construct_processor(&self.configuration, cx)?,
))
}
fn update(&mut self, cx: UpdateContext) {
self.constructor.update(&self.configuration, cx);
}
}
pub trait AudioNodeProcessor: 'static + Send {
fn events(&mut self, info: &ProcInfo, events: &mut ProcEvents, extra: &mut ProcExtra) {
let _ = info;
let _ = events;
let _ = extra;
}
fn bypassed(&mut self, bypassed: bool) {
let _ = bypassed;
}
fn process(
&mut self,
info: &ProcInfo,
buffers: ProcBuffers,
extra: &mut ProcExtra,
) -> ProcessStatus {
let _ = info;
let _ = buffers;
let _ = extra;
ProcessStatus::Bypass
}
fn stream_stopped(&mut self, context: &mut ProcStreamCtx) {
let _ = context;
}
fn new_stream(&mut self, stream_info: &StreamInfo, context: &mut ProcStreamCtx) {
let _ = stream_info;
let _ = context;
}
}
impl AudioNodeProcessor for Box<dyn AudioNodeProcessor> {
fn events(&mut self, info: &ProcInfo, events: &mut ProcEvents, extra: &mut ProcExtra) {
self.as_mut().events(info, events, extra);
}
fn bypassed(&mut self, bypassed: bool) {
self.as_mut().bypassed(bypassed);
}
fn process(
&mut self,
info: &ProcInfo,
buffers: ProcBuffers,
extra: &mut ProcExtra,
) -> ProcessStatus {
self.as_mut().process(info, buffers, extra)
}
fn stream_stopped(&mut self, context: &mut ProcStreamCtx) {
self.as_mut().stream_stopped(context)
}
fn new_stream(&mut self, stream_info: &StreamInfo, context: &mut ProcStreamCtx) {
self.as_mut().new_stream(stream_info, context)
}
}
pub struct ProcStreamCtx<'a> {
pub store: &'a mut ProcStore,
pub logger: &'a mut RealtimeLogger,
}
pub const NUM_SCRATCH_BUFFERS: usize = 8;
#[derive(Debug)]
pub struct ProcBuffers<'a, 'b> {
pub inputs: &'a [&'b [f32]],
pub outputs: &'a mut [&'b mut [f32]],
}
impl<'a, 'b> ProcBuffers<'a, 'b> {
pub fn check_for_silence_on_outputs(&self, min_amp: f32) -> ProcessStatus {
let mut silent = true;
for buffer in self.outputs.iter() {
if !is_buffer_silent(buffer, min_amp) {
silent = false;
break;
}
}
if silent {
ProcessStatus::ClearAllOutputs
} else {
ProcessStatus::OutputsModified
}
}
pub fn inputs_settled_at_zero(&self) -> bool {
let mut settled_at_zero = true;
for ch in self.inputs.iter() {
settled_at_zero = ch.iter().rev().take(2).all(|s| *s == 0.0);
if !settled_at_zero {
break;
}
}
settled_at_zero
}
}
pub struct ProcExtra {
pub scratch_buffers: ConstSequentialBuffer<f32, NUM_SCRATCH_BUFFERS>,
pub declick_values: DeclickValues,
pub logger: RealtimeLogger,
pub store: ProcStore,
}
#[derive(Debug)]
pub struct ProcInfo {
pub frames: usize,
pub in_silence_mask: SilenceMask,
pub out_silence_mask: SilenceMask,
pub in_constant_mask: ConstantMask,
pub out_constant_mask: ConstantMask,
pub in_connected_mask: ConnectedMask,
pub out_connected_mask: ConnectedMask,
pub sample_rate: NonZeroU32,
pub sample_rate_recip: f64,
pub clock_samples: InstantSamples,
pub total_cpu_seconds_recip: f64,
pub duration_since_stream_start: Duration,
pub stream_status: StreamStatus,
pub dropped_frames: u32,
pub process_to_playback_delay: Option<Duration>,
pub did_just_unbypass: bool,
#[cfg(feature = "musical_transport")]
pub transport_info: Option<TransportInfo>,
}
impl ProcInfo {
pub fn clock_seconds(&self) -> InstantSeconds {
self.clock_samples
.to_seconds(self.sample_rate, self.sample_rate_recip)
}
pub fn clock_samples_range(&self) -> Range<InstantSamples> {
self.clock_samples..self.clock_samples + DurationSamples(self.frames as i64)
}
pub fn clock_seconds_range(&self) -> Range<InstantSeconds> {
self.clock_seconds()
..(self.clock_samples + DurationSamples(self.frames as i64))
.to_seconds(self.sample_rate, self.sample_rate_recip)
}
#[cfg(feature = "musical_transport")]
pub fn playhead(&self) -> Option<InstantMusical> {
self.transport_info.as_ref().and_then(|transport_info| {
transport_info
.start_clock_samples
.map(|start_clock_samples| {
transport_info.transport.samples_to_musical(
self.clock_samples,
start_clock_samples,
transport_info.speed_multiplier,
self.sample_rate,
self.sample_rate_recip,
)
})
})
}
#[cfg(feature = "musical_transport")]
pub fn playhead_range(&self) -> Option<Range<InstantMusical>> {
self.transport_info.as_ref().and_then(|transport_info| {
transport_info
.start_clock_samples
.map(|start_clock_samples| {
transport_info.transport.samples_to_musical(
self.clock_samples,
start_clock_samples,
transport_info.speed_multiplier,
self.sample_rate,
self.sample_rate_recip,
)
..transport_info.transport.samples_to_musical(
self.clock_samples + DurationSamples(self.frames as i64),
start_clock_samples,
transport_info.speed_multiplier,
self.sample_rate,
self.sample_rate_recip,
)
})
})
}
#[cfg(feature = "musical_transport")]
pub fn transport_is_playing(&self) -> bool {
self.transport_info
.as_ref()
.map(|t| t.playing())
.unwrap_or(false)
}
#[cfg(feature = "musical_transport")]
pub fn musical_to_samples(&self, musical: InstantMusical) -> Option<InstantSamples> {
self.transport_info.as_ref().and_then(|transport_info| {
transport_info
.start_clock_samples
.map(|start_clock_samples| {
transport_info.transport.musical_to_samples(
musical,
start_clock_samples,
transport_info.speed_multiplier,
self.sample_rate,
)
})
})
}
}
#[cfg(feature = "musical_transport")]
#[derive(Debug, Clone, PartialEq)]
pub struct TransportInfo {
pub transport: MusicalTransport,
pub start_clock_samples: Option<InstantSamples>,
pub beats_per_minute: f64,
pub speed_multiplier: f64,
}
#[cfg(feature = "musical_transport")]
impl TransportInfo {
pub const fn playing(&self) -> bool {
self.start_clock_samples.is_some()
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StreamStatus: u32 {
const INPUT_OVERFLOW = 0b001;
const OUTPUT_UNDERFLOW = 0b010;
const CLOSED = 0b100;
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcessStatus {
#[default]
ClearAllOutputs,
Bypass,
OutputsModified,
OutputsModifiedWithMask(MaskType),
}
impl ProcessStatus {
pub const fn outputs_modified_with_silence_mask(mask: SilenceMask) -> Self {
Self::OutputsModifiedWithMask(MaskType::Silence(mask))
}
pub const fn outputs_modified_with_constant_mask(mask: ConstantMask) -> Self {
Self::OutputsModifiedWithMask(MaskType::Constant(mask))
}
}
pub struct ProcStore(HashMap<TypeId, Box<dyn Any + Send>>);
impl ProcStore {
pub fn with_capacity(capacity: usize) -> Self {
let mut h = HashMap::default();
h.reserve(capacity);
Self(h)
}
pub fn insert<S: Send + 'static>(&mut self, resource: S) -> Result<(), S> {
if let Entry::Vacant(e) = self.0.entry(TypeId::of::<S>()) {
e.insert(Box::new(resource));
Ok(())
} else {
Err(resource)
}
}
pub fn insert_any<S: Send + 'static>(
&mut self,
resource: Box<dyn Any + Send>,
type_id: TypeId,
) -> Result<(), Box<dyn Any + Send>> {
if let Entry::Vacant(e) = self.0.entry(type_id) {
e.insert(resource);
Ok(())
} else {
Err(resource)
}
}
pub fn entry<'a, S: Send + 'static>(&'a mut self) -> ProcStoreEntry<'a, S> {
ProcStoreEntry {
boxed_entry: self.0.entry(TypeId::of::<S>()),
type_: PhantomData,
}
}
pub fn contains<S: Send + 'static>(&self) -> bool {
self.0.contains_key(&TypeId::of::<S>())
}
pub fn get<S: Send + 'static>(&self) -> &S {
self.try_get().unwrap()
}
pub fn get_mut<S: Send + 'static>(&mut self) -> &mut S {
self.try_get_mut().unwrap()
}
pub fn try_get<S: Send + 'static>(&self) -> Option<&S> {
self.0
.get(&TypeId::of::<S>())
.map(|s| s.downcast_ref().unwrap())
}
pub fn try_get_mut<S: Send + 'static>(&mut self) -> Option<&mut S> {
self.0
.get_mut(&TypeId::of::<S>())
.map(|s| s.downcast_mut().unwrap())
}
}
pub struct ProcStoreEntry<'a, S: Send + 'static> {
pub boxed_entry: Entry<'a, TypeId, Box<dyn Any + Send>>,
type_: PhantomData<S>,
}
impl<'a, S: Send + 'static> ProcStoreEntry<'a, S> {
pub fn or_insert_with(self, default: impl FnOnce() -> S) -> &'a mut S {
self.boxed_entry
.or_insert_with(|| Box::new((default)()))
.downcast_mut()
.unwrap()
}
pub fn or_insert_with_any(self, default: impl FnOnce() -> Box<dyn Any + Send>) -> &'a mut S {
self.boxed_entry
.or_insert_with(default)
.downcast_mut()
.unwrap()
}
pub fn and_modify(self, f: impl FnOnce(&mut S)) -> Self {
let entry = self
.boxed_entry
.and_modify(|e| (f)(e.downcast_mut().unwrap()));
Self {
boxed_entry: entry,
type_: PhantomData,
}
}
}