Skip to main content

AudioGraphCompiler

Struct AudioGraphCompiler 

Source
pub struct AudioGraphCompiler;

Implementations§

Source§

impl AudioGraphCompiler

Source

pub fn compile( world: &World, source_root: ComponentId, ) -> Result<CompiledAudioGraph, CompileAudioGraphError>

Examples found in repository?
examples/audio-graph-example.rs (line 580)
6fn main() {
7    mittens_engine::example_support::ensure_model_assets();
8    utils::logger::init();
9
10    // Debug toggle: allow isolating stack overflows to audio vs. non-audio init.
11    // Set `CAT_AUDIO_EXAMPLE_DISABLE_AUDIO=1` to skip creating audio components and
12    // skip scheduling audio notes. The UI/text/cube visualization will still spawn.
13    let audio_enabled = std::env::var("CAT_AUDIO_EXAMPLE_DISABLE_AUDIO")
14        .ok()
15        .as_deref()
16        != Some("1");
17
18    // If set, we still build audio graph components but we don't start the CPAL output stream.
19    // This helps distinguish "audio graph / scheduling" issues from "CPAL backend" issues.
20    let audio_output_enabled = std::env::var("CAT_AUDIO_EXAMPLE_AUDIO_OUTPUT_OFF")
21        .ok()
22        .as_deref()
23        != Some("1");
24
25    println!("[audio-graph-example] start");
26
27    let world = engine::ecs::World::default();
28    let mut universe = engine::Universe::new(world);
29
30    println!("[audio-graph-example] universe created");
31
32    // Minimal scene with a camera so the window opens (copied from animation-example).
33    let clear = universe
34        .world
35        .add_component(engine::ecs::component::BackgroundColorComponent::new());
36    let clear_c = universe
37        .world
38        .add_component(engine::ecs::component::ColorComponent::rgba(
39            0.07, 0.07, 0.07, 1.0,
40        ));
41    let _ = universe.world.add_child(clear, clear_c);
42    universe.add(clear);
43
44    // Ambient light so unlit areas aren't pitch black.
45    // Keep it dark to match the background clear color.
46    // (User request) 2.5x brighter.
47    let ambient = universe
48        .world
49        .add_component(engine::ecs::component::AmbientLightComponent::rgb(
50            0.075, 0.075, 0.075,
51        ));
52    universe.add(ambient);
53
54    // Input-driven camera rig.
55    let input = universe
56        .world
57        .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
58    let rig_transform = universe.world.add_component(
59        engine::ecs::component::TransformComponent::new().with_position(2.0, 0.0, 7.0),
60    );
61    let input_mode = universe.world.add_component(
62        engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
63    );
64    let camera3d = universe.world.add_component(
65        engine::ecs::component::Camera3DComponent::new()
66            .with_far(250.0)
67            .with_fov(70.0),
68    );
69    let _ = universe.attach(input, input_mode);
70    let _ = universe.attach(input, rig_transform);
71    let _ = universe.attach(rig_transform, camera3d);
72
73    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
74    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
75    universe.add(input);
76
77    // Directional light (sun-ish). Note: the renderer interprets the node's world position
78    // as a direction vector (see DirectionalLightComponent docs).
79    let light_tx = universe.world.add_component(
80        engine::ecs::component::TransformComponent::new().with_position(0.2, 0.7, 1.0),
81    );
82    let light = universe.world.add_component(
83        engine::ecs::component::DirectionalLightComponent::new()
84            .with_color(1.0, 1.0, 1.0)
85            .with_intensity(0.35),
86    );
87    let _ = universe.attach(light_tx, light);
88    universe.add(light_tx);
89
90    // --- Background clouds (occluded + lit) ---
91    // Mirrors the vtuber example: use a BackgroundComponent stage so the cloud volume
92    // self-occludes and is lit, but renders as background.
93    let bg_root = universe.world.add_component(
94        engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
95    );
96    universe.add(bg_root);
97    let mut cloud_params = example_util::CloudRingParams::default();
98    cloud_params.cloud_count = 7;
99    cloud_params.radius = 22.0;
100    // Move the ring up by ~one cloud height. The cloud generator uses a ~4.0 unit
101    // vertical spread for puff offsets, so +4.0 is a good "one height" bump.
102    cloud_params.center_y = 6.0;
103    cloud_params.puffs_per_cloud = 26;
104    cloud_params.angle_jitter = 0.0;
105    cloud_params.high_y_probability = 0.0;
106    cloud_params.high_y_multiplier = 1.0;
107    cloud_params.seed = 0xA0_D1_0C_01u32;
108    example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
109
110    // ClockComponent sets global tempo.
111    if audio_enabled {
112        let clock = universe
113            .world
114            .add_component(engine::ecs::component::ClockComponent::new().with_bpm(128.0));
115        universe.add(clock);
116    }
117
118    if audio_enabled {
119        println!("[audio-graph-example] clock added");
120    } else {
121        println!("[audio-graph-example] audio disabled; skipping clock");
122    }
123
124    // Audio output + 2 oscillator sources.
125    let audio_out = if audio_enabled {
126        let audio_out_comp = if audio_output_enabled {
127            engine::ecs::component::AudioOutputComponent::new()
128        } else {
129            engine::ecs::component::AudioOutputComponent::off()
130        };
131        let audio_out = universe.world.add_component(audio_out_comp);
132        universe.add(audio_out);
133        Some(audio_out)
134    } else {
135        None
136    };
137
138    match (audio_enabled, audio_output_enabled) {
139        (false, _) => println!("[audio-graph-example] audio disabled; skipping audio output"),
140        (true, true) => println!("[audio-graph-example] audio output added (CPAL on)"),
141        (true, false) => println!("[audio-graph-example] audio output added (CPAL off)"),
142    }
143
144    // Track A: square lead.
145    let osc_a_comp =
146        if audio_enabled {
147            let osc_a = engine::ecs::component::AudioOscillator::square()
148                .with_frequency(110.0)
149                .with_amplitude(0.12)
150                .with_enabled(false);
151            let osc_a_comp = universe.world.add_component(
152                engine::ecs::component::AudioOscillatorComponent::single(osc_a),
153            );
154            if let Some(audio_out) = audio_out {
155                let _ = universe.attach(audio_out, osc_a_comp);
156            }
157            Some(osc_a_comp)
158        } else {
159            None
160        };
161
162    if audio_enabled {
163        println!("[audio-graph-example] track A created");
164    } else {
165        println!("[audio-graph-example] audio disabled; skipping track A");
166    }
167
168    // Effect tree A (single chain):
169    // osc_a
170    //   Gain
171    //     BandPass
172    //       Limiter
173    let mut bp_a_comp: Option<engine::ecs::ComponentId> = None;
174    if let Some(osc_a_comp) = osc_a_comp {
175        let gain_a = universe
176            .world
177            .add_component(engine::ecs::component::AudioGainComponent::new(3.2));
178        let bp_a = universe.world.add_component(
179            engine::ecs::component::AudioBandPassFilterComponent::new(120.0, 3.0, 0.40),
180        );
181        bp_a_comp = Some(bp_a);
182        let lim_a =
183            universe
184                .world
185                .add_component(engine::ecs::component::AudioLimiterComponent::new(
186                    4.0, 80.0, 0.90,
187                ));
188
189        let _ = universe.attach(osc_a_comp, gain_a);
190        let _ = universe.attach(gain_a, bp_a);
191        let _ = universe.attach(bp_a, lim_a);
192    }
193
194    if audio_enabled {
195        println!("[audio-graph-example] track A effects attached");
196    }
197
198    // --- Visual layout helpers (copied/adapted from animation-example) ---
199    fn spawn_text(
200        universe: &mut engine::Universe,
201        pos: (f32, f32, f32),
202        scale: f32,
203        wrap_cols: usize,
204        text: &str,
205    ) {
206        let tx = universe.world.add_component(
207            engine::ecs::component::TransformComponent::new()
208                .with_position(pos.0, pos.1, pos.2)
209                .with_scale(scale, scale, 1.0),
210        );
211        let t =
212            universe
213                .world
214                .add_component(engine::ecs::component::TextComponent::with_word_wrap(
215                    text, wrap_cols,
216                ));
217        let _ = universe.attach(tx, t);
218
219        // TextSystem looks for an immediate TextureFilteringComponent child.
220        let filtering = universe
221            .world
222            .add_component(engine::ecs::component::TextureFilteringComponent::nearest());
223        let _ = universe.attach(t, filtering);
224
225        // TextSystem also supports styling from immediate Emissive children.
226        let emissive = universe
227            .world
228            .add_component(engine::ecs::component::EmissiveComponent::on());
229        let _ = universe.attach(t, emissive);
230
231        universe.add(tx);
232    }
233
234    fn spawn_emissive_cube(
235        universe: &mut engine::Universe,
236        parent: engine::ecs::ComponentId,
237        pos: (f32, f32, f32),
238        scale: f32,
239        rgba: [f32; 4],
240    ) {
241        let tx = universe.world.add_component(
242            engine::ecs::component::TransformComponent::new()
243                .with_position(pos.0, pos.1, pos.2)
244                .with_scale(scale, scale, scale),
245        );
246        let r = universe
247            .world
248            .add_component(engine::ecs::component::RenderableComponent::cube());
249        let c = universe
250            .world
251            .add_component(engine::ecs::component::ColorComponent::rgba(
252                rgba[0], rgba[1], rgba[2], rgba[3],
253            ));
254        let e = universe
255            .world
256            .add_component(engine::ecs::component::EmissiveComponent::on());
257        let _ = universe.attach(parent, tx);
258        let _ = universe.attach(tx, r);
259        let _ = universe.attach(r, c);
260        let _ = universe.attach(r, e);
261    }
262
263    fn spawn_op_cube(
264        universe: &mut engine::Universe,
265        parent: engine::ecs::ComponentId,
266        pos: (f32, f32, f32),
267        scale: f32,
268        base_rgba: [f32; 4],
269    ) -> engine::ecs::ComponentId {
270        let tx = universe.world.add_component(
271            engine::ecs::component::TransformComponent::new()
272                .with_position(pos.0, pos.1, pos.2)
273                .with_scale(scale, scale, scale),
274        );
275        let r = universe
276            .world
277            .add_component(engine::ecs::component::RenderableComponent::cube());
278        let c = universe
279            .world
280            .add_component(engine::ecs::component::ColorComponent::rgba(
281                base_rgba[0],
282                base_rgba[1],
283                base_rgba[2],
284                base_rgba[3],
285            ));
286        let e = universe
287            .world
288            .add_component(engine::ecs::component::EmissiveComponent::on());
289
290        let _ = universe.attach(parent, tx);
291        let _ = universe.attach(tx, r);
292        let _ = universe.attach(r, c);
293        let _ = universe.attach(r, e);
294
295        tx
296    }
297
298    // --- HUD / lane (single track) ---
299    let lane_x = -2.6_f32;
300    let lane_title_z = -0.4_f32;
301    let lane_cfg_z = -0.4_f32;
302    let lane_pat_z = -1.3_f32;
303    let lane_graph_z = -1.15_f32;
304
305    // Content for pattern/chain/graph starts at x=0; keep section labels aligned there.
306    // This also keeps them from overlapping the config block (which is anchored at lane_x).
307    let lane_labels_x = lane_x + 1.6_f32;
308
309    let track_a_y = 0.9_f32;
310
311    spawn_text(
312        &mut universe,
313        (lane_x, track_a_y + 0.55, lane_title_z),
314        0.09,
315        42,
316        "Track A: AudioOscillator::square()",
317    );
318    spawn_text(
319        &mut universe,
320        (lane_x, track_a_y + 0.25, lane_cfg_z),
321        0.07,
322        48,
323        "oscillators=1\nfrequency_hz=110.0\namplitude=0.12\nenabled=false\nlookahead=0.10s",
324    );
325
326    let viz_root = universe.world.add_component(
327        engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
328    );
329    universe.add(viz_root);
330
331    println!("[audio-graph-example] viz root added");
332
333    // Small identifier cubes next to titles.
334    spawn_emissive_cube(
335        &mut universe,
336        viz_root,
337        (lane_x - 0.28, track_a_y + 0.55, lane_title_z),
338        0.16,
339        [0.85, 0.40, 1.00, 1.0],
340    );
341
342    // Pattern roots so we can reset via one SetColor action.
343    let track_a_pattern_root = universe.world.add_component(
344        engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
345    );
346    let _ = universe.attach(viz_root, track_a_pattern_root);
347
348    // Labels for pattern/chain/graph sections.
349    spawn_text(
350        &mut universe,
351        (lane_labels_x, track_a_y, lane_title_z),
352        0.06,
353        42,
354        "pattern",
355    );
356    spawn_text(
357        &mut universe,
358        (lane_labels_x, track_a_y - 0.40, lane_title_z),
359        0.06,
360        42,
361        "graph",
362    );
363
364    // --- Processing chain + graph visualization (compiled graph → cubes + labels) ---
365    use engine::ecs::system::audio_graph_compiler::{
366        AudioGraphCompiler, AudioGraphNode, AudioGraphNodeKind,
367    };
368
369    fn effect_grey_for_depth(depth: usize) -> f32 {
370        // depth=1 => medium grey; deeper => lighter, capped.
371        let base = 0.55;
372        let step = 0.13;
373        let d = depth.saturating_sub(1) as f32;
374        (base + step * d).min(0.92)
375    }
376
377    fn node_rgba(node: &AudioGraphNode, depth: usize) -> [f32; 4] {
378        match node.kind {
379            AudioGraphNodeKind::OscillatorSource { .. } => [1.0, 0.78, 0.22, 1.0],
380            _ => {
381                let g = effect_grey_for_depth(depth);
382                [g, g, g, 1.0]
383            }
384        }
385    }
386
387    fn node_label(node: &AudioGraphNode) -> String {
388        match &node.kind {
389            AudioGraphNodeKind::OscillatorSource { voices } => {
390                format!("OscillatorSource voices={voices}")
391            }
392            AudioGraphNodeKind::Gain { gain } => {
393                format!("Gain gain={gain:.3}")
394            }
395            AudioGraphNodeKind::LowPass {
396                cutoff_hz,
397                resonance,
398            } => {
399                format!("LowPass cutoff={cutoff_hz:.1}Hz res={resonance:.3}")
400            }
401            AudioGraphNodeKind::BandPass {
402                center_hz,
403                bandwidth_octaves,
404                resonance,
405            } => {
406                format!(
407                    "BandPass center={center_hz:.1}Hz bw={bandwidth_octaves:.3}oct res={resonance:.3}"
408                )
409            }
410            AudioGraphNodeKind::HighPass {
411                cutoff_hz,
412                resonance,
413            } => {
414                format!("HighPass cutoff={cutoff_hz:.1}Hz res={resonance:.3}")
415            }
416            AudioGraphNodeKind::Limiter {
417                attack_ms,
418                release_ms,
419                threshold,
420            } => {
421                format!("Limiter atk={attack_ms:.1}ms rel={release_ms:.1}ms thr={threshold:.3}")
422            }
423            AudioGraphNodeKind::ClipSource => "ClipSource".to_string(),
424        }
425    }
426
427    fn compute_layout(
428        node: &AudioGraphNode,
429        depth: usize,
430        x_cursor: &mut i32,
431        out: &mut std::collections::HashMap<*const AudioGraphNode, (i32, usize)>,
432    ) -> i32 {
433        // Depth-only layout:
434        // - keep children directly below their parent on X
435        // - use Z sibling offsets (in spawn_graph_tree) to show branching
436        let _ = x_cursor;
437        for ch in node.children.iter() {
438            let _ = compute_layout(ch, depth + 1, x_cursor, out);
439        }
440
441        let my_x = 0;
442        out.insert(node as *const AudioGraphNode, (my_x, depth));
443        my_x
444    }
445
446    fn spawn_graph_tree(
447        universe: &mut engine::Universe,
448        parent: engine::ecs::ComponentId,
449        node: &AudioGraphNode,
450        bp_component: Option<engine::ecs::ComponentId>,
451        bp_label_out: &mut Option<engine::ecs::ComponentId>,
452        origin: (f32, f32, f32),
453        layout: &std::collections::HashMap<*const AudioGraphNode, (i32, usize)>,
454        dx: f32,
455        dy: f32,
456        cube_scale: f32,
457        sibling_index: usize,
458        sibling_count: usize,
459    ) {
460        let Some((x_unit, depth)) = layout.get(&(node as *const AudioGraphNode)).copied() else {
461            return;
462        };
463
464        let x = origin.0 + (x_unit as f32) * dx;
465        let y = origin.1 - (depth as f32) * dy;
466
467        // Push siblings “behind” each other along Z to reduce overlap between
468        // a sibling's cube and another node's label.
469        let dz_sibling = 0.18;
470        let z = origin.2 - (sibling_index as f32) * dz_sibling;
471
472        // Keep text slightly in front of its cube.
473        let z_text = z + 0.03;
474
475        let rgba = node_rgba(node, depth);
476        let cube_tx = spawn_op_cube(universe, parent, (x, y, z), cube_scale, rgba);
477        let _ = cube_tx;
478
479        // Label next to the cube.
480        let label = node_label(node);
481        let tx = universe.world.add_component(
482            engine::ecs::component::TransformComponent::new()
483                .with_position(x + 0.14, y + 0.02, z_text)
484                .with_scale(0.06, 0.06, 1.0),
485        );
486        let t =
487            universe
488                .world
489                .add_component(engine::ecs::component::TextComponent::with_word_wrap(
490                    &label, 25,
491                ));
492        let _ = universe.attach(parent, tx);
493        let _ = universe.attach(tx, t);
494
495        if bp_component == Some(node.component) {
496            *bp_label_out = Some(t);
497        }
498
499        let filtering = universe
500            .world
501            .add_component(engine::ecs::component::TextureFilteringComponent::nearest());
502        let _ = universe.attach(t, filtering);
503
504        let emissive = universe
505            .world
506            .add_component(engine::ecs::component::EmissiveComponent::on());
507        let _ = universe.attach(t, emissive);
508
509        universe.add(tx);
510
511        // Mix/branch label if branching.
512        if node.children.len() > 1 {
513            let mut weights: Vec<f32> = Vec::with_capacity(node.children.len());
514            for i in 0..node.children.len() {
515                let w = node
516                    .mix
517                    .as_ref()
518                    .map(|m| m.weights.get(i).copied().unwrap_or(1.0))
519                    .unwrap_or(1.0);
520                weights.push(w);
521            }
522            let mix_label = if node.mix.is_some() {
523                format!("Mix weights={weights:?}")
524            } else {
525                format!("Mix <implicit> w={weights:?}")
526            };
527            let mix_tx = universe.world.add_component(
528                engine::ecs::component::TransformComponent::new()
529                    .with_position(x + 0.14, y - 0.11, z_text)
530                    .with_scale(0.05, 0.05, 1.0),
531            );
532            let mix_t = universe.world.add_component(
533                engine::ecs::component::TextComponent::with_word_wrap(&mix_label, 25),
534            );
535            let _ = universe.attach(parent, mix_tx);
536            let _ = universe.attach(mix_tx, mix_t);
537
538            let filtering = universe
539                .world
540                .add_component(engine::ecs::component::TextureFilteringComponent::nearest());
541            let _ = universe.attach(mix_t, filtering);
542
543            let emissive = universe
544                .world
545                .add_component(engine::ecs::component::EmissiveComponent::on());
546            let _ = universe.attach(mix_t, emissive);
547
548            universe.add(mix_tx);
549        }
550
551        let child_count = node.children.len().max(1);
552        for (i, ch) in node.children.iter().enumerate() {
553            // Each node's children form a sibling group; offset them in Z.
554            // For the root node, sibling_index is 0.
555            let _ = sibling_count;
556            spawn_graph_tree(
557                universe,
558                parent,
559                ch,
560                bp_component,
561                bp_label_out,
562                origin,
563                layout,
564                dx,
565                dy,
566                cube_scale,
567                i,
568                child_count,
569            );
570        }
571    }
572
573    // Compile + display chain and graph for the track.
574    let compiled_a = if audio_enabled {
575        println!("[audio-graph-example] compiling graph...");
576        let Some(osc_a_comp) = osc_a_comp else {
577            panic!("audio_enabled but track A was not created");
578        };
579        let compiled_a =
580            AudioGraphCompiler::compile(&universe.world, osc_a_comp).expect("compile A");
581        println!("[audio-graph-example] graph compiled");
582        Some(compiled_a)
583    } else {
584        println!("[audio-graph-example] audio disabled; skipping graph compilation");
585        None
586    };
587
588    // Full compiled graph visualization (tree layout + mix labels).
589    let mut bp_graph_label_text: Option<engine::ecs::ComponentId> = None;
590    if let Some(compiled_a) = &compiled_a {
591        let mut x_cursor = 0;
592        let mut layout: std::collections::HashMap<*const AudioGraphNode, (i32, usize)> =
593            std::collections::HashMap::new();
594        let _root_x = compute_layout(&compiled_a.root, 0, &mut x_cursor, &mut layout);
595
596        // With depth-only X layout, we keep the tree centered at x=0.
597        // Pull the graph up now that we don't show the separate chain view.
598        let origin = (0.0, track_a_y - 0.40, lane_graph_z);
599
600        spawn_graph_tree(
601            &mut universe,
602            viz_root,
603            &compiled_a.root,
604            bp_a_comp,
605            &mut bp_graph_label_text,
606            origin,
607            &layout,
608            0.45,
609            0.28,
610            0.08,
611            0,
612            1,
613        );
614    } else {
615        spawn_text(
616            &mut universe,
617            (0.0, track_a_y - 0.40, lane_graph_z),
618            0.06,
619            60,
620            "(audio disabled)",
621        );
622    }
623
624    // Animation with 16 keyframes drives scheduled notes + pattern highlights.
625    let anim = universe
626        .world
627        .add_component(engine::ecs::component::AnimationComponent::new());
628
629    let dur_a = 0.85_f32;
630    let beat_spacing = 0.38_f32;
631
632    let a_dark = [0.18, 0.08, 0.22, 1.0];
633    let a_bright = [0.85, 0.40, 1.00, 1.0];
634
635    for i in 0..16 {
636        let kf_beat = i as f64;
637        let kf = universe
638            .world
639            .add_component(engine::ecs::component::KeyframeComponent::new(kf_beat));
640
641        let _ = universe.attach(anim, kf);
642
643        // Scheduled audio note (sample-accurate via lookahead).
644        // Pulse every beat.
645        if audio_enabled {
646            let Some(osc_a_comp) = osc_a_comp else {
647                panic!("audio_enabled but track A was not created");
648            };
649
650            let note_a = engine::ecs::component::MusicNote::c(1, dur_a).with_velocity(0.80);
651
652            let act_a = universe
653                .world
654                .add_component(engine::ecs::component::ActionComponent::new(
655                    engine::ecs::IntentValue::AudioSchedulePlay {
656                        component_ids: vec![osc_a_comp],
657                        beat_offset: 0.0,
658                        beat_context: None,
659                        note: Some(note_a),
660                        gain: None,
661                        rate: None,
662                        duration: None,
663                    },
664                ));
665
666            let _ = universe.attach(kf, act_a);
667        }
668
669        // Keyframed band-pass center.
670        // This is an immediate parameter update applied RT-side (no graph rebuild).
671        if audio_enabled {
672            if let Some(bp_a_comp) = bp_a_comp {
673                let t = (i as f32) / 15.0;
674                let center_hz = 10.0 + t * (1000.0 - 10.0);
675
676                let bp_center =
677                    universe
678                        .world
679                        .add_component(engine::ecs::component::ActionComponent::new(
680                            engine::ecs::IntentValue::AudioBandPassSetCenterHz {
681                                component_ids: vec![bp_a_comp],
682                                center_hz,
683                            },
684                        ));
685                let _ = universe.attach(kf, bp_center);
686
687                // Update the BandPass node label in the graph visualization.
688                if let Some(text_id) = bp_graph_label_text {
689                    let label =
690                        universe
691                            .world
692                            .add_component(engine::ecs::component::ActionComponent::new(
693                                engine::ecs::IntentValue::SetText {
694                                    component_ids: vec![text_id],
695                                    text: format!("BandPass center={center_hz:.1}Hz"),
696                                },
697                            ));
698                    let _ = universe.attach(kf, label);
699                }
700            }
701        }
702
703        // Visualization reset per keyframe.
704        let reset_a = universe
705            .world
706            .add_component(engine::ecs::component::ActionComponent::new(
707                engine::ecs::IntentValue::SetColor {
708                    component_ids: vec![track_a_pattern_root],
709                    rgba: a_dark,
710                },
711            ));
712        let _ = universe.attach(kf, reset_a);
713
714        // Pattern cube per step for each track.
715        let x = (kf_beat as f32) * beat_spacing;
716        let cube_a = spawn_op_cube(
717            &mut universe,
718            track_a_pattern_root,
719            (x, track_a_y, lane_pat_z),
720            0.10,
721            a_dark,
722        );
723
724        let bright_a = universe
725            .world
726            .add_component(engine::ecs::component::ActionComponent::new(
727                engine::ecs::IntentValue::SetColor {
728                    component_ids: vec![cube_a],
729                    rgba: a_bright,
730                },
731            ));
732        let _ = universe.attach(kf, bright_a);
733    }
734    universe.add(anim);
735
736    println!("[audio-graph-example] animation created");
737
738    // Keep window open.
739    println!("[audio-graph-example] processing commands...");
740    universe.systems.process_commands(
741        &mut universe.world,
742        &mut universe.visuals,
743        &mut universe.render_assets,
744        &mut universe.command_queue,
745    );
746
747    println!("[audio-graph-example] commands processed; launching window");
748
749    engine::Windowing::run_app(universe).expect("Windowing failed");
750}

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more