@devaloop/devalang 0.0.1-alpha.16-hotfix.1 → 0.0.1-alpha.17
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/config.toml +2 -0
- package/.devalang +6 -10
- package/.github/workflows/ci.yml +19 -8
- package/Cargo.toml +18 -2
- package/README.md +80 -33
- package/docs/CHANGELOG.md +56 -0
- package/docs/ROADMAP.md +6 -3
- package/examples/index.deva +52 -35
- package/out-tsc/bin/index.d.ts +2 -0
- package/out-tsc/core/functions/index.d.ts +37 -0
- package/out-tsc/core/functions/index.js +76 -0
- package/out-tsc/core/index.d.ts +6 -0
- package/out-tsc/core/index.js +22 -0
- package/out-tsc/core/types/index.d.ts +4 -0
- package/out-tsc/core/types/index.js +20 -0
- package/out-tsc/core/types/plugin.d.ts +18 -0
- package/out-tsc/core/types/plugin.js +2 -0
- package/out-tsc/core/types/result.d.ts +27 -0
- package/out-tsc/core/types/result.js +2 -0
- package/out-tsc/core/types/statement.d.ts +106 -0
- package/out-tsc/core/types/statement.js +2 -0
- package/out-tsc/core/types/value.d.ts +43 -0
- package/out-tsc/core/types/value.js +2 -0
- package/out-tsc/index.d.ts +7 -0
- package/out-tsc/index.js +41 -2
- package/out-tsc/pkg/devalang_core.d.ts +7 -0
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
- package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
- package/out-tsc/scripts/copy-wasm-dts.js +73 -0
- package/out-tsc/scripts/postinstall.d.ts +1 -0
- package/out-tsc/scripts/postinstall.js +33 -23
- package/out-tsc/scripts/version/bump.d.ts +1 -0
- package/out-tsc/scripts/version/fetch.d.ts +1 -0
- package/out-tsc/scripts/version/index.d.ts +1 -0
- package/out-tsc/scripts/version/sync.d.ts +1 -0
- package/package.json +16 -4
- package/project-version.json +3 -3
- package/rust/cli/bank/api.rs +122 -0
- package/rust/cli/bank/commands.rs +275 -0
- package/rust/cli/bank/mod.rs +29 -0
- package/rust/cli/build/commands.rs +97 -0
- package/rust/cli/build/mod.rs +2 -0
- package/rust/cli/build/process.rs +146 -0
- package/rust/cli/{check.rs → check/mod.rs} +18 -31
- package/rust/cli/discover/commands.rs +253 -0
- package/rust/cli/discover/config.rs +111 -0
- package/rust/cli/discover/fs.rs +19 -0
- package/rust/cli/discover/install.rs +103 -0
- package/rust/cli/discover/metadata.rs +48 -0
- package/rust/cli/discover/mod.rs +5 -0
- package/rust/cli/{init.rs → init/commands.rs} +88 -87
- package/rust/cli/init/mod.rs +1 -0
- package/rust/{installer → cli/install}/addon.rs +5 -9
- package/rust/cli/install/bank.rs +53 -0
- package/rust/cli/{install.rs → install/commands.rs} +9 -9
- package/rust/{installer → cli/install}/mod.rs +2 -3
- package/rust/cli/install/plugin.rs +61 -0
- package/rust/cli/{login.rs → login/commands.rs} +8 -11
- package/rust/cli/login/mod.rs +1 -0
- package/rust/cli/mod.rs +2 -3
- package/rust/cli/{driver.rs → parser.rs} +19 -2
- package/rust/cli/play/commands.rs +324 -0
- package/rust/cli/play/io.rs +17 -0
- package/rust/cli/play/mod.rs +5 -0
- package/rust/cli/play/process.rs +150 -0
- package/rust/cli/play/realtime.rs +91 -0
- package/rust/cli/play/utils.rs +23 -0
- package/rust/cli/telemetry/commands.rs +22 -0
- package/rust/cli/telemetry/event_creator.rs +80 -0
- package/rust/cli/telemetry/mod.rs +3 -0
- package/rust/cli/telemetry/send.rs +51 -0
- package/rust/cli/{template.rs → template/commands.rs} +1 -1
- package/rust/cli/template/mod.rs +1 -0
- package/rust/cli/{update.rs → update/commands.rs} +6 -6
- package/rust/cli/update/mod.rs +1 -0
- package/rust/config/driver.rs +57 -72
- package/rust/config/mod.rs +1 -2
- package/rust/config/ops.rs +26 -0
- package/rust/config/settings.rs +60 -50
- package/rust/core/audio/engine/helpers.rs +146 -0
- package/rust/core/audio/engine/mod.rs +7 -0
- package/rust/core/audio/engine/sample.rs +298 -0
- package/rust/core/audio/engine/synth.rs +310 -0
- package/rust/core/audio/evaluator.rs +15 -12
- package/rust/core/audio/interpreter/arrow_call.rs +99 -24
- package/rust/core/audio/interpreter/call.rs +81 -60
- package/rust/core/audio/interpreter/condition.rs +3 -2
- package/rust/core/audio/interpreter/driver.rs +206 -151
- package/rust/core/audio/interpreter/let_.rs +1 -1
- package/rust/core/audio/interpreter/load.rs +2 -1
- package/rust/core/audio/interpreter/loop_.rs +7 -6
- package/rust/core/audio/interpreter/sleep.rs +2 -1
- package/rust/core/audio/interpreter/spawn.rs +45 -57
- package/rust/core/audio/interpreter/tempo.rs +31 -10
- package/rust/core/audio/interpreter/trigger.rs +2 -2
- package/rust/core/audio/loader/trigger.rs +4 -7
- package/rust/core/audio/player.rs +6 -0
- package/rust/core/audio/renderer.rs +5 -7
- package/rust/core/audio/special/env.rs +3 -1
- package/rust/core/audio/special/math.rs +4 -4
- package/rust/core/audio/special/modulator.rs +2 -2
- package/rust/core/builder/mod.rs +9 -3
- package/rust/core/debugger/lexer.rs +1 -1
- package/rust/core/debugger/mod.rs +6 -0
- package/rust/core/debugger/module.rs +4 -4
- package/rust/core/debugger/preprocessor.rs +1 -1
- package/rust/core/debugger/store.rs +2 -2
- package/rust/core/error/mod.rs +189 -0
- package/rust/core/lexer/handler/arrow.rs +1 -1
- package/rust/core/lexer/handler/at.rs +1 -1
- package/rust/core/lexer/handler/brace.rs +2 -2
- package/rust/core/lexer/handler/colon.rs +1 -1
- package/rust/core/lexer/handler/comment.rs +1 -1
- package/rust/core/lexer/handler/dot.rs +1 -1
- package/rust/core/lexer/handler/driver.rs +1 -1
- package/rust/core/lexer/handler/identifier.rs +1 -1
- package/rust/core/lexer/handler/mod.rs +1 -2
- package/rust/core/lexer/handler/number.rs +1 -1
- package/rust/core/lexer/handler/operator.rs +1 -1
- package/rust/core/lexer/handler/parenthesis.rs +2 -2
- package/rust/core/lexer/handler/slash.rs +1 -1
- package/rust/core/lexer/handler/string.rs +1 -1
- package/rust/core/lexer/mod.rs +22 -12
- package/rust/core/lexer/token.rs +90 -97
- package/rust/core/mod.rs +0 -1
- package/rust/core/parser/driver.rs +66 -13
- package/rust/core/parser/handler/arrow_call.rs +28 -8
- package/rust/core/parser/handler/at.rs +55 -21
- package/rust/core/parser/handler/bank.rs +14 -4
- package/rust/core/parser/handler/condition.rs +6 -3
- package/rust/core/parser/handler/dot.rs +2 -1
- package/rust/core/parser/handler/identifier/automate.rs +13 -16
- package/rust/core/parser/handler/identifier/call.rs +4 -4
- package/rust/core/parser/handler/identifier/emit.rs +9 -5
- package/rust/core/parser/handler/identifier/function.rs +20 -7
- package/rust/core/parser/handler/identifier/group.rs +11 -7
- package/rust/core/parser/handler/identifier/let_.rs +24 -9
- package/rust/core/parser/handler/identifier/mod.rs +6 -5
- package/rust/core/parser/handler/identifier/on.rs +16 -7
- package/rust/core/parser/handler/identifier/print.rs +6 -9
- package/rust/core/parser/handler/identifier/sleep.rs +12 -5
- package/rust/core/parser/handler/identifier/spawn.rs +4 -4
- package/rust/core/parser/handler/identifier/synth.rs +79 -9
- package/rust/core/parser/handler/loop_.rs +39 -14
- package/rust/core/parser/handler/tempo.rs +9 -5
- package/rust/core/parser/mod.rs +0 -1
- package/rust/core/parser/statement.rs +6 -137
- package/rust/core/plugin/loader.rs +41 -27
- package/rust/core/plugin/runner.rs +68 -17
- package/rust/core/preprocessor/loader.rs +155 -33
- package/rust/core/preprocessor/processor.rs +2 -2
- package/rust/core/preprocessor/resolver/bank.rs +6 -8
- package/rust/core/preprocessor/resolver/call.rs +20 -24
- package/rust/core/preprocessor/resolver/condition.rs +6 -8
- package/rust/core/preprocessor/resolver/driver.rs +14 -16
- package/rust/core/preprocessor/resolver/function.rs +6 -6
- package/rust/core/preprocessor/resolver/group.rs +6 -8
- package/rust/core/preprocessor/resolver/loop_.rs +8 -10
- package/rust/core/preprocessor/resolver/spawn.rs +19 -23
- package/rust/core/preprocessor/resolver/synth.rs +6 -8
- package/rust/core/preprocessor/resolver/tempo.rs +6 -8
- package/rust/core/preprocessor/resolver/trigger.rs +22 -19
- package/rust/core/preprocessor/resolver/value.rs +99 -4
- package/rust/core/store/export.rs +28 -28
- package/rust/core/store/function.rs +6 -0
- package/rust/core/store/global.rs +7 -1
- package/rust/core/store/import.rs +28 -28
- package/rust/core/store/variable.rs +1 -1
- package/rust/core/utils/mod.rs +0 -1
- package/rust/lib.rs +102 -9
- package/rust/main.rs +156 -45
- package/rust/types/Cargo.toml +8 -0
- package/rust/types/src/addons.rs +55 -0
- package/rust/types/src/ast.rs +198 -0
- package/rust/types/src/config.rs +74 -0
- package/rust/types/src/lib.rs +12 -0
- package/rust/types/src/telemetry.rs +85 -0
- package/rust/utils/Cargo.toml +23 -0
- package/rust/utils/{error.rs → src/error.rs} +186 -200
- package/rust/utils/src/file.rs +94 -0
- package/rust/utils/src/first_usage.rs +97 -0
- package/rust/utils/{mod.rs → src/lib.rs} +1 -1
- package/rust/utils/{logger.rs → src/logger.rs} +17 -12
- package/rust/utils/src/path.rs +88 -0
- package/rust/utils/src/signature.rs +41 -0
- package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
- package/rust/utils/src/version.rs +27 -0
- package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
- package/rust/web/api.rs +5 -0
- package/rust/web/cdn.rs +34 -0
- package/templates/minimal/README.md +98 -54
- package/templates/welcome/README.md +98 -54
- package/templates/welcome/src/index.deva +56 -8
- package/templates/welcome/src/variables.deva +2 -4
- package/tests/rust/TODO.md +0 -0
- package/tests/typescript/index.spec.ts +136 -0
- package/tests/typescript/playhead.spec.ts +36 -0
- package/tests/typescript/render_e2e.spec.ts +77 -0
- package/tsconfig.json +1 -1
- package/typescript/core/functions/index.ts +83 -0
- package/typescript/core/index.ts +6 -0
- package/typescript/core/types/index.ts +4 -0
- package/typescript/core/types/plugin.ts +19 -0
- package/typescript/core/types/result.ts +29 -0
- package/typescript/core/types/statement.ts +47 -0
- package/typescript/core/types/value.ts +29 -0
- package/typescript/index.ts +7 -2
- package/typescript/pkg/devalang_core.d.ts +4 -0
- package/typescript/scripts/copy-wasm-dts.ts +41 -0
- package/typescript/scripts/postinstall.ts +45 -32
- package/rust/cli/bank.rs +0 -462
- package/rust/cli/build.rs +0 -252
- package/rust/cli/generator.rs +0 -1
- package/rust/cli/play.rs +0 -1123
- package/rust/cli/telemetry.rs +0 -19
- package/rust/common/api.rs +0 -5
- package/rust/common/cdn.rs +0 -5
- package/rust/config/loader.rs +0 -165
- package/rust/config/stats.rs +0 -257
- package/rust/core/audio/engine.rs +0 -696
- package/rust/core/shared/bank.rs +0 -21
- package/rust/core/shared/duration.rs +0 -9
- package/rust/core/shared/mod.rs +0 -3
- package/rust/core/shared/value.rs +0 -35
- package/rust/core/utils/validation.rs +0 -35
- package/rust/installer/bank.rs +0 -62
- package/rust/installer/plugin.rs +0 -54
- package/rust/installer/utils.rs +0 -56
- package/rust/utils/file.rs +0 -38
- package/rust/utils/first_usage.rs +0 -76
- package/rust/utils/signature.rs +0 -19
- package/rust/utils/telemetry.rs +0 -292
- package/rust/utils/version.rs +0 -15
- /package/rust/{common → web}/mod.rs +0 -0
- /package/rust/{common → web}/sso.rs +0 -0
|
@@ -1,26 +1,68 @@
|
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
use devalang_utils::logger::{LogLevel, Logger};
|
|
1
3
|
use rayon::prelude::*;
|
|
2
4
|
|
|
3
|
-
use crate::{
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
tempo::interprete_tempo_statement, trigger::interprete_trigger_statement,
|
|
13
|
-
},
|
|
5
|
+
use crate::core::{
|
|
6
|
+
audio::{
|
|
7
|
+
engine::AudioEngine,
|
|
8
|
+
interpreter::{
|
|
9
|
+
arrow_call::interprete_call_arrow_statement, call::interprete_call_statement,
|
|
10
|
+
function::interprete_function_statement, let_::interprete_let_statement,
|
|
11
|
+
load::interprete_load_statement, loop_::interprete_loop_statement,
|
|
12
|
+
sleep::interprete_sleep_statement, spawn::interprete_spawn_statement,
|
|
13
|
+
tempo::interprete_tempo_statement, trigger::interprete_trigger_statement,
|
|
14
14
|
},
|
|
15
|
-
parser::statement::{Statement, StatementKind},
|
|
16
|
-
shared::value::Value,
|
|
17
|
-
store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
|
|
18
15
|
},
|
|
19
|
-
|
|
16
|
+
parser::statement::{Statement, StatementKind},
|
|
17
|
+
store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
|
|
20
18
|
};
|
|
21
19
|
|
|
20
|
+
// WASM playhead callback support (only compiled for wasm32 target)
|
|
21
|
+
#[cfg(target_arch = "wasm32")]
|
|
22
|
+
use serde::Serialize;
|
|
23
|
+
|
|
24
|
+
#[cfg(target_arch = "wasm32")]
|
|
25
|
+
use wasm_bindgen::prelude::JsValue;
|
|
26
|
+
|
|
27
|
+
#[cfg(target_arch = "wasm32")]
|
|
28
|
+
use std::cell::RefCell;
|
|
29
|
+
|
|
30
|
+
#[cfg(target_arch = "wasm32")]
|
|
31
|
+
thread_local! {
|
|
32
|
+
static PLAYHEAD_CB: RefCell<Option<js_sys::Function>> = RefCell::new(None);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[cfg(target_arch = "wasm32")]
|
|
36
|
+
pub fn register_playhead_callback(cb: js_sys::Function) {
|
|
37
|
+
PLAYHEAD_CB.with(|c| *c.borrow_mut() = Some(cb));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[cfg(target_arch = "wasm32")]
|
|
41
|
+
pub fn unregister_playhead_callback() {
|
|
42
|
+
PLAYHEAD_CB.with(|c| *c.borrow_mut() = None);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#[cfg(target_arch = "wasm32")]
|
|
46
|
+
fn emit_playhead(time: f32, line: usize, column: usize) {
|
|
47
|
+
#[derive(Serialize)]
|
|
48
|
+
struct PlayheadEvent {
|
|
49
|
+
time: f32,
|
|
50
|
+
line: usize,
|
|
51
|
+
column: usize,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let ev = PlayheadEvent { time, line, column };
|
|
55
|
+
if let Ok(v) = serde_wasm_bindgen::to_value(&ev) {
|
|
56
|
+
PLAYHEAD_CB.with(|c| {
|
|
57
|
+
if let Some(f) = c.borrow().as_ref() {
|
|
58
|
+
let _ = f.call1(&JsValue::NULL, &v);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
22
64
|
pub fn run_audio_program(
|
|
23
|
-
statements: &
|
|
65
|
+
statements: &[Statement],
|
|
24
66
|
audio_engine: &mut AudioEngine,
|
|
25
67
|
_entry: String,
|
|
26
68
|
_output: String,
|
|
@@ -36,7 +78,7 @@ pub fn run_audio_program(
|
|
|
36
78
|
global_store,
|
|
37
79
|
global_store.variables.clone(),
|
|
38
80
|
global_store.functions.clone(),
|
|
39
|
-
|
|
81
|
+
statements,
|
|
40
82
|
base_bpm,
|
|
41
83
|
base_duration,
|
|
42
84
|
0.0,
|
|
@@ -46,6 +88,13 @@ pub fn run_audio_program(
|
|
|
46
88
|
(max_end_time, cursor_time)
|
|
47
89
|
}
|
|
48
90
|
|
|
91
|
+
/// Execute a block of statements and schedule audio into the provided
|
|
92
|
+
/// AudioEngine. This function is the core of offline rendering and
|
|
93
|
+
/// performs the following responsibilities:
|
|
94
|
+
/// - sequential evaluation of statements (load, let, trigger, loop, etc.)
|
|
95
|
+
/// - parallel execution of `spawn` blocks (using rayon) and merging results
|
|
96
|
+
/// - scheduling of periodic events (beat/bar) once at the root depth
|
|
97
|
+
/// - emitting playhead events (when compiled for wasm32) after each sequential statement
|
|
49
98
|
pub fn execute_audio_block(
|
|
50
99
|
audio_engine: &mut AudioEngine,
|
|
51
100
|
global_store: &GlobalStore,
|
|
@@ -71,7 +120,7 @@ pub fn execute_audio_block(
|
|
|
71
120
|
for stmt in others {
|
|
72
121
|
match &stmt.kind {
|
|
73
122
|
StatementKind::Load { .. } => {
|
|
74
|
-
if let Some(new_table) = interprete_load_statement(
|
|
123
|
+
if let Some(new_table) = interprete_load_statement(stmt, &mut variable_table) {
|
|
75
124
|
variable_table = new_table;
|
|
76
125
|
}
|
|
77
126
|
}
|
|
@@ -116,26 +165,26 @@ pub fn execute_audio_block(
|
|
|
116
165
|
}
|
|
117
166
|
}
|
|
118
167
|
StatementKind::Let { .. } => {
|
|
119
|
-
if let Some(new_table) = interprete_let_statement(
|
|
168
|
+
if let Some(new_table) = interprete_let_statement(stmt, &mut variable_table) {
|
|
120
169
|
variable_table = new_table;
|
|
121
170
|
}
|
|
122
171
|
}
|
|
123
172
|
StatementKind::Function { .. } => {
|
|
124
173
|
if let Some(new_functions) =
|
|
125
|
-
interprete_function_statement(
|
|
174
|
+
interprete_function_statement(stmt, &mut functions_table)
|
|
126
175
|
{
|
|
127
176
|
functions_table = new_functions;
|
|
128
177
|
}
|
|
129
178
|
}
|
|
130
179
|
StatementKind::Tempo => {
|
|
131
|
-
if let Some((new_bpm, new_duration)) = interprete_tempo_statement(
|
|
180
|
+
if let Some((new_bpm, new_duration)) = interprete_tempo_statement(stmt) {
|
|
132
181
|
base_bpm = new_bpm;
|
|
133
182
|
base_duration = new_duration;
|
|
134
183
|
}
|
|
135
184
|
}
|
|
136
185
|
StatementKind::Trigger { .. } => {
|
|
137
186
|
if let Some((new_cursor, new_max, _)) = interprete_trigger_statement(
|
|
138
|
-
|
|
187
|
+
stmt,
|
|
139
188
|
audio_engine,
|
|
140
189
|
&variable_table,
|
|
141
190
|
base_duration,
|
|
@@ -148,13 +197,13 @@ pub fn execute_audio_block(
|
|
|
148
197
|
}
|
|
149
198
|
StatementKind::Sleep => {
|
|
150
199
|
let (new_cursor, new_max) =
|
|
151
|
-
interprete_sleep_statement(
|
|
200
|
+
interprete_sleep_statement(stmt, cursor_time, max_end_time);
|
|
152
201
|
cursor_time = new_cursor;
|
|
153
202
|
max_end_time = new_max;
|
|
154
203
|
}
|
|
155
204
|
StatementKind::Loop => {
|
|
156
205
|
let (new_max, new_cursor) = interprete_loop_statement(
|
|
157
|
-
|
|
206
|
+
stmt,
|
|
158
207
|
audio_engine,
|
|
159
208
|
global_store,
|
|
160
209
|
&variable_table,
|
|
@@ -169,7 +218,7 @@ pub fn execute_audio_block(
|
|
|
169
218
|
}
|
|
170
219
|
StatementKind::Call { .. } => {
|
|
171
220
|
let (new_max, _) = interprete_call_statement(
|
|
172
|
-
|
|
221
|
+
stmt,
|
|
173
222
|
audio_engine,
|
|
174
223
|
&variable_table,
|
|
175
224
|
&functions_table,
|
|
@@ -184,16 +233,16 @@ pub fn execute_audio_block(
|
|
|
184
233
|
}
|
|
185
234
|
StatementKind::ArrowCall { .. } => {
|
|
186
235
|
let (new_max, new_cursor) = interprete_call_arrow_statement(
|
|
187
|
-
|
|
236
|
+
stmt,
|
|
188
237
|
audio_engine,
|
|
189
238
|
&variable_table,
|
|
239
|
+
global_store,
|
|
190
240
|
base_bpm,
|
|
191
241
|
base_duration,
|
|
192
242
|
&mut max_end_time,
|
|
193
243
|
Some(&mut cursor_time),
|
|
194
244
|
true,
|
|
195
245
|
);
|
|
196
|
-
|
|
197
246
|
cursor_time = new_cursor;
|
|
198
247
|
|
|
199
248
|
if new_max > max_end_time {
|
|
@@ -203,7 +252,7 @@ pub fn execute_audio_block(
|
|
|
203
252
|
StatementKind::Automate { .. } => {
|
|
204
253
|
if let Some(new_table) =
|
|
205
254
|
crate::core::audio::interpreter::automate::interprete_automate_statement(
|
|
206
|
-
|
|
255
|
+
stmt,
|
|
207
256
|
&mut variable_table,
|
|
208
257
|
)
|
|
209
258
|
{
|
|
@@ -237,7 +286,7 @@ pub fn execute_audio_block(
|
|
|
237
286
|
)
|
|
238
287
|
{
|
|
239
288
|
logger.log_message(LogLevel::Print, &res);
|
|
240
|
-
} else if let Some(val) = variable_table.get(
|
|
289
|
+
} else if let Some(val) = variable_table.get(s) {
|
|
241
290
|
logger.log_message(LogLevel::Print, &format!("{:?}", val));
|
|
242
291
|
} else if s.contains("$env")
|
|
243
292
|
|| s.contains("$math")
|
|
@@ -251,12 +300,12 @@ pub fn execute_audio_block(
|
|
|
251
300
|
);
|
|
252
301
|
match v {
|
|
253
302
|
Value::Number(n) => {
|
|
254
|
-
logger.log_message(LogLevel::Print, &format!("{}", n))
|
|
303
|
+
logger.log_message(LogLevel::Print, &format!("{}", n));
|
|
255
304
|
}
|
|
256
305
|
_ => logger.log_message(LogLevel::Print, s),
|
|
257
306
|
}
|
|
258
307
|
} else {
|
|
259
|
-
logger.log_message(LogLevel::Print, s)
|
|
308
|
+
logger.log_message(LogLevel::Print, s);
|
|
260
309
|
}
|
|
261
310
|
}
|
|
262
311
|
Value::Number(n) => {
|
|
@@ -266,18 +315,19 @@ pub fn execute_audio_block(
|
|
|
266
315
|
if let Some(val) = variable_table.get(name) {
|
|
267
316
|
match val {
|
|
268
317
|
Value::Number(n) => {
|
|
269
|
-
logger.log_message(LogLevel::Print, &format!("{}", n))
|
|
318
|
+
logger.log_message(LogLevel::Print, &format!("{}", n));
|
|
270
319
|
}
|
|
271
320
|
Value::String(s) => logger.log_message(LogLevel::Print, s),
|
|
272
321
|
Value::Boolean(b) => {
|
|
273
|
-
logger.log_message(LogLevel::Print, &format!("{}", b))
|
|
322
|
+
logger.log_message(LogLevel::Print, &format!("{}", b));
|
|
274
323
|
}
|
|
275
324
|
other => {
|
|
276
|
-
logger
|
|
325
|
+
logger
|
|
326
|
+
.log_message(LogLevel::Print, &format!("{:?}", other));
|
|
277
327
|
}
|
|
278
328
|
}
|
|
279
329
|
} else {
|
|
280
|
-
logger.log_message(LogLevel::Print, name)
|
|
330
|
+
logger.log_message(LogLevel::Print, name);
|
|
281
331
|
}
|
|
282
332
|
}
|
|
283
333
|
v => logger.log_message(LogLevel::Print, &format!("{:?}", v)),
|
|
@@ -286,6 +336,12 @@ pub fn execute_audio_block(
|
|
|
286
336
|
}
|
|
287
337
|
_ => {}
|
|
288
338
|
}
|
|
339
|
+
|
|
340
|
+
// Emit playhead event for UI bindings when building real-time playback
|
|
341
|
+
#[cfg(target_arch = "wasm32")]
|
|
342
|
+
{
|
|
343
|
+
emit_playhead(cursor_time, stmt.line, stmt.column);
|
|
344
|
+
}
|
|
289
345
|
}
|
|
290
346
|
|
|
291
347
|
// Execute parallel spawns (collect results)
|
|
@@ -316,31 +372,30 @@ pub fn execute_audio_block(
|
|
|
316
372
|
}
|
|
317
373
|
}
|
|
318
374
|
|
|
319
|
-
// ─────────────────────────────────────────────────────────────
|
|
320
375
|
// Built-in periodic events (e.g., on beat(n), on bar(n))
|
|
321
376
|
// Emit handlers across the timeline up to max_end_time.
|
|
322
377
|
// If no audio was scheduled (max_end_time == 0.0), skip.
|
|
323
378
|
// Don't schedule periodic events if we're already inside an event handler
|
|
324
379
|
let in_event = matches!(variable_table.get("__in_event"), Some(Value::Boolean(true)));
|
|
325
380
|
let depth_is_root = matches!(variable_table.get("__depth"), Some(Value::Number(n)) if (*n - 1.0).abs() < f32::EPSILON);
|
|
326
|
-
if max_end_time > 0.0 && !in_event && depth_is_root {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
381
|
+
if max_end_time > 0.0 && !in_event && depth_is_root && !global_store.events.is_empty() {
|
|
382
|
+
// Beat-based handlers (support "beat" and "$beat")
|
|
383
|
+
for ev_key in ["beat", "$beat"] {
|
|
384
|
+
if let Some(handlers) = global_store.get_event_handlers(ev_key) {
|
|
385
|
+
let mut seen: std::collections::HashSet<(usize, usize, usize)> =
|
|
386
|
+
std::collections::HashSet::new();
|
|
387
|
+
// Default every 1 beat if args missing
|
|
388
|
+
for h in handlers {
|
|
389
|
+
let key = (h.line, h.column, h.indent);
|
|
390
|
+
if !seen.insert(key) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if let StatementKind::On { event, args, body } = &h.kind {
|
|
394
|
+
let every: f32 = args
|
|
395
|
+
.as_ref()
|
|
396
|
+
.and_then(|v| v.first())
|
|
397
|
+
.and_then(|x| {
|
|
398
|
+
match x {
|
|
344
399
|
Value::Number(n) => Some(*n),
|
|
345
400
|
Value::Identifier(s) => {
|
|
346
401
|
// Try to resolve from variables first, fallback to parsing the literal
|
|
@@ -350,14 +405,81 @@ pub fn execute_audio_block(
|
|
|
350
405
|
}
|
|
351
406
|
}
|
|
352
407
|
_ => None,
|
|
408
|
+
}
|
|
409
|
+
})
|
|
410
|
+
.unwrap_or(1.0)
|
|
411
|
+
.max(0.0001);
|
|
412
|
+
let step = base_duration * every;
|
|
413
|
+
// Start from first full bar boundary after t=0
|
|
414
|
+
let mut t = step;
|
|
415
|
+
while t <= max_end_time {
|
|
416
|
+
// Prepare event context
|
|
417
|
+
let mut vt = variable_table.clone();
|
|
418
|
+
let mut ctx = std::collections::HashMap::new();
|
|
419
|
+
ctx.insert("name".to_string(), Value::String(event.clone()));
|
|
420
|
+
if let Some(a) = args.clone() {
|
|
421
|
+
ctx.insert("args".to_string(), Value::Array(a));
|
|
422
|
+
}
|
|
423
|
+
vt.set("event".to_string(), Value::Map(ctx));
|
|
424
|
+
vt.set("beat".to_string(), Value::Number(t / base_duration));
|
|
425
|
+
// Prevent nested scheduling
|
|
426
|
+
vt.set("__in_event".to_string(), Value::Boolean(true));
|
|
427
|
+
|
|
428
|
+
let (_m, _c) = execute_audio_block(
|
|
429
|
+
audio_engine,
|
|
430
|
+
global_store,
|
|
431
|
+
vt,
|
|
432
|
+
functions_table.clone(),
|
|
433
|
+
body,
|
|
434
|
+
base_bpm,
|
|
435
|
+
base_duration,
|
|
436
|
+
max_end_time,
|
|
437
|
+
t,
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
t += step;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Bar-based handlers (default 4/4 time => 4 beats per bar); support "bar" and "$bar"
|
|
448
|
+
for ev_key in ["bar", "$bar"] {
|
|
449
|
+
if let Some(handlers) = global_store.get_event_handlers(ev_key) {
|
|
450
|
+
let mut seen: std::collections::HashSet<(usize, usize, usize)> =
|
|
451
|
+
std::collections::HashSet::new();
|
|
452
|
+
for h in handlers {
|
|
453
|
+
let key = (h.line, h.column, h.indent);
|
|
454
|
+
if !seen.insert(key) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if let StatementKind::On { event, args, body } = &h.kind {
|
|
458
|
+
let bar_beats = 4.0f32; // TODO: time signature support
|
|
459
|
+
let first_only = args.as_ref().and_then(|v| v.first()).is_none();
|
|
460
|
+
|
|
461
|
+
let every_bar: f32 = if first_only {
|
|
462
|
+
1.0
|
|
463
|
+
} else {
|
|
464
|
+
args.as_ref()
|
|
465
|
+
.and_then(|v| v.first())
|
|
466
|
+
.and_then(|x| match x {
|
|
467
|
+
Value::Number(n) => Some(*n),
|
|
468
|
+
Value::Identifier(s) => match variable_table.get(s) {
|
|
469
|
+
Some(Value::Number(n)) => Some(*n),
|
|
470
|
+
_ => s.parse::<f32>().ok(),
|
|
471
|
+
},
|
|
472
|
+
_ => None,
|
|
353
473
|
})
|
|
354
474
|
.unwrap_or(1.0)
|
|
355
|
-
.max(0.0001)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
475
|
+
.max(0.0001)
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
let step = base_duration * bar_beats * every_bar;
|
|
479
|
+
|
|
480
|
+
if first_only {
|
|
481
|
+
let t = step; // first full bar after t=0
|
|
482
|
+
if t <= max_end_time {
|
|
361
483
|
let mut vt = variable_table.clone();
|
|
362
484
|
let mut ctx = std::collections::HashMap::new();
|
|
363
485
|
ctx.insert("name".to_string(), Value::String(event.clone()));
|
|
@@ -380,101 +502,34 @@ pub fn execute_audio_block(
|
|
|
380
502
|
max_end_time,
|
|
381
503
|
t,
|
|
382
504
|
);
|
|
383
|
-
|
|
384
|
-
t += step;
|
|
385
505
|
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
let mut seen: std::collections::HashSet<(usize, usize, usize)> =
|
|
395
|
-
std::collections::HashSet::new();
|
|
396
|
-
for h in handlers {
|
|
397
|
-
let key = (h.line, h.column, h.indent);
|
|
398
|
-
if !seen.insert(key) {
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
if let StatementKind::On { event, args, body } = &h.kind {
|
|
402
|
-
let bar_beats = 4.0f32; // TODO: time signature support
|
|
403
|
-
let first_only = args.as_ref().and_then(|v| v.get(0)).is_none();
|
|
404
|
-
|
|
405
|
-
let every_bar: f32 = if first_only {
|
|
406
|
-
1.0
|
|
407
|
-
} else {
|
|
408
|
-
args.as_ref()
|
|
409
|
-
.and_then(|v| v.get(0))
|
|
410
|
-
.and_then(|x| match x {
|
|
411
|
-
Value::Number(n) => Some(*n),
|
|
412
|
-
Value::Identifier(s) => match variable_table.get(s) {
|
|
413
|
-
Some(Value::Number(n)) => Some(*n),
|
|
414
|
-
_ => s.parse::<f32>().ok(),
|
|
415
|
-
},
|
|
416
|
-
_ => None,
|
|
417
|
-
})
|
|
418
|
-
.unwrap_or(1.0)
|
|
419
|
-
.max(0.0001)
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
let step = base_duration * bar_beats * every_bar;
|
|
423
|
-
|
|
424
|
-
if first_only {
|
|
425
|
-
let t = step; // first full bar after t=0
|
|
426
|
-
if t <= max_end_time {
|
|
427
|
-
let mut vt = variable_table.clone();
|
|
428
|
-
let mut ctx = std::collections::HashMap::new();
|
|
429
|
-
ctx.insert("name".to_string(), Value::String(event.clone()));
|
|
430
|
-
if let Some(a) = args.clone() {
|
|
431
|
-
ctx.insert("args".to_string(), Value::Array(a));
|
|
432
|
-
}
|
|
433
|
-
vt.set("event".to_string(), Value::Map(ctx));
|
|
434
|
-
vt.set("beat".to_string(), Value::Number(t / base_duration));
|
|
435
|
-
// Prevent nested scheduling
|
|
436
|
-
vt.set("__in_event".to_string(), Value::Boolean(true));
|
|
437
|
-
|
|
438
|
-
let (_m, _c) = execute_audio_block(
|
|
439
|
-
audio_engine,
|
|
440
|
-
global_store,
|
|
441
|
-
vt,
|
|
442
|
-
functions_table.clone(),
|
|
443
|
-
body,
|
|
444
|
-
base_bpm,
|
|
445
|
-
base_duration,
|
|
446
|
-
max_end_time,
|
|
447
|
-
t,
|
|
448
|
-
);
|
|
506
|
+
} else {
|
|
507
|
+
let mut t = step; // start from first full bar after t=0
|
|
508
|
+
while t <= max_end_time {
|
|
509
|
+
let mut vt = variable_table.clone();
|
|
510
|
+
let mut ctx = std::collections::HashMap::new();
|
|
511
|
+
ctx.insert("name".to_string(), Value::String(event.clone()));
|
|
512
|
+
if let Some(a) = args.clone() {
|
|
513
|
+
ctx.insert("args".to_string(), Value::Array(a));
|
|
449
514
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
let mut ctx = std::collections::HashMap::new();
|
|
455
|
-
ctx.insert("name".to_string(), Value::String(event.clone()));
|
|
456
|
-
if let Some(a) = args.clone() {
|
|
457
|
-
ctx.insert("args".to_string(), Value::Array(a));
|
|
458
|
-
}
|
|
459
|
-
vt.set("event".to_string(), Value::Map(ctx));
|
|
460
|
-
vt.set("beat".to_string(), Value::Number(t / base_duration));
|
|
461
|
-
// Prevent nested scheduling
|
|
462
|
-
vt.set("__in_event".to_string(), Value::Boolean(true));
|
|
515
|
+
vt.set("event".to_string(), Value::Map(ctx));
|
|
516
|
+
vt.set("beat".to_string(), Value::Number(t / base_duration));
|
|
517
|
+
// Prevent nested scheduling
|
|
518
|
+
vt.set("__in_event".to_string(), Value::Boolean(true));
|
|
463
519
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
520
|
+
let (_m, _c) = execute_audio_block(
|
|
521
|
+
audio_engine,
|
|
522
|
+
global_store,
|
|
523
|
+
vt,
|
|
524
|
+
functions_table.clone(),
|
|
525
|
+
body,
|
|
526
|
+
base_bpm,
|
|
527
|
+
base_duration,
|
|
528
|
+
max_end_time,
|
|
529
|
+
t,
|
|
530
|
+
);
|
|
475
531
|
|
|
476
|
-
|
|
477
|
-
}
|
|
532
|
+
t += step;
|
|
478
533
|
}
|
|
479
534
|
}
|
|
480
535
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
|
|
1
3
|
use crate::core::{
|
|
2
4
|
audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
|
|
3
5
|
parser::statement::Statement,
|
|
4
|
-
shared::value::Value,
|
|
5
6
|
store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
|
|
6
7
|
};
|
|
7
8
|
|
|
@@ -27,7 +28,7 @@ pub fn interprete_loop_statement(
|
|
|
27
28
|
loop_value.get("array"),
|
|
28
29
|
loop_value.get("body"),
|
|
29
30
|
) {
|
|
30
|
-
let
|
|
31
|
+
let engine = audio_engine;
|
|
31
32
|
let mut cur_time = cursor_time;
|
|
32
33
|
let mut max_time = max_end_time;
|
|
33
34
|
|
|
@@ -36,11 +37,11 @@ pub fn interprete_loop_statement(
|
|
|
36
37
|
scoped_vars.set(var_name.clone(), item.clone());
|
|
37
38
|
|
|
38
39
|
let (block_end_time, new_cursor) = execute_audio_block(
|
|
39
|
-
|
|
40
|
+
engine,
|
|
40
41
|
global_store,
|
|
41
42
|
scoped_vars,
|
|
42
43
|
functions_table.clone(),
|
|
43
|
-
|
|
44
|
+
loop_body,
|
|
44
45
|
base_bpm,
|
|
45
46
|
base_duration,
|
|
46
47
|
max_time,
|
|
@@ -84,13 +85,13 @@ pub fn interprete_loop_statement(
|
|
|
84
85
|
}
|
|
85
86
|
};
|
|
86
87
|
|
|
87
|
-
let
|
|
88
|
+
let engine = audio_engine;
|
|
88
89
|
let mut cur_time = cursor_time;
|
|
89
90
|
let mut max_time = max_end_time;
|
|
90
91
|
|
|
91
92
|
for _ in 0..loop_count {
|
|
92
93
|
let (block_end_time, new_cursor) = execute_audio_block(
|
|
93
|
-
|
|
94
|
+
engine,
|
|
94
95
|
global_store,
|
|
95
96
|
variable_table.clone(),
|
|
96
97
|
functions_table.clone(),
|