devalang_wasm/engine/audio/interpreter/statements/
loop_.rs1use crate::engine::audio::interpreter::driver::AudioInterpreter;
2use crate::language::syntax::ast::{Statement, Value};
3use anyhow::Result;
4use std::sync::atomic::AtomicUsize;
5
6static BG_WORKER_COUNTER: AtomicUsize = AtomicUsize::new(1);
7
8impl AudioInterpreter {
9 pub fn execute_loop(&mut self, count: &Value, body: &[Statement]) -> Result<()> {
10 match count {
11 Value::Number(n) => {
12 let loop_count = (*n) as usize;
13 for _ in 0..loop_count {
14 self.collect_events(body)?;
15 if self.break_flag {
17 self.break_flag = false;
18 break;
19 }
20 }
21 Ok(())
22 }
23 Value::Identifier(ident) if ident == "pass" => {
24 if self.background_event_tx.is_none() {
26 let beat_secs = if self.bpm > 0.0 { 60.0 / self.bpm } else { 1.0 };
27 let interval_secs = beat_secs.max(0.001);
28 let mut iter_count: usize = 0;
29 let hard_iter_cap: usize = 100_000;
30 let start = self.cursor_time;
31 let render_target = self.special_vars.total_duration.max(1.0);
32
33 loop {
34 let before_cursor = self.cursor_time;
35 let prev = self.suppress_beat_emit;
39 self.suppress_beat_emit = true;
40 let res = self.collect_events(body);
41 self.suppress_beat_emit = prev;
43 res?;
44 if self.break_flag {
46 self.break_flag = false;
47 break;
48 }
49 iter_count = iter_count.saturating_add(1);
50 let after_cursor = self.cursor_time;
51 if (after_cursor - before_cursor).abs() < f32::EPSILON {
54 self.cursor_time += interval_secs;
55 }
56 if let Some(deadline) = self.schedule_deadline {
58 if self.cursor_time >= deadline {
59 break;
60 }
61 }
62 if self.cursor_time - start >= render_target {
63 break;
64 }
65 if iter_count > hard_iter_cap {
66 break;
67 }
68 }
69 return Ok(());
70 }
71
72 anyhow::bail!("Live background worker path should be handled earlier");
74 }
75 Value::Call { name, args } if name == "pass" => {
76 let mut interval_ms: u64 = 1000;
78 if let Some(Value::Number(n)) = args.get(0) {
79 interval_ms = (*n) as u64;
80 }
81 let interval_secs = (interval_ms as f32) / 1000.0;
82 let mut iter_count: usize = 0;
83 let hard_iter_cap: usize = 100_000;
84 let start = self.cursor_time;
85 let render_target = self.special_vars.total_duration.max(1.0);
86 loop {
87 let before_cursor = self.cursor_time;
88 let prev = self.suppress_beat_emit;
89 self.suppress_beat_emit = true;
90 let res = self.collect_events(body);
91 self.suppress_beat_emit = prev;
92 res?;
93 if self.break_flag {
95 self.break_flag = false;
96 break;
97 }
98 iter_count = iter_count.saturating_add(1);
99 let after_cursor = self.cursor_time;
100 if (after_cursor - before_cursor).abs() < f32::EPSILON {
101 self.cursor_time += interval_secs;
102 }
103 if let Some(deadline) = self.schedule_deadline {
105 if self.cursor_time >= deadline {
106 break;
107 }
108 }
109 if self.cursor_time - start >= render_target {
110 break;
111 }
112 if iter_count > hard_iter_cap {
113 break;
114 }
115 }
116 Ok(())
117 }
118 Value::Duration(duration) => {
119 let interval_secs = self.duration_to_seconds(duration)?;
121 let mut iter_count: usize = 0;
122 let hard_iter_cap: usize = 100_000;
123 let start = self.cursor_time;
124 let render_target = self.special_vars.total_duration.max(1.0);
125 loop {
126 let before_cursor = self.cursor_time;
127 let prev = self.suppress_beat_emit;
128 self.suppress_beat_emit = true;
129 let res = self.collect_events(body);
130 self.suppress_beat_emit = prev;
131 res?;
132 if self.break_flag {
134 self.break_flag = false;
135 break;
136 }
137 iter_count = iter_count.saturating_add(1);
138 let after_cursor = self.cursor_time;
139 if (after_cursor - before_cursor).abs() < f32::EPSILON {
140 self.cursor_time += interval_secs;
141 }
142 if let Some(deadline) = self.schedule_deadline {
144 if self.cursor_time >= deadline {
145 break;
146 }
147 }
148 if self.cursor_time - start >= render_target {
149 break;
150 }
151 if iter_count > hard_iter_cap {
152 break;
153 }
154 }
155 Ok(())
156 }
157 Value::Null => {
158 let start_time = self.cursor_time;
160 let time_limit = 60.0_f32;
161 loop {
162 let before_cursor = self.cursor_time;
163 let prev = self.suppress_beat_emit;
164 self.suppress_beat_emit = true;
165 let res = self.collect_events(body);
166 self.suppress_beat_emit = prev;
167 res?;
168 if self.break_flag {
170 self.break_flag = false;
171 break;
172 }
173 if (self.cursor_time - before_cursor).abs() < f32::EPSILON {
174 break;
175 }
176 if let Some(deadline) = self.schedule_deadline {
178 if self.cursor_time >= deadline {
179 break;
180 }
181 }
182 if self.cursor_time - start_time >= time_limit {
183 break;
184 }
185 }
186 Ok(())
187 }
188 other => anyhow::bail!(
189 "❌ Loop iterator must be a number, 'pass' or null, found: {:?}",
190 other
191 ),
192 }
193 }
194
195 pub fn execute_for(
196 &mut self,
197 variable: &str,
198 iterable: &Value,
199 body: &[Statement],
200 ) -> Result<()> {
201 let items = match iterable {
202 Value::Array(arr) => arr.clone(),
203 Value::Identifier(ident) => {
204 if let Some(Value::Array(arr)) = self.variables.get(ident) {
205 arr.clone()
206 } else {
207 anyhow::bail!("❌ For iterable '{}' must be an array", ident)
208 }
209 }
210 Value::Range { start, end } => {
211 let start_val = match start.as_ref() {
212 Value::Number(n) => *n as i32,
213 _ => anyhow::bail!("❌ Range start must be a number"),
214 };
215 let end_val = match end.as_ref() {
216 Value::Number(n) => *n as i32,
217 _ => anyhow::bail!("❌ Range end must be a number"),
218 };
219 (start_val..end_val)
220 .map(|i| Value::Number(i as f32))
221 .collect()
222 }
223 _ => anyhow::bail!(
224 "❌ For iterable must be an array or range, found: {:?}",
225 iterable
226 ),
227 };
228
229 for item in items.iter() {
230 let old_value = self.variables.insert(variable.to_string(), item.clone());
231 self.collect_events(body)?;
232 if self.break_flag {
234 self.break_flag = false;
235 match old_value {
237 Some(val) => {
238 self.variables.insert(variable.to_string(), val);
239 }
240 None => {
241 self.variables.remove(variable);
242 }
243 }
244 break;
245 }
246 match old_value {
247 Some(val) => {
248 self.variables.insert(variable.to_string(), val);
249 }
250 None => {
251 self.variables.remove(variable);
252 }
253 }
254 }
255
256 Ok(())
257 }
258}