use core::any::TypeId;
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::ChannelBuffer;
use crate::dsp::volume::is_buffer_silent;
use crate::log::RealtimeLogger;
use crate::mask::{ConnectedMask, ConstantMask, MaskType, SilenceMask};
use crate::{
channel_config::{ChannelConfig, ChannelCount},
clock::{DurationSamples, InstantSamples, InstantSeconds},
dsp::declick::DeclickValues,
event::{NodeEvent, NodeEventType, ProcEvents},
StreamInfo,
};
#[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 AudioNodeInfo {
debug_name: &'static str,
channel_config: ChannelConfig,
call_update_method: bool,
custom_state: Option<Box<dyn Any>>,
latency_frames: u32,
}
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,
}
}
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
}
}
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,
}
}
}
#[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 trait AudioNode {
type Configuration: Default;
fn info(&self, configuration: &Self::Configuration) -> AudioNodeInfo;
fn construct_processor(
&self,
configuration: &Self::Configuration,
cx: ConstructProcessorContext,
) -> impl AudioNodeProcessor;
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) -> AudioNodeInfo;
fn construct_processor(&self, cx: ConstructProcessorContext) -> Box<dyn AudioNodeProcessor>;
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) -> AudioNodeInfo {
self.constructor.info(&self.configuration)
}
fn construct_processor(&self, cx: ConstructProcessorContext) -> Box<dyn AudioNodeProcessor> {
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 process(
&mut self,
info: &ProcInfo,
buffers: ProcBuffers,
events: &mut ProcEvents,
extra: &mut ProcExtra,
) -> ProcessStatus;
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 new_stream(&mut self, stream_info: &StreamInfo, context: &mut ProcStreamCtx) {
self.as_mut().new_stream(stream_info, context)
}
fn process(
&mut self,
info: &ProcInfo,
buffers: ProcBuffers,
events: &mut ProcEvents,
extra: &mut ProcExtra,
) -> ProcessStatus {
self.as_mut().process(info, buffers, events, extra)
}
fn stream_stopped(&mut self, context: &mut ProcStreamCtx) {
self.as_mut().stream_stopped(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, amp_epsilon: f32) -> ProcessStatus {
let mut silent = true;
for buffer in self.outputs.iter() {
if !is_buffer_silent(buffer, amp_epsilon) {
silent = false;
break;
}
}
if silent {
ProcessStatus::ClearAllOutputs
} else {
ProcessStatus::OutputsModified
}
}
}
pub struct ProcExtra {
pub scratch_buffers: ChannelBuffer<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 prev_output_was_silent: bool,
pub sample_rate: NonZeroU32,
pub sample_rate_recip: f64,
pub clock_samples: InstantSamples,
pub duration_since_stream_start: Duration,
pub stream_status: StreamStatus,
pub dropped_frames: u32,
#[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 = 0b01;
const OUTPUT_UNDERFLOW = 0b10;
}
}
#[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 self.0.contains_key(&TypeId::of::<S>()) {
Err(resource)
} else {
self.0.insert(TypeId::of::<S>(), Box::new(resource));
Ok(())
}
}
pub fn insert_any<S: Send + 'static>(
&mut self,
resource: Box<dyn Any + Send>,
type_id: TypeId,
) -> Result<(), Box<dyn Any + Send>> {
if self.0.contains_key(&type_id) {
Err(resource)
} else {
self.0.insert(type_id, resource);
Ok(())
}
}
pub fn entry<'a, S: Send + 'static>(&'a mut self) -> ProcStoreEntry<'a, S> {
ProcStoreEntry {
boxed_entry: self.0.entry(TypeId::of::<S>()),
type_: PhantomData::default(),
}
}
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::default(),
}
}
}