@devaloop/devalang 0.0.1-alpha.15 → 0.0.1-alpha.16

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 (173) hide show
  1. package/.devalang +2 -0
  2. package/.github/workflows/ci.yml +92 -0
  3. package/Cargo.toml +60 -58
  4. package/README.md +1 -1
  5. package/docs/CHANGELOG.md +34 -1
  6. package/docs/CONTRIBUTING.md +101 -1
  7. package/docs/ROADMAP.md +1 -1
  8. package/docs/TODO.md +1 -1
  9. package/examples/automation.deva +1 -3
  10. package/examples/bank.deva +4 -4
  11. package/examples/events.deva +12 -0
  12. package/examples/function.deva +4 -4
  13. package/examples/index.deva +3 -5
  14. package/examples/loop.deva +5 -11
  15. package/examples/pattern.deva +8 -0
  16. package/examples/plugin.deva +12 -11
  17. package/examples/variables.deva +1 -1
  18. package/out-tsc/bin/index.js +51 -7
  19. package/out-tsc/index.js +3 -1
  20. package/out-tsc/scripts/postbuild.js +9 -10
  21. package/out-tsc/scripts/postinstall.js +49 -0
  22. package/package.json +12 -4
  23. package/project-version.json +3 -3
  24. package/rust/cli/bank.rs +462 -455
  25. package/rust/cli/build.rs +252 -199
  26. package/rust/cli/check.rs +221 -180
  27. package/rust/cli/driver.rs +297 -292
  28. package/rust/cli/generator.rs +1 -0
  29. package/rust/cli/init.rs +87 -79
  30. package/rust/cli/install.rs +35 -32
  31. package/rust/cli/login.rs +127 -134
  32. package/rust/cli/mod.rs +13 -11
  33. package/rust/cli/play.rs +1123 -218
  34. package/rust/cli/telemetry.rs +19 -0
  35. package/rust/cli/template.rs +69 -57
  36. package/rust/cli/update.rs +6 -4
  37. package/rust/common/api.rs +5 -5
  38. package/rust/common/mod.rs +3 -3
  39. package/rust/config/driver.rs +118 -94
  40. package/rust/config/loader.rs +165 -156
  41. package/rust/config/mod.rs +4 -2
  42. package/rust/config/settings.rs +91 -0
  43. package/rust/config/stats.rs +257 -0
  44. package/rust/core/audio/engine.rs +696 -659
  45. package/rust/core/audio/evaluator.rs +263 -132
  46. package/rust/core/audio/interpreter/arrow_call.rs +198 -187
  47. package/rust/core/audio/interpreter/call.rs +98 -95
  48. package/rust/core/audio/interpreter/condition.rs +70 -71
  49. package/rust/core/audio/interpreter/driver.rs +487 -231
  50. package/rust/core/audio/interpreter/function.rs +26 -21
  51. package/rust/core/audio/interpreter/let_.rs +38 -26
  52. package/rust/core/audio/interpreter/load.rs +18 -18
  53. package/rust/core/audio/interpreter/loop_.rs +113 -106
  54. package/rust/core/audio/interpreter/mod.rs +14 -14
  55. package/rust/core/audio/interpreter/sleep.rs +27 -28
  56. package/rust/core/audio/interpreter/spawn.rs +105 -102
  57. package/rust/core/audio/interpreter/tempo.rs +19 -16
  58. package/rust/core/audio/interpreter/trigger.rs +239 -210
  59. package/rust/core/audio/loader/mod.rs +1 -1
  60. package/rust/core/audio/loader/trigger.rs +100 -94
  61. package/rust/core/audio/mod.rs +7 -7
  62. package/rust/core/audio/player.rs +64 -64
  63. package/rust/core/audio/renderer.rs +56 -53
  64. package/rust/core/audio/special/easing.rs +189 -120
  65. package/rust/core/audio/special/env.rs +43 -41
  66. package/rust/core/audio/special/math.rs +102 -92
  67. package/rust/core/audio/special/mod.rs +9 -9
  68. package/rust/core/audio/special/modulator.rs +143 -120
  69. package/rust/core/builder/mod.rs +80 -85
  70. package/rust/core/debugger/lexer.rs +27 -27
  71. package/rust/core/debugger/mod.rs +24 -23
  72. package/rust/core/debugger/module.rs +55 -47
  73. package/rust/core/debugger/preprocessor.rs +27 -27
  74. package/rust/core/debugger/store.rs +40 -39
  75. package/rust/core/error/mod.rs +80 -69
  76. package/rust/core/lexer/handler/arrow.rs +82 -82
  77. package/rust/core/lexer/handler/at.rs +21 -21
  78. package/rust/core/lexer/handler/brace.rs +41 -41
  79. package/rust/core/lexer/handler/colon.rs +21 -21
  80. package/rust/core/lexer/handler/comment.rs +30 -30
  81. package/rust/core/lexer/handler/dot.rs +21 -21
  82. package/rust/core/lexer/handler/driver.rs +337 -292
  83. package/rust/core/lexer/handler/identifier.rs +46 -43
  84. package/rust/core/lexer/handler/indent.rs +66 -66
  85. package/rust/core/lexer/handler/mod.rs +16 -16
  86. package/rust/core/lexer/handler/newline.rs +23 -23
  87. package/rust/core/lexer/handler/number.rs +31 -31
  88. package/rust/core/lexer/handler/operator.rs +46 -46
  89. package/rust/core/lexer/handler/parenthesis.rs +41 -41
  90. package/rust/core/lexer/handler/slash.rs +21 -21
  91. package/rust/core/lexer/handler/string.rs +63 -63
  92. package/rust/core/lexer/mod.rs +54 -51
  93. package/rust/core/lexer/token.rs +97 -94
  94. package/rust/core/mod.rs +11 -11
  95. package/rust/core/parser/driver.rs +513 -490
  96. package/rust/core/parser/handler/arrow_call.rs +233 -227
  97. package/rust/core/parser/handler/at.rs +245 -162
  98. package/rust/core/parser/handler/bank.rs +94 -69
  99. package/rust/core/parser/handler/condition.rs +80 -74
  100. package/rust/core/parser/handler/dot.rs +143 -135
  101. package/rust/core/parser/handler/identifier/automate.rs +257 -194
  102. package/rust/core/parser/handler/identifier/call.rs +91 -88
  103. package/rust/core/parser/handler/identifier/emit.rs +66 -0
  104. package/rust/core/parser/handler/identifier/function.rs +100 -91
  105. package/rust/core/parser/handler/identifier/group.rs +85 -75
  106. package/rust/core/parser/handler/identifier/let_.rs +158 -143
  107. package/rust/core/parser/handler/identifier/mod.rs +54 -56
  108. package/rust/core/parser/handler/identifier/on.rs +98 -0
  109. package/rust/core/parser/handler/identifier/print.rs +52 -29
  110. package/rust/core/parser/handler/identifier/sleep.rs +36 -33
  111. package/rust/core/parser/handler/identifier/spawn.rs +91 -88
  112. package/rust/core/parser/handler/identifier/synth.rs +65 -63
  113. package/rust/core/parser/handler/loop_.rs +170 -89
  114. package/rust/core/parser/handler/mod.rs +8 -8
  115. package/rust/core/parser/handler/tempo.rs +53 -47
  116. package/rust/core/parser/mod.rs +4 -4
  117. package/rust/core/parser/statement.rs +142 -113
  118. package/rust/core/plugin/loader.rs +123 -48
  119. package/rust/core/plugin/mod.rs +2 -1
  120. package/rust/core/plugin/runner.rs +296 -0
  121. package/rust/core/preprocessor/loader.rs +515 -326
  122. package/rust/core/preprocessor/mod.rs +4 -4
  123. package/rust/core/preprocessor/module.rs +60 -58
  124. package/rust/core/preprocessor/processor.rs +99 -101
  125. package/rust/core/preprocessor/resolver/bank.rs +51 -48
  126. package/rust/core/preprocessor/resolver/call.rs +100 -101
  127. package/rust/core/preprocessor/resolver/condition.rs +97 -97
  128. package/rust/core/preprocessor/resolver/driver.rs +310 -280
  129. package/rust/core/preprocessor/resolver/function.rs +69 -68
  130. package/rust/core/preprocessor/resolver/group.rs +96 -91
  131. package/rust/core/preprocessor/resolver/let_.rs +32 -28
  132. package/rust/core/preprocessor/resolver/loop_.rs +320 -121
  133. package/rust/core/preprocessor/resolver/mod.rs +15 -15
  134. package/rust/core/preprocessor/resolver/spawn.rs +76 -73
  135. package/rust/core/preprocessor/resolver/synth.rs +56 -50
  136. package/rust/core/preprocessor/resolver/tempo.rs +50 -49
  137. package/rust/core/preprocessor/resolver/trigger.rs +113 -115
  138. package/rust/core/preprocessor/resolver/value.rs +81 -81
  139. package/rust/core/shared/duration.rs +9 -9
  140. package/rust/core/shared/mod.rs +3 -3
  141. package/rust/core/shared/value.rs +35 -32
  142. package/rust/core/store/function.rs +34 -34
  143. package/rust/core/store/global.rs +55 -38
  144. package/rust/core/store/mod.rs +5 -5
  145. package/rust/core/store/variable.rs +37 -34
  146. package/rust/core/utils/mod.rs +2 -2
  147. package/rust/core/utils/path.rs +37 -31
  148. package/rust/core/utils/validation.rs +35 -36
  149. package/rust/installer/addon.rs +84 -80
  150. package/rust/installer/bank.rs +62 -65
  151. package/rust/installer/mod.rs +5 -5
  152. package/rust/installer/plugin.rs +54 -55
  153. package/rust/installer/utils.rs +56 -56
  154. package/rust/lib.rs +156 -164
  155. package/rust/main.rs +250 -144
  156. package/rust/utils/error.rs +200 -51
  157. package/rust/utils/file.rs +38 -35
  158. package/rust/utils/first_usage.rs +76 -0
  159. package/rust/utils/logger.rs +195 -143
  160. package/rust/utils/mod.rs +9 -7
  161. package/rust/utils/signature.rs +19 -17
  162. package/rust/utils/spinner.rs +22 -19
  163. package/rust/utils/telemetry.rs +292 -0
  164. package/rust/utils/watcher.rs +34 -33
  165. package/templates/minimal/README.md +97 -121
  166. package/templates/welcome/README.md +97 -121
  167. package/typescript/bin/index.ts +19 -5
  168. package/typescript/index.ts +3 -1
  169. package/typescript/scripts/postbuild.ts +10 -6
  170. package/typescript/scripts/postinstall.ts +56 -0
  171. package/typescript/scripts/version/bump.ts +0 -1
  172. package/typescript/scripts/version/index.ts +0 -1
  173. package/out-tsc/bin/devalang.exe +0 -0
