@devaloop/devalang 0.0.1-beta.1 → 0.0.1-beta.3

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 (207) hide show
  1. package/.devalang +9 -10
  2. package/Cargo.toml +84 -80
  3. package/README.md +10 -7
  4. package/docs/CHANGELOG.md +83 -0
  5. package/docs/ROADMAP.md +6 -2
  6. package/docs/TODO.md +3 -14
  7. package/examples/bus.deva +10 -0
  8. package/examples/chain.deva +19 -0
  9. package/examples/effect.deva +2 -0
  10. package/examples/filter.deva +11 -0
  11. package/examples/lfo.deva +9 -0
  12. package/examples/plugin.deva +10 -10
  13. package/examples/routing.deva +23 -0
  14. package/examples/synth.deva +11 -1
  15. package/examples/synth_types.deva +17 -0
  16. package/out-tsc/bin/project-version.json +6 -0
  17. package/out-tsc/core/functions/index.d.ts +5 -0
  18. package/out-tsc/core/functions/index.js +11 -0
  19. package/out-tsc/pkg/devalang_core.d.ts +2 -0
  20. package/out-tsc/pkg/devalang_core.js +17 -2
  21. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +1 -0
  22. package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
  23. package/out-tsc/scripts/version/copy-to-binary.js +79 -0
  24. package/package.json +23 -10
  25. package/project-version.json +3 -3
  26. package/rust/bindings/Cargo.toml +9 -0
  27. package/rust/bindings/src/lib.rs +86 -0
  28. package/rust/cli/addon/commands.rs +35 -0
  29. package/rust/cli/addon/download.rs +234 -0
  30. package/rust/cli/addon/install.rs +33 -0
  31. package/rust/cli/addon/list.rs +224 -0
  32. package/rust/cli/addon/metadata.rs +124 -0
  33. package/rust/cli/addon/mod.rs +8 -0
  34. package/rust/cli/addon/remove.rs +271 -0
  35. package/rust/cli/addon/update.rs +305 -0
  36. package/rust/cli/{install/addon.rs → addon/utils.rs} +34 -43
  37. package/rust/cli/build/commands.rs +153 -103
  38. package/rust/cli/build/mod.rs +2 -2
  39. package/rust/cli/build/process.rs +165 -146
  40. package/rust/cli/check/mod.rs +208 -208
  41. package/rust/cli/discover/commands.rs +53 -31
  42. package/rust/cli/discover/config.rs +2 -4
  43. package/rust/cli/discover/install.rs +139 -28
  44. package/rust/cli/discover/metadata.rs +3 -3
  45. package/rust/cli/login/commands.rs +124 -124
  46. package/rust/cli/me/commands.rs +52 -0
  47. package/rust/cli/me/mod.rs +1 -0
  48. package/rust/cli/mod.rs +2 -2
  49. package/rust/cli/parser.rs +76 -70
  50. package/rust/cli/play/commands.rs +375 -324
  51. package/rust/cli/play/mod.rs +5 -5
  52. package/rust/cli/play/process.rs +159 -150
  53. package/rust/cli/play/realtime.rs +91 -91
  54. package/rust/cli/telemetry/commands.rs +22 -22
  55. package/rust/cli/telemetry/event_creator.rs +80 -80
  56. package/rust/cli/telemetry/mod.rs +3 -3
  57. package/rust/cli/telemetry/send.rs +51 -51
  58. package/rust/cli/template/commands.rs +69 -69
  59. package/rust/config/driver.rs +112 -103
  60. package/rust/config/mod.rs +3 -3
  61. package/rust/config/ops.rs +26 -26
  62. package/rust/config/settings.rs +101 -101
  63. package/rust/core/audio/engine/driver.rs +237 -0
  64. package/rust/core/audio/engine/export.rs +169 -0
  65. package/rust/core/audio/engine/helpers.rs +178 -170
  66. package/rust/core/audio/engine/mod.rs +56 -7
  67. package/rust/core/audio/engine/notes/dsp.rs +88 -0
  68. package/rust/core/audio/engine/notes/mod.rs +53 -0
  69. package/rust/core/audio/engine/notes/params.rs +294 -0
  70. package/rust/core/audio/engine/sample/insert.rs +300 -0
  71. package/rust/core/audio/engine/sample/mod.rs +40 -0
  72. package/rust/core/audio/engine/sample/padding.rs +170 -0
  73. package/rust/core/audio/evaluator/condition.rs +61 -0
  74. package/rust/core/audio/evaluator/mod.rs +9 -0
  75. package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +152 -310
  76. package/rust/core/audio/evaluator/rhs.rs +16 -0
  77. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  78. package/rust/core/audio/interpreter/driver.rs +574 -542
  79. package/rust/core/audio/interpreter/mod.rs +2 -14
  80. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +179 -0
  81. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -0
  82. package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
  83. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +3 -0
  84. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +371 -0
  85. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  86. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  87. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  88. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  89. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  90. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  91. package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +2 -4
  92. package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +36 -5
  93. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +72 -71
  94. package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +24 -26
  95. package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +36 -38
  96. package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +17 -19
  97. package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +115 -114
  98. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  99. package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
  100. package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +54 -4
  101. package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
  102. package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +242 -239
  103. package/rust/core/audio/loader/trigger.rs +98 -97
  104. package/rust/core/audio/mod.rs +6 -7
  105. package/rust/core/audio/special/easing.rs +189 -189
  106. package/rust/core/audio/special/env.rs +45 -45
  107. package/rust/core/audio/special/math.rs +134 -134
  108. package/rust/core/audio/special/modulator.rs +143 -143
  109. package/rust/core/builder/mod.rs +129 -86
  110. package/rust/core/debugger/{module.rs → logs.rs} +52 -55
  111. package/rust/core/debugger/mod.rs +30 -30
  112. package/rust/core/debugger/store.rs +38 -40
  113. package/rust/core/error/mod.rs +269 -269
  114. package/rust/core/lexer/driver.rs +2 -4
  115. package/rust/core/mod.rs +9 -10
  116. package/rust/core/parser/driver/block.rs +111 -0
  117. package/rust/core/parser/driver/cursor.rs +82 -0
  118. package/rust/core/parser/driver/driver_impl.rs +159 -0
  119. package/rust/core/parser/driver/mod.rs +6 -0
  120. package/rust/core/parser/driver/parse_array.rs +120 -0
  121. package/rust/core/parser/driver/parse_map.rs +247 -0
  122. package/rust/core/parser/driver/parser.rs +160 -0
  123. package/rust/core/parser/handler/arrow_call.rs +90 -15
  124. package/rust/core/parser/handler/at.rs +279 -279
  125. package/rust/core/parser/handler/bank.rs +104 -104
  126. package/rust/core/parser/handler/condition.rs +83 -83
  127. package/rust/core/parser/handler/dot.rs +148 -148
  128. package/rust/core/parser/handler/identifier/automate.rs +254 -254
  129. package/rust/core/parser/handler/identifier/call.rs +91 -91
  130. package/rust/core/parser/handler/identifier/emit.rs +70 -70
  131. package/rust/core/parser/handler/identifier/function.rs +113 -113
  132. package/rust/core/parser/handler/identifier/group.rs +89 -89
  133. package/rust/core/parser/handler/identifier/let_.rs +173 -173
  134. package/rust/core/parser/handler/identifier/mod.rs +55 -55
  135. package/rust/core/parser/handler/identifier/on.rs +107 -107
  136. package/rust/core/parser/handler/identifier/print.rs +49 -49
  137. package/rust/core/parser/handler/identifier/sleep.rs +96 -43
  138. package/rust/core/parser/handler/identifier/spawn.rs +91 -91
  139. package/rust/core/parser/handler/identifier/synth.rs +39 -3
  140. package/rust/core/parser/handler/loop_.rs +194 -194
  141. package/rust/core/parser/handler/pattern.rs +25 -2
  142. package/rust/core/parser/handler/tempo.rs +105 -57
  143. package/rust/core/parser/statement.rs +10 -11
  144. package/rust/core/plugin/loader.rs +137 -137
  145. package/rust/core/plugin/runner/mod.rs +11 -0
  146. package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +206 -72
  147. package/rust/core/plugin/runner/wasm32.rs +44 -0
  148. package/rust/core/preprocessor/loader/inject.rs +313 -0
  149. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  150. package/rust/core/preprocessor/loader/mod.rs +235 -0
  151. package/rust/core/preprocessor/module.rs +55 -60
  152. package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +107 -114
  153. package/rust/core/preprocessor/processor/mod.rs +1 -0
  154. package/rust/core/preprocessor/resolver/function.rs +69 -69
  155. package/rust/core/preprocessor/resolver/group.rs +122 -94
  156. package/rust/core/preprocessor/resolver/pattern.rs +14 -2
  157. package/rust/core/store/global.rs +57 -61
  158. package/rust/core/store/mod.rs +1 -5
  159. package/rust/lib.rs +323 -308
  160. package/rust/macros/Cargo.toml +14 -0
  161. package/rust/macros/src/lib.rs +52 -0
  162. package/rust/main.rs +336 -143
  163. package/rust/types/Cargo.toml +1 -1
  164. package/rust/types/src/addons.rs +57 -55
  165. package/rust/types/src/config.rs +82 -74
  166. package/rust/types/src/lib.rs +15 -12
  167. package/rust/types/src/plugin.rs +20 -0
  168. package/rust/types/src/store.rs +139 -0
  169. package/rust/types/src/telemetry.rs +85 -85
  170. package/rust/utils/Cargo.toml +5 -2
  171. package/rust/utils/src/file.rs +477 -94
  172. package/rust/utils/src/first_usage.rs +97 -97
  173. package/rust/utils/src/lib.rs +9 -9
  174. package/rust/utils/src/logger.rs +200 -200
  175. package/rust/utils/src/path.rs +158 -88
  176. package/rust/utils/src/signature.rs +41 -41
  177. package/rust/utils/src/spinner.rs +20 -20
  178. package/rust/utils/src/version.rs +58 -27
  179. package/rust/utils/src/watcher.rs +46 -46
  180. package/rust/web/api.rs +5 -5
  181. package/rust/web/auth.rs +5 -0
  182. package/rust/web/cdn.rs +34 -34
  183. package/rust/web/forge.rs +5 -0
  184. package/rust/web/mod.rs +2 -0
  185. package/tests/integration.rs +21 -21
  186. package/typescript/core/functions/index.ts +11 -0
  187. package/typescript/pkg/devalang_core.ts +20 -4
  188. package/typescript/scripts/version/copy-to-binary.ts +82 -0
  189. package/rust/cli/bank/api.rs +0 -122
  190. package/rust/cli/bank/commands.rs +0 -275
  191. package/rust/cli/bank/mod.rs +0 -29
  192. package/rust/cli/install/bank.rs +0 -53
  193. package/rust/cli/install/commands.rs +0 -35
  194. package/rust/cli/install/mod.rs +0 -4
  195. package/rust/cli/install/plugin.rs +0 -61
  196. package/rust/core/audio/engine/sample.rs +0 -366
  197. package/rust/core/audio/engine/synth.rs +0 -325
  198. package/rust/core/audio/interpreter/arrow_call.rs +0 -311
  199. package/rust/core/audio/renderer.rs +0 -54
  200. package/rust/core/parser/driver.rs +0 -584
  201. package/rust/core/preprocessor/loader.rs +0 -637
  202. package/rust/core/store/export.rs +0 -28
  203. package/rust/core/store/function.rs +0 -40
  204. package/rust/core/store/import.rs +0 -28
  205. package/rust/core/store/variable.rs +0 -51
  206. package/rust/core/utils/mod.rs +0 -1
  207. package/rust/core/utils/path.rs +0 -37
