@devaloop/devalang 0.0.1-alpha.16-hotfix.1 → 0.0.1-alpha.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +6 -10
  3. package/.github/workflows/ci.yml +19 -8
  4. package/Cargo.toml +18 -2
  5. package/README.md +80 -33
  6. package/docs/CHANGELOG.md +56 -0
  7. package/docs/ROADMAP.md +6 -3
  8. package/examples/index.deva +52 -35
  9. package/out-tsc/bin/index.d.ts +2 -0
  10. package/out-tsc/core/functions/index.d.ts +37 -0
  11. package/out-tsc/core/functions/index.js +76 -0
  12. package/out-tsc/core/index.d.ts +6 -0
  13. package/out-tsc/core/index.js +22 -0
  14. package/out-tsc/core/types/index.d.ts +4 -0
  15. package/out-tsc/core/types/index.js +20 -0
  16. package/out-tsc/core/types/plugin.d.ts +18 -0
  17. package/out-tsc/core/types/plugin.js +2 -0
  18. package/out-tsc/core/types/result.d.ts +27 -0
  19. package/out-tsc/core/types/result.js +2 -0
  20. package/out-tsc/core/types/statement.d.ts +106 -0
  21. package/out-tsc/core/types/statement.js +2 -0
  22. package/out-tsc/core/types/value.d.ts +43 -0
  23. package/out-tsc/core/types/value.js +2 -0
  24. package/out-tsc/index.d.ts +7 -0
  25. package/out-tsc/index.js +41 -2
  26. package/out-tsc/pkg/devalang_core.d.ts +7 -0
  27. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  28. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  29. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  30. package/out-tsc/scripts/postinstall.d.ts +1 -0
  31. package/out-tsc/scripts/postinstall.js +33 -23
  32. package/out-tsc/scripts/version/bump.d.ts +1 -0
  33. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  34. package/out-tsc/scripts/version/index.d.ts +1 -0
  35. package/out-tsc/scripts/version/sync.d.ts +1 -0
  36. package/package.json +16 -4
  37. package/project-version.json +3 -3
  38. package/rust/cli/bank/api.rs +122 -0
  39. package/rust/cli/bank/commands.rs +275 -0
  40. package/rust/cli/bank/mod.rs +29 -0
  41. package/rust/cli/build/commands.rs +97 -0
  42. package/rust/cli/build/mod.rs +2 -0
  43. package/rust/cli/build/process.rs +146 -0
  44. package/rust/cli/{check.rs → check/mod.rs} +18 -31
  45. package/rust/cli/discover/commands.rs +253 -0
  46. package/rust/cli/discover/config.rs +111 -0
  47. package/rust/cli/discover/fs.rs +19 -0
  48. package/rust/cli/discover/install.rs +103 -0
  49. package/rust/cli/discover/metadata.rs +48 -0
  50. package/rust/cli/discover/mod.rs +5 -0
  51. package/rust/cli/{init.rs → init/commands.rs} +88 -87
  52. package/rust/cli/init/mod.rs +1 -0
  53. package/rust/{installer → cli/install}/addon.rs +5 -9
  54. package/rust/cli/install/bank.rs +53 -0
  55. package/rust/cli/{install.rs → install/commands.rs} +9 -9
  56. package/rust/{installer → cli/install}/mod.rs +2 -3
  57. package/rust/cli/install/plugin.rs +61 -0
  58. package/rust/cli/{login.rs → login/commands.rs} +8 -11
  59. package/rust/cli/login/mod.rs +1 -0
  60. package/rust/cli/mod.rs +2 -3
  61. package/rust/cli/{driver.rs → parser.rs} +19 -2
  62. package/rust/cli/play/commands.rs +324 -0
  63. package/rust/cli/play/io.rs +17 -0
  64. package/rust/cli/play/mod.rs +5 -0
  65. package/rust/cli/play/process.rs +150 -0
  66. package/rust/cli/play/realtime.rs +91 -0
  67. package/rust/cli/play/utils.rs +23 -0
  68. package/rust/cli/telemetry/commands.rs +22 -0
  69. package/rust/cli/telemetry/event_creator.rs +80 -0
  70. package/rust/cli/telemetry/mod.rs +3 -0
  71. package/rust/cli/telemetry/send.rs +51 -0
  72. package/rust/cli/{template.rs → template/commands.rs} +1 -1
  73. package/rust/cli/template/mod.rs +1 -0
  74. package/rust/cli/{update.rs → update/commands.rs} +6 -6
  75. package/rust/cli/update/mod.rs +1 -0
  76. package/rust/config/driver.rs +57 -72
  77. package/rust/config/mod.rs +1 -2
  78. package/rust/config/ops.rs +26 -0
  79. package/rust/config/settings.rs +60 -50
  80. package/rust/core/audio/engine/helpers.rs +146 -0
  81. package/rust/core/audio/engine/mod.rs +7 -0
  82. package/rust/core/audio/engine/sample.rs +298 -0
  83. package/rust/core/audio/engine/synth.rs +310 -0
  84. package/rust/core/audio/evaluator.rs +15 -12
  85. package/rust/core/audio/interpreter/arrow_call.rs +99 -24
  86. package/rust/core/audio/interpreter/call.rs +81 -60
  87. package/rust/core/audio/interpreter/condition.rs +3 -2
  88. package/rust/core/audio/interpreter/driver.rs +206 -151
  89. package/rust/core/audio/interpreter/let_.rs +1 -1
  90. package/rust/core/audio/interpreter/load.rs +2 -1
  91. package/rust/core/audio/interpreter/loop_.rs +7 -6
  92. package/rust/core/audio/interpreter/sleep.rs +2 -1
  93. package/rust/core/audio/interpreter/spawn.rs +45 -57
  94. package/rust/core/audio/interpreter/tempo.rs +31 -10
  95. package/rust/core/audio/interpreter/trigger.rs +2 -2
  96. package/rust/core/audio/loader/trigger.rs +4 -7
  97. package/rust/core/audio/player.rs +6 -0
  98. package/rust/core/audio/renderer.rs +5 -7
  99. package/rust/core/audio/special/env.rs +3 -1
  100. package/rust/core/audio/special/math.rs +4 -4
  101. package/rust/core/audio/special/modulator.rs +2 -2
  102. package/rust/core/builder/mod.rs +9 -3
  103. package/rust/core/debugger/lexer.rs +1 -1
  104. package/rust/core/debugger/mod.rs +6 -0
  105. package/rust/core/debugger/module.rs +4 -4
  106. package/rust/core/debugger/preprocessor.rs +1 -1
  107. package/rust/core/debugger/store.rs +2 -2
  108. package/rust/core/error/mod.rs +189 -0
  109. package/rust/core/lexer/handler/arrow.rs +1 -1
  110. package/rust/core/lexer/handler/at.rs +1 -1
  111. package/rust/core/lexer/handler/brace.rs +2 -2
  112. package/rust/core/lexer/handler/colon.rs +1 -1
  113. package/rust/core/lexer/handler/comment.rs +1 -1
  114. package/rust/core/lexer/handler/dot.rs +1 -1
  115. package/rust/core/lexer/handler/driver.rs +1 -1
  116. package/rust/core/lexer/handler/identifier.rs +1 -1
  117. package/rust/core/lexer/handler/mod.rs +1 -2
  118. package/rust/core/lexer/handler/number.rs +1 -1
  119. package/rust/core/lexer/handler/operator.rs +1 -1
  120. package/rust/core/lexer/handler/parenthesis.rs +2 -2
  121. package/rust/core/lexer/handler/slash.rs +1 -1
  122. package/rust/core/lexer/handler/string.rs +1 -1
  123. package/rust/core/lexer/mod.rs +22 -12
  124. package/rust/core/lexer/token.rs +90 -97
  125. package/rust/core/mod.rs +0 -1
  126. package/rust/core/parser/driver.rs +66 -13
  127. package/rust/core/parser/handler/arrow_call.rs +28 -8
  128. package/rust/core/parser/handler/at.rs +55 -21
  129. package/rust/core/parser/handler/bank.rs +14 -4
  130. package/rust/core/parser/handler/condition.rs +6 -3
  131. package/rust/core/parser/handler/dot.rs +2 -1
  132. package/rust/core/parser/handler/identifier/automate.rs +13 -16
  133. package/rust/core/parser/handler/identifier/call.rs +4 -4
  134. package/rust/core/parser/handler/identifier/emit.rs +9 -5
  135. package/rust/core/parser/handler/identifier/function.rs +20 -7
  136. package/rust/core/parser/handler/identifier/group.rs +11 -7
  137. package/rust/core/parser/handler/identifier/let_.rs +24 -9
  138. package/rust/core/parser/handler/identifier/mod.rs +6 -5
  139. package/rust/core/parser/handler/identifier/on.rs +16 -7
  140. package/rust/core/parser/handler/identifier/print.rs +6 -9
  141. package/rust/core/parser/handler/identifier/sleep.rs +12 -5
  142. package/rust/core/parser/handler/identifier/spawn.rs +4 -4
  143. package/rust/core/parser/handler/identifier/synth.rs +79 -9
  144. package/rust/core/parser/handler/loop_.rs +39 -14
  145. package/rust/core/parser/handler/tempo.rs +9 -5
  146. package/rust/core/parser/mod.rs +0 -1
  147. package/rust/core/parser/statement.rs +6 -137
  148. package/rust/core/plugin/loader.rs +41 -27
  149. package/rust/core/plugin/runner.rs +68 -17
  150. package/rust/core/preprocessor/loader.rs +155 -33
  151. package/rust/core/preprocessor/processor.rs +2 -2
  152. package/rust/core/preprocessor/resolver/bank.rs +6 -8
  153. package/rust/core/preprocessor/resolver/call.rs +20 -24
  154. package/rust/core/preprocessor/resolver/condition.rs +6 -8
  155. package/rust/core/preprocessor/resolver/driver.rs +14 -16
  156. package/rust/core/preprocessor/resolver/function.rs +6 -6
  157. package/rust/core/preprocessor/resolver/group.rs +6 -8
  158. package/rust/core/preprocessor/resolver/loop_.rs +8 -10
  159. package/rust/core/preprocessor/resolver/spawn.rs +19 -23
  160. package/rust/core/preprocessor/resolver/synth.rs +6 -8
  161. package/rust/core/preprocessor/resolver/tempo.rs +6 -8
  162. package/rust/core/preprocessor/resolver/trigger.rs +22 -19
  163. package/rust/core/preprocessor/resolver/value.rs +99 -4
  164. package/rust/core/store/export.rs +28 -28
  165. package/rust/core/store/function.rs +6 -0
  166. package/rust/core/store/global.rs +7 -1
  167. package/rust/core/store/import.rs +28 -28
  168. package/rust/core/store/variable.rs +1 -1
  169. package/rust/core/utils/mod.rs +0 -1
  170. package/rust/lib.rs +102 -9
  171. package/rust/main.rs +156 -45
  172. package/rust/types/Cargo.toml +8 -0
  173. package/rust/types/src/addons.rs +55 -0
  174. package/rust/types/src/ast.rs +198 -0
  175. package/rust/types/src/config.rs +74 -0
  176. package/rust/types/src/lib.rs +12 -0
  177. package/rust/types/src/telemetry.rs +85 -0
  178. package/rust/utils/Cargo.toml +23 -0
  179. package/rust/utils/{error.rs → src/error.rs} +186 -200
  180. package/rust/utils/src/file.rs +94 -0
  181. package/rust/utils/src/first_usage.rs +97 -0
  182. package/rust/utils/{mod.rs → src/lib.rs} +1 -1
  183. package/rust/utils/{logger.rs → src/logger.rs} +17 -12
  184. package/rust/utils/src/path.rs +88 -0
  185. package/rust/utils/src/signature.rs +41 -0
  186. package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
  187. package/rust/utils/src/version.rs +27 -0
  188. package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
  189. package/rust/web/api.rs +5 -0
  190. package/rust/web/cdn.rs +34 -0
  191. package/templates/minimal/README.md +98 -54
  192. package/templates/welcome/README.md +98 -54
  193. package/templates/welcome/src/index.deva +56 -8
  194. package/templates/welcome/src/variables.deva +2 -4
  195. package/tests/rust/TODO.md +0 -0
  196. package/tests/typescript/index.spec.ts +136 -0
  197. package/tests/typescript/playhead.spec.ts +36 -0
  198. package/tests/typescript/render_e2e.spec.ts +77 -0
  199. package/tsconfig.json +1 -1
  200. package/typescript/core/functions/index.ts +83 -0
  201. package/typescript/core/index.ts +6 -0
  202. package/typescript/core/types/index.ts +4 -0
  203. package/typescript/core/types/plugin.ts +19 -0
  204. package/typescript/core/types/result.ts +29 -0
  205. package/typescript/core/types/statement.ts +47 -0
  206. package/typescript/core/types/value.ts +29 -0
  207. package/typescript/index.ts +7 -2
  208. package/typescript/pkg/devalang_core.d.ts +4 -0
  209. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  210. package/typescript/scripts/postinstall.ts +45 -32
  211. package/rust/cli/bank.rs +0 -462
  212. package/rust/cli/build.rs +0 -252
  213. package/rust/cli/generator.rs +0 -1
  214. package/rust/cli/play.rs +0 -1123
  215. package/rust/cli/telemetry.rs +0 -19
  216. package/rust/common/api.rs +0 -5
  217. package/rust/common/cdn.rs +0 -5
  218. package/rust/config/loader.rs +0 -165
  219. package/rust/config/stats.rs +0 -257
  220. package/rust/core/audio/engine.rs +0 -696
  221. package/rust/core/shared/bank.rs +0 -21
  222. package/rust/core/shared/duration.rs +0 -9
  223. package/rust/core/shared/mod.rs +0 -3
  224. package/rust/core/shared/value.rs +0 -35
  225. package/rust/core/utils/validation.rs +0 -35
  226. package/rust/installer/bank.rs +0 -62
  227. package/rust/installer/plugin.rs +0 -54
  228. package/rust/installer/utils.rs +0 -56
  229. package/rust/utils/file.rs +0 -38
  230. package/rust/utils/first_usage.rs +0 -76
  231. package/rust/utils/signature.rs +0 -19
  232. package/rust/utils/telemetry.rs +0 -292
  233. package/rust/utils/version.rs +0 -15
  234. /package/rust/{common → web}/mod.rs +0 -0
  235. /package/rust/{common → web}/sso.rs +0 -0
