@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
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
use crate::core::parser::statement::StatementKind;
|
|
2
|
-
use devalang_types::store::VariableTable;
|
|
3
|
-
use devalang_types::{Duration, Value};
|
|
4
|
-
|
|
5
|
-
pub fn load_trigger(
|
|
6
|
-
trigger: &Value,
|
|
7
|
-
duration: &Duration,
|
|
8
|
-
_effects: &Option<Value>,
|
|
9
|
-
base_duration: f32,
|
|
10
|
-
variable_table: VariableTable,
|
|
11
|
-
) -> (String, f32) {
|
|
12
|
-
let mut trigger_path = String::new();
|
|
13
|
-
let mut duration_as_secs = 0.0;
|
|
14
|
-
|
|
15
|
-
match trigger {
|
|
16
|
-
Value::String(src) => {
|
|
17
|
-
trigger_path = src.to_string();
|
|
18
|
-
}
|
|
19
|
-
Value::Identifier(src) => {
|
|
20
|
-
trigger_path = src.to_string();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
Value::Map(map) => {
|
|
24
|
-
if let Some(Value::String(src)) = map.get("entity") {
|
|
25
|
-
trigger_path = format!("devalang://bank/{}", src);
|
|
26
|
-
} else if let Some(Value::Identifier(src)) = map.get("entity") {
|
|
27
|
-
trigger_path = format!("devalang://bank/{}", src);
|
|
28
|
-
} else {
|
|
29
|
-
eprintln!(
|
|
30
|
-
"❌ Trigger map must contain an 'entity' key with a string or identifier value."
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
Value::Sample(src) => {
|
|
35
|
-
trigger_path = src.to_string();
|
|
36
|
-
}
|
|
37
|
-
Value::Statement(stmt) => {
|
|
38
|
-
if let StatementKind::Trigger {
|
|
39
|
-
entity,
|
|
40
|
-
duration: _,
|
|
41
|
-
effects: _,
|
|
42
|
-
} = &stmt.kind
|
|
43
|
-
{
|
|
44
|
-
trigger_path = entity.clone();
|
|
45
|
-
} else {
|
|
46
|
-
eprintln!(
|
|
47
|
-
"❌ Trigger statement must be of type 'Trigger', found: {:?}",
|
|
48
|
-
stmt.kind
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
_ => {
|
|
53
|
-
eprintln!(
|
|
54
|
-
"❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
|
|
55
|
-
trigger
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
match duration {
|
|
61
|
-
Duration::Identifier(duration_identifier) => {
|
|
62
|
-
if duration_identifier == "auto" {
|
|
63
|
-
duration_as_secs = base_duration;
|
|
64
|
-
} else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
|
|
65
|
-
duration_as_secs = *num;
|
|
66
|
-
} else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
|
|
67
|
-
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
68
|
-
} else if let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
|
|
69
|
-
{
|
|
70
|
-
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
71
|
-
} else {
|
|
72
|
-
eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
Duration::Number(num) => {
|
|
77
|
-
duration_as_secs = *num;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
Duration::Auto => {
|
|
81
|
-
duration_as_secs = base_duration;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
Duration::Beat(beat_str) => {
|
|
85
|
-
let parts: Vec<&str> = beat_str.split('/').collect();
|
|
86
|
-
|
|
87
|
-
if parts.len() == 2 {
|
|
88
|
-
let numerator: f32 = parts[0].parse().unwrap_or(1.0);
|
|
89
|
-
let denominator: f32 = parts[1].parse().unwrap_or(1.0);
|
|
90
|
-
duration_as_secs = (numerator / denominator) * base_duration;
|
|
91
|
-
} else {
|
|
92
|
-
eprintln!("❌ Invalid beat duration format: {}", beat_str);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
(trigger_path, duration_as_secs)
|
|
98
|
-
}
|
|
1
|
+
use crate::core::parser::statement::StatementKind;
|
|
2
|
+
use devalang_types::store::VariableTable;
|
|
3
|
+
use devalang_types::{Duration, Value};
|
|
4
|
+
|
|
5
|
+
pub fn load_trigger(
|
|
6
|
+
trigger: &Value,
|
|
7
|
+
duration: &Duration,
|
|
8
|
+
_effects: &Option<Value>,
|
|
9
|
+
base_duration: f32,
|
|
10
|
+
variable_table: VariableTable,
|
|
11
|
+
) -> (String, f32) {
|
|
12
|
+
let mut trigger_path = String::new();
|
|
13
|
+
let mut duration_as_secs = 0.0;
|
|
14
|
+
|
|
15
|
+
match trigger {
|
|
16
|
+
Value::String(src) => {
|
|
17
|
+
trigger_path = src.to_string();
|
|
18
|
+
}
|
|
19
|
+
Value::Identifier(src) => {
|
|
20
|
+
trigger_path = src.to_string();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Value::Map(map) => {
|
|
24
|
+
if let Some(Value::String(src)) = map.get("entity") {
|
|
25
|
+
trigger_path = format!("devalang://bank/{}", src);
|
|
26
|
+
} else if let Some(Value::Identifier(src)) = map.get("entity") {
|
|
27
|
+
trigger_path = format!("devalang://bank/{}", src);
|
|
28
|
+
} else {
|
|
29
|
+
eprintln!(
|
|
30
|
+
"❌ Trigger map must contain an 'entity' key with a string or identifier value."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
Value::Sample(src) => {
|
|
35
|
+
trigger_path = src.to_string();
|
|
36
|
+
}
|
|
37
|
+
Value::Statement(stmt) => {
|
|
38
|
+
if let StatementKind::Trigger {
|
|
39
|
+
entity,
|
|
40
|
+
duration: _,
|
|
41
|
+
effects: _,
|
|
42
|
+
} = &stmt.kind
|
|
43
|
+
{
|
|
44
|
+
trigger_path = entity.clone();
|
|
45
|
+
} else {
|
|
46
|
+
eprintln!(
|
|
47
|
+
"❌ Trigger statement must be of type 'Trigger', found: {:?}",
|
|
48
|
+
stmt.kind
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
_ => {
|
|
53
|
+
eprintln!(
|
|
54
|
+
"❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
|
|
55
|
+
trigger
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
match duration {
|
|
61
|
+
Duration::Identifier(duration_identifier) => {
|
|
62
|
+
if duration_identifier == "auto" {
|
|
63
|
+
duration_as_secs = base_duration;
|
|
64
|
+
} else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
|
|
65
|
+
duration_as_secs = *num;
|
|
66
|
+
} else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
|
|
67
|
+
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
68
|
+
} else if let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
|
|
69
|
+
{
|
|
70
|
+
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
71
|
+
} else {
|
|
72
|
+
eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Duration::Number(num) => {
|
|
77
|
+
duration_as_secs = *num;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Duration::Auto => {
|
|
81
|
+
duration_as_secs = base_duration;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Duration::Beat(beat_str) => {
|
|
85
|
+
let parts: Vec<&str> = beat_str.split('/').collect();
|
|
86
|
+
|
|
87
|
+
if parts.len() == 2 {
|
|
88
|
+
let numerator: f32 = parts[0].parse().unwrap_or(1.0);
|
|
89
|
+
let denominator: f32 = parts[1].parse().unwrap_or(1.0);
|
|
90
|
+
duration_as_secs = (numerator / denominator) * base_duration;
|
|
91
|
+
} else {
|
|
92
|
+
eprintln!("❌ Invalid beat duration format: {}", beat_str);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
(trigger_path, duration_as_secs)
|
|
98
|
+
}
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source};
|
|
2
|
-
use std::{fs::File, io::BufReader};
|
|
3
|
-
|
|
4
|
-
pub struct AudioPlayer {
|
|
5
|
-
_stream: OutputStream,
|
|
6
|
-
handle: OutputStreamHandle,
|
|
7
|
-
sink: Sink,
|
|
8
|
-
last_path: Option<String>,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
impl Default for AudioPlayer {
|
|
12
|
-
fn default() -> Self {
|
|
13
|
-
Self::new()
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
impl AudioPlayer {
|
|
18
|
-
pub fn new() -> Self {
|
|
19
|
-
let (stream, handle) = OutputStream::try_default().unwrap();
|
|
20
|
-
let sink = Sink::try_new(&handle).unwrap();
|
|
21
|
-
|
|
22
|
-
Self {
|
|
23
|
-
_stream: stream,
|
|
24
|
-
handle,
|
|
25
|
-
sink,
|
|
26
|
-
last_path: None,
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
fn load_source(&self, path: &str) -> Option<impl Source<Item = f32> + Send + 'static> {
|
|
31
|
-
if let Ok(file) = File::open(path) {
|
|
32
|
-
let reader = BufReader::new(file);
|
|
33
|
-
match Decoder::new(reader) {
|
|
34
|
-
Ok(decoder) => Some(decoder.convert_samples()),
|
|
35
|
-
Err(e) => {
|
|
36
|
-
eprintln!("❌ Failed to decode audio file '{}': {}", path, e);
|
|
37
|
-
None
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
eprintln!("❌ Could not open audio file: {}", path);
|
|
42
|
-
None
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
pub fn play_file_once(&mut self, path: &str) {
|
|
47
|
-
self.sink.stop();
|
|
48
|
-
self.sink = Sink::try_new(&self.handle).unwrap();
|
|
49
|
-
self.sink.set_volume(1.0);
|
|
50
|
-
|
|
51
|
-
if let Some(source) = self.load_source(path) {
|
|
52
|
-
self.sink.append(source);
|
|
53
|
-
self.last_path = Some(path.to_string());
|
|
54
|
-
} else {
|
|
55
|
-
eprintln!("⚠️ Skipping playback: failed to load '{}'", path);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
pub fn replay_last(&mut self) {
|
|
60
|
-
if let Some(path) = self.last_path.clone() {
|
|
61
|
-
self.play_file_once(&path);
|
|
62
|
-
} else {
|
|
63
|
-
eprintln!("⚠️ No previous audio to replay.");
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
pub fn wait_until_end(&self) {
|
|
68
|
-
self.sink.sleep_until_end();
|
|
69
|
-
}
|
|
70
|
-
}
|
|
1
|
+
use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source};
|
|
2
|
+
use std::{fs::File, io::BufReader};
|
|
3
|
+
|
|
4
|
+
pub struct AudioPlayer {
|
|
5
|
+
_stream: OutputStream,
|
|
6
|
+
handle: OutputStreamHandle,
|
|
7
|
+
sink: Sink,
|
|
8
|
+
last_path: Option<String>,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
impl Default for AudioPlayer {
|
|
12
|
+
fn default() -> Self {
|
|
13
|
+
Self::new()
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl AudioPlayer {
|
|
18
|
+
pub fn new() -> Self {
|
|
19
|
+
let (stream, handle) = OutputStream::try_default().unwrap();
|
|
20
|
+
let sink = Sink::try_new(&handle).unwrap();
|
|
21
|
+
|
|
22
|
+
Self {
|
|
23
|
+
_stream: stream,
|
|
24
|
+
handle,
|
|
25
|
+
sink,
|
|
26
|
+
last_path: None,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn load_source(&self, path: &str) -> Option<impl Source<Item = f32> + Send + 'static> {
|
|
31
|
+
if let Ok(file) = File::open(path) {
|
|
32
|
+
let reader = BufReader::new(file);
|
|
33
|
+
match Decoder::new(reader) {
|
|
34
|
+
Ok(decoder) => Some(decoder.convert_samples()),
|
|
35
|
+
Err(e) => {
|
|
36
|
+
eprintln!("❌ Failed to decode audio file '{}': {}", path, e);
|
|
37
|
+
None
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
eprintln!("❌ Could not open audio file: {}", path);
|
|
42
|
+
None
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn play_file_once(&mut self, path: &str) {
|
|
47
|
+
self.sink.stop();
|
|
48
|
+
self.sink = Sink::try_new(&self.handle).unwrap();
|
|
49
|
+
self.sink.set_volume(1.0);
|
|
50
|
+
|
|
51
|
+
if let Some(source) = self.load_source(path) {
|
|
52
|
+
self.sink.append(source);
|
|
53
|
+
self.last_path = Some(path.to_string());
|
|
54
|
+
} else {
|
|
55
|
+
eprintln!("⚠️ Skipping playback: failed to load '{}'", path);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn replay_last(&mut self) {
|
|
60
|
+
if let Some(path) = self.last_path.clone() {
|
|
61
|
+
self.play_file_once(&path);
|
|
62
|
+
} else {
|
|
63
|
+
eprintln!("⚠️ No previous audio to replay.");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
pub fn wait_until_end(&self) {
|
|
68
|
+
self.sink.sleep_until_end();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
pub mod easing;
|
|
2
|
-
pub mod env;
|
|
3
|
-
pub mod math;
|
|
4
|
-
pub mod modulator;
|
|
5
|
-
|
|
6
|
-
pub use easing::find_and_eval_first_easing_call;
|
|
7
|
-
pub use env::resolve_env_atom;
|
|
8
|
-
pub use math::find_and_eval_first_math_call;
|
|
9
|
-
pub use modulator::find_and_eval_first_mod_call;
|
|
1
|
+
pub mod easing;
|
|
2
|
+
pub mod env;
|
|
3
|
+
pub mod math;
|
|
4
|
+
pub mod modulator;
|
|
5
|
+
|
|
6
|
+
pub use easing::find_and_eval_first_easing_call;
|
|
7
|
+
pub use env::resolve_env_atom;
|
|
8
|
+
pub use math::find_and_eval_first_math_call;
|
|
9
|
+
pub use modulator::find_and_eval_first_mod_call;
|
package/rust/core/builder/mod.rs
CHANGED
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
use crate::core::audio::engine::render_audio_with_modules;
|
|
2
|
-
use crate::core::parser::statement::Statement;
|
|
3
|
-
use crate::core::store::global::GlobalStore;
|
|
4
|
-
use devalang_utils::logger::Logger;
|
|
5
|
-
use std::io::Write;
|
|
6
|
-
use std::{collections::HashMap, fs::create_dir_all};
|
|
7
|
-
|
|
8
|
-
pub struct Builder {}
|
|
9
|
-
|
|
10
|
-
impl Default for Builder {
|
|
11
|
-
fn default() -> Self {
|
|
12
|
-
Self::new()
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
impl Builder {
|
|
17
|
-
pub fn new() -> Self {
|
|
18
|
-
Builder {}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
pub fn build_ast(
|
|
22
|
-
&self,
|
|
23
|
-
modules: &HashMap<String, Vec<Statement>>,
|
|
24
|
-
out_dir: &str,
|
|
25
|
-
compress: bool,
|
|
26
|
-
) {
|
|
27
|
-
for (name, statements) in modules {
|
|
28
|
-
let formatted_name = name.split("/").last().unwrap_or(name);
|
|
29
|
-
let formatted_name = formatted_name.replace(".deva", "");
|
|
30
|
-
|
|
31
|
-
create_dir_all(format!("{}/ast", out_dir)).expect("Failed to create AST directory");
|
|
32
|
-
|
|
33
|
-
let file_path = format!("{}/ast/{}.json", out_dir, formatted_name);
|
|
34
|
-
let mut file = std::fs::File::create(file_path).expect("Failed to create AST file");
|
|
35
|
-
let content = if compress {
|
|
36
|
-
serde_json::to_string(&statements).expect("Failed to serialize AST")
|
|
37
|
-
} else {
|
|
38
|
-
serde_json::to_string_pretty(&statements).expect("Failed to serialize AST")
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
file.write_all(content.as_bytes())
|
|
42
|
-
.expect("Failed to write AST to file");
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
pub fn build_audio(
|
|
47
|
-
&self,
|
|
48
|
-
modules: &HashMap<String, Vec<Statement>>,
|
|
49
|
-
normalized_output_dir: &str,
|
|
50
|
-
global_store: &mut GlobalStore,
|
|
51
|
-
audio_format: Option<String>,
|
|
52
|
-
sample_rate: Option<u32>,
|
|
53
|
-
) {
|
|
54
|
-
let logger = Logger::new();
|
|
55
|
-
|
|
56
|
-
let audio_engines =
|
|
57
|
-
render_audio_with_modules(modules.clone(), normalized_output_dir, global_store);
|
|
58
|
-
|
|
59
|
-
create_dir_all(format!("{}/audio", normalized_output_dir))
|
|
60
|
-
.expect("Failed to create audio directory");
|
|
61
|
-
|
|
62
|
-
for (module_name, mut audio_engine) in audio_engines {
|
|
63
|
-
let formatted_module_name = module_name
|
|
64
|
-
.split('/')
|
|
65
|
-
.next_back()
|
|
66
|
-
.unwrap_or(&module_name)
|
|
67
|
-
.replace(".deva", "");
|
|
68
|
-
|
|
69
|
-
let output_path = format!(
|
|
70
|
-
"{}/audio/{}.wav",
|
|
71
|
-
normalized_output_dir, formatted_module_name
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
match audio_engine.generate_wav_file(&output_path, audio_format.clone(), sample_rate) {
|
|
75
|
-
Ok(_) => {}
|
|
76
|
-
Err(msg) => {
|
|
77
|
-
logger.log_error_with_stacktrace(
|
|
78
|
-
&format!(
|
|
79
|
-
"Unable to generate WAV file for module '{}': {}",
|
|
80
|
-
formatted_module_name, msg
|
|
81
|
-
),
|
|
82
|
-
&module_name,
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
pub fn build_midi(
|
|
90
|
-
&self,
|
|
91
|
-
modules: &HashMap<String, Vec<Statement>>,
|
|
92
|
-
normalized_output_dir: &str,
|
|
93
|
-
global_store: &mut GlobalStore,
|
|
94
|
-
) {
|
|
95
|
-
let logger = Logger::new();
|
|
96
|
-
|
|
97
|
-
let audio_engines =
|
|
98
|
-
render_audio_with_modules(modules.clone(), normalized_output_dir, global_store);
|
|
99
|
-
|
|
100
|
-
create_dir_all(format!("{}/midi", normalized_output_dir))
|
|
101
|
-
.expect("Failed to create MIDI directory");
|
|
102
|
-
|
|
103
|
-
for (module_name, mut audio_engine) in audio_engines {
|
|
104
|
-
let formatted_module_name = module_name
|
|
105
|
-
.split('/')
|
|
106
|
-
.next_back()
|
|
107
|
-
.unwrap_or(&module_name)
|
|
108
|
-
.replace(".deva", "");
|
|
109
|
-
|
|
110
|
-
let output_path = format!(
|
|
111
|
-
"{}/midi/{}.mid",
|
|
112
|
-
normalized_output_dir, formatted_module_name
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
match audio_engine.generate_midi_file(&output_path, None, None) {
|
|
116
|
-
Ok(_) => {}
|
|
117
|
-
Err(msg) => {
|
|
118
|
-
logger.log_error_with_stacktrace(
|
|
119
|
-
&format!(
|
|
120
|
-
"Unable to generate MIDI file for module '{}': {}",
|
|
121
|
-
formatted_module_name, msg
|
|
122
|
-
),
|
|
123
|
-
&module_name,
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
1
|
+
use crate::core::audio::engine::render_audio_with_modules;
|
|
2
|
+
use crate::core::parser::statement::Statement;
|
|
3
|
+
use crate::core::store::global::GlobalStore;
|
|
4
|
+
use devalang_utils::logger::Logger;
|
|
5
|
+
use std::io::Write;
|
|
6
|
+
use std::{collections::HashMap, fs::create_dir_all};
|
|
7
|
+
|
|
8
|
+
pub struct Builder {}
|
|
9
|
+
|
|
10
|
+
impl Default for Builder {
|
|
11
|
+
fn default() -> Self {
|
|
12
|
+
Self::new()
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl Builder {
|
|
17
|
+
pub fn new() -> Self {
|
|
18
|
+
Builder {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn build_ast(
|
|
22
|
+
&self,
|
|
23
|
+
modules: &HashMap<String, Vec<Statement>>,
|
|
24
|
+
out_dir: &str,
|
|
25
|
+
compress: bool,
|
|
26
|
+
) {
|
|
27
|
+
for (name, statements) in modules {
|
|
28
|
+
let formatted_name = name.split("/").last().unwrap_or(name);
|
|
29
|
+
let formatted_name = formatted_name.replace(".deva", "");
|
|
30
|
+
|
|
31
|
+
create_dir_all(format!("{}/ast", out_dir)).expect("Failed to create AST directory");
|
|
32
|
+
|
|
33
|
+
let file_path = format!("{}/ast/{}.json", out_dir, formatted_name);
|
|
34
|
+
let mut file = std::fs::File::create(file_path).expect("Failed to create AST file");
|
|
35
|
+
let content = if compress {
|
|
36
|
+
serde_json::to_string(&statements).expect("Failed to serialize AST")
|
|
37
|
+
} else {
|
|
38
|
+
serde_json::to_string_pretty(&statements).expect("Failed to serialize AST")
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
file.write_all(content.as_bytes())
|
|
42
|
+
.expect("Failed to write AST to file");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn build_audio(
|
|
47
|
+
&self,
|
|
48
|
+
modules: &HashMap<String, Vec<Statement>>,
|
|
49
|
+
normalized_output_dir: &str,
|
|
50
|
+
global_store: &mut GlobalStore,
|
|
51
|
+
audio_format: Option<String>,
|
|
52
|
+
sample_rate: Option<u32>,
|
|
53
|
+
) {
|
|
54
|
+
let logger = Logger::new();
|
|
55
|
+
|
|
56
|
+
let audio_engines =
|
|
57
|
+
render_audio_with_modules(modules.clone(), normalized_output_dir, global_store);
|
|
58
|
+
|
|
59
|
+
create_dir_all(format!("{}/audio", normalized_output_dir))
|
|
60
|
+
.expect("Failed to create audio directory");
|
|
61
|
+
|
|
62
|
+
for (module_name, mut audio_engine) in audio_engines {
|
|
63
|
+
let formatted_module_name = module_name
|
|
64
|
+
.split('/')
|
|
65
|
+
.next_back()
|
|
66
|
+
.unwrap_or(&module_name)
|
|
67
|
+
.replace(".deva", "");
|
|
68
|
+
|
|
69
|
+
let output_path = format!(
|
|
70
|
+
"{}/audio/{}.wav",
|
|
71
|
+
normalized_output_dir, formatted_module_name
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
match audio_engine.generate_wav_file(&output_path, audio_format.clone(), sample_rate) {
|
|
75
|
+
Ok(_) => {}
|
|
76
|
+
Err(msg) => {
|
|
77
|
+
logger.log_error_with_stacktrace(
|
|
78
|
+
&format!(
|
|
79
|
+
"Unable to generate WAV file for module '{}': {}",
|
|
80
|
+
formatted_module_name, msg
|
|
81
|
+
),
|
|
82
|
+
&module_name,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
pub fn build_midi(
|
|
90
|
+
&self,
|
|
91
|
+
modules: &HashMap<String, Vec<Statement>>,
|
|
92
|
+
normalized_output_dir: &str,
|
|
93
|
+
global_store: &mut GlobalStore,
|
|
94
|
+
) {
|
|
95
|
+
let logger = Logger::new();
|
|
96
|
+
|
|
97
|
+
let audio_engines =
|
|
98
|
+
render_audio_with_modules(modules.clone(), normalized_output_dir, global_store);
|
|
99
|
+
|
|
100
|
+
create_dir_all(format!("{}/midi", normalized_output_dir))
|
|
101
|
+
.expect("Failed to create MIDI directory");
|
|
102
|
+
|
|
103
|
+
for (module_name, mut audio_engine) in audio_engines {
|
|
104
|
+
let formatted_module_name = module_name
|
|
105
|
+
.split('/')
|
|
106
|
+
.next_back()
|
|
107
|
+
.unwrap_or(&module_name)
|
|
108
|
+
.replace(".deva", "");
|
|
109
|
+
|
|
110
|
+
let output_path = format!(
|
|
111
|
+
"{}/midi/{}.mid",
|
|
112
|
+
normalized_output_dir, formatted_module_name
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
match audio_engine.generate_midi_file(&output_path, None, None) {
|
|
116
|
+
Ok(_) => {}
|
|
117
|
+
Err(msg) => {
|
|
118
|
+
logger.log_error_with_stacktrace(
|
|
119
|
+
&format!(
|
|
120
|
+
"Unable to generate MIDI file for module '{}': {}",
|
|
121
|
+
formatted_module_name, msg
|
|
122
|
+
),
|
|
123
|
+
&module_name,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
use crate::core::{debugger::Debugger, lexer::token::Token};
|
|
2
|
-
use std::{collections::HashMap, fs::create_dir_all};
|
|
3
|
-
|
|
4
|
-
pub fn write_lexer_log_file(
|
|
5
|
-
output_dir: &str,
|
|
6
|
-
file_name: &str,
|
|
7
|
-
modules: HashMap<String, Vec<Token>>,
|
|
8
|
-
) {
|
|
9
|
-
let debugger = Debugger::new();
|
|
10
|
-
let mut content = String::new();
|
|
11
|
-
|
|
12
|
-
let log_directory = format!("{}/logs", output_dir);
|
|
13
|
-
|
|
14
|
-
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
15
|
-
|
|
16
|
-
for (path, tokens) in modules {
|
|
17
|
-
content.push_str(&format!("--- Resolved Tokens for {} ---\n", path));
|
|
18
|
-
|
|
19
|
-
for token in tokens {
|
|
20
|
-
content.push_str(&format!("{:?}\n", token));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
content.push('\n');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
debugger.write_log_file(&log_directory, file_name, &content);
|
|
27
|
-
}
|
|
1
|
+
use crate::core::{debugger::Debugger, lexer::token::Token};
|
|
2
|
+
use std::{collections::HashMap, fs::create_dir_all};
|
|
3
|
+
|
|
4
|
+
pub fn write_lexer_log_file(
|
|
5
|
+
output_dir: &str,
|
|
6
|
+
file_name: &str,
|
|
7
|
+
modules: HashMap<String, Vec<Token>>,
|
|
8
|
+
) {
|
|
9
|
+
let debugger = Debugger::new();
|
|
10
|
+
let mut content = String::new();
|
|
11
|
+
|
|
12
|
+
let log_directory = format!("{}/logs", output_dir);
|
|
13
|
+
|
|
14
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
15
|
+
|
|
16
|
+
for (path, tokens) in modules {
|
|
17
|
+
content.push_str(&format!("--- Resolved Tokens for {} ---\n", path));
|
|
18
|
+
|
|
19
|
+
for token in tokens {
|
|
20
|
+
content.push_str(&format!("{:?}\n", token));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
content.push('\n');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
debugger.write_log_file(&log_directory, file_name, &content);
|
|
27
|
+
}
|