Skip to main content

khora_data/ecs/systems/
audio_playback_writeback.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Drains the [`AudioPlaybackWriteback`](crate::flow::AudioPlaybackWriteback)
16//! slot the `SpatialMixingLane` wrote into during the CLAD descent and
17//! applies the per-source playback-state updates back to `AudioSource`
18//! components.
19//!
20//! This system is the canonical Lane → Deck → DataSystem writeback
21//! pattern: the lane is a pure strategy that publishes into the typed
22//! `OutputDeck`; the engine threads the same deck through to the
23//! `Maintenance`-phase `DataSystem`s; this system drains the slot and
24//! mutates the World. No subsystem-specific code lives in the engine.
25
26use khora_core::lane::OutputDeck;
27use khora_core::Runtime;
28
29use crate::ecs::{AudioSource, DataSystemRegistration, TickPhase, World};
30use crate::flow::AudioPlaybackWriteback;
31
32fn audio_playback_writeback(world: &mut World, _runtime: &Runtime, deck: &mut OutputDeck) {
33    let drained = deck.take::<AudioPlaybackWriteback>();
34    if drained.updates.is_empty() {
35        return;
36    }
37
38    // Apply each update to its source. The lane uses the entity's index
39    // (its position in the projected `AudioView::sources`) as the
40    // writeback key, matching the order of `world.query::<&AudioSource>`
41    // — so we match by linear order here too.
42    let mut iter = world.query_mut::<&mut AudioSource>();
43    for update in drained.updates {
44        if let Some(source) = iter.next() {
45            source.state = update.new_state;
46        }
47    }
48}
49
50inventory::submit! {
51    DataSystemRegistration {
52        name: "audio_playback_writeback",
53        phase: TickPhase::Maintenance,
54        run: audio_playback_writeback,
55        order_hint: 10, // After physics_world_writeback (default 0).
56        runs_after: &[],
57    }
58}