@devaloop/devalang 0.0.1-beta.2 → 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.
- package/Cargo.toml +84 -81
- package/README.md +3 -2
- package/docs/CHANGELOG.md +41 -0
- package/docs/ROADMAP.md +3 -3
- package/examples/chain.deva +19 -0
- package/examples/plugin.deva +10 -10
- package/examples/routing.deva +23 -0
- package/out-tsc/bin/project-version.json +6 -0
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -8
- package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
- package/out-tsc/scripts/version/copy-to-binary.js +79 -0
- package/package.json +23 -10
- package/project-version.json +3 -3
- package/rust/bindings/Cargo.toml +9 -0
- package/rust/bindings/src/lib.rs +86 -0
- package/rust/cli/addon/commands.rs +35 -0
- package/rust/cli/addon/download.rs +234 -0
- package/rust/cli/addon/install.rs +33 -0
- package/rust/cli/addon/list.rs +224 -0
- package/rust/cli/addon/metadata.rs +124 -0
- package/rust/cli/addon/mod.rs +8 -0
- package/rust/cli/addon/remove.rs +271 -0
- package/rust/cli/addon/update.rs +305 -0
- package/rust/cli/{install/addon.rs → addon/utils.rs} +109 -118
- package/rust/cli/build/commands.rs +153 -153
- package/rust/cli/build/process.rs +165 -165
- package/rust/cli/check/mod.rs +208 -208
- package/rust/cli/discover/commands.rs +275 -253
- package/rust/cli/discover/config.rs +109 -111
- package/rust/cli/discover/fs.rs +19 -19
- package/rust/cli/discover/install.rs +214 -103
- package/rust/cli/discover/metadata.rs +48 -48
- package/rust/cli/discover/mod.rs +5 -5
- package/rust/cli/me/commands.rs +52 -0
- package/rust/cli/me/mod.rs +1 -0
- package/rust/cli/mod.rs +12 -12
- package/rust/cli/parser.rs +30 -69
- package/rust/cli/play/commands.rs +375 -375
- package/rust/cli/play/process.rs +159 -159
- package/rust/core/audio/engine/driver.rs +19 -2
- package/rust/core/audio/engine/export.rs +169 -169
- package/rust/core/audio/engine/mod.rs +56 -56
- package/rust/core/audio/engine/notes/dsp.rs +88 -85
- package/rust/core/audio/engine/notes/mod.rs +53 -44
- package/rust/core/audio/engine/notes/params.rs +294 -294
- package/rust/core/audio/engine/sample/insert.rs +148 -47
- package/rust/core/audio/engine/sample/mod.rs +40 -40
- package/rust/core/audio/engine/sample/padding.rs +170 -170
- package/rust/core/audio/evaluator/condition.rs +61 -61
- package/rust/core/audio/evaluator/numeric.rs +152 -152
- package/rust/core/audio/evaluator/rhs.rs +16 -16
- package/rust/core/audio/evaluator/string_expr.rs +94 -94
- package/rust/core/audio/interpreter/driver.rs +574 -574
- package/rust/core/audio/interpreter/mod.rs +2 -2
- package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +9 -5
- package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -384
- package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +1 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +66 -11
- package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -3
- package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -192
- package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -24
- package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -116
- package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -97
- package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -100
- package/rust/core/audio/interpreter/statements/automate.rs +16 -16
- package/rust/core/audio/interpreter/statements/call.rs +31 -1
- package/rust/core/audio/interpreter/statements/condition.rs +72 -72
- package/rust/core/audio/interpreter/statements/function.rs +24 -24
- package/rust/core/audio/interpreter/statements/let_.rs +36 -36
- package/rust/core/audio/interpreter/statements/load.rs +17 -17
- package/rust/core/audio/interpreter/statements/loop_.rs +115 -115
- package/rust/core/audio/interpreter/statements/spawn.rs +51 -2
- package/rust/core/audio/interpreter/statements/trigger.rs +242 -239
- package/rust/core/audio/loader/trigger.rs +98 -98
- package/rust/core/audio/player.rs +70 -70
- package/rust/core/audio/special/mod.rs +9 -9
- package/rust/core/builder/mod.rs +129 -129
- package/rust/core/debugger/lexer.rs +27 -27
- package/rust/core/debugger/logs.rs +52 -52
- package/rust/core/debugger/preprocessor.rs +27 -27
- package/rust/core/debugger/store.rs +38 -38
- package/rust/core/lexer/driver.rs +59 -59
- package/rust/core/lexer/handler/arrow.rs +82 -82
- package/rust/core/lexer/handler/at.rs +21 -21
- package/rust/core/lexer/handler/brace.rs +41 -41
- package/rust/core/lexer/handler/colon.rs +21 -21
- package/rust/core/lexer/handler/comment.rs +30 -30
- package/rust/core/lexer/handler/dot.rs +21 -21
- package/rust/core/lexer/handler/driver.rs +337 -337
- package/rust/core/lexer/handler/identifier.rs +47 -47
- package/rust/core/lexer/handler/indent.rs +66 -66
- package/rust/core/lexer/handler/mod.rs +15 -15
- package/rust/core/lexer/handler/newline.rs +23 -23
- package/rust/core/lexer/handler/number.rs +31 -31
- package/rust/core/lexer/handler/operator.rs +46 -46
- package/rust/core/lexer/handler/parenthesis.rs +41 -41
- package/rust/core/lexer/handler/slash.rs +21 -21
- package/rust/core/lexer/handler/string.rs +63 -63
- package/rust/core/lexer/mod.rs +3 -3
- package/rust/core/mod.rs +9 -9
- package/rust/core/parser/driver/block.rs +111 -111
- package/rust/core/parser/driver/cursor.rs +82 -82
- package/rust/core/parser/driver/driver_impl.rs +21 -1
- package/rust/core/parser/driver/mod.rs +6 -6
- package/rust/core/parser/driver/parse_array.rs +120 -120
- package/rust/core/parser/driver/parse_map.rs +247 -223
- package/rust/core/parser/driver/parser.rs +160 -160
- package/rust/core/parser/handler/arrow_call.rs +65 -14
- package/rust/core/parser/handler/identifier/synth.rs +171 -135
- package/rust/core/parser/handler/mod.rs +9 -9
- package/rust/core/parser/handler/pattern.rs +24 -1
- package/rust/core/plugin/loader.rs +137 -137
- package/rust/core/plugin/mod.rs +2 -2
- package/rust/core/plugin/runner/non_wasm.rs +481 -297
- package/rust/core/plugin/runner/wasm32.rs +1 -0
- package/rust/core/preprocessor/loader/inject.rs +313 -278
- package/rust/core/preprocessor/loader/loader_helpers.rs +110 -110
- package/rust/core/preprocessor/loader/mod.rs +235 -235
- package/rust/core/preprocessor/module.rs +55 -55
- package/rust/core/preprocessor/processor/handlers.rs +107 -107
- package/rust/core/preprocessor/resolver/bank.rs +49 -49
- package/rust/core/preprocessor/resolver/call.rs +124 -124
- package/rust/core/preprocessor/resolver/condition.rs +95 -95
- package/rust/core/preprocessor/resolver/driver.rs +324 -324
- package/rust/core/preprocessor/resolver/function.rs +69 -69
- package/rust/core/preprocessor/resolver/group.rs +122 -122
- package/rust/core/preprocessor/resolver/let_.rs +32 -32
- package/rust/core/preprocessor/resolver/loop_.rs +318 -318
- package/rust/core/preprocessor/resolver/mod.rs +16 -16
- package/rust/core/preprocessor/resolver/pattern.rs +95 -83
- package/rust/core/preprocessor/resolver/spawn.rs +99 -99
- package/rust/core/preprocessor/resolver/synth.rs +54 -54
- package/rust/core/preprocessor/resolver/tempo.rs +48 -48
- package/rust/core/preprocessor/resolver/trigger.rs +116 -116
- package/rust/core/preprocessor/resolver/value.rs +176 -176
- package/rust/core/store/global.rs +57 -57
- package/rust/lib.rs +323 -323
- package/rust/macros/Cargo.toml +14 -0
- package/rust/macros/src/lib.rs +52 -0
- package/rust/main.rs +311 -142
- package/rust/types/Cargo.toml +1 -1
- package/rust/types/src/addons.rs +3 -1
- package/rust/types/src/config.rs +1 -3
- package/rust/utils/Cargo.toml +5 -2
- package/rust/utils/src/file.rs +397 -14
- package/rust/utils/src/path.rs +31 -2
- package/rust/utils/src/version.rs +38 -7
- package/rust/web/auth.rs +5 -0
- package/rust/web/forge.rs +5 -0
- package/rust/web/mod.rs +5 -3
- package/typescript/scripts/version/copy-to-binary.ts +82 -0
- package/rust/cli/bank/api.rs +0 -122
- package/rust/cli/bank/commands.rs +0 -306
- package/rust/cli/bank/mod.rs +0 -29
- package/rust/cli/install/bank.rs +0 -72
- package/rust/cli/install/commands.rs +0 -35
- package/rust/cli/install/mod.rs +0 -4
- package/rust/cli/install/plugin.rs +0 -80
|
@@ -109,6 +109,18 @@ pub fn interprete_call_statement(
|
|
|
109
109
|
let total_bar = 4.0 * base_duration;
|
|
110
110
|
let step_duration = total_bar / step_count; // seconds per step
|
|
111
111
|
|
|
112
|
+
// extract optional swing/humanize from pattern_stmt.value
|
|
113
|
+
let mut swing: f32 = 0.0;
|
|
114
|
+
let mut humanize: f32 = 0.0;
|
|
115
|
+
if let Value::Map(m) = &pattern_stmt.value {
|
|
116
|
+
if let Some(Value::Number(s)) = m.get("swing") {
|
|
117
|
+
swing = *s;
|
|
118
|
+
}
|
|
119
|
+
if let Some(Value::Number(h)) = m.get("humanize") {
|
|
120
|
+
humanize = *h;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
112
124
|
let mut updated_max = max_end_time;
|
|
113
125
|
|
|
114
126
|
for (i, ch) in pattern_str.chars().enumerate() {
|
|
@@ -117,7 +129,25 @@ pub fn interprete_call_statement(
|
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
// Schedule a trigger at cursor_time + offset
|
|
120
|
-
let event_time = cursor_time + (i as f32) * step_duration;
|
|
132
|
+
let mut event_time = cursor_time + (i as f32) * step_duration;
|
|
133
|
+
if swing.abs() > 0.0001 {
|
|
134
|
+
if i % 2 == 1 {
|
|
135
|
+
event_time += swing * step_duration;
|
|
136
|
+
} else {
|
|
137
|
+
event_time -= swing * step_duration;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if humanize.abs() > 0.0001 {
|
|
142
|
+
let jitter_range = humanize * step_duration;
|
|
143
|
+
let seed = (audio_engine.module_name.len() + i) as u64
|
|
144
|
+
+ (event_time.to_bits() as u64);
|
|
145
|
+
let mut x = seed.wrapping_mul(0x9E3779B97F4A7C15).rotate_left(13);
|
|
146
|
+
x ^= x >> 7;
|
|
147
|
+
let r = (x as i64 % 1000) as f32 / 1000.0;
|
|
148
|
+
let jitter = (r * 2.0 - 1.0) * jitter_range / 2.0;
|
|
149
|
+
event_time += jitter;
|
|
150
|
+
}
|
|
121
151
|
|
|
122
152
|
// Resolve trigger value similarly to interprete_trigger_statement
|
|
123
153
|
let mut trigger_val = Value::String(target_entity.clone());
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
use devalang_types::Value;
|
|
2
|
-
|
|
3
|
-
use crate::core::{
|
|
4
|
-
audio::{
|
|
5
|
-
engine::AudioEngine, evaluator::evaluate_condition_string,
|
|
6
|
-
interpreter::driver::execute_audio_block,
|
|
7
|
-
},
|
|
8
|
-
parser::statement::Statement,
|
|
9
|
-
store::global::GlobalStore,
|
|
10
|
-
};
|
|
11
|
-
use devalang_types::store::{FunctionTable, VariableTable};
|
|
12
|
-
|
|
13
|
-
pub fn interprete_condition_statement(
|
|
14
|
-
stmt: &Statement,
|
|
15
|
-
audio_engine: &mut AudioEngine,
|
|
16
|
-
global_store: &GlobalStore,
|
|
17
|
-
variable_table: &VariableTable,
|
|
18
|
-
functions_table: &FunctionTable,
|
|
19
|
-
base_bpm: f32,
|
|
20
|
-
base_duration: f32,
|
|
21
|
-
max_end_time: f32,
|
|
22
|
-
cursor_time: f32,
|
|
23
|
-
) -> (f32, f32) {
|
|
24
|
-
let cur_time = cursor_time;
|
|
25
|
-
let max_time = max_end_time;
|
|
26
|
-
|
|
27
|
-
let mut current = stmt.value.clone();
|
|
28
|
-
|
|
29
|
-
loop {
|
|
30
|
-
let Value::Map(map) = current else {
|
|
31
|
-
break;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
let should_execute = match map.get("condition") {
|
|
35
|
-
Some(Value::Boolean(b)) => *b,
|
|
36
|
-
Some(Value::String(expr)) => evaluate_condition_string(expr, &variable_table.clone()),
|
|
37
|
-
Some(_) => false,
|
|
38
|
-
None => true,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
if should_execute {
|
|
42
|
-
if let Some(Value::Block(block)) = map.get("body") {
|
|
43
|
-
let (new_max, cursor_time) = execute_audio_block(
|
|
44
|
-
audio_engine,
|
|
45
|
-
global_store,
|
|
46
|
-
variable_table.clone(),
|
|
47
|
-
functions_table.clone(),
|
|
48
|
-
block,
|
|
49
|
-
base_bpm,
|
|
50
|
-
base_duration,
|
|
51
|
-
max_time,
|
|
52
|
-
cur_time,
|
|
53
|
-
);
|
|
54
|
-
return (new_max, cursor_time);
|
|
55
|
-
} else {
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Advance to the next condition
|
|
61
|
-
match map.get("next") {
|
|
62
|
-
Some(Value::Map(next_map)) => {
|
|
63
|
-
current = Value::Map(next_map.clone());
|
|
64
|
-
}
|
|
65
|
-
_ => {
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
(max_end_time, cursor_time)
|
|
72
|
-
}
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
|
|
3
|
+
use crate::core::{
|
|
4
|
+
audio::{
|
|
5
|
+
engine::AudioEngine, evaluator::evaluate_condition_string,
|
|
6
|
+
interpreter::driver::execute_audio_block,
|
|
7
|
+
},
|
|
8
|
+
parser::statement::Statement,
|
|
9
|
+
store::global::GlobalStore,
|
|
10
|
+
};
|
|
11
|
+
use devalang_types::store::{FunctionTable, VariableTable};
|
|
12
|
+
|
|
13
|
+
pub fn interprete_condition_statement(
|
|
14
|
+
stmt: &Statement,
|
|
15
|
+
audio_engine: &mut AudioEngine,
|
|
16
|
+
global_store: &GlobalStore,
|
|
17
|
+
variable_table: &VariableTable,
|
|
18
|
+
functions_table: &FunctionTable,
|
|
19
|
+
base_bpm: f32,
|
|
20
|
+
base_duration: f32,
|
|
21
|
+
max_end_time: f32,
|
|
22
|
+
cursor_time: f32,
|
|
23
|
+
) -> (f32, f32) {
|
|
24
|
+
let cur_time = cursor_time;
|
|
25
|
+
let max_time = max_end_time;
|
|
26
|
+
|
|
27
|
+
let mut current = stmt.value.clone();
|
|
28
|
+
|
|
29
|
+
loop {
|
|
30
|
+
let Value::Map(map) = current else {
|
|
31
|
+
break;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let should_execute = match map.get("condition") {
|
|
35
|
+
Some(Value::Boolean(b)) => *b,
|
|
36
|
+
Some(Value::String(expr)) => evaluate_condition_string(expr, &variable_table.clone()),
|
|
37
|
+
Some(_) => false,
|
|
38
|
+
None => true,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if should_execute {
|
|
42
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
43
|
+
let (new_max, cursor_time) = execute_audio_block(
|
|
44
|
+
audio_engine,
|
|
45
|
+
global_store,
|
|
46
|
+
variable_table.clone(),
|
|
47
|
+
functions_table.clone(),
|
|
48
|
+
block,
|
|
49
|
+
base_bpm,
|
|
50
|
+
base_duration,
|
|
51
|
+
max_time,
|
|
52
|
+
cur_time,
|
|
53
|
+
);
|
|
54
|
+
return (new_max, cursor_time);
|
|
55
|
+
} else {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Advance to the next condition
|
|
61
|
+
match map.get("next") {
|
|
62
|
+
Some(Value::Map(next_map)) => {
|
|
63
|
+
current = Value::Map(next_map.clone());
|
|
64
|
+
}
|
|
65
|
+
_ => {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
(max_end_time, cursor_time)
|
|
72
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
use crate::core::parser::statement::{Statement, StatementKind};
|
|
2
|
-
use devalang_types::store::{FunctionDef, FunctionTable};
|
|
3
|
-
|
|
4
|
-
pub fn interprete_function_statement(
|
|
5
|
-
stmt: &Statement,
|
|
6
|
-
functions_table: &mut FunctionTable,
|
|
7
|
-
) -> Option<FunctionTable> {
|
|
8
|
-
if let StatementKind::Function {
|
|
9
|
-
name,
|
|
10
|
-
parameters,
|
|
11
|
-
body,
|
|
12
|
-
} = &stmt.kind
|
|
13
|
-
{
|
|
14
|
-
functions_table.add_function(FunctionDef {
|
|
15
|
-
name: name.clone(),
|
|
16
|
-
parameters: parameters.clone(),
|
|
17
|
-
body: body.clone(),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
return Some(functions_table.clone());
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
None
|
|
24
|
-
}
|
|
1
|
+
use crate::core::parser::statement::{Statement, StatementKind};
|
|
2
|
+
use devalang_types::store::{FunctionDef, FunctionTable};
|
|
3
|
+
|
|
4
|
+
pub fn interprete_function_statement(
|
|
5
|
+
stmt: &Statement,
|
|
6
|
+
functions_table: &mut FunctionTable,
|
|
7
|
+
) -> Option<FunctionTable> {
|
|
8
|
+
if let StatementKind::Function {
|
|
9
|
+
name,
|
|
10
|
+
parameters,
|
|
11
|
+
body,
|
|
12
|
+
} = &stmt.kind
|
|
13
|
+
{
|
|
14
|
+
functions_table.add_function(FunctionDef {
|
|
15
|
+
name: name.clone(),
|
|
16
|
+
parameters: parameters.clone(),
|
|
17
|
+
body: body.clone(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return Some(functions_table.clone());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
None
|
|
24
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
use crate::core::parser::statement::{Statement, StatementKind};
|
|
2
|
-
use devalang_types::Value;
|
|
3
|
-
use devalang_types::store::VariableTable;
|
|
4
|
-
|
|
5
|
-
pub fn interprete_let_statement(
|
|
6
|
-
stmt: &Statement,
|
|
7
|
-
variable_table: &mut VariableTable,
|
|
8
|
-
) -> Option<VariableTable> {
|
|
9
|
-
if let StatementKind::Let { name } = &stmt.kind {
|
|
10
|
-
// If RHS is a string and looks like an expression, evaluate it
|
|
11
|
-
let evaluated = match &stmt.value {
|
|
12
|
-
Value::String(s) if s.contains("$env") || s.contains("$math") => {
|
|
13
|
-
// We don't have direct env here; use defaults or infer from table
|
|
14
|
-
let bpm = if let Some(Value::Number(n)) = variable_table.get("bpm") {
|
|
15
|
-
*n
|
|
16
|
-
} else {
|
|
17
|
-
120.0
|
|
18
|
-
};
|
|
19
|
-
// Try to infer beat from time-based variables if any, else 0.0
|
|
20
|
-
let beat = if let Some(Value::Number(n)) = variable_table.get("beat") {
|
|
21
|
-
*n
|
|
22
|
-
} else {
|
|
23
|
-
0.0
|
|
24
|
-
};
|
|
25
|
-
crate::core::audio::evaluator::evaluate_rhs_into_value(s, variable_table, bpm, beat)
|
|
26
|
-
}
|
|
27
|
-
other => other.clone(),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
variable_table.set(name.to_string(), evaluated);
|
|
31
|
-
|
|
32
|
-
return Some(variable_table.clone());
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
None
|
|
36
|
-
}
|
|
1
|
+
use crate::core::parser::statement::{Statement, StatementKind};
|
|
2
|
+
use devalang_types::Value;
|
|
3
|
+
use devalang_types::store::VariableTable;
|
|
4
|
+
|
|
5
|
+
pub fn interprete_let_statement(
|
|
6
|
+
stmt: &Statement,
|
|
7
|
+
variable_table: &mut VariableTable,
|
|
8
|
+
) -> Option<VariableTable> {
|
|
9
|
+
if let StatementKind::Let { name } = &stmt.kind {
|
|
10
|
+
// If RHS is a string and looks like an expression, evaluate it
|
|
11
|
+
let evaluated = match &stmt.value {
|
|
12
|
+
Value::String(s) if s.contains("$env") || s.contains("$math") => {
|
|
13
|
+
// We don't have direct env here; use defaults or infer from table
|
|
14
|
+
let bpm = if let Some(Value::Number(n)) = variable_table.get("bpm") {
|
|
15
|
+
*n
|
|
16
|
+
} else {
|
|
17
|
+
120.0
|
|
18
|
+
};
|
|
19
|
+
// Try to infer beat from time-based variables if any, else 0.0
|
|
20
|
+
let beat = if let Some(Value::Number(n)) = variable_table.get("beat") {
|
|
21
|
+
*n
|
|
22
|
+
} else {
|
|
23
|
+
0.0
|
|
24
|
+
};
|
|
25
|
+
crate::core::audio::evaluator::evaluate_rhs_into_value(s, variable_table, bpm, beat)
|
|
26
|
+
}
|
|
27
|
+
other => other.clone(),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
variable_table.set(name.to_string(), evaluated);
|
|
31
|
+
|
|
32
|
+
return Some(variable_table.clone());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
None
|
|
36
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
use devalang_types::Value;
|
|
2
|
-
|
|
3
|
-
use crate::core::parser::statement::{Statement, StatementKind};
|
|
4
|
-
use devalang_types::store::VariableTable;
|
|
5
|
-
|
|
6
|
-
pub fn interprete_load_statement(
|
|
7
|
-
stmt: &Statement,
|
|
8
|
-
variable_table: &mut VariableTable,
|
|
9
|
-
) -> Option<VariableTable> {
|
|
10
|
-
if let StatementKind::Load { source, alias } = &stmt.kind {
|
|
11
|
-
variable_table.set(alias.to_string(), Value::Sample(source.clone()));
|
|
12
|
-
|
|
13
|
-
return Some(variable_table.clone());
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
None
|
|
17
|
-
}
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
|
|
3
|
+
use crate::core::parser::statement::{Statement, StatementKind};
|
|
4
|
+
use devalang_types::store::VariableTable;
|
|
5
|
+
|
|
6
|
+
pub fn interprete_load_statement(
|
|
7
|
+
stmt: &Statement,
|
|
8
|
+
variable_table: &mut VariableTable,
|
|
9
|
+
) -> Option<VariableTable> {
|
|
10
|
+
if let StatementKind::Load { source, alias } = &stmt.kind {
|
|
11
|
+
variable_table.set(alias.to_string(), Value::Sample(source.clone()));
|
|
12
|
+
|
|
13
|
+
return Some(variable_table.clone());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
None
|
|
17
|
+
}
|
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
use devalang_types::Value;
|
|
2
|
-
|
|
3
|
-
use crate::core::{
|
|
4
|
-
audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
|
|
5
|
-
parser::statement::Statement,
|
|
6
|
-
store::global::GlobalStore,
|
|
7
|
-
};
|
|
8
|
-
use devalang_types::store::{FunctionTable, VariableTable};
|
|
9
|
-
|
|
10
|
-
pub fn interprete_loop_statement(
|
|
11
|
-
stmt: &Statement,
|
|
12
|
-
audio_engine: &mut AudioEngine,
|
|
13
|
-
global_store: &GlobalStore,
|
|
14
|
-
variable_table: &VariableTable,
|
|
15
|
-
functions_table: &FunctionTable,
|
|
16
|
-
base_bpm: f32,
|
|
17
|
-
base_duration: f32,
|
|
18
|
-
max_end_time: f32,
|
|
19
|
-
cursor_time: f32,
|
|
20
|
-
) -> (f32, f32) {
|
|
21
|
-
if let Value::Map(loop_value) = &stmt.value {
|
|
22
|
-
// Foreach form: { foreach: Identifier(name), array: Array([...]), body: Block }
|
|
23
|
-
if let (
|
|
24
|
-
Some(Value::Identifier(var_name)),
|
|
25
|
-
Some(Value::Array(items)),
|
|
26
|
-
Some(Value::Block(loop_body)),
|
|
27
|
-
) = (
|
|
28
|
-
loop_value.get("foreach"),
|
|
29
|
-
loop_value.get("array"),
|
|
30
|
-
loop_value.get("body"),
|
|
31
|
-
) {
|
|
32
|
-
let engine = audio_engine;
|
|
33
|
-
let mut cur_time = cursor_time;
|
|
34
|
-
let mut max_time = max_end_time;
|
|
35
|
-
|
|
36
|
-
for item in items {
|
|
37
|
-
let mut scoped_vars = variable_table.clone();
|
|
38
|
-
scoped_vars.set(var_name.clone(), item.clone());
|
|
39
|
-
|
|
40
|
-
let (block_end_time, new_cursor) = execute_audio_block(
|
|
41
|
-
engine,
|
|
42
|
-
global_store,
|
|
43
|
-
scoped_vars,
|
|
44
|
-
functions_table.clone(),
|
|
45
|
-
loop_body,
|
|
46
|
-
base_bpm,
|
|
47
|
-
base_duration,
|
|
48
|
-
max_time,
|
|
49
|
-
cur_time,
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
cur_time = new_cursor.max(block_end_time);
|
|
53
|
-
max_time = max_time.max(cur_time);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return (max_time, cur_time);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let loop_count = match loop_value.get("iterator") {
|
|
60
|
-
Some(Value::Number(n)) => *n as usize,
|
|
61
|
-
Some(Value::Identifier(ident)) => {
|
|
62
|
-
if let Some(Value::Number(n)) = variable_table.get(ident) {
|
|
63
|
-
*n as usize
|
|
64
|
-
} else {
|
|
65
|
-
eprintln!("❌ Loop iterator must be a number, found: {:?}", ident);
|
|
66
|
-
return (max_end_time, cursor_time);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
_ => {
|
|
70
|
-
eprintln!(
|
|
71
|
-
"❌ Loop iterator must be a number, found: {:?}",
|
|
72
|
-
loop_value.get("iterator")
|
|
73
|
-
);
|
|
74
|
-
return (max_end_time, cursor_time);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
let loop_body = match loop_value.get("body") {
|
|
79
|
-
Some(Value::Block(body)) => body.clone(),
|
|
80
|
-
_ => {
|
|
81
|
-
eprintln!(
|
|
82
|
-
"❌ Loop body must be a block, found: {:?}",
|
|
83
|
-
loop_value.get("body")
|
|
84
|
-
);
|
|
85
|
-
return (max_end_time, cursor_time);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
let engine = audio_engine;
|
|
90
|
-
let mut cur_time = cursor_time;
|
|
91
|
-
let mut max_time = max_end_time;
|
|
92
|
-
|
|
93
|
-
for _ in 0..loop_count {
|
|
94
|
-
let (block_end_time, new_cursor) = execute_audio_block(
|
|
95
|
-
engine,
|
|
96
|
-
global_store,
|
|
97
|
-
variable_table.clone(),
|
|
98
|
-
functions_table.clone(),
|
|
99
|
-
&loop_body,
|
|
100
|
-
base_bpm,
|
|
101
|
-
base_duration,
|
|
102
|
-
max_time,
|
|
103
|
-
cur_time,
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
cur_time = new_cursor.max(block_end_time);
|
|
107
|
-
max_time = max_time.max(cur_time);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return (max_time, cur_time);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
eprintln!("❌ Loop statement value is not a map");
|
|
114
|
-
(max_end_time, cursor_time)
|
|
115
|
-
}
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
|
|
3
|
+
use crate::core::{
|
|
4
|
+
audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
|
|
5
|
+
parser::statement::Statement,
|
|
6
|
+
store::global::GlobalStore,
|
|
7
|
+
};
|
|
8
|
+
use devalang_types::store::{FunctionTable, VariableTable};
|
|
9
|
+
|
|
10
|
+
pub fn interprete_loop_statement(
|
|
11
|
+
stmt: &Statement,
|
|
12
|
+
audio_engine: &mut AudioEngine,
|
|
13
|
+
global_store: &GlobalStore,
|
|
14
|
+
variable_table: &VariableTable,
|
|
15
|
+
functions_table: &FunctionTable,
|
|
16
|
+
base_bpm: f32,
|
|
17
|
+
base_duration: f32,
|
|
18
|
+
max_end_time: f32,
|
|
19
|
+
cursor_time: f32,
|
|
20
|
+
) -> (f32, f32) {
|
|
21
|
+
if let Value::Map(loop_value) = &stmt.value {
|
|
22
|
+
// Foreach form: { foreach: Identifier(name), array: Array([...]), body: Block }
|
|
23
|
+
if let (
|
|
24
|
+
Some(Value::Identifier(var_name)),
|
|
25
|
+
Some(Value::Array(items)),
|
|
26
|
+
Some(Value::Block(loop_body)),
|
|
27
|
+
) = (
|
|
28
|
+
loop_value.get("foreach"),
|
|
29
|
+
loop_value.get("array"),
|
|
30
|
+
loop_value.get("body"),
|
|
31
|
+
) {
|
|
32
|
+
let engine = audio_engine;
|
|
33
|
+
let mut cur_time = cursor_time;
|
|
34
|
+
let mut max_time = max_end_time;
|
|
35
|
+
|
|
36
|
+
for item in items {
|
|
37
|
+
let mut scoped_vars = variable_table.clone();
|
|
38
|
+
scoped_vars.set(var_name.clone(), item.clone());
|
|
39
|
+
|
|
40
|
+
let (block_end_time, new_cursor) = execute_audio_block(
|
|
41
|
+
engine,
|
|
42
|
+
global_store,
|
|
43
|
+
scoped_vars,
|
|
44
|
+
functions_table.clone(),
|
|
45
|
+
loop_body,
|
|
46
|
+
base_bpm,
|
|
47
|
+
base_duration,
|
|
48
|
+
max_time,
|
|
49
|
+
cur_time,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
cur_time = new_cursor.max(block_end_time);
|
|
53
|
+
max_time = max_time.max(cur_time);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (max_time, cur_time);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let loop_count = match loop_value.get("iterator") {
|
|
60
|
+
Some(Value::Number(n)) => *n as usize,
|
|
61
|
+
Some(Value::Identifier(ident)) => {
|
|
62
|
+
if let Some(Value::Number(n)) = variable_table.get(ident) {
|
|
63
|
+
*n as usize
|
|
64
|
+
} else {
|
|
65
|
+
eprintln!("❌ Loop iterator must be a number, found: {:?}", ident);
|
|
66
|
+
return (max_end_time, cursor_time);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
_ => {
|
|
70
|
+
eprintln!(
|
|
71
|
+
"❌ Loop iterator must be a number, found: {:?}",
|
|
72
|
+
loop_value.get("iterator")
|
|
73
|
+
);
|
|
74
|
+
return (max_end_time, cursor_time);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
let loop_body = match loop_value.get("body") {
|
|
79
|
+
Some(Value::Block(body)) => body.clone(),
|
|
80
|
+
_ => {
|
|
81
|
+
eprintln!(
|
|
82
|
+
"❌ Loop body must be a block, found: {:?}",
|
|
83
|
+
loop_value.get("body")
|
|
84
|
+
);
|
|
85
|
+
return (max_end_time, cursor_time);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
let engine = audio_engine;
|
|
90
|
+
let mut cur_time = cursor_time;
|
|
91
|
+
let mut max_time = max_end_time;
|
|
92
|
+
|
|
93
|
+
for _ in 0..loop_count {
|
|
94
|
+
let (block_end_time, new_cursor) = execute_audio_block(
|
|
95
|
+
engine,
|
|
96
|
+
global_store,
|
|
97
|
+
variable_table.clone(),
|
|
98
|
+
functions_table.clone(),
|
|
99
|
+
&loop_body,
|
|
100
|
+
base_bpm,
|
|
101
|
+
base_duration,
|
|
102
|
+
max_time,
|
|
103
|
+
cur_time,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
cur_time = new_cursor.max(block_end_time);
|
|
107
|
+
max_time = max_time.max(cur_time);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (max_time, cur_time);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
eprintln!("❌ Loop statement value is not a map");
|
|
114
|
+
(max_end_time, cursor_time)
|
|
115
|
+
}
|
|
@@ -71,7 +71,22 @@ pub fn interprete_spawn_statement(
|
|
|
71
71
|
|
|
72
72
|
// Pattern case: allow spawning a pattern similar to call
|
|
73
73
|
if let Some(pattern_stmt) = find_pattern(name, variable_table, global_store) {
|
|
74
|
-
|
|
74
|
+
// Support Value::String(pattern) or Value::Map({ pattern: "..", swing: .., humanize: .. })
|
|
75
|
+
let mut pat_opt: Option<String> = None;
|
|
76
|
+
let mut opts_map: Option<std::collections::HashMap<String, Value>> = None;
|
|
77
|
+
|
|
78
|
+
match &pattern_stmt.value {
|
|
79
|
+
Value::String(s) => pat_opt = Some(s.clone()),
|
|
80
|
+
Value::Map(m) => {
|
|
81
|
+
if let Some(Value::String(s)) = m.get("pattern") {
|
|
82
|
+
pat_opt = Some(s.clone());
|
|
83
|
+
}
|
|
84
|
+
opts_map = Some(m.clone());
|
|
85
|
+
}
|
|
86
|
+
_ => {}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if let Some(pat) = pat_opt {
|
|
75
90
|
let mut target_entity = name.clone();
|
|
76
91
|
if let StatementKind::Pattern { name: _n, target } = &pattern_stmt.kind {
|
|
77
92
|
if let Some(t) = target {
|
|
@@ -97,6 +112,18 @@ pub fn interprete_spawn_statement(
|
|
|
97
112
|
let total_bar = 4.0 * base_duration;
|
|
98
113
|
let step_duration = total_bar / step_count;
|
|
99
114
|
|
|
115
|
+
// extract optional swing/humanize from pattern_stmt.value
|
|
116
|
+
let mut swing: f32 = 0.0;
|
|
117
|
+
let mut humanize: f32 = 0.0;
|
|
118
|
+
if let Value::Map(m) = &pattern_stmt.value {
|
|
119
|
+
if let Some(Value::Number(s)) = m.get("swing") {
|
|
120
|
+
swing = *s;
|
|
121
|
+
}
|
|
122
|
+
if let Some(Value::Number(h)) = m.get("humanize") {
|
|
123
|
+
humanize = *h;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
100
127
|
let mut updated_max = max_end_time;
|
|
101
128
|
|
|
102
129
|
for (i, ch) in pattern_str.chars().enumerate() {
|
|
@@ -104,7 +131,29 @@ pub fn interprete_spawn_statement(
|
|
|
104
131
|
continue;
|
|
105
132
|
}
|
|
106
133
|
|
|
107
|
-
|
|
134
|
+
// Apply swing: shift every other step by +/- swing*step_duration
|
|
135
|
+
let mut event_time = cursor_time + (i as f32) * step_duration;
|
|
136
|
+
if swing.abs() > 0.0001 {
|
|
137
|
+
// swing applies to off-beats: shift odd steps forward, even steps back
|
|
138
|
+
if i % 2 == 1 {
|
|
139
|
+
event_time += swing * step_duration;
|
|
140
|
+
} else {
|
|
141
|
+
event_time -= swing * step_duration;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Apply humanize: small random jitter in [-humanize*step_duration/2, +...]
|
|
146
|
+
if humanize.abs() > 0.0001 {
|
|
147
|
+
let jitter_range = humanize * step_duration;
|
|
148
|
+
// lightweight RNG using a simple hash to avoid adding rand dependency
|
|
149
|
+
let seed = (audio_engine.module_name.len() + i) as u64
|
|
150
|
+
+ (event_time.to_bits() as u64);
|
|
151
|
+
let mut x = seed.wrapping_mul(0x9E3779B97F4A7C15).rotate_left(13);
|
|
152
|
+
x ^= x >> 7;
|
|
153
|
+
let r = (x as i64 % 1000) as f32 / 1000.0; // [0,1)
|
|
154
|
+
let jitter = (r * 2.0 - 1.0) * jitter_range / 2.0;
|
|
155
|
+
event_time += jitter;
|
|
156
|
+
}
|
|
108
157
|
|
|
109
158
|
let mut trigger_val = Value::String(target_entity.clone());
|
|
110
159
|
if let Some(val) = variable_table.variables.get(&target_entity) {
|