@devaloop/devalang 0.0.1-alpha.16-hotfix.3 → 0.0.1-alpha.18

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 (239) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +10 -10
  3. package/.github/workflows/ci.yml +0 -1
  4. package/Cargo.toml +18 -2
  5. package/README.md +82 -34
  6. package/docs/CHANGELOG.md +91 -0
  7. package/docs/ROADMAP.md +7 -4
  8. package/docs/TODO.md +1 -1
  9. package/examples/index.deva +55 -35
  10. package/examples/pattern.deva +5 -5
  11. package/out-tsc/bin/index.d.ts +2 -0
  12. package/out-tsc/core/functions/index.d.ts +37 -0
  13. package/out-tsc/core/functions/index.js +76 -0
  14. package/out-tsc/core/index.d.ts +6 -0
  15. package/out-tsc/core/index.js +22 -0
  16. package/out-tsc/core/types/index.d.ts +4 -0
  17. package/out-tsc/core/types/index.js +20 -0
  18. package/out-tsc/core/types/plugin.d.ts +18 -0
  19. package/out-tsc/core/types/plugin.js +2 -0
  20. package/out-tsc/core/types/result.d.ts +27 -0
  21. package/out-tsc/core/types/result.js +2 -0
  22. package/out-tsc/core/types/statement.d.ts +106 -0
  23. package/out-tsc/core/types/statement.js +2 -0
  24. package/out-tsc/core/types/value.d.ts +43 -0
  25. package/out-tsc/core/types/value.js +2 -0
  26. package/out-tsc/index.d.ts +7 -0
  27. package/out-tsc/index.js +41 -2
  28. package/out-tsc/pkg/devalang_core.d.ts +7 -0
  29. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  30. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  31. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  32. package/out-tsc/scripts/postinstall.d.ts +1 -0
  33. package/out-tsc/scripts/postinstall.js +33 -23
  34. package/out-tsc/scripts/version/bump.d.ts +1 -0
  35. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  36. package/out-tsc/scripts/version/index.d.ts +1 -0
  37. package/out-tsc/scripts/version/sync.d.ts +1 -0
  38. package/package.json +16 -4
  39. package/project-version.json +3 -3
  40. package/rust/cli/bank/api.rs +122 -0
  41. package/rust/cli/bank/commands.rs +275 -0
  42. package/rust/cli/bank/mod.rs +29 -0
  43. package/rust/cli/build/commands.rs +107 -0
  44. package/rust/cli/build/mod.rs +2 -0
  45. package/rust/cli/build/process.rs +146 -0
  46. package/rust/cli/{check.rs → check/mod.rs} +18 -31
  47. package/rust/cli/discover/commands.rs +253 -0
  48. package/rust/cli/discover/config.rs +111 -0
  49. package/rust/cli/discover/fs.rs +19 -0
  50. package/rust/cli/discover/install.rs +103 -0
  51. package/rust/cli/discover/metadata.rs +48 -0
  52. package/rust/cli/discover/mod.rs +5 -0
  53. package/rust/cli/{init.rs → init/commands.rs} +88 -87
  54. package/rust/cli/init/mod.rs +1 -0
  55. package/rust/cli/install/addon.rs +126 -0
  56. package/rust/cli/install/bank.rs +53 -0
  57. package/rust/cli/{install.rs → install/commands.rs} +9 -9
  58. package/rust/{installer → cli/install}/mod.rs +2 -3
  59. package/rust/cli/install/plugin.rs +61 -0
  60. package/rust/cli/{login.rs → login/commands.rs} +8 -11
  61. package/rust/cli/login/mod.rs +1 -0
  62. package/rust/cli/mod.rs +2 -2
  63. package/rust/cli/{driver.rs → parser.rs} +7 -2
  64. package/rust/cli/play/commands.rs +324 -0
  65. package/rust/cli/play/io.rs +17 -0
  66. package/rust/cli/play/mod.rs +5 -0
  67. package/rust/cli/play/process.rs +150 -0
  68. package/rust/cli/play/realtime.rs +91 -0
  69. package/rust/cli/play/utils.rs +23 -0
  70. package/rust/cli/{telemetry.rs → telemetry/commands.rs} +4 -4
  71. package/rust/cli/telemetry/event_creator.rs +80 -0
  72. package/rust/cli/telemetry/mod.rs +3 -0
  73. package/rust/cli/telemetry/send.rs +51 -0
  74. package/rust/cli/{template.rs → template/commands.rs} +1 -1
  75. package/rust/cli/template/mod.rs +1 -0
  76. package/rust/cli/{update.rs → update/commands.rs} +6 -6
  77. package/rust/cli/update/mod.rs +1 -0
  78. package/rust/config/driver.rs +57 -72
  79. package/rust/config/mod.rs +1 -2
  80. package/rust/config/ops.rs +26 -0
  81. package/rust/config/settings.rs +40 -42
  82. package/rust/core/audio/engine/helpers.rs +158 -0
  83. package/rust/core/audio/engine/mod.rs +7 -0
  84. package/rust/core/audio/engine/sample.rs +359 -0
  85. package/rust/core/audio/engine/synth.rs +325 -0
  86. package/rust/core/audio/evaluator.rs +68 -27
  87. package/rust/core/audio/interpreter/arrow_call.rs +113 -33
  88. package/rust/core/audio/interpreter/call.rs +232 -56
  89. package/rust/core/audio/interpreter/condition.rs +3 -2
  90. package/rust/core/audio/interpreter/driver.rs +206 -151
  91. package/rust/core/audio/interpreter/let_.rs +1 -1
  92. package/rust/core/audio/interpreter/load.rs +2 -1
  93. package/rust/core/audio/interpreter/loop_.rs +7 -6
  94. package/rust/core/audio/interpreter/sleep.rs +2 -1
  95. package/rust/core/audio/interpreter/spawn.rs +186 -54
  96. package/rust/core/audio/interpreter/tempo.rs +31 -10
  97. package/rust/core/audio/interpreter/trigger.rs +2 -2
  98. package/rust/core/audio/loader/trigger.rs +4 -7
  99. package/rust/core/audio/player.rs +6 -0
  100. package/rust/core/audio/renderer.rs +5 -7
  101. package/rust/core/audio/special/env.rs +3 -1
  102. package/rust/core/audio/special/math.rs +26 -6
  103. package/rust/core/audio/special/modulator.rs +2 -2
  104. package/rust/core/builder/mod.rs +9 -3
  105. package/rust/core/debugger/lexer.rs +1 -1
  106. package/rust/core/debugger/mod.rs +6 -0
  107. package/rust/core/debugger/module.rs +4 -4
  108. package/rust/core/debugger/preprocessor.rs +1 -1
  109. package/rust/core/debugger/store.rs +2 -2
  110. package/rust/core/error/mod.rs +189 -0
  111. package/rust/core/lexer/driver.rs +61 -0
  112. package/rust/core/lexer/handler/arrow.rs +1 -1
  113. package/rust/core/lexer/handler/at.rs +1 -1
  114. package/rust/core/lexer/handler/brace.rs +2 -2
  115. package/rust/core/lexer/handler/colon.rs +1 -1
  116. package/rust/core/lexer/handler/comment.rs +1 -1
  117. package/rust/core/lexer/handler/dot.rs +1 -1
  118. package/rust/core/lexer/handler/driver.rs +1 -1
  119. package/rust/core/lexer/handler/identifier.rs +4 -3
  120. package/rust/core/lexer/handler/mod.rs +1 -2
  121. package/rust/core/lexer/handler/number.rs +1 -1
  122. package/rust/core/lexer/handler/operator.rs +1 -1
  123. package/rust/core/lexer/handler/parenthesis.rs +2 -2
  124. package/rust/core/lexer/handler/slash.rs +1 -1
  125. package/rust/core/lexer/handler/string.rs +1 -1
  126. package/rust/core/lexer/mod.rs +1 -52
  127. package/rust/core/lexer/token.rs +91 -97
  128. package/rust/core/mod.rs +0 -1
  129. package/rust/core/parser/driver.rs +78 -22
  130. package/rust/core/parser/handler/arrow_call.rs +28 -8
  131. package/rust/core/parser/handler/at.rs +55 -21
  132. package/rust/core/parser/handler/bank.rs +14 -4
  133. package/rust/core/parser/handler/condition.rs +6 -3
  134. package/rust/core/parser/handler/dot.rs +5 -3
  135. package/rust/core/parser/handler/identifier/automate.rs +13 -16
  136. package/rust/core/parser/handler/identifier/call.rs +4 -4
  137. package/rust/core/parser/handler/identifier/emit.rs +9 -5
  138. package/rust/core/parser/handler/identifier/function.rs +20 -7
  139. package/rust/core/parser/handler/identifier/group.rs +11 -7
  140. package/rust/core/parser/handler/identifier/let_.rs +24 -9
  141. package/rust/core/parser/handler/identifier/mod.rs +6 -5
  142. package/rust/core/parser/handler/identifier/on.rs +16 -7
  143. package/rust/core/parser/handler/identifier/print.rs +6 -9
  144. package/rust/core/parser/handler/identifier/sleep.rs +12 -5
  145. package/rust/core/parser/handler/identifier/spawn.rs +4 -4
  146. package/rust/core/parser/handler/identifier/synth.rs +79 -9
  147. package/rust/core/parser/handler/loop_.rs +38 -13
  148. package/rust/core/parser/handler/mod.rs +1 -0
  149. package/rust/core/parser/handler/pattern.rs +74 -0
  150. package/rust/core/parser/handler/tempo.rs +9 -5
  151. package/rust/core/parser/mod.rs +0 -1
  152. package/rust/core/parser/statement.rs +6 -137
  153. package/rust/core/plugin/loader.rs +41 -27
  154. package/rust/core/plugin/runner.rs +68 -17
  155. package/rust/core/preprocessor/loader.rs +181 -99
  156. package/rust/core/preprocessor/processor.rs +9 -9
  157. package/rust/core/preprocessor/resolver/bank.rs +6 -8
  158. package/rust/core/preprocessor/resolver/call.rs +47 -23
  159. package/rust/core/preprocessor/resolver/condition.rs +6 -8
  160. package/rust/core/preprocessor/resolver/driver.rs +28 -28
  161. package/rust/core/preprocessor/resolver/function.rs +6 -6
  162. package/rust/core/preprocessor/resolver/group.rs +6 -8
  163. package/rust/core/preprocessor/resolver/loop_.rs +8 -10
  164. package/rust/core/preprocessor/resolver/mod.rs +1 -0
  165. package/rust/core/preprocessor/resolver/pattern.rs +75 -0
  166. package/rust/core/preprocessor/resolver/spawn.rs +45 -22
  167. package/rust/core/preprocessor/resolver/synth.rs +6 -8
  168. package/rust/core/preprocessor/resolver/tempo.rs +6 -8
  169. package/rust/core/preprocessor/resolver/trigger.rs +22 -19
  170. package/rust/core/preprocessor/resolver/value.rs +99 -4
  171. package/rust/core/store/export.rs +28 -28
  172. package/rust/core/store/function.rs +6 -0
  173. package/rust/core/store/global.rs +7 -1
  174. package/rust/core/store/import.rs +28 -28
  175. package/rust/core/store/variable.rs +16 -2
  176. package/rust/core/utils/mod.rs +0 -1
  177. package/rust/lib.rs +102 -9
  178. package/rust/main.rs +159 -45
  179. package/rust/types/Cargo.toml +11 -0
  180. package/rust/types/src/addons.rs +55 -0
  181. package/rust/types/src/ast.rs +202 -0
  182. package/rust/types/src/config.rs +74 -0
  183. package/rust/types/src/lib.rs +12 -0
  184. package/rust/types/src/telemetry.rs +85 -0
  185. package/rust/utils/Cargo.toml +26 -0
  186. package/rust/utils/{error.rs → src/error.rs} +186 -200
  187. package/rust/utils/src/file.rs +94 -0
  188. package/rust/utils/src/first_usage.rs +97 -0
  189. package/rust/utils/{mod.rs → src/lib.rs} +1 -1
  190. package/rust/utils/{logger.rs → src/logger.rs} +17 -12
  191. package/rust/utils/src/path.rs +88 -0
  192. package/rust/utils/src/signature.rs +41 -0
  193. package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
  194. package/rust/utils/src/version.rs +27 -0
  195. package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
  196. package/rust/web/cdn.rs +34 -0
  197. package/templates/minimal/README.md +98 -54
  198. package/templates/welcome/README.md +98 -54
  199. package/templates/welcome/src/index.deva +56 -8
  200. package/templates/welcome/src/variables.deva +2 -4
  201. package/tests/rust/TODO.md +0 -0
  202. package/tests/typescript/index.spec.ts +136 -0
  203. package/tests/typescript/playhead.spec.ts +36 -0
  204. package/tests/typescript/render_e2e.spec.ts +77 -0
  205. package/tsconfig.json +1 -1
  206. package/typescript/core/functions/index.ts +83 -0
  207. package/typescript/core/index.ts +6 -0
  208. package/typescript/core/types/index.ts +4 -0
  209. package/typescript/core/types/plugin.ts +19 -0
  210. package/typescript/core/types/result.ts +29 -0
  211. package/typescript/core/types/statement.ts +47 -0
  212. package/typescript/core/types/value.ts +29 -0
  213. package/typescript/index.ts +7 -2
  214. package/typescript/pkg/devalang_core.d.ts +4 -0
  215. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  216. package/rust/cli/bank.rs +0 -462
  217. package/rust/cli/build.rs +0 -252
  218. package/rust/cli/play.rs +0 -1123
  219. package/rust/common/cdn.rs +0 -5
  220. package/rust/config/loader.rs +0 -165
  221. package/rust/config/stats.rs +0 -257
  222. package/rust/core/audio/engine.rs +0 -696
  223. package/rust/core/shared/bank.rs +0 -21
  224. package/rust/core/shared/duration.rs +0 -9
  225. package/rust/core/shared/mod.rs +0 -3
  226. package/rust/core/shared/value.rs +0 -35
  227. package/rust/core/utils/validation.rs +0 -35
  228. package/rust/installer/addon.rs +0 -84
  229. package/rust/installer/bank.rs +0 -62
  230. package/rust/installer/plugin.rs +0 -54
  231. package/rust/installer/utils.rs +0 -56
  232. package/rust/utils/file.rs +0 -38
  233. package/rust/utils/first_usage.rs +0 -83
  234. package/rust/utils/signature.rs +0 -19
  235. package/rust/utils/telemetry.rs +0 -292
  236. package/rust/utils/version.rs +0 -15
  237. /package/rust/{common → web}/api.rs +0 -0
  238. /package/rust/{common → web}/mod.rs +0 -0
  239. /package/rust/{common → web}/sso.rs +0 -0
