use crate::{
context::{AudioContext, StreamRestartEvent, StreamStartEvent},
edge::{AudioGraphInput, AudioGraphOutput, PendingConnections},
node::{FirewheelNode, FirewheelNodeInfo},
};
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_seedling_macros::{NodeLabel, PoolLabel};
use bevy_transform::prelude::Transform;
use std::fmt::Debug;
pub(super) struct GraphPlugin;
impl Plugin for GraphPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<AudioGraphTemplate>()
.add_systems(
PreStartup,
(crate::context::initialize_context, insert_io, set_up_graph)
.chain()
.in_set(SeedlingStartupSystems::GraphSetup),
)
.add_systems(
Last,
add_default_transforms.before(crate::SeedlingSystems::Acquire),
)
.add_observer(connect_io::<StreamStartEvent>)
.add_observer(connect_io::<StreamRestartEvent>);
}
}
#[derive(Debug, SystemSet, PartialEq, Eq, Hash, Clone)]
pub enum SeedlingStartupSystems {
GraphSetup,
StreamInitialization,
}
#[derive(PoolLabel, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
pub struct SpatialPool;
fn add_default_transforms(
q: Query<
Entity,
(
With<crate::prelude::SamplePlayer>,
With<SpatialPool>,
Without<Transform>,
),
>,
mut commands: Commands,
) {
for entity in &q {
commands.entity(entity).insert(Transform::default());
}
}
#[derive(PoolLabel, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
pub struct MusicPool;
#[derive(NodeLabel, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
pub struct SoundEffectsBus;
#[derive(Debug, Default, Clone, Copy, Resource)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
pub enum AudioGraphTemplate {
#[default]
Game,
Minimal,
Empty,
}
fn insert_io(mut commands: Commands) {
commands.spawn((AudioGraphInput, PendingConnections::default()));
commands.spawn((AudioGraphOutput, PendingConnections::default()));
}
fn connect_io<E: Event>(
_: On<E>,
input: Query<Entity, With<AudioGraphInput>>,
output: Query<Entity, With<AudioGraphOutput>>,
mut commands: Commands,
mut context: ResMut<AudioContext>,
) -> Result {
context.with(|ctx| {
let node_id = ctx.graph_in_node_id();
let info = FirewheelNodeInfo::new(ctx.node_info(node_id).unwrap());
commands
.entity(input.single()?)
.insert((info, Name::new("Audio Input Node")))
.insert_if_new(FirewheelNode(node_id));
let node_id = ctx.graph_out_node_id();
let info = FirewheelNodeInfo::new(ctx.node_info(node_id).unwrap());
commands
.entity(output.single()?)
.insert((info, Name::new("Audio Output Node")))
.insert_if_new(FirewheelNode(node_id));
Ok(())
})
}
fn set_up_graph(mut commands: Commands, config: Res<AudioGraphTemplate>) {
use crate::prelude::*;
match *config {
AudioGraphTemplate::Game => {
commands
.spawn((MainBus, VolumeNode::default(), Name::new("Main Bus")))
.chain_node(LimiterNode::new(0.003, 0.15))
.connect(AudioGraphOutput);
commands.spawn((
SoundEffectsBus,
VolumeNode::default(),
Name::new("Sound Effects Bus"),
));
commands
.spawn((
crate::pool::dynamic::DynamicBus,
VolumeNode::default(),
Name::new("Dynamic Bus"),
))
.connect(SoundEffectsBus);
commands
.spawn((
SamplerPool(DefaultPool),
Name::new("Default Sampler Pool"),
sample_effects![VolumeNode::default()],
))
.connect(SoundEffectsBus);
commands
.spawn((
SamplerPool(SpatialPool),
Name::new("Spatial Sampler Pool"),
sample_effects![VolumeNode::default(), SpatialBasicNode::default()],
))
.connect(SoundEffectsBus);
commands.spawn((
SamplerPool(MusicPool),
Name::new("Music Sampler Pool"),
sample_effects![VolumeNode::default()],
));
}
AudioGraphTemplate::Minimal => {
commands
.spawn((MainBus, VolumeNode::default(), Name::new("Main Bus")))
.connect(AudioGraphOutput);
commands.spawn((
crate::pool::dynamic::DynamicBus,
VolumeNode::default(),
Name::new("Dynamic Bus"),
));
commands.spawn((
SamplerPool(DefaultPool),
Name::new("Default Sampler Pool"),
sample_effects![VolumeNode::default()],
));
}
AudioGraphTemplate::Empty => {}
}
}