@@ -1,26 +1,68 @@
1
+ use devalang_types::Value;
2
+ use devalang_utils::logger::{LogLevel, Logger};
1
3
  use rayon::prelude::*;
2
4
 
3
- use crate::{
4
- core::{
5
- audio::{
6
- engine::AudioEngine,
7
- interpreter::{
8
- arrow_call::interprete_call_arrow_statement, call::interprete_call_statement,
9
- function::interprete_function_statement, let_::interprete_let_statement,
10
- load::interprete_load_statement, loop_::interprete_loop_statement,
11
- sleep::interprete_sleep_statement, spawn::interprete_spawn_statement,
12
- tempo::interprete_tempo_statement, trigger::interprete_trigger_statement,
13
- },
5
+ use crate::core::{
6
+ audio::{
7
+ engine::AudioEngine,
8
+ interpreter::{
9
+ arrow_call::interprete_call_arrow_statement, call::interprete_call_statement,
10
+ function::interprete_function_statement, let_::interprete_let_statement,
11
+ load::interprete_load_statement, loop_::interprete_loop_statement,
12
+ sleep::interprete_sleep_statement, spawn::interprete_spawn_statement,
13
+ tempo::interprete_tempo_statement, trigger::interprete_trigger_statement,
14
14
  },
15
- parser::statement::{Statement, StatementKind},
16
- shared::value::Value,
17
- store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
18
15
  },
19
- utils::logger::{LogLevel, Logger},
16
+ parser::statement::{Statement, StatementKind},
17
+ store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
20
18
  };
21
19
 
20
+ // WASM playhead callback support (only compiled for wasm32 target)
21
+ #[cfg(target_arch = "wasm32")]
22
+ use serde::Serialize;
23
+
24
+ #[cfg(target_arch = "wasm32")]
25
+ use wasm_bindgen::prelude::JsValue;
26
+
27
+ #[cfg(target_arch = "wasm32")]
28
+ use std::cell::RefCell;
29
+
30
+ #[cfg(target_arch = "wasm32")]
31
+ thread_local! {
32
+ static PLAYHEAD_CB: RefCell<Option<js_sys::Function>> = RefCell::new(None);
33
+ }
34
+
35
+ #[cfg(target_arch = "wasm32")]
36
+ pub fn register_playhead_callback(cb: js_sys::Function) {
37
+ PLAYHEAD_CB.with(|c| *c.borrow_mut() = Some(cb));
38
+ }
39
+
40
+ #[cfg(target_arch = "wasm32")]
41
+ pub fn unregister_playhead_callback() {
42
+ PLAYHEAD_CB.with(|c| *c.borrow_mut() = None);
43
+ }
44
+
45
+ #[cfg(target_arch = "wasm32")]
46
+ fn emit_playhead(time: f32, line: usize, column: usize) {
47
+ #[derive(Serialize)]
48
+ struct PlayheadEvent {
49
+ time: f32,
50
+ line: usize,
51
+ column: usize,
52
+ }
53
+
54
+ let ev = PlayheadEvent { time, line, column };
55
+ if let Ok(v) = serde_wasm_bindgen::to_value(&ev) {
56
+ PLAYHEAD_CB.with(|c| {
57
+ if let Some(f) = c.borrow().as_ref() {
58
+ let _ = f.call1(&JsValue::NULL, &v);
59
+ }
60
+ });
61
+ }
62
+ }
63
+
22
64
  pub fn run_audio_program(
23
- statements: &Vec<Statement>,
65
+ statements: &[Statement],
24
66
  audio_engine: &mut AudioEngine,
25
67
  _entry: String,
26
68
  _output: String,
@@ -36,7 +78,7 @@ pub fn run_audio_program(
36
78
  global_store,
37
79
  global_store.variables.clone(),
38
80
  global_store.functions.clone(),
39
- &statements,
81
+ statements,
40
82
  base_bpm,
41
83
  base_duration,
42
84
  0.0,
@@ -46,6 +88,13 @@ pub fn run_audio_program(
46
88
  (max_end_time, cursor_time)
47
89
  }
48
90
 
91
+ /// Execute a block of statements and schedule audio into the provided
92
+ /// AudioEngine. This function is the core of offline rendering and
93
+ /// performs the following responsibilities:
94
+ /// - sequential evaluation of statements (load, let, trigger, loop, etc.)
95
+ /// - parallel execution of `spawn` blocks (using rayon) and merging results
96
+ /// - scheduling of periodic events (beat/bar) once at the root depth
97
+ /// - emitting playhead events (when compiled for wasm32) after each sequential statement
49
98
  pub fn execute_audio_block(
50
99
  audio_engine: &mut AudioEngine,
51
100
  global_store: &GlobalStore,
@@ -71,7 +120,7 @@ pub fn execute_audio_block(
71
120
  for stmt in others {
72
121
  match &stmt.kind {
73
122
  StatementKind::Load { .. } => {
74
- if let Some(new_table) = interprete_load_statement(&stmt, &mut variable_table) {
123
+ if let Some(new_table) = interprete_load_statement(stmt, &mut variable_table) {
75
124
  variable_table = new_table;
76
125
  }
77
126
  }
@@ -116,26 +165,26 @@ pub fn execute_audio_block(
116
165
  }
117
166
  }
118
167
  StatementKind::Let { .. } => {
119
- if let Some(new_table) = interprete_let_statement(&stmt, &mut variable_table) {
168
+ if let Some(new_table) = interprete_let_statement(stmt, &mut variable_table) {
120
169
  variable_table = new_table;
121
170
  }
122
171
  }
123
172
  StatementKind::Function { .. } => {
124
173
  if let Some(new_functions) =
125
- interprete_function_statement(&stmt, &mut functions_table)
174
+ interprete_function_statement(stmt, &mut functions_table)
126
175
  {
127
176
  functions_table = new_functions;
128
177
  }
129
178
  }
130
179
  StatementKind::Tempo => {
131
- if let Some((new_bpm, new_duration)) = interprete_tempo_statement(&stmt) {
180
+ if let Some((new_bpm, new_duration)) = interprete_tempo_statement(stmt) {
132
181
  base_bpm = new_bpm;
133
182
  base_duration = new_duration;
134
183
  }
135
184
  }
136
185
  StatementKind::Trigger { .. } => {
137
186
  if let Some((new_cursor, new_max, _)) = interprete_trigger_statement(
138
- &stmt,
187
+ stmt,
139
188
  audio_engine,
140
189
  &variable_table,
141
190
  base_duration,
@@ -148,13 +197,13 @@ pub fn execute_audio_block(
148
197
  }
149
198
  StatementKind::Sleep => {
150
199
  let (new_cursor, new_max) =
151
- interprete_sleep_statement(&stmt, cursor_time, max_end_time);
200
+ interprete_sleep_statement(stmt, cursor_time, max_end_time);
152
201
  cursor_time = new_cursor;
153
202
  max_end_time = new_max;
154
203
  }
155
204
  StatementKind::Loop => {
156
205
  let (new_max, new_cursor) = interprete_loop_statement(
157
- &stmt,
206
+ stmt,
158
207
  audio_engine,
159
208
  global_store,
160
209
  &variable_table,
@@ -169,7 +218,7 @@ pub fn execute_audio_block(
169
218
  }
170
219
  StatementKind::Call { .. } => {
171
220
  let (new_max, _) = interprete_call_statement(
172
- &stmt,
221
+ stmt,
173
222
  audio_engine,
174
223
  &variable_table,
175
224
  &functions_table,
@@ -184,16 +233,16 @@ pub fn execute_audio_block(
184
233
  }
185
234
  StatementKind::ArrowCall { .. } => {
186
235
  let (new_max, new_cursor) = interprete_call_arrow_statement(
187
- &stmt,
236
+ stmt,
188
237
  audio_engine,
189
238
  &variable_table,
239
+ global_store,
190
240
  base_bpm,
191
241
  base_duration,
192
242
  &mut max_end_time,
193
243
  Some(&mut cursor_time),
194
244
  true,
195
245
  );
196
-
197
246
  cursor_time = new_cursor;
198
247
 
199
248
  if new_max > max_end_time {
@@ -203,7 +252,7 @@ pub fn execute_audio_block(
203
252
  StatementKind::Automate { .. } => {
204
253
  if let Some(new_table) =
205
254
  crate::core::audio::interpreter::automate::interprete_automate_statement(
206
- &stmt,
255
+ stmt,
207
256
  &mut variable_table,
208
257
  )
209
258
  {
@@ -237,7 +286,7 @@ pub fn execute_audio_block(
237
286
  )
238
287
  {
239
288
  logger.log_message(LogLevel::Print, &res);
240
- } else if let Some(val) = variable_table.get(&s) {
289
+ } else if let Some(val) = variable_table.get(s) {
241
290
  logger.log_message(LogLevel::Print, &format!("{:?}", val));
242
291
  } else if s.contains("$env")
243
292
  || s.contains("$math")
@@ -251,12 +300,12 @@ pub fn execute_audio_block(
251
300
  );
252
301
  match v {
253
302
  Value::Number(n) => {
254
- logger.log_message(LogLevel::Print, &format!("{}", n))
303
+ logger.log_message(LogLevel::Print, &format!("{}", n));
255
304
  }
256
305
  _ => logger.log_message(LogLevel::Print, s),
257
306
  }
258
307
  } else {
259
- logger.log_message(LogLevel::Print, s)
308
+ logger.log_message(LogLevel::Print, s);
260
309
  }
261
310
  }
262
311
  Value::Number(n) => {
@@ -266,18 +315,19 @@ pub fn execute_audio_block(
266
315
  if let Some(val) = variable_table.get(name) {
267
316
  match val {
268
317
  Value::Number(n) => {
269
- logger.log_message(LogLevel::Print, &format!("{}", n))
318
+ logger.log_message(LogLevel::Print, &format!("{}", n));
270
319
  }
271
320
  Value::String(s) => logger.log_message(LogLevel::Print, s),
272
321
  Value::Boolean(b) => {
273
- logger.log_message(LogLevel::Print, &format!("{}", b))
322
+ logger.log_message(LogLevel::Print, &format!("{}", b));
274
323
  }
275
324
  other => {
276
- logger.log_message(LogLevel::Print, &format!("{:?}", other))
325
+ logger
326
+ .log_message(LogLevel::Print, &format!("{:?}", other));
277
327
  }
278
328
  }
279
329
  } else {
280
- logger.log_message(LogLevel::Print, name)
330
+ logger.log_message(LogLevel::Print, name);
281
331
  }
282
332
  }
283
333
  v => logger.log_message(LogLevel::Print, &format!("{:?}", v)),
@@ -286,6 +336,12 @@ pub fn execute_audio_block(
286
336
  }
287
337
  _ => {}
288
338
  }
339
+
340
+ // Emit playhead event for UI bindings when building real-time playback
341
+ #[cfg(target_arch = "wasm32")]
342
+ {
343
+ emit_playhead(cursor_time, stmt.line, stmt.column);
344
+ }
289
345
  }
290
346
 
291
347
  // Execute parallel spawns (collect results)
@@ -316,31 +372,30 @@ pub fn execute_audio_block(
316
372
  }
317
373
  }
318
374
 
319
- // ─────────────────────────────────────────────────────────────
320
375
  // Built-in periodic events (e.g., on beat(n), on bar(n))
321
376
  // Emit handlers across the timeline up to max_end_time.
322
377
  // If no audio was scheduled (max_end_time == 0.0), skip.
323
378
  // Don't schedule periodic events if we're already inside an event handler
324
379
  let in_event = matches!(variable_table.get("__in_event"), Some(Value::Boolean(true)));
325
380
  let depth_is_root = matches!(variable_table.get("__depth"), Some(Value::Number(n)) if (*n - 1.0).abs() < f32::EPSILON);
326
- if max_end_time > 0.0 && !in_event && depth_is_root {
327
- if !global_store.events.is_empty() {
328
- // Beat-based handlers (support "beat" and "$beat")
329
- for ev_key in ["beat", "$beat"] {
330
- if let Some(handlers) = global_store.get_event_handlers(ev_key) {
331
- let mut seen: std::collections::HashSet<(usize, usize, usize)> =
332
- std::collections::HashSet::new();
333
- // Default every 1 beat if args missing
334
- for h in handlers {
335
- let key = (h.line, h.column, h.indent);
336
- if !seen.insert(key) {
337
- continue;
338
- }
339
- if let StatementKind::On { event, args, body } = &h.kind {
340
- let every: f32 = args
341
- .as_ref()
342
- .and_then(|v| v.get(0))
343
- .and_then(|x| match x {
381
+ if max_end_time > 0.0 && !in_event && depth_is_root && !global_store.events.is_empty() {
382
+ // Beat-based handlers (support "beat" and "$beat")
383
+ for ev_key in ["beat", "$beat"] {
384
+ if let Some(handlers) = global_store.get_event_handlers(ev_key) {
385
+ let mut seen: std::collections::HashSet<(usize, usize, usize)> =
386
+ std::collections::HashSet::new();
387
+ // Default every 1 beat if args missing
388
+ for h in handlers {
389
+ let key = (h.line, h.column, h.indent);
390
+ if !seen.insert(key) {
391
+ continue;
392
+ }
393
+ if let StatementKind::On { event, args, body } = &h.kind {
394
+ let every: f32 = args
395
+ .as_ref()
396
+ .and_then(|v| v.first())
397
+ .and_then(|x| {
398
+ match x {
344
399
  Value::Number(n) => Some(*n),
345
400
  Value::Identifier(s) => {
346
401
  // Try to resolve from variables first, fallback to parsing the literal
@@ -350,14 +405,81 @@ pub fn execute_audio_block(
350
405
  }
351
406
  }
352
407
  _ => None,
408
+ }
409
+ })
410
+ .unwrap_or(1.0)
411
+ .max(0.0001);
412
+ let step = base_duration * every;
413
+ // Start from first full bar boundary after t=0
414
+ let mut t = step;
415
+ while t <= max_end_time {
416
+ // Prepare event context
417
+ let mut vt = variable_table.clone();
418
+ let mut ctx = std::collections::HashMap::new();
419
+ ctx.insert("name".to_string(), Value::String(event.clone()));
420
+ if let Some(a) = args.clone() {
421
+ ctx.insert("args".to_string(), Value::Array(a));
422
+ }
423
+ vt.set("event".to_string(), Value::Map(ctx));
424
+ vt.set("beat".to_string(), Value::Number(t / base_duration));
425
+ // Prevent nested scheduling
426
+ vt.set("__in_event".to_string(), Value::Boolean(true));
427
+
428
+ let (_m, _c) = execute_audio_block(
429
+ audio_engine,
430
+ global_store,
431
+ vt,
432
+ functions_table.clone(),
433
+ body,
434
+ base_bpm,
435
+ base_duration,
436
+ max_end_time,
437
+ t,
438
+ );
439
+
440
+ t += step;
441
+ }
442
+ }
443
+ }
444
+ }
445
+ }
446
+
447
+ // Bar-based handlers (default 4/4 time => 4 beats per bar); support "bar" and "$bar"
448
+ for ev_key in ["bar", "$bar"] {
449
+ if let Some(handlers) = global_store.get_event_handlers(ev_key) {
450
+ let mut seen: std::collections::HashSet<(usize, usize, usize)> =
451
+ std::collections::HashSet::new();
452
+ for h in handlers {
453
+ let key = (h.line, h.column, h.indent);
454
+ if !seen.insert(key) {
455
+ continue;
456
+ }
457
+ if let StatementKind::On { event, args, body } = &h.kind {
458
+ let bar_beats = 4.0f32; // TODO: time signature support
459
+ let first_only = args.as_ref().and_then(|v| v.first()).is_none();
460
+
461
+ let every_bar: f32 = if first_only {
462
+ 1.0
463
+ } else {
464
+ args.as_ref()
465
+ .and_then(|v| v.first())
466
+ .and_then(|x| match x {
467
+ Value::Number(n) => Some(*n),
468
+ Value::Identifier(s) => match variable_table.get(s) {
469
+ Some(Value::Number(n)) => Some(*n),
470
+ _ => s.parse::<f32>().ok(),
471
+ },
472
+ _ => None,
353
473
  })
354
474
  .unwrap_or(1.0)
355
- .max(0.0001);
356
- let step = base_duration * every;
357
- // Start from first full bar boundary after t=0
358
- let mut t = step;
359
- while t <= max_end_time {
360
- // Prepare event context
475
+ .max(0.0001)
476
+ };
477
+
478
+ let step = base_duration * bar_beats * every_bar;
479
+
480
+ if first_only {
481
+ let t = step; // first full bar after t=0
482
+ if t <= max_end_time {
361
483
  let mut vt = variable_table.clone();
362
484
  let mut ctx = std::collections::HashMap::new();
363
485
  ctx.insert("name".to_string(), Value::String(event.clone()));
@@ -380,101 +502,34 @@ pub fn execute_audio_block(
380
502
  max_end_time,
381
503
  t,
382
504
  );
383
-
384
- t += step;
385
505
  }
386
- }
387
- }
388
- }
389
- }
390
-
391
- // Bar-based handlers (default 4/4 time => 4 beats per bar); support "bar" and "$bar"
392
- for ev_key in ["bar", "$bar"] {
393
- if let Some(handlers) = global_store.get_event_handlers(ev_key) {
394
- let mut seen: std::collections::HashSet<(usize, usize, usize)> =
395
- std::collections::HashSet::new();
396
- for h in handlers {
397
- let key = (h.line, h.column, h.indent);
398
- if !seen.insert(key) {
399
- continue;
400
- }
401
- if let StatementKind::On { event, args, body } = &h.kind {
402
- let bar_beats = 4.0f32; // TODO: time signature support
403
- let first_only = args.as_ref().and_then(|v| v.get(0)).is_none();
404
-
405
- let every_bar: f32 = if first_only {
406
- 1.0
407
- } else {
408
- args.as_ref()
409
- .and_then(|v| v.get(0))
410
- .and_then(|x| match x {
411
- Value::Number(n) => Some(*n),
412
- Value::Identifier(s) => match variable_table.get(s) {
413
- Some(Value::Number(n)) => Some(*n),
414
- _ => s.parse::<f32>().ok(),
415
- },
416
- _ => None,
417
- })
418
- .unwrap_or(1.0)
419
- .max(0.0001)
420
- };
421
-
422
- let step = base_duration * bar_beats * every_bar;
423
-
424
- if first_only {
425
- let t = step; // first full bar after t=0
426
- if t <= max_end_time {
427
- let mut vt = variable_table.clone();
428
- let mut ctx = std::collections::HashMap::new();
429
- ctx.insert("name".to_string(), Value::String(event.clone()));
430
- if let Some(a) = args.clone() {
431
- ctx.insert("args".to_string(), Value::Array(a));
432
- }
433
- vt.set("event".to_string(), Value::Map(ctx));
434
- vt.set("beat".to_string(), Value::Number(t / base_duration));
435
- // Prevent nested scheduling
436
- vt.set("__in_event".to_string(), Value::Boolean(true));
437
-
438
- let (_m, _c) = execute_audio_block(
439
- audio_engine,
440
- global_store,
441
- vt,
442
- functions_table.clone(),
443
- body,
444
- base_bpm,
445
- base_duration,
446
- max_end_time,
447
- t,
448
- );
506
+ } else {
507
+ let mut t = step; // start from first full bar after t=0
508
+ while t <= max_end_time {
509
+ let mut vt = variable_table.clone();
510
+ let mut ctx = std::collections::HashMap::new();
511
+ ctx.insert("name".to_string(), Value::String(event.clone()));
512
+ if let Some(a) = args.clone() {
513
+ ctx.insert("args".to_string(), Value::Array(a));
449
514
  }
450
- } else {
451
- let mut t = step; // start from first full bar after t=0
452
- while t <= max_end_time {
453
- let mut vt = variable_table.clone();
454
- let mut ctx = std::collections::HashMap::new();
455
- ctx.insert("name".to_string(), Value::String(event.clone()));
456
- if let Some(a) = args.clone() {
457
- ctx.insert("args".to_string(), Value::Array(a));
458
- }
459
- vt.set("event".to_string(), Value::Map(ctx));
460
- vt.set("beat".to_string(), Value::Number(t / base_duration));
461
- // Prevent nested scheduling
462
- vt.set("__in_event".to_string(), Value::Boolean(true));
515
+ vt.set("event".to_string(), Value::Map(ctx));
516
+ vt.set("beat".to_string(), Value::Number(t / base_duration));
517
+ // Prevent nested scheduling
518
+ vt.set("__in_event".to_string(), Value::Boolean(true));
463
519
 
464
- let (_m, _c) = execute_audio_block(
465
- audio_engine,
466
- global_store,
467
- vt,
468
- functions_table.clone(),
469
- body,
470
- base_bpm,
471
- base_duration,
472
- max_end_time,
473
- t,
474
- );
520
+ let (_m, _c) = execute_audio_block(
521
+ audio_engine,
522
+ global_store,
523
+ vt,
524
+ functions_table.clone(),
525
+ body,
526
+ base_bpm,
527
+ base_duration,
528
+ max_end_time,
529
+ t,
530
+ );
475
531
 
476
- t += step;
477
- }
532
+ t += step;
478
533
  }
479
534
  }
480
535
  }
@@ -1,8 +1,8 @@
1
1
  use crate::core::{
2
2
  parser::statement::{Statement, StatementKind},
3
- shared::value::Value,
4
3
  store::variable::VariableTable,
5
4
  };
5
+ use devalang_types::Value;
6
6
 
7
7
  pub fn interprete_let_statement(
8
8
  stmt: &Statement,
@@ -1,6 +1,7 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::{
2
4
  parser::statement::{Statement, StatementKind},
3
- shared::value::Value,
4
5
  store::variable::VariableTable,
5
6
  };
6
7
 
@@ -1,7 +1,8 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::{
2
4
  audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
3
5
  parser::statement::Statement,
4
- shared::value::Value,
5
6
  store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
6
7
  };
7
8
 
@@ -27,7 +28,7 @@ pub fn interprete_loop_statement(
27
28
  loop_value.get("array"),
28
29
  loop_value.get("body"),
29
30
  ) {
30
- let mut engine = audio_engine;
31
+ let engine = audio_engine;
31
32
  let mut cur_time = cursor_time;
32
33
  let mut max_time = max_end_time;
33
34
 
@@ -36,11 +37,11 @@ pub fn interprete_loop_statement(
36
37
  scoped_vars.set(var_name.clone(), item.clone());
37
38
 
38
39
  let (block_end_time, new_cursor) = execute_audio_block(
39
- &mut engine,
40
+ engine,
40
41
  global_store,
41
42
  scoped_vars,
42
43
  functions_table.clone(),
43
- &loop_body,
44
+ loop_body,
44
45
  base_bpm,
45
46
  base_duration,
46
47
  max_time,
@@ -84,13 +85,13 @@ pub fn interprete_loop_statement(
84
85
  }
85
86
  };
86
87
 
87
- let mut engine = audio_engine;
88
+ let engine = audio_engine;
88
89
  let mut cur_time = cursor_time;
89
90
  let mut max_time = max_end_time;
90
91
 
91
92
  for _ in 0..loop_count {
92
93
  let (block_end_time, new_cursor) = execute_audio_block(
93
- &mut engine,
94
+ engine,
94
95
  global_store,
95
96
  variable_table.clone(),
96
97
  functions_table.clone(),
@@ -1,4 +1,5 @@
1
- use crate::core::{parser::statement::Statement, shared::value::Value};
1
+ use crate::core::parser::statement::Statement;
2
+ use devalang_types::Value;
2
3
 
3
4
  pub fn interprete_sleep_statement(
4
5
  stmt: &Statement,