@@ -2,7 +2,8 @@ use crate::core::audio::special::{
2
2
  find_and_eval_first_easing_call, find_and_eval_first_math_call, find_and_eval_first_mod_call,
3
3
  resolve_env_atom,
4
4
  };
5
- use crate::core::{shared::value::Value, store::variable::VariableTable};
5
+ use crate::core::store::variable::VariableTable;
6
+ use devalang_types::Value;
6
7
 
7
8
  pub fn evaluate_condition_string(expr: &str, vars: &VariableTable) -> bool {
8
9
  let tokens: Vec<&str> = expr.split_whitespace().collect();
@@ -14,22 +15,46 @@ pub fn evaluate_condition_string(expr: &str, vars: &VariableTable) -> bool {
14
15
  let op = tokens[1];
15
16
  let right = tokens[2];
16
17
 
17
- let left_val = match vars.get(left) {
18
- Some(Value::Number(n)) => *n,
19
- _ => {
20
- return false;
18
+ // Resolve left and right to numeric values where possible. Accept numbers, variables or env atoms.
19
+ fn resolve_for_cond(s: &str, vars: &VariableTable) -> Option<f32> {
20
+ if let Ok(n) = s.parse::<f32>() {
21
+ return Some(n);
22
+ }
23
+ if let Some(Value::Number(n)) = vars.get(s) {
24
+ return Some(*n);
21
25
  }
26
+ if let Some(v) = resolve_env_atom(s, 120.0, 1.0) {
27
+ return Some(v);
28
+ }
29
+ None
30
+ }
31
+
32
+ let left_val = match resolve_for_cond(left, vars) {
33
+ Some(v) => v,
34
+ None => return false,
22
35
  };
23
36
 
24
- let right_val: f32 = right.parse().unwrap_or(0.0);
37
+ let right_val = match resolve_for_cond(right, vars) {
38
+ Some(v) => v,
39
+ None => return false,
40
+ };
25
41
 
26
42
  match op {
27
43
  ">" => left_val > right_val,
28
44
  "<" => left_val < right_val,
29
45
  ">=" => left_val >= right_val,
30
46
  "<=" => left_val <= right_val,
31
- "==" => (left_val - right_val).abs() < f32::EPSILON,
32
- "!=" => (left_val - right_val).abs() > f32::EPSILON,
47
+ "==" => {
48
+ // relative epsilon for floating comparisons
49
+ let diff = (left_val - right_val).abs();
50
+ let largest = left_val.abs().max(right_val.abs()).max(1.0);
51
+ diff <= (f32::EPSILON * largest)
52
+ }
53
+ "!=" => {
54
+ let diff = (left_val - right_val).abs();
55
+ let largest = left_val.abs().max(right_val.abs()).max(1.0);
56
+ diff > (f32::EPSILON * largest)
57
+ }
33
58
  _ => false,
34
59
  }
35
60
  }
@@ -61,25 +86,39 @@ pub fn evaluate_numeric_expression(
61
86
  // Shunting-like, simplified: first evaluate any $math.func(...) calls anywhere in the expression,
62
87
  // then fold remaining parentheses and evaluate left-to-right.
63
88
  fn eval(expr: &str, vars: &VariableTable, bpm: f32, beat: f32) -> Option<f32> {
64
- // 1) Replace $math.* calls progressively
89
+ // 1) Replace $math/$easing/$mod calls progressively with a max iteration guard
65
90
  let mut s = expr.to_string();
91
+ let mut iterations = 0u32;
92
+ const MAX_ITER: u32 = 64;
93
+
66
94
  // Evaluate modulators first (they may feed easing/math)
67
- while let Some(next) =
68
- find_and_eval_first_mod_call(&s, evaluate_numeric_expression, vars, bpm, beat)
69
- {
70
- s = next;
95
+ while iterations < MAX_ITER {
96
+ if let Some(next) = find_and_eval_first_mod_call(&s, evaluate_numeric_expression, vars, bpm, beat) {
97
+ s = next;
98
+ iterations += 1;
99
+ continue;
100
+ }
101
+ break;
71
102
  }
72
- // Then easing functions
73
- while let Some(next) =
74
- find_and_eval_first_easing_call(&s, evaluate_numeric_expression, vars, bpm, beat)
75
- {
76
- s = next;
103
+
104
+ iterations = 0;
105
+ while iterations < MAX_ITER {
106
+ if let Some(next) = find_and_eval_first_easing_call(&s, evaluate_numeric_expression, vars, bpm, beat) {
107
+ s = next;
108
+ iterations += 1;
109
+ continue;
110
+ }
111
+ break;
77
112
  }
78
- // Finally math transforms
79
- while let Some(next) =
80
- find_and_eval_first_math_call(&s, evaluate_numeric_expression, vars, bpm, beat)
81
- {
82
- s = next;
113
+
114
+ iterations = 0;
115
+ while iterations < MAX_ITER {
116
+ if let Some(next) = find_and_eval_first_math_call(&s, evaluate_numeric_expression, vars, bpm, beat) {
117
+ s = next;
118
+ iterations += 1;
119
+ continue;
120
+ }
121
+ break;
83
122
  }
84
123
 
85
124
  // 2) Evaluate remaining (pure) parentheses starting from innermost
@@ -148,7 +187,9 @@ pub fn evaluate_numeric_expression(
148
187
  }
149
188
  }
150
189
  (Some(_), None) => val,
151
- _ => return None,
190
+ _ => {
191
+ return None;
192
+ }
152
193
  });
153
194
  }
154
195
 
@@ -243,9 +284,9 @@ pub fn evaluate_string_expression(
243
284
  // Try variables first
244
285
  if let Some(val) = vars.get(&p) {
245
286
  match val {
246
- crate::core::shared::value::Value::String(s) => out.push_str(s),
247
- crate::core::shared::value::Value::Number(n) => out.push_str(&format!("{}", n)),
248
- crate::core::shared::value::Value::Boolean(b) => out.push_str(&format!("{}", b)),
287
+ Value::String(s) => out.push_str(s),
288
+ Value::Number(n) => out.push_str(&format!("{}", n)),
289
+ Value::Boolean(b) => out.push_str(&format!("{}", b)),
249
290
  other => out.push_str(&format!("{:?}", other)),
250
291
  }
251
292
  continue;
@@ -1,9 +1,11 @@
1
1
  use crate::core::{
2
2
  audio::engine::AudioEngine,
3
3
  parser::statement::{Statement, StatementKind},
4
- shared::value::Value,
5
- store::variable::VariableTable,
4
+ plugin::runner::WasmPluginRunner,
5
+ store::{global::GlobalStore, variable::VariableTable},
6
6
  };
7
+ use devalang_types::Value;
8
+ use devalang_utils::logger::{Logger, LogLevel};
7
9
 
8
10
  use std::collections::HashMap;
9
11
 
@@ -11,6 +13,7 @@ pub fn interprete_call_arrow_statement(
11
13
  stmt: &Statement,
12
14
  audio_engine: &mut AudioEngine,
13
15
  variable_table: &VariableTable,
16
+ global_store: &GlobalStore,
14
17
  base_bpm: f32,
15
18
  base_duration: f32,
16
19
  max_end_time: &mut f32,
@@ -26,38 +29,44 @@ pub fn interprete_call_arrow_statement(
26
29
  } = &stmt.kind
27
30
  {
28
31
  let Some(Value::Statement(synth_stmt)) = variable_table.get(target) else {
29
- println!("❌ Synth '{}' not found in variable table", target);
32
+ let logger = Logger::new();
33
+ logger.log_message(LogLevel::Error, &format!("Synth '{}' not found in variable table", target));
30
34
  return (*max_end_time, cursor_copy);
31
35
  };
32
36
 
33
37
  let Value::Map(synth_map) = &synth_stmt.value else {
34
- println!(
35
- "Invalid synth statement for '{}', expected a map.",
36
- target
37
- );
38
+ let logger = Logger::new();
39
+ logger.log_message(LogLevel::Error, &format!("Invalid synth statement for '{}', expected a map.", target));
38
40
  return (*max_end_time, cursor_copy);
39
41
  };
40
42
 
41
43
  let Some(Value::String(entity)) = synth_map.get("entity") else {
42
- println!("❌ Missing 'entity' key in synth '{}'.", target);
44
+ let logger = Logger::new();
45
+ logger.log_message(LogLevel::Error, &format!("Missing 'entity' key in synth '{}'.", target));
43
46
  return (*max_end_time, cursor_copy);
44
47
  };
45
48
 
46
49
  if entity != "synth" {
47
- println!("❌ '{}' is not a synth, entity is '{}'.", target, entity);
50
+ let logger = Logger::new();
51
+ logger.log_message(LogLevel::Error, &format!("'{}' is not a synth, entity is '{}'.", target, entity));
48
52
  return (*max_end_time, cursor_copy);
49
53
  }
50
54
 
51
55
  let Some(Value::Map(value_map)) = synth_map.get("value") else {
52
- println!("❌ Missing 'value' map in synth '{}'.", target);
56
+ let logger = Logger::new();
57
+ logger.log_message(LogLevel::Error, &format!("Missing 'value' map in synth '{}'.", target));
53
58
  return (*max_end_time, cursor_copy);
54
59
  };
55
60
 
56
- let Some(Value::String(waveform)) = value_map.get("waveform") else {
57
- println!("❌ Missing or invalid 'waveform' in synth '{}'.", target);
58
- return (*max_end_time, cursor_copy);
61
+ let waveform_str = match value_map.get("waveform") {
62
+ Some(Value::String(s)) => s.clone(),
63
+ Some(Value::Identifier(s)) => s.clone(),
64
+ _ => {
65
+ let logger = Logger::new();
66
+ logger.log_message(LogLevel::Error, &format!("Missing or invalid 'waveform' in synth '{}'.", target));
67
+ return (*max_end_time, cursor_copy);
68
+ }
59
69
  };
60
-
61
70
  let Some(Value::Map(params)) = value_map.get("parameters") else {
62
71
  println!("❌ Missing or invalid 'parameters' in synth '{}'.", target);
63
72
  return (*max_end_time, cursor_copy);
@@ -73,7 +82,7 @@ pub fn interprete_call_arrow_statement(
73
82
  .filter(|arg| !matches!(arg, Value::Unknown))
74
83
  .collect();
75
84
 
76
- let Some(Value::Identifier(note_name)) = filtered_args.get(0).map(|v| (*v).clone())
85
+ let Some(Value::Identifier(note_name)) = filtered_args.first().map(|v| (*v).clone())
77
86
  else {
78
87
  println!(
79
88
  "❌ Invalid or missing argument for 'note' method on '{}'.",
@@ -84,13 +93,10 @@ pub fn interprete_call_arrow_statement(
84
93
 
85
94
  let mut note_params = HashMap::new();
86
95
  if let Some(arg1) = filtered_args.get(1) {
87
- match (*arg1).clone() {
88
- Value::Map(map) => {
89
- for (key, value) in map {
90
- note_params.insert(key, value);
91
- }
96
+ if let Value::Map(map) = (*arg1).clone() {
97
+ for (key, value) in map {
98
+ note_params.insert(key, value);
92
99
  }
93
- _ => {}
94
100
  }
95
101
  }
96
102
 
@@ -134,16 +140,89 @@ pub fn interprete_call_arrow_statement(
134
140
  _ => None,
135
141
  };
136
142
 
137
- audio_engine.insert_note(
138
- waveform.clone(),
139
- final_freq,
140
- amp_note,
141
- start_time * 1000.0,
142
- duration_ms,
143
- synth_params,
144
- note_params,
145
- automation,
146
- );
143
+ // If waveform references a plugin alias (e.g., alias.synth), use the WASM plugin runner
144
+ if waveform_str.contains('.') && waveform_str.ends_with(".synth") {
145
+ let alias = waveform_str.split('.').next().unwrap_or("");
146
+ if let Some(Value::String(uri)) = variable_table.get(alias) {
147
+ if let Some(id) = uri.strip_prefix("devalang://plugin/") {
148
+ let mut parts = id.split('.');
149
+ let author = parts.next().unwrap_or("");
150
+ let name = parts.next().unwrap_or("");
151
+ let key = format!("{}:{}", author, name);
152
+ if let Some((_info, wasm_bytes)) = global_store.plugins.get(&key) {
153
+ // Prepare buffer (stereo f32)
154
+ let sample_rate = 44100.0_f32;
155
+ let total_samples = ((duration_ms / 1000.0) * sample_rate) as usize;
156
+ let channels = 2usize;
157
+ let start_index = ((start_time * sample_rate) as usize) * channels;
158
+ let required_len = start_index + total_samples * channels;
159
+ if audio_engine.buffer.len() < required_len {
160
+ audio_engine.buffer.resize(required_len, 0);
161
+ }
162
+ let mut fbuf = vec![0.0f32; total_samples * channels];
163
+ let runner = WasmPluginRunner::new();
164
+ let mut params_num: std::collections::HashMap<String, f32> =
165
+ std::collections::HashMap::new();
166
+ let mut params_str: std::collections::HashMap<String, String> =
167
+ std::collections::HashMap::new();
168
+ for (k, v) in synth_params.iter() {
169
+ match v {
170
+ Value::Number(n) => {
171
+ params_num.insert(k.clone(), *n);
172
+ }
173
+ Value::String(s) => {
174
+ params_str.insert(k.clone(), s.clone());
175
+ }
176
+ Value::Identifier(s) => {
177
+ params_str.insert(k.clone(), s.clone());
178
+ }
179
+ _ => {}
180
+ }
181
+ }
182
+ let _ = runner.render_note_with_params_in_place(
183
+ wasm_bytes,
184
+ &mut fbuf,
185
+ None,
186
+ final_freq,
187
+ amp_note,
188
+ duration_ms as i32,
189
+ 44100,
190
+ 2,
191
+ &params_num,
192
+ Some(&params_str),
193
+ );
194
+ for (i, sample) in
195
+ fbuf.iter().enumerate().take(total_samples * channels)
196
+ {
197
+ let s = (sample.clamp(-1.0, 1.0) * (i16::MAX as f32)) as i16;
198
+ let idx = start_index + i;
199
+ audio_engine.buffer[idx] =
200
+ audio_engine.buffer[idx].saturating_add(s);
201
+ }
202
+ } else {
203
+ let logger = Logger::new();
204
+ logger.log_message(LogLevel::Warning, &format!("Plugin bytes not found for key '{}' (alias '{}').", key, alias));
205
+ }
206
+ } else {
207
+ let logger = Logger::new();
208
+ logger.log_message(LogLevel::Warning, &format!("Invalid plugin URI in alias '{}': {}", alias, uri));
209
+ }
210
+ } else {
211
+ let logger = Logger::new();
212
+ logger.log_message(LogLevel::Warning, &format!("Plugin alias '{}' not found in variable table.", alias));
213
+ }
214
+ } else {
215
+ audio_engine.insert_note(
216
+ waveform_str.clone(),
217
+ final_freq,
218
+ amp_note,
219
+ start_time * 1000.0,
220
+ duration_ms,
221
+ synth_params,
222
+ note_params,
223
+ automation,
224
+ );
225
+ }
147
226
 
148
227
  *max_end_time = (*max_end_time).max(end_time);
149
228
 
@@ -155,7 +234,8 @@ pub fn interprete_call_arrow_statement(
155
234
 
156
235
  return (*max_end_time, end_time);
157
236
  } else {
158
- println!("❌ Unknown method '{}' on synth '{}'.", method, target);
237
+ let logger = Logger::new();
238
+ logger.log_message(LogLevel::Error, &format!("Unknown method '{}' on synth '{}'.", method, target));
159
239
  }
160
240
  }
161
241
 
@@ -181,7 +261,7 @@ fn extract_f32(map: &HashMap<String, Value>, key: &str, base_bpm: f32) -> Option
181
261
  }
182
262
 
183
263
  fn note_to_freq(note: &str) -> f32 {
184
- let notes = vec![
264
+ let notes = [
185
265
  "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
186
266
  ];
187
267