@@ -1,97 +1,98 @@
1
- use crate::core::{parser::statement::StatementKind, store::variable::VariableTable};
2
- use devalang_types::{Duration, Value};
3
-
4
- pub fn load_trigger(
5
- trigger: &Value,
6
- duration: &Duration,
7
- _effects: &Option<Value>,
8
- base_duration: f32,
9
- variable_table: VariableTable,
10
- ) -> (String, f32) {
11
- let mut trigger_path = String::new();
12
- let mut duration_as_secs = 0.0;
13
-
14
- match trigger {
15
- Value::String(src) => {
16
- trigger_path = src.to_string();
17
- }
18
- Value::Identifier(src) => {
19
- trigger_path = src.to_string();
20
- }
21
-
22
- Value::Map(map) => {
23
- if let Some(Value::String(src)) = map.get("entity") {
24
- trigger_path = format!("devalang://bank/{}", src);
25
- } else if let Some(Value::Identifier(src)) = map.get("entity") {
26
- trigger_path = format!("devalang://bank/{}", src);
27
- } else {
28
- eprintln!(
29
- "❌ Trigger map must contain an 'entity' key with a string or identifier value."
30
- );
31
- }
32
- }
33
- Value::Sample(src) => {
34
- trigger_path = src.to_string();
35
- }
36
- Value::Statement(stmt) => {
37
- if let StatementKind::Trigger {
38
- entity,
39
- duration: _,
40
- effects: _,
41
- } = &stmt.kind
42
- {
43
- trigger_path = entity.clone();
44
- } else {
45
- eprintln!(
46
- "❌ Trigger statement must be of type 'Trigger', found: {:?}",
47
- stmt.kind
48
- );
49
- }
50
- }
51
- _ => {
52
- eprintln!(
53
- "❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
54
- trigger
55
- );
56
- }
57
- }
58
-
59
- match duration {
60
- Duration::Identifier(duration_identifier) => {
61
- if duration_identifier == "auto" {
62
- duration_as_secs = base_duration;
63
- } else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
64
- duration_as_secs = *num;
65
- } else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
66
- duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
67
- } else if let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
68
- {
69
- duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
70
- } else {
71
- eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
72
- }
73
- }
74
-
75
- Duration::Number(num) => {
76
- duration_as_secs = *num;
77
- }
78
-
79
- Duration::Auto => {
80
- duration_as_secs = base_duration;
81
- }
82
-
83
- Duration::Beat(beat_str) => {
84
- let parts: Vec<&str> = beat_str.split('/').collect();
85
-
86
- if parts.len() == 2 {
87
- let numerator: f32 = parts[0].parse().unwrap_or(1.0);
88
- let denominator: f32 = parts[1].parse().unwrap_or(1.0);
89
- duration_as_secs = (numerator / denominator) * base_duration;
90
- } else {
91
- eprintln!("❌ Invalid beat duration format: {}", beat_str);
92
- }
93
- }
94
- }
95
-
96
- (trigger_path, duration_as_secs)
97
- }
1
+ use crate::core::parser::statement::StatementKind;
2
+ use devalang_types::store::VariableTable;
3
+ use devalang_types::{Duration, Value};
4
+
5
+ pub fn load_trigger(
6
+ trigger: &Value,
7
+ duration: &Duration,
8
+ _effects: &Option<Value>,
9
+ base_duration: f32,
10
+ variable_table: VariableTable,
11
+ ) -> (String, f32) {
12
+ let mut trigger_path = String::new();
13
+ let mut duration_as_secs = 0.0;
14
+
15
+ match trigger {
16
+ Value::String(src) => {
17
+ trigger_path = src.to_string();
18
+ }
19
+ Value::Identifier(src) => {
20
+ trigger_path = src.to_string();
21
+ }
22
+
23
+ Value::Map(map) => {
24
+ if let Some(Value::String(src)) = map.get("entity") {
25
+ trigger_path = format!("devalang://bank/{}", src);
26
+ } else if let Some(Value::Identifier(src)) = map.get("entity") {
27
+ trigger_path = format!("devalang://bank/{}", src);
28
+ } else {
29
+ eprintln!(
30
+ "❌ Trigger map must contain an 'entity' key with a string or identifier value."
31
+ );
32
+ }
33
+ }
34
+ Value::Sample(src) => {
35
+ trigger_path = src.to_string();
36
+ }
37
+ Value::Statement(stmt) => {
38
+ if let StatementKind::Trigger {
39
+ entity,
40
+ duration: _,
41
+ effects: _,
42
+ } = &stmt.kind
43
+ {
44
+ trigger_path = entity.clone();
45
+ } else {
46
+ eprintln!(
47
+ "❌ Trigger statement must be of type 'Trigger', found: {:?}",
48
+ stmt.kind
49
+ );
50
+ }
51
+ }
52
+ _ => {
53
+ eprintln!(
54
+ "❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
55
+ trigger
56
+ );
57
+ }
58
+ }
59
+
60
+ match duration {
61
+ Duration::Identifier(duration_identifier) => {
62
+ if duration_identifier == "auto" {
63
+ duration_as_secs = base_duration;
64
+ } else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
65
+ duration_as_secs = *num;
66
+ } else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
67
+ duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
68
+ } else if let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
69
+ {
70
+ duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
71
+ } else {
72
+ eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
73
+ }
74
+ }
75
+
76
+ Duration::Number(num) => {
77
+ duration_as_secs = *num;
78
+ }
79
+
80
+ Duration::Auto => {
81
+ duration_as_secs = base_duration;
82
+ }
83
+
84
+ Duration::Beat(beat_str) => {
85
+ let parts: Vec<&str> = beat_str.split('/').collect();
86
+
87
+ if parts.len() == 2 {
88
+ let numerator: f32 = parts[0].parse().unwrap_or(1.0);
89
+ let denominator: f32 = parts[1].parse().unwrap_or(1.0);
90
+ duration_as_secs = (numerator / denominator) * base_duration;
91
+ } else {
92
+ eprintln!("❌ Invalid beat duration format: {}", beat_str);
93
+ }
94
+ }
95
+ }
96
+
97
+ (trigger_path, duration_as_secs)
98
+ }
@@ -1,7 +1,6 @@
1
- pub mod engine;
2
- pub mod evaluator;
3
- pub mod interpreter;
4
- pub mod loader;
5
- pub mod player;
6
- pub mod renderer;
7
- pub mod special;
1
+ pub mod engine;
2
+ pub mod evaluator;
3
+ pub mod interpreter;
4
+ pub mod loader;
5
+ pub mod player;
6
+ pub mod special;
@@ -1,189 +1,189 @@
1
- use crate::core::store::variable::VariableTable;
2
-
3
- // Basic easing functions operating on t in [0,1]
4
- fn easing_value(func: &str, t: f32) -> Option<f32> {
5
- let x = t.clamp(0.0, 1.0);
6
- match func {
7
- "linear" => Some(x),
8
- "easeInQuad" => Some(x * x),
9
- "easeOutQuad" => Some(x * (2.0 - x)),
10
- "easeInOutQuad" => {
11
- if x < 0.5 {
12
- Some(2.0 * x * x)
13
- } else {
14
- Some(-1.0 + (4.0 - 2.0 * x) * x)
15
- }
16
- }
17
- // Cubic
18
- "easeInCubic" => Some(x * x * x),
19
- "easeOutCubic" => Some(1.0 - (1.0 - x).powi(3)),
20
- "easeInOutCubic" => {
21
- if x < 0.5 {
22
- Some(4.0 * x * x * x)
23
- } else {
24
- Some(1.0 - (-2.0 * x + 2.0).powi(3) / 2.0)
25
- }
26
- }
27
- // Quartic
28
- "easeInQuart" => Some(x.powi(4)),
29
- "easeOutQuart" => Some(1.0 - (1.0 - x).powi(4)),
30
- "easeInOutQuart" => {
31
- if x < 0.5 {
32
- Some(8.0 * x.powi(4))
33
- } else {
34
- Some(1.0 - (-2.0 * x + 2.0).powi(4) / 2.0)
35
- }
36
- }
37
- // Exponential
38
- "easeInExpo" => Some(if x <= 0.0 {
39
- 0.0
40
- } else {
41
- 2.0_f32.powf(10.0 * x - 10.0)
42
- }),
43
- "easeOutExpo" => Some(if x >= 1.0 {
44
- 1.0
45
- } else {
46
- 1.0 - 2.0_f32.powf(-10.0 * x)
47
- }),
48
- "easeInOutExpo" => Some(if x <= 0.0 {
49
- 0.0
50
- } else if x >= 1.0 {
51
- 1.0
52
- } else if x < 0.5 {
53
- 2.0_f32.powf(20.0 * x - 10.0) / 2.0
54
- } else {
55
- (2.0 - 2.0_f32.powf(-20.0 * x + 10.0)) / 2.0
56
- }),
57
- // Back (overshoot c ~ 1.70158)
58
- "easeInBack" => {
59
- let c = 1.70158;
60
- Some((c + 1.0) * x * x * x - c * x * x)
61
- }
62
- "easeOutBack" => {
63
- let c = 1.70158;
64
- let y = 1.0 - x;
65
- Some(1.0 - ((c + 1.0) * y * y * y - c * y * y))
66
- }
67
- "easeInOutBack" => {
68
- let c1 = 1.70158;
69
- let c2 = c1 * 1.525;
70
- let x2 = x * 2.0;
71
- if x2 < 1.0 {
72
- Some((x2 * x2 * ((c2 + 1.0) * x2 - c2)) / 2.0)
73
- } else {
74
- let x2 = x2 - 2.0;
75
- Some((x2 * x2 * ((c2 + 1.0) * x2 + c2)) / 2.0 + 1.0)
76
- }
77
- }
78
- // Elastic
79
- "easeInElastic" => {
80
- if x == 0.0 {
81
- Some(0.0)
82
- } else if x == 1.0 {
83
- Some(1.0)
84
- } else {
85
- let c = 2.0 * std::f32::consts::PI / 3.0;
86
- Some(-(2.0_f32.powf(10.0 * x - 10.0)) * ((x * 10.0 - 10.75) * c).sin())
87
- }
88
- }
89
- "easeOutElastic" => {
90
- if x == 0.0 {
91
- Some(0.0)
92
- } else if x == 1.0 {
93
- Some(1.0)
94
- } else {
95
- let c = 2.0 * std::f32::consts::PI / 3.0;
96
- Some(2.0_f32.powf(-10.0 * x) * ((x * 10.0 - 0.75) * c).sin() + 1.0)
97
- }
98
- }
99
- "easeInOutElastic" => {
100
- if x == 0.0 {
101
- Some(0.0)
102
- } else if x == 1.0 {
103
- Some(1.0)
104
- } else {
105
- let c = 2.0 * std::f32::consts::PI / 4.5;
106
- if x < 0.5 {
107
- Some(-(2.0_f32.powf(20.0 * x - 10.0)) * ((20.0 * x - 11.125) * c).sin() / 2.0)
108
- } else {
109
- Some(
110
- 2.0_f32.powf(-20.0 * x + 10.0) * ((20.0 * x - 11.125) * c).sin() / 2.0
111
- + 1.0,
112
- )
113
- }
114
- }
115
- }
116
- // Bounce helpers
117
- "easeInBounce" => Some(1.0 - bounce_out(1.0 - x)),
118
- "easeOutBounce" => Some(bounce_out(x)),
119
- "easeInOutBounce" => Some(if x < 0.5 {
120
- (1.0 - bounce_out(1.0 - 2.0 * x)) / 2.0
121
- } else {
122
- (1.0 + bounce_out(2.0 * x - 1.0)) / 2.0
123
- }),
124
- _ => None,
125
- }
126
- }
127
-
128
- fn bounce_out(x: f32) -> f32 {
129
- let n1 = 7.5625;
130
- let d1 = 2.75;
131
- if x < 1.0 / d1 {
132
- n1 * x * x
133
- } else if x < 2.0 / d1 {
134
- let x = x - 1.5 / d1;
135
- n1 * x * x + 0.75
136
- } else if x < 2.5 / d1 {
137
- let x = x - 2.25 / d1;
138
- n1 * x * x + 0.9375
139
- } else {
140
- let x = x - 2.625 / d1;
141
- n1 * x * x + 0.984375
142
- }
143
- }
144
-
145
- // Find and evaluate the first $easing.<fn>(...) occurrence in the string.
146
- // Accepts a single argument expression producing t in [0,1].
147
- pub fn find_and_eval_first_easing_call<EvalFn>(
148
- s: &str,
149
- eval: EvalFn,
150
- vars: &VariableTable,
151
- bpm: f32,
152
- beat: f32,
153
- ) -> Option<String>
154
- where
155
- EvalFn: Fn(&str, &VariableTable, f32, f32) -> Option<f32>,
156
- {
157
- let start = s.find("$easing.")?;
158
- let open_rel = s[start..].find('(')?;
159
- let open = start + open_rel;
160
- let func = &s[start + 9..open];
161
-
162
- // Find matching close parenthesis
163
- let mut depth: i32 = 0;
164
- let mut close_abs: Option<usize> = None;
165
- for (i, ch) in s[open..].char_indices() {
166
- match ch {
167
- '(' => depth += 1,
168
- ')' => {
169
- depth -= 1;
170
- if depth == 0 {
171
- close_abs = Some(open + i);
172
- break;
173
- }
174
- }
175
- _ => {}
176
- }
177
- }
178
- let close = close_abs?;
179
-
180
- let inner = &s[open + 1..close];
181
- let t = eval(inner, vars, bpm, beat)?;
182
- let result = easing_value(func, t)?;
183
-
184
- let mut replaced = String::new();
185
- replaced.push_str(&s[..start]);
186
- replaced.push_str(&result.to_string());
187
- replaced.push_str(&s[close + 1..]);
188
- Some(replaced)
189
- }
1
+ use devalang_types::VariableTable;
2
+
3
+ // Basic easing functions operating on t in [0,1]
4
+ fn easing_value(func: &str, t: f32) -> Option<f32> {
5
+ let x = t.clamp(0.0, 1.0);
6
+ match func {
7
+ "linear" => Some(x),
8
+ "easeInQuad" => Some(x * x),
9
+ "easeOutQuad" => Some(x * (2.0 - x)),
10
+ "easeInOutQuad" => {
11
+ if x < 0.5 {
12
+ Some(2.0 * x * x)
13
+ } else {
14
+ Some(-1.0 + (4.0 - 2.0 * x) * x)
15
+ }
16
+ }
17
+ // Cubic
18
+ "easeInCubic" => Some(x * x * x),
19
+ "easeOutCubic" => Some(1.0 - (1.0 - x).powi(3)),
20
+ "easeInOutCubic" => {
21
+ if x < 0.5 {
22
+ Some(4.0 * x * x * x)
23
+ } else {
24
+ Some(1.0 - (-2.0 * x + 2.0).powi(3) / 2.0)
25
+ }
26
+ }
27
+ // Quartic
28
+ "easeInQuart" => Some(x.powi(4)),
29
+ "easeOutQuart" => Some(1.0 - (1.0 - x).powi(4)),
30
+ "easeInOutQuart" => {
31
+ if x < 0.5 {
32
+ Some(8.0 * x.powi(4))
33
+ } else {
34
+ Some(1.0 - (-2.0 * x + 2.0).powi(4) / 2.0)
35
+ }
36
+ }
37
+ // Exponential
38
+ "easeInExpo" => Some(if x <= 0.0 {
39
+ 0.0
40
+ } else {
41
+ 2.0_f32.powf(10.0 * x - 10.0)
42
+ }),
43
+ "easeOutExpo" => Some(if x >= 1.0 {
44
+ 1.0
45
+ } else {
46
+ 1.0 - 2.0_f32.powf(-10.0 * x)
47
+ }),
48
+ "easeInOutExpo" => Some(if x <= 0.0 {
49
+ 0.0
50
+ } else if x >= 1.0 {
51
+ 1.0
52
+ } else if x < 0.5 {
53
+ 2.0_f32.powf(20.0 * x - 10.0) / 2.0
54
+ } else {
55
+ (2.0 - 2.0_f32.powf(-20.0 * x + 10.0)) / 2.0
56
+ }),
57
+ // Back (overshoot c ~ 1.70158)
58
+ "easeInBack" => {
59
+ let c = 1.70158;
60
+ Some((c + 1.0) * x * x * x - c * x * x)
61
+ }
62
+ "easeOutBack" => {
63
+ let c = 1.70158;
64
+ let y = 1.0 - x;
65
+ Some(1.0 - ((c + 1.0) * y * y * y - c * y * y))
66
+ }
67
+ "easeInOutBack" => {
68
+ let c1 = 1.70158;
69
+ let c2 = c1 * 1.525;
70
+ let x2 = x * 2.0;
71
+ if x2 < 1.0 {
72
+ Some((x2 * x2 * ((c2 + 1.0) * x2 - c2)) / 2.0)
73
+ } else {
74
+ let x2 = x2 - 2.0;
75
+ Some((x2 * x2 * ((c2 + 1.0) * x2 + c2)) / 2.0 + 1.0)
76
+ }
77
+ }
78
+ // Elastic
79
+ "easeInElastic" => {
80
+ if x == 0.0 {
81
+ Some(0.0)
82
+ } else if x == 1.0 {
83
+ Some(1.0)
84
+ } else {
85
+ let c = 2.0 * std::f32::consts::PI / 3.0;
86
+ Some(-(2.0_f32.powf(10.0 * x - 10.0)) * ((x * 10.0 - 10.75) * c).sin())
87
+ }
88
+ }
89
+ "easeOutElastic" => {
90
+ if x == 0.0 {
91
+ Some(0.0)
92
+ } else if x == 1.0 {
93
+ Some(1.0)
94
+ } else {
95
+ let c = 2.0 * std::f32::consts::PI / 3.0;
96
+ Some(2.0_f32.powf(-10.0 * x) * ((x * 10.0 - 0.75) * c).sin() + 1.0)
97
+ }
98
+ }
99
+ "easeInOutElastic" => {
100
+ if x == 0.0 {
101
+ Some(0.0)
102
+ } else if x == 1.0 {
103
+ Some(1.0)
104
+ } else {
105
+ let c = 2.0 * std::f32::consts::PI / 4.5;
106
+ if x < 0.5 {
107
+ Some(-(2.0_f32.powf(20.0 * x - 10.0)) * ((20.0 * x - 11.125) * c).sin() / 2.0)
108
+ } else {
109
+ Some(
110
+ 2.0_f32.powf(-20.0 * x + 10.0) * ((20.0 * x - 11.125) * c).sin() / 2.0
111
+ + 1.0,
112
+ )
113
+ }
114
+ }
115
+ }
116
+ // Bounce helpers
117
+ "easeInBounce" => Some(1.0 - bounce_out(1.0 - x)),
118
+ "easeOutBounce" => Some(bounce_out(x)),
119
+ "easeInOutBounce" => Some(if x < 0.5 {
120
+ (1.0 - bounce_out(1.0 - 2.0 * x)) / 2.0
121
+ } else {
122
+ (1.0 + bounce_out(2.0 * x - 1.0)) / 2.0
123
+ }),
124
+ _ => None,
125
+ }
126
+ }
127
+
128
+ fn bounce_out(x: f32) -> f32 {
129
+ let n1 = 7.5625;
130
+ let d1 = 2.75;
131
+ if x < 1.0 / d1 {
132
+ n1 * x * x
133
+ } else if x < 2.0 / d1 {
134
+ let x = x - 1.5 / d1;
135
+ n1 * x * x + 0.75
136
+ } else if x < 2.5 / d1 {
137
+ let x = x - 2.25 / d1;
138
+ n1 * x * x + 0.9375
139
+ } else {
140
+ let x = x - 2.625 / d1;
141
+ n1 * x * x + 0.984375
142
+ }
143
+ }
144
+
145
+ // Find and evaluate the first $easing.<fn>(...) occurrence in the string.
146
+ // Accepts a single argument expression producing t in [0,1].
147
+ pub fn find_and_eval_first_easing_call<EvalFn>(
148
+ s: &str,
149
+ eval: EvalFn,
150
+ vars: &VariableTable,
151
+ bpm: f32,
152
+ beat: f32,
153
+ ) -> Option<String>
154
+ where
155
+ EvalFn: Fn(&str, &VariableTable, f32, f32) -> Option<f32>,
156
+ {
157
+ let start = s.find("$easing.")?;
158
+ let open_rel = s[start..].find('(')?;
159
+ let open = start + open_rel;
160
+ let func = &s[start + 9..open];
161
+
162
+ // Find matching close parenthesis
163
+ let mut depth: i32 = 0;
164
+ let mut close_abs: Option<usize> = None;
165
+ for (i, ch) in s[open..].char_indices() {
166
+ match ch {
167
+ '(' => depth += 1,
168
+ ')' => {
169
+ depth -= 1;
170
+ if depth == 0 {
171
+ close_abs = Some(open + i);
172
+ break;
173
+ }
174
+ }
175
+ _ => {}
176
+ }
177
+ }
178
+ let close = close_abs?;
179
+
180
+ let inner = &s[open + 1..close];
181
+ let t = eval(inner, vars, bpm, beat)?;
182
+ let result = easing_value(func, t)?;
183
+
184
+ let mut replaced = String::new();
185
+ replaced.push_str(&s[..start]);
186
+ replaced.push_str(&result.to_string());
187
+ replaced.push_str(&s[close + 1..]);
188
+ Some(replaced)
189
+ }