pub struct AudioGraphCompiler;Implementations§
Source§impl AudioGraphCompiler
impl AudioGraphCompiler
Sourcepub fn compile(
world: &World,
source_root: ComponentId,
) -> Result<CompiledAudioGraph, CompileAudioGraphError>
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§
impl Freeze for AudioGraphCompiler
impl RefUnwindSafe for AudioGraphCompiler
impl Send for AudioGraphCompiler
impl Sync for AudioGraphCompiler
impl Unpin for AudioGraphCompiler
impl UnsafeUnpin for AudioGraphCompiler
impl UnwindSafe for AudioGraphCompiler
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
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>
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)
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)
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.