@devaloop/devalang 0.0.1-alpha.8 → 0.0.1-beta.1

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 (277) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +10 -4
  3. package/.github/workflows/ci.yml +103 -0
  4. package/Cargo.toml +80 -48
  5. package/README.md +135 -158
  6. package/docs/CHANGELOG.md +413 -1
  7. package/docs/CONTRIBUTING.md +101 -0
  8. package/docs/ROADMAP.md +10 -7
  9. package/docs/TODO.md +21 -9
  10. package/examples/automation.deva +42 -0
  11. package/examples/bank.deva +7 -0
  12. package/examples/condition.deva +8 -12
  13. package/examples/duration.deva +9 -0
  14. package/examples/events.deva +12 -0
  15. package/examples/function.deva +15 -0
  16. package/examples/group.deva +3 -3
  17. package/examples/index.deva +57 -10
  18. package/examples/loop.deva +7 -12
  19. package/examples/pattern.deva +8 -0
  20. package/examples/plugin.deva +16 -0
  21. package/examples/synth.deva +14 -0
  22. package/examples/variables.deva +2 -2
  23. package/out-tsc/bin/index.d.ts +2 -0
  24. package/out-tsc/bin/index.js +51 -7
  25. package/out-tsc/core/functions/index.d.ts +37 -0
  26. package/out-tsc/core/functions/index.js +76 -0
  27. package/out-tsc/core/index.d.ts +6 -0
  28. package/out-tsc/core/index.js +22 -0
  29. package/out-tsc/core/types/index.d.ts +4 -0
  30. package/out-tsc/core/types/index.js +20 -0
  31. package/out-tsc/core/types/plugin.d.ts +18 -0
  32. package/out-tsc/core/types/plugin.js +2 -0
  33. package/out-tsc/core/types/result.d.ts +27 -0
  34. package/out-tsc/core/types/result.js +2 -0
  35. package/out-tsc/core/types/statement.d.ts +106 -0
  36. package/out-tsc/core/types/statement.js +2 -0
  37. package/out-tsc/core/types/value.d.ts +43 -0
  38. package/out-tsc/core/types/value.js +2 -0
  39. package/out-tsc/index.d.ts +7 -0
  40. package/out-tsc/index.js +42 -1
  41. package/out-tsc/pkg/devalang_core.d.ts +13 -0
  42. package/out-tsc/pkg/devalang_core.js +50 -0
  43. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  44. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  45. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  46. package/out-tsc/scripts/postinstall.d.ts +1 -0
  47. package/out-tsc/scripts/postinstall.js +83 -0
  48. package/out-tsc/scripts/version/bump.d.ts +1 -0
  49. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  50. package/out-tsc/scripts/version/fetch.js +1 -5
  51. package/out-tsc/scripts/version/index.d.ts +1 -0
  52. package/out-tsc/scripts/version/sync.d.ts +1 -0
  53. package/package.json +28 -7
  54. package/project-version.json +4 -4
  55. package/rust/cli/bank/api.rs +122 -0
  56. package/rust/cli/bank/commands.rs +275 -0
  57. package/rust/cli/bank/mod.rs +29 -0
  58. package/rust/cli/build/commands.rs +103 -0
  59. package/rust/cli/build/mod.rs +2 -0
  60. package/rust/cli/build/process.rs +146 -0
  61. package/rust/cli/check/mod.rs +208 -0
  62. package/rust/cli/discover/commands.rs +253 -0
  63. package/rust/cli/discover/config.rs +111 -0
  64. package/rust/cli/discover/fs.rs +19 -0
  65. package/rust/cli/discover/install.rs +103 -0
  66. package/rust/cli/discover/metadata.rs +48 -0
  67. package/rust/cli/discover/mod.rs +5 -0
  68. package/rust/cli/{init.rs → init/commands.rs} +32 -23
  69. package/rust/cli/init/mod.rs +1 -0
  70. package/rust/cli/install/addon.rs +118 -0
  71. package/rust/cli/install/bank.rs +53 -0
  72. package/rust/cli/install/commands.rs +35 -0
  73. package/rust/cli/install/mod.rs +4 -0
  74. package/rust/cli/install/plugin.rs +61 -0
  75. package/rust/cli/login/commands.rs +124 -0
  76. package/rust/cli/login/mod.rs +1 -0
  77. package/rust/cli/mod.rs +12 -205
  78. package/rust/cli/parser.rs +314 -0
  79. package/rust/cli/play/commands.rs +324 -0
  80. package/rust/cli/play/io.rs +17 -0
  81. package/rust/cli/play/mod.rs +5 -0
  82. package/rust/cli/play/process.rs +150 -0
  83. package/rust/cli/play/realtime.rs +91 -0
  84. package/rust/cli/play/utils.rs +23 -0
  85. package/rust/cli/telemetry/commands.rs +22 -0
  86. package/rust/cli/telemetry/event_creator.rs +80 -0
  87. package/rust/cli/telemetry/mod.rs +3 -0
  88. package/rust/cli/telemetry/send.rs +51 -0
  89. package/rust/cli/{template.rs → template/commands.rs} +69 -57
  90. package/rust/cli/template/mod.rs +1 -0
  91. package/rust/cli/update/commands.rs +6 -0
  92. package/rust/cli/update/mod.rs +1 -0
  93. package/rust/config/driver.rs +103 -0
  94. package/rust/config/mod.rs +3 -16
  95. package/rust/config/ops.rs +26 -0
  96. package/rust/config/settings.rs +101 -0
  97. package/rust/core/audio/engine/helpers.rs +170 -0
  98. package/rust/core/audio/engine/mod.rs +7 -0
  99. package/rust/core/audio/engine/sample.rs +366 -0
  100. package/rust/core/audio/engine/synth.rs +325 -0
  101. package/rust/core/audio/evaluator.rs +310 -31
  102. package/rust/core/audio/interpreter/arrow_call.rs +311 -0
  103. package/rust/core/audio/interpreter/automate.rs +18 -0
  104. package/rust/core/audio/interpreter/call.rs +294 -42
  105. package/rust/core/audio/interpreter/condition.rs +71 -65
  106. package/rust/core/audio/interpreter/driver.rs +542 -204
  107. package/rust/core/audio/interpreter/function.rs +26 -0
  108. package/rust/core/audio/interpreter/let_.rs +38 -19
  109. package/rust/core/audio/interpreter/load.rs +19 -18
  110. package/rust/core/audio/interpreter/loop_.rs +114 -59
  111. package/rust/core/audio/interpreter/mod.rs +14 -11
  112. package/rust/core/audio/interpreter/sleep.rs +28 -36
  113. package/rust/core/audio/interpreter/spawn.rs +252 -65
  114. package/rust/core/audio/interpreter/tempo.rs +40 -16
  115. package/rust/core/audio/interpreter/trigger.rs +239 -69
  116. package/rust/core/audio/loader/mod.rs +1 -1
  117. package/rust/core/audio/loader/trigger.rs +97 -52
  118. package/rust/core/audio/mod.rs +7 -6
  119. package/rust/core/audio/player.rs +70 -54
  120. package/rust/core/audio/renderer.rs +54 -57
  121. package/rust/core/audio/special/easing.rs +189 -0
  122. package/rust/core/audio/special/env.rs +45 -0
  123. package/rust/core/audio/special/math.rs +134 -0
  124. package/rust/core/audio/special/mod.rs +9 -0
  125. package/rust/core/audio/special/modulator.rs +143 -0
  126. package/rust/core/builder/mod.rs +86 -80
  127. package/rust/core/debugger/lexer.rs +27 -27
  128. package/rust/core/debugger/mod.rs +30 -21
  129. package/rust/core/debugger/module.rs +55 -0
  130. package/rust/core/debugger/preprocessor.rs +27 -27
  131. package/rust/core/debugger/store.rs +40 -25
  132. package/rust/core/error/mod.rs +269 -60
  133. package/rust/core/lexer/driver.rs +61 -0
  134. package/rust/core/lexer/handler/arrow.rs +82 -0
  135. package/rust/core/lexer/handler/at.rs +21 -21
  136. package/rust/core/lexer/handler/brace.rs +41 -41
  137. package/rust/core/lexer/handler/colon.rs +21 -21
  138. package/rust/core/lexer/handler/comment.rs +30 -30
  139. package/rust/core/lexer/handler/dot.rs +21 -21
  140. package/rust/core/lexer/handler/driver.rs +337 -215
  141. package/rust/core/lexer/handler/identifier.rs +47 -40
  142. package/rust/core/lexer/handler/indent.rs +66 -52
  143. package/rust/core/lexer/handler/mod.rs +15 -13
  144. package/rust/core/lexer/handler/newline.rs +23 -23
  145. package/rust/core/lexer/handler/number.rs +31 -31
  146. package/rust/core/lexer/handler/operator.rs +46 -44
  147. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  148. package/rust/core/lexer/handler/slash.rs +21 -0
  149. package/rust/core/lexer/handler/string.rs +63 -63
  150. package/rust/core/lexer/mod.rs +3 -30
  151. package/rust/core/lexer/token.rs +21 -12
  152. package/rust/core/mod.rs +10 -10
  153. package/rust/core/parser/driver.rs +584 -312
  154. package/rust/core/parser/handler/arrow_call.rs +253 -0
  155. package/rust/core/parser/handler/at.rs +279 -162
  156. package/rust/core/parser/handler/bank.rs +104 -41
  157. package/rust/core/parser/handler/condition.rs +83 -74
  158. package/rust/core/parser/handler/dot.rs +148 -112
  159. package/rust/core/parser/handler/identifier/automate.rs +254 -0
  160. package/rust/core/parser/handler/identifier/call.rs +91 -0
  161. package/rust/core/parser/handler/identifier/emit.rs +70 -0
  162. package/rust/core/parser/handler/identifier/function.rs +113 -0
  163. package/rust/core/parser/handler/identifier/group.rs +89 -0
  164. package/rust/core/parser/handler/identifier/let_.rs +173 -0
  165. package/rust/core/parser/handler/identifier/mod.rs +55 -0
  166. package/rust/core/parser/handler/identifier/on.rs +107 -0
  167. package/rust/core/parser/handler/identifier/print.rs +49 -0
  168. package/rust/core/parser/handler/identifier/sleep.rs +43 -0
  169. package/rust/core/parser/handler/identifier/spawn.rs +91 -0
  170. package/rust/core/parser/handler/identifier/synth.rs +135 -0
  171. package/rust/core/parser/handler/loop_.rs +194 -66
  172. package/rust/core/parser/handler/mod.rs +9 -7
  173. package/rust/core/parser/handler/pattern.rs +74 -0
  174. package/rust/core/parser/handler/tempo.rs +57 -47
  175. package/rust/core/parser/mod.rs +3 -4
  176. package/rust/core/parser/statement.rs +11 -88
  177. package/rust/core/plugin/loader.rs +137 -0
  178. package/rust/core/plugin/mod.rs +2 -0
  179. package/rust/core/plugin/runner.rs +347 -0
  180. package/rust/core/preprocessor/loader.rs +637 -179
  181. package/rust/core/preprocessor/mod.rs +4 -4
  182. package/rust/core/preprocessor/module.rs +60 -53
  183. package/rust/core/preprocessor/processor.rs +114 -67
  184. package/rust/core/preprocessor/resolver/bank.rs +49 -47
  185. package/rust/core/preprocessor/resolver/call.rs +124 -53
  186. package/rust/core/preprocessor/resolver/condition.rs +95 -66
  187. package/rust/core/preprocessor/resolver/driver.rs +324 -182
  188. package/rust/core/preprocessor/resolver/function.rs +69 -0
  189. package/rust/core/preprocessor/resolver/group.rs +94 -118
  190. package/rust/core/preprocessor/resolver/let_.rs +32 -0
  191. package/rust/core/preprocessor/resolver/loop_.rs +318 -145
  192. package/rust/core/preprocessor/resolver/mod.rs +16 -10
  193. package/rust/core/preprocessor/resolver/pattern.rs +83 -0
  194. package/rust/core/preprocessor/resolver/spawn.rs +99 -53
  195. package/rust/core/preprocessor/resolver/synth.rs +54 -0
  196. package/rust/core/preprocessor/resolver/tempo.rs +48 -49
  197. package/rust/core/preprocessor/resolver/trigger.rs +116 -111
  198. package/rust/core/preprocessor/resolver/value.rs +176 -0
  199. package/rust/core/store/export.rs +28 -28
  200. package/rust/core/store/function.rs +40 -0
  201. package/rust/core/store/global.rs +61 -39
  202. package/rust/core/store/import.rs +28 -28
  203. package/rust/core/store/mod.rs +5 -4
  204. package/rust/core/store/variable.rs +51 -28
  205. package/rust/core/utils/mod.rs +1 -2
  206. package/rust/core/utils/path.rs +37 -46
  207. package/rust/lib.rs +308 -117
  208. package/rust/main.rs +364 -65
  209. package/rust/types/Cargo.toml +11 -0
  210. package/rust/types/src/addons.rs +55 -0
  211. package/rust/types/src/ast.rs +202 -0
  212. package/rust/types/src/config.rs +74 -0
  213. package/rust/types/src/lib.rs +12 -0
  214. package/rust/types/src/telemetry.rs +85 -0
  215. package/rust/utils/Cargo.toml +26 -0
  216. package/rust/utils/src/error.rs +186 -0
  217. package/rust/utils/src/file.rs +94 -0
  218. package/rust/utils/src/first_usage.rs +97 -0
  219. package/rust/utils/{mod.rs → src/lib.rs} +9 -6
  220. package/rust/utils/{logger.rs → src/logger.rs} +200 -123
  221. package/rust/utils/src/path.rs +88 -0
  222. package/rust/utils/src/signature.rs +41 -0
  223. package/rust/utils/{spinner.rs → src/spinner.rs} +20 -21
  224. package/rust/utils/src/version.rs +27 -0
  225. package/rust/utils/{watcher.rs → src/watcher.rs} +46 -33
  226. package/rust/web/api.rs +5 -0
  227. package/rust/web/cdn.rs +34 -0
  228. package/rust/web/mod.rs +3 -0
  229. package/rust/web/sso.rs +5 -0
  230. package/templates/minimal/README.md +143 -127
  231. package/templates/welcome/README.md +143 -127
  232. package/templates/welcome/src/index.deva +56 -8
  233. package/templates/welcome/src/variables.deva +2 -4
  234. package/tests/integration.rs +21 -0
  235. package/tests/rust/cli_check_build.rs +21 -0
  236. package/tests/rust/cli_help.rs +12 -0
  237. package/tests/rust/cli_template_list.rs +10 -0
  238. package/tests/rust/cli_version.rs +11 -0
  239. package/tests/typescript/index.spec.ts +136 -0
  240. package/tests/typescript/playhead.spec.ts +36 -0
  241. package/tests/typescript/render_e2e.spec.ts +77 -0
  242. package/tsconfig.json +12 -10
  243. package/typescript/bin/index.ts +19 -5
  244. package/typescript/core/functions/index.ts +83 -0
  245. package/typescript/core/index.ts +6 -0
  246. package/typescript/core/types/index.ts +4 -0
  247. package/typescript/core/types/plugin.ts +19 -0
  248. package/typescript/core/types/result.ts +29 -0
  249. package/typescript/core/types/statement.ts +47 -0
  250. package/typescript/core/types/value.ts +29 -0
  251. package/typescript/index.ts +8 -1
  252. package/typescript/pkg/devalang_core.d.ts +4 -0
  253. package/typescript/pkg/devalang_core.ts +49 -0
  254. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  255. package/typescript/scripts/postinstall.ts +85 -0
  256. package/typescript/scripts/version/bump.ts +0 -1
  257. package/typescript/scripts/version/fetch.ts +1 -6
  258. package/typescript/scripts/version/index.ts +0 -1
  259. package/docs/COMMANDS.md +0 -85
  260. package/docs/CONFIG.md +0 -30
  261. package/docs/SYNTAX.md +0 -210
  262. package/out-tsc/bin/devalang.exe +0 -0
  263. package/out-tsc/scripts/postbuild.js +0 -11
  264. package/rust/cli/build.rs +0 -137
  265. package/rust/cli/check.rs +0 -117
  266. package/rust/cli/play.rs +0 -193
  267. package/rust/config/loader.rs +0 -13
  268. package/rust/core/audio/engine.rs +0 -126
  269. package/rust/core/parser/handler/identifier.rs +0 -262
  270. package/rust/core/shared/duration.rs +0 -8
  271. package/rust/core/shared/mod.rs +0 -2
  272. package/rust/core/shared/value.rs +0 -18
  273. package/rust/core/utils/validation.rs +0 -35
  274. package/rust/utils/file.rs +0 -35
  275. package/rust/utils/signature.rs +0 -17
  276. package/rust/utils/version.rs +0 -15
  277. package/typescript/scripts/postbuild.ts +0 -8