@@ -1,187 +1,198 @@
1
- use crate::core::{
2
- audio::engine::AudioEngine,
3
- parser::statement::{ Statement, StatementKind },
4
- shared::value::Value,
5
- store::variable::VariableTable,
6
- };
7
-
8
- use std::collections::HashMap;
9
-
10
- pub fn interprete_call_arrow_statement(
11
- stmt: &Statement,
12
- audio_engine: &mut AudioEngine,
13
- variable_table: &VariableTable,
14
- base_bpm: f32,
15
- base_duration: f32,
16
- max_end_time: &mut f32,
17
- mut cursor_time: Option<&mut f32>,
18
- update_cursor: bool
19
- ) -> (f32, f32) {
20
- let cursor_copy = cursor_time
21
- .as_ref()
22
- .map(|c| **c)
23
- .unwrap_or(0.0);
24
-
25
- if let StatementKind::ArrowCall { target, method, args } = &stmt.kind {
26
- let Some(Value::Statement(synth_stmt)) = variable_table.get(target) else {
27
- println!("❌ Synth '{}' not found in variable table", target);
28
- return (*max_end_time, cursor_copy);
29
- };
30
-
31
- let Value::Map(synth_map) = &synth_stmt.value else {
32
- println!("❌ Invalid synth statement for '{}', expected a map.", target);
33
- return (*max_end_time, cursor_copy);
34
- };
35
-
36
- let Some(Value::String(entity)) = synth_map.get("entity") else {
37
- println!("❌ Missing 'entity' key in synth '{}'.", target);
38
- return (*max_end_time, cursor_copy);
39
- };
40
-
41
- if entity != "synth" {
42
- println!("❌ '{}' is not a synth, entity is '{}'.", target, entity);
43
- return (*max_end_time, cursor_copy);
44
- }
45
-
46
- let Some(Value::Map(value_map)) = synth_map.get("value") else {
47
- println!("❌ Missing 'value' map in synth '{}'.", target);
48
- return (*max_end_time, cursor_copy);
49
- };
50
-
51
- let Some(Value::String(waveform)) = value_map.get("waveform") else {
52
- println!("❌ Missing or invalid 'waveform' in synth '{}'.", target);
53
- return (*max_end_time, cursor_copy);
54
- };
55
-
56
- let Some(Value::Map(params)) = value_map.get("parameters") else {
57
- println!("❌ Missing or invalid 'parameters' in synth '{}'.", target);
58
- return (*max_end_time, cursor_copy);
59
- };
60
-
61
- // Synth parameters
62
- let synth_params = params.clone();
63
- let amp = extract_f32(&synth_params, "amp", base_bpm).unwrap_or(1.0);
64
-
65
- if method == "note" {
66
- let filtered_args: Vec<_> = args
67
- .iter()
68
- .filter(|arg| !matches!(arg, Value::Unknown))
69
- .collect();
70
-
71
- let Some(Value::Identifier(note_name)) = filtered_args.get(0).map(|v| (*v).clone()) else {
72
- println!("❌ Invalid or missing argument for 'note' method on '{}'.", target);
73
- return (*max_end_time, cursor_copy);
74
- };
75
-
76
- let mut note_params = HashMap::new();
77
- if let Some(arg1) = filtered_args.get(1) {
78
- match (*arg1).clone() {
79
- Value::Map(map) => {
80
- for (key, value) in map {
81
- note_params.insert(key, value);
82
- }
83
- }
84
- _ => {}
85
- }
86
- }
87
-
88
- // Note parameters and calculations
89
- let amp_note = extract_f32(&note_params, "amp", base_bpm).unwrap_or(amp);
90
- let duration_ms = extract_f32(&note_params, "duration", base_bpm)
91
- .unwrap_or(base_duration * 1000.0);
92
-
93
- let duration_secs = duration_ms / 1000.0;
94
- let final_freq = note_to_freq(&note_name);
95
- let start_time = cursor_copy;
96
- let end_time = start_time + duration_secs;
97
-
98
- // Fetch automation map if present:
99
- // - Global (per-synth): key "<target>__automation" => map with key "params"
100
- // - Per-note: note parameter "automate" => map
101
- let auto_key = format!("{}__automation", target);
102
- let synth_automation = match variable_table.get(&auto_key) {
103
- Some(Value::Map(map)) => match map.get("params") {
104
- Some(Value::Map(p)) => Some(p.clone()),
105
- _ => None,
106
- },
107
- _ => None,
108
- };
109
-
110
- let note_automation = match note_params.get("automate") {
111
- Some(Value::Map(m)) => Some(m.clone()),
112
- _ => None,
113
- };
114
-
115
- // Merge: per-note overrides synth automation per key (volume/pan/pitch)
116
- let automation = match (synth_automation, note_automation) {
117
- (Some(mut a), Some(n)) => { for (k, v) in n { a.insert(k, v); } Some(a) },
118
- (None, Some(n)) => Some(n),
119
- (Some(a), None) => Some(a),
120
- _ => None,
121
- };
122
-
123
- audio_engine.insert_note(
124
- waveform.clone(),
125
- final_freq,
126
- amp_note,
127
- start_time * 1000.0,
128
- duration_ms,
129
- synth_params,
130
- note_params,
131
- automation
132
- );
133
-
134
- *max_end_time = (*max_end_time).max(end_time);
135
-
136
- if update_cursor {
137
- if let Some(c) = cursor_time.as_mut() {
138
- **c = end_time;
139
- }
140
- }
141
-
142
- return (*max_end_time, end_time);
143
- } else {
144
- println!("❌ Unknown method '{}' on synth '{}'.", method, target);
145
- }
146
- }
147
-
148
- (*max_end_time, cursor_copy)
149
- }
150
-
151
- fn extract_f32(map: &HashMap<String, Value>, key: &str, base_bpm: f32) -> Option<f32> {
152
- map.get(key).and_then(|v| {
153
- match v {
154
- Value::Number(n) => Some(*n),
155
- Value::Beat(beat_str) => {
156
- let parts: Vec<&str> = beat_str.split('/').collect();
157
- if parts.len() == 2 {
158
- let numerator = parts[0].parse::<f32>().ok()?;
159
- let denominator = parts[1].parse::<f32>().ok()?;
160
-
161
- Some((numerator / denominator) * ((60.0 / base_bpm) * 1000.0))
162
- } else {
163
- None
164
- }
165
- }
166
- _ => None,
167
- }
168
- })
169
- }
170
-
171
- fn note_to_freq(note: &str) -> f32 {
172
- let notes = vec!["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
173
-
174
- if note.len() < 2 || note.len() > 3 {
175
- return 440.0;
176
- }
177
-
178
- let (name, octave_str) = note.split_at(note.len() - 1);
179
- let semitone = notes
180
- .iter()
181
- .position(|&n| n == name)
182
- .unwrap_or(9) as i32;
183
- let octave = octave_str.parse::<i32>().unwrap_or(4);
184
- let midi_note = (octave + 1) * 12 + semitone;
185
-
186
- 440.0 * (2.0_f32).powf(((midi_note as f32) - 69.0) / 12.0)
187
- }
1
+ use crate::core::{
2
+ audio::engine::AudioEngine,
3
+ parser::statement::{Statement, StatementKind},
4
+ shared::value::Value,
5
+ store::variable::VariableTable,
6
+ };
7
+
8
+ use std::collections::HashMap;
9
+
10
+ pub fn interprete_call_arrow_statement(
11
+ stmt: &Statement,
12
+ audio_engine: &mut AudioEngine,
13
+ variable_table: &VariableTable,
14
+ base_bpm: f32,
15
+ base_duration: f32,
16
+ max_end_time: &mut f32,
17
+ mut cursor_time: Option<&mut f32>,
18
+ update_cursor: bool,
19
+ ) -> (f32, f32) {
20
+ let cursor_copy = cursor_time.as_ref().map(|c| **c).unwrap_or(0.0);
21
+
22
+ if let StatementKind::ArrowCall {
23
+ target,
24
+ method,
25
+ args,
26
+ } = &stmt.kind
27
+ {
28
+ let Some(Value::Statement(synth_stmt)) = variable_table.get(target) else {
29
+ println!("❌ Synth '{}' not found in variable table", target);
30
+ return (*max_end_time, cursor_copy);
31
+ };
32
+
33
+ let Value::Map(synth_map) = &synth_stmt.value else {
34
+ println!(
35
+ "❌ Invalid synth statement for '{}', expected a map.",
36
+ target
37
+ );
38
+ return (*max_end_time, cursor_copy);
39
+ };
40
+
41
+ let Some(Value::String(entity)) = synth_map.get("entity") else {
42
+ println!("❌ Missing 'entity' key in synth '{}'.", target);
43
+ return (*max_end_time, cursor_copy);
44
+ };
45
+
46
+ if entity != "synth" {
47
+ println!("❌ '{}' is not a synth, entity is '{}'.", target, entity);
48
+ return (*max_end_time, cursor_copy);
49
+ }
50
+
51
+ let Some(Value::Map(value_map)) = synth_map.get("value") else {
52
+ println!("❌ Missing 'value' map in synth '{}'.", target);
53
+ return (*max_end_time, cursor_copy);
54
+ };
55
+
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);
59
+ };
60
+
61
+ let Some(Value::Map(params)) = value_map.get("parameters") else {
62
+ println!("❌ Missing or invalid 'parameters' in synth '{}'.", target);
63
+ return (*max_end_time, cursor_copy);
64
+ };
65
+
66
+ // Synth parameters
67
+ let synth_params = params.clone();
68
+ let amp = extract_f32(&synth_params, "amp", base_bpm).unwrap_or(1.0);
69
+
70
+ if method == "note" {
71
+ let filtered_args: Vec<_> = args
72
+ .iter()
73
+ .filter(|arg| !matches!(arg, Value::Unknown))
74
+ .collect();
75
+
76
+ let Some(Value::Identifier(note_name)) = filtered_args.get(0).map(|v| (*v).clone())
77
+ else {
78
+ println!(
79
+ "❌ Invalid or missing argument for 'note' method on '{}'.",
80
+ target
81
+ );
82
+ return (*max_end_time, cursor_copy);
83
+ };
84
+
85
+ let mut note_params = HashMap::new();
86
+ 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
+ }
92
+ }
93
+ _ => {}
94
+ }
95
+ }
96
+
97
+ // Note parameters and calculations
98
+ let amp_note = extract_f32(&note_params, "amp", base_bpm).unwrap_or(amp);
99
+ let duration_ms =
100
+ extract_f32(&note_params, "duration", base_bpm).unwrap_or(base_duration * 1000.0);
101
+
102
+ let duration_secs = duration_ms / 1000.0;
103
+ let final_freq = note_to_freq(&note_name);
104
+ let start_time = cursor_copy;
105
+ let end_time = start_time + duration_secs;
106
+
107
+ // Fetch automation map if present:
108
+ // - Global (per-synth): key "<target>__automation" => map with key "params"
109
+ // - Per-note: note parameter "automate" => map
110
+ let auto_key = format!("{}__automation", target);
111
+ let synth_automation = match variable_table.get(&auto_key) {
112
+ Some(Value::Map(map)) => match map.get("params") {
113
+ Some(Value::Map(p)) => Some(p.clone()),
114
+ _ => None,
115
+ },
116
+ _ => None,
117
+ };
118
+
119
+ let note_automation = match note_params.get("automate") {
120
+ Some(Value::Map(m)) => Some(m.clone()),
121
+ _ => None,
122
+ };
123
+
124
+ // Merge: per-note overrides synth automation per key (volume/pan/pitch)
125
+ let automation = match (synth_automation, note_automation) {
126
+ (Some(mut a), Some(n)) => {
127
+ for (k, v) in n {
128
+ a.insert(k, v);
129
+ }
130
+ Some(a)
131
+ }
132
+ (None, Some(n)) => Some(n),
133
+ (Some(a), None) => Some(a),
134
+ _ => None,
135
+ };
136
+
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
+ );
147
+
148
+ *max_end_time = (*max_end_time).max(end_time);
149
+
150
+ if update_cursor {
151
+ if let Some(c) = cursor_time.as_mut() {
152
+ **c = end_time;
153
+ }
154
+ }
155
+
156
+ return (*max_end_time, end_time);
157
+ } else {
158
+ println!("❌ Unknown method '{}' on synth '{}'.", method, target);
159
+ }
160
+ }
161
+
162
+ (*max_end_time, cursor_copy)
163
+ }
164
+
165
+ fn extract_f32(map: &HashMap<String, Value>, key: &str, base_bpm: f32) -> Option<f32> {
166
+ map.get(key).and_then(|v| match v {
167
+ Value::Number(n) => Some(*n),
168
+ Value::Beat(beat_str) => {
169
+ let parts: Vec<&str> = beat_str.split('/').collect();
170
+ if parts.len() == 2 {
171
+ let numerator = parts[0].parse::<f32>().ok()?;
172
+ let denominator = parts[1].parse::<f32>().ok()?;
173
+
174
+ Some((numerator / denominator) * ((60.0 / base_bpm) * 1000.0))
175
+ } else {
176
+ None
177
+ }
178
+ }
179
+ _ => None,
180
+ })
181
+ }
182
+
183
+ fn note_to_freq(note: &str) -> f32 {
184
+ let notes = vec![
185
+ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
186
+ ];
187
+
188
+ if note.len() < 2 || note.len() > 3 {
189
+ return 440.0;
190
+ }
191
+
192
+ let (name, octave_str) = note.split_at(note.len() - 1);
193
+ let semitone = notes.iter().position(|&n| n == name).unwrap_or(9) as i32;
194
+ let octave = octave_str.parse::<i32>().unwrap_or(4);
195
+ let midi_note = (octave + 1) * 12 + semitone;
196
+
197
+ 440.0 * (2.0_f32).powf(((midi_note as f32) - 69.0) / 12.0)
198
+ }
@@ -1,95 +1,98 @@
1
- use crate::core::{
2
- audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
3
- parser::statement::{Statement, StatementKind},
4
- shared::value::Value,
5
- store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
6
- };
7
-
8
- pub fn interprete_call_statement(
9
- stmt: &Statement,
10
- audio_engine: &mut AudioEngine,
11
- variable_table: &VariableTable,
12
- functions: &FunctionTable,
13
- global_store: &GlobalStore,
14
- base_bpm: f32,
15
- base_duration: f32,
16
- max_end_time: f32,
17
- cursor_time: f32,
18
- ) -> (f32, f32) {
19
- match &stmt.kind {
20
- StatementKind::Call { name, args } => {
21
- // Classic function call case
22
- if let Some(func) = functions.functions.get(name) {
23
- if func.parameters.len() != args.len() {
24
- eprintln!(
25
- "❌ Function '{}' expects {} args, got {}",
26
- name,
27
- func.parameters.len(),
28
- args.len()
29
- );
30
- return (max_end_time, cursor_time);
31
- }
32
-
33
- let mut local_vars = VariableTable::with_parent(variable_table.clone());
34
- for (param, arg) in func.parameters.iter().zip(args) {
35
- local_vars.set(param.clone(), arg.clone());
36
- }
37
-
38
- return execute_audio_block(
39
- audio_engine,
40
- global_store,
41
- local_vars,
42
- functions.clone(),
43
- &func.body,
44
- base_bpm,
45
- base_duration,
46
- max_end_time,
47
- cursor_time,
48
- );
49
- }
50
-
51
- // Group case
52
- if let Some(group_stmt) = find_group(name, variable_table, global_store) {
53
- if let Value::Map(map) = &group_stmt.value {
54
- if let Some(Value::Block(body)) = map.get("body") {
55
- return execute_audio_block(
56
- audio_engine,
57
- global_store,
58
- variable_table.clone(),
59
- functions.clone(),
60
- &body,
61
- base_bpm,
62
- base_duration,
63
- max_end_time,
64
- cursor_time,
65
- );
66
- }
67
- }
68
- }
69
-
70
- eprintln!("❌ Function or group '{}' not found", name);
71
- }
72
-
73
- _ => eprintln!("❌ interprete_call_statement expected Call, got {:?}", stmt.kind),
74
- }
75
-
76
- (max_end_time, cursor_time)
77
- }
78
-
79
- fn find_group<'a>(
80
- name: &str,
81
- variable_table: &'a VariableTable,
82
- global_store: &'a GlobalStore,
83
- ) -> Option<&'a Statement> {
84
- if let Some(Value::Statement(stmt_box)) = variable_table.get(name) {
85
- if let StatementKind::Group = stmt_box.kind {
86
- return Some(stmt_box);
87
- }
88
- }
89
- if let Some(Value::Statement(stmt_box)) = global_store.variables.variables.get(name) {
90
- if let StatementKind::Group = stmt_box.kind {
91
- return Some(stmt_box);
92
- }
93
- }
94
- None
95
- }
1
+ use crate::core::{
2
+ audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
3
+ parser::statement::{Statement, StatementKind},
4
+ shared::value::Value,
5
+ store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
6
+ };
7
+
8
+ pub fn interprete_call_statement(
9
+ stmt: &Statement,
10
+ audio_engine: &mut AudioEngine,
11
+ variable_table: &VariableTable,
12
+ functions: &FunctionTable,
13
+ global_store: &GlobalStore,
14
+ base_bpm: f32,
15
+ base_duration: f32,
16
+ max_end_time: f32,
17
+ cursor_time: f32,
18
+ ) -> (f32, f32) {
19
+ match &stmt.kind {
20
+ StatementKind::Call { name, args } => {
21
+ // Classic function call case
22
+ if let Some(func) = functions.functions.get(name) {
23
+ if func.parameters.len() != args.len() {
24
+ eprintln!(
25
+ "❌ Function '{}' expects {} args, got {}",
26
+ name,
27
+ func.parameters.len(),
28
+ args.len()
29
+ );
30
+ return (max_end_time, cursor_time);
31
+ }
32
+
33
+ let mut local_vars = VariableTable::with_parent(variable_table.clone());
34
+ for (param, arg) in func.parameters.iter().zip(args) {
35
+ local_vars.set(param.clone(), arg.clone());
36
+ }
37
+
38
+ return execute_audio_block(
39
+ audio_engine,
40
+ global_store,
41
+ local_vars,
42
+ functions.clone(),
43
+ &func.body,
44
+ base_bpm,
45
+ base_duration,
46
+ max_end_time,
47
+ cursor_time,
48
+ );
49
+ }
50
+
51
+ // Group case
52
+ if let Some(group_stmt) = find_group(name, variable_table, global_store) {
53
+ if let Value::Map(map) = &group_stmt.value {
54
+ if let Some(Value::Block(body)) = map.get("body") {
55
+ return execute_audio_block(
56
+ audio_engine,
57
+ global_store,
58
+ variable_table.clone(),
59
+ functions.clone(),
60
+ &body,
61
+ base_bpm,
62
+ base_duration,
63
+ max_end_time,
64
+ cursor_time,
65
+ );
66
+ }
67
+ }
68
+ }
69
+
70
+ eprintln!("❌ Function or group '{}' not found", name);
71
+ }
72
+
73
+ _ => eprintln!(
74
+ "❌ interprete_call_statement expected Call, got {:?}",
75
+ stmt.kind
76
+ ),
77
+ }
78
+
79
+ (max_end_time, cursor_time)
80
+ }
81
+
82
+ fn find_group<'a>(
83
+ name: &str,
84
+ variable_table: &'a VariableTable,
85
+ global_store: &'a GlobalStore,
86
+ ) -> Option<&'a Statement> {
87
+ if let Some(Value::Statement(stmt_box)) = variable_table.get(name) {
88
+ if let StatementKind::Group = stmt_box.kind {
89
+ return Some(stmt_box);
90
+ }
91
+ }
92
+ if let Some(Value::Statement(stmt_box)) = global_store.variables.variables.get(name) {
93
+ if let StatementKind::Group = stmt_box.kind {
94
+ return Some(stmt_box);
95
+ }
96
+ }
97
+ None
98
+ }