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 self.cursor_time - start >= render_target {
57 break;
58 }
59 if iter_count > hard_iter_cap {
60 break;
61 }
62 }
63 return Ok(());
64 }
65
66 anyhow::bail!("Live background worker path should be handled earlier");
68 }
69 Value::Call { name, args } if name == "pass" => {
70 let mut interval_ms: u64 = 1000;
72 if let Some(Value::Number(n)) = args.get(0) {
73 interval_ms = (*n) as u64;
74 }
75 let interval_secs = (interval_ms as f32) / 1000.0;
76 let mut iter_count: usize = 0;
77 let hard_iter_cap: usize = 100_000;
78 let start = self.cursor_time;
79 let render_target = self.special_vars.total_duration.max(1.0);
80 loop {
81 let before_cursor = self.cursor_time;
82 let prev = self.suppress_beat_emit;
83 self.suppress_beat_emit = true;
84 let res = self.collect_events(body);
85 self.suppress_beat_emit = prev;
86 res?;
87 if self.break_flag {
89 self.break_flag = false;
90 break;
91 }
92 iter_count = iter_count.saturating_add(1);
93 let after_cursor = self.cursor_time;
94 if (after_cursor - before_cursor).abs() < f32::EPSILON {
95 self.cursor_time += interval_secs;
96 }
97 if self.cursor_time - start >= render_target {
98 break;
99 }
100 if iter_count > hard_iter_cap {
101 break;
102 }
103 }
104 Ok(())
105 }
106 Value::Null => {
107 let start_time = self.cursor_time;
109 let time_limit = 60.0_f32;
110 loop {
111 let before_cursor = self.cursor_time;
112 let prev = self.suppress_beat_emit;
113 self.suppress_beat_emit = true;
114 let res = self.collect_events(body);
115 self.suppress_beat_emit = prev;
116 res?;
117 if self.break_flag {
119 self.break_flag = false;
120 break;
121 }
122 if (self.cursor_time - before_cursor).abs() < f32::EPSILON {
123 break;
124 }
125 if self.cursor_time - start_time >= time_limit {
126 break;
127 }
128 }
129 Ok(())
130 }
131 other => anyhow::bail!(
132 "❌ Loop iterator must be a number, 'pass' or null, found: {:?}",
133 other
134 ),
135 }
136 }
137
138 pub fn execute_for(
139 &mut self,
140 variable: &str,
141 iterable: &Value,
142 body: &[Statement],
143 ) -> Result<()> {
144 let items = match iterable {
145 Value::Array(arr) => arr.clone(),
146 Value::Identifier(ident) => {
147 if let Some(Value::Array(arr)) = self.variables.get(ident) {
148 arr.clone()
149 } else {
150 anyhow::bail!("❌ For iterable '{}' must be an array", ident)
151 }
152 }
153 Value::Range { start, end } => {
154 let start_val = match start.as_ref() {
155 Value::Number(n) => *n as i32,
156 _ => anyhow::bail!("❌ Range start must be a number"),
157 };
158 let end_val = match end.as_ref() {
159 Value::Number(n) => *n as i32,
160 _ => anyhow::bail!("❌ Range end must be a number"),
161 };
162 (start_val..end_val)
163 .map(|i| Value::Number(i as f32))
164 .collect()
165 }
166 _ => anyhow::bail!(
167 "❌ For iterable must be an array or range, found: {:?}",
168 iterable
169 ),
170 };
171
172 for item in items.iter() {
173 let old_value = self.variables.insert(variable.to_string(), item.clone());
174 self.collect_events(body)?;
175 if self.break_flag {
177 self.break_flag = false;
178 match old_value {
180 Some(val) => {
181 self.variables.insert(variable.to_string(), val);
182 }
183 None => {
184 self.variables.remove(variable);
185 }
186 }
187 break;
188 }
189 match old_value {
190 Some(val) => {
191 self.variables.insert(variable.to_string(), val);
192 }
193 None => {
194 self.variables.remove(variable);
195 }
196 }
197 }
198
199 Ok(())
200 }
201}