@devaloop/devalang 0.0.1-alpha.15 → 0.0.1-alpha.16-hotfix.0
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/.devalang +2 -0
- package/.github/workflows/ci.yml +92 -0
- package/Cargo.toml +60 -58
- package/README.md +1 -1
- package/docs/CHANGELOG.md +34 -1
- package/docs/CONTRIBUTING.md +101 -1
- package/docs/ROADMAP.md +1 -1
- package/docs/TODO.md +1 -1
- package/examples/automation.deva +1 -3
- package/examples/bank.deva +4 -4
- package/examples/events.deva +12 -0
- package/examples/function.deva +4 -4
- package/examples/index.deva +3 -5
- package/examples/loop.deva +5 -11
- package/examples/pattern.deva +8 -0
- package/examples/plugin.deva +12 -11
- package/examples/variables.deva +1 -1
- package/out-tsc/bin/index.js +51 -7
- package/out-tsc/index.js +3 -1
- package/out-tsc/scripts/postbuild.js +9 -10
- package/out-tsc/scripts/postinstall.js +49 -0
- package/package.json +12 -4
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +462 -455
- package/rust/cli/build.rs +252 -199
- package/rust/cli/check.rs +221 -180
- package/rust/cli/driver.rs +297 -292
- package/rust/cli/generator.rs +1 -0
- package/rust/cli/init.rs +87 -79
- package/rust/cli/install.rs +35 -32
- package/rust/cli/login.rs +127 -134
- package/rust/cli/mod.rs +13 -11
- package/rust/cli/play.rs +1123 -218
- package/rust/cli/telemetry.rs +19 -0
- package/rust/cli/template.rs +69 -57
- package/rust/cli/update.rs +6 -4
- package/rust/common/api.rs +5 -5
- package/rust/common/mod.rs +3 -3
- package/rust/config/driver.rs +118 -94
- package/rust/config/loader.rs +165 -156
- package/rust/config/mod.rs +4 -2
- package/rust/config/settings.rs +91 -0
- package/rust/config/stats.rs +257 -0
- package/rust/core/audio/engine.rs +696 -659
- package/rust/core/audio/evaluator.rs +263 -132
- package/rust/core/audio/interpreter/arrow_call.rs +198 -187
- package/rust/core/audio/interpreter/call.rs +98 -95
- package/rust/core/audio/interpreter/condition.rs +70 -71
- package/rust/core/audio/interpreter/driver.rs +487 -231
- package/rust/core/audio/interpreter/function.rs +26 -21
- package/rust/core/audio/interpreter/let_.rs +38 -26
- package/rust/core/audio/interpreter/load.rs +18 -18
- package/rust/core/audio/interpreter/loop_.rs +113 -106
- package/rust/core/audio/interpreter/mod.rs +14 -14
- package/rust/core/audio/interpreter/sleep.rs +27 -28
- package/rust/core/audio/interpreter/spawn.rs +105 -102
- package/rust/core/audio/interpreter/tempo.rs +19 -16
- package/rust/core/audio/interpreter/trigger.rs +239 -210
- package/rust/core/audio/loader/mod.rs +1 -1
- package/rust/core/audio/loader/trigger.rs +100 -94
- package/rust/core/audio/mod.rs +7 -7
- package/rust/core/audio/player.rs +64 -64
- package/rust/core/audio/renderer.rs +56 -53
- package/rust/core/audio/special/easing.rs +189 -120
- package/rust/core/audio/special/env.rs +43 -41
- package/rust/core/audio/special/math.rs +102 -92
- package/rust/core/audio/special/mod.rs +9 -9
- package/rust/core/audio/special/modulator.rs +143 -120
- package/rust/core/builder/mod.rs +80 -85
- package/rust/core/debugger/lexer.rs +27 -27
- package/rust/core/debugger/mod.rs +24 -23
- package/rust/core/debugger/module.rs +55 -47
- package/rust/core/debugger/preprocessor.rs +27 -27
- package/rust/core/debugger/store.rs +40 -39
- package/rust/core/error/mod.rs +80 -69
- 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 -292
- package/rust/core/lexer/handler/identifier.rs +46 -43
- package/rust/core/lexer/handler/indent.rs +66 -66
- package/rust/core/lexer/handler/mod.rs +16 -16
- 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 +54 -51
- package/rust/core/lexer/token.rs +97 -94
- package/rust/core/mod.rs +11 -11
- package/rust/core/parser/driver.rs +513 -490
- package/rust/core/parser/handler/arrow_call.rs +233 -227
- package/rust/core/parser/handler/at.rs +245 -162
- package/rust/core/parser/handler/bank.rs +94 -69
- package/rust/core/parser/handler/condition.rs +80 -74
- package/rust/core/parser/handler/dot.rs +143 -135
- package/rust/core/parser/handler/identifier/automate.rs +257 -194
- package/rust/core/parser/handler/identifier/call.rs +91 -88
- package/rust/core/parser/handler/identifier/emit.rs +66 -0
- package/rust/core/parser/handler/identifier/function.rs +100 -91
- package/rust/core/parser/handler/identifier/group.rs +85 -75
- package/rust/core/parser/handler/identifier/let_.rs +158 -143
- package/rust/core/parser/handler/identifier/mod.rs +54 -56
- package/rust/core/parser/handler/identifier/on.rs +98 -0
- package/rust/core/parser/handler/identifier/print.rs +52 -29
- package/rust/core/parser/handler/identifier/sleep.rs +36 -33
- package/rust/core/parser/handler/identifier/spawn.rs +91 -88
- package/rust/core/parser/handler/identifier/synth.rs +65 -63
- package/rust/core/parser/handler/loop_.rs +170 -89
- package/rust/core/parser/handler/mod.rs +8 -8
- package/rust/core/parser/handler/tempo.rs +53 -47
- package/rust/core/parser/mod.rs +4 -4
- package/rust/core/parser/statement.rs +142 -113
- package/rust/core/plugin/loader.rs +123 -48
- package/rust/core/plugin/mod.rs +2 -1
- package/rust/core/plugin/runner.rs +296 -0
- package/rust/core/preprocessor/loader.rs +515 -326
- package/rust/core/preprocessor/mod.rs +4 -4
- package/rust/core/preprocessor/module.rs +60 -58
- package/rust/core/preprocessor/processor.rs +99 -101
- package/rust/core/preprocessor/resolver/bank.rs +51 -48
- package/rust/core/preprocessor/resolver/call.rs +100 -101
- package/rust/core/preprocessor/resolver/condition.rs +97 -97
- package/rust/core/preprocessor/resolver/driver.rs +310 -280
- package/rust/core/preprocessor/resolver/function.rs +69 -68
- package/rust/core/preprocessor/resolver/group.rs +96 -91
- package/rust/core/preprocessor/resolver/let_.rs +32 -28
- package/rust/core/preprocessor/resolver/loop_.rs +320 -121
- package/rust/core/preprocessor/resolver/mod.rs +15 -15
- package/rust/core/preprocessor/resolver/spawn.rs +76 -73
- package/rust/core/preprocessor/resolver/synth.rs +56 -50
- package/rust/core/preprocessor/resolver/tempo.rs +50 -49
- package/rust/core/preprocessor/resolver/trigger.rs +113 -115
- package/rust/core/preprocessor/resolver/value.rs +81 -81
- package/rust/core/shared/duration.rs +9 -9
- package/rust/core/shared/mod.rs +3 -3
- package/rust/core/shared/value.rs +35 -32
- package/rust/core/store/function.rs +34 -34
- package/rust/core/store/global.rs +55 -38
- package/rust/core/store/mod.rs +5 -5
- package/rust/core/store/variable.rs +37 -34
- package/rust/core/utils/mod.rs +2 -2
- package/rust/core/utils/path.rs +37 -31
- package/rust/core/utils/validation.rs +35 -36
- package/rust/installer/addon.rs +84 -80
- package/rust/installer/bank.rs +62 -65
- package/rust/installer/mod.rs +5 -5
- package/rust/installer/plugin.rs +54 -55
- package/rust/installer/utils.rs +56 -56
- package/rust/lib.rs +156 -164
- package/rust/main.rs +250 -144
- package/rust/utils/error.rs +200 -51
- package/rust/utils/file.rs +38 -35
- package/rust/utils/first_usage.rs +76 -0
- package/rust/utils/logger.rs +195 -143
- package/rust/utils/mod.rs +9 -7
- package/rust/utils/signature.rs +19 -17
- package/rust/utils/spinner.rs +22 -19
- package/rust/utils/telemetry.rs +292 -0
- package/rust/utils/watcher.rs +34 -33
- package/templates/minimal/README.md +97 -121
- package/templates/welcome/README.md +97 -121
- package/typescript/bin/index.ts +19 -5
- package/typescript/index.ts +3 -1
- package/typescript/scripts/postbuild.ts +10 -6
- package/typescript/scripts/postinstall.ts +56 -0
- package/typescript/scripts/version/bump.ts +0 -1
- package/typescript/scripts/version/index.ts +0 -1
- package/out-tsc/bin/devalang.exe +0 -0
package/rust/core/audio/mod.rs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
pub mod engine;
|
|
2
|
-
pub mod
|
|
3
|
-
pub mod
|
|
4
|
-
pub mod
|
|
5
|
-
pub mod
|
|
6
|
-
pub mod
|
|
7
|
-
pub mod special;
|
|
1
|
+
pub mod engine;
|
|
2
|
+
pub mod evaluator;
|
|
3
|
+
pub mod interpreter;
|
|
4
|
+
pub mod loader;
|
|
5
|
+
pub mod player;
|
|
6
|
+
pub mod renderer;
|
|
7
|
+
pub mod special;
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
use rodio::{
|
|
2
|
-
use std::{
|
|
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) -> Option<impl Source<Item = f32> + Send + 'static> {
|
|
25
|
-
if let Ok(file) = File::open(path) {
|
|
26
|
-
let reader = BufReader::new(file);
|
|
27
|
-
match Decoder::new(reader) {
|
|
28
|
-
Ok(decoder) => Some(decoder.convert_samples()),
|
|
29
|
-
Err(e) => {
|
|
30
|
-
eprintln!("❌ Failed to decode audio file '{}': {}", path, e);
|
|
31
|
-
None
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
eprintln!("❌ Could not open audio file: {}", path);
|
|
36
|
-
None
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
pub fn play_file_once(&mut self, path: &str) {
|
|
41
|
-
self.sink.stop();
|
|
42
|
-
self.sink = Sink::try_new(&self.handle).unwrap();
|
|
43
|
-
self.sink.set_volume(1.0);
|
|
44
|
-
|
|
45
|
-
if let Some(source) = self.load_source(path) {
|
|
46
|
-
self.sink.append(source);
|
|
47
|
-
self.last_path = Some(path.to_string());
|
|
48
|
-
} else {
|
|
49
|
-
eprintln!("⚠️ Skipping playback: failed to load '{}'", path);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
pub fn replay_last(&mut self) {
|
|
54
|
-
if let Some(path) = self.last_path.clone() {
|
|
55
|
-
self.play_file_once(&path);
|
|
56
|
-
} else {
|
|
57
|
-
eprintln!("⚠️ No previous audio to replay.");
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
pub fn wait_until_end(&self) {
|
|
62
|
-
self.sink.sleep_until_end();
|
|
63
|
-
}
|
|
64
|
-
}
|
|
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) -> Option<impl Source<Item = f32> + Send + 'static> {
|
|
25
|
+
if let Ok(file) = File::open(path) {
|
|
26
|
+
let reader = BufReader::new(file);
|
|
27
|
+
match Decoder::new(reader) {
|
|
28
|
+
Ok(decoder) => Some(decoder.convert_samples()),
|
|
29
|
+
Err(e) => {
|
|
30
|
+
eprintln!("❌ Failed to decode audio file '{}': {}", path, e);
|
|
31
|
+
None
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
eprintln!("❌ Could not open audio file: {}", path);
|
|
36
|
+
None
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn play_file_once(&mut self, path: &str) {
|
|
41
|
+
self.sink.stop();
|
|
42
|
+
self.sink = Sink::try_new(&self.handle).unwrap();
|
|
43
|
+
self.sink.set_volume(1.0);
|
|
44
|
+
|
|
45
|
+
if let Some(source) = self.load_source(path) {
|
|
46
|
+
self.sink.append(source);
|
|
47
|
+
self.last_path = Some(path.to_string());
|
|
48
|
+
} else {
|
|
49
|
+
eprintln!("⚠️ Skipping playback: failed to load '{}'", path);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub fn replay_last(&mut self) {
|
|
54
|
+
if let Some(path) = self.last_path.clone() {
|
|
55
|
+
self.play_file_once(&path);
|
|
56
|
+
} else {
|
|
57
|
+
eprintln!("⚠️ No previous audio to replay.");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pub fn wait_until_end(&self) {
|
|
62
|
+
self.sink.sleep_until_end();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1,53 +1,56 @@
|
|
|
1
|
-
use
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
pub fn render_audio_with_modules(
|
|
12
|
-
modules: HashMap<String, Vec<Statement>>,
|
|
13
|
-
output_dir: &str,
|
|
14
|
-
global_store: &mut GlobalStore
|
|
15
|
-
) -> HashMap<String, AudioEngine> {
|
|
16
|
-
let mut result = HashMap::new();
|
|
17
|
-
|
|
18
|
-
for (module_name, statements) in modules {
|
|
19
|
-
let mut global_max_end_time: f32 = 0.0;
|
|
20
|
-
let mut audio_engine = AudioEngine::new(module_name.clone());
|
|
21
|
-
|
|
22
|
-
// Apply global variables to the initial engine
|
|
23
|
-
if let Some(module) = global_store.get_module(&module_name) {
|
|
24
|
-
// interprete statements to fill the audio buffer
|
|
25
|
-
let (module_max_end_time, _cursor_time) = run_audio_program(
|
|
26
|
-
&statements,
|
|
27
|
-
&mut audio_engine,
|
|
28
|
-
module_name.clone(),
|
|
29
|
-
output_dir.to_string(),
|
|
30
|
-
module.variable_table.clone(),
|
|
31
|
-
module.function_table.clone(),
|
|
32
|
-
global_store
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// Verify if the buffer is silent (all samples are zero)
|
|
36
|
-
if audio_engine.buffer.iter().all(|&s| s == 0) {
|
|
37
|
-
let logger = Logger::new();
|
|
38
|
-
logger.log_message(
|
|
39
|
-
LogLevel::Warning,
|
|
40
|
-
&format!(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
1
|
+
use crate::{
|
|
2
|
+
core::{
|
|
3
|
+
audio::{engine::AudioEngine, interpreter::driver::run_audio_program},
|
|
4
|
+
parser::statement::Statement,
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
},
|
|
7
|
+
utils::logger::{LogLevel, Logger},
|
|
8
|
+
};
|
|
9
|
+
use std::collections::HashMap;
|
|
10
|
+
|
|
11
|
+
pub fn render_audio_with_modules(
|
|
12
|
+
modules: HashMap<String, Vec<Statement>>,
|
|
13
|
+
output_dir: &str,
|
|
14
|
+
global_store: &mut GlobalStore,
|
|
15
|
+
) -> HashMap<String, AudioEngine> {
|
|
16
|
+
let mut result = HashMap::new();
|
|
17
|
+
|
|
18
|
+
for (module_name, statements) in modules {
|
|
19
|
+
let mut global_max_end_time: f32 = 0.0;
|
|
20
|
+
let mut audio_engine = AudioEngine::new(module_name.clone());
|
|
21
|
+
|
|
22
|
+
// Apply global variables to the initial engine
|
|
23
|
+
if let Some(module) = global_store.get_module(&module_name) {
|
|
24
|
+
// interprete statements to fill the audio buffer
|
|
25
|
+
let (module_max_end_time, _cursor_time) = run_audio_program(
|
|
26
|
+
&statements,
|
|
27
|
+
&mut audio_engine,
|
|
28
|
+
module_name.clone(),
|
|
29
|
+
output_dir.to_string(),
|
|
30
|
+
module.variable_table.clone(),
|
|
31
|
+
module.function_table.clone(),
|
|
32
|
+
global_store,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Verify if the buffer is silent (all samples are zero)
|
|
36
|
+
if audio_engine.buffer.iter().all(|&s| s == 0) {
|
|
37
|
+
let logger = Logger::new();
|
|
38
|
+
logger.log_message(
|
|
39
|
+
LogLevel::Warning,
|
|
40
|
+
&format!(
|
|
41
|
+
"Module '{}' ignored: silent buffer (no non-zero samples)",
|
|
42
|
+
module_name
|
|
43
|
+
),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Determines the maximum end time for the module
|
|
48
|
+
global_max_end_time = global_max_end_time.max(module_max_end_time);
|
|
49
|
+
audio_engine.set_duration(global_max_end_time);
|
|
50
|
+
|
|
51
|
+
result.insert(module_name, audio_engine);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
result
|
|
56
|
+
}
|
|
@@ -1,120 +1,189 @@
|
|
|
1
|
-
use crate::core::store::variable::VariableTable;
|
|
2
|
-
|
|
3
|
-
// Basic easing functions operating on t in [0,1]
|
|
4
|
-
fn easing_value(func: &str, t: f32) -> Option<f32> {
|
|
5
|
-
let x = t.clamp(0.0, 1.0);
|
|
6
|
-
match func {
|
|
7
|
-
"linear" => Some(x),
|
|
8
|
-
"easeInQuad" => Some(x * x),
|
|
9
|
-
"easeOutQuad" => Some(x * (2.0 - x)),
|
|
10
|
-
"easeInOutQuad" => {
|
|
11
|
-
if x < 0.5 {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
1
|
+
use crate::core::store::variable::VariableTable;
|
|
2
|
+
|
|
3
|
+
// Basic easing functions operating on t in [0,1]
|
|
4
|
+
fn easing_value(func: &str, t: f32) -> Option<f32> {
|
|
5
|
+
let x = t.clamp(0.0, 1.0);
|
|
6
|
+
match func {
|
|
7
|
+
"linear" => Some(x),
|
|
8
|
+
"easeInQuad" => Some(x * x),
|
|
9
|
+
"easeOutQuad" => Some(x * (2.0 - x)),
|
|
10
|
+
"easeInOutQuad" => {
|
|
11
|
+
if x < 0.5 {
|
|
12
|
+
Some(2.0 * x * x)
|
|
13
|
+
} else {
|
|
14
|
+
Some(-1.0 + (4.0 - 2.0 * x) * x)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Cubic
|
|
18
|
+
"easeInCubic" => Some(x * x * x),
|
|
19
|
+
"easeOutCubic" => Some(1.0 - (1.0 - x).powi(3)),
|
|
20
|
+
"easeInOutCubic" => {
|
|
21
|
+
if x < 0.5 {
|
|
22
|
+
Some(4.0 * x * x * x)
|
|
23
|
+
} else {
|
|
24
|
+
Some(1.0 - (-2.0 * x + 2.0).powi(3) / 2.0)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Quartic
|
|
28
|
+
"easeInQuart" => Some(x.powi(4)),
|
|
29
|
+
"easeOutQuart" => Some(1.0 - (1.0 - x).powi(4)),
|
|
30
|
+
"easeInOutQuart" => {
|
|
31
|
+
if x < 0.5 {
|
|
32
|
+
Some(8.0 * x.powi(4))
|
|
33
|
+
} else {
|
|
34
|
+
Some(1.0 - (-2.0 * x + 2.0).powi(4) / 2.0)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Exponential
|
|
38
|
+
"easeInExpo" => Some(if x <= 0.0 {
|
|
39
|
+
0.0
|
|
40
|
+
} else {
|
|
41
|
+
2.0_f32.powf(10.0 * x - 10.0)
|
|
42
|
+
}),
|
|
43
|
+
"easeOutExpo" => Some(if x >= 1.0 {
|
|
44
|
+
1.0
|
|
45
|
+
} else {
|
|
46
|
+
1.0 - 2.0_f32.powf(-10.0 * x)
|
|
47
|
+
}),
|
|
48
|
+
"easeInOutExpo" => Some(if x <= 0.0 {
|
|
49
|
+
0.0
|
|
50
|
+
} else if x >= 1.0 {
|
|
51
|
+
1.0
|
|
52
|
+
} else if x < 0.5 {
|
|
53
|
+
2.0_f32.powf(20.0 * x - 10.0) / 2.0
|
|
54
|
+
} else {
|
|
55
|
+
(2.0 - 2.0_f32.powf(-20.0 * x + 10.0)) / 2.0
|
|
56
|
+
}),
|
|
57
|
+
// Back (overshoot c ~ 1.70158)
|
|
58
|
+
"easeInBack" => {
|
|
59
|
+
let c = 1.70158;
|
|
60
|
+
Some((c + 1.0) * x * x * x - c * x * x)
|
|
61
|
+
}
|
|
62
|
+
"easeOutBack" => {
|
|
63
|
+
let c = 1.70158;
|
|
64
|
+
let y = 1.0 - x;
|
|
65
|
+
Some(1.0 - ((c + 1.0) * y * y * y - c * y * y))
|
|
66
|
+
}
|
|
67
|
+
"easeInOutBack" => {
|
|
68
|
+
let c1 = 1.70158;
|
|
69
|
+
let c2 = c1 * 1.525;
|
|
70
|
+
let x2 = x * 2.0;
|
|
71
|
+
if x2 < 1.0 {
|
|
72
|
+
Some((x2 * x2 * ((c2 + 1.0) * x2 - c2)) / 2.0)
|
|
73
|
+
} else {
|
|
74
|
+
let x2 = x2 - 2.0;
|
|
75
|
+
Some((x2 * x2 * ((c2 + 1.0) * x2 + c2)) / 2.0 + 1.0)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Elastic
|
|
79
|
+
"easeInElastic" => {
|
|
80
|
+
if x == 0.0 {
|
|
81
|
+
Some(0.0)
|
|
82
|
+
} else if x == 1.0 {
|
|
83
|
+
Some(1.0)
|
|
84
|
+
} else {
|
|
85
|
+
let c = 2.0 * std::f32::consts::PI / 3.0;
|
|
86
|
+
Some(-(2.0_f32.powf(10.0 * x - 10.0)) * ((x * 10.0 - 10.75) * c).sin())
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
"easeOutElastic" => {
|
|
90
|
+
if x == 0.0 {
|
|
91
|
+
Some(0.0)
|
|
92
|
+
} else if x == 1.0 {
|
|
93
|
+
Some(1.0)
|
|
94
|
+
} else {
|
|
95
|
+
let c = 2.0 * std::f32::consts::PI / 3.0;
|
|
96
|
+
Some(2.0_f32.powf(-10.0 * x) * ((x * 10.0 - 0.75) * c).sin() + 1.0)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
"easeInOutElastic" => {
|
|
100
|
+
if x == 0.0 {
|
|
101
|
+
Some(0.0)
|
|
102
|
+
} else if x == 1.0 {
|
|
103
|
+
Some(1.0)
|
|
104
|
+
} else {
|
|
105
|
+
let c = 2.0 * std::f32::consts::PI / 4.5;
|
|
106
|
+
if x < 0.5 {
|
|
107
|
+
Some(-(2.0_f32.powf(20.0 * x - 10.0)) * ((20.0 * x - 11.125) * c).sin() / 2.0)
|
|
108
|
+
} else {
|
|
109
|
+
Some(
|
|
110
|
+
2.0_f32.powf(-20.0 * x + 10.0) * ((20.0 * x - 11.125) * c).sin() / 2.0
|
|
111
|
+
+ 1.0,
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Bounce helpers
|
|
117
|
+
"easeInBounce" => Some(1.0 - bounce_out(1.0 - x)),
|
|
118
|
+
"easeOutBounce" => Some(bounce_out(x)),
|
|
119
|
+
"easeInOutBounce" => Some(if x < 0.5 {
|
|
120
|
+
(1.0 - bounce_out(1.0 - 2.0 * x)) / 2.0
|
|
121
|
+
} else {
|
|
122
|
+
(1.0 + bounce_out(2.0 * x - 1.0)) / 2.0
|
|
123
|
+
}),
|
|
124
|
+
_ => None,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn bounce_out(x: f32) -> f32 {
|
|
129
|
+
let n1 = 7.5625;
|
|
130
|
+
let d1 = 2.75;
|
|
131
|
+
if x < 1.0 / d1 {
|
|
132
|
+
n1 * x * x
|
|
133
|
+
} else if x < 2.0 / d1 {
|
|
134
|
+
let x = x - 1.5 / d1;
|
|
135
|
+
n1 * x * x + 0.75
|
|
136
|
+
} else if x < 2.5 / d1 {
|
|
137
|
+
let x = x - 2.25 / d1;
|
|
138
|
+
n1 * x * x + 0.9375
|
|
139
|
+
} else {
|
|
140
|
+
let x = x - 2.625 / d1;
|
|
141
|
+
n1 * x * x + 0.984375
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Find and evaluate the first $easing.<fn>(...) occurrence in the string.
|
|
146
|
+
// Accepts a single argument expression producing t in [0,1].
|
|
147
|
+
pub fn find_and_eval_first_easing_call<EvalFn>(
|
|
148
|
+
s: &str,
|
|
149
|
+
eval: EvalFn,
|
|
150
|
+
vars: &VariableTable,
|
|
151
|
+
bpm: f32,
|
|
152
|
+
beat: f32,
|
|
153
|
+
) -> Option<String>
|
|
154
|
+
where
|
|
155
|
+
EvalFn: Fn(&str, &VariableTable, f32, f32) -> Option<f32>,
|
|
156
|
+
{
|
|
157
|
+
let start = s.find("$easing.")?;
|
|
158
|
+
let open_rel = s[start..].find('(')?;
|
|
159
|
+
let open = start + open_rel;
|
|
160
|
+
let func = &s[start + 9..open];
|
|
161
|
+
|
|
162
|
+
// Find matching close parenthesis
|
|
163
|
+
let mut depth: i32 = 0;
|
|
164
|
+
let mut close_abs: Option<usize> = None;
|
|
165
|
+
for (i, ch) in s[open..].char_indices() {
|
|
166
|
+
match ch {
|
|
167
|
+
'(' => depth += 1,
|
|
168
|
+
')' => {
|
|
169
|
+
depth -= 1;
|
|
170
|
+
if depth == 0 {
|
|
171
|
+
close_abs = Some(open + i);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
_ => {}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
let close = close_abs?;
|
|
179
|
+
|
|
180
|
+
let inner = &s[open + 1..close];
|
|
181
|
+
let t = eval(inner, vars, bpm, beat)?;
|
|
182
|
+
let result = easing_value(func, t)?;
|
|
183
|
+
|
|
184
|
+
let mut replaced = String::new();
|
|
185
|
+
replaced.push_str(&s[..start]);
|
|
186
|
+
replaced.push_str(&result.to_string());
|
|
187
|
+
replaced.push_str(&s[close + 1..]);
|
|
188
|
+
Some(replaced)
|
|
189
|
+
}
|
|
@@ -1,41 +1,43 @@
|
|
|
1
|
-
use crate::core::store::variable::VariableTable;
|
|
2
|
-
use std::sync::OnceLock;
|
|
3
|
-
use std::time::{
|
|
4
|
-
|
|
5
|
-
static SESSION_SEED: OnceLock<f32> = OnceLock::new();
|
|
6
|
-
|
|
7
|
-
pub fn get_session_seed() -> f32 {
|
|
8
|
-
*SESSION_SEED.get_or_init(|| {
|
|
9
|
-
let now = SystemTime::now()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"$env.
|
|
23
|
-
|
|
24
|
-
"$env.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
1
|
+
use crate::core::store::variable::VariableTable;
|
|
2
|
+
use std::sync::OnceLock;
|
|
3
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
4
|
+
|
|
5
|
+
static SESSION_SEED: OnceLock<f32> = OnceLock::new();
|
|
6
|
+
|
|
7
|
+
pub fn get_session_seed() -> f32 {
|
|
8
|
+
*SESSION_SEED.get_or_init(|| {
|
|
9
|
+
let now = SystemTime::now()
|
|
10
|
+
.duration_since(UNIX_EPOCH)
|
|
11
|
+
.unwrap_or_default();
|
|
12
|
+
// Build a stable 0..1 seed from nanos
|
|
13
|
+
let nanos = now.subsec_nanos();
|
|
14
|
+
((nanos as f32) / 1_000_000_000.0).clamp(0.0, 1.0)
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Resolve special environment variables like $env.bpm, $env.beat, $env.position
|
|
19
|
+
// For now, $env.position is treated as an alias of beat.
|
|
20
|
+
pub fn resolve_env_atom(atom: &str, bpm: f32, beat: f32) -> Option<f32> {
|
|
21
|
+
match atom {
|
|
22
|
+
"$env.bpm" => Some(bpm),
|
|
23
|
+
"$env.beat" => Some(beat),
|
|
24
|
+
"$env.position" => Some(beat),
|
|
25
|
+
// Optional seed for deterministic randomness
|
|
26
|
+
"$env.seed" => Some(get_session_seed()),
|
|
27
|
+
_ => None,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Utility: resolve an identifier or numeric literal to f32 using the variable table
|
|
32
|
+
pub fn resolve_atom_or_var(atom: &str, vars: &VariableTable, bpm: f32, beat: f32) -> Option<f32> {
|
|
33
|
+
if let Some(v) = resolve_env_atom(atom, bpm, beat) {
|
|
34
|
+
return Some(v);
|
|
35
|
+
}
|
|
36
|
+
if let Ok(n) = atom.parse::<f32>() {
|
|
37
|
+
return Some(n);
|
|
38
|
+
}
|
|
39
|
+
if let Some(crate::core::shared::value::Value::Number(n)) = vars.get(atom) {
|
|
40
|
+
return Some(*n);
|
|
41
|
+
}
|
|
42
|
+
None
|
|
43
|
+
}
|