@@ -1,69 +1,239 @@
1
- use crate::core::{
2
- audio::{ engine::AudioEngine, loader::trigger::load_trigger },
3
- parser::statement::{ Statement, StatementKind },
4
- shared::{ duration::Duration, value::Value },
5
- store::variable::VariableTable,
6
- };
7
-
8
- pub fn interprete_trigger_statement(
9
- stmt: &Statement,
10
- audio_engine: &mut AudioEngine,
11
- variable_table: &VariableTable,
12
- base_duration: f32,
13
- cursor_time: f32,
14
- max_end_time: f32
15
- ) -> Option<(f32, f32, AudioEngine)> {
16
- if let StatementKind::Trigger { entity, duration } = &stmt.kind {
17
- if let Some(trigger_val) = variable_table.get(entity) {
18
- let duration_secs = match duration {
19
- Duration::Number(n) => *n,
20
-
21
- Duration::Identifier(id) => {
22
- if id == "auto" {
23
- 1.0
24
- } else {
25
- match variable_table.get(id) {
26
- Some(Value::Number(n)) => *n,
27
- Some(Value::Identifier(other)) if other == "auto" => 1.0,
28
- Some(other) => {
29
- eprintln!(
30
- "❌ Invalid duration reference '{}': expected number, got {:?}",
31
- id,
32
- other
33
- );
34
- return None;
35
- }
36
- None => {
37
- eprintln!("❌ Duration identifier '{}' not found", id);
38
- return None;
39
- }
40
- }
41
- }
42
- }
43
-
44
- Duration::Auto => 1.0,
45
- };
46
-
47
- let duration_final = duration_secs * base_duration;
48
-
49
- let (src, _) = load_trigger(
50
- trigger_val,
51
- duration,
52
- base_duration,
53
- variable_table.clone()
54
- );
55
-
56
- let mut updated_engine = audio_engine.clone();
57
- updated_engine.insert(&src, cursor_time, duration_final, None);
58
-
59
- let new_cursor_time = cursor_time + duration_final;
60
- let new_max_end_time = new_cursor_time.max(max_end_time);
61
-
62
- return Some((new_cursor_time, new_max_end_time, updated_engine));
63
- } else {
64
- eprintln!("❌ Unknown trigger entity: {}", entity);
65
- }
66
- }
67
-
68
- None
69
- }
1
+ use std::collections::HashMap;
2
+
3
+ use crate::core::{
4
+ audio::{engine::AudioEngine, loader::trigger::load_trigger},
5
+ parser::statement::{Statement, StatementKind},
6
+ store::variable::VariableTable,
7
+ };
8
+ use devalang_types::{Duration, Value};
9
+ use devalang_utils::logger::Logger;
10
+
11
+ pub fn interprete_trigger_statement(
12
+ stmt: &Statement,
13
+ audio_engine: &mut AudioEngine,
14
+ variable_table: &VariableTable,
15
+ base_duration: f32,
16
+ cursor_time: f32,
17
+ max_end_time: f32,
18
+ ) -> Option<(f32, f32, AudioEngine)> {
19
+ if let StatementKind::Trigger {
20
+ entity,
21
+ duration,
22
+ effects,
23
+ } = &stmt.kind
24
+ {
25
+ let mut trigger_val = Value::String(entity.clone());
26
+ let mut trigger_src = String::new();
27
+
28
+ match variable_table.variables.get(entity) {
29
+ Some(Value::Identifier(id)) => {
30
+ // Get real value from global variable table
31
+ if let Some(global_table) = &variable_table.parent {
32
+ if let Some(val) = global_table.get(id) {
33
+ trigger_val = val.clone();
34
+ } else {
35
+ eprintln!(
36
+ "❌ Trigger entity '{}' not found in global variable table",
37
+ id
38
+ );
39
+ return None;
40
+ }
41
+ } else if let Some(val) = variable_table.get(id) {
42
+ trigger_val = val.clone();
43
+ } else {
44
+ eprintln!("❌ Trigger entity '{}' not found in variable table", id);
45
+ return None;
46
+ }
47
+ }
48
+ Some(Value::Sample(sample_src)) => {
49
+ // If the entity is a sample, we use its path directly
50
+ trigger_src = sample_src.clone();
51
+ }
52
+ Some(Value::Map(map)) => {
53
+ // If the entity is a map, we assume it contains an "entity" key
54
+ if let Some(Value::String(src)) = map.get("entity") {
55
+ trigger_val = Value::String(src.clone());
56
+ } else if let Some(Value::Identifier(src)) = map.get("entity") {
57
+ trigger_val = Value::Identifier(src.clone());
58
+ } else {
59
+ eprintln!(
60
+ "❌ Trigger map must contain an 'entity' key with a string or identifier value."
61
+ );
62
+ return None;
63
+ }
64
+ }
65
+ _ => {
66
+ trigger_val = Value::String(entity.clone());
67
+ }
68
+ }
69
+
70
+ // If trigger could not be resolved to a known mapping or explicit path, abort early
71
+ if let Value::String(s) = &trigger_val {
72
+ let is_protocol = s.starts_with("devalang://");
73
+ let is_var = variable_table.get(s).is_some()
74
+ || variable_table
75
+ .parent
76
+ .as_ref()
77
+ .and_then(|p| p.get(s))
78
+ .is_some();
79
+ let looks_like_path = s.contains('/')
80
+ || s.ends_with(".wav")
81
+ || s.ends_with(".mp3")
82
+ || s.ends_with(".ogg");
83
+ if !is_protocol && !is_var && !looks_like_path {
84
+ let logger = Logger::new();
85
+ logger.log_error_with_stacktrace(
86
+ &format!("unknown trigger: {}", s),
87
+ &format!("{}:{}:{}", audio_engine.module_name, stmt.line, stmt.column),
88
+ );
89
+ return None;
90
+ }
91
+ }
92
+
93
+ let duration_secs = match duration {
94
+ Duration::Number(n) => *n,
95
+
96
+ Duration::Identifier(id) => {
97
+ if id == "auto" {
98
+ 1.0
99
+ } else {
100
+ match variable_table.get(id) {
101
+ Some(Value::Number(n)) => *n,
102
+ Some(Value::Identifier(other)) if other == "auto" => 1.0,
103
+ Some(other) => {
104
+ eprintln!(
105
+ "❌ Invalid duration reference '{}': expected number, got {:?}",
106
+ id, other
107
+ );
108
+ return None;
109
+ }
110
+ None => {
111
+ eprintln!("❌ Duration identifier '{}' not found", id);
112
+ return None;
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ Duration::Beat(beat_str) => {
119
+ let parts: Vec<&str> = beat_str.split('/').collect();
120
+ if parts.len() != 2 {
121
+ eprintln!("❌ Invalid beat duration format: {}", beat_str);
122
+ return None;
123
+ }
124
+
125
+ let numerator: f32 = parts[0].parse().unwrap_or(1.0);
126
+ let denominator: f32 = parts[1].parse().unwrap_or(4.0);
127
+
128
+ let beats = (numerator / denominator) * 4.0;
129
+
130
+ beats * base_duration
131
+ }
132
+
133
+ Duration::Auto => base_duration,
134
+ };
135
+
136
+ let final_variable_table = if let Some(parent) = &variable_table.parent {
137
+ VariableTable {
138
+ variables: parent.variables.clone(),
139
+ parent: None,
140
+ }
141
+ } else {
142
+ variable_table.clone()
143
+ };
144
+
145
+ let (src, sample_length) = load_trigger(
146
+ &trigger_val,
147
+ duration,
148
+ effects,
149
+ base_duration,
150
+ final_variable_table.clone(),
151
+ );
152
+
153
+ if trigger_src.is_empty() {
154
+ trigger_src = src;
155
+ }
156
+
157
+ let effects = extract_effects(stmt.value.clone());
158
+ let one_shot = effects
159
+ .as_ref()
160
+ .and_then(|map| map.get("one_shot"))
161
+ .and_then(|v| match v {
162
+ Value::Identifier(id) if id == "true" => Some(true),
163
+ Value::String(s) if s == "true" => Some(true),
164
+ _ => None,
165
+ })
166
+ .unwrap_or(false);
167
+
168
+ let play_length = if one_shot {
169
+ sample_length // play entire sample
170
+ } else {
171
+ duration_secs.min(sample_length)
172
+ };
173
+
174
+ let trigger_src = match trigger_val.get("entity") {
175
+ Some(Value::String(src)) => src.clone(),
176
+ Some(Value::Identifier(id)) => id.clone(),
177
+ Some(Value::Statement(stmt)) => {
178
+ if let StatementKind::Trigger { entity, .. } = &stmt.kind {
179
+ entity.clone()
180
+ } else {
181
+ eprintln!("❌ Invalid trigger statement in map: expected 'Trigger' kind");
182
+ return None;
183
+ }
184
+ }
185
+ _ => trigger_src,
186
+ };
187
+
188
+ if let Some(effects_map) = effects {
189
+ audio_engine.insert_sample(
190
+ &trigger_src,
191
+ cursor_time,
192
+ play_length,
193
+ Some(effects_map),
194
+ &final_variable_table,
195
+ );
196
+ } else {
197
+ audio_engine.insert_sample(
198
+ &trigger_src,
199
+ cursor_time,
200
+ play_length,
201
+ None,
202
+ &final_variable_table,
203
+ );
204
+ }
205
+
206
+ let new_cursor_time = cursor_time + duration_secs; // advance by beat duration
207
+ let new_max_end_time = (cursor_time + play_length).max(max_end_time);
208
+
209
+ let updated_engine = audio_engine.clone();
210
+
211
+ return Some((new_cursor_time, new_max_end_time, updated_engine));
212
+ }
213
+
214
+ None
215
+ }
216
+
217
+ fn extract_effects(value: Value) -> Option<HashMap<String, Value>> {
218
+ if let Value::Map(map) = value {
219
+ let mut effects = HashMap::new();
220
+
221
+ for (key, val) in map {
222
+ if key == "effects" {
223
+ if let Value::Map(effect_map) = val {
224
+ for (effect_key, effect_val) in effect_map {
225
+ effects.insert(effect_key, effect_val);
226
+ }
227
+ } else {
228
+ return None; // effects must be a map
229
+ }
230
+ } else {
231
+ return Some(effects);
232
+ }
233
+ }
234
+
235
+ Some(effects)
236
+ } else {
237
+ None
238
+ }
239
+ }
@@ -1 +1 @@
1
- pub mod trigger;
1
+ pub mod trigger;
@@ -1,52 +1,97 @@
1
- use crate::core::{ shared::{ duration::Duration, value::Value }, store::variable::VariableTable };
2
-
3
- pub fn load_trigger(
4
- trigger: &Value,
5
- duration: &Duration,
6
- base_duration: f32,
7
- variable_table: VariableTable
8
- ) -> (String, f32) {
9
- let mut trigger_path = String::new();
10
- let mut duration_as_secs = 0.0;
11
-
12
- match trigger {
13
- Value::String(src) => {
14
- trigger_path = src.to_string();
15
- }
16
- _ => {
17
- eprintln!("❌ Invalid trigger type. Expected a text variable.");
18
- }
19
- }
20
-
21
- match duration {
22
- Duration::Identifier(duration_identifier) => {
23
- if duration_identifier == "auto" {
24
- duration_as_secs = base_duration;
25
- } else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
26
- duration_as_secs = *num;
27
- } else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
28
- duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
29
- } else if
30
- let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
31
- {
32
- duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
33
- } else {
34
- eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
35
- }
36
- }
37
-
38
- Duration::Number(num) => {
39
- duration_as_secs = *num;
40
- }
41
-
42
- Duration::Auto => {
43
- duration_as_secs = base_duration;
44
- }
45
-
46
- _ => {
47
- eprintln!("❌ Invalid duration type. Expected an identifier.");
48
- }
49
- }
50
-
51
- (trigger_path, duration_as_secs)
52
- }
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,6 +1,7 @@
1
- pub mod engine;
2
- pub mod interpreter;
3
- pub mod loader;
4
- pub mod player;
5
- pub mod renderer;
6
- pub mod evaluator;
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,54 +1,70 @@
1
- use rodio::{ Decoder, OutputStream, OutputStreamHandle, Sink, Source };
2
- use std::{ fs::File, io::BufReader };
3
-
4
- pub struct AudioPlayer {
5
- _stream: OutputStream,
6
- handle: OutputStreamHandle,
7
- sink: Sink,
8
- last_path: Option<String>,
9
- }
10
-
11
- impl AudioPlayer {
12
- pub fn new() -> Self {
13
- let (stream, handle) = OutputStream::try_default().unwrap();
14
- let sink = Sink::try_new(&handle).unwrap();
15
-
16
- Self {
17
- _stream: stream,
18
- handle,
19
- sink,
20
- last_path: None,
21
- }
22
- }
23
-
24
- fn load_source(&self, path: &str) -> impl Source<Item = f32> + Send + 'static {
25
- let file = File::open(path).unwrap();
26
- let reader = BufReader::new(file);
27
-
28
- Decoder::new(reader).unwrap().convert_samples()
29
- }
30
-
31
- pub fn play_file_once(&mut self, path: &str) {
32
- self.sink.stop();
33
- self.sink = Sink::try_new(&self.handle).unwrap();
34
-
35
- self.sink.set_volume(1.0);
36
-
37
- let source = self.load_source(path);
38
-
39
- self.sink.append(source);
40
- self.last_path = Some(path.to_string());
41
- }
42
-
43
- pub fn replay_last(&mut self) {
44
- if let Some(path) = self.last_path.clone() {
45
- self.play_file_once(&path);
46
- } else {
47
- eprintln!("⚠️ No previous audio to replay.");
48
- }
49
- }
50
-
51
- pub fn wait_until_end(&self) {
52
- self.sink.sleep_until_end();
53
- }
54
- }
1
+ use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source};
2
+ use std::{fs::File, io::BufReader};
3
+
4
+ pub struct AudioPlayer {
5
+ _stream: OutputStream,
6
+ handle: OutputStreamHandle,
7
+ sink: Sink,
8
+ last_path: Option<String>,
9
+ }
10
+
11
+ impl Default for AudioPlayer {
12
+ fn default() -> Self {
13
+ Self::new()
14
+ }
15
+ }
16
+
17
+ impl AudioPlayer {
18
+ pub fn new() -> Self {
19
+ let (stream, handle) = OutputStream::try_default().unwrap();
20
+ let sink = Sink::try_new(&handle).unwrap();
21
+
22
+ Self {
23
+ _stream: stream,
24
+ handle,
25
+ sink,
26
+ last_path: None,
27
+ }
28
+ }
29
+
30
+ fn load_source(&self, path: &str) -> Option<impl Source<Item = f32> + Send + 'static> {
31
+ if let Ok(file) = File::open(path) {
32
+ let reader = BufReader::new(file);
33
+ match Decoder::new(reader) {
34
+ Ok(decoder) => Some(decoder.convert_samples()),
35
+ Err(e) => {
36
+ eprintln!("❌ Failed to decode audio file '{}': {}", path, e);
37
+ None
38
+ }
39
+ }
40
+ } else {
41
+ eprintln!("❌ Could not open audio file: {}", path);
42
+ None
43
+ }
44
+ }
45
+
46
+ pub fn play_file_once(&mut self, path: &str) {
47
+ self.sink.stop();
48
+ self.sink = Sink::try_new(&self.handle).unwrap();
49
+ self.sink.set_volume(1.0);
50
+
51
+ if let Some(source) = self.load_source(path) {
52
+ self.sink.append(source);
53
+ self.last_path = Some(path.to_string());
54
+ } else {
55
+ eprintln!("⚠️ Skipping playback: failed to load '{}'", path);
56
+ }
57
+ }
58
+
59
+ pub fn replay_last(&mut self) {
60
+ if let Some(path) = self.last_path.clone() {
61
+ self.play_file_once(&path);
62
+ } else {
63
+ eprintln!("⚠️ No previous audio to replay.");
64
+ }
65
+ }
66
+
67
+ pub fn wait_until_end(&self) {
68
+ self.sink.sleep_until_end();
69
+ }
70
+ }