audiobox/mixer/
virtualization.rs1use super::{Mixer, TAILS};
3
4pub const VIRTUAL_VOICE_QUANTUM: u64 = 32;
6pub const MAX_LOGICAL_VOICES: usize = 4096;
8
9#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct VirtualVoices {
12 pub capacity: usize,
14 pub threshold: f32,
19}
20impl Default for VirtualVoices {
21 fn default() -> Self {
22 Self {
23 capacity: 256,
24 threshold: 0.0001,
25 }
26 }
27}
28
29struct Candidate {
30 slot: usize,
31 priority: u8,
32 level: f64,
33 order: u64,
34}
35pub(super) struct Scheduler {
36 pub settings: VirtualVoices,
37 pub dirty: bool,
38 quantum: Option<u64>,
39 candidates: Vec<Candidate>,
40}
41impl Scheduler {
42 pub fn new(settings: VirtualVoices, slots: usize) -> Self {
43 Self {
44 settings,
45 dirty: true,
46 quantum: None,
47 candidates: Vec::with_capacity(slots),
48 }
49 }
50}
51impl Mixer {
52 pub fn physical_voices(&self) -> usize {
54 if self.buses[0].paused {
55 return 0;
56 }
57 self.voices
58 .iter()
59 .flatten()
60 .filter(|v| !v.virtualized && !v.paused() && !self.buses[v.bus].paused)
61 .count()
62 }
63 pub fn virtual_voice_count(&self) -> usize {
65 if self.buses[0].paused {
66 return 0;
67 }
68 self.voices
69 .iter()
70 .flatten()
71 .filter(|v| v.virtualized && !v.paused() && !self.buses[v.bus].paused)
72 .count()
73 }
74 pub(super) fn select_physical(&mut self, frame: u64) {
75 let quantum = frame / VIRTUAL_VOICE_QUANTUM;
76 let Some(scheduler) = &self.virtualization else {
77 return;
78 };
79 if !scheduler.dirty && scheduler.quantum == Some(quantum) {
80 return;
81 }
82 for voice in self.voices.iter_mut().flatten() {
85 voice.settle();
86 }
87 self.retire_done();
88 let scheduler = self.virtualization.as_mut().unwrap();
89 scheduler.dirty = false;
90 scheduler.quantum = Some(quantum);
91 scheduler.candidates.clear();
92 let end = (frame - frame % VIRTUAL_VOICE_QUANTUM) + VIRTUAL_VOICE_QUANTUM - 1;
93 for (slot, voice) in self.voices.iter_mut().enumerate() {
94 let Some(voice) = voice else { continue };
95 if !voice.live() {
96 continue;
97 }
98 voice.virtualized = true;
99 if self.buses[0].paused || self.buses[voice.bus].paused || voice.paused() {
100 continue;
101 }
102 let level = voice.source_peak * voice.level_bound(frame, end);
103 if level > scheduler.settings.threshold as f64 {
104 scheduler.candidates.push(Candidate {
105 slot,
106 priority: voice.priority,
107 level,
108 order: voice.order,
109 });
110 }
111 }
112 scheduler.candidates.sort_unstable_by(|a, b| {
113 b.priority
114 .cmp(&a.priority)
115 .then(b.level.total_cmp(&a.level))
116 .then(a.order.cmp(&b.order))
117 });
118 let physical = scheduler.candidates.len().min(self.max_voices);
119 for candidate in &scheduler.candidates[..physical] {
120 self.voices[candidate.slot].as_mut().unwrap().virtualized = false;
121 }
122 scheduler.candidates.clear();
125 for (slot, voice) in self.voices.iter().enumerate() {
126 if let Some(voice) = voice.as_deref().filter(|v| !v.live() && !v.virtualized) {
127 scheduler.candidates.push(Candidate {
128 slot,
129 priority: voice.priority,
130 level: 0.0,
131 order: voice.order,
132 });
133 }
134 }
135 scheduler
136 .candidates
137 .sort_unstable_by_key(|v| std::cmp::Reverse(v.order));
138 for candidate in scheduler
139 .candidates
140 .iter()
141 .skip(self.max_voices + TAILS - physical)
142 {
143 self.voices[candidate.slot].as_mut().unwrap().virtualized = true;
144 }
145 }
146}
147
148pub(super) fn placement(
149 spatial: &super::SpatialConfig,
150 listener: &super::Listener,
151 emitter: &super::Emitter,
152 layout: super::OutputLayout,
153 cull: bool,
154) -> super::Placement {
155 let mut p = spatial.place_validated(listener, emitter, layout);
156 if cull {
157 let squared = (0..3)
158 .map(|i| {
159 let d = emitter.position[i] as f64 - listener.position[i] as f64;
160 d * d
161 })
162 .sum::<f64>();
163 let max = emitter.attenuation.max_distance as f64;
164 if squared >= max * max {
165 p.distance_gain = 0.0;
166 }
167 }
168 p
169}